Source of infinitecanvas

/* appjet:version 0.1 *//* * Infinite Canvas 0.1 * Copyright (c) Microsoft Corporation. All rights reserved. * For more information, contact igilmanms@gmail.com *//* appjet:server */import("storage");import("lib-json");import("dlog");var adminFeatures = false;function getScene(name){    if(!storage.scenes)        return null;    var scene = null;    storage.scenes.filter({'name': name}).forEach(function(item) { scene = item; });    return scene;}function getItems(scene){    if(!scene)        return null;    var sceneArray = new Array();    scene.items.sortBy('index').forEach(function(item) { sceneArray.push(item); });    return sceneArray;}function loadScene(name){    var scene = getScene(name);    if(!scene)        return null;    return getItems(scene);}function isFeatured(scene){    return scene.featured === 'true';}function isPublished(scene){    return scene.publish !== 'false';}function getQueryParam(tag){    var args = unescape(request.query).split('&');    var count = args.length;    var a;    for(a = 0; a < count; a++)    {        var argParts = args[a].split('=');        if(argParts[0] == tag)            return argParts[1];    }    return '';}function Clone(what){    for (i in what)        this[i] = what[i];}var logoHTML = """            <!-- Logo -->            <img src="http://i478.photobucket.com/albums/rr143/igilman/logo-1.png" style="position:absolute; left:0px; top:0px; z-index:500" onclick="window.location = '/'">""";var deleteHTML = """            <!-- Delete Panel -->            <div id="deletePanel" style="position:absolute; z-index:1000; width:350px; height:100px; padding:10px; background-color:#303030; border:1px solid #888888; visibility:hidden">                Are you sure you want to delete this comic?<br><br>                Comic Passcode:                <input type="password" id="deleteCode" size="25"><br><br>                <input value="Delete" onclick="doDelete();" type="button">                <input value="Cancel" onclick="toggleDeletePanel();" type="button">            </div>""";var path = request.path;if(path == '/convert'){    var disable = true;    if(disable)        print('nothing to do');    else if(storage.scenes)    {        storage.scenes.forEach(function(scene)        {            if(!scene.creationTime)                scene.creationTime = 1;            print('done: ' + scene.name);            printp('');        });    }}else if(path == '/setpass'){    var error = '';    if(!adminFeatures)        error = 'Admin features are turned off.';    if(!error)    {        var params = request.params;        var password = params.password;        if(storage.__export && storage.__export.password)            error = 'Password already set';        else        {            if(!storage.__export)                storage.__export = new StorableObject();            storage.__export.password = password;        }    }    var result = new Object();    if(error)    {        result.message = error;        result.success = false;    }    else    {        result.message = 'Password set successfully!';        result.success = true;    }    page.setMode('plain');    print(JSON.stringify(result));}else if(path == '/setsalt'){    var error = '';    if(!adminFeatures)        error = 'Admin features are turned off.';    if(!error)    {        var params = request.params;        var salt = params.salt;        if(storage.salt)            error = 'Salt already set';        else            storage.salt = salt;    }    var result = new Object();    if(error)    {        result.message = error;        result.success = false;    }    else    {        result.message = 'Salt set successfully!';        result.success = true;    }    page.setMode('plain');    print(JSON.stringify(result));}else if(path == '/save'){    var error = '';    var params = request.params;    var password = params.password;    var secure = (adminFeatures && password && storage.__export.password == password);    if(!error)    {        var name = params.name;        var code = params.code;        var author = params.author;        var publish = params.publish;        var fade = params.fade;        var creationTime;        var featured;        if(secure)        {            creationTime = parseInt(params.creationTime);            featured = params.featured;        }        var items = new Array();        var argItems = params.items.split(';');        var bCount = argItems.length;        var b;        for(b = 0; b < bCount; b++)        {            var itemParts = argItems[b].split('*');            if(itemParts.length != 5)                error = 'badly formed item';            else                items.push({index: b, url: itemParts[0],                        x: itemParts[1], y: itemParts[2],                        width: itemParts[3], height: itemParts[4]});        }    }    if(!error)    {        if(!name)            error = 'Please enter a title for this comic.';        else if(!code)            error = 'Please enter the passcode for this comic, or if you\'re saving it for the first time, make one up.';        else if(!author)            error = 'Please enter the name you\'d like to be credited with.';        else if(!items.length)            error = 'You can\'t save a comic with no images!';        else        {            if(!secure)                code = md5(code + storage.salt);            if(!storage.scenes)                storage.scenes = new StorableCollection();            var scene = getScene(name);            if(scene)            {                if(scene.code != code)                    error = 'This is the wrong passcode for this comic.';            }            else            {                scene = new StorableObject();                scene.name = name;                scene.code = code;                scene.creationTime = (new Date()).getTime();                storage.scenes.add(scene);            }            if(!error)            {                scene.author = author;                scene.publish = publish;                scene.fade = fade;                if(secure)                {                    scene.creationTime = creationTime;                    scene.featured = featured;                }                scene.items = new StorableCollection();                count = items.length;                for(a = 0; a < count; a++)                    scene.items.add(items[a]);            }        }    }    var result = new Object();    if(error)    {        result.message = error;        result.success = false;    }    else    {        result.message = 'Saved successfully!';        result.success = true;    }    page.setMode('plain');    print(JSON.stringify(result));}else if(path == '/delete'){    var error = '';    var params = request.params;    var password = params.password;    var secure = (adminFeatures && password && storage.__export.password == password);    var params = request.params;    var name = params.name;    var code = params.code;    if(!error)    {        if(!name)            error = 'Please enter the name for the comic you want to delete.';        else if(!code)            error = 'Please enter the passcode for the comic you want to delete.';        else if(!storage.scenes)            error = 'No comics to delete.';        else        {            code = md5(code + storage.salt);            var scene = getScene(name);            if(!scene)                error = 'There is no comic of that name.';            else if(scene.code != code && !secure)                error = 'This is the wrong passcode for this comic.';            else                storage.scenes.remove(scene);        }    }    var result = new Object();    if(error)    {        result.message = error;        result.success = false;    }    else    {        result.message = 'Deleted successfully.';        result.success = true;    }    page.setMode('plain');    print(JSON.stringify(result));}else if(path == '/feature'){    var error = '';    var params = request.params;    var password = params.password;    var name = params.name;    var secure = (adminFeatures && password && storage.__export.password == password);    if(!secure)        error = 'You\'re not authorized to do this.';    if(!error)    {        if(!name)            error = 'Please enter the name for the comic you want to feature.';        else if(!storage.scenes)            error = 'No comics to feature.';        else        {            var scene = getScene(name);            if(!scene)                error = 'There is no comic of that name.';            else                scene.featured = 'true';        }    }    var result = new Object();    if(error)    {        result.message = error;        result.success = false;    }    else    {        result.message = 'Featured successfully.';        result.success = true;    }    page.setMode('plain');    print(JSON.stringify(result));}else if(path == '/rss'){    page.setMode('plain');    print(html("""<?xml version="1.0" encoding="UTF-8"?><rss version="2.0">  <channel>    <title>Infinite Canvas Comics</title>    <link>http://infinitecanvas.jgate.de</link>    <description>Latest comics on Infinite Canvas</description>    <language>en-us</language>"""));    if(storage.scenes)    {        storage.scenes.sortBy('-creationTime').forEach(function(item)        {            if(isPublished(item))            {                var date = new Date();                date.setTime(item.creationTime);                var link = 'http://infinitecanvas.jgate.de/view?name=' + escape(item.name);                var name = item.name;                if(item.author)                    name += ' by ' + item.author;                name = name.replace(/&/g, '&');                print(html('    <item>\n'));                print(html('      <title>' + name + '</title>\n'));                print(html('      <description>' + name + '</description>\n'));                print(html('      <pubDate>' + date.toString() + '</pubDate>\n'));                print(html('      <guid>' + link + '</guid>\n'));                print(html('      <link>' + link + '</link>\n'));                print(html('    </item>\n'));            }        });    }    print(html("""  </channel></rss>"""));}else if(path == '/export'){    page.setMode('plain');    var json = false;    var secure = false;    var doList = (unescape(getQueryParam('list')) == 'true');    var name = unescape(getQueryParam('name'));    var password = unescape(getQueryParam('password'));    var format = unescape(getQueryParam('format'));    if(format == 'json')        json = true;    if(adminFeatures && password && password == storage.__export.password)        secure = true;    if(!json)    {        print(html("""<?xml version="1.0" encoding="UTF-8"?>"""));    }    if(doList)    {        if(storage.scenes && !json)        {            print(html('<scenes>\n'));            storage.scenes.sortBy('-creationTime').forEach(function(item)            {                if(isPublished(item) || secure)                    print(html('  <scene>' + escape(item.name) + '</scene>\n'));            });            print(html('</scenes>\n'));        }    }    else    {        var scene = getScene(name);        if(scene)        {            var sceneArray = getItems(scene);            if(json)            {                var sceneCopy = new Clone(scene);                sceneCopy.items = sceneArray;                if(!secure)                {                    delete sceneCopy.code;                    delete sceneCopy.creationTime;                    delete sceneCopy.publish;                    delete sceneCopy.featured;                }                print(JSON.stringify(sceneCopy));            }            else            {                name = name.replace(/&/g, '&');                print(html('<comic>\n'));                print(html('  <name>' + scene.name.replace(/&/g, '&') + '</name>\n'));                print(html('  <author>' + scene.author.replace(/&/g, '&') + '</author>\n'));                print(html('  <fade>' + scene.fade + '</fade>\n'));                if(secure)                {                    print(html('  <code>' + scene.code + '</code>\n'));                    print(html('  <creationTime>' + scene.creationTime + '</creationTime>\n'));                    print(html('  <publish>' + scene.publish + '</publish>\n'));                    print(html('  <featured>' + scene.featured + '</featured>\n'));                }                print(html('  <items>\n'));                var count = sceneArray.length;                var a;                for(a = 0; a < count; a++)                {                    var item = sceneArray[a];                    print(html('    <item>\n'));                    print(html('      <index>' + item.index + '</index>\n'));                    print(html('      <url>' + item.url.replace(/&/g, '&') + '</url>\n'));                    print(html('      <x>' + item.x + '</x>\n'));                    print(html('      <y>' + item.y + '</y>\n'));                    print(html('      <width>' + item.width + '</width>\n'));                    print(html('      <height>' + item.height + '</height>\n'));                    print(html('    </item>\n'));                }                print(html('  </items>\n'));                print(html('</comic>'));            }        }    }}else if(path == '/view'){    var comicName = unescape(getQueryParam('name'));    var sceneArray = null;    var fade = false;    var scene = getScene(comicName);    if(scene)    {        sceneArray = getItems(scene);        fade = scene.fade;        page.setTitle(scene.name + ' by ' + scene.author + ' | Infinite Canvas');    }    print(html("""        <div id="content" style="font-family:segoe UI, sans-serif; height:100%; width:100%">            """ + logoHTML + deleteHTML + """            <!-- Bottom Strip -->            <div style="color:#ffffff; position:absolute; z-index:500; bottom:-1px; height:26px; left:0px; right:0px; padding-left:5px; padding-top:5px; line-height:20px; width:100%;">                <img src="http://i478.photobucket.com/albums/rr143/igilman/gear_rest.png" style="position:absolute; left:6px; bottom:3px" onmouseover="showMenu()" onmouseout="hideMenu()">                <span style="position:absolute; cursor:default; right:105px; bottom:9px; font-size:30px; color:#888888" onclick="toggleHelpPanel()" title="Help">?</span>                <img id="fit" src="http://i478.photobucket.com/albums/rr143/igilman/viewAll_rest.png" style="position:absolute; right:68px; bottom:3px" onclick="controller.goToHome()" title="Zoom to Fit">                <img id="previous" src="http://i478.photobucket.com/albums/rr143/igilman/previous_rest.png" style="position:absolute; right:36px; bottom:3px" onclick="controller.goToPrevious()" title="Previous Panel">                <img id="next" src="http://i478.photobucket.com/albums/rr143/igilman/next_rest.png" style="position:absolute; right:6px; bottom:3px" onclick="controller.goToNext()" title="Next Panel">                <img id="nextHover" src="http://i478.photobucket.com/albums/rr143/igilman/next_hover.png" style="position:absolute; right:6px; bottom:3px;" onclick="controller.goToNext()" title="Next Panel">            </div>            <!-- Help Panel -->            <div id="helpPanel" class="help" style="position:absolute; z-index:1000; width:350px; height:190px; min-height:170px; padding:10px; background-color:#303030; border:1px solid #888888; visibility:hidden" onclick="toggleHelpPanel()">                <p><b>Infinite Canvas</b> (after <a href="http://en.wikipedia.org/wiki/Infinite_canvas" target="_blank">the term</a> coined by Scott McCloud) allows you to create and/or view comics unconstrained by page boundaries (either physical or virtual).</p>                <p><b>To view</b>, hit the Next button in the lower right.  You can also go to specific panels by clicking on them, or explore freely by dragging with your mouse and zooming with your mouse's scroll wheel.</p>                <p>Infinite Canvas is a side project from <a href="http://livelabs.com" target="_blank">Microsoft Live Labs</a>.  More info <a href="http://dragonosticism.wordpress.com/2009/01/22/infinite-canvas/" target="_blank">here</a>.</p>            </div>            <!-- Menu -->            <div id="menu" style="position:absolute; z-index:1000; left:5px; bottom:25px; padding:5px; background-color:#303030; border:1px solid #888888; visibility:hidden" onmouseover="showMenu()" onmouseout="hideMenu()">                <a href="#" onclick="toggleDeletePanel(); return false;">Delete</a><br>                <a href="#" onclick="doExportLink(); return false;">Export</a><br>                <a href="#" onclick="doEditLink(); return false;">Edit</a><br>            </div>        </div>        <script language="javascript" type="text/javascript">            var comicName = unescape('""" + escape(comicName) + """');            var menu = document.getElementById('menu');            var deletePanel = document.getElementById('deletePanel');            var helpPanel = document.getElementById('helpPanel');            var fitButton = document.getElementById('fit');            var previousButton = document.getElementById('previous');            var nextButton = document.getElementById('next');            var nextHover = document.getElementById('nextHover');            //------------------------------------------------------------------------------            start(eval('(' + '""" + JSON.stringify(sceneArray) + """' + ')'),                   'view'""" + fade + """);        </script>    """));}else if(path == '/create'){    var sceneArray = null;    var nameValue = '';    var authorValue = '';    var publishValue = 'checked';    var fadeValue = '';    var comicName = unescape(getQueryParam('name'));    var scene = getScene(comicName);    if(scene)    {        sceneArray = getItems(scene);        nameValue = 'value="' + scene.name + '"';        authorValue = 'value="' + scene.author + '"';        if(!isPublished(scene))            publishValue = '';        if(scene.fade === 'true')            fadeValue = 'checked';        page.setTitle('Edit: ' + scene.name + ' by ' + scene.author + ' | Infinite Canvas');    }    print(html("""        <div id="content" style="font-family:segoe UI, sans-serif; height:100%; width:100%">            """ + logoHTML + deleteHTML + """            <!-- Top Strip -->            <div style="color:#ffffff; position:absolute; z-index:500; height:26px; left:0px; right:0px; line-height:20px; width:100%;">                <span style="position:absolute; right:85px; top:6px">Selected Panel:</span>                <input value="Onionskin" onclick="controller.toggleSelectedItemOpacity()" type="button" style="position:absolute; right:6px; top:6px" title="Temporarily make the selected panel semi-transparent">            </div>            <!-- Bottom Strip -->            <div style="color:#ffffff; position:absolute; z-index:500; bottom:-1px; height:26px; left:0px; right:0px; padding-left:5px; padding-top:5px; line-height:20px; width:100%;">                <img src="http://i478.photobucket.com/albums/rr143/igilman/gear_rest.png" style="position:absolute; left:6px; bottom:3px" onmouseover="showMenu()" onmouseout="hideMenu()">                <input value="Add Image(s)" onclick="toggleAddPanel()" type="button" style="position:relative; left:36px; bottom:6px">                <input value="Arrange Horizontally" onclick="controller.doArrange();" type="button" style="position:relative; left:36px; bottom:6px">                <span style="position:relative; left:36px; bottom:3px"><input id="fade" onclick="updateFade()" type="checkbox" """ + fadeValue + """>Fade</span>                <input value="Save" onclick="toggleSavePanel()" type="button" style="position:relative; left:36px; bottom:6px">                <span style="position:absolute; cursor:default; right:105px; bottom:9px; font-size:30px; color:#888888" onclick="toggleHelpPanel()" title="Help">?</span>                <img src="http://i478.photobucket.com/albums/rr143/igilman/viewAll_rest.png" style="position:absolute; right:68px; bottom:3px" onclick="controller.goToHome()" title="Zoom to Fit">                <img src="http://i478.photobucket.com/albums/rr143/igilman/previous_rest.png" style="position:absolute; right:36px; bottom:3px" onclick="controller.goToPrevious()" title="Previous Panel">                <img src="http://i478.photobucket.com/albums/rr143/igilman/next_rest.png" style="position:absolute; right:6px; bottom:3px" onclick="controller.goToNext()" title="Next Panel">            </div>            <!-- Add Panel -->            <div id="addPanel" style="position:absolute; z-index:1000; width:500px; height:140px; padding:10px; background-color:#303030; border:1px solid #888888; visibility:hidden">                Add Image(s):                <input type="text" id="url" size="35">                <input value="Add" onclick="doAdd();" type="button">                <input value="Cancel" onclick="toggleAddPanel();" type="button">                <p class="help">Enter the URL for the image you'd like to add.  To add multiple images at once, separate them by a semi-colon.  Panels will be viewed in the order they're added.</p>                <p class="help">We don't yet support uploading images, so you have to provide URLs to images already on the web.  If you don't have a web server of your own, there are a number of services you can use, such as <a href="http://flickr.com/" target="_blank">Flickr</a>, <a href="http://photobucket.com/" target="_blank">Photobucket</a>, <a href="http://imageshack.us/" target="_blank">ImageShack</a> or <a href="http://imgur.com/" target="_blank">imgur</a>.</p>            </div>            <!-- Save Panel -->            <div id="savePanel" style="position:absolute; z-index:1000; width:350px; height:240px; padding:10px; background-color:#303030; border:1px solid #888888; visibility:hidden">                Comic Name:                <input type="text" id="name" size="25" """ + nameValue + """><br><br>                Comic Passcode:                <input type="password" id="code" size="25"><br><br>                Author Name:                <input id="author" size="25" """ + authorValue + """><br><br>                <input id="publish" type="checkbox" """ + publishValue + """>Publish to Directory<br><br>                <input value="Save" onclick="doSave();" type="button">                <input value="Cancel" onclick="toggleSavePanel();" type="button"><br>                <p class="help">Note that if you don't publish this comic, the only way you can get to it is by remembering the URL.</p>            </div>            <!-- Help Panel -->            <div id="helpPanel" class="help" style="position:absolute; z-index:1000; width:350px; height:380px; padding:10px; background-color:#303030; border:1px solid #888888; visibility:hidden" onclick="toggleHelpPanel()">                <p><b>Infinite Canvas</b> (after <a href="http://en.wikipedia.org/wiki/Infinite_canvas" target="_blank">the term</a> coined by Scott McCloud) allows you to create and/or view comics unconstrained by page boundaries (either physical or virtual).</p>                <p><b>To create</b>, hit the Add Image(s) button in the lower left.  To move images, drag them around.  To resize images, hold down the shift key while you drag them.  To move the canvas, drag where there is no image.  To zoom, use the scroll wheel on your mouse.  When you're happy, hit the Save button.</p>                <p>The Fade checkbox puts your comic in a mode where only one panel is visible at a time, and they fade from one to the next.  This is best for comics that rely heavily on zooming.  This feature is still experimental, and really only works in view mode.</p>                <p>Please note that this site is a <b>work in progress</b>, and any comics you put on here are not guaranteed to survive future changes.  We'll try our best, but you never know.</p>                <p>Infinite Canvas is a side project from <a href="http://livelabs.com" target="_blank">Microsoft Live Labs</a>.  More info <a href="http://dragonosticism.wordpress.com/2009/01/22/infinite-canvas/" target="_blank">here</a>.</p>            </div>            <!-- Menu -->            <div id="menu" style="position:absolute; z-index:1000; left:5px; bottom:25px; padding:5px; background-color:#303030; border:1px solid #888888; visibility:hidden" onmouseover="showMenu()" onmouseout="hideMenu()">                <a href="#" onclick="toggleDeletePanel(); return false;">Delete</a><br>                <a href="#" onclick="doExportLink(); return false;">Export</a><br>                <a href="#" onclick="doViewLink(); return false;">View</a><br>            </div>        </div>        <script language="javascript" type="text/javascript">            var comicName = unescape('""" + escape(comicName) + """');            var addPanel = document.getElementById('addPanel');            var savePanel = document.getElementById('savePanel');            var deletePanel = document.getElementById('deletePanel');            var menu = document.getElementById('menu');            var helpPanel = document.getElementById('helpPanel');            //------------------------------------------------------------------------------            function toggleAddPanel()            {                if(addPanel.style.visibility == 'hidden')                {                    positionPanels();                    addPanel.style.visibility = '';                    var e = document.getElementById('url');                    e.focus();                    e.select();                }                else                    addPanel.style.visibility = 'hidden';            }            //------------------------------------------------------------------------------            function toggleSavePanel()            {                if(savePanel.style.visibility == 'hidden')                {                    positionPanels();                    savePanel.style.visibility = '';                    var e = document.getElementById('name');                    e.focus();                    e.select();                }                else                    savePanel.style.visibility = 'hidden';            }            //------------------------------------------------------------------------------            function updateFade()            {                var element = document.getElementById('fade');                controller.setFade(element.checked);            }            //------------------------------------------------------------------------------            function doAdd()            {                var element = document.getElementById('url');                controller.addItems(element.value);                element.value = '';                addPanel.style.visibility = 'hidden';            }            //------------------------------------------------------------------------------            function doSave()            {                var values = getCreateValues();                comicName = values.name;                controller.save(values.name, values.code, values.author, values.publish, values.fade);                savePanel.style.visibility = 'hidden';            }            //------------------------------------------------------------------------------            function getCreateValues()            {                var result = new Object();                result.name = document.getElementById('name').value;                result.code = document.getElementById('code').value;                result.author = document.getElementById('author').value;                result.publish = document.getElementById('publish').checked;                result.fade = document.getElementById('fade').checked;                return result;            }            //------------------------------------------------------------------------------            function haveCreateValuesChanged()            {                var values = getCreateValues();                return (values.name != originalValues.name                        || values.author != originalValues.author                        || values.publish != originalValues.publish                        || values.fade != originalValues.fade);            }            //------------------------------------------------------------------------------            function checkExit()            {                if(isDirty() || haveCreateValuesChanged())                    return 'Your unsaved changes will be lost.';            }            //------------------------------------------------------------------------------            positionPanels();            var originalValues = getCreateValues();            start(eval('(' + '""" + JSON.stringify(sceneArray) + """' + ')'), 'create');            window.onbeforeunload = checkExit;        </script>    """));}else{    page.head.write('<link rel="alternate" type="application/rss+xml" title="Latest Infinite Canvas Comics (RSS)" href="http://infinitecanvas.jgate.de/rss" />');    print(html("""        <div id="content" style="font-family:segoe UI, sans-serif; width:700px; margin-left:auto; margin-right:auto">            <center><img src="http://i478.photobucket.com/albums/rr143/igilman/biglogo-1.png">            <div class="subtitle">A Funky Side Project from <a href="http://livelabs.com">Microsoft Live Labs</a></div>            <br><br>            <input value="Create" onclick="window.location = 'create'return false" type="button">            <br><br>            <div style="width:500px">    """));    if(storage.scenes)    {        var featured = new Array();        var normal = new Array();        storage.scenes.sortBy('-creationTime').forEach(function(item)        {            if(isPublished(item))            {                if(isFeatured(item))                    featured.push(item);                else                    normal.push(item);            }        });        if(featured.length)        {            print(html('<h2>Featured:</h2>'));            var count = featured.length;            var a;            for(a = 0; a < count; a++)            {                var item = featured[a];                print(link('/view?name=' + escape(item.name), item.name));                if(item.author)                    print(html(' <span class="help">by</span> ' + item.author));                printp('');            }            print(html('<br>'));        }        if(normal.length)        {            print(html('<h2>Recent:</h2>'));            var count = normal.length;            var a;            for(a = 0; a < count; a++)            {                var item = normal[a];                print(link('/view?name=' + escape(item.name), item.name));                if(item.author)                    print(html(' <span class="help">by</span> ' + item.author));                printp('');            }        }    }    print(html("""            </div>            <br><br>            <div class="help">                Contact: <a href="mailto:ian@iangilman.com?subject=Infinite%20Canvas">Ian Gilman</a>                <p>                Powered by <a href="http://appjet.com">AppJet</a> on <a href="http://jgate.de">JGate</a>.<br>                Site Copyright 2008-09, Microsoft Corp.<br>                All comics and artwork belong to their respective creators.<br><br>            </div>            </center>        </div>    """));}/* appjet:css */html{    height:100%;    width:100%;}body{    background-color:#151515;    height:100%;    width:100%;    padding:0px;    offset:0px;    margin:0px;    color:#ffffff;}h2{    color:#888888;}a{    border: none;    color: #ffffff;}img{    border:  none;}#appjetfooter{    display: none;}.help{    font-size: smaller;    color: #cccccc;}.help a:link, .help a:visited, .help a:hover, .help a:active{    color: #dddddd;}.subtitle{    font-size: small;    color: #888888;}.subtitle a:link, .subtitle a:visited, .subtitle a:hover, .subtitle a:active{    color: #999999;}#menu a:link, #menu a:visited, #menu a:hover, #menu a:active{    text-decoration:none;}/* appjet:client */function showMenu(){    menu.style.visibility = '';}function hideMenu(){    menu.style.visibility = 'hidden';}function doViewLink(){    window.location = 'view?name=' + escape(comicName);}function doEditLink(){    window.location = 'create?name=' + escape(comicName);}function doExportLink(){    window.location = 'export?name=' + escape(comicName);}function positionPanels(){    var ws = getWindowSize();    if(typeof(addPanel) != 'undefined')    {        addPanel.style.left = ((ws.x - parseInt(addPanel.style.width)) / 2) + 'px';        addPanel.style.top = ((ws.y - parseInt(addPanel.style.height)) / 2) + 'px';    }    if(typeof(savePanel) != 'undefined')    {        savePanel.style.left = ((ws.x - parseInt(savePanel.style.width)) / 2) + 'px';        savePanel.style.top = ((ws.y - parseInt(savePanel.style.height)) / 2) + 'px';    }    if(typeof(helpPanel) != 'undefined')    {        helpPanel.style.left = ((ws.x - parseInt(helpPanel.style.width)) / 2) + 'px';        helpPanel.style.top = ((ws.y - parseInt(helpPanel.style.height)) / 2) + 'px';    }    if(typeof(deletePanel) != 'undefined')    {        deletePanel.style.left = ((ws.x - parseInt(deletePanel.style.width)) / 2) + 'px';        deletePanel.style.top = ((ws.y - parseInt(deletePanel.style.height)) / 2) + 'px';    }}function toggleDeletePanel(){    if(deletePanel.style.visibility == 'hidden')    {        positionPanels();        deletePanel.style.visibility = '';        var e = document.getElementById('deleteCode');        e.focus();        e.select();    }    else        deletePanel.style.visibility = 'hidden';}function doDelete(){    var code = document.getElementById('deleteCode').value;    controller.deleteComic(comicName, code);    deletePanel.style.visibility = 'hidden';}function toggleHelpPanel(){    if(helpPanel.style.visibility == 'hidden')    {        positionPanels();        helpPanel.style.visibility = '';    }    else        helpPanel.style.visibility = 'hidden';}function Clone(what){    for (i in what)        this[i] = what[i];}function Controller(){    this.focusItem = null;    this.itemIndex = -1;}Controller.prototype.start = function(scene){    if(scene)    {        var count = scene.length;        var a;        for(a = 0; a < count; a++)        {            var item = scene[a];            this.addItem(item.url, new Point(parseFloat(item.x), parseFloat(item.y)),                    new Point(parseFloat(item.width), parseFloat(item.height)));        }    }    this.updateNavButtons();}Controller.prototype.update = function(){    // ___ Finish Up    needsUpdate = true;}Controller.prototype.addItems = function(urls){    if(urls)    {        var words = urls.split(';');        var count = words.length;        var a;        for(a = 0; a < count; a++)        {            var word = trim(words[a]);            if(word)            {                this.addItem(word, new Point(10 * a, 0));                dirty = true;            }        }    }    this.goToHome();}Controller.prototype.addItem = function(url, pos, size){    if(!pos)        pos = new Point(10 * doc.items.length, 0);    var item = new ImageItem(url);    doc.addItem(item);    item.posX.setCurrentAndTarget(pos.x);    item.posY.setCurrentAndTarget(pos.y);    if(size)    {        item.sizeX.setCurrentAndTarget(size.x);        item.sizeY.setCurrentAndTarget(size.y);    }    else    {        item.sizeX.setCurrentAndTarget(1000);        item.sizeY.setCurrentAndTarget(1000);        item.size = -1;    }    if(fadeStrip && pageMode == 'view')        item.opacity.setCurrentAndTarget(0);    return item;}Controller.prototype.doArrange = function(){    x = 0;    var count = doc.items.length;    var a;    for(a = 0; a < count; a++)    {        var item = doc.items[a];        item.posX.target = x + (item.sizeX.target / 2);        item.posY.target = 0;        x += item.sizeX.target + 10;    }    this.goToHome();}Controller.prototype.goToHome = function(immediately){    if(doc.items.length)        viewport.centerOn(doc.getBounds(), 0.9, immediately);}Controller.prototype.goToNext = function(immediately, stopHover){    if(fadeStrip && this.focusItem)        this.focusItem.opacity.target = 0.0;    this.itemIndex++;    if(this.itemIndex >= doc.items.length)        this.itemIndex = -1;    var item = doc.items[this.itemIndex];    this.doSelectItem(item, immediately);    if(fadeStrip && item)    {        item.opacity.target = 1.0;        if(this.itemIndex == 0)            item.opacity.current = 1.0;    }    this.updateNavButtons();    if(typeof(stopHover) == 'undefined' || stopHover)        stopNextHover();}Controller.prototype.goToPrevious = function(){    if(fadeStrip && this.focusItem)        this.focusItem.opacity.target = 0.0;    if(this.itemIndex <= -1)        this.itemIndex = doc.items.length;    this.itemIndex--;    var item = doc.items[this.itemIndex];    this.doSelectItem(item);    if(fadeStrip && item)        item.opacity.target = 1.0;    this.updateNavButtons();}Controller.prototype.handleItemMouseDown = function(item){    this.setFocusItem(item);}Controller.prototype.doSelectItem = function(item, immediately){    this.setFocusItem(item);    if(item)        this.goToFocusItem(immediately);}Controller.prototype.handleItemClick = function(item, immediately){    if(item)    {        var zoom = viewport.zoomSpring.target;        var panX = viewport.panX.target;        var panY = viewport.panY.target;        var bounds = item.getBounds();        var x = (item.posX.target - panX) * zoom;        var y = (item.posY.target - panY) * zoom;        var neededZoom = viewport.getZoomForCenteringOn(bounds, this.centerFit());        if(zoom / neededZoom > 0.9                && neededZoom / zoom > 0.9                && Math.abs(x) < 20                && Math.abs(y) < 20)        {            if(item.opacity.target == 1.0)            {                var count = doc.items.length;                var a;                for(a = 0; a < count; a++)                {                    if(doc.items[a] == item)                    {                        if(a < count - 1)                        {                            this.itemIndex = a;                            this.goToNext(immediately, true);                        }                    }                }            }            return;        }    }    this.doSelectItem(item, immediately);}Controller.prototype.centerFit = function(){    return (fadeStrip ? 0.9 : 0.6);}Controller.prototype.goToFocusItem = function(immediately){    if(this.focusItem)    {        var r = this.focusItem.getBounds();        viewport.centerOn(r, this.centerFit(), immediately);    }    else        this.goToHome(immediately);}Controller.prototype.setFocusItem = function(item){    if(item == this.focusItem)        return;    if(this.focusItem && pageMode == 'create')        this.focusItem.isSelected = false;    this.focusItem = item;    if(this.focusItem && pageMode == 'create')        this.focusItem.isSelected = true;}Controller.prototype.toggleSelectedItemOpacity = function(){    if(!this.focusItem)        return;    if(this.focusItem.opacity.target > 0.5)        this.focusItem.opacity.target = 0.5;    else        this.focusItem.opacity.target = 1.0;}Controller.prototype.setFade = function(value){    if(value == fadeStrip)        return;    fadeStrip = value;    this.updateNavButtons();}Controller.prototype.updateNavButtons = function(){    if(pageMode == 'view')    {        if(fadeStrip && typeof(fitButton) != 'undefined')            fitButton.style.visibility = 'hidden';        var hideNext = (this.itemIndex == doc.items.length - 1);        if(typeof(nextButton) != 'undefined')        {            if(hideNext)                nextButton.style.visibility = 'hidden';            else                nextButton.style.visibility = '';        }        if(typeof(nextHover) != 'undefined')        {            if(hideNext)                nextHover.style.visibility = 'hidden';            else                nextHover.style.visibility = '';        }        if(typeof(previousButton) != 'undefined')        {            if(this.itemIndex == 0)                previousButton.style.visibility = 'hidden';            else                previousButton.style.visibility = '';        }    }}Controller.prototype.save = function(name, code, author, publish, fade){    if(!name)        alert('Please enter a comic name');    else if(!code)        alert('Please enter a comic passcode');    else if(!author)        alert('Please enter an author name');    else    {        var args = '';        var count = doc.items.length;        var a;        for(a = 0; a < count; a++)        {            var item = doc.items[a];            args += item.url + '*' + item.posX.target + '*' + item.posY.target                    + '*' + item.sizeX.target + '*' + item.sizeY.target;            if(a < count - 1)                args += ';';        }    //    alert(args);        var params = 'name=' + escape(name) + '&code=' + escape(code) + '&author=' + escape(author)                + '&publish=' + escape(publish) + '&fade=' + escape(fade)                + '&items=' + escape(args);        postXML('save', params, parseSave, this, 1);    }}Controller.prototype.deleteComic = function(name, code){    if(!name)        alert('Please enter a comic name');    else if(!code)        alert('Please enter a comic passcode');    else    {        var params = 'name=' + escape(name) + '&code=' + escape(code);        postXML('delete', params, parseDelete, this, 1);    }}function parseSave(http_request, requester){    var file = http_request.responseText;    if(!file)        return;    var result = eval('(' + file + ')');    alert(result.message);    handleSaveCompletion(result.success);}function parseDelete(http_request, requester){    var file = http_request.responseText;    if(!file)        return;    var result = eval('(' + file + ')');    alert(result.message);    handleDeleteCompletion(result.success);}function Document(){    this.items = Array();    //------------------------------------------------------------------------------    this.addItem = function(item)    {        this.items.push(item);        item.addToWorld();    }    //------------------------------------------------------------------------------    this.removeItem = function(item)    {        item.removeFromWorld();        var a;        for(a = 0; a < this.items.length; a++)        {            if(this.items[a] == item)            {                this.items.splice(a, 1);                break;            }        }    }    //------------------------------------------------------------------------------    this.getBounds = function(current)    {        var count = this.items.length;        if(count == 0)            return new Rect();        var r = this.items[0].getBounds(current);        var left = r.left;        var top = r.top;        var right = r.left + r.width;        var bottom = r.top + r.height;        var a;        for(a = 1; a < count; a++)        {            r = this.items[a].getBounds(current);            left = Math.min(left, r.left);            top = Math.min(top, r.top);            right = Math.max(right, r.left + r.width);            bottom = Math.max(bottom, r.top + r.height);        }        return new Rect(left, top, right - left, bottom - top);    }}function linkClick(event){    event = getEvent(event);    var img = getEventTarget(event);    var item = null;    if(img && img.mdItem)    {        item = img.mdItem;        var now = getMilliseconds();        if(item.lastClickTime + 500 > now)            return true;        item.lastClickTime = now;        stopEvent(event);    }    return false;}this.onImageLoad = function(img, item, success){    //debug('imageLoad: ' + img.src + ' ' + success);    drawer.isLoadingImage = false;    var big = false;    if(img.src == item.bigURL)        big = true;    if(big)    {        if(item.bigTile)            return//already have it    }    else    {        if(item.tile)            return//already have it    }    if(success)    {        var baseElement = document.getElementById('content');        img.onmouseover = albumMouseOver;        img.onmouseout = albumMouseOut;        img.style.position = 'absolute';        img.style.visibility = 'hidden';        img.opacityMaster = 0;        img.onmousedown = dragStart;        if(item.linkURL)        {            var link = document.createElement('a');            link.target = '_blank';            link.href = item.linkURL;            link.onclick = linkClick;            link.style.cursor = 'default';            baseElement.appendChild(link);            link.appendChild(img);        }        else            baseElement.appendChild(img);        item.lastClickTime = 0;        if(typeof(img.naturalWidth) == 'undefined')        {            img.naturalWidth = img.width;            img.naturalHeight = img.height;        }        if(img.naturalWidth < 10 || img.naturalHeight < 10)            tileFailedLoad(item);        img.mdItem = item;        if(big)            item.setBigTile(img, link);        else            item.setTile(img, link);        needsUpdate = true;    }    else if(!big) // $$$ I suppose we should black list big tiles that fail        tileFailedLoad(item);}function Drawer(){    this.isLoadingImage = false;    this.loadBailTime = 0;    this.loadTimeout = 5000;    this.focusItemIndex = -1;    this.albumLoadingStatusChanged = false;    this.albumInfoChanged = false;    //------------------------------------------------------------------------------    this.update = function()    {        var changed = false;        var lastFocusItemIndex = this.focusItemIndex;        this.focusItemIndex = -1;        var topVolume = 0;        var floor = 0.08;        var ceil = 0.44;        var windowSize = getWindowSize();        var windowBox = new Rect(0, 0, windowSize.x, windowSize.y);        var windowCenterX = windowSize.x / 2;        var windowCenterY = windowSize.y / 2;        var windowArea = windowSize.x * windowSize.y;        var floorArea = windowArea * floor;        var maxWidth = windowSize.x * 1.6;        var maxHeight = windowSize.y * 1.6;        var minWidth = 3;        var minHeight = 3;        var zoom = viewport.zoomSpring.current;        var panX = viewport.panX.current;        var panY = viewport.panY.current;        //artistDiv.style.left = ((-100 - panX) * zoom) + windowCenterX + 'px';        //artistDiv.style.top = ((-4 - panY) * zoom) + windowCenterY + 'px';        //artistDiv.style.width = 200 * zoom + 'px';        //artistDiv.style.height = 8 * zoom + 'px';        //artistDiv.style.fontSize = 8 * zoom + 'px';        var itemToLoad = null;        var loadBig = false;        var item;        var tile;        var bigTile;        var left;        var top;        var width;        var height;        var intersectionLeft;        var intersectionTop;        var intersectionRight;        var intersectionBottom;        var intersectionWidth;        var intersectionHeight;        var intersectionArea;        var onScreen;        var boxCenterX;        var boxCenterY;        var centerDist;        var score;        var percentage;        var foundHoverItem = false;        for(a = 0; a < doc.items.length; a++)        {            item = doc.items[a];            // ___ Item animation            if(item.update())                changed = true;            if(item.isLoaded())            {                onScreen = false;                if(item.opacity.current > 0.01)                {                    // ___ Position                    left = ((item.getLeft(true) - panX) * zoom) + windowCenterX;                    top = ((item.getTop(true) - panY) * zoom) + windowCenterY;                    width = item.sizeX.current * zoom;                    height = item.sizeY.current * zoom;                    // ___ Volume                    intersectionLeft = Math.max(left, 0);                    intersectionTop = Math.max(top, 0);                    intersectionRight = Math.min(left + width, windowSize.x);                    intersectionBottom = Math.min(top + height, windowSize.y);                    intersectionWidth = intersectionRight - intersectionLeft;                    intersectionHeight = intersectionBottom - intersectionTop;                    if(intersectionWidth > 0 && intersectionHeight > 0)                        onScreen = true;                }                tile = item.tile;                bigTile = item.bigTile;                div = item.div;                if(tile) // ImageItem                {                    if(onScreen)                    {                        if(width > tile.naturalWidth)                        {                            if(bigTile)                            {                                tile.style.visibility = 'hidden';                                tile = bigTile;                            }                            else if(!itemToLoad && item.bigURL)                            {                                itemToLoad = item;                                loadBig = true;                            }                        }                        else if(bigTile)                            bigTile.style.visibility = 'hidden';                        if(item.isSelected)                        {                            tile.style.border = '2px solid red';                            left -= 2;                            top -= 2;                        }                        else                            tile.style.border = '';                        tile.style.left = left + 'px';                        tile.style.top = top + 'px';                        tile.style.width = width + 'px';                        tile.style.height = height + 'px';                        // ___ opacity                        setOpacity(tile, item.opacity.current);                        var firstTime = (tile.opacityMaster == 0);                        if(false && tile.opacityMaster < 1)                        {                            if(hasBrowserAnimation)                                tile.opacityMaster = 1;                            else                            {                                tile.opacityMaster = Math.min(tile.opacityMaster + 0.34, 1);                                tile.style.opacity = tile.opacityMaster;                                changed = true;                            }                        }                        tile.style.visibility = '';                        // ___ Initial set up                        if(firstTime)                        {                            tile.opacityMaster = 1;                        }                    }                    else                    {                        tile.style.visibility = 'hidden';                        if(bigTile)                            bigTile.style.visibility = 'hidden';                    }                }                else if(div) // TextItem                {                    if(onScreen)                    {                        div.style.left = left + 'px';                        div.style.top = top + 'px';                        div.style.width = width + 'px';                        div.style.height = height + 'px';                        div.style.fontSize = item.fontSize * zoom + 'px';                        div.style.visibility = '';                    }                    else                    {                        div.style.visibility = 'hidden';                    }                }            }            else if(!itemToLoad)                itemToLoad = item;        }        if(itemToLoad && (!this.isLoadingImage || this.loadBailTime < getMilliseconds()))        {            this.isLoadingImage = true;            this.loadBailTime = getMilliseconds() + this.loadTimeout;            if(loadBig)                new ImageLoader(itemToLoad.bigURL, onImageLoad, itemToLoad);            else                new ImageLoader(itemToLoad.url, onImageLoad, itemToLoad);        }        return changed;    }}function ImageItem(url){    Item.apply(this);    this.url = url;    this.bigURL = '';    this.tile = null;    this.bigTile = null;    this.attached = false;    this.link = null;    this.bigLink = null;}ImageItem.prototype = new Item();ImageItem.prototype.isLoaded = function(){    return (this.tile ? true : false);}ImageItem.prototype.getIdentifier = function(){    return this.url;}ImageItem.prototype.addToWorld = function(){    if(this.attached)        return;    var baseElement = document.getElementById('content');    if(this.tile)    {        this.tile.style.visibility = 'hidden';        if(this.link)            baseElement.appendChild(this.link);        else            baseElement.appendChild(this.tile);    }    if(this.bigTile)    {        this.bigTile.style.visibility = 'hidden';        if(this.bigLink)            baseElement.appendChild(this.bigLink);        else            baseElement.appendChild(this.bigTile);    }    this.attached = true;}ImageItem.prototype.removeFromWorld = function(){    if(!this.attached)        return;    var baseElement = document.getElementById('content');    if(this.link)        baseElement.removeChild(this.link);    else if(this.tile)        baseElement.removeChild(this.tile);    if(this.bigLink)        baseElement.removeChild(this.bigLink);    else if(this.bigTile)        baseElement.removeChild(this.bigTile);    this.attached = false;}ImageItem.prototype.setTile = function(img, link){    this.tile = img;    this.link = link;    if(this.size == -1)    {        this.sizeX.setCurrentAndTarget(img.naturalWidth);        this.sizeY.setCurrentAndTarget(img.naturalHeight);    }    else if(this.size)    {        if(img.naturalWidth > img.naturalHeight)        {            this.sizeX.setCurrentAndTarget(this.size);            this.sizeY.setCurrentAndTarget(img.naturalHeight * (this.size / img.naturalWidth));        }        else        {            this.sizeY.setCurrentAndTarget(this.size);            this.sizeX.setCurrentAndTarget(img.naturalWidth * (this.size / img.naturalHeight));        }    }    this.attached = true;}ImageItem.prototype.setBigTile = function(img, link){    this.bigTile = img;    this.bigLink = link;}function ImageLoader(path, callback, callbackArg){    // store the callback    this.callback = callback;    this.callbackArg = callbackArg;    // create new Image object and add to array    this.image = new Image;    // set up event handlers for the Image object    this.image.onload = ImageLoader.prototype.onload;    this.image.onerror = ImageLoader.prototype.onerror;    this.image.onabort = ImageLoader.prototype.onabort;    this.image.loader = this;    // assign the .src property of the Image object    this.image.src = path;    //debug('image ' + path + ' complete ' + this.image.complete);}ImageLoader.prototype.onCompletion = function(success){    this.callback(this.image, this.callbackArg, success);}ImageLoader.prototype.onload = function(){    this.loader.onCompletion(true);}ImageLoader.prototype.onerror = function(){    this.loader.onCompletion(false);}ImageLoader.prototype.onabort = function(){    this.loader.onCompletion(false);}function Item(){    var damper = 3.25;    this.posX = new Spring(damper);    this.posY = new Spring(damper);    this.sizeX = new Spring(damper);    this.sizeY = new Spring(damper);    this.opacity = new Spring(8);    this.posX.setCurrentAndTarget(0);    this.posY.setCurrentAndTarget(0);    this.sizeX.setCurrentAndTarget(10);    this.sizeY.setCurrentAndTarget(10);    this.opacity.setCurrentAndTarget(1.0);}Item.prototype.update = function(){    var changed = false;    if(this.posX.step())        changed = true;    if(this.posY.step())        changed = true;    if(this.sizeX.step())        changed = true;    if(this.sizeY.step())        changed = true;    if(this.opacity.step())        changed = true;    return changed;}Item.prototype.isLoaded = function(){    return false;}Item.prototype.getIdentifier = function(){    return 0;}Item.prototype.removeFromWorld = function(){}Item.prototype.getBounds = function(current){    return new Rect(this.getLeft(current), this.getTop(current),            this.getWidth(current), this.getHeight(current));}Item.prototype.getLeft = function(current){    return (current ? this.posX.current : this.posX.target) - (this.getWidth(current) / 2);}Item.prototype.getTop = function(current){    return (current ? this.posY.current : this.posY.target) - (this.getHeight(current) / 2);}Item.prototype.getWidth = function(current){    return current ? this.sizeX.current : this.sizeX.target;}Item.prototype.getHeight = function(current){    return current ? this.sizeY.current : this.sizeY.target;}function Point(a, b){    if(typeof(a) != 'undefined')    {        this.x = a;        if(typeof(b) != 'undefined')            this.y = b;        else            this.y = a;    }    else    {        this.x = 0;        this.y = 0;    }    //------------------------------------------------------------------------------    this.isEqualTo = function(a)    {        if(typeof(a.x) != 'undefined' && typeof(a.y) != 'undefined')        {            return (this.x == a.x && this.y == a.y);        }        else        {            return (this.x == a && this.y == a);        }    }    //------------------------------------------------------------------------------    this.add = function(a)    {        if(typeof(a.x) != 'undefined' && typeof(a.y) != 'undefined')        {            this.x += a.x;            this.y += a.y;        }        else        {            this.x += a;            this.y += a;        }    }    //------------------------------------------------------------------------------    this.subtract = function(a)    {        if(typeof(a.x) != 'undefined' && typeof(a.y) != 'undefined')        {            this.x -= a.x;            this.y -= a.y;        }        else        {            this.x -= a;            this.y -= a;        }    }    //------------------------------------------------------------------------------    this.multiply = function(a)    {        if(typeof(a.x) != 'undefined' && typeof(a.y) != 'undefined')        {            this.x *= a.x;            this.y *= a.y;        }        else        {            this.x *= a;            this.y *= a;        }    }    //------------------------------------------------------------------------------    this.divide = function(a)    {        if(typeof(a.x) != 'undefined' && typeof(a.y) != 'undefined')        {            this.x /= a.x;            this.y /= a.y;        }        else        {            this.x /= a;            this.y /= a;        }    }    //------------------------------------------------------------------------------    this.round = function()    {        this.x = Math.round(this.x);        this.y = Math.round(this.y);    }    //------------------------------------------------------------------------------    this.normalize = function()    {        var absX = Math.abs(this.x);        var absY = Math.abs(this.y);        var ratio;        if(absX > absY)            ratio = 1 / absX;        else            ratio = 1 / absY;        this.x *= ratio;        this.y *= ratio;    }    //------------------------------------------------------------------------------    this.toString = function()    {        return new String() + this.x + ',' + this.y;    }}function Rect(a, b, c, d){    if(typeof(a) != 'undefined' && typeof(b) != 'undefined' && typeof(c) != 'undefined' && typeof(d) != 'undefined')    {        this.left = a;        this.top = b;        this.width = c;        this.height = d;    }    else    {        this.left = 0;        this.top = 0;        this.width = 0;        this.height = 0;    }    //------------------------------------------------------------------------------    this.size = function()    {        return new Point(this.width, this.height);    }    //------------------------------------------------------------------------------    this.getArea = function()    {        if(this.width <= 0 || this.height <= 0)            return 0;        return this.width * this.height;    }    //------------------------------------------------------------------------------    this.getCenter = function()    {        return new Point(this.left + (this.width / 2), this.top + (this.height / 2));    }    //------------------------------------------------------------------------------    this.getIntersection = function(r)    {        var left = Math.max(this.left, r.left);        var top = Math.max(this.top, r.top);        var right = Math.min(this.left + this.width, r.left + r.width);        var bottom = Math.min(this.top + this.height, r.top + r.height);        return new Rect(left, top, right - left, bottom - top);    }    //------------------------------------------------------------------------------    this.intersects = function(r)    {        return this.getIntersection(r).getArea() > 0;    }}function Spring(damper){    if(typeof(damper) != 'undefined')        this.damper = damper;    else        this.damper = 8;    this.current = 0;    this.target = 0;    this.setCurrentAndTarget = function(value)    {        this.current = value;        this.target = value;    }    this.step = function()    {        var diff = (this.target - this.current) / this.damper;        if(Math.abs(diff) < 0.000001)        {            diff = this.target - this.current;            this.current = this.target;        }        else            this.current += diff;        return diff;    }}var isIE = (navigator.userAgent.indexOf("MSIE") != -1);var isFF = (navigator.userAgent.indexOf("Firefox") != -1);var isFF3 = (navigator.userAgent.indexOf("Firefox/3") != -1);var isSafari = (navigator.userAgent.indexOf("Safari") != -1);function detectBrowserAnimation(){    return false;    var userAgent = navigator.userAgent;    if(isSafari)    {        var tag = 'Version/';        var version = userAgent.substr(userAgent.indexOf(tag) + tag.length);        version = version.substr(0, version.indexOf(' '));        var digits = version.split('.');        var digit0 = parseInt(digits[0]);        var digit1 = parseInt(digits[1]);        if(digit0 > 3 || (digit0 == 3 && digit1 >= 1))            return true;    }    return false;}var hasBrowserAnimation = detectBrowserAnimation();function getURLParam(strParamName){    var strReturn = "";    var strHref = window.location.href;    if(strHref.indexOf("?") > -1)    {        var strQueryString = strHref.substr(strHref.indexOf("?")).toLowerCase();        var aQueryString = strQueryString.split("&");        for(var iParam = 0; iParam < aQueryString.length; iParam++)        {            if(aQueryString[iParam].indexOf(strParamName + "=") > -1)            {                var aParam = aQueryString[iParam].split("=");                strReturn = aParam[1];                break;            }        }    }  return strReturn;}function getWindowSize(){    var result = new Point();    iftypeofwindow.innerWidth ) == 'number' )    {        //Non-IE        result.x = window.innerWidth;        result.y = window.innerHeight;    }    else if(document.documentElement && (document.documentElement.clientWidth || document.documentElement.clientHeight))    {        //IE 6+ in 'standards compliant mode'        result.x = document.documentElement.clientWidth;        result.y = document.documentElement.clientHeight;    }    else if(document.body && ( document.body.clientWidth || document.body.clientHeight))    {        //IE 4 compatible        result.x = document.body.clientWidth;        result.y = document.body.clientHeight;    }    return result;}function getMouse(event){    if(typeof(event) != 'undefined')    {        if(isIE)            return new Point(window.event.clientX, window.event.clientY);        else            return new Point(event.clientX, event.clientY);    }}function getEvent(event){    if(isIE)        return window.event;    else        return event;}function getEventTarget(event){    if(isIE)        return event.srcElement;    else        return event.target;}function stopEvent(event){    if (!event)        event = window.event;    if (event.stopPropagation)        event.stopPropagation();    if (event.preventDefault)        event.preventDefault();    event.cancel = true;    event.cancelBubble = true;    event.returnValue = false;    return false;}function getMilliseconds(){    var utilityDate = new Date();    return utilityDate.getTime();}function getRandomInt(cap){    return Math.floor(Math.random() * cap);}function assert(description, condition){    if(showAsserts && !condition)        alert('assertion failed: ' + description);}function trim(str){    return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');}function setOpacity(element, opacity){    element.style.opacity = opacity;       if(isIE)           element.style.filter = 'alpha(opacity=' + (opacity * 100) + ')';}function addEvent(elmt, eventName, handler, useCapture){    // technique from:    // http://blog.paranoidferret.com/index.php/2007/08/10/javascript-working-with-events/    if (elmt.addEventListener) {        if (eventName == "mousewheel") {            elmt.addEventListener("DOMMouseScroll", handler, useCapture);        }        // we are still going to add the mousewheel -- not a mistake!        // this is for opera, since it uses onmousewheel but needs addEventListener.        elmt.addEventListener(eventName, handler, useCapture);    } else if (elmt.attachEvent) {        elmt.attachEvent("on" + eventName, handler);        if (useCapture && elmt.setCapture) {            elmt.setCapture();        }    } else {        Seadragon.Debug.fail("Unable to attach event handler, no known technique.");    }}function Viewport(){    this.noSprings = false;    this.minZoom = 0.1;    this.maxZoom = 5000;    this.panX = new Spring(6);    this.panY = new Spring(6);    this.zoomSpring = new Spring(6);    this.zoomCenter = new Point();    this.changed = true;    var windowSize = getWindowSize();    this.zoomCenter.x = (windowSize.x / 2);    this.zoomCenter.y = (windowSize.y / 2);    this.panX.setCurrentAndTarget(0);    this.panY.setCurrentAndTarget(0);    this.zoomSpring.setCurrentAndTarget(1);    //------------------------------------------------------------------------------    this.zoom = function(delta, center, immediately)    {        this.setZoom(this.zoomSpring.target * delta, center, immediately);    }    //------------------------------------------------------------------------------    this.setZoom = function(value, center, immediately)    {        var windowSize = getWindowSize();        var windowCenter = new Clone(windowSize);        windowCenter.divide(2);        if(!center)            center = windowCenter;        if(this.noSprings)            immediately = true;  //          value = this.maxZoom;    //    else if(value < this.minZoom)      //      value = this.minZoom;        this.zoomSpring.target = value;        if(immediately)        {            if(this.zoomSpring.current != value)            {                if(center.isEqualTo(windowCenter) == false)                {                    var oldZoomDistance = new Clone(center);                    oldZoomDistance.subtract(windowCenter);                    var newZoomDistance = new Clone(oldZoomDistance);                    oldZoomDistance.divide(new Point(this.zoomSpring.current));                    newZoomDistance.divide(new Point(value));                    var addToPan = new Clone(oldZoomDistance);                    addToPan.subtract(newZoomDistance);                    this.pan(addToPan, true);                }                this.zoomSpring.current = this.zoomSpring.target;                this.changed = true;            }            this.zoomCenter = windowCenter;        }        else            this.zoomCenter = new Clone(center);    }    //------------------------------------------------------------------------------    this.panByPixels = function(delta)    {        var newDelta = new Clone(delta);        newDelta.divide(new Point(this.zoomSpring.current));        this.pan(newDelta, false);    }    //------------------------------------------------------------------------------    this.pan = function(delta, immediately)    {        var newPan = new Point(this.panX.target, this.panY.target);        newPan.add(delta);        this.setPan(newPan, immediately);    }    //------------------------------------------------------------------------------    this.setPan = function(value, immediately)    {        if(this.noSprings)            immediately = true;        this.panX.target = value.x;        this.panY.target = value.y;        if(immediately)        {            this.panX.current = value.x;            this.panY.current = value.y;            this.changed = true;        }    }    //------------------------------------------------------------------------------    this.shiftPan = function(value)    {        this.panX.target += value.x;        this.panY.target += value.y;        this.panX.current += value.x;        this.panY.current += value.y;        this.changed = true;    }    //------------------------------------------------------------------------------    this.centerOn = function(bounds, fit, immediately)    {        var windowSize = getWindowSize();        var windowCenter = new Clone(windowSize);        windowCenter.divide(2);        var contentSize = bounds.size();        var oldZoom = this.zoomSpring.current;        var newZoom = this.getZoomForCenteringOn(bounds, fit);        var newPan = new Point(bounds.left + (contentSize.x / 2), bounds.top + (contentSize.y / 2));        if(Math.abs(1 - (newZoom / oldZoom)) < 0.001)        {            this.setZoom(newZoom, new Point(windowSize.x, windowSize.y), immediately);            this.setPan(newPan, immediately);        }        else        {            var addToPan = new Point(newPan.x - this.panX.current, newPan.y - this.panY.current);            var multiplier = (oldZoom * newZoom) / (newZoom - oldZoom);            var distance = new Point(multiplier * addToPan.x, multiplier * addToPan.y);            var newZoomCenter = new Point(windowCenter.x + distance.x, windowCenter.y + distance.y);            this.setZoom(newZoom, newZoomCenter, immediately);        }    }    //------------------------------------------------------------------------------    this.getZoomForCenteringOn = function(bounds, fit)    {        if(typeof(fit) == 'undefined')            fit = 0.9;        var windowSize = getWindowSize();        var targetSize = new Point(windowSize.x * fit, windowSize.y * fit);        var contentSize = bounds.size();        var zoom = targetSize.y / contentSize.y;        if(contentSize.x * zoom > targetSize.x)            zoom = targetSize.x / contentSize.x;        return zoom;    }    //------------------------------------------------------------------------------    this.update = function()    {        var windowSize = getWindowSize();        var windowCenter = new Clone(windowSize);        windowCenter.divide(2);        var changed = this.changed;        this.changed = false;        // ___ zoom        var oldZoomDistance = new Clone(this.zoomCenter);        oldZoomDistance.x -= windowCenter.x;        oldZoomDistance.y -= windowCenter.y;        var newZoomDistance = new Clone(oldZoomDistance);        oldZoomDistance.x /= this.zoomSpring.current;        oldZoomDistance.y /= this.zoomSpring.current;        if(this.zoomSpring.step())            changed = true;        newZoomDistance.x /= this.zoomSpring.current;        newZoomDistance.y /= this.zoomSpring.current;        var addToPan = new Clone(oldZoomDistance);        addToPan.x -= newZoomDistance.x;        addToPan.y -= newZoomDistance.y;        this.panX.current = this.panX.current + addToPan.x;        this.panY.current = this.panY.current + addToPan.y;        this.panX.target = this.panX.target + addToPan.x;        this.panY.target = this.panY.target + addToPan.y;        // ___ pan        if(this.panX.step())            changed = true;        if(this.panY.step())            changed = true;        // ___ debug readout    //     var debug = document.getElementById('debug');    //     debug.innerHTML = 'this.zoomSpring.current ' + this.zoomSpring.current + '<br>'    //             + 'this.zoomSpring.target ' + this.zoomSpring.target;        return changed;    }}var pendingXML = new Array();var priorityPendingXML = new Array();var xmlInProcess = null;var xmlQueues = new Array();xmlQueues.push(new Array());xmlQueues.push(new Array());xmlQueues.push(new Array());function getXML(url, callback, object, priority, timeout){    if(typeof(timeout) == 'undefined')        timeout = -1;    var job = new Object();    job.type = 'GET';    job.url = url;    job.params = null;    job.xmlCallback = callback;    job.callbackObject = object;    job.timeout = timeout;    xmlQueues[priority].push(job);}function postXML(url, params, callback, object, priority, timeout){    if(typeof(timeout) == 'undefined')        timeout = -1;    var job = new Object();    job.type = 'POST';    job.url = url;    job.params = params;    job.xmlCallback = callback;    job.callbackObject = object;    job.timeout = timeout;    xmlQueues[priority].push(job);}function updateXML(){    if(xmlInProcess)    {        if(xmlInProcess.job.timeout != -1                && xmlInProcess.start + xmlInProcess.job.timeout < getMilliseconds())        {            xmlInProcess.http_request.onreadystatechange = null;            xmlInProcess.http_request.abort();            xmlInProcess.job.xmlCallback(null, xmlInProcess.job.callbackObject);            xmlInProcess = null;        }    }    else    {        var job = null;        var a;        for (a = 0; a < xmlQueues.length; a++)        {            if(xmlQueues[a].length)            {                job = xmlQueues[a].shift();                break;            }        }        if(!job)            return;        // ___ start one        xmlInProcess = new Object();        xmlInProcess.job = job;        xmlInProcess.http_request = false;        xmlInProcess.start = getMilliseconds();        if (window.XMLHttpRequest)        { // Mozilla, Safari,...            xmlInProcess.http_request = new XMLHttpRequest();            if (xmlInProcess.http_request.overrideMimeType)                xmlInProcess.http_request.overrideMimeType('text/xml');        }        else if (window.ActiveXObject)        { // IE            try            {                xmlInProcess.http_request = new ActiveXObject("Msxml2.XMLHTTP");            }            catch (e)            {                try                {                    xmlInProcess.http_request = new ActiveXObject("Microsoft.XMLHTTP");                }                catch (e)                {                }            }        }        if (!xmlInProcess.http_request)        {            xmlInProcess.job.xmlCallback(null, xmlInProcess.job.callbackObject);            return;        }        xmlInProcess.http_request.onreadystatechange = xmlReadyStateChange;        xmlInProcess.http_request.open(xmlInProcess.job.type, xmlInProcess.job.url, true);        if(xmlInProcess.job.type == 'POST')        {            //Send the proper header information along with the request            xmlInProcess.http_request.setRequestHeader("Content-type""application/x-www-form-urlencoded");            xmlInProcess.http_request.setRequestHeader("Content-length", xmlInProcess.job.params.length);            xmlInProcess.http_request.setRequestHeader("Connection""close");        }        xmlInProcess.http_request.send(xmlInProcess.job.params);    }}function xmlReadyStateChange(){    if (xmlInProcess && xmlInProcess.http_request.readyState == 4)    {        // debug('got one');        xmlInProcess.job.xmlCallback(xmlInProcess.http_request, xmlInProcess.job.callbackObject);        xmlInProcess = null;    }}function getXMLNode(fromNode, tag){    var nodes = fromNode.getElementsByTagName(tag);    return nodes[0];}function getXMLData(fromNode, tag){    var node;    if(typeof(tag) == 'undefined')        node = fromNode;    else        node = getXMLNode(fromNode, tag);    if(node && node.firstChild)        return node.firstChild.data;    return null;}function getXMLAttribute(fromNode, tag){    return fromNode.attributes.getNamedItem(tag).nodeValue;}var drag = new Object();drag.lastMouse = new Point();drag.distance = 0;drag.item = null;var viewport = new Viewport();var drawer = new Drawer();var doc = new Document();var controller = new Controller();var needsUpdate = true;var lastUpdate = 0;var averageFPS = 0;var highFPS = 0;var lowFPS = 1000;var fpsOn = false;var hoverItem = null;var pageMode = 'view';var fadeStrip = true;var nextHoverMode = 'none';var nextHoverOpacity = 1.0;var dirty = false;function start(scene, mode, fade){    if(typeof(fade) == 'undefined')        fade = false;    pageMode = mode;    fadeStrip = fade;    document.body.style.overflow = 'hidden';    document.body.onmousedown = dragStart;    document.body.scroll = 'no';  //      document.body.onkeypress = function(event) { return handleKey(event); };    if (window.addEventListener)        window.addEventListener('DOMMouseScroll', wheel, false);    window.onmousewheel = document.onmousewheel = wheel;    var windowSize = getWindowSize();    var windowCenter = new Clone(windowSize);    windowCenter.divide(2);    viewport.setZoom(3, windowCenter, true);    if(typeof(nextHover) != 'undefined')        nextHoverMode = 'down';    window.setInterval(update, 66);    controller.start(scene);    if(mode == 'view')        controller.goToNext(truefalse);    else        controller.goToHome(true);}function update(){    if(viewport.update() || needsUpdate)        needsUpdate = drawer.update();    updateXML();    controller.update();    if(nextHoverMode == 'up')    {        nextHoverOpacity += 0.1;        if(nextHoverOpacity >= 1.0)        {            nextHoverOpacity = 1.0;            nextHoverMode = 'down';        }        setOpacity(nextHover, nextHoverOpacity);    }    else if(nextHoverMode == 'down')    {        nextHoverOpacity -= 0.1;        if(nextHoverOpacity <= 0)        {            nextHoverOpacity = 0;            nextHoverMode = 'up';        }        setOpacity(nextHover, nextHoverOpacity);    }    if(fpsOn)    {        var now = getMilliseconds();        if(lastUpdate)        {            var diff = now - lastUpdate;            if(diff)            {                var fps = 1000 / diff;                if(averageFPS)                    averageFPS = ((averageFPS * 10) + fps) / 11;                else                    averageFPS = fps;                if(fps > highFPS)                    highFPS = fps;                if(fps < lowFPS)                    lowFPS = fps;                var element = document.getElementById('fps');                element.innerHTML = 'average: ' + parseInt(averageFPS) + '<br>'                        + 'high: ' + parseInt(highFPS) + '<br>'                        + 'low: ' + parseInt(lowFPS) + '<br>'                        + 'count: ' + doc.items.length;            }        }        lastUpdate = now;    }}function stopNextHover(){    if(typeof(nextHover) != 'undefined')    {        nextHoverMode = 'none';        nextHoverOpacity = 0;        setOpacity(nextHover, nextHoverOpacity);    }}function isDirty(){    return dirty;}function wheel(event){    var delta = 0;    if (!event)       event = window.event;    if (event.wheelDelta)    {       delta = event.wheelDelta / 120;       if (window.opera)           delta = -delta;    }    else if (event.detail)        delta = -event.detail;    if(!isIE)        delta /= 3;    if (delta)    {       var p;       if(isFF3)           p = new Point(event.clientX, event.clientY);       else if(isFF)           p = new Point(event.screenX, event.screenY);       else           p = new Point(event.x, event.y);       viewport.zoom(Math.pow(1.2, delta), p);    }}function dragStart(event){    event = getEvent(event);    var img = getEventTarget(event);    var item = null;    if(img && img.mdItem)        item = img.mdItem;    else if(img.id != 'content')        return;    drag.mode = (event.shiftKey ? 'size' : 'move');    drag.lastMouse = getMouse(event);    drag.distance = 0;    drag.item = item;    controller.handleItemMouseDown(item);    if(isIE)    {        document.attachEvent("onmousemove", dragMove);        document.attachEvent("onmouseup", dragStop);        window.event.cancelBubble = true;        window.event.returnValue = false;    }    else    {        document.addEventListener("mousemove", dragMove, true);        document.addEventListener("mouseup", dragStop, true);        event.preventDefault();    }}function dragMove(event){    var p = getMouse(event);    var delta = new Clone(drag.lastMouse);    delta.subtract(p);    drag.distance += Math.abs(delta.x) + Math.abs(delta.y);    if(drag.item && pageMode == 'create')    {        var newDelta = new Clone(delta);        newDelta.divide(new Point(viewport.zoomSpring.current));        if(drag.mode == 'size')        {            if(Math.abs(newDelta.x) > Math.abs(newDelta.y))                newDelta.y = newDelta.x * (drag.item.sizeY.target / drag.item.sizeX.target);            else                newDelta.x = newDelta.y * (drag.item.sizeX.target / drag.item.sizeY.target);            drag.item.sizeX.target -= newDelta.x;            drag.item.sizeY.target -= newDelta.y;            dirty = true;        }        else        {            drag.item.posX.target -= newDelta.x;            drag.item.posY.target -= newDelta.y;            dirty = true;        }    }    else        viewport.panByPixels(delta);    drag.lastMouse = new Clone(p);    if(isIE)    {        window.event.cancelBubble = true;        window.event.returnValue = false;    }    else        event.preventDefault();}function dragStop(event){    if(drag.distance < 5)    {        controller.handleItemClick(drag.item);        controller.auto = false;    }    if(isIE)    {        document.detachEvent("onmousemove", dragMove);        document.detachEvent("onmouseup", dragStop);    }    else    {        document.removeEventListener("mousemove", dragMove, true);        document.removeEventListener("mouseup", dragStop, true);    }}function tileFailedLoad(item){    doc.removeItem(item);}function handleSaveCompletion(success){    if(success)    {        dirty = false;        if(typeof(originalValues) != 'undefined' && typeof(getCreateValues) != 'undefined')        {            var leave = false;            var values = getCreateValues();            if(values.name != originalValues.name)                leave = true;            originalValues = values;            if(leave)                window.location = 'create?name=' + escape(values.name);        }    }}function handleDeleteCompletion(success){    if(success)        window.location = '/';}function albumMouseOver(event){    return;    event = getEvent(event);    var img = getEventTarget(event);    if(img && img.mdItem)    {        if(img.mdItem.parentItem)            hoverItem = img.mdItem.parentItem;        else            hoverItem = img.mdItem;    }    else        hoverItem = null;    needsUpdate = true;}function albumMouseOut(event){    return;    event = getEvent(event);    var img = getEventTarget(event);    if(img && img.mdItem && img.mdItem == hoverItem)        hoverItem = null;}function handleKey(event){    var keycode;    if(window.event)        keycode = window.event.keyCode;    else if(event)        keycode = event.which;    else        return;    if(keycode >= 97 && keycode <= 122) // lowercase        keycode += 65 - 97; // make uppercase    if(keycode == 65) // A    ;    else if(keycode == 81) // Q    {        highFPS = 0;        lowFPS = 1000;        if(fpsOn == false)        {            var element = document.getElementById('fps');            element.style.visibility = '';            fpsOn = true;        }    }    else if(keycode == 83) // S        ;    else if(keycode == 82) // R        ;    else if(keycode == 70) // F        ;    else if(keycode == 72) // H        controller.goToHome();    else if(keycode == 73) // I        ;    else if(keycode == 87) // W        ;    else if(keycode == 88) // X        ;    else if(keycode == 78)// N        ;    else if(keycode == 79) // O        ;    else if(keycode == 46) // .        ;    else if(keycode == 44) // ,        ;    else if(keycode == 91) // [        ;    else if(keycode == 93) // ]        ;    else if(keycode == 13) // Enter        ;    else if(keycode == 32) // spacebar        controller.goToNext();    return false;}function resize(){}function debug(text){    var element = document.getElementById('debug');    element.innerHTML += text + '<br>';}function clearDebug(){    var element = document.getElementById('debug');    element.innerHTML = '';}function debug2(text){    var element = document.getElementById('debug2');    element.innerHTML += text + '<br>';}function clearDebug2(){    var element = document.getElementById('debug2');    element.innerHTML = '';}

Go Back to this app | Get plain source

Powered by AppJet on JGate
source
rendered in 25.691s