]> git.mxchange.org Git - friendica.git/commitdiff
Move js/ folder under view/
authorHypolite Petovan <mrpetovan@gmail.com>
Sun, 28 Jan 2018 21:17:36 +0000 (16:17 -0500)
committerHypolite Petovan <mrpetovan@gmail.com>
Sun, 28 Jan 2018 21:17:36 +0000 (16:17 -0500)
- Move library/cropper to view/js/cropper
- Move library/jquery-textcomplete to view/js/jquery-textcomplete

118 files changed:
js/acl.js [deleted file]
js/ajaxupload.js [deleted file]
js/autocomplete.js [deleted file]
js/country.js [deleted file]
js/filebrowser.js [deleted file]
js/jquery.textinputs.js [deleted file]
js/main.js [deleted file]
js/modernizr.js [deleted file]
library/cropper/cropper.css [deleted file]
library/cropper/cropper.html [deleted file]
library/cropper/cropper.js [deleted file]
library/cropper/cropper.uncompressed.js [deleted file]
library/cropper/lib/builder.js [deleted file]
library/cropper/lib/controls.js [deleted file]
library/cropper/lib/dragdrop.js [deleted file]
library/cropper/lib/effects.js [deleted file]
library/cropper/lib/prototype.js [deleted file]
library/cropper/lib/scriptaculous.js [deleted file]
library/cropper/lib/slider.js [deleted file]
library/cropper/lib/unittest.js [deleted file]
library/cropper/licence.txt [deleted file]
library/cropper/marqueeHoriz.gif [deleted file]
library/cropper/marqueeVert.gif [deleted file]
library/cropper/tests/castle.jpg [deleted file]
library/cropper/tests/castleMed.jpg [deleted file]
library/cropper/tests/example-Basic.htm [deleted file]
library/cropper/tests/example-CSS-Absolute.htm [deleted file]
library/cropper/tests/example-CSS-Float.htm [deleted file]
library/cropper/tests/example-CSS-Relative.htm [deleted file]
library/cropper/tests/example-CoordsOnLoad.htm [deleted file]
library/cropper/tests/example-CoordsOnLoadWithRatio.htm [deleted file]
library/cropper/tests/example-Dimensions.htm [deleted file]
library/cropper/tests/example-DynamicImage.htm [deleted file]
library/cropper/tests/example-FixedRatio.htm [deleted file]
library/cropper/tests/example-MinimumDimensions.htm [deleted file]
library/cropper/tests/example-MinimumWidth.htm [deleted file]
library/cropper/tests/example-Preview.htm [deleted file]
library/cropper/tests/poppy.jpg [deleted file]
library/cropper/tests/staticHTMLStructure.htm [deleted file]
library/jquery-textcomplete/CHANGELOG.md [deleted file]
library/jquery-textcomplete/LICENSE [deleted file]
library/jquery-textcomplete/README.md [deleted file]
library/jquery-textcomplete/jquery.textcomplete.css [deleted file]
library/jquery-textcomplete/jquery.textcomplete.js [deleted file]
library/jquery-textcomplete/jquery.textcomplete.min.js [deleted file]
library/jquery-textcomplete/jquery.textcomplete.min.map [deleted file]
util/minifyjs.sh
view/js/acl.js [new file with mode: 0644]
view/js/ajaxupload.js [new file with mode: 0644]
view/js/autocomplete.js [new file with mode: 0644]
view/js/country.js [new file with mode: 0644]
view/js/cropper/cropper.css [new file with mode: 0644]
view/js/cropper/cropper.html [new file with mode: 0644]
view/js/cropper/cropper.js [new file with mode: 0644]
view/js/cropper/cropper.uncompressed.js [new file with mode: 0644]
view/js/cropper/lib/builder.js [new file with mode: 0644]
view/js/cropper/lib/controls.js [new file with mode: 0644]
view/js/cropper/lib/dragdrop.js [new file with mode: 0644]
view/js/cropper/lib/effects.js [new file with mode: 0644]
view/js/cropper/lib/prototype.js [new file with mode: 0644]
view/js/cropper/lib/scriptaculous.js [new file with mode: 0644]
view/js/cropper/lib/slider.js [new file with mode: 0644]
view/js/cropper/lib/unittest.js [new file with mode: 0644]
view/js/cropper/licence.txt [new file with mode: 0644]
view/js/cropper/marqueeHoriz.gif [new file with mode: 0644]
view/js/cropper/marqueeVert.gif [new file with mode: 0644]
view/js/cropper/tests/castle.jpg [new file with mode: 0644]
view/js/cropper/tests/castleMed.jpg [new file with mode: 0644]
view/js/cropper/tests/example-Basic.htm [new file with mode: 0644]
view/js/cropper/tests/example-CSS-Absolute.htm [new file with mode: 0644]
view/js/cropper/tests/example-CSS-Float.htm [new file with mode: 0644]
view/js/cropper/tests/example-CSS-Relative.htm [new file with mode: 0644]
view/js/cropper/tests/example-CoordsOnLoad.htm [new file with mode: 0644]
view/js/cropper/tests/example-CoordsOnLoadWithRatio.htm [new file with mode: 0644]
view/js/cropper/tests/example-Dimensions.htm [new file with mode: 0644]
view/js/cropper/tests/example-DynamicImage.htm [new file with mode: 0644]
view/js/cropper/tests/example-FixedRatio.htm [new file with mode: 0644]
view/js/cropper/tests/example-MinimumDimensions.htm [new file with mode: 0644]
view/js/cropper/tests/example-MinimumWidth.htm [new file with mode: 0644]
view/js/cropper/tests/example-Preview.htm [new file with mode: 0644]
view/js/cropper/tests/poppy.jpg [new file with mode: 0644]
view/js/cropper/tests/staticHTMLStructure.htm [new file with mode: 0644]
view/js/filebrowser.js [new file with mode: 0644]
view/js/jquery-textcomplete/CHANGELOG.md [new file with mode: 0644]
view/js/jquery-textcomplete/LICENSE [new file with mode: 0644]
view/js/jquery-textcomplete/README.md [new file with mode: 0644]
view/js/jquery-textcomplete/jquery.textcomplete.css [new file with mode: 0644]
view/js/jquery-textcomplete/jquery.textcomplete.js [new file with mode: 0644]
view/js/jquery-textcomplete/jquery.textcomplete.min.js [new file with mode: 0644]
view/js/jquery-textcomplete/jquery.textcomplete.min.map [new file with mode: 0644]
view/js/jquery.textinputs.js [new file with mode: 0644]
view/js/main.js [new file with mode: 0644]
view/js/modernizr.js [new file with mode: 0644]
view/templates/crophead.tpl
view/templates/filebrowser.tpl
view/templates/head.tpl
view/templates/jot-header.tpl
view/templates/msg-header.tpl
view/templates/profed_head.tpl
view/theme/frio/templates/filebrowser.tpl
view/theme/frio/templates/head.tpl
view/theme/frio/templates/jot-header.tpl
view/theme/frio/templates/theme_settings.tpl
view/theme/frost-mobile/templates/cropend.tpl
view/theme/frost-mobile/templates/crophead.tpl
view/theme/frost-mobile/templates/end.tpl
view/theme/frost-mobile/templates/jot-end.tpl
view/theme/frost-mobile/templates/msg-end.tpl
view/theme/frost-mobile/templates/profed_end.tpl
view/theme/frost-mobile/templates/wallmsg-end.tpl
view/theme/frost/templates/cropend.tpl
view/theme/frost/templates/crophead.tpl
view/theme/frost/templates/end.tpl
view/theme/frost/templates/jot-end.tpl
view/theme/frost/templates/msg-end.tpl
view/theme/frost/templates/profed_end.tpl
view/theme/frost/templates/wallmsg-end.tpl
view/theme/smoothly/templates/jot-header.tpl

diff --git a/js/acl.js b/js/acl.js
deleted file mode 100644 (file)
index 257e2c1..0000000
--- a/js/acl.js
+++ /dev/null
@@ -1,359 +0,0 @@
-function ACL(backend_url, preset, automention, is_mobile){
-
-       this.url = backend_url;
-       this.automention = automention;
-       this.is_mobile = is_mobile;
-
-
-       this.kp_timer = null;
-
-       if (preset == undefined) {
-               preset = [];
-       }
-       this.allow_cid = (preset[0] || []);
-       this.allow_gid = (preset[1] || []);
-       this.deny_cid  = (preset[2] || []);
-       this.deny_gid  = (preset[3] || []);
-       this.group_uids = [];
-       this.forumCache = null;
-
-       if (this.is_mobile) {
-               this.nw = 1;
-       } else {
-               this.nw = 4;
-       }
-
-
-       this.list_content = $("#acl-list-content");
-       this.item_tpl = unescape($(".acl-list-item[rel=acl-template]").html());
-       this.showall = $("#acl-showall");
-
-       if (preset.length==0) {
-               this.showall.addClass("selected");
-       }
-
-       /*events*/
-       this.showall.click(this.on_showall.bind(this));
-       $(document).on("click", ".acl-button-show", this.on_button_show.bind(this));
-       $(document).on("click", ".acl-button-hide", this.on_button_hide.bind(this));
-       $("#acl-search").keypress(this.on_search.bind(this));
-       $("#acl-wrapper").parents("form").submit(this.on_submit.bind(this));
-
-       /* add/remove mentions  */
-       this.element = $("#profile-jot-text");
-       this.htmlelm = this.element.get()[0];
-
-       /* startup! */
-       this.get(0,100);
-}
-
-ACL.prototype.remove_mention = function(id) {
-       if (!this.automention) {
-               return;
-       }
-       var nick = this.data[id].nick;
-       var addr = this.data[id].addr;
-
-       if (addr != "") {
-               var searchText = "!" + addr + " ";
-       } else {
-               var searchText = "!" + nick + "+" + id + " ";
-       }
-
-       var start = this.element.val().indexOf(searchText);
-       if (start < 0) {
-               return;
-       }
-       var end = start + searchText.length;
-       this.element.setSelection(start, end).replaceSelectedText('').collapseSelection(false);
-};
-
-ACL.prototype.add_mention = function(id) {
-       if (!this.automention) {
-               return;
-       }
-       var nick = this.data[id].nick;
-       var addr = this.data[id].addr;
-
-       if (addr != "") {
-               var searchText = "!" + addr + " ";
-       } else {
-               var searchText = "!" + nick + "+" + id + " ";
-       }
-
-       if (this.element.val().indexOf( searchText) >= 0 ) {
-               return;
-       }
-       this.element.val(searchText + this.element.val()).trigger('change');
-}
-
-ACL.prototype.on_submit = function(){
-       var aclfields = $("#acl-fields").html("");
-       $(this.allow_gid).each(function(i,v){
-               aclfields.append("<input type='hidden' name='group_allow[]' value='"+v+"'>");
-       });
-       $(this.allow_cid).each(function(i,v){
-               aclfields.append("<input type='hidden' name='contact_allow[]' value='"+v+"'>");
-       });
-       $(this.deny_gid).each(function(i,v){
-               aclfields.append("<input type='hidden' name='group_deny[]' value='"+v+"'>");
-       });
-       $(this.deny_cid).each(function(i,v){
-               aclfields.append("<input type='hidden' name='contact_deny[]' value='"+v+"'>");
-       });
-};
-
-ACL.prototype.search = function(){
-       var srcstr = $("#acl-search").val();
-       this.list_content.html("");
-       this.get(0,100, srcstr);
-};
-
-ACL.prototype.on_search = function(event){
-       if (this.kp_timer) clearTimeout(this.kp_timer);
-       this.kp_timer = setTimeout( this.search.bind(this), 1000);
-};
-
-ACL.prototype.on_showall = function(event){
-       event.preventDefault()
-       event.stopPropagation();
-
-       if (this.showall.hasClass("selected")){
-               return false;
-       }
-       this.showall.addClass("selected");
-
-       this.allow_cid = [];
-       this.allow_gid = [];
-       this.deny_cid  = [];
-       this.deny_gid  = [];
-
-       this.update_view();
-
-       return false;
-};
-
-ACL.prototype.on_button_show = function(event){
-       event.preventDefault()
-       event.stopImmediatePropagation()
-       event.stopPropagation();
-
-       this.set_allow($(event.target).parent().attr('id'));
-
-       return false;
-};
-
-ACL.prototype.on_button_hide = function(event){
-       event.preventDefault()
-       event.stopImmediatePropagation()
-       event.stopPropagation();
-
-       this.set_deny($(event.target).parent().attr('id'));
-
-       return false;
-};
-
-ACL.prototype.set_allow = function(itemid) {
-       type = itemid[0];
-       id   = parseInt(itemid.substr(1));
-
-       switch (type){
-               case "g":
-                       if (this.allow_gid.indexOf(id) < 0) {
-                               this.allow_gid.push(id);
-                       }else {
-                               this.allow_gid.remove(id);
-                       }
-                       if (this.deny_gid.indexOf(id) >= 0) {
-                               this.deny_gid.remove(id);
-                       }
-                       break;
-               case "c":
-                       if (this.allow_cid.indexOf(id) < 0){
-                               this.allow_cid.push(id);
-                               if (this.data[id].forum == "1") {
-                                       // If we have select already a forum,
-                                       // we need to remove the old one (because friendica does
-                                       // allow only one forum as receiver).
-                                       if (this.forumCache !== null && this.forumCache !== id) {
-                                               this.deselectCid(this.forumCache);
-                                       }
-                                       // Update the forum cache.
-                                       this.forumCache = id;
-                                       this.add_mention(id);
-                               }
-                       } else {
-                               this.allow_cid.remove(id);
-                               if (this.data[id].forum == "1") {
-                                       this.remove_mention(id);
-                               }
-                       }
-                       if (this.deny_cid.indexOf(id) >=0 ) {
-                               this.deny_cid.remove(id);
-                       }
-                       break;
-       }
-       this.update_view();
-};
-
-ACL.prototype.set_deny = function(itemid){
-       type = itemid[0];
-       id     = parseInt(itemid.substr(1));
-
-       switch(type){
-               case "g":
-                       if (this.deny_gid.indexOf(id)<0){
-                               this.deny_gid.push(id)
-                       } else {
-                               this.deny_gid.remove(id);
-                       }
-                       if (this.allow_gid.indexOf(id)>=0) this.allow_gid.remove(id);
-                       break;
-               case "c":
-                       if (this.data[id].forum=="1") this.remove_mention(id);
-                       if (this.deny_cid.indexOf(id)<0){
-                               this.deny_cid.push(id)
-                       } else {
-                               this.deny_cid.remove(id);
-                       }
-                       if (this.allow_cid.indexOf(id)>=0) this.allow_cid.remove(id);
-                       break;
-       }
-       this.update_view();
-};
-
-ACL.prototype.is_show_all = function() {
-       return (this.allow_gid.length==0 && this.allow_cid.length==0 &&
-               this.deny_gid.length==0 && this.deny_cid.length==0);
-};
-
-ACL.prototype.update_view = function(){
-       if (this.is_show_all()){
-                       this.showall.addClass("selected");
-                       /* jot acl */
-                               $('#jot-perms-icon').removeClass('lock').addClass('unlock');
-                               $('#jot-public').show();
-                               $('.profile-jot-net input').attr('disabled', false);
-                               if(typeof editor != 'undefined' && editor != false) {
-                                       $('#profile-jot-desc').html(ispublic);
-                               }
-
-       } else {
-                       this.showall.removeClass("selected");
-                       /* jot acl */
-                               $('#jot-perms-icon').removeClass('unlock').addClass('lock');
-                               $('#jot-public').hide();
-                               $('.profile-jot-net input').attr('disabled', 'disabled');
-                               $('#profile-jot-desc').html('&nbsp;');
-       }
-       $("#acl-list-content .acl-list-item").each(function(){
-               $(this).removeClass("groupshow grouphide");
-       });
-
-       $("#acl-list-content .acl-list-item").each(function(index, element){
-               itemid = $(element).attr('id');
-               type = itemid[0];
-               id       = parseInt(itemid.substr(1));
-
-               btshow = $(element).children(".acl-button-show").removeClass("selected");
-               bthide = $(element).children(".acl-button-hide").removeClass("selected");
-
-               switch(type){
-                       case "g":
-                               var uclass = "";
-                               if (this.allow_gid.indexOf(id)>=0){
-                                       btshow.addClass("selected");
-                                       bthide.removeClass("selected");
-                                       uclass="groupshow";
-                               }
-                               if (this.deny_gid.indexOf(id)>=0){
-                                       btshow.removeClass("selected");
-                                       bthide.addClass("selected");
-                                       uclass="grouphide";
-                               }
-
-                               $(this.group_uids[id]).each(function(i,v) {
-                                       if(uclass == "grouphide")
-                                               $("#c"+v).removeClass("groupshow");
-                                       if(uclass != "") {
-                                               var cls = $("#c"+v).attr('class');
-                                               if( cls == undefined)
-                                                       return true;
-                                               var hiding = cls.indexOf('grouphide');
-                                               if(hiding == -1)
-                                                       $("#c"+v).addClass(uclass);
-                                       }
-                               });
-
-                               break;
-                       case "c":
-                               if (this.allow_cid.indexOf(id)>=0){
-                                       btshow.addClass("selected");
-                                       bthide.removeClass("selected");
-                               }
-                               if (this.deny_cid.indexOf(id)>=0){
-                                       btshow.removeClass("selected");
-                                       bthide.addClass("selected");
-                               }
-               }
-
-       }.bind(this));
-
-}
-
-ACL.prototype.get = function(start,count, search){
-       var postdata = {
-               start:start,
-               count:count,
-               search:search,
-       }
-
-       $.ajax({
-               type:'POST',
-               url: this.url,
-               data: postdata,
-               dataType: 'json',
-               success:this.populate.bind(this)
-       });
-};
-
-ACL.prototype.populate = function(data){
-       var height = Math.ceil(data.tot / this.nw) * 42;
-       this.list_content.height(height);
-       this.data = {};
-       $(data.items).each(function(index, item) {
-               if (item.separator != undefined) {
-                       html = "<hr class='clear'>";
-               } else {
-                       html = "<div class='acl-list-item {4} {5} type{2}' title='{6}' id='{2}{3}'>"+this.item_tpl+"</div>";
-                       html = html.format(item.photo, item.name, item.type, item.id, (item.forum=='1'?'forum':''), item.network, item.link);
-                       if (item.uids != undefined) {
-                               this.group_uids[item.id] = item.uids;
-                       }
-               }
-               this.list_content.append(html);
-               this.data[item.id] = item;
-       }.bind(this));
-       $(".acl-list-item img[data-src]", this.list_content).each(function(i, el){
-               // Add src attribute for images with a data-src attribute
-               $(el).attr('src', $(el).data("src"));
-       });
-
-       this.update_view();
-};
-
-/**
- * @brief Deselect previous selected contact.
- * 
- * @param {int} id The contact ID.
- * @returns {void}
- */
-ACL.prototype.deselectCid = function(id) {
-       if (this.allow_cid.indexOf(id) >= 0) {
-               this.allow_cid.remove(id);
-       }
-       if (this.deny_cid.indexOf(id) >=0 ) {
-               this.deny_cid.remove(id);
-       }
-       this.remove_mention(id);
-};
diff --git a/js/ajaxupload.js b/js/ajaxupload.js
deleted file mode 100644 (file)
index 1c34b11..0000000
+++ /dev/null
@@ -1,703 +0,0 @@
-/**
- * AJAX Upload ( http://valums.com/ajax-upload/ ) 
- * Copyright (c) Andris Valums
- * Licensed under the MIT license ( http://valums.com/mit-license/ )
- * Thanks to Gary Haran, David Mark, Corey Burns and others for contributions. 
- */
-
-(function () {
-    /* global window */
-    /* jslint browser: true, devel: true, undef: true, nomen: true, bitwise: true, regexp: true, newcap: true, immed: true */
-    
-    /**
-     * Wrapper for FireBug's console.log
-     */
-    function log(){
-        if (typeof(console) != 'undefined' && typeof(console.log) == 'function'){            
-            Array.prototype.unshift.call(arguments, '[Ajax Upload]');
-            console.log( Array.prototype.join.call(arguments, ' '));
-        }
-    } 
-
-    /**
-     * Attaches event to a dom element.
-     * @param {Element} el
-     * @param type event name
-     * @param fn callback This refers to the passed element
-     */
-    function addEvent(el, type, fn){
-        if (el.addEventListener) {
-            el.addEventListener(type, fn, false);
-        } else if (el.attachEvent) {
-            el.attachEvent('on' + type, function(){
-                fn.call(el);
-               });
-           } else {
-            throw new Error('not supported or DOM not loaded');
-        }
-    }   
-    
-    /**
-     * Attaches resize event to a window, limiting
-     * number of event fired. Fires only when encounteres
-     * delay of 100 after series of events.
-     * 
-     * Some browsers fire event multiple times when resizing
-     * http://www.quirksmode.org/dom/events/resize.html
-     * 
-     * @param fn callback This refers to the passed element
-     */
-    function addResizeEvent(fn){
-        var timeout;
-               
-           addEvent(window, 'resize', function(){
-            if (timeout){
-                clearTimeout(timeout);
-            }
-            timeout = setTimeout(fn, 100);                        
-        });
-    }    
-
-    // Get offset adding all offsets, slow fall-back method
-    var getOffsetSlow = function(el){
-        var top = 0, left = 0;
-        do {
-            top += el.offsetTop || 0;
-            left += el.offsetLeft || 0;
-            el = el.offsetParent;
-        } while (el);
-        
-        return {
-            left: left,
-            top: top
-        };
-    };
-
-    
-    // Needs more testing, will be rewriten for next version        
-    // getOffset function copied from jQuery lib (http://jquery.com/)
-    if (document.documentElement.getBoundingClientRect){
-        // Get Offset using getBoundingClientRect
-        // http://ejohn.org/blog/getboundingclientrect-is-awesome/
-        var getOffset = function(el){
-            var box = el.getBoundingClientRect();
-            var doc = el.ownerDocument;
-            var body = doc.body;
-            var docElem = doc.documentElement; // for ie 
-            var clientTop = docElem.clientTop || body.clientTop || 0;
-            var clientLeft = docElem.clientLeft || body.clientLeft || 0;
-             
-            // In Internet Explorer 7 getBoundingClientRect property is treated as physical,
-            // while others are logical. Make all logical, like in IE8.        
-            var zoom = 1;            
-            if (body.getBoundingClientRect) {
-                var bound = body.getBoundingClientRect();
-                zoom = (bound.right - bound.left) / body.clientWidth;
-            }
-
-            // some CSS layouts gives 0 width and/or bounding boxes
-            // in this case we fall back to the slow method
-            if (zoom == 0 || body.clientWidth == 0)
-                return getOffsetSlow(el);
-            
-            if (zoom > 1) {
-                clientTop = 0;
-                clientLeft = 0;
-            }
-            
-            var top = box.top / zoom + (window.pageYOffset || docElem && docElem.scrollTop / zoom || body.scrollTop / zoom) - clientTop, left = box.left / zoom + (window.pageXOffset || docElem && docElem.scrollLeft / zoom || body.scrollLeft / zoom) - clientLeft;
-            
-            return {
-                top: top,
-                left: left
-            };
-        };        
-    } else {
-      var getOffset = getOffsetSlow;
-    }
-    
-    /**
-     * Returns left, top, right and bottom properties describing the border-box,
-     * in pixels, with the top-left relative to the body
-     * @param {Element} el
-     * @return {Object} Contains left, top, right,bottom
-     */
-    function getBox(el){
-        var left, right, top, bottom;
-        var offset = getOffset(el);
-        left = offset.left;
-        top = offset.top;
-        
-        right = left + el.offsetWidth;
-        bottom = top + el.offsetHeight;
-        
-        return {
-            left: left,
-            right: right,
-            top: top,
-            bottom: bottom
-        };
-    }
-    
-    /**
-     * Helper that takes object literal
-     * and add all properties to element.style
-     * @param {Element} el
-     * @param {Object} styles
-     */
-    function addStyles(el, styles){
-        for (var name in styles) {
-            if (styles.hasOwnProperty(name)) {
-                el.style[name] = styles[name];
-            }
-        }
-    }
-        
-    /**
-     * Function places an absolutely positioned
-     * element on top of the specified element
-     * copying position and dimentions.
-     * @param {Element} from
-     * @param {Element} to
-     */    
-    function copyLayout(from, to){
-           var box = getBox(from);
-        
-        addStyles(to, {
-               position: 'absolute',                    
-               left : box.left + 'px',
-               top : box.top + 'px',
-               width : from.offsetWidth + 'px',
-               height : from.offsetHeight + 'px'
-           });        
-       to.title = from.title;
-
-    }
-
-    /**
-    * Creates and returns element from html chunk
-    * Uses innerHTML to create an element
-    */
-    var toElement = (function(){
-        var div = document.createElement('div');
-        return function(html){
-            div.innerHTML = html;
-            var el = div.firstChild;
-            return div.removeChild(el);
-        };
-    })();
-            
-    /**
-     * Function generates unique id
-     * @return unique id 
-     */
-    var getUID = (function(){
-        var id = 0;
-        return function(){
-            return 'ValumsAjaxUpload' + id++;
-        };
-    })();        
-    /**
-     * Get file name from path
-     * @param {String} file path to file
-     * @return filename
-     */  
-    function fileFromPath(file){
-        return file.replace(/.*(\/|\\)/, "");
-    }
-    
-    /**
-     * Get file extension lowercase
-     * @param {String} file name
-     * @return file extenstion
-     */    
-    function getExt(file){
-        return (-1 !== file.indexOf('.')) ? file.replace(/.*[.]/, '') : '';
-    }
-
-    function hasClass(el, name){        
-        var re = new RegExp('\\b' + name + '\\b');        
-        return re.test(el.className);
-    }    
-    function addClass(el, name){
-        if ( ! hasClass(el, name)){   
-            el.className += ' ' + name;
-        }
-    }    
-    function removeClass(el, name){
-        var re = new RegExp('\\b' + name + '\\b');                
-        el.className = el.className.replace(re, '');        
-    }
-    
-    function removeNode(el){
-        el.parentNode.removeChild(el);
-    }
-
-    /**
-     * Easy styling and uploading
-     * @constructor
-     * @param button An element you want convert to 
-     * upload button. Tested dimentions up to 500x500px
-     * @param {Object} options See defaults below.
-     */
-    window.AjaxUpload = function(button, options){
-        this._settings = {
-            // Location of the server-side upload script
-            action: 'upload.php',
-            // File upload name
-            name: 'userfile',
-            // Additional data to send
-            data: {},
-            // Submit file as soon as it's selected
-            autoSubmit: true,
-            // The type of data that you're expecting back from the server.
-            // html and xml are detected automatically.
-            // Only useful when you are using json data as a response.
-            // Set to "json" in that case. 
-            responseType: false,
-            // Class applied to button when mouse is hovered
-            hoverClass: 'hover',
-            // Class applied to button when button is focused
-            focusClass: 'focus',
-            // Class applied to button when AU is disabled
-            disabledClass: 'disabled',            
-            // When user selects a file, useful with autoSubmit disabled
-            // You can return false to cancel upload                   
-            onChange: function(file, extension){
-            },
-            // Callback to fire before file is uploaded
-            // You can return false to cancel upload
-            onSubmit: function(file, extension){
-            },
-            // Fired when file upload is completed
-            // WARNING! DO NOT USE "FALSE" STRING AS A RESPONSE!
-            onComplete: function(file, response){
-            }
-        };
-                        
-        // Merge the users options with our defaults
-        for (var i in options) {
-            if (options.hasOwnProperty(i)){
-                this._settings[i] = options[i];
-            }
-        }
-                
-        // button isn't necessary a dom element
-        if (button.jquery){
-            // jQuery object was passed
-            button = button[0];
-        } else if (typeof button == "string") {
-            if (/^#.*/.test(button)){
-                // If jQuery user passes #elementId don't break it                                     
-                button = button.slice(1);                
-            }
-            
-            button = document.getElementById(button);
-        }
-        
-        if ( ! button || button.nodeType !== 1){
-            throw new Error("Please make sure that you're passing a valid element"); 
-        }
-                
-        if ( button.nodeName.toUpperCase() == 'A'){
-            // disable link                       
-            addEvent(button, 'click', function(e){
-                if (e && e.preventDefault){
-                    e.preventDefault();
-                } else if (window.event){
-                    window.event.returnValue = false;
-                }
-            });
-        }
-                    
-        // DOM element
-        this._button = button;        
-        // DOM element                 
-        this._input = null;
-        // If disabled clicking on button won't do anything
-        this._disabled = false;
-        
-        // if the button was disabled before refresh if will remain
-        // disabled in FireFox, let's fix it
-        this.enable();        
-        
-        this._rerouteClicks();
-    };
-    
-    // assigning methods to our class
-    AjaxUpload.prototype = {
-        setData: function(data){
-            this._settings.data = data;
-        },
-        disable: function(){            
-            addClass(this._button, this._settings.disabledClass);
-            this._disabled = true;
-            
-            var nodeName = this._button.nodeName.toUpperCase();            
-            if (nodeName == 'INPUT' || nodeName == 'BUTTON'){
-                this._button.setAttribute('disabled', 'disabled');
-            }            
-            
-            // hide input
-            if (this._input){
-                // We use visibility instead of display to fix problem with Safari 4
-                // The problem is that the value of input doesn't change if it 
-                // has display none when user selects a file           
-                this._input.parentNode.style.visibility = 'hidden';
-            }
-        },
-        enable: function(){
-            removeClass(this._button, this._settings.disabledClass);
-            this._button.removeAttribute('disabled');
-            this._disabled = false;
-            
-        },
-        /**
-         * Creates invisible file input 
-         * that will hover above the button
-         * <div><input type='file' /></div>
-         */
-        _createInput: function(){ 
-            var self = this;
-                        
-            var input = document.createElement("input");
-            input.setAttribute('type', 'file');
-            input.setAttribute('name', this._settings.name);
-
-            addStyles(input, {
-                'position' : 'absolute',
-                // in Opera only 'browse' button
-                // is clickable and it is located at
-                // the right side of the input
-                'right' : 0,
-                'margin' : 0,
-                'padding' : 0,
-                'fontSize' : '480px',
-                // in Firefox if font-family is set to
-                // 'inherit' the input doesn't work
-                'fontFamily' : 'sans-serif',
-                'cursor' : 'pointer'
-            });            
-
-            var div = document.createElement("div");                        
-            addStyles(div, {
-                'display' : 'block',
-                'position' : 'absolute',
-                'overflow' : 'hidden',
-                'margin' : 0,
-                'padding' : 0,                
-                'opacity' : 0,
-                // Make sure browse button is in the right side
-                // in Internet Explorer
-                'direction' : 'ltr',
-                //Max zIndex supported by Opera 9.0-9.2
-                'zIndex': 2147483583,
-                               'cursor' : 'pointer'
-
-            });
-            
-            // Make sure that element opacity exists.
-            // Otherwise use IE filter            
-            if ( div.style.opacity !== "0") {
-                if (typeof(div.filters) == 'undefined'){
-                    throw new Error('Opacity not supported by the browser');
-                }
-                div.style.filter = "alpha(opacity=0)";
-            }            
-            
-            addEvent(input, 'change', function(){
-                 
-                if ( ! input || input.value === ''){                
-                    return;                
-                }
-                            
-                // Get filename from input, required                
-                // as some browsers have path instead of it          
-                var file = fileFromPath(input.value);
-                                
-                if (false === self._settings.onChange.call(self, file, getExt(file))){
-                    self._clearInput();                
-                    return;
-                }
-                
-                // Submit form when value is changed
-                if (self._settings.autoSubmit) {
-                    self.submit();
-                }
-            });            
-
-            addEvent(input, 'mouseover', function(){
-                addClass(self._button, self._settings.hoverClass);
-            });
-            
-            addEvent(input, 'mouseout', function(){
-                removeClass(self._button, self._settings.hoverClass);
-                removeClass(self._button, self._settings.focusClass);
-                
-                // We use visibility instead of display to fix problem with Safari 4
-                // The problem is that the value of input doesn't change if it 
-                // has display none when user selects a file           
-                input.parentNode.style.visibility = 'hidden';
-
-            });   
-                        
-            addEvent(input, 'focus', function(){
-                addClass(self._button, self._settings.focusClass);
-            });
-            
-            addEvent(input, 'blur', function(){
-                removeClass(self._button, self._settings.focusClass);
-            });
-            
-               div.appendChild(input);
-            document.body.appendChild(div);
-              
-            this._input = input;
-        },
-        _clearInput : function(){
-            if (!this._input){
-                return;
-            }            
-                             
-            // this._input.value = ''; Doesn't work in IE6                               
-            removeNode(this._input.parentNode);
-            this._input = null;                                                                   
-            this._createInput();
-            
-            removeClass(this._button, this._settings.hoverClass);
-            removeClass(this._button, this._settings.focusClass);
-        },
-        /**
-         * Function makes sure that when user clicks upload button,
-         * the this._input is clicked instead
-         */
-        _rerouteClicks: function(){
-            var self = this;
-            
-            // IE will later display 'access denied' error
-            // if you use using self._input.click()
-            // other browsers just ignore click()
-
-            addEvent(self._button, 'mouseover', function(){
-                if (self._disabled){
-                    return;
-                }
-                                
-                if ( ! self._input){
-                       self._createInput();
-                }
-                
-                var div = self._input.parentNode;                            
-                copyLayout(self._button, div);
-                div.style.visibility = 'visible';
-                                
-            });
-            
-            
-            // commented because we now hide input on mouseleave
-            /**
-             * When the window is resized the elements 
-             * can be misaligned if button position depends
-             * on window size
-             */
-            //addResizeEvent(function(){
-            //    if (self._input){
-            //        copyLayout(self._button, self._input.parentNode);
-            //    }
-            //});            
-                                         
-        },
-        /**
-         * Creates iframe with unique name
-         * @return {Element} iframe
-         */
-        _createIframe: function(){
-            // We can't use getTime, because it sometimes return
-            // same value in safari :(
-            var id = getUID();            
-             
-            // We can't use following code as the name attribute
-            // won't be properly registered in IE6, and new window
-            // on form submit will open
-            // var iframe = document.createElement('iframe');
-            // iframe.setAttribute('name', id);                        
-            var iframe = toElement('<iframe src="javascript:false;" name="' + id + '" />');
-            // src="javascript:false; was added
-            // because it possibly removes ie6 prompt 
-            // "This page contains both secure and nonsecure items"
-            // Anyway, it doesn't do any harm.            
-            iframe.setAttribute('id', id);
-            
-            iframe.style.display = 'none';
-            document.body.appendChild(iframe);
-            
-            return iframe;
-        },
-        /**
-         * Creates form, that will be submitted to iframe
-         * @param {Element} iframe Where to submit
-         * @return {Element} form
-         */
-        _createForm: function(iframe){
-            var settings = this._settings;
-                        
-            // We can't use the following code in IE6
-            // var form = document.createElement('form');
-            // form.setAttribute('method', 'post');
-            // form.setAttribute('enctype', 'multipart/form-data');
-            // Because in this case file won't be attached to request                    
-            var form = toElement('<form method="post" enctype="multipart/form-data"></form>');
-                        
-            form.setAttribute('action', settings.action);
-            form.setAttribute('target', iframe.name);                                   
-            form.style.display = 'none';
-            document.body.appendChild(form);
-            
-            // Create hidden input element for each data key
-            for (var prop in settings.data) {
-                if (settings.data.hasOwnProperty(prop)){
-                    var el = document.createElement("input");
-                    el.setAttribute('type', 'hidden');
-                    el.setAttribute('name', prop);
-                    el.setAttribute('value', settings.data[prop]);
-                    form.appendChild(el);
-                }
-            }
-            return form;
-        },
-        /**
-         * Gets response from iframe and fires onComplete event when ready
-         * @param iframe
-         * @param file Filename to use in onComplete callback 
-         */
-        _getResponse : function(iframe, file){            
-            // getting response
-            var toDeleteFlag = false, self = this, settings = this._settings;   
-               
-            addEvent(iframe, 'load', function(){                
-                
-                if (// For Safari 
-                    iframe.src == "javascript:'%3Chtml%3E%3C/html%3E';" ||
-                    // For FF, IE
-                    iframe.src == "javascript:'<html></html>';"){                                                                        
-                        // First time around, do not delete.
-                        // We reload to blank page, so that reloading main page
-                        // does not re-submit the post.
-                        
-                        if (toDeleteFlag) {
-                            // Fix busy state in FF3
-                            setTimeout(function(){
-                                removeNode(iframe);
-                            }, 0);
-                        }
-                                                
-                        return;
-                }
-                
-                var doc = iframe.contentDocument ? iframe.contentDocument : window.frames[iframe.id].document;
-                
-                // fixing Opera 9.26,10.00
-                if (doc.readyState && doc.readyState != 'complete') {
-                   // Opera fires load event multiple times
-                   // Even when the DOM is not ready yet
-                   // this fix should not affect other browsers
-                   return;
-                }
-                
-                // fixing Opera 9.64
-                if (doc.body && doc.body.innerHTML == "false") {
-                    // In Opera 9.64 event was fired second time
-                    // when body.innerHTML changed from false 
-                    // to server response approx. after 1 sec
-                    return;
-                }
-                
-                var response;
-                
-                if (doc.XMLDocument) {
-                    // response is a xml document Internet Explorer property
-                    response = doc.XMLDocument;
-                } else if (doc.body){
-                    // response is html document or plain text
-                    response = doc.body.innerHTML;
-                    
-                    if (settings.responseType && settings.responseType.toLowerCase() == 'json') {
-                        // If the document was sent as 'application/javascript' or
-                        // 'text/javascript', then the browser wraps the text in a <pre>
-                        // tag and performs html encoding on the contents.  In this case,
-                        // we need to pull the original text content from the text node's
-                        // nodeValue property to retrieve the unmangled content.
-                        // Note that IE6 only understands text/html
-                        if (doc.body.firstChild && doc.body.firstChild.nodeName.toUpperCase() == 'PRE') {
-                            doc.normalize();
-                            response = doc.body.firstChild.firstChild.nodeValue;
-                        }
-                        
-                        if (response) {
-                            response = eval("(" + response + ")");
-                        } else {
-                            response = {};
-                        }
-                    }
-                } else {
-                    // response is a xml document
-                    response = doc;
-                }
-                
-                settings.onComplete.call(self, file, response);
-                
-                // Reload blank page, so that reloading main page
-                // does not re-submit the post. Also, remember to
-                // delete the frame
-                toDeleteFlag = true;
-                
-                // Fix IE mixed content issue
-                iframe.src = "javascript:'<html></html>';";
-            });            
-        },        
-        /**
-         * Upload file contained in this._input
-         */
-        submit: function(){                        
-            var self = this, settings = this._settings;
-            
-            if ( ! this._input || this._input.value === ''){                
-                return;                
-            }
-                                    
-            var file = fileFromPath(this._input.value);
-            
-            // user returned false to cancel upload
-            if (false === settings.onSubmit.call(this, file, getExt(file))){
-                this._clearInput();                
-                return;
-            }
-            
-            // sending request    
-            var iframe = this._createIframe();
-            var form = this._createForm(iframe);
-            
-            // assuming following structure
-            // div -> input type='file'
-            removeNode(this._input.parentNode);            
-            removeClass(self._button, self._settings.hoverClass);
-            removeClass(self._button, self._settings.focusClass);
-                        
-            form.appendChild(this._input);
-                        
-            form.submit();
-
-            // request set, clean up                
-            removeNode(form); form = null;                          
-            removeNode(this._input); this._input = null;            
-            
-            // Get response from iframe and fire onComplete event when ready
-            this._getResponse(iframe, file);            
-
-            // get ready for next request            
-            this._createInput();
-        }
-    };
-})(); 
diff --git a/js/autocomplete.js b/js/autocomplete.js
deleted file mode 100644 (file)
index 219ad79..0000000
+++ /dev/null
@@ -1,490 +0,0 @@
-/**
- * @brief Friendica people autocomplete
- *
- * require jQuery, jquery.textcomplete
- *
- * for further documentation look at:
- * http://yuku-t.com/jquery-textcomplete/
- *
- * https://github.com/yuku-t/jquery-textcomplete/blob/master/doc/how_to_use.md
- */
-
-
-function contact_search(term, callback, backend_url, type, mode) {
-
-       // Check if there is a conversation id to include the unkonwn contacts of the conversation
-       var conv_id = document.activeElement.id.match(/\d+$/);
-
-       // Check if there is a cached result that contains the same information we would get with a full server-side search
-       var bt = backend_url+type;
-       if(!(bt in contact_search.cache)) contact_search.cache[bt] = {};
-
-       var lterm = term.toLowerCase(); // Ignore case
-       for(var t in contact_search.cache[bt]) {
-               if(lterm.indexOf(t) >= 0) { // A more broad search has been performed already, so use those results
-                       // Filter old results locally
-                       var matching = contact_search.cache[bt][t].filter(function (x) { return (x.name.toLowerCase().indexOf(lterm) >= 0 || (typeof x.nick !== 'undefined' && x.nick.toLowerCase().indexOf(lterm) >= 0)); }); // Need to check that nick exists because groups don't have one
-                       matching.unshift({forum:false, text: term, replace: term});
-                       setTimeout(function() { callback(matching); } , 1); // Use "pseudo-thread" to avoid some problems
-                       return;
-               }
-       }
-
-       var postdata = {
-               start:0,
-               count:100,
-               search:term,
-               type:type,
-       };
-
-       if(conv_id !== null)
-               postdata['conversation'] = conv_id[0];
-
-       if(mode !== null)
-               postdata['smode'] = mode;
-
-
-       $.ajax({
-               type:'POST',
-               url: backend_url,
-               data: postdata,
-               dataType: 'json',
-               success: function(data){
-                       // Cache results if we got them all (more information would not improve results)
-                       // data.count represents the maximum number of items
-                       if(data.items.length -1 < data.count) {
-                               contact_search.cache[bt][lterm] = data.items;
-                       }
-                       var items = data.items.slice(0);
-                       items.unshift({taggable:false, text: term, replace: term});
-                       callback(items);
-               },
-       }).fail(function () {callback([]); }); // Callback must be invoked even if something went wrong.
-}
-contact_search.cache = {};
-
-
-function contact_format(item) {
-       // Show contact information if not explicitly told to show something else
-       if(typeof item.text === 'undefined') {
-               var desc = ((item.label) ? item.nick + ' ' + item.label : item.nick);
-               var forum = ((item.forum) ? 'forum' : '');
-               if(typeof desc === 'undefined') desc = '';
-               if(desc) desc = ' ('+desc+')';
-               return "<div class='{0}' title='{4}'><img class='acpopup-img' src='{1}'><span class='acpopup-contactname'>{2}</span><span class='acpopup-sub-text'>{3}</span><div class='clear'></div></div>".format(forum, item.photo, item.name, desc, item.link);
-       }
-       else
-               return "<div>" + item.text + "</div>";
-}
-
-function editor_replace(item) {
-       if (typeof item.replace !== 'undefined') {
-               return '$1$2' + item.replace;
-       }
-
-       if (typeof item.addr !== 'undefined') {
-               return '$1$2' + item.addr + ' ';
-       }
-
-       // $2 ensures that prefix (@,@!) is preserved
-       var id = item.id;
-
-       // don't add the id if it is empty (the id empty eg. if there are unknow contacts in thread)
-       if (id.length < 1) {
-               return '$1$2' + item.nick.replace(' ', '') + ' ';
-       }
-       // 16 chars of hash should be enough. Full hash could be used if it can be done in a visually appealing way.
-       // 16 chars is also the minimum length in the backend (otherwise it's interpreted as a local id).
-       if (id.length > 16) {
-               id = item.id.substring(0,16);
-       }
-       return '$1$2' + item.nick.replace(' ', '') + '+' + id + ' ';
-}
-
-function basic_replace(item) {
-       if(typeof item.replace !== 'undefined')
-               return '$1'+item.replace;
-
-       return '$1'+item.name+' ';
-}
-
-function webbie_replace(item) {
-       if(typeof item.replace !== 'undefined')
-               return '$1'+item.replace;
-
-       return '$1'+item.nick+' ';
-}
-
-function trim_replace(item) {
-       if(typeof item.replace !== 'undefined')
-               return '$1'+item.replace;
-
-       return '$1'+item.name;
-}
-
-
-function submit_form(e) {
-       $(e).parents('form').submit();
-}
-
-function getWord(text, caretPos) {
-       var index = text.indexOf(caretPos);
-       var postText = text.substring(caretPos, caretPos+8);
-       if ((postText.indexOf("[/list]") > 0) || postText.indexOf("[/ul]") > 0 || postText.indexOf("[/ol]") > 0) {
-               return postText;
-       }
-}
-
-function getCaretPosition(ctrl) {
-       var CaretPos = 0;   // IE Support
-       if (document.selection) {
-               ctrl.focus();
-               var Sel = document.selection.createRange();
-               Sel.moveStart('character', -ctrl.value.length);
-               CaretPos = Sel.text.length;
-       }
-       // Firefox support
-       else if (ctrl.selectionStart || ctrl.selectionStart == '0')
-               CaretPos = ctrl.selectionStart;
-       return (CaretPos);
-}
-
-function setCaretPosition(ctrl, pos){
-       if(ctrl.setSelectionRange) {
-               ctrl.focus();
-               ctrl.setSelectionRange(pos,pos);
-       }
-       else if (ctrl.createTextRange) {
-               var range = ctrl.createTextRange();
-               range.collapse(true);
-               range.moveEnd('character', pos);
-               range.moveStart('character', pos);
-               range.select();
-       }
-}
-
-function listNewLineAutocomplete(id) {
-       var text = document.getElementById(id);
-       var caretPos = getCaretPosition(text)
-       var word = getWord(text.value, caretPos);
-       if (word != null) {
-               var textBefore = text.value.substring(0, caretPos);
-               var textAfter  = text.value.substring(caretPos, text.length);
-               $('#' + id).val(textBefore + '\r\n[*] ' + textAfter).trigger('change');
-               setCaretPosition(text, caretPos + 5);
-               return true;
-       }
-       else {
-               return false;
-       }
-}
-
-function string2bb(element) {
-       if(element == 'bold') return 'b';
-       else if(element == 'italic') return 'i';
-       else if(element == 'underline') return 'u';
-       else if(element == 'overline') return 'o';
-       else if(element == 'strike') return 's';
-       else return element;
-}
-
-/**
- * jQuery plugin 'editor_autocomplete'
- */
-(function( $ ) {
-       $.fn.editor_autocomplete = function(backend_url) {
-
-               // Autocomplete contacts
-               contacts = {
-                       match: /(^|\s)(@\!*)([^ \n]+)$/,
-                       index: 3,
-                       search: function(term, callback) { contact_search(term, callback, backend_url, 'c'); },
-                       replace: editor_replace,
-                       template: contact_format,
-               };
-
-               // Autocomplete forums
-               forums = {
-                       match: /(^|\s)(!\!*)([^ \n]+)$/,
-                       index: 3,
-                       search: function(term, callback) { contact_search(term, callback, backend_url, 'f'); },
-                       replace: editor_replace,
-                       template: contact_format,
-               };
-
-               // Autocomplete smilies e.g. ":like"
-               smilies = {
-                       match: /(^|\s)(:[a-z]{2,})$/,
-                       index: 2,
-                       search: function(term, callback) { $.getJSON('smilies/json').done(function(data) { callback($.map(data, function(entry) { return entry.text.indexOf(term) === 0 ? entry : null; })); }); },
-                       template: function(item) { return item.icon + ' ' + item.text; },
-                       replace: function(item) { return "$1" + item.text + ' '; },
-               };
-
-               this.attr('autocomplete','off');
-               this.textcomplete([contacts, forums, smilies], {className:'acpopup', zIndex:10000});
-       };
-})( jQuery );
-
-/**
- * jQuery plugin 'search_autocomplete'
- */
-(function( $ ) {
-       $.fn.search_autocomplete = function(backend_url) {
-               // Autocomplete contacts
-               contacts = {
-                       match: /(^@)([^\n]{2,})$/,
-                       index: 2,
-                       search: function(term, callback) { contact_search(term, callback, backend_url, 'x', 'contact'); },
-                       replace: webbie_replace,
-                       template: contact_format,
-               };
-
-               // Autocomplete forum accounts
-               community = {
-                       match: /(^!)([^\n]{2,})$/,
-                       index: 2,
-                       search: function(term, callback) { contact_search(term, callback, backend_url, 'x', 'community'); },
-                       replace: webbie_replace,
-                       template: contact_format,
-               };
-               this.attr('autocomplete', 'off');
-               var a = this.textcomplete([contacts, community], {className:'acpopup', maxCount:100, zIndex: 10000, appendTo:'nav'});
-               a.on('textComplete:select', function(e, value, strategy) { submit_form(this); });
-       };
-})( jQuery );
-
-(function( $ ) {
-       $.fn.contact_autocomplete = function(backend_url, typ, autosubmit, onselect) {
-               if(typeof typ === 'undefined') typ = '';
-               if(typeof autosubmit === 'undefined') autosubmit = false;
-
-               // Autocomplete contacts
-               contacts = {
-                       match: /(^)([^\n]+)$/,
-                       index: 2,
-                       search: function(term, callback) { contact_search(term, callback, backend_url, typ); },
-                       replace: basic_replace,
-                       template: contact_format,
-               };
-
-               this.attr('autocomplete','off');
-               var a = this.textcomplete([contacts], {className:'acpopup', zIndex:10000});
-
-               if(autosubmit)
-                       a.on('textComplete:select', function(e,value,strategy) { submit_form(this); });
-
-               if(typeof onselect !== 'undefined')
-                       a.on('textComplete:select', function(e, value, strategy) { onselect(value); });
-       };
-})( jQuery );
-
-
-(function( $ ) {
-       $.fn.name_autocomplete = function(backend_url, typ, autosubmit, onselect) {
-               if(typeof typ === 'undefined') typ = '';
-               if(typeof autosubmit === 'undefined') autosubmit = false;
-
-               // Autocomplete contacts
-               names = {
-                       match: /(^)([^\n]+)$/,
-                       index: 2,
-                       search: function(term, callback) { contact_search(term, callback, backend_url, typ); },
-                       replace: trim_replace,
-                       template: contact_format,
-               };
-
-               this.attr('autocomplete','off');
-               var a = this.textcomplete([names], {className:'acpopup', zIndex:10000});
-
-               if(autosubmit)
-                       a.on('textComplete:select', function(e,value,strategy) { submit_form(this); });
-
-               if(typeof onselect !== 'undefined')
-                       a.on('textComplete:select', function(e, value, strategy) { onselect(value); });
-       };
-})( jQuery );
-
-(function( $ ) {
-       $.fn.bbco_autocomplete = function(type) {
-
-               if(type=='bbcode') {
-                       var open_close_elements = ['bold', 'italic', 'underline', 'overline', 'strike', 'quote', 'code', 'spoiler', 'map', 'img', 'url', 'audio', 'video', 'embed', 'youtube', 'vimeo', 'list', 'ul', 'ol', 'li', 'table', 'tr', 'th', 'td', 'center', 'color', 'font', 'size', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'nobb', 'noparse', 'pre', 'abstract'];
-                       var open_elements = ['*', 'hr'];
-
-                       var elements = open_close_elements.concat(open_elements);
-               }
-
-               bbco = {
-                       match: /\[(\w*\**)$/,
-                       search: function (term, callback) {
-                               callback($.map(elements, function (element) {
-                                       return element.indexOf(term) === 0 ? element : null;
-                               }));
-                       },
-                       index: 1,
-                       replace: function (element) {
-                               element = string2bb(element);
-                               if(open_elements.indexOf(element) < 0) {
-                                       if(element === 'list' || element === 'ol' || element === 'ul') {
-                                               return ['\[' + element + '\]' + '\n\[*\] ', '\n\[/' + element + '\]'];
-                                       }
-                                       else if(element === 'table') {
-                                               return ['\[' + element + '\]' + '\n\[tr\]', '\[/tr\]\n\[/' + element + '\]'];
-                                       }
-                                       else {
-                                               return ['\[' + element + '\]', '\[/' + element + '\]'];
-                                       }
-                               }
-                               else {
-                                       return '\[' + element + '\] ';
-                               }
-                       }
-               };
-
-               this.attr('autocomplete','off');
-               var a = this.textcomplete([bbco], {className:'acpopup', zIndex:10000});
-
-               a.on('textComplete:select', function(e, value, strategy) { value; });
-
-               a.keypress(function(e){
-                       if (e.keyCode == 13) {
-                               var x = listNewLineAutocomplete(this.id);
-                               if(x) {
-                                       e.stopImmediatePropagation();
-                                       e.preventDefault();
-                               }
-                       }
-               });
-       };
-})( jQuery );
-
-/**
- * Friendica people autocomplete legacy code
- *
- * require jQuery, jquery.textareas
- */
-function ACPopup(elm, backend_url){
-       this.idsel = -1;
-       this.element = elm;
-       this.searchText = '';
-       this.ready = true;
-       this.kp_timer = false;
-       this.url = backend_url;
-
-       this.conversation_id = null;
-       var conv_id = this.element.id.match(/\d+$/);
-       if (conv_id) {
-               this.conversation_id = conv_id[0];
-       }
-
-       var w = $(elm).width();
-       var h = $(elm).height();
-
-       var style = $(elm).offset();
-       style.top = style.top + h;
-       style.width = w;
-       style.position = 'absolute';
-       style.display = 'none';
-
-       this.cont = $('<div class="acpopup-mce"></div>');
-       this.cont.css(style);
-
-       $('body').append(this.cont);
-}
-
-ACPopup.prototype.close = function(){
-       $(this.cont).remove();
-       this.ready=false;
-}
-ACPopup.prototype.search = function(text){
-       var that = this;
-       this.searchText=text;
-       if (this.kp_timer) clearTimeout(this.kp_timer);
-       this.kp_timer = setTimeout( function(){that._search();}, 500);
-}
-
-ACPopup.prototype._search = function(){
-       console.log("_search");
-       var that = this;
-       var postdata = {
-               start:0,
-               count:100,
-               search:this.searchText,
-               type:'c',
-               conversation: this.conversation_id,
-       }
-
-       $.ajax({
-               type:'POST',
-               url: this.url,
-               data: postdata,
-               dataType: 'json',
-               success:function(data){
-                       that.cont.html("");
-                       if (data.tot>0){
-                               that.cont.show();
-                               $(data.items).each(function(){
-                                       var html = "<img src='{0}' height='16px' width='16px'>{1} ({2})".format(this.photo, this.name, this.nick);
-                                       var nick = this.nick.replace(' ','');
-                                       if (this.id!=='')  nick += '+' + this.id;
-                                       that.add(html, nick + ' - ' + this.link);
-                               });
-                       } else {
-                               that.cont.hide();
-                       }
-               }
-       });
-
-}
-
-ACPopup.prototype.add = function(label, value){
-       var that = this;
-       var elm = $('<div class="acpopupitem" title="' + value + '">' + label + '</div>');
-       elm.click(function(e){
-               t = $(this).attr('title').replace(new RegExp(' \- .*'), '');
-               el = $(that.element);
-               sel = el.getSelection();
-               sel.start = sel.start - that.searchText.length;
-               el.setSelection(sel.start, sel.end).replaceSelectedText(t + ' ').collapseSelection(false);
-               that.close();
-       });
-       $(this.cont).append(elm);
-}
-
-ACPopup.prototype.onkey = function(event){
-       if (event.keyCode == '13') {
-               if(this.idsel > -1) {
-                       this.cont.children()[this.idsel].click();
-                       event.preventDefault();
-               } else {
-                       this.close();
-               }
-       }
-       if (event.keyCode == '38') { //cursor up
-               var cmax = this.cont.children().size() - 1;
-               this.idsel--;
-               if (this.idsel < 0) {
-                       this.idsel = cmax;
-               }
-               event.preventDefault();
-       }
-       if (event.keyCode == '40' || event.keyCode == '9') { //cursor down
-               var cmax = this.cont.children().size() - 1;
-               this.idsel++;
-               if (this.idsel > cmax) {
-                       this.idsel = 0;
-               }
-               event.preventDefault();
-       }
-
-       if (event.keyCode == '38' || event.keyCode == '40' || event.keyCode == '9') {
-               this.cont.children().removeClass('selected');
-               $(this.cont.children()[this.idsel]).addClass('selected');
-       }
-
-       if (event.keyCode == '27') { //ESC
-               this.close();
-       }
-}
-
diff --git a/js/country.js b/js/country.js
deleted file mode 100644 (file)
index c6384a9..0000000
+++ /dev/null
@@ -1,438 +0,0 @@
-//<!--  Docs at: http://www.microcosmotalk.com/tech/scripts/\r
-// NOTE:\r
-// This code is placed into the public domain and may be used in any manner desired.\r
-// \r
-// Jim Carlock obtained the list of country names and state names from an HTML\r
-// document at Microsoft's website.\r
-//\r
-// Thursday, January 13, 2005, 7:00:52 PM\r
-// \r
-var gLngMaxStateLength=0;\r
-var gLngMaxCountryLength=0;\r
-var gLngNumberCountries=253;\r
-var gLngNumberStates=0;\r
-var gLngSelectedCountry=0;\r
-var gLngSelectedState=0;\r
-var gArCountryInfo;\r
-var gArStateInfo;\r
-// NOTE:\r
-// Some editors may exhibit problems viewing 2803 characters...\r
-var sCountryString = "|Afghanistan|Albania|Algeria|American Samoa|Angola|Anguilla|Antartica|Antigua and Barbuda|Argentina|Armenia|Aruba|Ashmore and Cartier Island|Australia|Austria|Azerbaijan|Bahamas|Bahrain|Bangladesh|Barbados|Belarus|Belgium|Belize|Benin|Bermuda|Bhutan|Bolivia|Bosnia and Herzegovina|Botswana|Brazil|British Virgin Islands|Brunei|Bulgaria|Burkina Faso|Burma|Burundi|Cambodia|Cameroon|Canada|Cape Verde|Cayman Islands|Central African Republic|Chad|Chile|China|Christmas Island|Clipperton Island|Cocos (Keeling) Islands|Colombia|Comoros|Congo, Democratic Republic of the|Congo, Republic of the|Cook Islands|Costa Rica|Cote d'Ivoire|Croatia|Cuba|Cyprus|Czech Republic|Denmark|Djibouti|Dominica|Dominican Republic|Ecuador|Egypt|El Salvador|Equatorial Guinea|Eritrea|Estonia|Ethiopia|Europa Island|Falkland Islands (Islas Malvinas)|Faroe Islands|Fiji|Finland|France|French Guiana|French Polynesia|French Southern and Antarctic Lands|Gabon|Gambia, The|Gaza Strip|Georgia|Germany|Ghana|Gibraltar|Glorioso Islands|Greece|Greenland|Grenada|Guadeloupe|Guam|Guatemala|Guernsey|Guinea|Guinea-Bissau|Guyana|Haiti|Heard Island and McDonald Islands|Holy See (Vatican City)|Honduras|Hong Kong|Howland Island|Hungary|Iceland|India|Indonesia|Iran|Iraq|Ireland|Ireland, Northern|Israel|Italy|Jamaica|Jan Mayen|Japan|Jarvis Island|Jersey|Johnston Atoll|Jordan|Juan de Nova Island|Kazakhstan|Kenya|Kiribati|Korea, North|Korea, South|Kuwait|Kyrgyzstan|Laos|Latvia|Lebanon|Lesotho|Liberia|Libya|Liechtenstein|Lithuania|Luxembourg|Macau|Macedonia, Former Yugoslav Republic of|Madagascar|Malawi|Malaysia|Maldives|Mali|Malta|Man, Isle of|Marshall Islands|Martinique|Mauritania|Mauritius|Mayotte|Mexico|Micronesia, Federated States of|Midway Islands|Moldova|Monaco|Mongolia|Montserrat|Morocco|Mozambique|Namibia|Nauru|Nepal|Netherlands|Netherlands Antilles|New Caledonia|New Zealand|Nicaragua|Niger|Nigeria|Niue|Norfolk Island|Northern Mariana Islands|Norway|Oman|Pakistan|Palau|Panama|Papua New Guinea|Paraguay|Peru|Philippines|Pitcaim Islands|Poland|Portugal|Puerto Rico|Qatar|Reunion|Romania|Russia|Rwanda|Saint Helena|Saint Kitts and Nevis|Saint Lucia|Saint Pierre and Miquelon|Saint Vincent and the Grenadines|Samoa|San Marino|Sao Tome and Principe|Saudi Arabia|Scotland|Senegal|Seychelles|Sierra Leone|Singapore|Slovakia|Slovenia|Solomon Islands|Somalia|South Africa|South Georgia and South Sandwich Islands|Spain|Spratly Islands|Sri Lanka|Sudan|Suriname|Svalbard|Swaziland|Sweden|Switzerland|Syria|Taiwan|Tajikistan|Tanzania|Thailand|Tobago|Toga|Tokelau|Tonga|Trinidad|Tunisia|Turkey|Turkmenistan|Tuvalu|Uganda|Ukraine|United Arab Emirates|United Kingdom|Uruguay|USA|Uzbekistan|Vanuatu|Venezuela|Vietnam|Virgin Islands|Wales|Wallis and Futuna|West Bank|Western Sahara|Yemen|Yugoslavia|Zambia|Zimbabwe|Friendicaland"\r
-var aStates = new Array();\r
-\r
-aStates[0]="";\r
-aStates[1]="|Badakhshan|Badghis|Baghlan|Balkh|Bamian|Farah|Faryab|Ghazni|Ghowr|Helmand|Herat|Jowzjan|Kabol|Kandahar|Kapisa|Konar|Kondoz|Laghman|Lowgar|Nangarhar|Nimruz|Oruzgan|Paktia|Paktika|Parvan|Samangan|Sar-e Pol|Takhar|Vardak|Zabol";\r
-aStates[2]="|Berat|Bulqize|Delvine|Devoll (Bilisht)|Diber (Peshkopi)|Durres|Elbasan|Fier|Gjirokaster|Gramsh|Has (Krume)|Kavaje|Kolonje (Erseke)|Korce|Kruje|Kucove|Kukes|Kurbin|Lezhe|Librazhd|Lushnje|Malesi e Madhe (Koplik)|Mallakaster (Ballsh)|Mat (Burrel)|Mirdite (Rreshen)|Peqin|Permet|Pogradec|Puke|Sarande|Shkoder|Skrapar (Corovode)|Tepelene|Tirane (Tirana)|Tirane (Tirana)|Tropoje (Bajram Curri)|Vlore";\r
-aStates[3]="|Adrar|Ain Defla|Ain Temouchent|Alger|Annaba|Batna|Bechar|Bejaia|Biskra|Blida|Bordj Bou Arreridj|Bouira|Boumerdes|Chlef|Constantine|Djelfa|El Bayadh|El Oued|El Tarf|Ghardaia|Guelma|Illizi|Jijel|Khenchela|Laghouat|M'Sila|Mascara|Medea|Mila|Mostaganem|Naama|Oran|Ouargla|Oum el Bouaghi|Relizane|Saida|Setif|Sidi Bel Abbes|Skikda|Souk Ahras|Tamanghasset|Tebessa|Tiaret|Tindouf|Tipaza|Tissemsilt|Tizi Ouzou|Tlemcen";\r
-aStates[4]="|Eastern|Manu'a|Rose Island|Swains Island|Western";\r
-aStates[5]="|Andorra la Vella|Bengo|Benguela|Bie|Cabinda|Canillo|Cuando Cubango|Cuanza Norte|Cuanza Sul|Cunene|Encamp|Escaldes-Engordany|Huambo|Huila|La Massana|Luanda|Lunda Norte|Lunda Sul|Malanje|Moxico|Namibe|Ordino|Sant Julia de Loria|Uige|Zaire";\r
-aStates[6]="|Anguilla";\r
-aStates[7]="|Antartica";\r
-aStates[8]="|Barbuda|Redonda|Saint George|Saint John|Saint Mary|Saint Paul|Saint Peter|Saint Philip";\r
-aStates[9]="|Antartica e Islas del Atlantico Sur|Buenos Aires|Buenos Aires Capital Federal|Catamarca|Chaco|Chubut|Cordoba|Corrientes|Entre Rios|Formosa|Jujuy|La Pampa|La Rioja|Mendoza|Misiones|Neuquen|Rio Negro|Salta|San Juan|San Luis|Santa Cruz|Santa Fe|Santiago del Estero|Tierra del Fuego|Tucuman";\r
-aStates[10]="|Aragatsotn|Ararat|Armavir|Geghark'unik'|Kotayk'|Lorri|Shirak|Syunik'|Tavush|Vayots' Dzor|Yerevan";\r
-aStates[11]="|Aruba";\r
-aStates[12]="|Ashmore and Cartier Island";\r
-aStates[13]="|Australian Capital Territory|New South Wales|Northern Territory|Queensland|South Australia|Tasmania|Victoria|Western Australia";\r
-aStates[14]="|Burgenland|Kaernten|Niederoesterreich|Oberoesterreich|Salzburg|Steiermark|Tirol|Vorarlberg|Wien";\r
-aStates[15]="|Abseron Rayonu|Agcabadi Rayonu|Agdam Rayonu|Agdas Rayonu|Agstafa Rayonu|Agsu Rayonu|Ali Bayramli Sahari|Astara Rayonu|Baki Sahari|Balakan Rayonu|Barda Rayonu|Beylaqan Rayonu|Bilasuvar Rayonu|Cabrayil Rayonu|Calilabad Rayonu|Daskasan Rayonu|Davaci Rayonu|Fuzuli Rayonu|Gadabay Rayonu|Ganca Sahari|Goranboy Rayonu|Goycay Rayonu|Haciqabul Rayonu|Imisli Rayonu|Ismayilli Rayonu|Kalbacar Rayonu|Kurdamir Rayonu|Lacin Rayonu|Lankaran Rayonu|Lankaran Sahari|Lerik Rayonu|Masalli Rayonu|Mingacevir Sahari|Naftalan Sahari|Naxcivan Muxtar Respublikasi|Neftcala Rayonu|Oguz Rayonu|Qabala Rayonu|Qax Rayonu|Qazax Rayonu|Qobustan Rayonu|Quba Rayonu|Qubadli Rayonu|Qusar Rayonu|Saatli Rayonu|Sabirabad Rayonu|Saki Rayonu|Saki Sahari|Salyan Rayonu|Samaxi Rayonu|Samkir Rayonu|Samux Rayonu|Siyazan Rayonu|Sumqayit Sahari|Susa Rayonu|Susa Sahari|Tartar Rayonu|Tovuz Rayonu|Ucar Rayonu|Xacmaz Rayonu|Xankandi Sahari|Xanlar Rayonu|Xizi Rayonu|Xocali Rayonu|Xocavand Rayonu|Yardimli Rayonu|Yevlax Rayonu|Yevlax Sahari|Zangilan Rayonu|Zaqatala Rayonu|Zardab Rayonu";\r
-aStates[16]="|Acklins and Crooked Islands|Bimini|Cat Island|Exuma|Freeport|Fresh Creek|Governor's Harbour|Green Turtle Cay|Harbour Island|High Rock|Inagua|Kemps Bay|Long Island|Marsh Harbour|Mayaguana|New Providence|Nicholls Town and Berry Islands|Ragged Island|Rock Sound|San Salvador and Rum Cay|Sandy Point";\r
-aStates[17]="|Al Hadd|Al Manamah|Al Mintaqah al Gharbiyah|Al Mintaqah al Wusta|Al Mintaqah ash Shamaliyah|Al Muharraq|Ar Rifa' wa al Mintaqah al Janubiyah|Jidd Hafs|Juzur Hawar|Madinat 'Isa|Madinat Hamad|Sitrah";\r
-aStates[18]="|Barisal|Chittagong|Dhaka|Khulna|Rajshahi|Sylhet";\r
-aStates[19]="|Bridgetown|Christ Church|Saint Andrew|Saint George|Saint James|Saint John|Saint Joseph|Saint Lucy|Saint Michael|Saint Peter|Saint Philip|Saint Thomas";\r
-aStates[20]="|Brestskaya (Brest)|Homyel'skaya (Homyel')|Horad Minsk|Hrodzyenskaya (Hrodna)|Mahilyowskaya (Mahilyow)|Minskaya|Vitsyebskaya (Vitsyebsk)";\r
-aStates[21]="|Antwerpen|Brabant Wallon|Brussels Capitol Region|Hainaut|Liege|Limburg|Luxembourg|Namur|Oost-Vlaanderen|Vlaams Brabant|West-Vlaanderen";\r
-aStates[22]="|Belize|Cayo|Corozal|Orange Walk|Stann Creek|Toledo";\r
-aStates[23]="|Alibori|Atakora|Atlantique|Borgou|Collines|Couffo|Donga|Littoral|Mono|Oueme|Plateau|Zou";\r
-aStates[24]="|Devonshire|Hamilton (City)|Hamilton|Paget|Pembroke|Saint George|Saint Georges|Sandys|Smiths|Southampton|Warwick";\r
-aStates[25]="|Bumthang|Chhukha|Chirang|Daga|Geylegphug|Ha|Lhuntshi|Mongar|Paro|Pemagatsel|Punakha|Samchi|Samdrup Jongkhar|Shemgang|Tashigang|Thimphu|Tongsa|Wangdi Phodrang";\r
-aStates[26]="|Beni|Chuquisaca|Cochabamba|La Paz|Oruro|Pando|Potosi|Santa Cruz|Tarija";\r
-aStates[27]="|Federation of Bosnia and Herzegovina|Republika Srpska";\r
-aStates[28]="|Central|Chobe|Francistown|Gaborone|Ghanzi|Kgalagadi|Kgatleng|Kweneng|Lobatse|Ngamiland|North-East|Selebi-Pikwe|South-East|Southern";\r
-aStates[29]="|Acre|Alagoas|Amapa|Amazonas|Bahia|Ceara|Distrito Federal|Espirito Santo|Goias|Maranhao|Mato Grosso|Mato Grosso do Sul|Minas Gerais|Para|Paraiba|Parana|Pernambuco|Piaui|Rio de Janeiro|Rio Grande do Norte|Rio Grande do Sul|Rondonia|Roraima|Santa Catarina|Sao Paulo|Sergipe|Tocantins";\r
-aStates[30]="|Anegada|Jost Van Dyke|Tortola|Virgin Gorda";\r
-aStates[31]="|Belait|Brunei and Muara|Temburong|Tutong";\r
-aStates[32]="|Blagoevgrad|Burgas|Dobrich|Gabrovo|Haskovo|Kardzhali|Kyustendil|Lovech|Montana|Pazardzhik|Pernik|Pleven|Plovdiv|Razgrad|Rousse|Shumen|Silistra|Sliven|Smolyan|Sofia|Stara Zagora|Turgovishte|Varna|Veliko Turnovo|Vidin|Vratsa|Yambol";\r
-aStates[33]="|Bale|Bam|Banwa|Bazega|Bougouriba|Boulgou|Boulkiemde|Comoe|Ganzourgou|Gnagna|Gourma|Houet|Ioba|Kadiogo|Kenedougou|Komandjari|Kompienga|Kossi|Koupelogo|Kouritenga|Kourweogo|Leraba|Loroum|Mouhoun|Nahouri|Namentenga|Naumbiel|Nayala|Oubritenga|Oudalan|Passore|Poni|Samentenga|Sanguie|Seno|Sissili|Soum|Sourou|Tapoa|Tuy|Yagha|Yatenga|Ziro|Zondomo|Zoundweogo";\r
-aStates[34]="|Ayeyarwady|Bago|Chin State|Kachin State|Kayah State|Kayin State|Magway|Mandalay|Mon State|Rakhine State|Sagaing|Shan State|Tanintharyi|Yangon";\r
-aStates[35]="|Bubanza|Bujumbura|Bururi|Cankuzo|Cibitoke|Gitega|Karuzi|Kayanza|Kirundo|Makamba|Muramvya|Muyinga|Mwaro|Ngozi|Rutana|Ruyigi";\r
-aStates[36]="|Banteay Mean Cheay|Batdambang|Kampong Cham|Kampong Chhnang|Kampong Spoe|Kampong Thum|Kampot|Kandal|Kaoh Kong|Keb|Kracheh|Mondol Kiri|Otdar Mean Cheay|Pailin|Phnum Penh|Pouthisat|Preah Seihanu (Sihanoukville)|Preah Vihear|Prey Veng|Rotanah Kiri|Siem Reab|Stoeng Treng|Svay Rieng|Takev";\r
-aStates[37]="|Adamaoua|Centre|Est|Extreme-Nord|Littoral|Nord|Nord-Ouest|Ouest|Sud|Sud-Ouest";\r
-aStates[38]="|Alberta|British Columbia|Manitoba|New Brunswick|Newfoundland|Northwest Territories|Nova Scotia|Nunavut|Ontario|Prince Edward Island|Quebec|Saskatchewan|Yukon Territory";\r
-aStates[39]="|Boa Vista|Brava|Maio|Mosteiros|Paul|Porto Novo|Praia|Ribeira Grande|Sal|Santa Catarina|Santa Cruz|Sao Domingos|Sao Filipe|Sao Nicolau|Sao Vicente|Tarrafal";\r
-aStates[40]="|Creek|Eastern|Midland|South Town|Spot Bay|Stake Bay|West End|Western";\r
-aStates[41]="|Bamingui-Bangoran|Bangui|Basse-Kotto|Gribingui|Haut-Mbomou|Haute-Kotto|Haute-Sangha|Kemo-Gribingui|Lobaye|Mbomou|Nana-Mambere|Ombella-Mpoko|Ouaka|Ouham|Ouham-Pende|Sangha|Vakaga";\r
-aStates[42]="|Batha|Biltine|Borkou-Ennedi-Tibesti|Chari-Baguirmi|Guera|Kanem|Lac|Logone Occidental|Logone Oriental|Mayo-Kebbi|Moyen-Chari|Ouaddai|Salamat|Tandjile";\r
-aStates[43]="|Aisen del General Carlos Ibanez del Campo|Antofagasta|Araucania|Atacama|Bio-Bio|Coquimbo|Libertador General Bernardo O'Higgins|Los Lagos|Magallanes y de la Antartica Chilena|Maule|Region Metropolitana (Santiago)|Tarapaca|Valparaiso";\r
-aStates[44]="|Anhui|Beijing|Chongqing|Fujian|Gansu|Guangdong|Guangxi|Guizhou|Hainan|Hebei|Heilongjiang|Henan|Hubei|Hunan|Jiangsu|Jiangxi|Jilin|Liaoning|Nei Mongol|Ningxia|Qinghai|Shaanxi|Shandong|Shanghai|Shanxi|Sichuan|Tianjin|Xinjiang|Xizang (Tibet)|Yunnan|Zhejiang";\r
-aStates[45]="|Christmas Island";\r
-aStates[46]="|Clipperton Island";\r
-aStates[47]="|Direction Island|Home Island|Horsburgh Island|North Keeling Island|South Island|West Island";\r
-aStates[48]="|Amazonas|Antioquia|Arauca|Atlantico|Bolivar|Boyaca|Caldas|Caqueta|Casanare|Cauca|Cesar|Choco|Cordoba|Cundinamarca|Distrito Capital de Santa Fe de Bogota|Guainia|Guaviare|Huila|La Guajira|Magdalena|Meta|Narino|Norte de Santander|Putumayo|Quindio|Risaralda|San Andres y Providencia|Santander|Sucre|Tolima|Valle del Cauca|Vaupes|Vichada";\r
-// <!-- -->\r
-aStates[49]="|Anjouan (Nzwani)|Domoni|Fomboni|Grande Comore (Njazidja)|Moheli (Mwali)|Moroni|Moutsamoudou";\r
-aStates[50]="|Bandundu|Bas-Congo|Equateur|Kasai-Occidental|Kasai-Oriental|Katanga|Kinshasa|Maniema|Nord-Kivu|Orientale|Sud-Kivu";\r
-aStates[51]="|Bouenza|Brazzaville|Cuvette|Kouilou|Lekoumou|Likouala|Niari|Plateaux|Pool|Sangha";\r
-aStates[52]="|Aitutaki|Atiu|Avarua|Mangaia|Manihiki|Manuae|Mauke|Mitiaro|Nassau Island|Palmerston|Penrhyn|Pukapuka|Rakahanga|Rarotonga|Suwarrow|Takutea";\r
-aStates[53]="|Alajuela|Cartago|Guanacaste|Heredia|Limon|Puntarenas|San Jose";\r
-aStates[54]="|Abengourou|Abidjan|Aboisso|Adiake'|Adzope|Agboville|Agnibilekrou|Ale'pe'|Bangolo|Beoumi|Biankouma|Bocanda|Bondoukou|Bongouanou|Bouafle|Bouake|Bouna|Boundiali|Dabakala|Dabon|Daloa|Danane|Daoukro|Dimbokro|Divo|Duekoue|Ferkessedougou|Gagnoa|Grand Bassam|Grand-Lahou|Guiglo|Issia|Jacqueville|Katiola|Korhogo|Lakota|Man|Mankono|Mbahiakro|Odienne|Oume|Sakassou|San-Pedro|Sassandra|Seguela|Sinfra|Soubre|Tabou|Tanda|Tiassale|Tiebissou|Tingrela|Touba|Toulepleu|Toumodi|Vavoua|Yamoussoukro|Zuenoula";\r
-aStates[55]="|Bjelovarsko-Bilogorska Zupanija|Brodsko-Posavska Zupanija|Dubrovacko-Neretvanska Zupanija|Istarska Zupanija|Karlovacka Zupanija|Koprivnicko-Krizevacka Zupanija|Krapinsko-Zagorska Zupanija|Licko-Senjska Zupanija|Medimurska Zupanija|Osjecko-Baranjska Zupanija|Pozesko-Slavonska Zupanija|Primorsko-Goranska Zupanija|Sibensko-Kninska Zupanija|Sisacko-Moslavacka Zupanija|Splitsko-Dalmatinska Zupanija|Varazdinska Zupanija|Viroviticko-Podravska Zupanija|Vukovarsko-Srijemska Zupanija|Zadarska Zupanija|Zagreb|Zagrebacka Zupanija";\r
-aStates[56]="|Camaguey|Ciego de Avila|Cienfuegos|Ciudad de La Habana|Granma|Guantanamo|Holguin|Isla de la Juventud|La Habana|Las Tunas|Matanzas|Pinar del Rio|Sancti Spiritus|Santiago de Cuba|Villa Clara";\r
-aStates[57]="|Famagusta|Kyrenia|Larnaca|Limassol|Nicosia|Paphos";\r
-aStates[58]="|Brnensky|Budejovicky|Jihlavsky|Karlovarsky|Kralovehradecky|Liberecky|Olomoucky|Ostravsky|Pardubicky|Plzensky|Praha|Stredocesky|Ustecky|Zlinsky";\r
-aStates[59]="|Aalborg|Aarhus|Bornholm|Esbjerg|Falster|Frederiksberg|Fyn|Hillerød|Horsens|Kolding|København|Als|Langeland|Lolland|Midtjylland|Nordjylland|Nordsjælland|Odense|Randers|Roskilde|Syddanmark|Morsø|Møn|Sydsjælland|Vejle|Vestsjælland|Viborg";\r
-aStates[60]="|'Ali Sabih|Dikhil|Djibouti|Obock|Tadjoura";\r
-aStates[61]="|Saint Andrew|Saint David|Saint George|Saint John|Saint Joseph|Saint Luke|Saint Mark|Saint Patrick|Saint Paul|Saint Peter";\r
-aStates[62]="|Azua|Baoruco|Barahona|Dajabon|Distrito Nacional|Duarte|El Seibo|Elias Pina|Espaillat|Hato Mayor|Independencia|La Altagracia|La Romana|La Vega|Maria Trinidad Sanchez|Monsenor Nouel|Monte Cristi|Monte Plata|Pedernales|Peravia|Puerto Plata|Salcedo|Samana|San Cristobal|San Juan|San Pedro de Macoris|Sanchez Ramirez|Santiago|Santiago Rodriguez|Valverde";\r
-// <!-- -->\r
-aStates[63]="|Azuay|Bolivar|Canar|Carchi|Chimborazo|Cotopaxi|El Oro|Esmeraldas|Galapagos|Guayas|Imbabura|Loja|Los Rios|Manabi|Morona-Santiago|Napo|Orellana|Pastaza|Pichincha|Sucumbios|Tungurahua|Zamora-Chinchipe";\r
-aStates[64]="|Ad Daqahliyah|Al Bahr al Ahmar|Al Buhayrah|Al Fayyum|Al Gharbiyah|Al Iskandariyah|Al Isma'iliyah|Al Jizah|Al Minufiyah|Al Minya|Al Qahirah|Al Qalyubiyah|Al Wadi al Jadid|As Suways|Ash Sharqiyah|Aswan|Asyut|Bani Suwayf|Bur Sa'id|Dumyat|Janub Sina'|Kafr ash Shaykh|Matruh|Qina|Shamal Sina'|Suhaj";\r
-aStates[65]="|Ahuachapan|Cabanas|Chalatenango|Cuscatlan|La Libertad|La Paz|La Union|Morazan|San Miguel|San Salvador|San Vicente|Santa Ana|Sonsonate|Usulutan";\r
-aStates[66]="|Annobon|Bioko Norte|Bioko Sur|Centro Sur|Kie-Ntem|Litoral|Wele-Nzas";\r
-aStates[67]="|Akale Guzay|Barka|Denkel|Hamasen|Sahil|Semhar|Senhit|Seraye";\r
-aStates[68]="|Harjumaa (Tallinn)|Hiiumaa (Kardla)|Ida-Virumaa (Johvi)|Jarvamaa (Paide)|Jogevamaa (Jogeva)|Laane-Virumaa (Rakvere)|Laanemaa (Haapsalu)|Parnumaa (Parnu)|Polvamaa (Polva)|Raplamaa (Rapla)|Saaremaa (Kuessaare)|Tartumaa (Tartu)|Valgamaa (Valga)|Viljandimaa (Viljandi)|Vorumaa (Voru)"\r
-aStates[69]="|Adis Abeba (Addis Ababa)|Afar|Amara|Dire Dawa|Gambela Hizboch|Hareri Hizb|Oromiya|Sumale|Tigray|YeDebub Biheroch Bihereseboch na Hizboch";\r
-aStates[70]="|Europa Island";\r
-aStates[71]="|Falkland Islands (Islas Malvinas)"\r
-aStates[72]="|Bordoy|Eysturoy|Mykines|Sandoy|Skuvoy|Streymoy|Suduroy|Tvoroyri|Vagar";\r
-aStates[73]="|Central|Eastern|Northern|Rotuma|Western";\r
-aStates[74]="|Aland|Etela-Suomen Laani|Ita-Suomen Laani|Lansi-Suomen Laani|Lappi|Oulun Laani";\r
-aStates[75]="|Alsace|Aquitaine|Auvergne|Basse-Normandie|Bourgogne|Bretagne|Centre|Champagne-Ardenne|Corse|Franche-Comté|Haute-Normandie|Île-de-France|Languedoc-Roussillon|Limousin|Lorraine|Midi-Pyrénées|Nord-Pas-de-Calais|Pays de la Loire|Picardie|Poitou-Charentes|Provence-Alpes-Côte d’Azur|Rhône-Alpes";\r
-aStates[76]="|French Guiana";\r
-aStates[77]="|Archipel des Marquises|Archipel des Tuamotu|Archipel des Tubuai|Iles du Vent|Iles Sous-le-Vent";\r
-aStates[78]="|Adelie Land|Ile Crozet|Iles Kerguelen|Iles Saint-Paul et Amsterdam";\r
-aStates[79]="|Estuaire|Haut-Ogooue|Moyen-Ogooue|Ngounie|Nyanga|Ogooue-Ivindo|Ogooue-Lolo|Ogooue-Maritime|Woleu-Ntem";\r
-aStates[80]="|Banjul|Central River|Lower River|North Bank|Upper River|Western";\r
-aStates[81]="|Gaza Strip";\r
-aStates[82]="|Abashis|Abkhazia or Ap'khazet'is Avtonomiuri Respublika (Sokhumi)|Adigenis|Ajaria or Acharis Avtonomiuri Respublika (Bat'umi)|Akhalgoris|Akhalk'alak'is|Akhalts'ikhis|Akhmetis|Ambrolauris|Aspindzis|Baghdat'is|Bolnisis|Borjomis|Ch'khorotsqus|Ch'okhatauris|Chiat'ura|Dedop'listsqaros|Dmanisis|Dushet'is|Gardabanis|Gori|Goris|Gurjaanis|Javis|K'arelis|K'ut'aisi|Kaspis|Kharagaulis|Khashuris|Khobis|Khonis|Lagodekhis|Lanch'khut'is|Lentekhis|Marneulis|Martvilis|Mestiis|Mts'khet'is|Ninotsmindis|Onis|Ozurget'is|P'ot'i|Qazbegis|Qvarlis|Rust'avi|Sach'kheris|Sagarejos|Samtrediis|Senakis|Sighnaghis|T'bilisi|T'elavis|T'erjolis|T'et'ritsqaros|T'ianet'is|Tqibuli|Ts'ageris|Tsalenjikhis|Tsalkis|Tsqaltubo|Vanis|Zestap'onis|Zugdidi|Zugdidis";\r
-aStates[83]="|Baden-Württemberg|Bayern|Berlin|Brandenburg|Bremen|Hamburg|Hessen|Mecklenburg-Vorpommern|Niedersachsen|Nordrhein-Westfalen|Rheinland-Pfalz|Saarland|Sachsen|Sachsen-Anhalt|Schleswig-Holstein|Thüringen";\r
-aStates[84]="|Ashanti|Brong-Ahafo|Central|Eastern|Greater Accra|Northern|Upper East|Upper West|Volta|Western";\r
-aStates[85]="|Gibraltar";\r
-aStates[86]="|Ile du Lys|Ile Glorieuse";\r
-aStates[87]="|Aitolia kai Akarnania|Akhaia|Argolis|Arkadhia|Arta|Attiki|Ayion Oros (Mt. Athos)|Dhodhekanisos|Drama|Evritania|Evros|Evvoia|Florina|Fokis|Fthiotis|Grevena|Ilia|Imathia|Ioannina|Irakleion|Kardhitsa|Kastoria|Kavala|Kefallinia|Kerkyra|Khalkidhiki|Khania|Khios|Kikladhes|Kilkis|Korinthia|Kozani|Lakonia|Larisa|Lasithi|Lesvos|Levkas|Magnisia|Messinia|Pella|Pieria|Preveza|Rethimni|Rodhopi|Samos|Serrai|Thesprotia|Thessaloniki|Trikala|Voiotia|Xanthi|Zakinthos";\r
-aStates[88]="|Avannaa (Nordgronland)|Kitaa (Vestgronland)|Tunu (Ostgronland)"\r
-aStates[89]="|Carriacou and Petit Martinique|Saint Andrew|Saint David|Saint George|Saint John|Saint Mark|Saint Patrick";\r
-aStates[90]="|Basse-Terre|Grande-Terre|Îles de la Petite Terre|Îles des Saintes|Marie-Galante";\r
-aStates[91]="|Guam";\r
-aStates[92]="|Alta Verapaz|Baja Verapaz|Chimaltenango|Chiquimula|El Progreso|Escuintla|Guatemala|Huehuetenango|Izabal|Jalapa|Jutiapa|Peten|Quetzaltenango|Quiche|Retalhuleu|Sacatepequez|San Marcos|Santa Rosa|Solola|Suchitepequez|Totonicapan|Zacapa";\r
-aStates[93]="|Castel|Forest|St. Andrew|St. Martin|St. Peter Port|St. Pierre du Bois|St. Sampson|St. Saviour|Torteval|Vale";\r
-aStates[94]="|Beyla|Boffa|Boke|Conakry|Coyah|Dabola|Dalaba|Dinguiraye|Dubreka|Faranah|Forecariah|Fria|Gaoual|Gueckedou|Kankan|Kerouane|Kindia|Kissidougou|Koubia|Koundara|Kouroussa|Labe|Lelouma|Lola|Macenta|Mali|Mamou|Mandiana|Nzerekore|Pita|Siguiri|Telimele|Tougue|Yomou";\r
-aStates[95]="|Bafata|Biombo|Bissau|Bolama-Bijagos|Cacheu|Gabu|Oio|Quinara|Tombali";\r
-aStates[96]="|Barima-Waini|Cuyuni-Mazaruni|Demerara-Mahaica|East Berbice-Corentyne|Essequibo Islands-West Demerara|Mahaica-Berbice|Pomeroon-Supenaam|Potaro-Siparuni|Upper Demerara-Berbice|Upper Takutu-Upper Essequibo";\r
-aStates[97]="|Artibonite|Centre|Grand'Anse|Nord|Nord-Est|Nord-Ouest|Ouest|Sud|Sud-Est";\r
-aStates[98]="|Heard Island and McDonald Islands";\r
-aStates[99]="|Holy See (Vatican City)"\r
-aStates[100]="|Atlantida|Choluteca|Colon|Comayagua|Copan|Cortes|El Paraiso|Francisco Morazan|Gracias a Dios|Intibuca|Islas de la Bahia|La Paz|Lempira|Ocotepeque|Olancho|Santa Barbara|Valle|Yoro";\r
-aStates[101]="|Hong Kong";\r
-aStates[102]="|Howland Island";\r
-aStates[103]="|Bacs-Kiskun|Baranya|Bekes|Bekescsaba|Borsod-Abauj-Zemplen|Budapest|Csongrad|Debrecen|Dunaujvaros|Eger|Fejer|Gyor|Gyor-Moson-Sopron|Hajdu-Bihar|Heves|Hodmezovasarhely|Jasz-Nagykun-Szolnok|Kaposvar|Kecskemet|Komarom-Esztergom|Miskolc|Nagykanizsa|Nograd|Nyiregyhaza|Pecs|Pest|Somogy|Sopron|Szabolcs-Szatmar-Bereg|Szeged|Szekesfehervar|Szolnok|Szombathely|Tatabanya|Tolna|Vas|Veszprem|Veszprem (City)|Zala|Zalaegerszeg";\r
-aStates[104]="|Akranes|Akureyri|Arnessysla|Austur-Bardhastrandarsysla|Austur-Hunavatnssysla|Austur-Skaftafellssysla|Borgarfjardharsysla|Dalasysla|Eyjafjardharsysla|Gullbringusysla|Hafnarfjordhur|Husavik|Isafjordhur|Keflavik|Kjosarsysla|Kopavogur|Myrasysla|Neskaupstadhur|Nordhur-Isafjardharsysla|Nordhur-Mulasys-la|Nordhur-Thingeyjarsysla|Olafsfjordhur|Rangarvallasysla|Reykjavik|Saudharkrokur|Seydhisfjordhur|Siglufjordhur|Skagafjardharsysla|Snaefellsnes-og Hnappadalssysla|Strandasysla|Sudhur-Mulasysla|Sudhur-Thingeyjarsysla|Vesttmannaeyjar|Vestur-Bardhastrandarsysla|Vestur-Hunavatnssysla|Vestur-Isafjardharsysla|Vestur-Skaftafellssysla";\r
-aStates[105]="|Andaman and Nicobar Islands|Andhra Pradesh|Arunachal Pradesh|Assam|Bihar|Chandigarh|Chhattisgarh|Dadra and Nagar Haveli|Daman and Diu|Delhi|Goa|Gujarat|Haryana|Himachal Pradesh|Jammu and Kashmir|Jharkhand|Karnataka|Kerala|Lakshadweep|Madhya Pradesh|Maharashtra|Manipur|Meghalaya|Mizoram|Nagaland|Orissa|Pondicherry|Punjab|Rajasthan|Sikkim|Tamil Nadu|Tripura|Uttar Pradesh|Uttaranchal|West Bengal";\r
-aStates[106]="|Aceh|Bali|Banten|Bengkulu|East Timor|Gorontalo|Irian Jaya|Jakarta Raya|Jambi|Jawa Barat|Jawa Tengah|Jawa Timur|Kalimantan Barat|Kalimantan Selatan|Kalimantan Tengah|Kalimantan Timur|Kepulauan Bangka Belitung|Lampung|Maluku|Maluku Utara|Nusa Tenggara Barat|Nusa Tenggara Timur|Riau|Sulawesi Selatan|Sulawesi Tengah|Sulawesi Tenggara|Sulawesi Utara|Sumatera Barat|Sumatera Selatan|Sumatera Utara|Yogyakarta";\r
-aStates[107]="|Ardabil|Azarbayjan-e Gharbi|Azarbayjan-e Sharqi|Bushehr|Chahar Mahall va Bakhtiari|Esfahan|Fars|Gilan|Golestan|Hamadan|Hormozgan|Ilam|Kerman|Kermanshah|Khorasan|Khuzestan|Kohgiluyeh va Buyer Ahmad|Kordestan|Lorestan|Markazi|Mazandaran|Qazvin|Qom|Semnan|Sistan va Baluchestan|Tehran|Yazd|Zanjan";\r
-aStates[108]="|Al Anbar|Al Basrah|Al Muthanna|Al Qadisiyah|An Najaf|Arbil|As Sulaymaniyah|At Ta'mim|Babil|Baghdad|Dahuk|Dhi Qar|Diyala|Karbala'|Maysan|Ninawa|Salah ad Din|Wasit";\r
-aStates[109]="|Antrim|Armargh|Carlow|Cavan|Clare|Cork|Derry|Donegal|Down|Dún Laoghaire–Rathdown|Fermanagh|Dublin|Fingal|Galway|Kerry|Kildare|Kilkenny|Laois|Leitrim|Limerick|Longford|Louth|Mayo|Meath|Monaghan|Offaly|Roscommon|Sligo|Tipperary|Tyrone|Waterford|Westmeath|Wexford|Wicklow";\r
-aStates[110]="|Antrim|Armagh|Belfast|Down|Fermanagh|Londonderry|Tyrone";\r
-aStates[111]="|Central|Haifa|Jerusalem|Northern|Southern|Tel Aviv";\r
-aStates[112]="|Abruzzi|Basilicata|Calabria|Campania|Emilia-Romagna|Friuli-Venezia Giulia|Lazio|Liguria|Lombardia|Marche|Molise|Piemonte|Puglia|Sardegna|Sicilia|Toscana|Trentino-Alto Adige|Umbria|Valle d'Aosta|Veneto";\r
-aStates[113]="|Clarendon|Hanover|Kingston|Manchester|Portland|Saint Andrew|Saint Ann|Saint Catherine|Saint Elizabeth|Saint James|Saint Mary|Saint Thomas|Trelawny|Westmoreland";\r
-aStates[114]="|Jan Mayen";\r
-aStates[115]="|Aichi|Akita|Aomori|Chiba|Ehime|Fukui|Fukuoka|Fukushima|Gifu|Gumma|Hiroshima|Hokkaido|Hyogo|Ibaraki|Ishikawa|Iwate|Kagawa|Kagoshima|Kanagawa|Kochi|Kumamoto|Kyoto|Mie|Miyagi|Miyazaki|Nagano|Nagasaki|Nara|Niigata|Oita|Okayama|Okinawa|Osaka|Saga|Saitama|Shiga|Shimane|Shizuoka|Tochigi|Tokushima|Tokyo|Tottori|Toyama|Wakayama|Yamagata|Yamaguchi|Yamanashi";\r
-aStates[116]="|Jarvis Island";\r
-aStates[117]="|Jersey";\r
-aStates[118]="|Johnston Atoll";\r
-aStates[119]="|'Amman|Ajlun|Al 'Aqabah|Al Balqa'|Al Karak|Al Mafraq|At Tafilah|Az Zarqa'|Irbid|Jarash|Ma'an|Madaba";\r
-aStates[120]="|Juan de Nova Island";\r
-aStates[121]="|Almaty|Aqmola|Aqtobe|Astana|Atyrau|Batys Qazaqstan|Bayqongyr|Mangghystau|Ongtustik Qazaqstan|Pavlodar|Qaraghandy|Qostanay|Qyzylorda|Shyghys Qazaqstan|Soltustik Qazaqstan|Zhambyl";\r
-aStates[122]="|Central|Coast|Eastern|Nairobi Area|North Eastern|Nyanza|Rift Valley|Western";\r
-aStates[123]="|Abaiang|Abemama|Aranuka|Arorae|Banaba (District)|Banaba|Beru|Butaritari|Central Gilberts (District)|Gilbert Islands (Unit)|Kanton|Kiritimati|Kuria|Line Islands (District)|Line Islands (Unit)|Maiana|Makin|Marakei|Nikunau|Nonouti|Northern Gilberts (District)|Onotoa|Phoenix Islands (Unit)|Southern Gilberts (District)|Tabiteuea|Tabuaeran|Tamana|Tarawa (District)|Tarawa|Teraina";\r
-aStates[124]="|Chagang-do (Chagang Province)|Hamgyong-bukto (North Hamgyong Province)|Hamgyong-namdo (South Hamgyong Province)|Hwanghae-bukto (North Hwanghae Province)|Hwanghae-namdo (South Hwanghae Province)|Kaesong-si (Kaesong City)|Kangwon-do (Kangwon Province)|Namp'o-si (Namp'o City)|P'yongan-bukto (North P'yongan Province)|P'yongan-namdo (South P'yongan Province)|P'yongyang-si (P'yongyang City)|Yanggang-do (Yanggang Province)"\r
-aStates[125]="|Ch'ungch'ong-bukto|Ch'ungch'ong-namdo|Cheju-do|Cholla-bukto|Cholla-namdo|Inch'on-gwangyoksi|Kangwon-do|Kwangju-gwangyoksi|Kyonggi-do|Kyongsang-bukto|Kyongsang-namdo|Pusan-gwangyoksi|Soul-t'ukpyolsi|Taegu-gwangyoksi|Taejon-gwangyoksi|Ulsan-gwangyoksi";\r
-aStates[126]="|Al 'Asimah|Al Ahmadi|Al Farwaniyah|Al Jahra'|Hawalli";\r
-aStates[127]="|Batken Oblasty|Bishkek Shaary|Chuy Oblasty (Bishkek)|Jalal-Abad Oblasty|Naryn Oblasty|Osh Oblasty|Talas Oblasty|Ysyk-Kol Oblasty (Karakol)"\r
-aStates[128]="|Attapu|Bokeo|Bolikhamxai|Champasak|Houaphan|Khammouan|Louangnamtha|Louangphabang|Oudomxai|Phongsali|Salavan|Savannakhet|Viangchan City|Viangchan|Xaignabouli|Xaisomboun|Xekong|Xiangkhoang";\r
-aStates[129]="|Aizkraukles Rajons|Aluksnes Rajons|Balvu Rajons|Bauskas Rajons|Cesu Rajons|Daugavpils|Daugavpils Rajons|Dobeles Rajons|Gulbenes Rajons|Jekabpils Rajons|Jelgava|Jelgavas Rajons|Jurmala|Kraslavas Rajons|Kuldigas Rajons|Leipaja|Liepajas Rajons|Limbazu Rajons|Ludzas Rajons|Madonas Rajons|Ogres Rajons|Preilu Rajons|Rezekne|Rezeknes Rajons|Riga|Rigas Rajons|Saldus Rajons|Talsu Rajons|Tukuma Rajons|Valkas Rajons|Valmieras Rajons|Ventspils|Ventspils Rajons";\r
-aStates[130]="|Beyrouth|Ech Chimal|Ej Jnoub|El Bekaa|Jabal Loubnane";\r
-aStates[131]="|Berea|Butha-Buthe|Leribe|Mafeteng|Maseru|Mohales Hoek|Mokhotlong|Qacha's Nek|Quthing|Thaba-Tseka";\r
-aStates[132]="|Bomi|Bong|Grand Bassa|Grand Cape Mount|Grand Gedeh|Grand Kru|Lofa|Margibi|Maryland|Montserrado|Nimba|River Cess|Sinoe";\r
-aStates[133]="|Ajdabiya|Al 'Aziziyah|Al Fatih|Al Jabal al Akhdar|Al Jufrah|Al Khums|Al Kufrah|An Nuqat al Khams|Ash Shati'|Awbari|Az Zawiyah|Banghazi|Darnah|Ghadamis|Gharyan|Misratah|Murzuq|Sabha|Sawfajjin|Surt|Tarabulus|Tarhunah|Tubruq|Yafran|Zlitan";\r
-aStates[134]="|Balzers|Eschen|Gamprin|Mauren|Planken|Ruggell|Schaan|Schellenberg|Triesen|Triesenberg|Vaduz";\r
-aStates[135]="|Akmenes Rajonas|Alytaus Rajonas|Alytus|Anyksciu Rajonas|Birstonas|Birzu Rajonas|Druskininkai|Ignalinos Rajonas|Jonavos Rajonas|Joniskio Rajonas|Jurbarko Rajonas|Kaisiadoriu Rajonas|Kaunas|Kauno Rajonas|Kedainiu Rajonas|Kelmes Rajonas|Klaipeda|Klaipedos Rajonas|Kretingos Rajonas|Kupiskio Rajonas|Lazdiju Rajonas|Marijampole|Marijampoles Rajonas|Mazeikiu Rajonas|Moletu Rajonas|Neringa Pakruojo Rajonas|Palanga|Panevezio Rajonas|Panevezys|Pasvalio Rajonas|Plunges Rajonas|Prienu Rajonas|Radviliskio Rajonas|Raseiniu Rajonas|Rokiskio Rajonas|Sakiu Rajonas|Salcininku Rajonas|Siauliai|Siauliu Rajonas|Silales Rajonas|Silutes Rajonas|Sirvintu Rajonas|Skuodo Rajonas|Svencioniu Rajonas|Taurages Rajonas|Telsiu Rajonas|Traku Rajonas|Ukmerges Rajonas|Utenos Rajonas|Varenos Rajonas|Vilkaviskio Rajonas|Vilniaus Rajonas|Vilnius|Zarasu Rajonas";\r
-aStates[136]="|Diekirch|Grevenmacher|Luxembourg";\r
-aStates[137]="|Macau";\r
-aStates[138]="|Aracinovo|Bac|Belcista|Berovo|Bistrica|Bitola|Blatec|Bogdanci|Bogomila|Bogovinje|Bosilovo|Brvenica|Cair (Skopje)|Capari|Caska|Cegrane|Centar (Skopje)|Centar Zupa|Cesinovo|Cucer-Sandevo|Debar|Delcevo|Delogozdi|Demir Hisar|Demir Kapija|Dobrusevo|Dolna Banjica|Dolneni|Dorce Petrov (Skopje)|Drugovo|Dzepciste|Gazi Baba (Skopje)|Gevgelija|Gostivar|Gradsko|Ilinden|Izvor|Jegunovce|Kamenjane|Karbinci|Karpos (Skopje)|Kavadarci|Kicevo|Kisela Voda (Skopje)|Klecevce|Kocani|Konce|Kondovo|Konopiste|Kosel|Kratovo|Kriva Palanka|Krivogastani|Krusevo|Kuklis|Kukurecani|Kumanovo|Labunista|Lipkovo|Lozovo|Lukovo|Makedonska Kamenica|Makedonski Brod|Mavrovi Anovi|Meseista|Miravci|Mogila|Murtino|Negotino|Negotino-Poloska|Novaci|Novo Selo|Oblesevo|Ohrid|Orasac|Orizari|Oslomej|Pehcevo|Petrovec|Plasnia|Podares|Prilep|Probistip|Radovis|Rankovce|Resen|Rosoman|Rostusa|Samokov|Saraj|Sipkovica|Sopiste|Sopotnika|Srbinovo|Star Dojran|Staravina|Staro Nagoricane|Stip|Struga|Strumica|Studenicani|Suto Orizari (Skopje)|Sveti Nikole|Tearce|Tetovo|Topolcani|Valandovo|Vasilevo|Veles|Velesta|Vevcani|Vinica|Vitoliste|Vranestica|Vrapciste|Vratnica|Vrutok|Zajas|Zelenikovo|Zileno|Zitose|Zletovo|Zrnovci";\r
-aStates[139]="|Antananarivo|Antsiranana|Fianarantsoa|Mahajanga|Toamasina|Toliara";\r
-aStates[140]="|Balaka|Blantyre|Chikwawa|Chiradzulu|Chitipa|Dedza|Dowa|Karonga|Kasungu|Likoma|Lilongwe|Machinga (Kasupe)|Mangochi|Mchinji|Mulanje|Mwanza|Mzimba|Nkhata Bay|Nkhotakota|Nsanje|Ntcheu|Ntchisi|Phalombe|Rumphi|Salima|Thyolo|Zomba";\r
-aStates[141]="|Johor|Kedah|Kelantan|Labuan|Melaka|Negeri Sembilan|Pahang|Perak|Perlis|Pulau Pinang|Sabah|Sarawak|Selangor|Terengganu|Wilayah Persekutuan";\r
-aStates[142]="|Alifu|Baa|Dhaalu|Faafu|Gaafu Alifu|Gaafu Dhaalu|Gnaviyani|Haa Alifu|Haa Dhaalu|Kaafu|Laamu|Lhaviyani|Maale|Meemu|Noonu|Raa|Seenu|Shaviyani|Thaa|Vaavu";\r
-aStates[143]="|Gao|Kayes|Kidal|Koulikoro|Mopti|Segou|Sikasso|Tombouctou";\r
-aStates[144]="|Valletta";\r
-aStates[145]="|Man, Isle of";\r
-aStates[146]="|Ailinginae|Ailinglaplap|Ailuk|Arno|Aur|Bikar|Bikini|Bokak|Ebon|Enewetak|Erikub|Jabat|Jaluit|Jemo|Kili|Kwajalein|Lae|Lib|Likiep|Majuro|Maloelap|Mejit|Mili|Namorik|Namu|Rongelap|Rongrik|Toke|Ujae|Ujelang|Utirik|Wotho|Wotje";\r
-aStates[147]="|Martinique";\r
-aStates[148]="|Adrar|Assaba|Brakna|Dakhlet Nouadhibou|Gorgol|Guidimaka|Hodh Ech Chargui|Hodh El Gharbi|Inchiri|Nouakchott|Tagant|Tiris Zemmour|Trarza";\r
-aStates[149]="|Agalega Islands|Black River|Cargados Carajos Shoals|Flacq|Grand Port|Moka|Pamplemousses|Plaines Wilhems|Port Louis|Riviere du Rempart|Rodrigues|Savanne";\r
-aStates[150]="|Mayotte";\r
-aStates[151]="|Aguascalientes|Baja California|Baja California Sur|Campeche|Chiapas|Chihuahua|Coahuila de Zaragoza|Colima|Distrito Federal|Durango|Guanajuato|Guerrero|Hidalgo|Jalisco|Mexico|Michoacan de Ocampo|Morelos|Nayarit|Nuevo Leon|Oaxaca|Puebla|Queretaro de Arteaga|Quintana Roo|San Luis Potosi|Sinaloa|Sonora|Tabasco|Tamaulipas|Tlaxcala|Veracruz-Llave|Yucatan|Zacatecas";\r
-aStates[152]="|Chuuk (Truk)|Kosrae|Pohnpei|Yap";\r
-aStates[153]="|Midway Islands";\r
-aStates[154]="|Balti|Cahul|Chisinau (City)|Chisinau|Dubasari|Edinet|Gagauzia|Lapusna|Orhei|Soroca|Tighina|Ungheni";\r
-aStates[155]="|Fontvieille|La Condamine|Monaco-Ville|Monte-Carlo";\r
-aStates[156]="|Arhangay|Bayan-Olgiy|Bayanhongor|Bulgan|Darhan|Dornod|Dornogovi|Dundgovi|Dzavhan|Erdenet|Govi-Altay|Hentiy|Hovd|Hovsgol|Omnogovi|Ovorhangay|Selenge|Suhbaatar|Tov|Ulaanbaatar|Uvs";\r
-aStates[157]="|Saint Anthony|Saint Georges|Saint Peter's";\r
-aStates[158]="|Agadir|Al Hoceima|Azilal|Ben Slimane|Beni Mellal|Boulemane|Casablanca|Chaouen|El Jadida|El Kelaa des Srarhna|Er Rachidia|Essaouira|Fes|Figuig|Guelmim|Ifrane|Kenitra|Khemisset|Khenifra|Khouribga|Laayoune|Larache|Marrakech|Meknes|Nador|Ouarzazate|Oujda|Rabat-Sale|Safi|Settat|Sidi Kacem|Tan-Tan|Tanger|Taounate|Taroudannt|Tata|Taza|Tetouan|Tiznit";\r
-aStates[159]="|Cabo Delgado|Gaza|Inhambane|Manica|Maputo|Nampula|Niassa|Sofala|Tete|Zambezia";\r
-aStates[160]="|Caprivi|Erongo|Hardap|Karas|Khomas|Kunene|Ohangwena|Okavango|Omaheke|Omusati|Oshana|Oshikoto|Otjozondjupa";\r
-aStates[161]="|Aiwo|Anabar|Anetan|Anibare|Baiti|Boe|Buada|Denigomodu|Ewa|Ijuw|Meneng|Nibok|Uaboe|Yaren";\r
-aStates[162]="|Bagmati|Bheri|Dhawalagiri|Gandaki|Janakpur|Karnali|Kosi|Lumbini|Mahakali|Mechi|Narayani|Rapti|Sagarmatha|Seti";\r
-aStates[163]="|Drenthe|Flevoland|Friesland|Gelderland|Groningen|Limburg|Noord-Brabant|Noord-Holland|Overijssel|Utrecht|Zeeland|Zuid-Holland";\r
-aStates[164]="|Netherlands Antilles";\r
-aStates[165]="|Iles Loyaute|Nord|Sud";\r
-aStates[166]="|Akaroa|Amuri|Ashburton|Bay of Islands|Bruce|Buller|Chatham Islands|Cheviot|Clifton|Clutha|Cook|Dannevirke|Egmont|Eketahuna|Ellesmere|Eltham|Eyre|Featherston|Franklin|Golden Bay|Great Barrier Island|Grey|Hauraki Plains|Hawera|Hawke's Bay|Heathcote|Hikurangi|Hobson|Hokianga|Horowhenua|Hurunui|Hutt|Inangahua|Inglewood|Kaikoura|Kairanga|Kiwitea|Lake|Mackenzie|Malvern|Manaia|Manawatu|Mangonui|Maniototo|Marlborough|Masterton|Matamata|Mount Herbert|Ohinemuri|Opotiki|Oroua|Otamatea|Otorohanga|Oxford|Pahiatua|Paparua|Patea|Piako|Pohangina|Raglan|Rangiora|Rangitikei|Rodney|Rotorua|Runanga|Saint Kilda|Silverpeaks|Southland|Stewart Island|Stratford|Strathallan|Taranaki|Taumarunui|Taupo|Tauranga|Thames-Coromandel|Tuapeka|Vincent|Waiapu|Waiheke|Waihemo|Waikato|Waikohu|Waimairi|Waimarino|Waimate|Waimate West|Waimea|Waipa|Waipawa|Waipukurau|Wairarapa South|Wairewa|Wairoa|Waitaki|Waitomo|Waitotara|Wallace|Wanganui|Waverley|Westland|Whakatane|Whangarei|Whangaroa|Woodville";\r
-aStates[167]="|Atlantico Norte|Atlantico Sur|Boaco|Carazo|Chinandega|Chontales|Esteli|Granada|Jinotega|Leon|Madriz|Managua|Masaya|Matagalpa|Nueva Segovia|Rio San Juan|Rivas";\r
-aStates[168]="|Agadez|Diffa|Dosso|Maradi|Niamey|Tahoua|Tillaberi|Zinder";\r
-aStates[169]="|Abia|Abuja Federal Capital Territory|Adamawa|Akwa Ibom|Anambra|Bauchi|Bayelsa|Benue|Borno|Cross River|Delta|Ebonyi|Edo|Ekiti|Enugu|Gombe|Imo|Jigawa|Kaduna|Kano|Katsina|Kebbi|Kogi|Kwara|Lagos|Nassarawa|Niger|Ogun|Ondo|Osun|Oyo|Plateau|Rivers|Sokoto|Taraba|Yobe|Zamfara";\r
-aStates[170]="|Niue";\r
-aStates[171]="|Norfolk Island";\r
-aStates[172]="|Northern Islands|Rota|Saipan|Tinian";\r
-aStates[173]="|Akershus|Aust-Agder|Buskerud|Finnmark|Hedmark|Hordaland|More og Romsdal|Nord-Trondelag|Nordland|Oppland|Oslo|Ostfold|Rogaland|Sogn og Fjordane|Sor-Trondelag|Telemark|Troms|Vest-Agder|Vestfold";\r
-aStates[174]="|Ad Dakhiliyah|Al Batinah|Al Wusta|Ash Sharqiyah|Az Zahirah|Masqat|Musandam|Zufar";\r
-aStates[175]="|Balochistan|Federally Administered Tribal Areas|Islamabad Capital Territory|North-West Frontier Province|Punjab|Sindh";\r
-aStates[176]="|Aimeliik|Airai|Angaur|Hatobohei|Kayangel|Koror|Melekeok|Ngaraard|Ngarchelong|Ngardmau|Ngatpang|Ngchesar|Ngeremlengui|Ngiwal|Palau Island|Peleliu|Sonsoral|Tobi";\r
-aStates[177]="|Bocas del Toro|Chiriqui|Cocle|Colon|Darien|Herrera|Los Santos|Panama|San Blas|Veraguas";\r
-aStates[178]="|Bougainville|Central|Chimbu|East New Britain|East Sepik|Eastern Highlands|Enga|Gulf|Madang|Manus|Milne Bay|Morobe|National Capital|New Ireland|Northern|Sandaun|Southern Highlands|West New Britain|Western|Western Highlands";\r
-aStates[179]="|Alto Paraguay|Alto Parana|Amambay|Asuncion (city)|Boqueron|Caaguazu|Caazapa|Canindeyu|Central|Concepcion|Cordillera|Guaira|Itapua|Misiones|Neembucu|Paraguari|Presidente Hayes|San Pedro";\r
-aStates[180]="|Amazonas|Ancash|Apurimac|Arequipa|Ayacucho|Cajamarca|Callao|Cusco|Huancavelica|Huanuco|Ica|Junin|La Libertad|Lambayeque|Lima|Loreto|Madre de Dios|Moquegua|Pasco|Piura|Puno|San Martin|Tacna|Tumbes|Ucayali";\r
-aStates[181]="|Abra|Agusan del Norte|Agusan del Sur|Aklan|Albay|Angeles|Antique|Aurora|Bacolod|Bago|Baguio|Bais|Basilan|Basilan City|Bataan|Batanes|Batangas|Batangas City|Benguet|Bohol|Bukidnon|Bulacan|Butuan|Cabanatuan|Cadiz|Cagayan|Cagayan de Oro|Calbayog|Caloocan|Camarines Norte|Camarines Sur|Camiguin|Canlaon|Capiz|Catanduanes|Cavite|Cavite City|Cebu|Cebu City|Cotabato|Dagupan|Danao|Dapitan|Davao City Davao|Davao del Sur|Davao Oriental|Dipolog|Dumaguete|Eastern Samar|General Santos|Gingoog|Ifugao|Iligan|Ilocos Norte|Ilocos Sur|Iloilo|Iloilo City|Iriga|Isabela|Kalinga-Apayao|La Carlota|La Union|Laguna|Lanao del Norte|Lanao del Sur|Laoag|Lapu-Lapu|Legaspi|Leyte|Lipa|Lucena|Maguindanao|Mandaue|Manila|Marawi|Marinduque|Masbate|Mindoro Occidental|Mindoro Oriental|Misamis Occidental|Misamis Oriental|Mountain|Naga|Negros Occidental|Negros Oriental|North Cotabato|Northern Samar|Nueva Ecija|Nueva Vizcaya|Olongapo|Ormoc|Oroquieta|Ozamis|Pagadian|Palawan|Palayan|Pampanga|Pangasinan|Pasay|Puerto Princesa|Quezon|Quezon City|Quirino|Rizal|Romblon|Roxas|Samar|San Carlos (in Negros Occidental)|San Carlos (in Pangasinan)|San Jose|San Pablo|Silay|Siquijor|Sorsogon|South Cotabato|Southern Leyte|Sultan Kudarat|Sulu|Surigao|Surigao del Norte|Surigao del Sur|Tacloban|Tagaytay|Tagbilaran|Tangub|Tarlac|Tawitawi|Toledo|Trece Martires|Zambales|Zamboanga|Zamboanga del Norte|Zamboanga del Sur";\r
-aStates[182]="|Pitcaim Islands";\r
-aStates[183]="|Dolnoslaskie|Kujawsko-Pomorskie|Lodzkie|Lubelskie|Lubuskie|Malopolskie|Mazowieckie|Opolskie|Podkarpackie|Podlaskie|Pomorskie|Slaskie|Swietokrzyskie|Warminsko-Mazurskie|Wielkopolskie|Zachodniopomorskie";\r
-aStates[184]="|Acores (Azores)|Aveiro|Beja|Braga|Braganca|Castelo Branco|Coimbra|Evora|Faro|Guarda|Leiria|Lisboa|Madeira|Portalegre|Porto|Santarem|Setubal|Viana do Castelo|Vila Real|Viseu";\r
-aStates[185]="|Adjuntas|Aguada|Aguadilla|Aguas Buenas|Aibonito|Anasco|Arecibo|Arroyo|Barceloneta|Barranquitas|Bayamon|Cabo Rojo|Caguas|Camuy|Canovanas|Carolina|Catano|Cayey|Ceiba|Ciales|Cidra|Coamo|Comerio|Corozal|Culebra|Dorado|Fajardo|Florida|Guanica|Guayama|Guayanilla|Guaynabo|Gurabo|Hatillo|Hormigueros|Humacao|Isabela|Jayuya|Juana Diaz|Juncos|Lajas|Lares|Las Marias|Las Piedras|Loiza|Luquillo|Manati|Maricao|Maunabo|Mayaguez|Moca|Morovis|Naguabo|Naranjito|Orocovis|Patillas|Penuelas|Ponce|Quebradillas|Rincon|Rio Grande|Sabana Grande|Salinas|San German|San Juan|San Lorenzo|San Sebastian|Santa Isabel|Toa Alta|Toa Baja|Trujillo Alto|Utuado|Vega Alta|Vega Baja|Vieques|Villalba|Yabucoa|Yauco";\r
-aStates[186]="|Ad Dawhah|Al Ghuwayriyah|Al Jumayliyah|Al Khawr|Al Wakrah|Ar Rayyan|Jarayan al Batinah|Madinat ash Shamal|Umm Salal";\r
-aStates[187]="|Réunion";\r
-aStates[188]="|Alba|Arad|Arges|Bacau|Bihor|Bistrita-Nasaud|Botosani|Braila|Brasov|Bucuresti|Buzau|Calarasi|Caras-Severin|Cluj|Constanta|Covasna|Dimbovita|Dolj|Galati|Giurgiu|Gorj|Harghita|Hunedoara|Ialomita|Iasi|Maramures|Mehedinti|Mures|Neamt|Olt|Prahova|Salaj|Satu Mare|Sibiu|Suceava|Teleorman|Timis|Tulcea|Vaslui|Vilcea|Vrancea";\r
-aStates[189]="|Adygeya (Maykop)|Aginskiy Buryatskiy (Aginskoye)|Altay (Gorno-Altaysk)|Altayskiy (Barnaul)|Amurskaya (Blagoveshchensk)|Arkhangel'skaya|Astrakhanskaya|Bashkortostan (Ufa)|Belgorodskaya|Bryanskaya|Buryatiya (Ulan-Ude)|Chechnya (Groznyy)|Chelyabinskaya|Chitinskaya|Chukotskiy (Anadyr')|Chuvashiya (Cheboksary)|Dagestan (Makhachkala)|Evenkiyskiy (Tura)|Ingushetiya (Nazran')|Irkutskaya|Ivanovskaya|Kabardino-Balkariya (Nal'chik)|Kaliningradskaya|Kalmykiya (Elista)|Kaluzhskaya|Kamchatskaya (Petropavlovsk-Kamchatskiy)|Karachayevo-Cherkesiya (Cherkessk)|Kareliya (Petrozavodsk)|Kemerovskaya|Khabarovskiy|Khakasiya (Abakan)|Khanty-Mansiyskiy (Khanty-Mansiysk)|Kirovskaya|Komi (Syktyvkar)|Komi-Permyatskiy (Kudymkar)|Koryakskiy (Palana)|Kostromskaya|Krasnodarskiy|Krasnoyarskiy|Kurganskaya|Kurskaya|Leningradskaya|Lipetskaya|Magadanskaya|Mariy-El (Yoshkar-Ola)|Mordoviya (Saransk)|Moskovskaya|Moskva (Moscow)|Murmanskaya|Nenetskiy (Nar'yan-Mar)|Nizhegorodskaya|Novgorodskaya|Novosibirskaya|Omskaya|Orenburgskaya|Orlovskaya (Orel)|Penzenskaya|Permskaya|Primorskiy (Vladivostok)|Pskovskaya|Rostovskaya|Ryazanskaya|Sakha (Yakutsk)|Sakhalinskaya (Yuzhno-Sakhalinsk)|Samarskaya|Sankt-Peterburg (Saint Petersburg)|Saratovskaya|Severnaya Osetiya-Alaniya [North Ossetia] (Vladikavkaz)|Smolenskaya|Stavropol'skiy|Sverdlovskaya (Yekaterinburg)|Tambovskaya|Tatarstan (Kazan')|Taymyrskiy (Dudinka)|Tomskaya|Tul'skaya|Tverskaya|Tyumenskaya|Tyva (Kyzyl)|Udmurtiya (Izhevsk)|Ul'yanovskaya|Ust'-Ordynskiy Buryatskiy (Ust'-Ordynskiy)|Vladimirskaya|Volgogradskaya|Vologodskaya|Voronezhskaya|Yamalo-Nenetskiy (Salekhard)|Yaroslavskaya|Yevreyskaya";\r
-aStates[190]="|Butare|Byumba|Cyangugu|Gikongoro|Gisenyi|Gitarama|Kibungo|Kibuye|Kigali Rurale|Kigali-ville|Ruhengeri|Umutara";\r
-aStates[191]="|Ascension|Saint Helena|Tristan da Cunha";\r
-aStates[192]="|Christ Church Nichola Town|Saint Anne Sandy Point|Saint George Basseterre|Saint George Gingerland|Saint James Windward|Saint John Capisterre|Saint John Figtree|Saint Mary Cayon|Saint Paul Capisterre|Saint Paul Charlestown|Saint Peter Basseterre|Saint Thomas Lowland|Saint Thomas Middle Island|Trinity Palmetto Point";\r
-aStates[193]="|Anse-la-Raye|Castries|Choiseul|Dauphin|Dennery|Gros Islet|Laborie|Micoud|Praslin|Soufriere|Vieux Fort";\r
-aStates[194]="|Miquelon|Saint Pierre";\r
-aStates[195]="|Charlotte|Grenadines|Saint Andrew|Saint David|Saint George|Saint Patrick";\r
-aStates[196]="|A'ana|Aiga-i-le-Tai|Atua|Fa'asaleleaga|Gaga'emauga|Gagaifomauga|Palauli|Satupa'itea|Tuamasaga|Va'a-o-Fonoti|Vaisigano";\r
-aStates[197]="|Acquaviva|Borgo Maggiore|Chiesanuova|Domagnano|Faetano|Fiorentino|Monte Giardino|San Marino|Serravalle";\r
-aStates[198]="|Principe|Sao Tome";\r
-aStates[199]="|'Asir|Al Bahah|Al Hudud ash Shamaliyah|Al Jawf|Al Madinah|Al Qasim|Ar Riyad|Ash Sharqiyah (Eastern Province)|Ha'il|Jizan|Makkah|Najran|Tabuk";\r
-aStates[200]="|Aberdeenshire|Angus|Argyll|Ayrshire|Banffshire|Berwickshire|Bute|Caithness|Clackmannanshire|Cromartyshire|Dumfriesshire|Dunbartonshire|Dundee City|East Lothian|Edinburgh|Fife|Glasgow City|Inverness-shire|Kincardineshire|Kinross-shire|Kirkcudbrightshire|Lanarkshire|Midlothian|Moray|Nairnshire|Orkney Islands|Peeblesshire|Perthshire|Renfrewshire|Ross and Cromarty|Ross-shire|Roxburghshire|Selkirkshire|Shetland Islands|Stirlingshire|Sutherland|West Lothian|Wigtownshire";\r
-aStates[201]="|Dakar|Diourbel|Fatick|Kaolack|Kolda|Louga|Saint-Louis|Tambacounda|Thies|Ziguinchor";\r
-aStates[202]="|Anse aux Pins|Anse Boileau|Anse Etoile|Anse Louis|Anse Royale|Baie Lazare|Baie Sainte Anne|Beau Vallon|Bel Air|Bel Ombre|Cascade|Glacis|Grand' Anse (on Mahe)|Grand' Anse (on Praslin)|La Digue|La Riviere Anglaise|Mont Buxton|Mont Fleuri|Plaisance|Pointe La Rue|Port Glaud|Saint Louis|Takamaka";\r
-aStates[203]="|Eastern|Northern|Southern|Western";\r
-aStates[204]="|Singapore";\r
-aStates[205]="|Banskobystricky|Bratislavsky|Kosicky|Nitriansky|Presovsky|Trenciansky|Trnavsky|Zilinsky";\r
-aStates[206]="|Ajdovscina|Beltinci|Bled|Bohinj|Borovnica|Bovec|Brda|Brezice|Brezovica|Cankova-Tisina|Celje|Cerklje na Gorenjskem|Cerknica|Cerkno|Crensovci|Crna na Koroskem|Crnomelj|Destrnik-Trnovska Vas|Divaca|Dobrepolje|Dobrova-Horjul-Polhov Gradec|Dol pri Ljubljani|Domzale|Dornava|Dravograd|Duplek|Gorenja Vas-Poljane|Gorisnica|Gornja Radgona|Gornji Grad|Gornji Petrovci|Grosuplje|Hodos Salovci|Hrastnik|Hrpelje-Kozina|Idrija|Ig|Ilirska Bistrica|Ivancna Gorica|Izola|Jesenice|Jursinci|Kamnik|Kanal|Kidricevo|Kobarid|Kobilje|Kocevje|Komen|Koper|Kozje|Kranj|Kranjska Gora|Krsko|Kungota|Kuzma|Lasko|Lenart|Lendava|Litija|Ljubljana|Ljubno|Ljutomer|Logatec|Loska Dolina|Loski Potok|Luce|Lukovica|Majsperk|Maribor|Medvode|Menges|Metlika|Mezica|Miren-Kostanjevica|Mislinja|Moravce|Moravske Toplice|Mozirje|Murska Sobota|Muta|Naklo|Nazarje|Nova Gorica|Novo Mesto|Odranci|Ormoz|Osilnica|Pesnica|Piran|Pivka|Podcetrtek|Podvelka-Ribnica|Postojna|Preddvor|Ptuj|Puconci|Race-Fram|Radece|Radenci|Radlje ob Dravi|Radovljica|Ravne-Prevalje|Ribnica|Rogasevci|Rogaska Slatina|Rogatec|Ruse|Semic|Sencur|Sentilj|Sentjernej|Sentjur pri Celju|Sevnica|Sezana|Skocjan|Skofja Loka|Skofljica|Slovenj Gradec|Slovenska Bistrica|Slovenske Konjice|Smarje pri Jelsah|Smartno ob Paki|Sostanj|Starse|Store|Sveti Jurij|Tolmin|Trbovlje|Trebnje|Trzic|Turnisce|Velenje|Velike Lasce|Videm|Vipava|Vitanje|Vodice|Vojnik|Vrhnika|Vuzenica|Zagorje ob Savi|Zalec|Zavrc|Zelezniki|Ziri|Zrece";\r
-aStates[207]="|Bellona|Central|Choiseul (Lauru)|Guadalcanal|Honiara|Isabel|Makira|Malaita|Rennell|Temotu|Western";\r
-aStates[208]="|Awdal|Bakool|Banaadir|Bari|Bay|Galguduud|Gedo|Hiiraan|Jubbada Dhexe|Jubbada Hoose|Mudug|Nugaal|Sanaag|Shabeellaha Dhexe|Shabeellaha Hoose|Sool|Togdheer|Woqooyi Galbeed";\r
-aStates[209]="|Eastern Cape|Free State|Gauteng|KwaZulu-Natal|Mpumalanga|North-West|Northern Cape|Northern Province|Western Cape";\r
-aStates[210]="|Bird Island|Bristol Island|Clerke Rocks|Montagu Island|Saunders Island|South Georgia|Southern Thule|Traversay Islands";\r
-aStates[211]="|Andalucia|Aragon|Asturias|Ceuta|Islas Baleares|Islas Chafarinas|Islas Canarias|Cantabria|Castilla y Leon|Castilla-La Mancha|Catalunya|Extremadura|Galicia|La Rioja|Madrid|Melilla|Murcia|Navarra|Pais Vasco|Peñón de Alhucemas|Peñón de Vélez de la Gomera|Valencia";\r
-aStates[212]="|Spratly Islands";\r
-aStates[213]="|Central|Eastern|North Central|North Eastern|North Western|Northern|Sabaragamuwa|Southern|Uva|Western";\r
-aStates[214]="|A'ali an Nil|Al Bahr al Ahmar|Al Buhayrat|Al Jazirah|Al Khartum|Al Qadarif|Al Wahdah|An Nil al Abyad|An Nil al Azraq|Ash Shamaliyah|Bahr al Jabal|Gharb al Istiwa'iyah|Gharb Bahr al Ghazal|Gharb Darfur|Gharb Kurdufan|Janub Darfur|Janub Kurdufan|Junqali|Kassala|Nahr an Nil|Shamal Bahr al Ghazal|Shamal Darfur|Shamal Kurdufan|Sharq al Istiwa'iyah|Sinnar|Warab";\r
-aStates[215]="|Brokopondo|Commewijne|Coronie|Marowijne|Nickerie|Para|Paramaribo|Saramacca|Sipaliwini|Wanica";\r
-aStates[216]="|Barentsoya|Bjornoya|Edgeoya|Hopen|Kvitoya|Nordaustandet|Prins Karls Forland|Spitsbergen";\r
-aStates[217]="|Hhohho|Lubombo|Manzini|Shiselweni";\r
-aStates[218]="|Blekinge|Dalarnas|Gavleborgs|Gotlands|Hallands|Jamtlands|Jonkopings|Kalmar|Kronobergs|Norrbottens|Orebro|Ostergotlands|Skane|Sodermanlands|Stockholms|Uppsala|Varmlands|Vasterbottens|Vasternorrlands|Vastmanlands|Vastra Gotalands";\r
-aStates[219]="|Aargau|Ausser-Rhoden|Basel-Landschaft|Basel-Stadt|Bern|Fribourg|Geneve|Glarus|Graubunden|Inner-Rhoden|Jura|Luzern|Neuchatel|Nidwalden|Obwalden|Sankt Gallen|Schaffhausen|Schwyz|Solothurn|Thurgau|Ticino|Uri|Valais|Vaud|Zug|Zurich";\r
-aStates[220]="|Al Hasakah|Al Ladhiqiyah|Al Qunaytirah|Ar Raqqah|As Suwayda'|Dar'a|Dayr az Zawr|Dimashq|Halab|Hamah|Hims|Idlib|Rif Dimashq|Tartus";\r
-aStates[221]="|Chang-hua|Chi-lung|Chia-i (City)|Chia-i|Chung-hsing-hsin-ts'un|Hsin-chu (City)|Hsin-chu|Hua-lien|I-lan|Kao-hsiung (City)|Kao-hsiung|Miao-li|Nan-t'ou|P'eng-hu|P'ing-tung|T'ai-chung (City)|T'ai-chung|T'ai-nan (City)|T'ai-nan|T'ai-pei (City)|T'ai-pei|T'ai-tung|T'ao-yuan|Yun-lin";\r
-aStates[222]="|Viloyati Khatlon|Viloyati Leninobod|Viloyati Mukhtori Kuhistoni Badakhshon";\r
-aStates[223]="|Arusha|Dar es Salaam|Dodoma|Iringa|Kagera|Kigoma|Kilimanjaro|Lindi|Mara|Mbeya|Morogoro|Mtwara|Mwanza|Pemba North|Pemba South|Pwani|Rukwa|Ruvuma|Shinyanga|Singida|Tabora|Tanga|Zanzibar Central/South|Zanzibar North|Zanzibar Urban/West";\r
-aStates[224]="|Amnat Charoen|Ang Thong|Buriram|Chachoengsao|Chai Nat|Chaiyaphum|Chanthaburi|Chiang Mai|Chiang Rai|Chon Buri|Chumphon|Kalasin|Kamphaeng Phet|Kanchanaburi|Khon Kaen|Krabi|Krung Thep Mahanakhon (Bangkok)|Lampang|Lamphun|Loei|Lop Buri|Mae Hong Son|Maha Sarakham|Mukdahan|Nakhon Nayok|Nakhon Pathom|Nakhon Phanom|Nakhon Ratchasima|Nakhon Sawan|Nakhon Si Thammarat|Nan|Narathiwat|Nong Bua Lamphu|Nong Khai|Nonthaburi|Pathum Thani|Pattani|Phangnga|Phatthalung|Phayao|Phetchabun|Phetchaburi|Phichit|Phitsanulok|Phra Nakhon Si Ayutthaya|Phrae|Phuket|Prachin Buri|Prachuap Khiri Khan|Ranong|Ratchaburi|Rayong|Roi Et|Sa Kaeo|Sakon Nakhon|Samut Prakan|Samut Sakhon|Samut Songkhram|Sara Buri|Satun|Sing Buri|Sisaket|Songkhla|Sukhothai|Suphan Buri|Surat Thani|Surin|Tak|Trang|Trat|Ubon Ratchathani|Udon Thani|Uthai Thani|Uttaradit|Yala|Yasothon";\r
-aStates[225]="|Tobago";\r
-aStates[226]="|De La Kara|Des Plateaux|Des Savanes|Du Centre|Maritime";\r
-aStates[227]="|Atafu|Fakaofo|Nukunonu";\r
-aStates[228]="|Ha'apai|Tongatapu|Vava'u";\r
-aStates[229]="|Arima|Caroni|Mayaro|Nariva|Port-of-Spain|Saint Andrew|Saint David|Saint George|Saint Patrick|San Fernando|Victoria";\r
-aStates[230]="|Ariana|Beja|Ben Arous|Bizerte|El Kef|Gabes|Gafsa|Jendouba|Kairouan|Kasserine|Kebili|Mahdia|Medenine|Monastir|Nabeul|Sfax|Sidi Bou Zid|Siliana|Sousse|Tataouine|Tozeur|Tunis|Zaghouan";\r
-aStates[231]="|Adana|Adiyaman|Afyon|Agri|Aksaray|Amasya|Ankara|Antalya|Ardahan|Artvin|Aydin|Balikesir|Bartin|Batman|Bayburt|Bilecik|Bingol|Bitlis|Bolu|Burdur|Bursa|Canakkale|Cankiri|Corum|Denizli|Diyarbakir|Duzce|Edirne|Elazig|Erzincan|Erzurum|Eskisehir|Gaziantep|Giresun|Gumushane|Hakkari|Hatay|Icel|Igdir|Isparta|Istanbul|Izmir|Kahramanmaras|Karabuk|Karaman|Kars|Kastamonu|Kayseri|Kilis|Kirikkale|Kirklareli|Kirsehir|Kocaeli|Konya|Kutahya|Malatya|Manisa|Mardin|Mugla|Mus|Nevsehir|Nigde|Ordu|Osmaniye|Rize|Sakarya|Samsun|Sanliurfa|Siirt|Sinop|Sirnak|Sivas|Tekirdag|Tokat|Trabzon|Tunceli|Usak|Van|Yalova|Yozgat|Zonguldak";\r
-aStates[232]="|Ahal Welayaty|Balkan Welayaty|Dashhowuz Welayaty|Lebap Welayaty|Mary Welayaty";\r
-aStates[233]="|Tuvalu";\r
-aStates[234]="|Adjumani|Apac|Arua|Bugiri|Bundibugyo|Bushenyi|Busia|Gulu|Hoima|Iganga|Jinja|Kabale|Kabarole|Kalangala|Kampala|Kamuli|Kapchorwa|Kasese|Katakwi|Kibale|Kiboga|Kisoro|Kitgum|Kotido|Kumi|Lira|Luwero|Masaka|Masindi|Mbale|Mbarara|Moroto|Moyo|Mpigi|Mubende|Mukono|Nakasongola|Nebbi|Ntungamo|Pallisa|Rakai|Rukungiri|Sembabule|Soroti|Tororo";\r
-aStates[235]="|Avtonomna Respublika Krym (Simferopol')|Cherkas'ka (Cherkasy)|Chernihivs'ka (Chernihiv)|Chernivets'ka (Chernivtsi)|Dnipropetrovs'ka (Dnipropetrovs'k)|Donets'ka (Donets'k)|Ivano-Frankivs'ka (Ivano-Frankivs'k)|Kharkivs'ka (Kharkiv)|Khersons'ka (Kherson)|Khmel'nyts'ka (Khmel'nyts'kyy)|Kirovohrads'ka (Kirovohrad)|Kyyiv|Kyyivs'ka (Kiev)|L'vivs'ka (L'viv)|Luhans'ka (Luhans'k)|Mykolayivs'ka (Mykolayiv)|Odes'ka (Odesa)|Poltavs'ka (Poltava)|Rivnens'ka (Rivne)|Sevastopol'|Sums'ka (Sumy)|Ternopil's'ka (Ternopil')|Vinnyts'ka (Vinnytsya)|Volyns'ka (Luts'k)|Zakarpats'ka (Uzhhorod)|Zaporiz'ka (Zaporizhzhya)|Zhytomyrs'ka (Zhytomyr)"\r
-aStates[236]="|'Ajman|Abu Zaby (Abu Dhabi)|Al Fujayrah|Ash Shariqah (Sharjah)|Dubayy (Dubai)|Ra's al Khaymah|Umm al Qaywayn";\r
-aStates[237]="|Aberdeen|Aberdeenshire|Anglesey|Angus|Antrim|Argyl|Armagh|Avon|Ayrshire|Banffshire|Bedfordshire|Belfast|Berwickshire|Brecknockshire|Bristol|Buckinghamshire|Bute|Caernarfonshire|Cardiganshire|Caithness|Cambridgeshire|Carmarthenshire|Chesire|Clackmannashire|Cleveland|Clwyd|Cornwall|Cromartyshire|Cumberland|Cumbria|Denbighshire|Derbyshire|Devon|Dfyed|Dorset|Down|Dumfriesshire|Dunbartonshire|Dundee|Durham|East Lothian|East Suffolk|Derry|East Sussex|Edinburgh|Essex|Fermanagh|Fife|Flintshire|Glasgow|Glamorgan|Gloucestershire|Greater London|Greater Manchester|Gwent|Gwynedd|Hampshire|Hereford and Worcester|Herefordshire|Inverness-shire|Hertfordshire|Humberside|Huntingdon and Peterborough|Huntingdonshire|Isle of Ely|Isle of Wight|Kent|Kincardineshire|Kincross-shire|Kirkcudbrightshire|Lanarkshire|Lancashire|Leicestershire|Lincolnshire|London|Londonderry|Merionethshire|Merseyside|Middlesex|Mid Glamorgan|Midlothian|Monmouthshire|Montgomeryshire|Moray|Nairnshire|Norfolk|Northamptonshire|Northumberland|North Humberside|North Yorkshire|Nottinghamshire|Orkney|Oxfordshire|Peeblesshire|Pembrokeshire|Perthshire|Powys|Radnorshire|Renfrewshire|Ross And Cromarty|Ross-shire|Roxburghshire|Selkirkshire|Shetland|Stirlingshire|Sutherland|Soke of Peterborough|Rutland|Shropshire|Somerset|South Glamorgan|South Humberside|South Yorkshire|Staffordshite|Suffolk|Surrey|Sussex|Tyne and Wear|Tyrone|Warwickshire|West Glamorgan|West Lothian|West Midlands|Westmorland|West Suffolk|West Sussex|West Yorkshire|Wigtownshire|Wiltshire|Worcestershire|Yorkshire";\r
-aStates[238]="|Artigas|Canelones|Cerro Largo|Colonia|Durazno|Flores|Florida|Lavalleja|Maldonado|Montevideo|Paysandu|Rio Negro|Rivera|Rocha|Salto|San Jose|Soriano|Tacuarembo|Treinta y Tres";\r
-aStates[239]="|Alabama|Alaska|Arizona|Arkansas|California|Colorado|Connecticut|Delaware|District of Columbia|Florida|Georgia|Hawaii|Idaho|Illinois|Indiana|Iowa|Kansas|Kentucky|Louisiana|Maine|Maryland|Massachusetts|Michigan|Minnesota|Mississippi|Missouri|Montana|Nebraska|Nevada|New Hampshire|New Jersey|New Mexico|New York|North Carolina|North Dakota|Ohio|Oklahoma|Oregon|Pennsylvania|Rhode Island|South Carolina|South Dakota|Tennessee|Texas|Utah|Vermont|Virginia|Washington|West Virginia|Wisconsin|Wyoming";\r
-aStates[240]="|Andijon Wiloyati|Bukhoro Wiloyati|Farghona Wiloyati|Jizzakh Wiloyati|Khorazm Wiloyati (Urganch)|Namangan Wiloyati|Nawoiy Wiloyati|Qashqadaryo Wiloyati (Qarshi)|Qoraqalpoghiston (Nukus)|Samarqand Wiloyati|Sirdaryo Wiloyati (Guliston)|Surkhondaryo Wiloyati (Termiz)|Toshkent Shahri|Toshkent Wiloyati";\r
-aStates[241]="|Malampa|Penama|Sanma|Shefa|Tafea|Torba";\r
-aStates[242]="|Amazonas|Anzoategui|Apure|Aragua|Barinas|Bolivar|Carabobo|Cojedes|Delta Amacuro|Dependencias Federales|Distrito Federal|Falcon|Guarico|Lara|Merida|Miranda|Monagas|Nueva Esparta|Portuguesa|Sucre|Tachira|Trujillo|Vargas|Yaracuy|Zulia";\r
-aStates[243]="|An Giang|Ba Ria-Vung Tau|Bac Giang|Bac Kan|Bac Lieu|Bac Ninh|Ben Tre|Binh Dinh|Binh Duong|Binh Phuoc|Binh Thuan|Ca Mau|Can Tho|Cao Bang|Da Nang|Dac Lak|Dong Nai|Dong Thap|Gia Lai|Ha Giang|Ha Nam|Ha Noi|Ha Tay|Ha Tinh|Hai Duong|Hai Phong|Ho Chi Minh|Hoa Binh|Hung Yen|Khanh Hoa|Kien Giang|Kon Tum|Lai Chau|Lam Dong|Lang Son|Lao Cai|Long An|Nam Dinh|Nghe An|Ninh Binh|Ninh Thuan|Phu Tho|Phu Yen|Quang Binh|Quang Nam|Quang Ngai|Quang Ninh|Quang Tri|Soc Trang|Son La|Tay Ninh|Thai Binh|Thai Nguyen|Thanh Hoa|Thua Thien-Hue|Tien Giang|Tra Vinh|Tuyen Quang|Vinh Long|Vinh Phuc|Yen Bai";\r
-aStates[244]="|Saint Croix|Saint John|Saint Thomas";\r
-aStates[245]="|Anglesey|Brecknockshire|Caernfonshire|Cardiganshire|Carmarthenshire|Clwyd|Denbighshire|Dyfed|Flintshire|Glamorgan|Gwent|Gwynedd|Merionethshire|Mid Glamorgan|Monmouthsire|Montgomeryshire|Pembrokeshire|Powys|Radnorshire|South Glamorgan|West Glamorgan";\r
-aStates[246]="|Alo|Sigave|Wallis";\r
-aStates[247]="|West Bank";\r
-aStates[248]="|Western Sahara";\r
-aStates[249]="|'Adan|'Ataq|Abyan|Al Bayda'|Al Hudaydah|Al Jawf|Al Mahrah|Al Mahwit|Dhamar|Hadhramawt|Hajjah|Ibb|Lahij|Ma'rib|Sa'dah|San'a'|Ta'izz";\r
-aStates[250]="|Kosovo|Montenegro|Serbia|Vojvodina";\r
-aStates[251]="|Central|Copperbelt|Eastern|Luapula|Lusaka|North-Western|Northern|Southern|Western";\r
-aStates[252]="|Bulawayo|Harare|ManicalandMashonaland Central|Mashonaland East|Mashonaland West|Masvingo|Matabeleland North|Matabeleland South|Midlands";\r
-aStates[253]="|Self Hosted|Private Server|Architects Of Sleep|Chaos Friends|DFRN|Distributed Friend Network|ErrLock|Free-Beer.ch|Foojbook|Free-Haven|Friendica.eu|Friendika.me.4.it|Friendika - I Ask Questions|Frndc.com|Hikado|Hipatia|Hungerfreunde|Kaluguran Community|Kak Ste|Karl.Markx.pm|Loozah Social Club|MyFriendica.net|MyFriendNetwork|Oi!|OpenMindSpace|Optimistisch|Pplsnet|Recolutionari.es|Repatr.de|SPRACI|Styliztique|Sysfu Social Club|theshi.re|Tumpambae|Uzmiac|Other";\r
-/* \r
- * gArCountryInfo\r
- * (0) Country name\r
- * (1) Name length\r
- * (2) Number of states\r
- * (3) Max state length\r
- */\r
-gLngNumberCountries = sCountryString.split("|").length;\r
-//gArCountryInfo = new Array(gLngNumberCountries);\r
-gArCountryInfo=sCountryString.split("|");\r
-/* \r
- * gArStateInfo[country][statenames][namelengths]\r
- * (0) Country\r
- * (1) States (Multi-sized Array)\r
- *    (0) name\r
- *    (1) nameLength\r
- */\r
-gArStateInfo   = new Array(gLngNumberCountries);\r
-\r
-/* \r
- * fInitgArStateInfo()\r
- * purpose: build gArStateInfo array\r
- * gArStateInfo[Country][States][1]\r
- *   (0) State name\r
- *   (1) State name length\r
- * gArStateInfo[i] is an array of state names\r
- * gArStateInfo[i][j]=state name, name length\r
- */\r
-function fInitgArStateInfo() {\r
- var i=0, j=0, sStateName="", iNumberOfStates=0;\r
- var iMaxLength=0, iLength=0;\r
- var oldNumber=0;\r
\r
- for (i=0;i<gLngNumberCountries;i++) {\r
-  // i is selected country\r
-  iNumberOfStates=aStates[i].split("|").length+1;\r
-  gLngNumberStates=gLngNumberStates+iNumberOfStates;\r
-  gArStateInfo[i]=new Array(iNumberOfStates);\r
-  iMaxLength=0;\r
-  \r
-  // Add the additional information\r
-  for (j=0;j<iNumberOfStates;j++) {\r
-   if (iLength>iMaxLength) {\r
-    iMaxLength=iLength;\r
-    gArStateInfo[i][j][0]=sStateName;\r
-   }\r
-  }\r
-  gArCountryInfo[i][3]=parseInt(iMaxLength);\r
- }\r
- Update_Globals();\r
- return;\r
-}\r
-\r
-/* \r
- * Working on this one.\r
- * Fills in region from the arrays\r
- * \r
- */\r
-function xFillState() {\r
- var i=0;\r
\r
- // reset region\r
- document.form1.region.options.length=0;\r
\r
- // get selected country\r
- gLngSelectedCountry=document.form1.country_name.selectedIndex;\r
\r
- // get number of states for selected country\r
- gLngNumberStates=gArCountryInfo[[gLngSelectedCountry][2]];\r
\r
- // update options in region\r
- for (i=0;i<gLngNumberStates;i++) {\r
-  document.form1.region.options[i]=new\r
-    Option(gArStateInfo[[gLngSelectedCountry][i][0]]);\r
- }\r
- gLngSelectedState=\r
-  document.form1.region.options.selectedIndex;\r
\r
- return;\r
-}\r
-\r
-/* \r
- * FillStates() function works.\r
- * Fills region from aStates\r
- */\r
-function Fill_States(current) {\r
-       var i=0, iLen=0, iNumStates=0;\r
\r
-       // reset region\r
-       document.form1.region.options.length=0;\r
-       // get selected country\r
-       gLngSelectedCountry=document.form1.country_name.selectedIndex;\r
-       iNumStates = aStates[gLngSelectedCountry].split("|").length;\r
\r
-       // update the text boxes\r
-\r
-       // fill the state combobox with the list of states\r
-       for (i=0;i<iNumStates;i++) {\r
-               document.form1.region.options[i]=new \r
-                       Option(aStates[document.form1.country_name.selectedIndex].split("|")[i]);\r
-\r
-               sRegionName=document.form1.region.options[i];\r
-               if(sRegionName.text == current) {\r
-                       document.form1.region.selectedIndex = i;\r
-               }\r
-\r
-       }\r
-\r
-       return;\r
-}\r
-\r
-/*\r
- * FillCountry()\r
- * gArCountryInfo matrix holds the following information:\r
- * (0) Country name\r
- * (1) Name length\r
- * (2) Number of states\r
- * (3) Max state length\r
- */\r
-function Fill_Country(current) {\r
-       var i=0;\r
-       var sCountryName="";\r
\r
-       // reset country_name.options\r
-       document.form1.country_name.options.length=0;\r
-       // get number of countries from the string\r
\r
-       // ----------------------------------------------------\r
-       // gArCountryInfo = new Array(gLngNumberCountries, 4);\r
-       // ----------------------------------------------------\r
-       for (i=0;i<gLngNumberCountries;i++) {\r
-               gArCountryInfo[i]=new Array(4);\r
-       }\r
\r
-       for (i=0;i<gLngNumberCountries;i++) {\r
-               document.form1.country_name.options[i]=new Option(sCountryString.split("|")[i]);\r
-               sCountryName=document.form1.country_name.options[i];\r
-               if(sCountryName.text == current) {\r
-                       document.form1.country_name.selectedIndex = i;\r
-               }\r
-               gArCountryInfo[i]=\r
-                       [sCountryName, \r
-                               parseInt(sCountryName.length),\r
-                               aStates,\r
-                       0];\r
-       }\r
-\r
-       fInitgArStateInfo();\r
-\r
-       return;\r
-}\r
-\r
-function Update_Globals() {\r
-       gLngSelectedCountry=parseInt(document.form1.country_name.selectedIndex);\r
-       gLngSelectedState=parseInt(document.form1.region.selectedIndex);\r
-       return;\r
-}\r
-\r
-\r
-//-->\r
diff --git a/js/filebrowser.js b/js/filebrowser.js
deleted file mode 100644 (file)
index 78cee0e..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-/**\r
- * Filebrowser - Friendica Communications Server\r
- *\r
- * Copyright (c) 2010-2015 the Friendica Project\r
- *\r
- * This program is free software: you can redistribute it and/or modify\r
- * it under the terms of the GNU Affero General Public License as published by\r
- * the Free Software Foundation, either version 3 of the License, or\r
- * (at your option) any later version.\r
- *\r
- * This code handle user interaction for image/file upload/browser dialog.\r
- * Is loaded from filebrowser_plain.tpl\r
- *\r
- * To load filebrowser in colorbox, call\r
- *\r
- *      Dialog.doImageBrowser(eventname, id);\r
- *\r
- * or\r
- *\r
- *      Dialog.doFileBrowser(eventname, id);\r
- *\r
- * where:\r
- *\r
- *             eventname: event name to catch return value\r
- *             id: id returned to event handler\r
- *\r
- * When user select an item, an event in fired in parent page, on body element\r
- * The event is named\r
- *\r
- *             fbrowser.<type>.[<eventname>]\r
- *\r
- * <type> will be one of "image" or "file", and the event handler will\r
- * get the following params:\r
- *\r
- *             filemane: filename of item choosed by user\r
- *             embed: bbcode to embed element into posts\r
- *             id: id from caller code\r
- *\r
- * example:\r
- *\r
- *             // open dialog for select an image for a textarea with id "myeditor"\r
- *             var id="myeditor";\r
- *             Dialog.doImageBrowser("example", id);\r
- *\r
- *             // setup event handler to get user selection\r
- *             $("body").on("fbrowser.image.example", function(event, filename, bbcode, id) {\r
- *                     // close colorbox\r
- *                     $.colorbox.close();\r
- *                     // replace textxarea text with bbcode\r
- *                     $(id).value = bbcode;\r
- *             });\r
- **/\r
-\r
-var FileBrowser = {\r
-       nickname : "",\r
-       type : "",\r
-       event: "",\r
-       id : null,\r
-\r
-       init: function(nickname, type) {\r
-               FileBrowser.nickname = nickname;\r
-               FileBrowser.type = type;\r
-               FileBrowser.event = "fbrowser."+type;\r
-               if (location['hash']!=="") {\r
-                       var h = location['hash'].replace("#","");\r
-                       FileBrowser.event = FileBrowser.event + "." + h.split("-")[0];\r
-                       FileBrowser.id = h.split("-")[1];\r
-               }\r
-\r
-               console.log("FileBrowser:", nickname, type,FileBrowser.event, FileBrowser.id );\r
-\r
-               $(".error a.close").on("click", function(e) {\r
-                       e.preventDefault();\r
-                       $(".error").addClass("hidden");\r
-               });\r
-\r
-               $(".folders a, .path a").on("click", function(e){\r
-                       e.preventDefault();\r
-                       var url = baseurl + "/fbrowser/" + FileBrowser.type + "/" + this.dataset.folder + "?mode=minimal" + location['hash'];\r
-                       location.href = url;\r
-               });\r
-\r
-               $(".photo-album-photo-link").on('click', function(e){\r
-                       e.preventDefault();\r
-\r
-                       var embed = "";\r
-                       if (FileBrowser.type == "image") {\r
-                               embed = "[url="+this.dataset.link+"][img]"+this.dataset.img+"[/img][/url]";\r
-                       }\r
-                       if (FileBrowser.type=="file") {\r
-                               // attachment links are "baseurl/attach/id"; we need id\r
-                               embed = "[attachment]"+this.dataset.link.split("/").pop()+"[/attachment]";\r
-                       }\r
-                       console.log(FileBrowser.event, this.dataset.filename, embed, FileBrowser.id);\r
-                       parent.$("body").trigger(FileBrowser.event, [\r
-                               this.dataset.filename,\r
-                               embed,\r
-                               FileBrowser.id\r
-                       ]);\r
-\r
-               });\r
-\r
-               if ($("#upload-image").length)\r
-                       var image_uploader = new window.AjaxUpload(\r
-                               'upload-image',\r
-                               { action: 'wall_upload/'+FileBrowser.nickname+'?response=json',\r
-                                       name: 'userfile',\r
-                                       responseType: 'json',\r
-                                       onSubmit: function(file,ext) { $('#profile-rotator').show(); $(".error").addClass('hidden'); },\r
-                                       onComplete: function(file,response) {\r
-                                               if (response['error']!= undefined) {\r
-                                                       $(".error span").html(response['error']);\r
-                                                       $(".error").removeClass('hidden');\r
-                                                       $('#profile-rotator').hide();\r
-                                                       return;\r
-                                               }\r
-                                               location = baseurl + "/fbrowser/image/?mode=minimal"+location['hash'];\r
-                                               location.reload(true);\r
-                                       }\r
-                               }\r
-                       );\r
-\r
-               if ($("#upload-file").length)\r
-                       var file_uploader = new window.AjaxUpload(\r
-                               'upload-file',\r
-                               { action: 'wall_attach/'+FileBrowser.nickname+'?response=json',\r
-                                       name: 'userfile',\r
-                                       onSubmit: function(file,ext) { $('#profile-rotator').show(); $(".error").addClass('hidden'); },\r
-                                       onComplete: function(file,response) {\r
-                                               if (response['error']!= undefined) {\r
-                                                       $(".error span").html(response['error']);\r
-                                                       $(".error").removeClass('hidden');\r
-                                                       $('#profile-rotator').hide();\r
-                                                       return;\r
-                                               }\r
-                                               location = baseurl + "/fbrowser/file/?mode=minimal"+location['hash'];\r
-                                               location.reload(true);\r
-                                       }\r
-                               }\r
-               );\r
-       }\r
-};\r
-\r
diff --git a/js/jquery.textinputs.js b/js/jquery.textinputs.js
deleted file mode 100644 (file)
index fd6d145..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- Rangy Text Inputs, a cross-browser textarea and text input library plug-in for jQuery.
-
- Part of Rangy, a cross-browser JavaScript range and selection library
- http://code.google.com/p/rangy/
-
- Depends on jQuery 1.0 or later.
-
- Copyright 2010, Tim Down
- Licensed under the MIT license.
- Version: 0.1.205
- Build date: 5 November 2010
-*/
-(function(n){function o(e,g){var a=typeof e[g];return a==="function"||!!(a=="object"&&e[g])||a=="unknown"}function p(e,g,a){if(g<0)g+=e.value.length;if(typeof a=="undefined")a=g;if(a<0)a+=e.value.length;return{start:g,end:a}}function k(){return typeof document.body=="object"&&document.body?document.body:document.getElementsByTagName("body")[0]}var i,h,q,l,r,s,t,u,m;n(document).ready(function(){function e(a,b){return function(){var c=this.jquery?this[0]:this,d=c.nodeName.toLowerCase();if(c.nodeType==
-1&&(d=="textarea"||d=="input"&&c.type=="text")){c=[c].concat(Array.prototype.slice.call(arguments));c=a.apply(this,c);if(!b)return c}if(b)return this}}var g=document.createElement("textarea");k().appendChild(g);if(typeof g.selectionStart!="undefined"&&typeof g.selectionEnd!="undefined"){i=function(a){return{start:a.selectionStart,end:a.selectionEnd,length:a.selectionEnd-a.selectionStart,text:a.value.slice(a.selectionStart,a.selectionEnd)}};h=function(a,b,c){b=p(a,b,c);a.selectionStart=b.start;a.selectionEnd=
-b.end};m=function(a,b){if(b)a.selectionEnd=a.selectionStart;else a.selectionStart=a.selectionEnd}}else if(o(g,"createTextRange")&&typeof document.selection=="object"&&document.selection&&o(document.selection,"createRange")){i=function(a){var b=0,c=0,d,f,j;if((j=document.selection.createRange())&&j.parentElement()==a){f=a.value.length;d=a.value.replace(/\r\n/g,"\n");c=a.createTextRange();c.moveToBookmark(j.getBookmark());j=a.createTextRange();j.collapse(false);if(c.compareEndPoints("StartToEnd",j)>
--1)b=c=f;else{b=-c.moveStart("character",-f);b+=d.slice(0,b).split("\n").length-1;if(c.compareEndPoints("EndToEnd",j)>-1)c=f;else{c=-c.moveEnd("character",-f);c+=d.slice(0,c).split("\n").length-1}}}return{start:b,end:c,length:c-b,text:a.value.slice(b,c)}};h=function(a,b,c){b=p(a,b,c);c=a.createTextRange();var d=b.start-(a.value.slice(0,b.start).split("\r\n").length-1);c.collapse(true);if(b.start==b.end)c.move("character",d);else{c.moveEnd("character",b.end-(a.value.slice(0,b.end).split("\r\n").length-
-1));c.moveStart("character",d)}c.select()};m=function(a,b){var c=document.selection.createRange();c.collapse(b);c.select()}}else{k().removeChild(g);window.console&&window.console.log&&window.console.log("TextInputs module for Rangy not supported in your browser. Reason: No means of finding text input caret position");return}k().removeChild(g);l=function(a,b,c,d){var f;if(b!=c){f=a.value;a.value=f.slice(0,b)+f.slice(c)}d&&h(a,b,b)};q=function(a){var b=i(a);l(a,b.start,b.end,true)};u=function(a){var b=
-i(a),c;if(b.start!=b.end){c=a.value;a.value=c.slice(0,b.start)+c.slice(b.end)}h(a,b.start,b.start);return b.text};r=function(a,b,c,d){var f=a.value;a.value=f.slice(0,c)+b+f.slice(c);if(d){b=c+b.length;h(a,b,b)}};s=function(a,b){var c=i(a),d=a.value;a.value=d.slice(0,c.start)+b+d.slice(c.end);c=c.start+b.length;h(a,c,c)};t=function(a,b,c){var d=i(a),f=a.value;a.value=f.slice(0,d.start)+b+d.text+c+f.slice(d.end);b=d.start+b.length;h(a,b,b+d.length)};n.fn.extend({getSelection:e(i,false),setSelection:e(h,
-true),collapseSelection:e(m,true),deleteSelectedText:e(q,true),deleteText:e(l,true),extractSelectedText:e(u,false),insertText:e(r,true),replaceSelectedText:e(s,true),surroundSelectedText:e(t,true)})})})(jQuery);
\ No newline at end of file
diff --git a/js/main.js b/js/main.js
deleted file mode 100644 (file)
index 77c240f..0000000
+++ /dev/null
@@ -1,922 +0,0 @@
-       function resizeIframe(obj) {
-               //obj.style.height = 0;
-               _resizeIframe(obj, 0);
-       }
-
-       function _resizeIframe(obj, desth) {
-               var h = obj.style.height;
-               var ch = obj.contentWindow.document.body.scrollHeight;
-               if (h == (ch + 'px')) {
-                       return;
-               }
-               if (desth == ch && ch>0) {
-                       obj.style.height  = ch + 'px';
-               }
-               setTimeout(_resizeIframe, 100, obj, ch);
-       }
-
-       function openClose(theID) {
-               if(document.getElementById(theID).style.display == "block") {
-                       document.getElementById(theID).style.display = "none"
-               }
-               else {
-                       document.getElementById(theID).style.display = "block"
-               }
-       }
-
-       function openMenu(theID) {
-               var el = document.getElementById(theID)
-               if (el) {
-                       el.style.display = "block";
-               }
-       }
-
-       function closeMenu(theID) {
-               var el = document.getElementById(theID)
-               if (el) {
-                       el.style.display = "none";
-               }
-       }
-
-       function decodeHtml(html) {
-               var txt = document.createElement("textarea");
-               txt.innerHTML = html;
-               return txt.value;
-       }
-
-
-       var src = null;
-       var prev = null;
-       var livetime = null;
-       var force_update = false;
-       var stopped = false;
-       var totStopped = false;
-       var timer = null;
-       var pr = 0;
-       var liking = 0;
-       var in_progress = false;
-       var langSelect = false;
-       var commentBusy = false;
-       var last_popup_menu = null;
-       var last_popup_button = null;
-       var lockLoadContent = false;
-
-       $(function() {
-               $.ajaxSetup({cache: false});
-
-               /* setup comment textarea buttons */
-               /* comment textarea buttons needs some "data-*" attributes to work:
-                *              data-role="insert-formatting" : to mark the element as a formatting button
-                *              data-bbcode="<string>" : name of the bbcode element to insert. insertFormatting() will insert it as "[name][/name]"
-                *              data-id="<string>" : id of the comment, used to find other comment-related element, like the textarea
-                * */
-               $('body').on('click','[data-role="insert-formatting"]', function(e) {
-                       e.preventDefault();
-                       var o = $(this);
-                       var bbcode  = o.data('bbcode');
-                       var id = o.data('id');
-                       if (bbcode=="img") {
-                               Dialog.doImageBrowser("comment", id);
-                               return;
-                       }
-                       insertFormatting(bbcode, id);
-               });
-
-               /* event from comment textarea button popups */
-               /* insert returned bbcode at cursor position or replace selected text */
-               $("body").on("fbrowser.image.comment", function(e, filename, bbcode, id) {
-                       $.colorbox.close();
-                       var textarea = document.getElementById("comment-edit-text-" +id);
-                       var start = textarea.selectionStart;
-                       var end = textarea.selectionEnd;
-                       textarea.value = textarea.value.substring(0, start) + bbcode + textarea.value.substring(end, textarea.value.length);
-                       $(textarea).trigger('change');
-               });
-
-
-
-               /* setup onoff widgets */
-               $(".onoff input").each(function(){
-                       val = $(this).val();
-                       id = $(this).attr("id");
-                       $("#"+id+"_onoff ."+ (val==0?"on":"off")).addClass("hidden");
-
-               });
-               $(".onoff > a").click(function(event){
-                       event.preventDefault();
-                       var input = $(this).siblings("input");
-                       var val = 1-input.val();
-                       var id = input.attr("id");
-                       $("#"+id+"_onoff ."+ (val==0?"on":"off")).addClass("hidden");
-                       $("#"+id+"_onoff ."+ (val==1?"on":"off")).removeClass("hidden");
-                       input.val(val);
-               });
-
-               /* popup menus */
-               function close_last_popup_menu() {
-                       if(last_popup_menu) {
-                               last_popup_menu.hide();
-                               last_popup_menu.off('click', function(e) {e.stopPropagation()});
-                               last_popup_button.removeClass("selected");
-                               last_popup_menu = null;
-                               last_popup_button = null;
-                       }
-               }
-               $('a[rel^="#"]').click(function(e){
-                       e.preventDefault();
-                       var parent = $(this).parent();
-                       var isSelected = (last_popup_button && parent.attr('id') == last_popup_button.attr('id'));
-                       close_last_popup_menu();
-                       if(isSelected) return false;
-                       menu = $( $(this).attr('rel') );
-                       e.preventDefault();
-                       e.stopPropagation();
-                       if (menu.attr('popup')=="false") return false;
-                       parent.toggleClass("selected");
-                       menu.toggle();
-                       if (menu.css("display") == "none") {
-                               last_popup_menu = null;
-                               last_popup_button = null;
-                       } else {
-                               last_popup_menu = menu;
-                               last_popup_menu.on('click', function(e) {e.stopPropagation()});
-                               last_popup_button = parent;
-                               $('#nav-notifications-menu').perfectScrollbar('update');
-                       }
-                       return false;
-               });
-               $('html').click(function() {
-                       close_last_popup_menu();
-               });
-
-               // fancyboxes
-               $("a.popupbox").colorbox({
-                       'inline' : true,
-                       'transition' : 'elastic',
-                       'maxWidth' : '100%'
-               });
-               $("a.ajax-popupbox").colorbox({
-                       'transition' : 'elastic',
-                       'maxWidth' : '100%'
-               });
-
-               /* notifications template */
-               var notifications_tpl= unescape($("#nav-notifications-template[rel=template]").html());
-               var notifications_all = unescape($('<div>').append( $("#nav-notifications-see-all").clone() ).html()); //outerHtml hack
-               var notifications_mark = unescape($('<div>').append( $("#nav-notifications-mark-all").clone() ).html()); //outerHtml hack
-               var notifications_empty = unescape($("#nav-notifications-menu").html());
-
-               /* enable perfect-scrollbars for different elements */
-               $('#nav-notifications-menu, aside').perfectScrollbar();
-
-               /* nav update event  */
-               $('nav').bind('nav-update', function(e, data){
-                       var invalid = data.invalid || 0;
-                       if(invalid == 1) { window.location.href=window.location.href }
-
-                       ['net', 'home', 'intro', 'mail', 'events', 'birthdays', 'notify'].forEach(function(type) {
-                               var number = data[type];
-                               if (number == 0) {
-                                       number = '';
-                                       $('#' + type + '-update').removeClass('show');
-                               } else {
-                                       $('#' + type + '-update').addClass('show');
-                               }
-                               $('#' + type + '-update').text(number);
-                       });
-
-                       var intro = data['intro'];
-                       if(intro == 0) { intro = ''; $('#intro-update-li').removeClass('show') } else { $('#intro-update-li').addClass('show') }
-                       $('#intro-update-li').html(intro);
-
-                       var mail = data['mail'];
-                       if(mail == 0) { mail = ''; $('#mail-update-li').removeClass('show') } else { $('#mail-update-li').addClass('show') }
-                       $('#mail-update-li').html(mail);
-
-                       $(".sidebar-group-li .notify").removeClass("show");
-                       $(data.groups).each(function(key, group) {
-                               var gid = group.id;
-                               var gcount = group.count;
-                               $(".group-"+gid+" .notify").addClass("show").text(gcount);
-                       });
-
-                       $(".forum-widget-entry .notify").removeClass("show");
-                       $(data.forums).each(function(key, forum) {
-                               var fid = forum.id;
-                               var fcount = forum.count;
-                               $(".forum-"+fid+" .notify").addClass("show").text(fcount);
-                       });
-
-                       if (data.notifications.length == 0) {
-                               $("#nav-notifications-menu").html(notifications_empty);
-                       } else {
-                               var nnm = $("#nav-notifications-menu");
-                               nnm.html(notifications_all + notifications_mark);
-
-                               var lastItemStorageKey = "notification-lastitem:" + localUser;
-                               var notification_lastitem = parseInt(localStorage.getItem(lastItemStorageKey));
-                               var notification_id = 0;
-
-                               // Insert notifs into the notifications-menu
-                               $(data.notifications).each(function(key, notif){
-                                       var text = notif.message.format('<span class="contactname">' + notif.name + '</span>');
-                                       var contact = ('<a href="' + notif.url + '"><span class="contactname">' + notif.name + '</span></a>');
-                                       var seenclass = (notif.seen == 1) ? "notify-seen" : "notify-unseen";
-                                       var html = notifications_tpl.format(
-                                               notif.href,                     // {0}  // link to the source
-                                               notif.photo,                    // {1}  // photo of the contact
-                                               text,                           // {2}  // preformatted text (autor + text)
-                                               notif.date,                     // {3}  // date of notification (time ago)
-                                               seenclass,                      // {4}  // visited status of the notification
-                                               new Date(notif.timestamp*1000), // {5}  // date of notification
-                                               notif.url,                      // {6}  // profile url of the contact
-                                               notif.message.format(contact),  // {7}  // preformatted html (text including author profile url)
-                                               ''                              // {8}  // Deprecated
-                                       );
-                                       nnm.append(html);
-                               });
-
-                               // Desktop Notifications
-                               $(data.notifications.reverse()).each(function(key, e){
-                                       notification_id = parseInt(e.timestamp);
-                                       if (notification_lastitem !== null && notification_id > notification_lastitem && Number(e.seen) === 0) {
-                                               if (getNotificationPermission() === "granted") {
-                                                       var notification = new Notification(document.title, {
-                                                                                         body: decodeHtml(e.message.replace('&rarr; ', '').format(e.name)),
-                                                                                         icon: e.photo,
-                                                                                        });
-                                                       notification['url'] = e.href;
-                                                       notification.addEventListener("click", function(ev){
-                                                               window.location = ev.target.url;
-                                                       });
-                                               }
-                                       }
-
-                               });
-                               notification_lastitem = notification_id;
-                               localStorage.setItem(lastItemStorageKey, notification_lastitem)
-
-                               $("img[data-src]", nnm).each(function(i, el){
-                                       // Add src attribute for images with a data-src attribute
-                                       // However, don't bother if the data-src attribute is empty, because
-                                       // an empty "src" tag for an image will cause some browsers
-                                       // to prefetch the root page of the Friendica hub, which will
-                                       // unnecessarily load an entire profile/ or network/ page
-                                       if($(el).data("src") != '') $(el).attr('src', $(el).data("src"));
-                               });
-                       }
-
-                       var notif = data['notify'];
-                       if (notif > 0){
-                               $("#nav-notifications-linkmenu").addClass("on");
-                       } else {
-                               $("#nav-notifications-linkmenu").removeClass("on");
-                       }
-
-                       $(data.sysmsgs.notice).each(function(key, message){
-                               $.jGrowl(message, {sticky: true, theme: 'notice'});
-                       });
-                       $(data.sysmsgs.info).each(function(key, message){
-                               $.jGrowl(message, {sticky: false, theme: 'info', life: 5000});
-                       });
-
-                       // Update the js scrollbars
-                       $('#nav-notifications-menu').perfectScrollbar('update');
-
-               });
-
-               NavUpdate();
-               // Allow folks to stop the ajax page updates with the pause/break key
-               $(document).keydown(function(event) {
-                       if(event.keyCode == '8') {
-                               var target = event.target || event.srcElement;
-                               if (!/input|textarea/i.test(target.nodeName)) {
-                                       return false;
-                               }
-                       }
-                       if(event.keyCode == '19' || (event.ctrlKey && event.which == '32')) {
-                               event.preventDefault();
-                               if(stopped == false) {
-                                       stopped = true;
-                                       if (event.ctrlKey) {
-                                               totStopped = true;
-                                       }
-                                       $('#pause').html('<img src="images/pause.gif" alt="pause" style="border: 1px solid black;" />');
-                               } else {
-                                       unpause();
-                               }
-                       } else {
-                               if (!totStopped) {
-                                       unpause();
-                               }
-                       }
-               });
-
-               // Scroll to the next/previous thread when pressing J and K
-               $(document).keydown(function (event) {
-                       var threads = $('.thread_level_1');
-                       if ((event.keyCode === 74 || event.keyCode === 75) && !$(event.target).is('textarea, input')) {
-                               var scrollTop = $(window).scrollTop();
-                               if (event.keyCode === 75) {
-                                       threads = $(threads.get().reverse());
-                               }
-                               threads.each(function(key, item) {
-                                       var comparison;
-                                       var top = $(item).offset().top - 100;
-                                       if (event.keyCode === 74) {
-                                               comparison = top > scrollTop + 1;
-                                       } else if (event.keyCode === 75) {
-                                               comparison = top < scrollTop - 1;
-                                       }
-                                       if (comparison) {
-                                               $('html, body').animate({ scrollTop: top }, 200);
-                                               return false;
-                                       }
-                               });
-                       }
-               });
-
-               // Set an event listener for infinite scroll
-               if(typeof infinite_scroll !== 'undefined') {
-                       $(window).scroll(function(e){
-                               if ($(document).height() != $(window).height()) {
-                                       // First method that is expected to work - but has problems with Chrome
-                                       if ($(window).scrollTop() > ($(document).height() - $(window).height() * 1.5))
-                                               loadScrollContent();
-                               } else {
-                                       // This method works with Chrome - but seems to be much slower in Firefox
-                                       if ($(window).scrollTop() > (($("section").height() + $("header").height() + $("footer").height()) - $(window).height() * 1.5))
-                                               loadScrollContent();
-                               }
-                       });
-               }
-
-
-       });
-
-       function NavUpdate() {
-
-               if (!stopped) {
-                       var pingCmd = 'ping?format=json' + ((localUser != 0) ? '&f=&uid=' + localUser : '');
-                       $.get(pingCmd, function(data) {
-                               if (data.result) {
-                                       // send nav-update event
-                                       $('nav').trigger('nav-update', data.result);
-
-                                       // start live update
-                                       ['network', 'profile', 'community', 'notes', 'display'].forEach(function (src) {
-                                               if ($('#live-' + src).length) {
-                                                       liveUpdate(src);
-                                               }
-                                       });
-                                       if ($('#live-photos').length) {
-                                               if (liking) {
-                                                       liking = 0;
-                                                       window.location.href = window.location.href;
-                                               }
-                                       }
-                               }
-                       }) ;
-               }
-               timer = setTimeout(NavUpdate, updateInterval);
-       }
-
-       function liveUpdate(src) {
-               if((src == null) || (stopped) || (! profile_uid)) { $('.like-rotator').hide(); return; }
-               if(($('.comment-edit-text-full').length) || (in_progress)) {
-                       if(livetime) {
-                               clearTimeout(livetime);
-                       }
-                       livetime = setTimeout(function() {liveUpdate(src)}, 5000);
-                       return;
-               }
-               if(livetime != null)
-                       livetime = null;
-
-               prev = 'live-' + src;
-
-               in_progress = true;
-
-               if ($(document).scrollTop() == 0)
-                       force_update = true;
-
-               var udargs = ((netargs.length) ? '/' + netargs : '');
-               var update_url = 'update_' + src + udargs + '&p=' + profile_uid + '&page=' + profile_page + '&force=' + ((force_update) ? 1 : 0);
-
-               $.get(update_url,function(data) {
-                       in_progress = false;
-                       force_update = false;
-                       //                      $('.collapsed-comments',data).each(function() {
-                       //      var ident = $(this).attr('id');
-                       //      var is_hidden = $('#' + ident).is(':hidden');
-                       //      if($('#' + ident).length) {
-                       //              $('#' + ident).replaceWith($(this));
-                       //              if(is_hidden)
-                       //                      $('#' + ident).hide();
-                       //      }
-                       //});
-
-                       // add a new thread
-                       $('.toplevel_item',data).each(function() {
-                               var ident = $(this).attr('id');
-
-                               if($('#' + ident).length == 0 && profile_page == 1) {
-                                       $('img',this).each(function() {
-                                               $(this).attr('src',$(this).attr('dst'));
-                                       });
-                                       $('#' + prev).after($(this));
-                               }
-                               else {
-                                       // Find out if the hidden comments are open, so we can keep it that way
-                                       // if a new comment has been posted
-                                       var id = $('.hide-comments-total', this).attr('id');
-                                       if(typeof id != 'undefined') {
-                                               id = id.split('-')[3];
-                                               var commentsOpen = $("#collapsed-comments-" + id).is(":visible");
-                                       }
-
-                                       $('img',this).each(function() {
-                                               $(this).attr('src',$(this).attr('dst'));
-                                       });
-                                       //vScroll = $(document).scrollTop();
-                                       $('html').height($('html').height());
-                                       $('#' + ident).replaceWith($(this));
-
-                                       if(typeof id != 'undefined') {
-                                               if(commentsOpen) showHideComments(id);
-                                       }
-                                       $('html').height('auto');
-                                       //$(document).scrollTop(vScroll);
-                               }
-                               prev = ident;
-                       });
-
-                       // reset vars for inserting individual items
-
-                       /*                      prev = 'live-' + src;
-
-                       $('.wall-item-outside-wrapper',data).each(function() {
-                               var ident = $(this).attr('id');
-
-                               if($('#' + ident).length == 0 && prev != 'live-' + src) {
-                                               $('img',this).each(function() {
-                                                       $(this).attr('src',$(this).attr('dst'));
-                                               });
-                                               $('#' + prev).after($(this));
-                               }
-                               else {
-                                       $('#' + ident + ' ' + '.wall-item-ago').replaceWith($(this).find('.wall-item-ago'));
-                                       if($('#' + ident + ' ' + '.comment-edit-text-empty').length)
-                                               $('#' + ident + ' ' + '.wall-item-comment-wrapper').replaceWith($(this).find('.wall-item-comment-wrapper'));
-                                       $('#' + ident + ' ' + '.hide-comments-total').replaceWith($(this).find('.hide-comments-total'));
-                                       $('#' + ident + ' ' + '.wall-item-like').replaceWith($(this).find('.wall-item-like'));
-                                       $('#' + ident + ' ' + '.wall-item-dislike').replaceWith($(this).find('.wall-item-dislike'));
-                                       $('#' + ident + ' ' + '.my-comment-photo').each(function() {
-                                               $(this).attr('src',$(this).attr('dst'));
-                                       });
-                               }
-                               prev = ident;
-                       });
-                       */
-                       $('.like-rotator').hide();
-                       if(commentBusy) {
-                               commentBusy = false;
-                               $('body').css('cursor', 'auto');
-                       }
-                       /* autocomplete @nicknames */
-                       $(".comment-edit-form  textarea").editor_autocomplete(baseurl+"/acl");
-                       /* autocomplete bbcode */
-                       $(".comment-edit-form  textarea").bbco_autocomplete('bbcode');
-               });
-       }
-
-       function imgbright(node) {
-               $(node).removeClass("drophide").addClass("drop");
-       }
-
-       function imgdull(node) {
-               $(node).removeClass("drop").addClass("drophide");
-       }
-
-       // Since our ajax calls are asynchronous, we will give a few
-       // seconds for the first ajax call (setting like/dislike), then
-       // run the updater to pick up any changes and display on the page.
-       // The updater will turn any rotators off when it's done.
-       // This function will have returned long before any of these
-       // events have completed and therefore there won't be any
-       // visible feedback that anything changed without all this
-       // trickery. This still could cause confusion if the "like" ajax call
-       // is delayed and NavUpdate runs before it completes.
-
-       function dolike(ident,verb) {
-               unpause();
-               $('#like-rotator-' + ident.toString()).show();
-               $.get('like/' + ident.toString() + '?verb=' + verb, NavUpdate );
-               liking = 1;
-               force_update = true;
-       }
-
-       function dosubthread(ident) {
-               unpause();
-               $('#like-rotator-' + ident.toString()).show();
-               $.get('subthread/' + ident.toString(), NavUpdate );
-               liking = 1;
-       }
-
-
-       function dostar(ident) {
-               ident = ident.toString();
-               $('#like-rotator-' + ident).show();
-               $.get('starred/' + ident, function(data) {
-                       if(data.match(/1/)) {
-                               $('#starred-' + ident).addClass('starred');
-                               $('#starred-' + ident).removeClass('unstarred');
-                               $('#star-' + ident).addClass('hidden');
-                               $('#unstar-' + ident).removeClass('hidden');
-                       }
-                       else {
-                               $('#starred-' + ident).addClass('unstarred');
-                               $('#starred-' + ident).removeClass('starred');
-                               $('#star-' + ident).removeClass('hidden');
-                               $('#unstar-' + ident).addClass('hidden');
-                       }
-                       $('#like-rotator-' + ident).hide();
-               });
-       }
-
-       function doignore(ident) {
-               ident = ident.toString();
-               $('#like-rotator-' + ident).show();
-               $.get('ignored/' + ident, function(data) {
-                       if(data.match(/1/)) {
-                               $('#ignored-' + ident).addClass('ignored');
-                               $('#ignored-' + ident).removeClass('unignored');
-                               $('#ignore-' + ident).addClass('hidden');
-                               $('#unignore-' + ident).removeClass('hidden');
-                       }
-                       else {
-                               $('#ignored-' + ident).addClass('unignored');
-                               $('#ignored-' + ident).removeClass('ignored');
-                               $('#ignore-' + ident).removeClass('hidden');
-                               $('#unignore-' + ident).addClass('hidden');
-                       }
-                       $('#like-rotator-' + ident).hide();
-               });
-       }
-
-       function getPosition(e) {
-               var cursor = {x:0, y:0};
-               if ( e.pageX || e.pageY  ) {
-                       cursor.x = e.pageX;
-                       cursor.y = e.pageY;
-               }
-               else {
-                       if( e.clientX || e.clientY ) {
-                               cursor.x = e.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft) - document.documentElement.clientLeft;
-                               cursor.y = e.clientY + (document.documentElement.scrollTop  || document.body.scrollTop)  - document.documentElement.clientTop;
-                       }
-                       else {
-                               if( e.x || e.y ) {
-                                       cursor.x = e.x;
-                                       cursor.y = e.y;
-                               }
-                       }
-               }
-               return cursor;
-       }
-
-       var lockvisible = false;
-
-       function lockview(event,id) {
-               event = event || window.event;
-               cursor = getPosition(event);
-               if(lockvisible) {
-                       lockviewhide();
-               }
-               else {
-                       lockvisible = true;
-                       $.get('lockview/' + id, function(data) {
-                               $('#panel').html(data);
-                               $('#panel').css({ 'left': cursor.x + 5 , 'top': cursor.y + 5});
-                               $('#panel').show();
-                       });
-               }
-       }
-
-       function lockviewhide() {
-               lockvisible = false;
-               $('#panel').hide();
-       }
-
-       function post_comment(id) {
-               unpause();
-               commentBusy = true;
-               $('body').css('cursor', 'wait');
-               $("#comment-preview-inp-" + id).val("0");
-               $.post(
-                       "item",
-                       $("#comment-edit-form-" + id).serialize(),
-                       function(data) {
-                               if(data.success) {
-                                       $("#comment-edit-wrapper-" + id).hide();
-                                       $("#comment-edit-text-" + id).val('');
-                                       var tarea = document.getElementById("comment-edit-text-" + id);
-                                       if(tarea)
-                                               commentClose(tarea,id);
-                                       if(timer) clearTimeout(timer);
-                                       timer = setTimeout(NavUpdate,10);
-                                       force_update = true;
-                               }
-                               if(data.reload) {
-                                       window.location.href=data.reload;
-                               }
-                       },
-                       "json"
-               );
-               return false;
-       }
-
-
-       function preview_comment(id) {
-               $("#comment-preview-inp-" + id).val("1");
-               $("#comment-edit-preview-" + id).show();
-               $.post(
-                       "item",
-                       $("#comment-edit-form-" + id).serialize(),
-                       function(data) {
-                               if(data.preview) {
-                                       $("#comment-edit-preview-" + id).html(data.preview);
-                                       $("#comment-edit-preview-" + id + " a").click(function() { return false; });
-                               }
-                       },
-                       "json"
-               );
-               return true;
-       }
-
-
-
-       function showHideComments(id) {
-               if( $("#collapsed-comments-" + id).is(":visible")) {
-                       $("#collapsed-comments-" + id).hide();
-                       $("#hide-comments-" + id).html(window.showMore);
-               }
-               else {
-                       $("#collapsed-comments-" + id).show();
-                       $("#hide-comments-" + id).html(window.showFewer);
-               }
-       }
-
-
-
-       function preview_post() {
-               $("#jot-preview").val("1");
-               $("#jot-preview-content").show();
-               $.post(
-                       "item",
-                       $("#profile-jot-form").serialize(),
-                       function(data) {
-                               if(data.preview) {
-                                       $("#jot-preview-content").html(data.preview);
-                                       $("#jot-preview-content" + " a").click(function() { return false; });
-                               }
-                       },
-                       "json"
-               );
-               $("#jot-preview").val("0");
-               return true;
-       }
-
-
-       function unpause() {
-               // unpause auto reloads if they are currently stopped
-               totStopped = false;
-               stopped = false;
-               $('#pause').html('');
-       }
-
-       // load more network content (used for infinite scroll)
-       function loadScrollContent() {
-               if (lockLoadContent) return;
-               lockLoadContent = true;
-
-               $("#scroll-loader").fadeIn('normal');
-
-               // the page number to load is one higher than the actual
-               // page number
-               infinite_scroll.pageno+=1;
-
-               match = $("span.received").last();
-               if (match.length > 0) {
-                       received = match[0].innerHTML;
-               } else {
-                       received = "0000-00-00 00:00:00";
-               }
-
-               match = $("span.created").last();
-               if (match.length > 0) {
-                       created = match[0].innerHTML;
-               } else {
-                       created = "0000-00-00 00:00:00";
-               }
-
-               match = $("span.commented").last();
-               if (match.length > 0) {
-                       commented = match[0].innerHTML;
-               } else {
-                       commented = "0000-00-00 00:00:00";
-               }
-
-               match = $("span.id").last();
-               if (match.length > 0) {
-                       id = match[0].innerHTML;
-               } else {
-                       id = "0";
-               }
-               // console.log("Received: " + received + " - Commented: " + commented+ " - Created: " + created + " - ID: " + id);
-
-               // get the raw content from the next page and insert this content
-               // right before "#conversation-end"
-               $.get('network?mode=raw' + infinite_scroll.reload_uri + '&last_received=' + received + '&last_commented=' + commented + '&last_created=' + created + '&last_id=' + id + '&page=' + infinite_scroll.pageno, function(data) {
-                       $("#scroll-loader").hide();
-                       if ($(data).length > 0) {
-
-                               $(data).insertBefore('#conversation-end');
-                               lockLoadContent = false;
-                       } else {
-                               $("#scroll-end").fadeIn('normal');
-                       }
-               });
-       }
-
-    function bin2hex(s){
-        // Converts the binary representation of data to hex
-        //
-        // version: 812.316
-        // discuss at: http://phpjs.org/functions/bin2hex
-        // +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
-        // +   bugfixed by: Onno Marsman
-        // +   bugfixed by: Linuxworld
-        // *     example 1: bin2hex('Kev');
-        // *     returns 1: '4b6576'
-        // *     example 2: bin2hex(String.fromCharCode(0x00));
-        // *     returns 2: '00'
-        var v,i, f = 0, a = [];
-        s += '';
-        f = s.length;
-
-        for (i = 0; i<f; i++) {
-            a[i] = s.charCodeAt(i).toString(16).replace(/^([\da-f])$/,"0$1");
-        }
-
-        return a.join('');
-    }
-
-       function groupChangeMember(gid, cid, sec_token) {
-               $('body .fakelink').css('cursor', 'wait');
-               $.get('group/' + gid + '/' + cid + "?t=" + sec_token, function(data) {
-                               $('#group-update-wrapper').html(data);
-                               $('body .fakelink').css('cursor', 'auto');
-               });
-       }
-
-       function profChangeMember(gid,cid) {
-               $('body .fakelink').css('cursor', 'wait');
-               $.get('profperm/' + gid + '/' + cid, function(data) {
-                               $('#prof-update-wrapper').html(data);
-                               $('body .fakelink').css('cursor', 'auto');
-               });
-       }
-
-       function contactgroupChangeMember(gid,cid) {
-               $('body').css('cursor', 'wait');
-               $.get('contactgroup/' + gid + '/' + cid, function(data) {
-                               $('body').css('cursor', 'auto');
-               });
-       }
-
-
-function checkboxhighlight(box) {
-  if($(box).is(':checked')) {
-       $(box).addClass('checkeditem');
-  }
-  else {
-       $(box).removeClass('checkeditem');
-  }
-}
-
-function notifyMarkAll() {
-       $.get('notify/mark/all', function(data) {
-               if(timer) clearTimeout(timer);
-               timer = setTimeout(NavUpdate,1000);
-               force_update = true;
-       });
-}
-
-/**
- * sprintf in javascript
- *     "{0} and {1}".format('zero','uno');
- **/
-String.prototype.format = function() {
-    var formatted = this;
-    for (var i = 0; i < arguments.length; i++) {
-        var regexp = new RegExp('\\{'+i+'\\}', 'gi');
-        formatted = formatted.replace(regexp, arguments[i]);
-    }
-    return formatted;
-};
-// Array Remove
-Array.prototype.remove = function(item) {
-  to=undefined; from=this.indexOf(item);
-  var rest = this.slice((to || from) + 1 || this.length);
-  this.length = from < 0 ? this.length + from : from;
-  return this.push.apply(this, rest);
-};
-
-function previewTheme(elm) {
-       theme = $(elm).val();
-       $.getJSON('pretheme?f=&theme=' + theme,function(data) {
-                       $('#theme-preview').html('<div id="theme-desc">' + data.desc + '</div><div id="theme-version">' + data.version + '</div><div id="theme-credits">' + data.credits + '</div><a href="' + data.img + '"><img src="' + data.img + '" width="320" height="240" alt="' + theme + '" /></a>');
-       });
-
-}
-
-// notification permission settings in localstorage
-// set by settings page
-function getNotificationPermission() {
-       if (window["Notification"] === undefined) {
-               return null;
-       }
-    if (Notification.permission === 'granted') {
-        var val = localStorage.getItem('notification-permissions');
-               if (val === null) return 'denied';
-               return val;
-    } else {
-        return Notification.permission;
-    }
-}
-
-/**
- * Show a dialog loaded from an url
- * By defaults this load the url in an iframe in colorbox
- * Themes can overwrite `show()` function to personalize it
- */
-var Dialog = {
-       /**
-        * Show the dialog
-        *
-        * @param string url
-        * @return object colorbox
-        */
-       show : function (url) {
-               var size = Dialog._get_size();
-               return $.colorbox({href: url, iframe:true,innerWidth: size.width+'px',innerHeight: size.height+'px'})
-       },
-
-       /**
-        * Show the Image browser dialog
-        *
-        * @param string name
-        * @param string id (optional)
-        * @return object
-        *
-        * The name will be used to build the event name
-        * fired by image browser dialog when the user select
-        * an image. The optional id will be passed as argument
-        * to the event handler
-        */
-       doImageBrowser : function (name, id) {
-               var url = Dialog._get_url("image",name,id);
-               return Dialog.show(url);
-       },
-
-       /**
-        * Show the File browser dialog
-        *
-        * @param string name
-        * @param string id (optional)
-        * @return object
-        *
-        * The name will be used to build the event name
-        * fired by file browser dialog when the user select
-        * a file. The optional id will be passed as argument
-        * to the event handler
-        */
-       doFileBrowser : function (name, id) {
-               var url = Dialog._get_url("file",name,id);
-               return Dialog.show(url);
-       },
-
-       _get_url : function(type, name, id) {
-               var hash = name;
-               if (id !== undefined) hash = hash + "-" + id;
-               return baseurl + "/fbrowser/"+type+"/?mode=minimal#"+hash;
-       },
-
-       _get_size: function() {
-               return {
-                       width: window.innerWidth-50,
-                       height: window.innerHeight-100
-               };
-       }
-}
diff --git a/js/modernizr.js b/js/modernizr.js
deleted file mode 100644 (file)
index f930bca..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-/* Modernizr 2.8.3 (Custom Build) | MIT & BSD
- * Build: http://modernizr.com/download/#-shiv-cssclasses-load
- */
-;window.Modernizr=function(a,b,c){function u(a){j.cssText=a}function v(a,b){return u(prefixes.join(a+";")+(b||""))}function w(a,b){return typeof a===b}function x(a,b){return!!~(""+a).indexOf(b)}function y(a,b,d){for(var e in a){var f=b[a[e]];if(f!==c)return d===!1?a[e]:w(f,"function")?f.bind(d||b):f}return!1}var d="2.8.3",e={},f=!0,g=b.documentElement,h="modernizr",i=b.createElement(h),j=i.style,k,l={}.toString,m={},n={},o={},p=[],q=p.slice,r,s={}.hasOwnProperty,t;!w(s,"undefined")&&!w(s.call,"undefined")?t=function(a,b){return s.call(a,b)}:t=function(a,b){return b in a&&w(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=q.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(q.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(q.call(arguments)))};return e});for(var z in m)t(m,z)&&(r=z.toLowerCase(),e[r]=m[z](),p.push((e[r]?"":"no-")+r));return e.addTest=function(a,b){if(typeof a=="object")for(var d in a)t(a,d)&&e.addTest(d,a[d]);else{a=a.toLowerCase();if(e[a]!==c)return e;b=typeof b=="function"?b():b,typeof f!="undefined"&&f&&(g.className+=" "+(b?"":"no-")+a),e[a]=b}return e},u(""),i=k=null,function(a,b){function l(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x<style>"+b+"</style>",d.insertBefore(c.lastChild,d.firstChild)}function m(){var a=s.elements;return typeof a=="string"?a.split(" "):a}function n(a){var b=j[a[h]];return b||(b={},i++,a[h]=i,j[i]=b),b}function o(a,c,d){c||(c=b);if(k)return c.createElement(a);d||(d=n(c));var g;return d.cache[a]?g=d.cache[a].cloneNode():f.test(a)?g=(d.cache[a]=d.createElem(a)).cloneNode():g=d.createElem(a),g.canHaveChildren&&!e.test(a)&&!g.tagUrn?d.frag.appendChild(g):g}function p(a,c){a||(a=b);if(k)return a.createDocumentFragment();c=c||n(a);var d=c.frag.cloneNode(),e=0,f=m(),g=f.length;for(;e<g;e++)d.createElement(f[e]);return d}function q(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return s.shivMethods?o(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+m().join().replace(/[\w\-]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(s,b.frag)}function r(a){a||(a=b);var c=n(a);return s.shivCSS&&!g&&!c.hasCSS&&(c.hasCSS=!!l(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),k||q(a,c),a}var c="3.7.0",d=a.html5||{},e=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,f=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,g,h="_html5shiv",i=0,j={},k;(function(){try{var a=b.createElement("a");a.innerHTML="<xyz></xyz>",g="hidden"in a,k=a.childNodes.length==1||function(){b.createElement("a");var a=b.createDocumentFragment();return typeof a.cloneNode=="undefined"||typeof a.createDocumentFragment=="undefined"||typeof a.createElement=="undefined"}()}catch(c){g=!0,k=!0}})();var s={elements:d.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video",version:c,shivCSS:d.shivCSS!==!1,supportsUnknownElements:k,shivMethods:d.shivMethods!==!1,type:"default",shivDocument:r,createElement:o,createDocumentFragment:p};a.html5=s,r(b)}(this,b),e._version=d,g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(f?" js "+p.join(" "):""),e}(this,this.document),function(a,b,c){function d(a){return"[object Function]"==o.call(a)}function e(a){return"string"==typeof a}function f(){}function g(a){return!a||"loaded"==a||"complete"==a||"uninitialized"==a}function h(){var a=p.shift();q=1,a?a.t?m(function(){("c"==a.t?B.injectCss:B.injectJs)(a.s,0,a.a,a.x,a.e,1)},0):(a(),h()):q=0}function i(a,c,d,e,f,i,j){function k(b){if(!o&&g(l.readyState)&&(u.r=o=1,!q&&h(),l.onload=l.onreadystatechange=null,b)){"img"!=a&&m(function(){t.removeChild(l)},50);for(var d in y[c])y[c].hasOwnProperty(d)&&y[c][d].onload()}}var j=j||B.errorTimeout,l=b.createElement(a),o=0,r=0,u={t:d,s:c,e:f,a:i,x:j};1===y[c]&&(r=1,y[c]=[]),"object"==a?l.data=c:(l.src=c,l.type=a),l.width=l.height="0",l.onerror=l.onload=l.onreadystatechange=function(){k.call(this,r)},p.splice(e,0,u),"img"!=a&&(r||2===y[c]?(t.insertBefore(l,s?null:n),m(k,j)):y[c].push(l))}function j(a,b,c,d,f){return q=0,b=b||"j",e(a)?i("c"==b?v:u,a,b,this.i++,c,d,f):(p.splice(this.i++,0,a),1==p.length&&h()),this}function k(){var a=B;return a.loader={load:j,i:0},a}var l=b.documentElement,m=a.setTimeout,n=b.getElementsByTagName("script")[0],o={}.toString,p=[],q=0,r="MozAppearance"in l.style,s=r&&!!b.createRange().compareNode,t=s?l:n.parentNode,l=a.opera&&"[object Opera]"==o.call(a.opera),l=!!b.attachEvent&&!l,u=r?"object":l?"script":"img",v=l?"script":u,w=Array.isArray||function(a){return"[object Array]"==o.call(a)},x=[],y={},z={timeout:function(a,b){return b.length&&(a.timeout=b[0]),a}},A,B;B=function(a){function b(a){var a=a.split("!"),b=x.length,c=a.pop(),d=a.length,c={url:c,origUrl:c,prefixes:a},e,f,g;for(f=0;f<d;f++)g=a[f].split("="),(e=z[g.shift()])&&(c=e(c,g));for(f=0;f<b;f++)c=x[f](c);return c}function g(a,e,f,g,h){var i=b(a),j=i.autoCallback;i.url.split(".").pop().split("?").shift(),i.bypass||(e&&(e=d(e)?e:e[a]||e[g]||e[a.split("/").pop().split("?")[0]]),i.instead?i.instead(a,e,f,g,h):(y[i.url]?i.noexec=!0:y[i.url]=1,f.load(i.url,i.forceCSS||!i.forceJS&&"css"==i.url.split(".").pop().split("?").shift()?"c":c,i.noexec,i.attrs,i.timeout),(d(e)||d(j))&&f.load(function(){k(),e&&e(i.origUrl,h,g),j&&j(i.origUrl,h,g),y[i.url]=2})))}function h(a,b){function c(a,c){if(a){if(e(a))c||(j=function(){var a=[].slice.call(arguments);k.apply(this,a),l()}),g(a,j,b,0,h);else if(Object(a)===a)for(n in m=function(){var b=0,c;for(c in a)a.hasOwnProperty(c)&&b++;return b}(),a)a.hasOwnProperty(n)&&(!c&&!--m&&(d(j)?j=function(){var a=[].slice.call(arguments);k.apply(this,a),l()}:j[n]=function(a){return function(){var b=[].slice.call(arguments);a&&a.apply(this,b),l()}}(k[n])),g(a[n],j,b,n,h))}else!c&&l()}var h=!!a.test,i=a.load||a.both,j=a.callback||f,k=j,l=a.complete||f,m,n;c(h?a.yep:a.nope,!!i),i&&c(i)}var i,j,l=this.yepnope.loader;if(e(a))g(a,0,l,0);else if(w(a))for(i=0;i<a.length;i++)j=a[i],e(j)?g(j,0,l,0):w(j)?B(j):Object(j)===j&&h(j,l);else Object(a)===a&&h(a,l)},B.addPrefix=function(a,b){z[a]=b},B.addFilter=function(a){x.push(a)},B.errorTimeout=1e4,null==b.readyState&&b.addEventListener&&(b.readyState="loading",b.addEventListener("DOMContentLoaded",A=function(){b.removeEventListener("DOMContentLoaded",A,0),b.readyState="complete"},0)),a.yepnope=k(),a.yepnope.executeStack=h,a.yepnope.injectJs=function(a,c,d,e,i,j){var k=b.createElement("script"),l,o,e=e||B.errorTimeout;k.src=a;for(o in d)k.setAttribute(o,d[o]);c=j?h:c||f,k.onreadystatechange=k.onload=function(){!l&&g(k.readyState)&&(l=1,c(),k.onload=k.onreadystatechange=null)},m(function(){l||(l=1,c(1))},e),i?k.onload():n.parentNode.insertBefore(k,n)},a.yepnope.injectCss=function(a,c,d,e,g,i){var e=b.createElement("link"),j,c=i?h:c||f;e.href=a,e.rel="stylesheet",e.type="text/css";for(j in d)e.setAttribute(j,d[j]);g||(n.parentNode.insertBefore(e,n),m(c,0))}}(this,document),Modernizr.load=function(){yepnope.apply(window,[].slice.call(arguments,0))};
\ No newline at end of file
diff --git a/library/cropper/cropper.css b/library/cropper/cropper.css
deleted file mode 100644 (file)
index c2e7598..0000000
+++ /dev/null
@@ -1,182 +0,0 @@
-.imgCrop_wrap {\r
-       /* width: 500px;   @done_in_js */\r
-       /* height: 375px;  @done_in_js */\r
-       position: relative;\r
-       cursor: crosshair;\r
-}\r
-\r
-/* an extra classname is applied for Opera < 9.0 to fix it's lack of opacity support */\r
-.imgCrop_wrap.opera8 .imgCrop_overlay,\r
-.imgCrop_wrap.opera8 .imgCrop_clickArea { \r
-       background-color: transparent;\r
-}\r
-\r
-/* fix for IE displaying all boxes at line-height by default, although they are still 1 pixel high until we combine them with the pointless span */\r
-.imgCrop_wrap,\r
-.imgCrop_wrap * {\r
-       font-size: 0;\r
-}\r
-\r
-.imgCrop_overlay {\r
-       background-color: #000;\r
-       opacity: 0.5;\r
-       filter:alpha(opacity=50);\r
-       position: absolute;\r
-       width: 100%;\r
-       height: 100%;\r
-}\r
-\r
-.imgCrop_selArea {\r
-       position: absolute;\r
-       /* @done_in_js \r
-       top: 20px;\r
-       left: 20px;\r
-       width: 200px;\r
-       height: 200px;\r
-       background: transparent url(castle.jpg) no-repeat  -210px -110px;\r
-       */\r
-       cursor: move;\r
-       z-index: 2;\r
-}\r
-\r
-/* clickArea is all a fix for IE 5.5 & 6 to allow the user to click on the given area */\r
-.imgCrop_clickArea {\r
-       width: 100%;\r
-       height: 100%;\r
-       background-color: #FFF;\r
-       opacity: 0.01;\r
-       filter:alpha(opacity=01);\r
-}\r
-\r
-.imgCrop_marqueeHoriz {\r
-       position: absolute;\r
-       width: 100%;\r
-       height: 1px;\r
-       background: transparent url(marqueeHoriz.gif) repeat-x 0 0;\r
-       z-index: 3;\r
-}\r
-\r
-.imgCrop_marqueeVert {\r
-       position: absolute;\r
-       height: 100%;\r
-       width: 1px;\r
-       background: transparent url(marqueeVert.gif) repeat-y 0 0;\r
-       z-index: 3;\r
-}\r
-\r
-/* \r
- *  FIX MARCHING ANTS IN IE\r
- *     As IE <6 tries to load background images we can uncomment the follwoing hack \r
- *  to remove that issue, not as pretty - but is anything in IE?\r
- *  And yes I do know that 'filter' is evil, but it will make it look semi decent in IE\r
- *\r
-* html .imgCrop_marqueeHoriz,\r
-* html .imgCrop_marqueeVert {\r
-       background: transparent;\r
-       filter: Invert; \r
-}\r
-* html .imgCrop_marqueeNorth { border-top: 1px dashed #000; }\r
-* html .imgCrop_marqueeEast  { border-right: 1px dashed #000; }\r
-* html .imgCrop_marqueeSouth { border-bottom: 1px dashed #000; }\r
-* html .imgCrop_marqueeWest  { border-left: 1px dashed #000; }\r
-*/\r
-\r
-.imgCrop_marqueeNorth { top: 0; left: 0; }\r
-.imgCrop_marqueeEast  { top: 0; right: 0; }\r
-.imgCrop_marqueeSouth { bottom: 0px; left: 0; }\r
-.imgCrop_marqueeWest  { top: 0; left: 0; }\r
-\r
-\r
-.imgCrop_handle {\r
-       position: absolute;\r
-       border: 1px solid #333;\r
-       width: 6px;\r
-       height: 6px;\r
-       background: #FFF;\r
-       opacity: 0.5;\r
-       filter:alpha(opacity=50);\r
-       z-index: 4;\r
-}\r
-\r
-/* fix IE 5 box model */\r
-* html .imgCrop_handle {\r
-       width: 8px;\r
-       height: 8px;\r
-       wid\th: 6px;\r
-       hei\ght: 6px;\r
-}\r
-\r
-.imgCrop_handleN {\r
-       top: -3px;\r
-       left: 0;\r
-       /* margin-left: 49%;    @done_in_js */\r
-       cursor: n-resize;\r
-}\r
-\r
-.imgCrop_handleNE { \r
-       top: -3px;\r
-       right: -3px;\r
-       cursor: ne-resize;\r
-}\r
-\r
-.imgCrop_handleE {\r
-       top: 0;\r
-       right: -3px;\r
-       /* margin-top: 49%;    @done_in_js */\r
-       cursor: e-resize;\r
-}\r
-\r
-.imgCrop_handleSE {\r
-       right: -3px;\r
-       bottom: -3px;\r
-       cursor: se-resize;\r
-}\r
-\r
-.imgCrop_handleS {\r
-       right: 0;\r
-       bottom: -3px;\r
-       /* margin-right: 49%; @done_in_js */\r
-       cursor: s-resize;\r
-}\r
-\r
-.imgCrop_handleSW {\r
-       left: -3px;\r
-       bottom: -3px;\r
-       cursor: sw-resize;\r
-}\r
-\r
-.imgCrop_handleW {\r
-       top: 0;\r
-       left: -3px;\r
-       /* margin-top: 49%;  @done_in_js */\r
-       cursor: w-resize;\r
-}\r
-\r
-.imgCrop_handleNW {\r
-       top: -3px;\r
-       left: -3px;\r
-       cursor: nw-resize;\r
-}\r
-\r
-/**\r
- * Create an area to click & drag around on as the default browser behaviour is to let you drag the image \r
- */\r
-.imgCrop_dragArea {\r
-       width: 100%;\r
-       height: 100%;\r
-       z-index: 200;\r
-       position: absolute;\r
-       top: 0;\r
-       left: 0;\r
-}\r
-\r
-.imgCrop_previewWrap {\r
-       /* width: 200px;  @done_in_js */\r
-       /* height: 200px; @done_in_js */\r
-       overflow: hidden;\r
-       position: relative;\r
-}\r
-\r
-.imgCrop_previewWrap img {\r
-       position: absolute;\r
-}
\ No newline at end of file
diff --git a/library/cropper/cropper.html b/library/cropper/cropper.html
deleted file mode 100644 (file)
index ebdf1ff..0000000
+++ /dev/null
@@ -1,228 +0,0 @@
-   1.
-      <script type="text/javascript" src="scripts/cropper/lib/prototype.js" language="javascript"></script>
-   2.
-      <script type="text/javascript" src="scripts/cropper/lib/scriptaculous.js?load=builder,dragdrop" language="javascript"></script>
-   3.
-      <script type="text/javascript" src="scripts/cropper/cropper.js" language="javascript"></script>
-
-Options
-
-ratioDim obj
-    The pixel dimensions to apply as a restrictive ratio, with properties x & y.
-minWidth int
-    The minimum width for the select area in pixels.
-minHeight int
-    The mimimum height for the select area in pixels.
-maxWidth int
-    The maximum width for the select areas in pixels (if both minWidth & maxWidth set to same the width of the cropper will be fixed)
-maxHeight int
-    The maximum height for the select areas in pixels (if both minHeight & maxHeight set to same the height of the cropper will be fixed)
-displayOnInit int
-    Whether to display the select area on initialisation, only used when providing minimum width & height or ratio.
-onEndCrop func
-    The callback function to provide the crop details to on end of a crop.
-captureKeys boolean
-    Whether to capture the keys for moving the select area, as these can cause some problems at the moment.
-onloadCoords obj
-    A coordinates object with properties x1, y1, x2 & y2; for the coordinates of the select area to display onload
-
-The callback function
-
-The callback function is a function that allows you to capture the crop co-ordinates when the user finished a crop movement, it is passed two arguments:
-
-    * coords, obj, coordinates object with properties x1, y1, x2 & y2; for the coordinates of the select area.
-    * dimensions, obj, dimensions object with properities width & height; for the dimensions of the select area.
-
-An example function which outputs the crop values to form fields:
-Display code as plain text
-JavaScript:
-
-   1.
-      function onEndCrop( coords, dimensions ) {
-   2.
-          $PR( 'x1' ).value = coords.x1;
-   3.
-          $PR( 'y1' ).value = coords.y1;
-   4.
-          $PR( 'x2' ).value = coords.x2;
-   5.
-          $PR( 'y2' ).value = coords.y2;
-   6.
-          $PR( 'width' ).value = dimensions.width;
-   7.
-          $PR( 'height' ).value = dimensions.height;
-   8.
-      }
-
-Basic interface
-
-This basic example will attach the cropper UI to the test image and return crop results to the provided callback function.
-Display code as plain text
-HTML:
-
-   1.
-      <img src="test.jpg" alt="Test image" id="testImage" width="500" height="333" />
-   2.
-       
-   3.
-          <script type="text/javascript" language="javascript">
-   4.
-          Event.observe( window, 'load', function() {
-   5.
-              new Cropper.Img(
-   6.
-                  'testImage',
-   7.
-                  { onEndCrop: onEndCrop }
-   8.
-              );
-   9.
-          } );
-  10.
-      </script>
-
-Minimum dimensions
-
-You can apply minimum dimensions to a single axis or both, this example applies minimum dimensions to both axis.
-Display code as plain text
-HTML:
-
-   1.
-      <img src="test.jpg" alt="Test image" id="testImage" width="500" height="333" />
-   2.
-       
-   3.
-      <script type="text/javascript" language="javascript">
-   4.
-          Event.observe( window, 'load', function() {
-   5.
-              new Cropper.Img(
-   6.
-                  'testImage',
-   7.
-                  {
-   8.
-                      minWidth: 220,
-   9.
-                      minHeight: 120,
-  10.
-                      onEndCrop: onEndCrop
-  11.
-                  }
-  12.
-              );
-  13.
-          } );
-  14.
-      </script>
-
-Select area ratio
-
-You can apply a ratio to the selection area, this example applies a 4:3 ratio to the select area.
-Display code as plain text
-HTML:
-
-   1.
-      <img src="test.jpg" alt="Test image" id="testImage" width="500" height="333" />
-   2.
-       
-   3.
-      <script type="text/javascript" language="javascript">
-   4.
-          Event.observe( window, 'load', function() {
-   5.
-              new Cropper.Img(
-   6.
-                  'testImage',
-   7.
-                  {
-   8.
-                      ratioDim: {
-   9.
-                          x: 220,
-  10.
-                          y: 165
-  11.
-                      },
-  12.
-                      displayOnInit: true,
-  13.
-                      onEndCrop: onEndCrop
-  14.
-                  }
-  15.
-              );
-  16.
-          } );
-  17.
-      </script>
-
-With crop preview
-
-You can display a dynamically prouced preview of the resulting crop by using the ImgWithPreview subclass, a preview can only be displayed when we have a fixed size (set via minWidth & minHeight options). Note that the displayOnInit option is not required as this is the default behaviour when displaying a crop preview.
-Display code as plain text
-HTML:
-
-   1.
-      <img src="test.jpg" alt="Test image" id="testImage" width="500" height="333" />
-   2.
-      <div id="previewWrap"></div>
-   3.
-       
-   4.
-      <script type="text/javascript" language="javascript">
-   5.
-          Event.observe( window, 'load', function() {
-   6.
-              new Cropper.ImgWithPreview(
-   7.
-                  'testImage',
-   8.
-                  {
-   9.
-                      previewWrap: 'previewWrap',
-  10.
-                      minWidth: 120,
-  11.
-                      minHeight: 120,
-  12.
-                      ratioDim: { x: 200, y: 120 },
-  13.
-                      onEndCrop: onEndCrop
-  14.
-                  }
-  15.
-              );
-  16.
-          } );
-  17.
-      </script>
-
-Known Issues
-
-    * Safari animated gifs, only one of each will animate, this seems to be a known Safari issue.
-    * After drawing an area and then clicking to start a new drag in IE 5.5 the rendered height appears as the last height until the user drags, this appears to be the related to another IE error (which has been fixed) where IE does not always redraw the select area properly.
-    * Lack of CSS opacity support in Opera before version 9 mean we disable those style rules, if Opera 8 support is important you & you want the overlay to work then you can use the Opera rules in the CSS to apply a black PNG with 50% alpha transparency to replicate the effect.
-    * Styling & borders on image, any CSS styling applied directly to the image itself (floats, borders, padding, margin, etc.) will cause problems with the cropper. The use of a wrapper element to apply these styles to is recommended.
-    * overflow: auto or overflow: scroll on parent will cause cropper to burst out of parent in IE and Opera when applied (maybe Mac browsers too) I'm not sure why yet.
-
-If you use CakePHP you will notice that including this in your script will break the CSS layout. This is due to the CSS rule
-
-form div{
-vertical-align: text-top;
-margin-left: 1em;
-margin-bottom:2em;
-overflow: auto;
-}
-
-A simple workaround is to add another rule directly after this like so:
-
-form div.no_cake, form div.no_cake div {
-margin:0;
-overflow:hidden;
-}
-
-and then in your code surround the img tag with a div with the class name of no_cake.
-
-Cheers
-
diff --git a/library/cropper/cropper.js b/library/cropper/cropper.js
deleted file mode 100644 (file)
index 427a9ba..0000000
+++ /dev/null
@@ -1,568 +0,0 @@
-/** \r
- * Copyright (c) 2006, David Spurr (http://www.defusion.org.uk/)\r
- * All rights reserved.\r
- * \r
- * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\r
- * \r
- *     * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\r
- *     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\r
- *     * Neither the name of the David Spurr nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\r
- * \r
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
- * \r
- * http://www.opensource.org/licenses/bsd-license.php\r
- * \r
- * See scriptaculous.js for full scriptaculous licence\r
- *\r
- * Modified 2013/06/01 Zach P to change $() to $PR() for eliminating conflicts with jQuery\r
- */\r
-\r
-var CropDraggable=Class.create();\r
-Object.extend(Object.extend(CropDraggable.prototype,Draggable.prototype),{initialize:function(_1){\r
-this.options=Object.extend({drawMethod:function(){\r
-}},arguments[1]||{});\r
-this.element=$PR(_1);\r
-this.handle=this.element;\r
-this.delta=this.currentDelta();\r
-this.dragging=false;\r
-this.eventMouseDown=this.initDrag.bindAsEventListener(this);\r
-Event.observe(this.handle,"mousedown",this.eventMouseDown);\r
-Draggables.register(this);\r
-},draw:function(_2){\r
-var _3=Position.cumulativeOffset(this.element);\r
-var d=this.currentDelta();\r
-_3[0]-=d[0];\r
-_3[1]-=d[1];\r
-var p=[0,1].map(function(i){\r
-return (_2[i]-_3[i]-this.offset[i]);\r
-}.bind(this));\r
-this.options.drawMethod(p);\r
-}});\r
-var Cropper={};\r
-Cropper.Img=Class.create();\r
-Cropper.Img.prototype={initialize:function(_7,_8){\r
-this.options=Object.extend({ratioDim:{x:0,y:0},minWidth:0,minHeight:0,displayOnInit:false,onEndCrop:Prototype.emptyFunction,captureKeys:true,onloadCoords:null,maxWidth:0,maxHeight:0},_8||{});\r
-this.img=$PR(_7);\r
-this.clickCoords={x:0,y:0};\r
-this.dragging=false;\r
-this.resizing=false;\r
-this.isWebKit=/Konqueror|Safari|KHTML/.test(navigator.userAgent);\r
-this.isIE=/MSIE/.test(navigator.userAgent);\r
-this.isOpera8=/Opera\s[1-8]/.test(navigator.userAgent);\r
-this.ratioX=0;\r
-this.ratioY=0;\r
-this.attached=false;\r
-this.fixedWidth=(this.options.maxWidth>0&&(this.options.minWidth>=this.options.maxWidth));\r
-this.fixedHeight=(this.options.maxHeight>0&&(this.options.minHeight>=this.options.maxHeight));\r
-if(typeof this.img=="undefined"){\r
-return;\r
-}\r
-$A(document.getElementsByTagName("script")).each(function(s){\r
-if(s.src.match(/cropper\.js/)){\r
-var _a=s.src.replace(/cropper\.js(.*)?/,"");\r
-var _b=document.createElement("link");\r
-_b.rel="stylesheet";\r
-_b.type="text/css";\r
-_b.href=_a+"cropper.css";\r
-_b.media="screen";\r
-document.getElementsByTagName("head")[0].appendChild(_b);\r
-}\r
-});\r
-if(this.options.ratioDim.x>0&&this.options.ratioDim.y>0){\r
-var _c=this.getGCD(this.options.ratioDim.x,this.options.ratioDim.y);\r
-this.ratioX=this.options.ratioDim.x/_c;\r
-this.ratioY=this.options.ratioDim.y/_c;\r
-}\r
-this.subInitialize();\r
-if(this.img.complete||this.isWebKit){\r
-this.onLoad();\r
-}else{\r
-Event.observe(this.img,"load",this.onLoad.bindAsEventListener(this));\r
-}\r
-},getGCD:function(a,b){\r
-if(b==0){\r
-return a;\r
-}\r
-return this.getGCD(b,a%b);\r
-},onLoad:function(){\r
-var _f="imgCrop_";\r
-var _10=this.img.parentNode;\r
-var _11="";\r
-if(this.isOpera8){\r
-_11=" opera8";\r
-}\r
-this.imgWrap=Builder.node("div",{"class":_f+"wrap"+_11});\r
-this.north=Builder.node("div",{"class":_f+"overlay "+_f+"north"},[Builder.node("span")]);\r
-this.east=Builder.node("div",{"class":_f+"overlay "+_f+"east"},[Builder.node("span")]);\r
-this.south=Builder.node("div",{"class":_f+"overlay "+_f+"south"},[Builder.node("span")]);\r
-this.west=Builder.node("div",{"class":_f+"overlay "+_f+"west"},[Builder.node("span")]);\r
-var _12=[this.north,this.east,this.south,this.west];\r
-this.dragArea=Builder.node("div",{"class":_f+"dragArea"},_12);\r
-this.handleN=Builder.node("div",{"class":_f+"handle "+_f+"handleN"});\r
-this.handleNE=Builder.node("div",{"class":_f+"handle "+_f+"handleNE"});\r
-this.handleE=Builder.node("div",{"class":_f+"handle "+_f+"handleE"});\r
-this.handleSE=Builder.node("div",{"class":_f+"handle "+_f+"handleSE"});\r
-this.handleS=Builder.node("div",{"class":_f+"handle "+_f+"handleS"});\r
-this.handleSW=Builder.node("div",{"class":_f+"handle "+_f+"handleSW"});\r
-this.handleW=Builder.node("div",{"class":_f+"handle "+_f+"handleW"});\r
-this.handleNW=Builder.node("div",{"class":_f+"handle "+_f+"handleNW"});\r
-this.selArea=Builder.node("div",{"class":_f+"selArea"},[Builder.node("div",{"class":_f+"marqueeHoriz "+_f+"marqueeNorth"},[Builder.node("span")]),Builder.node("div",{"class":_f+"marqueeVert "+_f+"marqueeEast"},[Builder.node("span")]),Builder.node("div",{"class":_f+"marqueeHoriz "+_f+"marqueeSouth"},[Builder.node("span")]),Builder.node("div",{"class":_f+"marqueeVert "+_f+"marqueeWest"},[Builder.node("span")]),this.handleN,this.handleNE,this.handleE,this.handleSE,this.handleS,this.handleSW,this.handleW,this.handleNW,Builder.node("div",{"class":_f+"clickArea"})]);\r
-this.imgWrap.appendChild(this.img);\r
-this.imgWrap.appendChild(this.dragArea);\r
-this.dragArea.appendChild(this.selArea);\r
-this.dragArea.appendChild(Builder.node("div",{"class":_f+"clickArea"}));\r
-_10.appendChild(this.imgWrap);\r
-this.startDragBind=this.startDrag.bindAsEventListener(this);\r
-Event.observe(this.dragArea,"mousedown",this.startDragBind);\r
-this.onDragBind=this.onDrag.bindAsEventListener(this);\r
-Event.observe(document,"mousemove",this.onDragBind);\r
-this.endCropBind=this.endCrop.bindAsEventListener(this);\r
-Event.observe(document,"mouseup",this.endCropBind);\r
-this.resizeBind=this.startResize.bindAsEventListener(this);\r
-this.handles=[this.handleN,this.handleNE,this.handleE,this.handleSE,this.handleS,this.handleSW,this.handleW,this.handleNW];\r
-this.registerHandles(true);\r
-if(this.options.captureKeys){\r
-this.keysBind=this.handleKeys.bindAsEventListener(this);\r
-Event.observe(document,"keypress",this.keysBind);\r
-}\r
-new CropDraggable(this.selArea,{drawMethod:this.moveArea.bindAsEventListener(this)});\r
-this.setParams();\r
-},registerHandles:function(_13){\r
-for(var i=0;i<this.handles.length;i++){\r
-var _15=$PR(this.handles[i]);\r
-if(_13){\r
-var _16=false;\r
-if(this.fixedWidth&&this.fixedHeight){\r
-_16=true;\r
-}else{\r
-if(this.fixedWidth||this.fixedHeight){\r
-var _17=_15.className.match(/([S|N][E|W])$/);\r
-var _18=_15.className.match(/(E|W)$/);\r
-var _19=_15.className.match(/(N|S)$/);\r
-if(_17){\r
-_16=true;\r
-}else{\r
-if(this.fixedWidth&&_18){\r
-_16=true;\r
-}else{\r
-if(this.fixedHeight&&_19){\r
-_16=true;\r
-}\r
-}\r
-}\r
-}\r
-}\r
-if(_16){\r
-_15.hide();\r
-}else{\r
-Event.observe(_15,"mousedown",this.resizeBind);\r
-}\r
-}else{\r
-_15.show();\r
-Event.stopObserving(_15,"mousedown",this.resizeBind);\r
-}\r
-}\r
-},setParams:function(){\r
-this.imgW=this.img.width;\r
-this.imgH=this.img.height;\r
-$PR(this.north).setStyle({height:0});\r
-$PR(this.east).setStyle({width:0,height:0});\r
-$PR(this.south).setStyle({height:0});\r
-$PR(this.west).setStyle({width:0,height:0});\r
-$PR(this.imgWrap).setStyle({"width":this.imgW+"px","height":this.imgH+"px"});\r
-$PR(this.selArea).hide();\r
-var _1a={x1:0,y1:0,x2:0,y2:0};\r
-var _1b=false;\r
-if(this.options.onloadCoords!=null){\r
-_1a=this.cloneCoords(this.options.onloadCoords);\r
-_1b=true;\r
-}else{\r
-if(this.options.ratioDim.x>0&&this.options.ratioDim.y>0){\r
-_1a.x1=Math.ceil((this.imgW-this.options.ratioDim.x)/2);\r
-_1a.y1=Math.ceil((this.imgH-this.options.ratioDim.y)/2);\r
-_1a.x2=_1a.x1+this.options.ratioDim.x;\r
-_1a.y2=_1a.y1+this.options.ratioDim.y;\r
-_1b=true;\r
-}\r
-}\r
-this.setAreaCoords(_1a,false,false,1);\r
-if(this.options.displayOnInit&&_1b){\r
-this.selArea.show();\r
-this.drawArea();\r
-this.endCrop();\r
-}\r
-this.attached=true;\r
-},remove:function(){\r
-if(this.attached){\r
-this.attached=false;\r
-this.imgWrap.parentNode.insertBefore(this.img,this.imgWrap);\r
-this.imgWrap.parentNode.removeChild(this.imgWrap);\r
-Event.stopObserving(this.dragArea,"mousedown",this.startDragBind);\r
-Event.stopObserving(document,"mousemove",this.onDragBind);\r
-Event.stopObserving(document,"mouseup",this.endCropBind);\r
-this.registerHandles(false);\r
-if(this.options.captureKeys){\r
-Event.stopObserving(document,"keypress",this.keysBind);\r
-}\r
-}\r
-},reset:function(){\r
-if(!this.attached){\r
-this.onLoad();\r
-}else{\r
-this.setParams();\r
-}\r
-this.endCrop();\r
-},handleKeys:function(e){\r
-var dir={x:0,y:0};\r
-if(!this.dragging){\r
-switch(e.keyCode){\r
-case (37):\r
-dir.x=-1;\r
-break;\r
-case (38):\r
-dir.y=-1;\r
-break;\r
-case (39):\r
-dir.x=1;\r
-break;\r
-case (40):\r
-dir.y=1;\r
-break;\r
-}\r
-if(dir.x!=0||dir.y!=0){\r
-if(e.shiftKey){\r
-dir.x*=10;\r
-dir.y*=10;\r
-}\r
-this.moveArea([this.areaCoords.x1+dir.x,this.areaCoords.y1+dir.y]);\r
-Event.stop(e);\r
-}\r
-}\r
-},calcW:function(){\r
-return (this.areaCoords.x2-this.areaCoords.x1);\r
-},calcH:function(){\r
-return (this.areaCoords.y2-this.areaCoords.y1);\r
-},moveArea:function(_1e){\r
-this.setAreaCoords({x1:_1e[0],y1:_1e[1],x2:_1e[0]+this.calcW(),y2:_1e[1]+this.calcH()},true,false);\r
-this.drawArea();\r
-},cloneCoords:function(_1f){\r
-return {x1:_1f.x1,y1:_1f.y1,x2:_1f.x2,y2:_1f.y2};\r
-},setAreaCoords:function(_20,_21,_22,_23,_24){\r
-if(_21){\r
-var _25=_20.x2-_20.x1;\r
-var _26=_20.y2-_20.y1;\r
-if(_20.x1<0){\r
-_20.x1=0;\r
-_20.x2=_25;\r
-}\r
-if(_20.y1<0){\r
-_20.y1=0;\r
-_20.y2=_26;\r
-}\r
-if(_20.x2>this.imgW){\r
-_20.x2=this.imgW;\r
-_20.x1=this.imgW-_25;\r
-}\r
-if(_20.y2>this.imgH){\r
-_20.y2=this.imgH;\r
-_20.y1=this.imgH-_26;\r
-}\r
-}else{\r
-if(_20.x1<0){\r
-_20.x1=0;\r
-}\r
-if(_20.y1<0){\r
-_20.y1=0;\r
-}\r
-if(_20.x2>this.imgW){\r
-_20.x2=this.imgW;\r
-}\r
-if(_20.y2>this.imgH){\r
-_20.y2=this.imgH;\r
-}\r
-if(_23!=null){\r
-if(this.ratioX>0){\r
-this.applyRatio(_20,{x:this.ratioX,y:this.ratioY},_23,_24);\r
-}else{\r
-if(_22){\r
-this.applyRatio(_20,{x:1,y:1},_23,_24);\r
-}\r
-}\r
-var _27=[this.options.minWidth,this.options.minHeight];\r
-var _28=[this.options.maxWidth,this.options.maxHeight];\r
-if(_27[0]>0||_27[1]>0||_28[0]>0||_28[1]>0){\r
-var _29={a1:_20.x1,a2:_20.x2};\r
-var _2a={a1:_20.y1,a2:_20.y2};\r
-var _2b={min:0,max:this.imgW};\r
-var _2c={min:0,max:this.imgH};\r
-if((_27[0]!=0||_27[1]!=0)&&_22){\r
-if(_27[0]>0){\r
-_27[1]=_27[0];\r
-}else{\r
-if(_27[1]>0){\r
-_27[0]=_27[1];\r
-}\r
-}\r
-}\r
-if((_28[0]!=0||_28[0]!=0)&&_22){\r
-if(_28[0]>0&&_28[0]<=_28[1]){\r
-_28[1]=_28[0];\r
-}else{\r
-if(_28[1]>0&&_28[1]<=_28[0]){\r
-_28[0]=_28[1];\r
-}\r
-}\r
-}\r
-if(_27[0]>0){\r
-this.applyDimRestriction(_29,_27[0],_23.x,_2b,"min");\r
-}\r
-if(_27[1]>1){\r
-this.applyDimRestriction(_2a,_27[1],_23.y,_2c,"min");\r
-}\r
-if(_28[0]>0){\r
-this.applyDimRestriction(_29,_28[0],_23.x,_2b,"max");\r
-}\r
-if(_28[1]>1){\r
-this.applyDimRestriction(_2a,_28[1],_23.y,_2c,"max");\r
-}\r
-_20={x1:_29.a1,y1:_2a.a1,x2:_29.a2,y2:_2a.a2};\r
-}\r
-}\r
-}\r
-this.areaCoords=_20;\r
-},applyDimRestriction:function(_2d,val,_2f,_30,_31){\r
-var _32;\r
-if(_31=="min"){\r
-_32=((_2d.a2-_2d.a1)<val);\r
-}else{\r
-_32=((_2d.a2-_2d.a1)>val);\r
-}\r
-if(_32){\r
-if(_2f==1){\r
-_2d.a2=_2d.a1+val;\r
-}else{\r
-_2d.a1=_2d.a2-val;\r
-}\r
-if(_2d.a1<_30.min){\r
-_2d.a1=_30.min;\r
-_2d.a2=val;\r
-}else{\r
-if(_2d.a2>_30.max){\r
-_2d.a1=_30.max-val;\r
-_2d.a2=_30.max;\r
-}\r
-}\r
-}\r
-},applyRatio:function(_33,_34,_35,_36){\r
-var _37;\r
-if(_36=="N"||_36=="S"){\r
-_37=this.applyRatioToAxis({a1:_33.y1,b1:_33.x1,a2:_33.y2,b2:_33.x2},{a:_34.y,b:_34.x},{a:_35.y,b:_35.x},{min:0,max:this.imgW});\r
-_33.x1=_37.b1;\r
-_33.y1=_37.a1;\r
-_33.x2=_37.b2;\r
-_33.y2=_37.a2;\r
-}else{\r
-_37=this.applyRatioToAxis({a1:_33.x1,b1:_33.y1,a2:_33.x2,b2:_33.y2},{a:_34.x,b:_34.y},{a:_35.x,b:_35.y},{min:0,max:this.imgH});\r
-_33.x1=_37.a1;\r
-_33.y1=_37.b1;\r
-_33.x2=_37.a2;\r
-_33.y2=_37.b2;\r
-}\r
-},applyRatioToAxis:function(_38,_39,_3a,_3b){\r
-var _3c=Object.extend(_38,{});\r
-var _3d=_3c.a2-_3c.a1;\r
-var _3e=Math.floor(_3d*_39.b/_39.a);\r
-var _3f;\r
-var _40;\r
-var _41=null;\r
-if(_3a.b==1){\r
-_3f=_3c.b1+_3e;\r
-if(_3f>_3b.max){\r
-_3f=_3b.max;\r
-_41=_3f-_3c.b1;\r
-}\r
-_3c.b2=_3f;\r
-}else{\r
-_3f=_3c.b2-_3e;\r
-if(_3f<_3b.min){\r
-_3f=_3b.min;\r
-_41=_3f+_3c.b2;\r
-}\r
-_3c.b1=_3f;\r
-}\r
-if(_41!=null){\r
-_40=Math.floor(_41*_39.a/_39.b);\r
-if(_3a.a==1){\r
-_3c.a2=_3c.a1+_40;\r
-}else{\r
-_3c.a1=_3c.a1=_3c.a2-_40;\r
-}\r
-}\r
-return _3c;\r
-},drawArea:function(){\r
-var _42=this.calcW();\r
-var _43=this.calcH();\r
-var px="px";\r
-var _45=[this.areaCoords.x1+px,this.areaCoords.y1+px,_42+px,_43+px,this.areaCoords.x2+px,this.areaCoords.y2+px,(this.img.width-this.areaCoords.x2)+px,(this.img.height-this.areaCoords.y2)+px];\r
-var _46=this.selArea.style;\r
-_46.left=_45[0];\r
-_46.top=_45[1];\r
-_46.width=_45[2];\r
-_46.height=_45[3];\r
-var _47=Math.ceil((_42-6)/2)+px;\r
-var _48=Math.ceil((_43-6)/2)+px;\r
-this.handleN.style.left=_47;\r
-this.handleE.style.top=_48;\r
-this.handleS.style.left=_47;\r
-this.handleW.style.top=_48;\r
-this.north.style.height=_45[1];\r
-var _49=this.east.style;\r
-_49.top=_45[1];\r
-_49.height=_45[3];\r
-_49.left=_45[4];\r
-_49.width=_45[6];\r
-var _4a=this.south.style;\r
-_4a.top=_45[5];\r
-_4a.height=_45[7];\r
-var _4b=this.west.style;\r
-_4b.top=_45[1];\r
-_4b.height=_45[3];\r
-_4b.width=_45[0];\r
-this.subDrawArea();\r
-this.forceReRender();\r
-},forceReRender:function(){\r
-if(this.isIE||this.isWebKit){\r
-var n=document.createTextNode(" ");\r
-var d,el,fixEL,i;\r
-if(this.isIE){\r
-fixEl=this.selArea;\r
-}else{\r
-if(this.isWebKit){\r
-fixEl=document.getElementsByClassName("imgCrop_marqueeSouth",this.imgWrap)[0];\r
-d=Builder.node("div","");\r
-d.style.visibility="hidden";\r
-var _4e=["SE","S","SW"];\r
-for(i=0;i<_4e.length;i++){\r
-el=document.getElementsByClassName("imgCrop_handle"+_4e[i],this.selArea)[0];\r
-if(el.childNodes.length){\r
-el.removeChild(el.childNodes[0]);\r
-}\r
-el.appendChild(d);\r
-}\r
-}\r
-}\r
-fixEl.appendChild(n);\r
-fixEl.removeChild(n);\r
-}\r
-},startResize:function(e){\r
-this.startCoords=this.cloneCoords(this.areaCoords);\r
-this.resizing=true;\r
-this.resizeHandle=Event.element(e).classNames().toString().replace(/([^N|NE|E|SE|S|SW|W|NW])+/,"");\r
-Event.stop(e);\r
-},startDrag:function(e){\r
-this.selArea.show();\r
-this.clickCoords=this.getCurPos(e);\r
-this.setAreaCoords({x1:this.clickCoords.x,y1:this.clickCoords.y,x2:this.clickCoords.x,y2:this.clickCoords.y},false,false,null);\r
-this.dragging=true;\r
-this.onDrag(e);\r
-Event.stop(e);\r
-},getCurPos:function(e){\r
-var el=this.imgWrap,wrapOffsets=Position.cumulativeOffset(el);\r
-while(el.nodeName!="BODY"){\r
-wrapOffsets[1]-=el.scrollTop||0;\r
-wrapOffsets[0]-=el.scrollLeft||0;\r
-el=el.parentNode;\r
-}\r
-return curPos={x:Event.pointerX(e)-wrapOffsets[0],y:Event.pointerY(e)-wrapOffsets[1]};\r
-},onDrag:function(e){\r
-if(this.dragging||this.resizing){\r
-var _54=null;\r
-var _55=this.getCurPos(e);\r
-var _56=this.cloneCoords(this.areaCoords);\r
-var _57={x:1,y:1};\r
-if(this.dragging){\r
-if(_55.x<this.clickCoords.x){\r
-_57.x=-1;\r
-}\r
-if(_55.y<this.clickCoords.y){\r
-_57.y=-1;\r
-}\r
-this.transformCoords(_55.x,this.clickCoords.x,_56,"x");\r
-this.transformCoords(_55.y,this.clickCoords.y,_56,"y");\r
-}else{\r
-if(this.resizing){\r
-_54=this.resizeHandle;\r
-if(_54.match(/E/)){\r
-this.transformCoords(_55.x,this.startCoords.x1,_56,"x");\r
-if(_55.x<this.startCoords.x1){\r
-_57.x=-1;\r
-}\r
-}else{\r
-if(_54.match(/W/)){\r
-this.transformCoords(_55.x,this.startCoords.x2,_56,"x");\r
-if(_55.x<this.startCoords.x2){\r
-_57.x=-1;\r
-}\r
-}\r
-}\r
-if(_54.match(/N/)){\r
-this.transformCoords(_55.y,this.startCoords.y2,_56,"y");\r
-if(_55.y<this.startCoords.y2){\r
-_57.y=-1;\r
-}\r
-}else{\r
-if(_54.match(/S/)){\r
-this.transformCoords(_55.y,this.startCoords.y1,_56,"y");\r
-if(_55.y<this.startCoords.y1){\r
-_57.y=-1;\r
-}\r
-}\r
-}\r
-}\r
-}\r
-this.setAreaCoords(_56,false,e.shiftKey,_57,_54);\r
-this.drawArea();\r
-Event.stop(e);\r
-}\r
-},transformCoords:function(_58,_59,_5a,_5b){\r
-var _5c=[_58,_59];\r
-if(_58>_59){\r
-_5c.reverse();\r
-}\r
-_5a[_5b+"1"]=_5c[0];\r
-_5a[_5b+"2"]=_5c[1];\r
-},endCrop:function(){\r
-this.dragging=false;\r
-this.resizing=false;\r
-this.options.onEndCrop(this.areaCoords,{width:this.calcW(),height:this.calcH()});\r
-},subInitialize:function(){\r
-},subDrawArea:function(){\r
-}};\r
-Cropper.ImgWithPreview=Class.create();\r
-Object.extend(Object.extend(Cropper.ImgWithPreview.prototype,Cropper.Img.prototype),{subInitialize:function(){\r
-this.hasPreviewImg=false;\r
-if(typeof (this.options.previewWrap)!="undefined"&&this.options.minWidth>0&&this.options.minHeight>0){\r
-this.previewWrap=$PR(this.options.previewWrap);\r
-this.previewImg=this.img.cloneNode(false);\r
-this.previewImg.id="imgCrop_"+this.previewImg.id;\r
-this.options.displayOnInit=true;\r
-this.hasPreviewImg=true;\r
-this.previewWrap.addClassName("imgCrop_previewWrap");\r
-this.previewWrap.setStyle({width:this.options.minWidth+"px",height:this.options.minHeight+"px"});\r
-this.previewWrap.appendChild(this.previewImg);\r
-}\r
-},subDrawArea:function(){\r
-if(this.hasPreviewImg){\r
-var _5d=this.calcW();\r
-var _5e=this.calcH();\r
-var _5f={x:this.imgW/_5d,y:this.imgH/_5e};\r
-var _60={x:_5d/this.options.minWidth,y:_5e/this.options.minHeight};\r
-var _61={w:Math.ceil(this.options.minWidth*_5f.x)+"px",h:Math.ceil(this.options.minHeight*_5f.y)+"px",x:"-"+Math.ceil(this.areaCoords.x1/_60.x)+"px",y:"-"+Math.ceil(this.areaCoords.y1/_60.y)+"px"};\r
-var _62=this.previewImg.style;\r
-_62.width=_61.w;\r
-_62.height=_61.h;\r
-_62.left=_61.x;\r
-_62.top=_61.y;\r
-}\r
-}});\r
-\r
diff --git a/library/cropper/cropper.uncompressed.js b/library/cropper/cropper.uncompressed.js
deleted file mode 100644 (file)
index 0ea0b80..0000000
+++ /dev/null
@@ -1,1333 +0,0 @@
-/**\r
- * Image Cropper (v. 1.2.0 - 2006-10-30 )\r
- * Copyright (c) 2006 David Spurr (http://www.defusion.org.uk/)\r
- * \r
- * The image cropper provides a way to draw a crop area on an image and capture\r
- * the coordinates of the drawn crop area.\r
- * \r
- * Features include:\r
- *             - Based on Prototype and Scriptaculous\r
- *             - Image editing package styling, the crop area functions and looks \r
- *               like those found in popular image editing software\r
- *             - Dynamic inclusion of required styles\r
- *             - Drag to draw areas\r
- *             - Shift drag to draw/resize areas as squares\r
- *             - Selection area can be moved \r
- *             - Seleciton area can be resized using resize handles\r
- *             - Allows dimension ratio limited crop areas\r
- *             - Allows minimum dimension crop areas\r
- *             - Allows maximum dimesion crop areas\r
- *             - If both min & max dimension options set to the same value for a single axis,then the cropper will not \r
- *               display the resize handles as appropriate (when min & max dimensions are passed for both axes this\r
- *               results in a 'fixed size' crop area)\r
- *             - Allows dynamic preview of resultant crop ( if minimum width & height are provided ), this is\r
- *               implemented as a subclass so can be excluded when not required\r
- *             - Movement of selection area by arrow keys ( shift + arrow key will move selection area by\r
- *               10 pixels )\r
- *             - All operations stay within bounds of image\r
- *             - All functionality & display compatible with most popular browsers supported by Prototype:\r
- *                     PC:     IE 7, 6 & 5.5, Firefox 1.5, Opera 8.5 (see known issues) & 9.0b\r
- *                     MAC: Camino 1.0, Firefox 1.5, Safari 2.0\r
- * \r
- * Requires:\r
- *             - Prototype v. 1.5.0_rc0 > (as packaged with Scriptaculous 1.6.1)\r
- *             - Scriptaculous v. 1.6.1 > modules: builder, dragdrop \r
- *             \r
- * Known issues:\r
- *             - Safari animated gifs, only one of each will animate, this seems to be a known Safari issue\r
- * \r
- *             - After drawing an area and then clicking to start a new drag in IE 5.5 the rendered height \r
- *        appears as the last height until the user drags, this appears to be the related to the error \r
- *        that the forceReRender() method fixes for IE 6, i.e. IE 5.5 is not redrawing the box properly.\r
- * \r
- *             - Lack of CSS opacity support in Opera before version 9 mean we disable those style rules, these \r
- *               could be fixed by using PNGs with transparency if Opera 8.5 support is high priority for you\r
- * \r
- *             - Marching ants keep reloading in IE <6 (not tested in IE7), it is a known issue in IE and I have \r
- *        found no viable workarounds that can be included in the release. If this really is an issue for you\r
- *        either try this post: http://mir.aculo.us/articles/2005/08/28/internet-explorer-and-ajax-image-caching-woes\r
- *        or uncomment the 'FIX MARCHING ANTS IN IE' rules in the CSS file\r
- *             \r
- *             - Styling & borders on image, any CSS styling applied directly to the image itself (floats, borders, padding, margin, etc.) will \r
- *               cause problems with the cropper. The use of a wrapper element to apply these styles to is recommended.\r
- * \r
- *             - overflow: auto or overflow: scroll on parent will cause cropper to burst out of parent in IE and Opera (maybe Mac browsers too)\r
- *               I'm not sure why yet.\r
- * \r
- * Usage:\r
- *             See Cropper.Img & Cropper.ImgWithPreview for usage details\r
- * \r
- * Changelog:\r
- * v1.2.0 - 2006-10-30\r
- *             + Added id to the preview image element using 'imgCrop_[originalImageID]'\r
- *      * #00001 - Fixed bug: Doesn't account for scroll offsets\r
- *      * #00009 - Fixed bug: Placing the cropper inside differently positioned elements causes incorrect co-ordinates and display\r
- *      * #00013 - Fixed bug: I-bar cursor appears on drag plane\r
- *      * #00014 - Fixed bug: If ID for image tag is not found in document script throws error\r
- *      * Fixed bug with drag start co-ordinates if wrapper element has moved in browser (e.g. dragged to a new position)\r
- *      * Fixed bug with drag start co-ordinates if image contained in a wrapper with scrolling - this may be buggy if image \r
- *               has other ancestors with scrolling applied (except the body)\r
- *      * #00015 - Fixed bug: When cropper removed and then reapplied onEndCrop callback gets called multiple times, solution suggestion from Bill Smith\r
- *      * Various speed increases & code cleanup which meant improved performance in Mac - which allowed removal of different overlay methods for\r
- *        IE and all other browsers, which led to a fix for:\r
- *             * #00010 - Fixed bug: Select area doesn't adhere to image size when image resized using img attributes\r
- *      - #00006 - Removed default behaviour of automatically setting a ratio when both min width & height passed, the ratioDimensions must be passed in\r
- *             + #00005 - Added ability to set maximum crop dimensions, if both min & max set as the same value then we'll get a fixed cropper size on the axes as appropriate\r
- *        and the resize handles will not be displayed as appropriate\r
- *             * Switched keydown for keypress for moving select area with cursor keys (makes for nicer action) - doesn't appear to work in Safari\r
- * \r
- * v1.1.3 - 2006-08-21\r
- *             * Fixed wrong cursor on western handle in CSS\r
- *             + #00008 & #00003 - Added feature: Allow to set dimensions & position for cropper on load\r
- *      * #00002 - Fixed bug: Pressing 'remove cropper' twice removes image in IE\r
- * \r
- * v1.1.2 - 2006-06-09\r
- *             * Fixed bugs with ratios when GCD is low (patch submitted by Andy Skelton)\r
- * \r
- * v1.1.1 - 2006-06-03\r
- *             * Fixed bug with rendering issues fix in IE 5.5\r
- *             * Fixed bug with endCrop callback issues once cropper had been removed & reset in IE\r
- * \r
- * v1.1.0 - 2006-06-02\r
- *             * Fixed bug with IE constantly trying to reload select area background image\r
- *             * Applied more robust fix to Safari & IE rendering issues\r
- *             + Added method to reset parameters - useful for when dynamically changing img cropper attached to\r
- *             + Added method to remove cropper from image\r
- * \r
- * v1.0.0 - 2006-05-18 \r
- *             + Initial verison\r
- * \r
- * \r
- * Copyright (c) 2006, David Spurr (http://www.defusion.org.uk/)\r
- * All rights reserved.\r
- * \r
- * \r
- * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\r
- * \r
- *     * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\r
- *     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\r
- *     * Neither the name of the David Spurr nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\r
- * \r
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
- * \r
- * http://www.opensource.org/licenses/bsd-license.php\r
- * \r
- * See scriptaculous.js for full scriptaculous licence\r
- *\r
- * Modified 2013/06/01 Zach P to change $() to $PR() for eliminating conflicts with jQuery\r
- */\r
\r
-/**\r
- * Extend the Draggable class to allow us to pass the rendering\r
- * down to the Cropper object.\r
- */\r
-var CropDraggable = Class.create();\r
-\r
-Object.extend( Object.extend( CropDraggable.prototype, Draggable.prototype), {\r
-       \r
-       initialize: function(element) {\r
-               this.options = Object.extend(\r
-                       {\r
-                               /**\r
-                                * The draw method to defer drawing to\r
-                                */\r
-                               drawMethod: function() {}\r
-                       }, \r
-                       arguments[1] || {}\r
-               );\r
-\r
-               this.element = $PR(element);\r
-\r
-               this.handle = this.element;\r
-\r
-               this.delta    = this.currentDelta();\r
-               this.dragging = false;   \r
-\r
-               this.eventMouseDown = this.initDrag.bindAsEventListener(this);\r
-               Event.observe(this.handle, "mousedown", this.eventMouseDown);\r
-\r
-               Draggables.register(this);\r
-       },\r
-       \r
-       /**\r
-        * Defers the drawing of the draggable to the supplied method\r
-        */\r
-       draw: function(point) {\r
-               var pos = Position.cumulativeOffset(this.element);\r
-               var d = this.currentDelta();\r
-               pos[0] -= d[0]; \r
-               pos[1] -= d[1];\r
-                               \r
-               var p = [0,1].map(function(i) { \r
-                       return (point[i]-pos[i]-this.offset[i]) \r
-               }.bind(this));\r
-                               \r
-               this.options.drawMethod( p );\r
-       }\r
-       \r
-});\r
-\r
-\r
-/**\r
- * The Cropper object, this will attach itself to the provided image by wrapping it with \r
- * the generated xHTML structure required by the cropper.\r
- * \r
- * Usage:\r
- *     @param obj Image element to attach to\r
- *     @param obj Optional options:\r
- *             - ratioDim obj \r
- *                     The pixel dimensions to apply as a restrictive ratio, with properties x & y\r
- * \r
- *             - minWidth int \r
- *                     The minimum width for the select area in pixels\r
- * \r
- *             - minHeight     int \r
- *                     The mimimum height for the select area in pixels\r
- * \r
- *             - maxWidth int\r
- *                     The maximum width for the select areas in pixels (if both minWidth & maxWidth set to same the width of the cropper will be fixed)\r
- * \r
- *             - maxHeight int\r
- *                     The maximum height for the select areas in pixels (if both minHeight & maxHeight set to same the height of the cropper will be fixed)\r
- * \r
- *             - displayOnInit int \r
- *                     Whether to display the select area on initialisation, only used when providing minimum width & height or ratio\r
- * \r
- *             - onEndCrop func\r
- *                     The callback function to provide the crop details to on end of a crop (see below)\r
- * \r
- *             - captureKeys boolean\r
- *                     Whether to capture the keys for moving the select area, as these can cause some problems at the moment\r
- * \r
- *             - onloadCoords obj\r
- *                     A coordinates object with properties x1, y1, x2 & y2; for the coordinates of the select area to display onload\r
- *     \r
- *----------------------------------------------\r
- * \r
- * The callback function provided via the onEndCrop option should accept the following parameters:\r
- *             - coords obj\r
- *                     The coordinates object with properties x1, y1, x2 & y2; for the coordinates of the select area\r
- * \r
- *             - dimensions obj\r
- *                     The dimensions object with properites width & height; for the dimensions of the select area\r
- *             \r
- *\r
- *             Example:\r
- *                     function onEndCrop( coords, dimensions ) {\r
- *                             $PR( 'x1' ).value       = coords.x1;\r
- *                             $PR( 'y1' ).value       = coords.y1;\r
- *                             $PR( 'x2' ).value       = coords.x2;\r
- *                             $PR( 'y2' ).value       = coords.y2;\r
- *                             $PR( 'width' ).value    = dimensions.width;\r
- *                             $PR( 'height' ).value   = dimensions.height;\r
- *                     }\r
- * \r
- */\r
-var Cropper = {};\r
-Cropper.Img = Class.create();\r
-Cropper.Img.prototype = {\r
-       \r
-       /**\r
-        * Initialises the class\r
-        * \r
-        * @access public\r
-        * @param obj Image element to attach to\r
-        * @param obj Options\r
-        * @return void\r
-        */\r
-       initialize: function(element, options) {\r
-               this.options = Object.extend(\r
-                       {\r
-                               /**\r
-                                * @var obj\r
-                                * The pixel dimensions to apply as a restrictive ratio\r
-                                */\r
-                               ratioDim: { x: 0, y: 0 },\r
-                               /**\r
-                                * @var int\r
-                                * The minimum pixel width, also used as restrictive ratio if min height passed too\r
-                                */\r
-                               minWidth:               0,\r
-                               /**\r
-                                * @var int\r
-                                * The minimum pixel height, also used as restrictive ratio if min width passed too\r
-                                */\r
-                               minHeight:              0,\r
-                               /**\r
-                                * @var boolean\r
-                                * Whether to display the select area on initialisation, only used when providing minimum width & height or ratio\r
-                                */\r
-                               displayOnInit:  false,\r
-                               /**\r
-                                * @var function\r
-                                * The call back function to pass the final values to\r
-                                */\r
-                               onEndCrop: Prototype.emptyFunction,\r
-                               /**\r
-                                * @var boolean\r
-                                * Whether to capture key presses or not\r
-                                */\r
-                               captureKeys: true,\r
-                               /**\r
-                                * @var obj Coordinate object x1, y1, x2, y2\r
-                                * The coordinates to optionally display the select area at onload\r
-                                */\r
-                               onloadCoords: null,\r
-                               /**\r
-                                * @var int\r
-                                * The maximum width for the select areas in pixels (if both minWidth & maxWidth set to same the width of the cropper will be fixed)\r
-                                */\r
-                               maxWidth: 0,\r
-                               /**\r
-                                * @var int\r
-                                * The maximum height for the select areas in pixels (if both minHeight & maxHeight set to same the height of the cropper will be fixed)\r
-                                */\r
-                               maxHeight: 0\r
-                       }, \r
-                       options || {}\r
-               );                              \r
-               /**\r
-                * @var obj\r
-                * The img node to attach to\r
-                */\r
-               this.img                        = $PR( element );\r
-               /**\r
-                * @var obj\r
-                * The x & y coordinates of the click point\r
-                */\r
-               this.clickCoords        = { x: 0, y: 0 };\r
-               /**\r
-                * @var boolean\r
-                * Whether the user is dragging\r
-                */\r
-               this.dragging           = false;\r
-               /**\r
-                * @var boolean\r
-                * Whether the user is resizing\r
-                */\r
-               this.resizing           = false;\r
-               /**\r
-                * @var boolean\r
-                * Whether the user is on a webKit browser\r
-                */\r
-               this.isWebKit           = /Konqueror|Safari|KHTML/.test( navigator.userAgent );\r
-               /**\r
-                * @var boolean\r
-                * Whether the user is on IE\r
-                */\r
-               this.isIE                       = /MSIE/.test( navigator.userAgent );\r
-               /**\r
-                * @var boolean\r
-                * Whether the user is on Opera below version 9\r
-                */\r
-               this.isOpera8           = /Opera\s[1-8]/.test( navigator.userAgent );\r
-               /**\r
-                * @var int\r
-                * The x ratio \r
-                */\r
-               this.ratioX                     = 0;\r
-               /**\r
-                * @var int\r
-                * The y ratio\r
-                */\r
-               this.ratioY                     = 0;\r
-               /**\r
-                * @var boolean\r
-                * Whether we've attached sucessfully\r
-                */\r
-               this.attached           = false;\r
-               /**\r
-                * @var boolean\r
-                * Whether we've got a fixed width (if minWidth EQ or GT maxWidth then we have a fixed width\r
-                * in the case of minWidth > maxWidth maxWidth wins as the fixed width)\r
-                */\r
-               this.fixedWidth         = ( this.options.maxWidth > 0 && ( this.options.minWidth >= this.options.maxWidth ) );\r
-               /**\r
-                * @var boolean\r
-                * Whether we've got a fixed height (if minHeight EQ or GT maxHeight then we have a fixed height\r
-                * in the case of minHeight > maxHeight maxHeight wins as the fixed height)\r
-                */\r
-               this.fixedHeight        = ( this.options.maxHeight > 0 && ( this.options.minHeight >= this.options.maxHeight ) );\r
-               \r
-               // quit if the image element doesn't exist\r
-               if( typeof this.img == 'undefined' ) return;\r
-                               \r
-               // include the stylesheet               \r
-               $A( document.getElementsByTagName( 'script' ) ).each( \r
-                       function(s) {\r
-                               if( s.src.match( /cropper\.js/ ) ) {\r
-                                       var path        = s.src.replace( /cropper\.js(.*)?/, '' );\r
-                                       // '<link rel="stylesheet" type="text/css" href="' + path + 'cropper.css" media="screen" />';\r
-                                       var style               = document.createElement( 'link' );\r
-                                       style.rel               = 'stylesheet';\r
-                                       style.type              = 'text/css';\r
-                                       style.href              = path + 'cropper.css';\r
-                                       style.media     = 'screen';\r
-                                       document.getElementsByTagName( 'head' )[0].appendChild( style );\r
-                               }\r
-               }\r
-           );   \r
-       \r
-               // calculate the ratio when neccessary\r
-               if( this.options.ratioDim.x > 0 && this.options.ratioDim.y > 0 ) {\r
-                       var gcd = this.getGCD( this.options.ratioDim.x, this.options.ratioDim.y );\r
-                       this.ratioX = this.options.ratioDim.x / gcd;\r
-                       this.ratioY = this.options.ratioDim.y / gcd;\r
-                       // dump( 'RATIO : ' + this.ratioX + ':' + this.ratioY + '\n' );\r
-               }\r
-                                                       \r
-               // initialise sub classes\r
-               this.subInitialize();\r
-\r
-               // only load the event observers etc. once the image is loaded\r
-               // this is done after the subInitialize() call just in case the sub class does anything\r
-               // that will affect the result of the call to onLoad()\r
-               if( this.img.complete || this.isWebKit ) this.onLoad(); // for some reason Safari seems to support img.complete but returns 'undefined' on the this.img object\r
-               else Event.observe( this.img, 'load', this.onLoad.bindAsEventListener( this) );         \r
-       },\r
-       \r
-       /**\r
-        * The Euclidean algorithm used to find the greatest common divisor\r
-        * \r
-        * @acces private\r
-        * @param int Value 1\r
-        * @param int Value 2\r
-        * @return int\r
-        */\r
-       getGCD : function( a , b ) {\r
-               if( b == 0 ) return a;\r
-               return this.getGCD(b, a % b );\r
-       },\r
-       \r
-       /**\r
-        * Attaches the cropper to the image once it has loaded\r
-        * \r
-        * @access private\r
-        * @return void\r
-        */\r
-       onLoad: function( ) {\r
-               /*\r
-                * Build the container and all related elements, will result in the following\r
-                *\r
-                * <div class="imgCrop_wrap">\r
-                *              <img ... this.img ... />\r
-                *              <div class="imgCrop_dragArea">\r
-                *                      <!-- the inner spans are only required for IE to stop it making the divs 1px high/wide -->\r
-                *                      <div class="imgCrop_overlay imageCrop_north"><span></span></div>\r
-                *                      <div class="imgCrop_overlay imageCrop_east"><span></span></div>\r
-                *                      <div class="imgCrop_overlay imageCrop_south"><span></span></div>\r
-                *                      <div class="imgCrop_overlay imageCrop_west"><span></span></div>\r
-                *                      <div class="imgCrop_selArea">\r
-                *                              <!-- marquees -->\r
-                *                              <!-- the inner spans are only required for IE to stop it making the divs 1px high/wide -->\r
-                *                              <div class="imgCrop_marqueeHoriz imgCrop_marqueeNorth"><span></span></div>\r
-                *                              <div class="imgCrop_marqueeVert imgCrop_marqueeEast"><span></span></div>\r
-                *                              <div class="imgCrop_marqueeHoriz imgCrop_marqueeSouth"><span></span></div>\r
-                *                              <div class="imgCrop_marqueeVert imgCrop_marqueeWest"><span></span></div>                        \r
-                *                              <!-- handles -->\r
-                *                              <div class="imgCrop_handle imgCrop_handleN"></div>\r
-                *                              <div class="imgCrop_handle imgCrop_handleNE"></div>\r
-                *                              <div class="imgCrop_handle imgCrop_handleE"></div>\r
-                *                              <div class="imgCrop_handle imgCrop_handleSE"></div>\r
-                *                              <div class="imgCrop_handle imgCrop_handleS"></div>\r
-                *                              <div class="imgCrop_handle imgCrop_handleSW"></div>\r
-                *                              <div class="imgCrop_handle imgCrop_handleW"></div>\r
-                *                              <div class="imgCrop_handle imgCrop_handleNW"></div>\r
-                *                              <div class="imgCrop_clickArea"></div>\r
-                *                      </div>  \r
-                *                      <div class="imgCrop_clickArea"></div>\r
-                *              </div>  \r
-                * </div>\r
-                */\r
-               var cNamePrefix = 'imgCrop_';\r
-               \r
-               // get the point to insert the container\r
-               var insertPoint = this.img.parentNode;\r
-               \r
-               // apply an extra class to the wrapper to fix Opera below version 9\r
-               var fixOperaClass = '';\r
-               if( this.isOpera8 ) fixOperaClass = ' opera8';\r
-               this.imgWrap = Builder.node( 'div', { 'class': cNamePrefix + 'wrap' + fixOperaClass } );\r
-               \r
-               this.north              = Builder.node( 'div', { 'class': cNamePrefix + 'overlay ' + cNamePrefix + 'north' }, [Builder.node( 'span' )] );\r
-               this.east               = Builder.node( 'div', { 'class': cNamePrefix + 'overlay ' + cNamePrefix + 'east' } , [Builder.node( 'span' )] );\r
-               this.south              = Builder.node( 'div', { 'class': cNamePrefix + 'overlay ' + cNamePrefix + 'south' }, [Builder.node( 'span' )] );\r
-               this.west               = Builder.node( 'div', { 'class': cNamePrefix + 'overlay ' + cNamePrefix + 'west' } , [Builder.node( 'span' )] );\r
-               \r
-               var overlays    = [ this.north, this.east, this.south, this.west ];\r
-\r
-               this.dragArea   = Builder.node( 'div', { 'class': cNamePrefix + 'dragArea' }, overlays );\r
-                                               \r
-               this.handleN    = Builder.node( 'div', { 'class': cNamePrefix + 'handle ' + cNamePrefix + 'handleN' } );\r
-               this.handleNE   = Builder.node( 'div', { 'class': cNamePrefix + 'handle ' + cNamePrefix + 'handleNE' } );\r
-               this.handleE    = Builder.node( 'div', { 'class': cNamePrefix + 'handle ' + cNamePrefix + 'handleE' } );\r
-               this.handleSE   = Builder.node( 'div', { 'class': cNamePrefix + 'handle ' + cNamePrefix + 'handleSE' } );\r
-               this.handleS    = Builder.node( 'div', { 'class': cNamePrefix + 'handle ' + cNamePrefix + 'handleS' } );\r
-               this.handleSW   = Builder.node( 'div', { 'class': cNamePrefix + 'handle ' + cNamePrefix + 'handleSW' } );\r
-               this.handleW    = Builder.node( 'div', { 'class': cNamePrefix + 'handle ' + cNamePrefix + 'handleW' } );\r
-               this.handleNW   = Builder.node( 'div', { 'class': cNamePrefix + 'handle ' + cNamePrefix + 'handleNW' } );\r
-                               \r
-               this.selArea    = Builder.node( 'div', { 'class': cNamePrefix + 'selArea' },\r
-                       [\r
-                               Builder.node( 'div', { 'class': cNamePrefix + 'marqueeHoriz ' + cNamePrefix + 'marqueeNorth' }, [Builder.node( 'span' )] ),\r
-                               Builder.node( 'div', { 'class': cNamePrefix + 'marqueeVert ' + cNamePrefix + 'marqueeEast' }  , [Builder.node( 'span' )] ),\r
-                               Builder.node( 'div', { 'class': cNamePrefix + 'marqueeHoriz ' + cNamePrefix + 'marqueeSouth' }, [Builder.node( 'span' )] ),\r
-                               Builder.node( 'div', { 'class': cNamePrefix + 'marqueeVert ' + cNamePrefix + 'marqueeWest' }  , [Builder.node( 'span' )] ),\r
-                               this.handleN,\r
-                               this.handleNE,\r
-                               this.handleE,\r
-                               this.handleSE,\r
-                               this.handleS,\r
-                               this.handleSW,\r
-                               this.handleW,\r
-                               this.handleNW,\r
-                               Builder.node( 'div', { 'class': cNamePrefix + 'clickArea' } )\r
-                       ]\r
-               );\r
-                               \r
-               this.imgWrap.appendChild( this.img );\r
-               this.imgWrap.appendChild( this.dragArea );\r
-               this.dragArea.appendChild( this.selArea );\r
-               this.dragArea.appendChild( Builder.node( 'div', { 'class': cNamePrefix + 'clickArea' } ) );\r
-\r
-               insertPoint.appendChild( this.imgWrap );\r
-\r
-               // add event observers\r
-               this.startDragBind      = this.startDrag.bindAsEventListener( this );\r
-               Event.observe( this.dragArea, 'mousedown', this.startDragBind );\r
-               \r
-               this.onDragBind         = this.onDrag.bindAsEventListener( this );\r
-               Event.observe( document, 'mousemove', this.onDragBind );\r
-               \r
-               this.endCropBind        = this.endCrop.bindAsEventListener( this );\r
-               Event.observe( document, 'mouseup', this.endCropBind );\r
-               \r
-               this.resizeBind         = this.startResize.bindAsEventListener( this );\r
-               this.handles = [ this.handleN, this.handleNE, this.handleE, this.handleSE, this.handleS, this.handleSW, this.handleW, this.handleNW ];\r
-               this.registerHandles( true );\r
-               \r
-               if( this.options.captureKeys ) {\r
-                       this.keysBind = this.handleKeys.bindAsEventListener( this );\r
-                       Event.observe( document, 'keypress', this.keysBind );\r
-               }\r
-\r
-               // attach the dragable to the select area\r
-               new CropDraggable( this.selArea, { drawMethod: this.moveArea.bindAsEventListener( this ) } );\r
-               \r
-               this.setParams();\r
-       },\r
-       \r
-       /**\r
-        * Manages adding or removing the handle event handler and hiding or displaying them as appropriate\r
-        * \r
-        * @access private\r
-        * @param boolean registration true = add, false = remove\r
-        * @return void\r
-        */\r
-       registerHandles: function( registration ) {     \r
-               for( var i = 0; i < this.handles.length; i++ ) {\r
-                       var handle = $PR( this.handles[i] );\r
-                       \r
-                       if( registration ) {\r
-                               var hideHandle  = false;        // whether to hide the handle\r
-                               \r
-                               // disable handles asappropriate if we've got fixed dimensions\r
-                               // if both dimensions are fixed we don't need to do much\r
-                               if( this.fixedWidth && this.fixedHeight ) hideHandle = true;\r
-                               else if( this.fixedWidth || this.fixedHeight ) {\r
-                                       // if one of the dimensions is fixed then just hide those handles\r
-                                       var isCornerHandle      = handle.className.match( /([S|N][E|W])$/ )\r
-                                       var isWidthHandle       = handle.className.match( /(E|W)$/ );\r
-                                       var isHeightHandle      = handle.className.match( /(N|S)$/ );\r
-                                       if( isCornerHandle ) hideHandle = true;\r
-                                       else if( this.fixedWidth && isWidthHandle ) hideHandle = true;\r
-                                       else if( this.fixedHeight && isHeightHandle ) hideHandle = true;\r
-                               }\r
-                               if( hideHandle ) handle.hide();\r
-                               else Event.observe( handle, 'mousedown', this.resizeBind );\r
-                       } else {\r
-                               handle.show();\r
-                               Event.stopObserving( handle, 'mousedown', this.resizeBind );\r
-                       }\r
-               }\r
-       },\r
-               \r
-       /**\r
-        * Sets up all the cropper parameters, this can be used to reset the cropper when dynamically\r
-        * changing the images\r
-        * \r
-        * @access private\r
-        * @return void\r
-        */\r
-       setParams: function() {\r
-               /**\r
-                * @var int\r
-                * The image width\r
-                */\r
-               this.imgW = this.img.width;\r
-               /**\r
-                * @var int\r
-                * The image height\r
-                */\r
-               this.imgH = this.img.height;                    \r
-\r
-               $PR( this.north ).setStyle( { height: 0 } );\r
-               $PR( this.east ).setStyle( { width: 0, height: 0 } );\r
-               $PR( this.south ).setStyle( { height: 0 } );\r
-               $PR( this.west ).setStyle( { width: 0, height: 0 } );\r
-               \r
-               // resize the container to fit the image\r
-               $PR( this.imgWrap ).setStyle( { 'width': this.imgW + 'px', 'height': this.imgH + 'px' } );\r
-               \r
-               // hide the select area\r
-               $PR( this.selArea ).hide();\r
-                                               \r
-               // setup the starting position of the select area\r
-               var startCoords = { x1: 0, y1: 0, x2: 0, y2: 0 };\r
-               var validCoordsSet = false;\r
-               \r
-               // display the select area \r
-               if( this.options.onloadCoords != null ) {\r
-                       // if we've being given some coordinates to \r
-                       startCoords = this.cloneCoords( this.options.onloadCoords );\r
-                       validCoordsSet = true;\r
-               } else if( this.options.ratioDim.x > 0 && this.options.ratioDim.y > 0 ) {\r
-                       // if there is a ratio limit applied and the then set it to initial ratio\r
-                       startCoords.x1 = Math.ceil( ( this.imgW - this.options.ratioDim.x ) / 2 );\r
-                       startCoords.y1 = Math.ceil( ( this.imgH - this.options.ratioDim.y ) / 2 );\r
-                       startCoords.x2 = startCoords.x1 + this.options.ratioDim.x;\r
-                       startCoords.y2 = startCoords.y1 + this.options.ratioDim.y;\r
-                       validCoordsSet = true;\r
-               }\r
-               \r
-               this.setAreaCoords( startCoords, false, false, 1 );\r
-               \r
-               if( this.options.displayOnInit && validCoordsSet ) {\r
-                       this.selArea.show();\r
-                       this.drawArea();\r
-                       this.endCrop();\r
-               }\r
-               \r
-               this.attached = true;\r
-       },\r
-       \r
-       /**\r
-        * Removes the cropper\r
-        * \r
-        * @access public\r
-        * @return void\r
-        */\r
-       remove: function() {\r
-               if( this.attached ) {\r
-                       this.attached = false;\r
-                       \r
-                       // remove the elements we inserted\r
-                       this.imgWrap.parentNode.insertBefore( this.img, this.imgWrap );\r
-                       this.imgWrap.parentNode.removeChild( this.imgWrap );\r
-                       \r
-                       // remove the event observers\r
-                       Event.stopObserving( this.dragArea, 'mousedown', this.startDragBind );\r
-                       Event.stopObserving( document, 'mousemove', this.onDragBind );          \r
-                       Event.stopObserving( document, 'mouseup', this.endCropBind );\r
-                       this.registerHandles( false );\r
-                       if( this.options.captureKeys ) Event.stopObserving( document, 'keypress', this.keysBind );\r
-               }\r
-       },\r
-       \r
-       /**\r
-        * Resets the cropper, can be used either after being removed or any time you wish\r
-        * \r
-        * @access public\r
-        * @return void\r
-        */\r
-       reset: function() {\r
-               if( !this.attached ) this.onLoad();\r
-               else this.setParams();\r
-               this.endCrop();\r
-       },\r
-       \r
-       /**\r
-        * Handles the key functionality, currently just using arrow keys to move, if the user\r
-        * presses shift then the area will move by 10 pixels\r
-        */\r
-       handleKeys: function( e ) {\r
-               var dir = { x: 0, y: 0 }; // direction to move it in & the amount in pixels\r
-               if( !this.dragging ) {\r
-                       \r
-                       // catch the arrow keys\r
-                       switch( e.keyCode ) {\r
-                               case( 37 ) : // left\r
-                                       dir.x = -1;\r
-                                       break;\r
-                               case( 38 ) : // up\r
-                                       dir.y = -1;\r
-                                       break;\r
-                               case( 39 ) : // right\r
-                                       dir.x = 1;\r
-                                       break\r
-                               case( 40 ) : // down\r
-                                       dir.y = 1;\r
-                                       break;\r
-                       }\r
-                       \r
-                       if( dir.x != 0 || dir.y != 0 ) {\r
-                               // if shift is pressed then move by 10 pixels\r
-                               if( e.shiftKey ) {\r
-                                       dir.x *= 10;\r
-                                       dir.y *= 10;\r
-                               }\r
-                               \r
-                               this.moveArea( [ this.areaCoords.x1 + dir.x, this.areaCoords.y1 + dir.y ] );\r
-                               Event.stop( e ); \r
-                       }\r
-               }\r
-       },\r
-       \r
-       /**\r
-        * Calculates the width from the areaCoords\r
-        * \r
-        * @access private\r
-        * @return int\r
-        */\r
-       calcW: function() {\r
-               return (this.areaCoords.x2 - this.areaCoords.x1)\r
-       },\r
-       \r
-       /**\r
-        * Calculates the height from the areaCoords\r
-        * \r
-        * @access private\r
-        * @return int\r
-        */\r
-       calcH: function() {\r
-               return (this.areaCoords.y2 - this.areaCoords.y1)\r
-       },\r
-       \r
-       /**\r
-        * Moves the select area to the supplied point (assumes the point is x1 & y1 of the select area)\r
-        * \r
-        * @access public\r
-        * @param array Point for x1 & y1 to move select area to\r
-        * @return void\r
-        */\r
-       moveArea: function( point ) {\r
-               // dump( 'moveArea        : ' + point[0] + ',' + point[1] + ',' + ( point[0] + ( this.areaCoords.x2 - this.areaCoords.x1 ) ) + ',' + ( point[1] + ( this.areaCoords.y2 - this.areaCoords.y1 ) ) + '\n' );\r
-               this.setAreaCoords( \r
-                       {\r
-                               x1: point[0], \r
-                               y1: point[1],\r
-                               x2: point[0] + this.calcW(),\r
-                               y2: point[1] + this.calcH()\r
-                       },\r
-                       true,\r
-                       false\r
-               );\r
-               this.drawArea();\r
-       },\r
-\r
-       /**\r
-        * Clones a co-ordinates object, stops problems with handling them by reference\r
-        * \r
-        * @access private\r
-        * @param obj Coordinate object x1, y1, x2, y2\r
-        * @return obj Coordinate object x1, y1, x2, y2\r
-        */\r
-       cloneCoords: function( coords ) {\r
-               return { x1: coords.x1, y1: coords.y1, x2: coords.x2, y2: coords.y2 };\r
-       },\r
-\r
-       /**\r
-        * Sets the select coords to those provided but ensures they don't go\r
-        * outside the bounding box\r
-        * \r
-        * @access private\r
-        * @param obj Coordinates x1, y1, x2, y2\r
-        * @param boolean Whether this is a move\r
-        * @param boolean Whether to apply squaring\r
-        * @param obj Direction of mouse along both axis x, y ( -1 = negative, 1 = positive ) only required when moving etc.\r
-        * @param string The current resize handle || null\r
-        * @return void\r
-        */\r
-       setAreaCoords: function( coords, moving, square, direction, resizeHandle ) {\r
-               // dump( 'setAreaCoords (in) : ' + coords.x1 + ',' + coords.y1 + ',' + coords.x2 + ',' + coords.y2 );\r
-               if( moving ) {\r
-                       // if moving\r
-                       var targW = coords.x2 - coords.x1;\r
-                       var targH = coords.y2 - coords.y1;\r
-                       \r
-                       // ensure we're within the bounds\r
-                       if( coords.x1 < 0 ) {\r
-                               coords.x1 = 0;\r
-                               coords.x2 = targW;\r
-                       }\r
-                       if( coords.y1 < 0 ) {\r
-                               coords.y1 = 0;\r
-                               coords.y2 = targH;\r
-                       }\r
-                       if( coords.x2 > this.imgW ) {\r
-                               coords.x2 = this.imgW;\r
-                               coords.x1 = this.imgW - targW;\r
-                       }\r
-                       if( coords.y2 > this.imgH ) {\r
-                               coords.y2 = this.imgH;\r
-                               coords.y1 = this.imgH - targH;\r
-                       }                       \r
-               } else {\r
-                       // ensure we're within the bounds\r
-                       if( coords.x1 < 0 ) coords.x1 = 0;\r
-                       if( coords.y1 < 0 ) coords.y1 = 0;\r
-                       if( coords.x2 > this.imgW ) coords.x2 = this.imgW;\r
-                       if( coords.y2 > this.imgH ) coords.y2 = this.imgH;\r
-                       \r
-                       // This is passed as null in onload\r
-                       if( direction != null ) {\r
-                                                               \r
-                               // apply the ratio or squaring where appropriate\r
-                               if( this.ratioX > 0 ) this.applyRatio( coords, { x: this.ratioX, y: this.ratioY }, direction, resizeHandle );\r
-                               else if( square ) this.applyRatio( coords, { x: 1, y: 1 }, direction, resizeHandle );\r
-                                                                               \r
-                               var mins = [ this.options.minWidth, this.options.minHeight ]; // minimum dimensions [x,y]                       \r
-                               var maxs = [ this.options.maxWidth, this.options.maxHeight ]; // maximum dimensions [x,y]\r
-               \r
-                               // apply dimensions where appropriate\r
-                               if( mins[0] > 0 || mins[1] > 0 || maxs[0] > 0 || maxs[1] > 0) {\r
-                               \r
-                                       var coordsTransX        = { a1: coords.x1, a2: coords.x2 };\r
-                                       var coordsTransY        = { a1: coords.y1, a2: coords.y2 };\r
-                                       var boundsX                     = { min: 0, max: this.imgW };\r
-                                       var boundsY                     = { min: 0, max: this.imgH };\r
-                                       \r
-                                       // handle squaring properly on single axis minimum dimensions\r
-                                       if( (mins[0] != 0 || mins[1] != 0) && square ) {\r
-                                               if( mins[0] > 0 ) mins[1] = mins[0];\r
-                                               else if( mins[1] > 0 ) mins[0] = mins[1];\r
-                                       }\r
-                                       \r
-                                       if( (maxs[0] != 0 || maxs[0] != 0) && square ) {\r
-                                               // if we have a max x value & it is less than the max y value then we set the y max to the max x (so we don't go over the minimum maximum of one of the axes - if that makes sense)\r
-                                               if( maxs[0] > 0 && maxs[0] <= maxs[1] ) maxs[1] = maxs[0];\r
-                                               else if( maxs[1] > 0 && maxs[1] <= maxs[0] ) maxs[0] = maxs[1];\r
-                                       }\r
-                                       \r
-                                       if( mins[0] > 0 ) this.applyDimRestriction( coordsTransX, mins[0], direction.x, boundsX, 'min' );\r
-                                       if( mins[1] > 1 ) this.applyDimRestriction( coordsTransY, mins[1], direction.y, boundsY, 'min' );\r
-                                       \r
-                                       if( maxs[0] > 0 ) this.applyDimRestriction( coordsTransX, maxs[0], direction.x, boundsX, 'max' );\r
-                                       if( maxs[1] > 1 ) this.applyDimRestriction( coordsTransY, maxs[1], direction.y, boundsY, 'max' );\r
-                                       \r
-                                       coords = { x1: coordsTransX.a1, y1: coordsTransY.a1, x2: coordsTransX.a2, y2: coordsTransY.a2 };\r
-                               }\r
-                               \r
-                       }\r
-               }\r
-               \r
-               // dump( 'setAreaCoords (out) : ' + coords.x1 + ',' + coords.y1 + ',' + coords.x2 + ',' + coords.y2 + '\n' );\r
-               this.areaCoords = coords;\r
-       },\r
-       \r
-       /**\r
-        * Applies the supplied dimension restriction to the supplied coordinates along a single axis\r
-        * \r
-        * @access private\r
-        * @param obj Single axis coordinates, a1, a2 (e.g. for the x axis a1 = x1 & a2 = x2)\r
-        * @param int The restriction value\r
-        * @param int The direction ( -1 = negative, 1 = positive )\r
-        * @param obj The bounds of the image ( for this axis )\r
-        * @param string The dimension restriction type ( 'min' | 'max' )\r
-        * @return void\r
-        */\r
-       applyDimRestriction: function( coords, val, direction, bounds, type ) {\r
-               var check;\r
-               if( type == 'min' ) check = ( ( coords.a2 - coords.a1 ) < val );\r
-               else check = ( ( coords.a2 - coords.a1 ) > val );\r
-               if( check ) {\r
-                       if( direction == 1 ) coords.a2 = coords.a1 + val;\r
-                       else coords.a1 = coords.a2 - val;\r
-                       \r
-                       // make sure we're still in the bounds (not too pretty for the user, but needed)\r
-                       if( coords.a1 < bounds.min ) {\r
-                               coords.a1 = bounds.min;\r
-                               coords.a2 = val;\r
-                       } else if( coords.a2 > bounds.max ) {\r
-                               coords.a1 = bounds.max - val;\r
-                               coords.a2 = bounds.max;\r
-                       }\r
-               }\r
-       },\r
-               \r
-       /**\r
-        * Applies the supplied ratio to the supplied coordinates\r
-        * \r
-        * @access private\r
-        * @param obj Coordinates, x1, y1, x2, y2\r
-        * @param obj Ratio, x, y\r
-        * @param obj Direction of mouse, x & y : -1 == negative 1 == positive\r
-        * @param string The current resize handle || null\r
-        * @return void\r
-        */\r
-       applyRatio : function( coords, ratio, direction, resizeHandle ) {\r
-               // dump( 'direction.y : ' + direction.y + '\n');\r
-               var newCoords;\r
-               if( resizeHandle == 'N' || resizeHandle == 'S' ) {\r
-                       // dump( 'north south \n');\r
-                       // if moving on either the lone north & south handles apply the ratio on the y axis\r
-                       newCoords = this.applyRatioToAxis( \r
-                               { a1: coords.y1, b1: coords.x1, a2: coords.y2, b2: coords.x2 },\r
-                               { a: ratio.y, b: ratio.x },\r
-                               { a: direction.y, b: direction.x },\r
-                               { min: 0, max: this.imgW }\r
-                       );\r
-                       coords.x1 = newCoords.b1;\r
-                       coords.y1 = newCoords.a1;\r
-                       coords.x2 = newCoords.b2;\r
-                       coords.y2 = newCoords.a2;\r
-               } else {\r
-                       // otherwise deal with it as if we're applying the ratio on the x axis\r
-                       newCoords = this.applyRatioToAxis( \r
-                               { a1: coords.x1, b1: coords.y1, a2: coords.x2, b2: coords.y2 },\r
-                               { a: ratio.x, b: ratio.y },\r
-                               { a: direction.x, b: direction.y },\r
-                               { min: 0, max: this.imgH }\r
-                       );\r
-                       coords.x1 = newCoords.a1;\r
-                       coords.y1 = newCoords.b1;\r
-                       coords.x2 = newCoords.a2;\r
-                       coords.y2 = newCoords.b2;\r
-               }\r
-               \r
-       },\r
-       \r
-       /**\r
-        * Applies the provided ratio to the provided coordinates based on provided direction & bounds,\r
-        * use to encapsulate functionality to make it easy to apply to either axis. This is probably\r
-        * quite hard to visualise so see the x axis example within applyRatio()\r
-        * \r
-        * Example in parameter details & comments is for requesting applying ratio to x axis.\r
-        * \r
-        * @access private\r
-        * @param obj Coords object (a1, b1, a2, b2) where a = x & b = y in example\r
-        * @param obj Ratio object (a, b) where a = x & b = y in example\r
-        * @param obj Direction object (a, b) where a = x & b = y in example\r
-        * @param obj Bounds (min, max)\r
-        * @return obj Coords object (a1, b1, a2, b2) where a = x & b = y in example\r
-        */\r
-       applyRatioToAxis: function( coords, ratio, direction, bounds ) {\r
-               var newCoords = Object.extend( coords, {} );\r
-               var calcDimA = newCoords.a2 - newCoords.a1;                     // calculate dimension a (e.g. width)\r
-               var targDimB = Math.floor( calcDimA * ratio.b / ratio.a );      // the target dimension b (e.g. height)\r
-               var targB;                                                                                      // to hold target b (e.g. y value)\r
-               var targDimA;                                           // to hold target dimension a (e.g. width)\r
-               var calcDimB = null;                                                            // to hold calculated dimension b (e.g. height)\r
-               \r
-               // dump( 'newCoords[0]: ' + newCoords.a1 + ',' + newCoords.b1 + ','+ newCoords.a2 + ',' + newCoords.b2 + '\n');\r
-                               \r
-               if( direction.b == 1 ) {                                                        // if travelling in a positive direction\r
-                       // make sure we're not going out of bounds\r
-                       targB = newCoords.b1 + targDimB;\r
-                       if( targB > bounds.max ) {\r
-                               targB = bounds.max;\r
-                               calcDimB = targB - newCoords.b1;                        // calcuate dimension b (e.g. height)\r
-                       }\r
-                       \r
-                       newCoords.b2 = targB;\r
-               } else {                                                                                        // if travelling in a negative direction\r
-                       // make sure we're not going out of bounds\r
-                       targB = newCoords.b2 - targDimB;\r
-                       if( targB < bounds.min ) {\r
-                               targB = bounds.min;\r
-                               calcDimB = targB + newCoords.b2;                        // calcuate dimension b (e.g. height)\r
-                       }\r
-                       newCoords.b1 = targB;\r
-               }\r
-               \r
-               // dump( 'newCoords[1]: ' + newCoords.a1 + ',' + newCoords.b1 + ','+ newCoords.a2 + ',' + newCoords.b2 + '\n');\r
-                       \r
-               // apply the calculated dimensions\r
-               if( calcDimB != null ) {\r
-                       targDimA = Math.floor( calcDimB * ratio.a / ratio.b );\r
-                       \r
-                       if( direction.a == 1 ) newCoords.a2 = newCoords.a1 + targDimA;\r
-                       else newCoords.a1 = newCoords.a1 = newCoords.a2 - targDimA;\r
-               }\r
-               \r
-               // dump( 'newCoords[2]: ' + newCoords.a1 + ',' + newCoords.b1 + ','+ newCoords.a2 + ',' + newCoords.b2 + '\n');\r
-                       \r
-               return newCoords;\r
-       },\r
-       \r
-       /**\r
-        * Draws the select area\r
-        * \r
-        * @access private\r
-        * @return void\r
-        */\r
-       drawArea: function( ) { \r
-               /*\r
-                * NOTE: I'm not using the Element.setStyle() shortcut as they make it \r
-                * quite sluggish on Mac based browsers\r
-                */\r
-               // dump( 'drawArea        : ' + this.areaCoords.x1 + ',' + this.areaCoords.y1 + ',' + this.areaCoords.x2 + ',' + this.areaCoords.y2 + '\n' );\r
-               var areaWidth     = this.calcW();\r
-               var areaHeight    = this.calcH();\r
-               \r
-               /*\r
-                * Calculate all the style strings before we use them, allows reuse & produces quicker\r
-                * rendering (especially noticable in Mac based browsers)\r
-                */\r
-               var px = 'px';\r
-               var params = [\r
-                       this.areaCoords.x1 + px,        // the left of the selArea\r
-                       this.areaCoords.y1 + px,                // the top of the selArea\r
-                       areaWidth + px,                                 // width of the selArea\r
-                       areaHeight + px,                                        // height of the selArea\r
-                       this.areaCoords.x2 + px,                // bottom of the selArea\r
-                       this.areaCoords.y2 + px,                // right of the selArea\r
-                       (this.img.width - this.areaCoords.x2) + px,     // right edge of selArea\r
-                       (this.img.height - this.areaCoords.y2) + px     // bottom edge of selArea\r
-               ];\r
-                               \r
-               // do the select area\r
-               var areaStyle                           = this.selArea.style;\r
-               areaStyle.left                          = params[0];\r
-               areaStyle.top                           = params[1];\r
-               areaStyle.width                         = params[2];\r
-               areaStyle.height                        = params[3];\r
-                               \r
-               // position the north, east, south & west handles\r
-               var horizHandlePos = Math.ceil( (areaWidth - 6) / 2 ) + px;\r
-               var vertHandlePos = Math.ceil( (areaHeight - 6) / 2 ) + px;\r
-               \r
-               this.handleN.style.left         = horizHandlePos;\r
-               this.handleE.style.top          = vertHandlePos;\r
-               this.handleS.style.left         = horizHandlePos;\r
-               this.handleW.style.top          = vertHandlePos;\r
-               \r
-               // draw the four overlays\r
-               this.north.style.height         = params[1];\r
-               \r
-               var eastStyle                           = this.east.style;\r
-               eastStyle.top                           = params[1];\r
-               eastStyle.height                        = params[3];\r
-               eastStyle.left                          = params[4];\r
-           eastStyle.width                             = params[6];\r
-          \r
-               var southStyle                          = this.south.style;\r
-               southStyle.top                          = params[5];\r
-               southStyle.height                       = params[7];\r
-          \r
-           var westStyle                       = this.west.style;\r
-           westStyle.top                               = params[1];\r
-           westStyle.height                    = params[3];\r
-               westStyle.width                         = params[0];\r
-               \r
-               // call the draw method on sub classes\r
-               this.subDrawArea();\r
-               \r
-               this.forceReRender();\r
-       },\r
-       \r
-       /**\r
-        * Force the re-rendering of the selArea element which fixes rendering issues in Safari \r
-        * & IE PC, especially evident when re-sizing perfectly vertical using any of the south handles\r
-        * \r
-        * @access private\r
-        * @return void\r
-        */\r
-       forceReRender: function() {\r
-               if( this.isIE || this.isWebKit) {\r
-                       var n = document.createTextNode(' ');\r
-                       var d,el,fixEL,i;\r
-               \r
-                       if( this.isIE ) fixEl = this.selArea;\r
-                       else if( this.isWebKit ) {\r
-                               fixEl = document.getElementsByClassName( 'imgCrop_marqueeSouth', this.imgWrap )[0];\r
-                               /* we have to be a bit more forceful for Safari, otherwise the the marquee &\r
-                                * the south handles still don't move\r
-                                */ \r
-                               d = Builder.node( 'div', '' );\r
-                               d.style.visibility = 'hidden';\r
-                               \r
-                               var classList = ['SE','S','SW'];\r
-                               for( i = 0; i < classList.length; i++ ) {\r
-                                       el = document.getElementsByClassName( 'imgCrop_handle' + classList[i], this.selArea )[0];\r
-                                       if( el.childNodes.length ) el.removeChild( el.childNodes[0] );\r
-                                       el.appendChild(d);\r
-                               }\r
-                       }\r
-                       fixEl.appendChild(n);\r
-                       fixEl.removeChild(n);\r
-               }\r
-       },\r
-       \r
-       /**\r
-        * Starts the resize\r
-        * \r
-        * @access private\r
-        * @param obj Event\r
-        * @return void\r
-        */\r
-       startResize: function( e ) {\r
-               this.startCoords = this.cloneCoords( this.areaCoords );\r
-               \r
-               this.resizing = true;\r
-               this.resizeHandle = Event.element( e ).classNames().toString().replace(/([^N|NE|E|SE|S|SW|W|NW])+/, '');\r
-               // dump( 'this.resizeHandle : ' + this.resizeHandle + '\n' );\r
-               Event.stop( e );\r
-       },\r
-       \r
-       /**\r
-        * Starts the drag\r
-        * \r
-        * @access private\r
-        * @param obj Event\r
-        * @return void\r
-        */\r
-       startDrag: function( e ) {      \r
-               this.selArea.show();\r
-               this.clickCoords = this.getCurPos( e );\r
-       \r
-       this.setAreaCoords( { x1: this.clickCoords.x, y1: this.clickCoords.y, x2: this.clickCoords.x, y2: this.clickCoords.y }, false, false, null );\r
-       \r
-       this.dragging = true;\r
-       this.onDrag( e ); // incase the user just clicks once after already making a selection\r
-       Event.stop( e );\r
-       },\r
-       \r
-       /**\r
-        * Gets the current cursor position relative to the image\r
-        * \r
-        * @access private\r
-        * @param obj Event\r
-        * @return obj x,y pixels of the cursor\r
-        */\r
-       getCurPos: function( e ) {\r
-               // get the offsets for the wrapper within the document\r
-               var el = this.imgWrap, wrapOffsets = Position.cumulativeOffset( el );\r
-               // remove any scrolling that is applied to the wrapper (this may be buggy) - don't count the scroll on the body as that won't affect us\r
-               while( el.nodeName != 'BODY' ) {\r
-                       wrapOffsets[1] -= el.scrollTop  || 0;\r
-                       wrapOffsets[0] -= el.scrollLeft || 0;\r
-                       el = el.parentNode;\r
-           }           \r
-               return curPos = { \r
-                       x: Event.pointerX(e) - wrapOffsets[0],\r
-                       y: Event.pointerY(e) - wrapOffsets[1]\r
-               }\r
-       },\r
-       \r
-       /**\r
-        * Performs the drag for both resize & inital draw dragging\r
-        * \r
-        * @access private\r
-        * @param obj Event\r
-        * @return void\r
-        */\r
-       onDrag: function( e ) {\r
-               if( this.dragging || this.resizing ) {  \r
-               \r
-                       var resizeHandle = null;\r
-                       var curPos = this.getCurPos( e );                       \r
-                       var newCoords = this.cloneCoords( this.areaCoords );\r
-                       var direction = { x: 1, y: 1 };\r
-                                               \r
-                   if( this.dragging ) {\r
-                       if( curPos.x < this.clickCoords.x ) direction.x = -1;\r
-                       if( curPos.y < this.clickCoords.y ) direction.y = -1;\r
-                       \r
-                               this.transformCoords( curPos.x, this.clickCoords.x, newCoords, 'x' );\r
-                               this.transformCoords( curPos.y, this.clickCoords.y, newCoords, 'y' );\r
-                       } else if( this.resizing ) {\r
-                               resizeHandle = this.resizeHandle;                       \r
-                               // do x movements first\r
-                               if( resizeHandle.match(/E/) ) {\r
-                                       // if we're moving an east handle\r
-                                       this.transformCoords( curPos.x, this.startCoords.x1, newCoords, 'x' );  \r
-                                       if( curPos.x < this.startCoords.x1 ) direction.x = -1;\r
-                               } else if( resizeHandle.match(/W/) ) {\r
-                                       // if we're moving an west handle\r
-                                       this.transformCoords( curPos.x, this.startCoords.x2, newCoords, 'x' );\r
-                                       if( curPos.x < this.startCoords.x2 ) direction.x = -1;\r
-                               }\r
-                                                                       \r
-                               // do y movements second\r
-                               if( resizeHandle.match(/N/) ) {\r
-                                       // if we're moving an north handle      \r
-                                       this.transformCoords( curPos.y, this.startCoords.y2, newCoords, 'y' );\r
-                                       if( curPos.y < this.startCoords.y2 ) direction.y = -1;\r
-                               } else if( resizeHandle.match(/S/) ) {\r
-                                       // if we're moving an south handle\r
-                                       this.transformCoords( curPos.y, this.startCoords.y1, newCoords, 'y' );  \r
-                                       if( curPos.y < this.startCoords.y1 ) direction.y = -1;\r
-                               }       \r
-                                                       \r
-                       }\r
-               \r
-                       this.setAreaCoords( newCoords, false, e.shiftKey, direction, resizeHandle );\r
-                       this.drawArea();\r
-                       Event.stop( e ); // stop the default event (selecting images & text) in Safari & IE PC\r
-               }\r
-       },\r
-       \r
-       /**\r
-        * Applies the appropriate transform to supplied co-ordinates, on the\r
-        * defined axis, depending on the relationship of the supplied values\r
-        * \r
-        * @access private\r
-        * @param int Current value of pointer\r
-        * @param int Base value to compare current pointer val to\r
-        * @param obj Coordinates to apply transformation on x1, x2, y1, y2\r
-        * @param string Axis to apply transformation on 'x' || 'y'\r
-        * @return void\r
-        */\r
-       transformCoords : function( curVal, baseVal, coords, axis ) {\r
-               var newVals = [ curVal, baseVal ];\r
-               if( curVal > baseVal ) newVals.reverse();\r
-               coords[ axis + '1' ] = newVals[0];\r
-               coords[ axis + '2' ] = newVals[1];              \r
-       },\r
-       \r
-       /**\r
-        * Ends the crop & passes the values of the select area on to the appropriate \r
-        * callback function on completion of a crop\r
-        * \r
-        * @access private\r
-        * @return void\r
-        */\r
-       endCrop : function() {\r
-               this.dragging = false;\r
-               this.resizing = false;\r
-               \r
-               this.options.onEndCrop(\r
-                       this.areaCoords,\r
-                       {\r
-                               width: this.calcW(), \r
-                               height: this.calcH() \r
-                       }\r
-               );\r
-       },\r
-       \r
-       /**\r
-        * Abstract method called on the end of initialization\r
-        * \r
-        * @access private\r
-        * @abstract\r
-        * @return void\r
-        */\r
-       subInitialize: function() {},\r
-       \r
-       /**\r
-        * Abstract method called on the end of drawArea()\r
-        * \r
-        * @access private\r
-        * @abstract\r
-        * @return void\r
-        */\r
-       subDrawArea: function() {}\r
-};\r
-\r
-\r
-\r
-\r
-/**\r
- * Extend the Cropper.Img class to allow for presentation of a preview image of the resulting crop,\r
- * the option for displayOnInit is always overridden to true when displaying a preview image\r
- * \r
- * Usage:\r
- *     @param obj Image element to attach to\r
- *     @param obj Optional options:\r
- *             - see Cropper.Img for base options\r
- *             \r
- *             - previewWrap obj\r
- *                     HTML element that will be used as a container for the preview image             \r
- */\r
-Cropper.ImgWithPreview = Class.create();\r
-\r
-Object.extend( Object.extend( Cropper.ImgWithPreview.prototype, Cropper.Img.prototype ), {\r
-       \r
-       /**\r
-        * Implements the abstract method from Cropper.Img to initialize preview image settings.\r
-        * Will only attach a preview image is the previewWrap element is defined and the minWidth\r
-        * & minHeight options are set.\r
-        * \r
-        * @see Croper.Img.subInitialize\r
-        */\r
-       subInitialize: function() {\r
-               /**\r
-                * Whether or not we've attached a preview image\r
-                * @var boolean\r
-                */\r
-               this.hasPreviewImg = false;\r
-               if( typeof(this.options.previewWrap) != 'undefined' \r
-                       && this.options.minWidth > 0 \r
-                       && this.options.minHeight > 0\r
-               ) {\r
-                       /**\r
-                        * The preview image wrapper element\r
-                        * @var obj HTML element\r
-                        */\r
-                       this.previewWrap        = $PR( this.options.previewWrap );\r
-                       /**\r
-                        * The preview image element\r
-                        * @var obj HTML IMG element\r
-                        */\r
-                       this.previewImg         = this.img.cloneNode( false );\r
-                       // set the ID of the preview image to be unique\r
-                       this.previewImg.id      = 'imgCrop_' + this.previewImg.id;\r
-                       \r
-                                               \r
-                       // set the displayOnInit option to true so we display the select area at the same time as the thumbnail\r
-                       this.options.displayOnInit = true;\r
-\r
-                       this.hasPreviewImg      = true;\r
-                       \r
-                       this.previewWrap.addClassName( 'imgCrop_previewWrap' );\r
-                       \r
-                       this.previewWrap.setStyle(\r
-                        { \r
-                               width: this.options.minWidth + 'px',\r
-                               height: this.options.minHeight + 'px'\r
-                        }\r
-                       );\r
-                       \r
-                       this.previewWrap.appendChild( this.previewImg );\r
-               }\r
-       },\r
-       \r
-       /**\r
-        * Implements the abstract method from Cropper.Img to draw the preview image\r
-        * \r
-        * @see Croper.Img.subDrawArea\r
-        */\r
-       subDrawArea: function() {\r
-               if( this.hasPreviewImg ) {\r
-                       // get the ratio of the select area to the src image\r
-                       var calcWidth = this.calcW();\r
-                       var calcHeight = this.calcH();\r
-                       // ratios for the dimensions of the preview image\r
-                       var dimRatio = { \r
-                               x: this.imgW / calcWidth, \r
-                               y: this.imgH / calcHeight \r
-                       }; \r
-                       //ratios for the positions within the preview\r
-                       var posRatio = { \r
-                               x: calcWidth / this.options.minWidth, \r
-                               y: calcHeight / this.options.minHeight \r
-                       };\r
-                       \r
-                       // setting the positions in an obj before apply styles for rendering speed increase\r
-                       var calcPos     = {\r
-                               w: Math.ceil( this.options.minWidth * dimRatio.x ) + 'px',\r
-                               h: Math.ceil( this.options.minHeight * dimRatio.y ) + 'px',\r
-                               x: '-' + Math.ceil( this.areaCoords.x1 / posRatio.x )  + 'px',\r
-                               y: '-' + Math.ceil( this.areaCoords.y1 / posRatio.y ) + 'px'\r
-                       }\r
-                       \r
-                       var previewStyle        = this.previewImg.style;\r
-                       previewStyle.width      = calcPos.w;\r
-                       previewStyle.height     = calcPos.h;\r
-                       previewStyle.left       = calcPos.x;\r
-                       previewStyle.top        = calcPos.y;\r
-               }\r
-       }\r
-       \r
-});\r
diff --git a/library/cropper/lib/builder.js b/library/cropper/lib/builder.js
deleted file mode 100644 (file)
index 5b15ba9..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
-//
-// See scriptaculous.js for full license.
-
-var Builder = {
-  NODEMAP: {
-    AREA: 'map',
-    CAPTION: 'table',
-    COL: 'table',
-    COLGROUP: 'table',
-    LEGEND: 'fieldset',
-    OPTGROUP: 'select',
-    OPTION: 'select',
-    PARAM: 'object',
-    TBODY: 'table',
-    TD: 'table',
-    TFOOT: 'table',
-    TH: 'table',
-    THEAD: 'table',
-    TR: 'table'
-  },
-  // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken,
-  //       due to a Firefox bug
-  node: function(elementName) {
-    elementName = elementName.toUpperCase();
-    
-    // try innerHTML approach
-    var parentTag = this.NODEMAP[elementName] || 'div';
-    var parentElement = document.createElement(parentTag);
-    try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
-      parentElement.innerHTML = "<" + elementName + "></" + elementName + ">";
-    } catch(e) {}
-    var element = parentElement.firstChild || null;
-      
-    // see if browser added wrapping tags
-    if(element && (element.tagName != elementName))
-      element = element.getElementsByTagName(elementName)[0];
-    
-    // fallback to createElement approach
-    if(!element) element = document.createElement(elementName);
-    
-    // abort if nothing could be created
-    if(!element) return;
-
-    // attributes (or text)
-    if(arguments[1])
-      if(this._isStringOrNumber(arguments[1]) ||
-        (arguments[1] instanceof Array)) {
-          this._children(element, arguments[1]);
-        } else {
-          var attrs = this._attributes(arguments[1]);
-          if(attrs.length) {
-            try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
-              parentElement.innerHTML = "<" +elementName + " " +
-                attrs + "></" + elementName + ">";
-            } catch(e) {}
-            element = parentElement.firstChild || null;
-            // workaround firefox 1.0.X bug
-            if(!element) {
-              element = document.createElement(elementName);
-              for(attr in arguments[1]) 
-                element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
-            }
-            if(element.tagName != elementName)
-              element = parentElement.getElementsByTagName(elementName)[0];
-            }
-        } 
-
-    // text, or array of children
-    if(arguments[2])
-      this._children(element, arguments[2]);
-
-     return element;
-  },
-  _text: function(text) {
-     return document.createTextNode(text);
-  },
-  _attributes: function(attributes) {
-    var attrs = [];
-    for(attribute in attributes)
-      attrs.push((attribute=='className' ? 'class' : attribute) +
-          '="' + attributes[attribute].toString().escapeHTML() + '"');
-    return attrs.join(" ");
-  },
-  _children: function(element, children) {
-    if(typeof children=='object') { // array can hold nodes and text
-      children.flatten().each( function(e) {
-        if(typeof e=='object')
-          element.appendChild(e)
-        else
-          if(Builder._isStringOrNumber(e))
-            element.appendChild(Builder._text(e));
-      });
-    } else
-      if(Builder._isStringOrNumber(children)) 
-         element.appendChild(Builder._text(children));
-  },
-  _isStringOrNumber: function(param) {
-    return(typeof param=='string' || typeof param=='number');
-  }
-}
\ No newline at end of file
diff --git a/library/cropper/lib/controls.js b/library/cropper/lib/controls.js
deleted file mode 100644 (file)
index 9606948..0000000
+++ /dev/null
@@ -1,815 +0,0 @@
-// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
-//           (c) 2005 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
-//           (c) 2005 Jon Tirsen (http://www.tirsen.com)
-// Contributors:
-//  Richard Livsey
-//  Rahul Bhargava
-//  Rob Wills
-// 
-// See scriptaculous.js for full license.
-
-// Autocompleter.Base handles all the autocompletion functionality 
-// that's independent of the data source for autocompletion. This
-// includes drawing the autocompletion menu, observing keyboard
-// and mouse events, and similar.
-//
-// Specific autocompleters need to provide, at the very least, 
-// a getUpdatedChoices function that will be invoked every time
-// the text inside the monitored textbox changes. This method 
-// should get the text for which to provide autocompletion by
-// invoking this.getToken(), NOT by directly accessing
-// this.element.value. This is to allow incremental tokenized
-// autocompletion. Specific auto-completion logic (AJAX, etc)
-// belongs in getUpdatedChoices.
-//
-// Tokenized incremental autocompletion is enabled automatically
-// when an autocompleter is instantiated with the 'tokens' option
-// in the options parameter, e.g.:
-// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
-// will incrementally autocomplete with a comma as the token.
-// Additionally, ',' in the above example can be replaced with
-// a token array, e.g. { tokens: [',', '\n'] } which
-// enables autocompletion on multiple tokens. This is most 
-// useful when one of the tokens is \n (a newline), as it 
-// allows smart autocompletion after linebreaks.
-
-var Autocompleter = {}
-Autocompleter.Base = function() {};
-Autocompleter.Base.prototype = {
-  baseInitialize: function(element, update, options) {
-    this.element     = $PR(element); 
-    this.update      = $PR(update);  
-    this.hasFocus    = false; 
-    this.changed     = false; 
-    this.active      = false; 
-    this.index       = 0;     
-    this.entryCount  = 0;
-
-    if (this.setOptions)
-      this.setOptions(options);
-    else
-      this.options = options || {};
-
-    this.options.paramName    = this.options.paramName || this.element.name;
-    this.options.tokens       = this.options.tokens || [];
-    this.options.frequency    = this.options.frequency || 0.4;
-    this.options.minChars     = this.options.minChars || 1;
-    this.options.onShow       = this.options.onShow || 
-    function(element, update){ 
-      if(!update.style.position || update.style.position=='absolute') {
-        update.style.position = 'absolute';
-        Position.clone(element, update, {setHeight: false, offsetTop: element.offsetHeight});
-      }
-      Effect.Appear(update,{duration:0.15});
-    };
-    this.options.onHide = this.options.onHide || 
-    function(element, update){ new Effect.Fade(update,{duration:0.15}) };
-
-    if (typeof(this.options.tokens) == 'string') 
-      this.options.tokens = new Array(this.options.tokens);
-
-    this.observer = null;
-    
-    this.element.setAttribute('autocomplete','off');
-
-    Element.hide(this.update);
-
-    Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this));
-    Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this));
-  },
-
-  show: function() {
-    if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
-    if(!this.iefix && 
-      (navigator.appVersion.indexOf('MSIE')>0) &&
-      (navigator.userAgent.indexOf('Opera')<0) &&
-      (Element.getStyle(this.update, 'position')=='absolute')) {
-      new Insertion.After(this.update, 
-       '<iframe id="' + this.update.id + '_iefix" '+
-       'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
-       'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
-      this.iefix = $PR(this.update.id+'_iefix');
-    }
-    if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
-  },
-  
-  fixIEOverlapping: function() {
-    Position.clone(this.update, this.iefix);
-    this.iefix.style.zIndex = 1;
-    this.update.style.zIndex = 2;
-    Element.show(this.iefix);
-  },
-
-  hide: function() {
-    this.stopIndicator();
-    if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
-    if(this.iefix) Element.hide(this.iefix);
-  },
-
-  startIndicator: function() {
-    if(this.options.indicator) Element.show(this.options.indicator);
-  },
-
-  stopIndicator: function() {
-    if(this.options.indicator) Element.hide(this.options.indicator);
-  },
-
-  onKeyPress: function(event) {
-    if(this.active)
-      switch(event.keyCode) {
-       case Event.KEY_TAB:
-       case Event.KEY_RETURN:
-         this.selectEntry();
-         Event.stop(event);
-       case Event.KEY_ESC:
-         this.hide();
-         this.active = false;
-         Event.stop(event);
-         return;
-       case Event.KEY_LEFT:
-       case Event.KEY_RIGHT:
-         return;
-       case Event.KEY_UP:
-         this.markPrevious();
-         this.render();
-         if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
-         return;
-       case Event.KEY_DOWN:
-         this.markNext();
-         this.render();
-         if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
-         return;
-      }
-     else 
-       if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN || 
-         (navigator.appVersion.indexOf('AppleWebKit') > 0 && event.keyCode == 0)) return;
-
-    this.changed = true;
-    this.hasFocus = true;
-
-    if(this.observer) clearTimeout(this.observer);
-      this.observer = 
-        setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
-  },
-
-  activate: function() {
-    this.changed = false;
-    this.hasFocus = true;
-    this.getUpdatedChoices();
-  },
-
-  onHover: function(event) {
-    var element = Event.findElement(event, 'LI');
-    if(this.index != element.autocompleteIndex) 
-    {
-        this.index = element.autocompleteIndex;
-        this.render();
-    }
-    Event.stop(event);
-  },
-  
-  onClick: function(event) {
-    var element = Event.findElement(event, 'LI');
-    this.index = element.autocompleteIndex;
-    this.selectEntry();
-    this.hide();
-  },
-  
-  onBlur: function(event) {
-    // needed to make click events working
-    setTimeout(this.hide.bind(this), 250);
-    this.hasFocus = false;
-    this.active = false;     
-  }, 
-  
-  render: function() {
-    if(this.entryCount > 0) {
-      for (var i = 0; i < this.entryCount; i++)
-        this.index==i ? 
-          Element.addClassName(this.getEntry(i),"selected") : 
-          Element.removeClassName(this.getEntry(i),"selected");
-        
-      if(this.hasFocus) { 
-        this.show();
-        this.active = true;
-      }
-    } else {
-      this.active = false;
-      this.hide();
-    }
-  },
-  
-  markPrevious: function() {
-    if(this.index > 0) this.index--
-      else this.index = this.entryCount-1;
-  },
-  
-  markNext: function() {
-    if(this.index < this.entryCount-1) this.index++
-      else this.index = 0;
-  },
-  
-  getEntry: function(index) {
-    return this.update.firstChild.childNodes[index];
-  },
-  
-  getCurrentEntry: function() {
-    return this.getEntry(this.index);
-  },
-  
-  selectEntry: function() {
-    this.active = false;
-    this.updateElement(this.getCurrentEntry());
-  },
-
-  updateElement: function(selectedElement) {
-    if (this.options.updateElement) {
-      this.options.updateElement(selectedElement);
-      return;
-    }
-    var value = '';
-    if (this.options.select) {
-      var nodes = document.getElementsByClassName(this.options.select, selectedElement) || [];
-      if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
-    } else
-      value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
-    
-    var lastTokenPos = this.findLastToken();
-    if (lastTokenPos != -1) {
-      var newValue = this.element.value.substr(0, lastTokenPos + 1);
-      var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/);
-      if (whitespace)
-        newValue += whitespace[0];
-      this.element.value = newValue + value;
-    } else {
-      this.element.value = value;
-    }
-    this.element.focus();
-    
-    if (this.options.afterUpdateElement)
-      this.options.afterUpdateElement(this.element, selectedElement);
-  },
-
-  updateChoices: function(choices) {
-    if(!this.changed && this.hasFocus) {
-      this.update.innerHTML = choices;
-      Element.cleanWhitespace(this.update);
-      Element.cleanWhitespace(this.update.firstChild);
-
-      if(this.update.firstChild && this.update.firstChild.childNodes) {
-        this.entryCount = 
-          this.update.firstChild.childNodes.length;
-        for (var i = 0; i < this.entryCount; i++) {
-          var entry = this.getEntry(i);
-          entry.autocompleteIndex = i;
-          this.addObservers(entry);
-        }
-      } else { 
-        this.entryCount = 0;
-      }
-
-      this.stopIndicator();
-
-      this.index = 0;
-      this.render();
-    }
-  },
-
-  addObservers: function(element) {
-    Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
-    Event.observe(element, "click", this.onClick.bindAsEventListener(this));
-  },
-
-  onObserverEvent: function() {
-    this.changed = false;   
-    if(this.getToken().length>=this.options.minChars) {
-      this.startIndicator();
-      this.getUpdatedChoices();
-    } else {
-      this.active = false;
-      this.hide();
-    }
-  },
-
-  getToken: function() {
-    var tokenPos = this.findLastToken();
-    if (tokenPos != -1)
-      var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,'');
-    else
-      var ret = this.element.value;
-
-    return /\n/.test(ret) ? '' : ret;
-  },
-
-  findLastToken: function() {
-    var lastTokenPos = -1;
-
-    for (var i=0; i<this.options.tokens.length; i++) {
-      var thisTokenPos = this.element.value.lastIndexOf(this.options.tokens[i]);
-      if (thisTokenPos > lastTokenPos)
-        lastTokenPos = thisTokenPos;
-    }
-    return lastTokenPos;
-  }
-}
-
-Ajax.Autocompleter = Class.create();
-Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), {
-  initialize: function(element, update, url, options) {
-    this.baseInitialize(element, update, options);
-    this.options.asynchronous  = true;
-    this.options.onComplete    = this.onComplete.bind(this);
-    this.options.defaultParams = this.options.parameters || null;
-    this.url                   = url;
-  },
-
-  getUpdatedChoices: function() {
-    entry = encodeURIComponent(this.options.paramName) + '=' + 
-      encodeURIComponent(this.getToken());
-
-    this.options.parameters = this.options.callback ?
-      this.options.callback(this.element, entry) : entry;
-
-    if(this.options.defaultParams) 
-      this.options.parameters += '&' + this.options.defaultParams;
-
-    new Ajax.Request(this.url, this.options);
-  },
-
-  onComplete: function(request) {
-    this.updateChoices(request.responseText);
-  }
-
-});
-
-// The local array autocompleter. Used when you'd prefer to
-// inject an array of autocompletion options into the page, rather
-// than sending out Ajax queries, which can be quite slow sometimes.
-//
-// The constructor takes four parameters. The first two are, as usual,
-// the id of the monitored textbox, and id of the autocompletion menu.
-// The third is the array you want to autocomplete from, and the fourth
-// is the options block.
-//
-// Extra local autocompletion options:
-// - choices - How many autocompletion choices to offer
-//
-// - partialSearch - If false, the autocompleter will match entered
-//                    text only at the beginning of strings in the 
-//                    autocomplete array. Defaults to true, which will
-//                    match text at the beginning of any *word* in the
-//                    strings in the autocomplete array. If you want to
-//                    search anywhere in the string, additionally set
-//                    the option fullSearch to true (default: off).
-//
-// - fullSsearch - Search anywhere in autocomplete array strings.
-//
-// - partialChars - How many characters to enter before triggering
-//                   a partial match (unlike minChars, which defines
-//                   how many characters are required to do any match
-//                   at all). Defaults to 2.
-//
-// - ignoreCase - Whether to ignore case when autocompleting.
-//                 Defaults to true.
-//
-// It's possible to pass in a custom function as the 'selector' 
-// option, if you prefer to write your own autocompletion logic.
-// In that case, the other options above will not apply unless
-// you support them.
-
-Autocompleter.Local = Class.create();
-Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
-  initialize: function(element, update, array, options) {
-    this.baseInitialize(element, update, options);
-    this.options.array = array;
-  },
-
-  getUpdatedChoices: function() {
-    this.updateChoices(this.options.selector(this));
-  },
-
-  setOptions: function(options) {
-    this.options = Object.extend({
-      choices: 10,
-      partialSearch: true,
-      partialChars: 2,
-      ignoreCase: true,
-      fullSearch: false,
-      selector: function(instance) {
-        var ret       = []; // Beginning matches
-        var partial   = []; // Inside matches
-        var entry     = instance.getToken();
-        var count     = 0;
-
-        for (var i = 0; i < instance.options.array.length &&  
-          ret.length < instance.options.choices ; i++) { 
-
-          var elem = instance.options.array[i];
-          var foundPos = instance.options.ignoreCase ? 
-            elem.toLowerCase().indexOf(entry.toLowerCase()) : 
-            elem.indexOf(entry);
-
-          while (foundPos != -1) {
-            if (foundPos == 0 && elem.length != entry.length) { 
-              ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" + 
-                elem.substr(entry.length) + "</li>");
-              break;
-            } else if (entry.length >= instance.options.partialChars && 
-              instance.options.partialSearch && foundPos != -1) {
-              if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
-                partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
-                  elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
-                  foundPos + entry.length) + "</li>");
-                break;
-              }
-            }
-
-            foundPos = instance.options.ignoreCase ? 
-              elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : 
-              elem.indexOf(entry, foundPos + 1);
-
-          }
-        }
-        if (partial.length)
-          ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
-        return "<ul>" + ret.join('') + "</ul>";
-      }
-    }, options || {});
-  }
-});
-
-// AJAX in-place editor
-//
-// see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor
-
-// Use this if you notice weird scrolling problems on some browsers,
-// the DOM might be a bit confused when this gets called so do this
-// waits 1 ms (with setTimeout) until it does the activation
-Field.scrollFreeActivate = function(field) {
-  setTimeout(function() {
-    Field.activate(field);
-  }, 1);
-}
-
-Ajax.InPlaceEditor = Class.create();
-Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99";
-Ajax.InPlaceEditor.prototype = {
-  initialize: function(element, url, options) {
-    this.url = url;
-    this.element = $PR(element);
-
-    this.options = Object.extend({
-      okButton: true,
-      okText: "ok",
-      cancelLink: true,
-      cancelText: "cancel",
-      savingText: "Saving...",
-      clickToEditText: "Click to edit",
-      okText: "ok",
-      rows: 1,
-      onComplete: function(transport, element) {
-        new Effect.Highlight(element, {startcolor: this.options.highlightcolor});
-      },
-      onFailure: function(transport) {
-        alert("Error communicating with the server: " + transport.responseText.stripTags());
-      },
-      callback: function(form) {
-        return Form.serialize(form);
-      },
-      handleLineBreaks: true,
-      loadingText: 'Loading...',
-      savingClassName: 'inplaceeditor-saving',
-      loadingClassName: 'inplaceeditor-loading',
-      formClassName: 'inplaceeditor-form',
-      highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor,
-      highlightendcolor: "#FFFFFF",
-      externalControl: null,
-      submitOnBlur: false,
-      ajaxOptions: {},
-      evalScripts: false
-    }, options || {});
-
-    if(!this.options.formId && this.element.id) {
-      this.options.formId = this.element.id + "-inplaceeditor";
-      if ($PR(this.options.formId)) {
-        // there's already a form with that name, don't specify an id
-        this.options.formId = null;
-      }
-    }
-    
-    if (this.options.externalControl) {
-      this.options.externalControl = $PR(this.options.externalControl);
-    }
-    
-    this.originalBackground = Element.getStyle(this.element, 'background-color');
-    if (!this.originalBackground) {
-      this.originalBackground = "transparent";
-    }
-    
-    this.element.title = this.options.clickToEditText;
-    
-    this.onclickListener = this.enterEditMode.bindAsEventListener(this);
-    this.mouseoverListener = this.enterHover.bindAsEventListener(this);
-    this.mouseoutListener = this.leaveHover.bindAsEventListener(this);
-    Event.observe(this.element, 'click', this.onclickListener);
-    Event.observe(this.element, 'mouseover', this.mouseoverListener);
-    Event.observe(this.element, 'mouseout', this.mouseoutListener);
-    if (this.options.externalControl) {
-      Event.observe(this.options.externalControl, 'click', this.onclickListener);
-      Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener);
-      Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener);
-    }
-  },
-  enterEditMode: function(evt) {
-    if (this.saving) return;
-    if (this.editing) return;
-    this.editing = true;
-    this.onEnterEditMode();
-    if (this.options.externalControl) {
-      Element.hide(this.options.externalControl);
-    }
-    Element.hide(this.element);
-    this.createForm();
-    this.element.parentNode.insertBefore(this.form, this.element);
-    Field.scrollFreeActivate(this.editField);
-    // stop the event to avoid a page refresh in Safari
-    if (evt) {
-      Event.stop(evt);
-    }
-    return false;
-  },
-  createForm: function() {
-    this.form = document.createElement("form");
-    this.form.id = this.options.formId;
-    Element.addClassName(this.form, this.options.formClassName)
-    this.form.onsubmit = this.onSubmit.bind(this);
-
-    this.createEditField();
-
-    if (this.options.textarea) {
-      var br = document.createElement("br");
-      this.form.appendChild(br);
-    }
-
-    if (this.options.okButton) {
-      okButton = document.createElement("input");
-      okButton.type = "submit";
-      okButton.value = this.options.okText;
-      okButton.className = 'editor_ok_button';
-      this.form.appendChild(okButton);
-    }
-
-    if (this.options.cancelLink) {
-      cancelLink = document.createElement("a");
-      cancelLink.href = "#";
-      cancelLink.appendChild(document.createTextNode(this.options.cancelText));
-      cancelLink.onclick = this.onclickCancel.bind(this);
-      cancelLink.className = 'editor_cancel';      
-      this.form.appendChild(cancelLink);
-    }
-  },
-  hasHTMLLineBreaks: function(string) {
-    if (!this.options.handleLineBreaks) return false;
-    return string.match(/<br/i) || string.match(/<p>/i);
-  },
-  convertHTMLLineBreaks: function(string) {
-    return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, "");
-  },
-  createEditField: function() {
-    var text;
-    if(this.options.loadTextURL) {
-      text = this.options.loadingText;
-    } else {
-      text = this.getText();
-    }
-
-    var obj = this;
-    
-    if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) {
-      this.options.textarea = false;
-      var textField = document.createElement("input");
-      textField.obj = this;
-      textField.type = "text";
-      textField.name = "value";
-      textField.value = text;
-      textField.style.backgroundColor = this.options.highlightcolor;
-      textField.className = 'editor_field';
-      var size = this.options.size || this.options.cols || 0;
-      if (size != 0) textField.size = size;
-      if (this.options.submitOnBlur)
-        textField.onblur = this.onSubmit.bind(this);
-      this.editField = textField;
-    } else {
-      this.options.textarea = true;
-      var textArea = document.createElement("textarea");
-      textArea.obj = this;
-      textArea.name = "value";
-      textArea.value = this.convertHTMLLineBreaks(text);
-      textArea.rows = this.options.rows;
-      textArea.cols = this.options.cols || 40;
-      textArea.className = 'editor_field';      
-      if (this.options.submitOnBlur)
-        textArea.onblur = this.onSubmit.bind(this);
-      this.editField = textArea;
-    }
-    
-    if(this.options.loadTextURL) {
-      this.loadExternalText();
-    }
-    this.form.appendChild(this.editField);
-  },
-  getText: function() {
-    return this.element.innerHTML;
-  },
-  loadExternalText: function() {
-    Element.addClassName(this.form, this.options.loadingClassName);
-    this.editField.disabled = true;
-    new Ajax.Request(
-      this.options.loadTextURL,
-      Object.extend({
-        asynchronous: true,
-        onComplete: this.onLoadedExternalText.bind(this)
-      }, this.options.ajaxOptions)
-    );
-  },
-  onLoadedExternalText: function(transport) {
-    Element.removeClassName(this.form, this.options.loadingClassName);
-    this.editField.disabled = false;
-    this.editField.value = transport.responseText.stripTags();
-  },
-  onclickCancel: function() {
-    this.onComplete();
-    this.leaveEditMode();
-    return false;
-  },
-  onFailure: function(transport) {
-    this.options.onFailure(transport);
-    if (this.oldInnerHTML) {
-      this.element.innerHTML = this.oldInnerHTML;
-      this.oldInnerHTML = null;
-    }
-    return false;
-  },
-  onSubmit: function() {
-    // onLoading resets these so we need to save them away for the Ajax call
-    var form = this.form;
-    var value = this.editField.value;
-    
-    // do this first, sometimes the ajax call returns before we get a chance to switch on Saving...
-    // which means this will actually switch on Saving... *after* we've left edit mode causing Saving...
-    // to be displayed indefinitely
-    this.onLoading();
-    
-    if (this.options.evalScripts) {
-      new Ajax.Request(
-        this.url, Object.extend({
-          parameters: this.options.callback(form, value),
-          onComplete: this.onComplete.bind(this),
-          onFailure: this.onFailure.bind(this),
-          asynchronous:true, 
-          evalScripts:true
-        }, this.options.ajaxOptions));
-    } else  {
-      new Ajax.Updater(
-        { success: this.element,
-          // don't update on failure (this could be an option)
-          failure: null }, 
-        this.url, Object.extend({
-          parameters: this.options.callback(form, value),
-          onComplete: this.onComplete.bind(this),
-          onFailure: this.onFailure.bind(this)
-        }, this.options.ajaxOptions));
-    }
-    // stop the event to avoid a page refresh in Safari
-    if (arguments.length > 1) {
-      Event.stop(arguments[0]);
-    }
-    return false;
-  },
-  onLoading: function() {
-    this.saving = true;
-    this.removeForm();
-    this.leaveHover();
-    this.showSaving();
-  },
-  showSaving: function() {
-    this.oldInnerHTML = this.element.innerHTML;
-    this.element.innerHTML = this.options.savingText;
-    Element.addClassName(this.element, this.options.savingClassName);
-    this.element.style.backgroundColor = this.originalBackground;
-    Element.show(this.element);
-  },
-  removeForm: function() {
-    if(this.form) {
-      if (this.form.parentNode) Element.remove(this.form);
-      this.form = null;
-    }
-  },
-  enterHover: function() {
-    if (this.saving) return;
-    this.element.style.backgroundColor = this.options.highlightcolor;
-    if (this.effect) {
-      this.effect.cancel();
-    }
-    Element.addClassName(this.element, this.options.hoverClassName)
-  },
-  leaveHover: function() {
-    if (this.options.backgroundColor) {
-      this.element.style.backgroundColor = this.oldBackground;
-    }
-    Element.removeClassName(this.element, this.options.hoverClassName)
-    if (this.saving) return;
-    this.effect = new Effect.Highlight(this.element, {
-      startcolor: this.options.highlightcolor,
-      endcolor: this.options.highlightendcolor,
-      restorecolor: this.originalBackground
-    });
-  },
-  leaveEditMode: function() {
-    Element.removeClassName(this.element, this.options.savingClassName);
-    this.removeForm();
-    this.leaveHover();
-    this.element.style.backgroundColor = this.originalBackground;
-    Element.show(this.element);
-    if (this.options.externalControl) {
-      Element.show(this.options.externalControl);
-    }
-    this.editing = false;
-    this.saving = false;
-    this.oldInnerHTML = null;
-    this.onLeaveEditMode();
-  },
-  onComplete: function(transport) {
-    this.leaveEditMode();
-    this.options.onComplete.bind(this)(transport, this.element);
-  },
-  onEnterEditMode: function() {},
-  onLeaveEditMode: function() {},
-  dispose: function() {
-    if (this.oldInnerHTML) {
-      this.element.innerHTML = this.oldInnerHTML;
-    }
-    this.leaveEditMode();
-    Event.stopObserving(this.element, 'click', this.onclickListener);
-    Event.stopObserving(this.element, 'mouseover', this.mouseoverListener);
-    Event.stopObserving(this.element, 'mouseout', this.mouseoutListener);
-    if (this.options.externalControl) {
-      Event.stopObserving(this.options.externalControl, 'click', this.onclickListener);
-      Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener);
-      Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
-    }
-  }
-};
-
-Ajax.InPlaceCollectionEditor = Class.create();
-Object.extend(Ajax.InPlaceCollectionEditor.prototype, Ajax.InPlaceEditor.prototype);
-Object.extend(Ajax.InPlaceCollectionEditor.prototype, {
-  createEditField: function() {
-    if (!this.cached_selectTag) {
-      var selectTag = document.createElement("select");
-      var collection = this.options.collection || [];
-      var optionTag;
-      collection.each(function(e,i) {
-        optionTag = document.createElement("option");
-        optionTag.value = (e instanceof Array) ? e[0] : e;
-        if(this.options.value==optionTag.value) optionTag.selected = true;
-        optionTag.appendChild(document.createTextNode((e instanceof Array) ? e[1] : e));
-        selectTag.appendChild(optionTag);
-      }.bind(this));
-      this.cached_selectTag = selectTag;
-    }
-
-    this.editField = this.cached_selectTag;
-    if(this.options.loadTextURL) this.loadExternalText();
-    this.form.appendChild(this.editField);
-    this.options.callback = function(form, value) {
-      return "value=" + encodeURIComponent(value);
-    }
-  }
-});
-
-// Delayed observer, like Form.Element.Observer, 
-// but waits for delay after last key input
-// Ideal for live-search fields
-
-Form.Element.DelayedObserver = Class.create();
-Form.Element.DelayedObserver.prototype = {
-  initialize: function(element, delay, callback) {
-    this.delay     = delay || 0.5;
-    this.element   = $PR(element);
-    this.callback  = callback;
-    this.timer     = null;
-    this.lastValue = $F(this.element); 
-    Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
-  },
-  delayedListener: function(event) {
-    if(this.lastValue == $F(this.element)) return;
-    if(this.timer) clearTimeout(this.timer);
-    this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
-    this.lastValue = $F(this.element);
-  },
-  onTimerEvent: function() {
-    this.timer = null;
-    this.callback(this.element, $F(this.element));
-  }
-};
diff --git a/library/cropper/lib/dragdrop.js b/library/cropper/lib/dragdrop.js
deleted file mode 100644 (file)
index baa607c..0000000
+++ /dev/null
@@ -1,915 +0,0 @@
-// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
-//           (c) 2005 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
-// 
-// See scriptaculous.js for full license.
-
-/*--------------------------------------------------------------------------*/
-
-var Droppables = {
-  drops: [],
-
-  remove: function(element) {
-    this.drops = this.drops.reject(function(d) { return d.element==$PR(element) });
-  },
-
-  add: function(element) {
-    element = $PR(element);
-    var options = Object.extend({
-      greedy:     true,
-      hoverclass: null,
-      tree:       false
-    }, arguments[1] || {});
-
-    // cache containers
-    if(options.containment) {
-      options._containers = [];
-      var containment = options.containment;
-      if((typeof containment == 'object') && 
-        (containment.constructor == Array)) {
-        containment.each( function(c) { options._containers.push($PR(c)) });
-      } else {
-        options._containers.push($PR(containment));
-      }
-    }
-    
-    if(options.accept) options.accept = [options.accept].flatten();
-
-    Element.makePositioned(element); // fix IE
-    options.element = element;
-
-    this.drops.push(options);
-  },
-  
-  findDeepestChild: function(drops) {
-    deepest = drops[0];
-      
-    for (i = 1; i < drops.length; ++i)
-      if (Element.isParent(drops[i].element, deepest.element))
-        deepest = drops[i];
-    
-    return deepest;
-  },
-
-  isContained: function(element, drop) {
-    var containmentNode;
-    if(drop.tree) {
-      containmentNode = element.treeNode; 
-    } else {
-      containmentNode = element.parentNode;
-    }
-    return drop._containers.detect(function(c) { return containmentNode == c });
-  },
-  
-  isAffected: function(point, element, drop) {
-    return (
-      (drop.element!=element) &&
-      ((!drop._containers) ||
-        this.isContained(element, drop)) &&
-      ((!drop.accept) ||
-        (Element.classNames(element).detect( 
-          function(v) { return drop.accept.include(v) } ) )) &&
-      Position.within(drop.element, point[0], point[1]) );
-  },
-
-  deactivate: function(drop) {
-    if(drop.hoverclass)
-      Element.removeClassName(drop.element, drop.hoverclass);
-    this.last_active = null;
-  },
-
-  activate: function(drop) {
-    if(drop.hoverclass)
-      Element.addClassName(drop.element, drop.hoverclass);
-    this.last_active = drop;
-  },
-
-  show: function(point, element) {
-    if(!this.drops.length) return;
-    var affected = [];
-    
-    if(this.last_active) this.deactivate(this.last_active);
-    this.drops.each( function(drop) {
-      if(Droppables.isAffected(point, element, drop))
-        affected.push(drop);
-    });
-        
-    if(affected.length>0) {
-      drop = Droppables.findDeepestChild(affected);
-      Position.within(drop.element, point[0], point[1]);
-      if(drop.onHover)
-        drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
-      
-      Droppables.activate(drop);
-    }
-  },
-
-  fire: function(event, element) {
-    if(!this.last_active) return;
-    Position.prepare();
-
-    if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
-      if (this.last_active.onDrop) 
-        this.last_active.onDrop(element, this.last_active.element, event);
-  },
-
-  reset: function() {
-    if(this.last_active)
-      this.deactivate(this.last_active);
-  }
-}
-
-var Draggables = {
-  drags: [],
-  observers: [],
-  
-  register: function(draggable) {
-    if(this.drags.length == 0) {
-      this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
-      this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
-      this.eventKeypress  = this.keyPress.bindAsEventListener(this);
-      
-      Event.observe(document, "mouseup", this.eventMouseUp);
-      Event.observe(document, "mousemove", this.eventMouseMove);
-      Event.observe(document, "keypress", this.eventKeypress);
-    }
-    this.drags.push(draggable);
-  },
-  
-  unregister: function(draggable) {
-    this.drags = this.drags.reject(function(d) { return d==draggable });
-    if(this.drags.length == 0) {
-      Event.stopObserving(document, "mouseup", this.eventMouseUp);
-      Event.stopObserving(document, "mousemove", this.eventMouseMove);
-      Event.stopObserving(document, "keypress", this.eventKeypress);
-    }
-  },
-  
-  activate: function(draggable) {
-    window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
-    this.activeDraggable = draggable;
-  },
-  
-  deactivate: function() {
-    this.activeDraggable = null;
-  },
-  
-  updateDrag: function(event) {
-    if(!this.activeDraggable) return;
-    var pointer = [Event.pointerX(event), Event.pointerY(event)];
-    // Mozilla-based browsers fire successive mousemove events with
-    // the same coordinates, prevent needless redrawing (moz bug?)
-    if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
-    this._lastPointer = pointer;
-    this.activeDraggable.updateDrag(event, pointer);
-  },
-  
-  endDrag: function(event) {
-    if(!this.activeDraggable) return;
-    this._lastPointer = null;
-    this.activeDraggable.endDrag(event);
-    this.activeDraggable = null;
-  },
-  
-  keyPress: function(event) {
-    if(this.activeDraggable)
-      this.activeDraggable.keyPress(event);
-  },
-  
-  addObserver: function(observer) {
-    this.observers.push(observer);
-    this._cacheObserverCallbacks();
-  },
-  
-  removeObserver: function(element) {  // element instead of observer fixes mem leaks
-    this.observers = this.observers.reject( function(o) { return o.element==element });
-    this._cacheObserverCallbacks();
-  },
-  
-  notify: function(eventName, draggable, event) {  // 'onStart', 'onEnd', 'onDrag'
-    if(this[eventName+'Count'] > 0)
-      this.observers.each( function(o) {
-        if(o[eventName]) o[eventName](eventName, draggable, event);
-      });
-  },
-  
-  _cacheObserverCallbacks: function() {
-    ['onStart','onEnd','onDrag'].each( function(eventName) {
-      Draggables[eventName+'Count'] = Draggables.observers.select(
-        function(o) { return o[eventName]; }
-      ).length;
-    });
-  }
-}
-
-/*--------------------------------------------------------------------------*/
-
-var Draggable = Class.create();
-Draggable.prototype = {
-  initialize: function(element) {
-    var options = Object.extend({
-      handle: false,
-      starteffect: function(element) {
-        element._opacity = Element.getOpacity(element); 
-        new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7}); 
-      },
-      reverteffect: function(element, top_offset, left_offset) {
-        var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
-        element._revert = new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur});
-      },
-      endeffect: function(element) {
-        var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0
-        new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity}); 
-      },
-      zindex: 1000,
-      revert: false,
-      scroll: false,
-      scrollSensitivity: 20,
-      scrollSpeed: 15,
-      snap: false   // false, or xy or [x,y] or function(x,y){ return [x,y] }
-    }, arguments[1] || {});
-
-    this.element = $PR(element);
-    
-    if(options.handle && (typeof options.handle == 'string')) {
-      var h = Element.childrenWithClassName(this.element, options.handle, true);
-      if(h.length>0) this.handle = h[0];
-    }
-    if(!this.handle) this.handle = $PR(options.handle);
-    if(!this.handle) this.handle = this.element;
-    
-    if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML)
-      options.scroll = $PR(options.scroll);
-
-    Element.makePositioned(this.element); // fix IE    
-
-    this.delta    = this.currentDelta();
-    this.options  = options;
-    this.dragging = false;   
-
-    this.eventMouseDown = this.initDrag.bindAsEventListener(this);
-    Event.observe(this.handle, "mousedown", this.eventMouseDown);
-    
-    Draggables.register(this);
-  },
-  
-  destroy: function() {
-    Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
-    Draggables.unregister(this);
-  },
-  
-  currentDelta: function() {
-    return([
-      parseInt(Element.getStyle(this.element,'left') || '0'),
-      parseInt(Element.getStyle(this.element,'top') || '0')]);
-  },
-  
-  initDrag: function(event) {
-    if(Event.isLeftClick(event)) {    
-      // abort on form elements, fixes a Firefox issue
-      var src = Event.element(event);
-      if(src.tagName && (
-        src.tagName=='INPUT' ||
-        src.tagName=='SELECT' ||
-        src.tagName=='OPTION' ||
-        src.tagName=='BUTTON' ||
-        src.tagName=='TEXTAREA')) return;
-        
-      if(this.element._revert) {
-        this.element._revert.cancel();
-        this.element._revert = null;
-      }
-      
-      var pointer = [Event.pointerX(event), Event.pointerY(event)];
-      var pos     = Position.cumulativeOffset(this.element);
-      this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
-      
-      Draggables.activate(this);
-      Event.stop(event);
-    }
-  },
-  
-  startDrag: function(event) {
-    this.dragging = true;
-    
-    if(this.options.zindex) {
-      this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
-      this.element.style.zIndex = this.options.zindex;
-    }
-    
-    if(this.options.ghosting) {
-      this._clone = this.element.cloneNode(true);
-      Position.absolutize(this.element);
-      this.element.parentNode.insertBefore(this._clone, this.element);
-    }
-    
-    if(this.options.scroll) {
-      if (this.options.scroll == window) {
-        var where = this._getWindowScroll(this.options.scroll);
-        this.originalScrollLeft = where.left;
-        this.originalScrollTop = where.top;
-      } else {
-        this.originalScrollLeft = this.options.scroll.scrollLeft;
-        this.originalScrollTop = this.options.scroll.scrollTop;
-      }
-    }
-    
-    Draggables.notify('onStart', this, event);
-    if(this.options.starteffect) this.options.starteffect(this.element);
-  },
-  
-  updateDrag: function(event, pointer) {
-    if(!this.dragging) this.startDrag(event);
-    Position.prepare();
-    Droppables.show(pointer, this.element);
-    Draggables.notify('onDrag', this, event);
-    this.draw(pointer);
-    if(this.options.change) this.options.change(this);
-    
-    if(this.options.scroll) {
-      this.stopScrolling();
-      
-      var p;
-      if (this.options.scroll == window) {
-        with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
-      } else {
-        p = Position.page(this.options.scroll);
-        p[0] += this.options.scroll.scrollLeft;
-        p[1] += this.options.scroll.scrollTop;
-        p.push(p[0]+this.options.scroll.offsetWidth);
-        p.push(p[1]+this.options.scroll.offsetHeight);
-      }
-      var speed = [0,0];
-      if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
-      if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
-      if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);
-      if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
-      this.startScrolling(speed);
-    }
-    
-    // fix AppleWebKit rendering
-    if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
-    
-    Event.stop(event);
-  },
-  
-  finishDrag: function(event, success) {
-    this.dragging = false;
-
-    if(this.options.ghosting) {
-      Position.relativize(this.element);
-      Element.remove(this._clone);
-      this._clone = null;
-    }
-
-    if(success) Droppables.fire(event, this.element);
-    Draggables.notify('onEnd', this, event);
-
-    var revert = this.options.revert;
-    if(revert && typeof revert == 'function') revert = revert(this.element);
-    
-    var d = this.currentDelta();
-    if(revert && this.options.reverteffect) {
-      this.options.reverteffect(this.element, 
-        d[1]-this.delta[1], d[0]-this.delta[0]);
-    } else {
-      this.delta = d;
-    }
-
-    if(this.options.zindex)
-      this.element.style.zIndex = this.originalZ;
-
-    if(this.options.endeffect) 
-      this.options.endeffect(this.element);
-
-    Draggables.deactivate(this);
-    Droppables.reset();
-  },
-  
-  keyPress: function(event) {
-    if(event.keyCode!=Event.KEY_ESC) return;
-    this.finishDrag(event, false);
-    Event.stop(event);
-  },
-  
-  endDrag: function(event) {
-    if(!this.dragging) return;
-    this.stopScrolling();
-    this.finishDrag(event, true);
-    Event.stop(event);
-  },
-  
-  draw: function(point) {
-    var pos = Position.cumulativeOffset(this.element);
-    var d = this.currentDelta();
-    pos[0] -= d[0]; pos[1] -= d[1];
-    
-    if(this.options.scroll && (this.options.scroll != window)) {
-      pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
-      pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
-    }
-    
-    var p = [0,1].map(function(i){ 
-      return (point[i]-pos[i]-this.offset[i]) 
-    }.bind(this));
-    
-    if(this.options.snap) {
-      if(typeof this.options.snap == 'function') {
-        p = this.options.snap(p[0],p[1],this);
-      } else {
-      if(this.options.snap instanceof Array) {
-        p = p.map( function(v, i) {
-          return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this))
-      } else {
-        p = p.map( function(v) {
-          return Math.round(v/this.options.snap)*this.options.snap }.bind(this))
-      }
-    }}
-    
-    var style = this.element.style;
-    if((!this.options.constraint) || (this.options.constraint=='horizontal'))
-      style.left = p[0] + "px";
-    if((!this.options.constraint) || (this.options.constraint=='vertical'))
-      style.top  = p[1] + "px";
-    if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
-  },
-  
-  stopScrolling: function() {
-    if(this.scrollInterval) {
-      clearInterval(this.scrollInterval);
-      this.scrollInterval = null;
-      Draggables._lastScrollPointer = null;
-    }
-  },
-  
-  startScrolling: function(speed) {
-    this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
-    this.lastScrolled = new Date();
-    this.scrollInterval = setInterval(this.scroll.bind(this), 10);
-  },
-  
-  scroll: function() {
-    var current = new Date();
-    var delta = current - this.lastScrolled;
-    this.lastScrolled = current;
-    if(this.options.scroll == window) {
-      with (this._getWindowScroll(this.options.scroll)) {
-        if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
-          var d = delta / 1000;
-          this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
-        }
-      }
-    } else {
-      this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
-      this.options.scroll.scrollTop  += this.scrollSpeed[1] * delta / 1000;
-    }
-    
-    Position.prepare();
-    Droppables.show(Draggables._lastPointer, this.element);
-    Draggables.notify('onDrag', this);
-    Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
-    Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
-    Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
-    if (Draggables._lastScrollPointer[0] < 0)
-      Draggables._lastScrollPointer[0] = 0;
-    if (Draggables._lastScrollPointer[1] < 0)
-      Draggables._lastScrollPointer[1] = 0;
-    this.draw(Draggables._lastScrollPointer);
-    
-    if(this.options.change) this.options.change(this);
-  },
-  
-  _getWindowScroll: function(w) {
-    var T, L, W, H;
-    with (w.document) {
-      if (w.document.documentElement && documentElement.scrollTop) {
-        T = documentElement.scrollTop;
-        L = documentElement.scrollLeft;
-      } else if (w.document.body) {
-        T = body.scrollTop;
-        L = body.scrollLeft;
-      }
-      if (w.innerWidth) {
-        W = w.innerWidth;
-        H = w.innerHeight;
-      } else if (w.document.documentElement && documentElement.clientWidth) {
-        W = documentElement.clientWidth;
-        H = documentElement.clientHeight;
-      } else {
-        W = body.offsetWidth;
-        H = body.offsetHeight
-      }
-    }
-    return { top: T, left: L, width: W, height: H };
-  }
-}
-
-/*--------------------------------------------------------------------------*/
-
-var SortableObserver = Class.create();
-SortableObserver.prototype = {
-  initialize: function(element, observer) {
-    this.element   = $PR(element);
-    this.observer  = observer;
-    this.lastValue = Sortable.serialize(this.element);
-  },
-  
-  onStart: function() {
-    this.lastValue = Sortable.serialize(this.element);
-  },
-  
-  onEnd: function() {
-    Sortable.unmark();
-    if(this.lastValue != Sortable.serialize(this.element))
-      this.observer(this.element)
-  }
-}
-
-var Sortable = {
-  sortables: {},
-  
-  _findRootElement: function(element) {
-    while (element.tagName != "BODY") {  
-      if(element.id && Sortable.sortables[element.id]) return element;
-      element = element.parentNode;
-    }
-  },
-
-  options: function(element) {
-    element = Sortable._findRootElement($PR(element));
-    if(!element) return;
-    return Sortable.sortables[element.id];
-  },
-  
-  destroy: function(element){
-    var s = Sortable.options(element);
-    
-    if(s) {
-      Draggables.removeObserver(s.element);
-      s.droppables.each(function(d){ Droppables.remove(d) });
-      s.draggables.invoke('destroy');
-      
-      delete Sortable.sortables[s.element.id];
-    }
-  },
-
-  create: function(element) {
-    element = $PR(element);
-    var options = Object.extend({ 
-      element:     element,
-      tag:         'li',       // assumes li children, override with tag: 'tagname'
-      dropOnEmpty: false,
-      tree:        false,
-      treeTag:     'ul',
-      overlap:     'vertical', // one of 'vertical', 'horizontal'
-      constraint:  'vertical', // one of 'vertical', 'horizontal', false
-      containment: element,    // also takes array of elements (or id's); or false
-      handle:      false,      // or a CSS class
-      only:        false,
-      hoverclass:  null,
-      ghosting:    false,
-      scroll:      false,
-      scrollSensitivity: 20,
-      scrollSpeed: 15,
-      format:      /^[^_]*_(.*)$/,
-      onChange:    Prototype.emptyFunction,
-      onUpdate:    Prototype.emptyFunction
-    }, arguments[1] || {});
-
-    // clear any old sortable with same element
-    this.destroy(element);
-
-    // build options for the draggables
-    var options_for_draggable = {
-      revert:      true,
-      scroll:      options.scroll,
-      scrollSpeed: options.scrollSpeed,
-      scrollSensitivity: options.scrollSensitivity,
-      ghosting:    options.ghosting,
-      constraint:  options.constraint,
-      handle:      options.handle };
-
-    if(options.starteffect)
-      options_for_draggable.starteffect = options.starteffect;
-
-    if(options.reverteffect)
-      options_for_draggable.reverteffect = options.reverteffect;
-    else
-      if(options.ghosting) options_for_draggable.reverteffect = function(element) {
-        element.style.top  = 0;
-        element.style.left = 0;
-      };
-
-    if(options.endeffect)
-      options_for_draggable.endeffect = options.endeffect;
-
-    if(options.zindex)
-      options_for_draggable.zindex = options.zindex;
-
-    // build options for the droppables  
-    var options_for_droppable = {
-      overlap:     options.overlap,
-      containment: options.containment,
-      tree:        options.tree,
-      hoverclass:  options.hoverclass,
-      onHover:     Sortable.onHover
-      //greedy:      !options.dropOnEmpty
-    }
-    
-    var options_for_tree = {
-      onHover:      Sortable.onEmptyHover,
-      overlap:      options.overlap,
-      containment:  options.containment,
-      hoverclass:   options.hoverclass
-    }
-
-    // fix for gecko engine
-    Element.cleanWhitespace(element); 
-
-    options.draggables = [];
-    options.droppables = [];
-
-    // drop on empty handling
-    if(options.dropOnEmpty || options.tree) {
-      Droppables.add(element, options_for_tree);
-      options.droppables.push(element);
-    }
-
-    (this.findElements(element, options) || []).each( function(e) {
-      // handles are per-draggable
-      var handle = options.handle ? 
-        Element.childrenWithClassName(e, options.handle)[0] : e;    
-      options.draggables.push(
-        new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
-      Droppables.add(e, options_for_droppable);
-      if(options.tree) e.treeNode = element;
-      options.droppables.push(e);      
-    });
-    
-    if(options.tree) {
-      (Sortable.findTreeElements(element, options) || []).each( function(e) {
-        Droppables.add(e, options_for_tree);
-        e.treeNode = element;
-        options.droppables.push(e);
-      });
-    }
-
-    // keep reference
-    this.sortables[element.id] = options;
-
-    // for onupdate
-    Draggables.addObserver(new SortableObserver(element, options.onUpdate));
-
-  },
-
-  // return all suitable-for-sortable elements in a guaranteed order
-  findElements: function(element, options) {
-    return Element.findChildren(
-      element, options.only, options.tree ? true : false, options.tag);
-  },
-  
-  findTreeElements: function(element, options) {
-    return Element.findChildren(
-      element, options.only, options.tree ? true : false, options.treeTag);
-  },
-
-  onHover: function(element, dropon, overlap) {
-    if(Element.isParent(dropon, element)) return;
-
-    if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
-      return;
-    } else if(overlap>0.5) {
-      Sortable.mark(dropon, 'before');
-      if(dropon.previousSibling != element) {
-        var oldParentNode = element.parentNode;
-        element.style.visibility = "hidden"; // fix gecko rendering
-        dropon.parentNode.insertBefore(element, dropon);
-        if(dropon.parentNode!=oldParentNode) 
-          Sortable.options(oldParentNode).onChange(element);
-        Sortable.options(dropon.parentNode).onChange(element);
-      }
-    } else {
-      Sortable.mark(dropon, 'after');
-      var nextElement = dropon.nextSibling || null;
-      if(nextElement != element) {
-        var oldParentNode = element.parentNode;
-        element.style.visibility = "hidden"; // fix gecko rendering
-        dropon.parentNode.insertBefore(element, nextElement);
-        if(dropon.parentNode!=oldParentNode) 
-          Sortable.options(oldParentNode).onChange(element);
-        Sortable.options(dropon.parentNode).onChange(element);
-      }
-    }
-  },
-  
-  onEmptyHover: function(element, dropon, overlap) {
-    var oldParentNode = element.parentNode;
-    var droponOptions = Sortable.options(dropon);
-        
-    if(!Element.isParent(dropon, element)) {
-      var index;
-      
-      var children = Sortable.findElements(dropon, {tag: droponOptions.tag});
-      var child = null;
-            
-      if(children) {
-        var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
-        
-        for (index = 0; index < children.length; index += 1) {
-          if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
-            offset -= Element.offsetSize (children[index], droponOptions.overlap);
-          } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
-            child = index + 1 < children.length ? children[index + 1] : null;
-            break;
-          } else {
-            child = children[index];
-            break;
-          }
-        }
-      }
-      
-      dropon.insertBefore(element, child);
-      
-      Sortable.options(oldParentNode).onChange(element);
-      droponOptions.onChange(element);
-    }
-  },
-
-  unmark: function() {
-    if(Sortable._marker) Element.hide(Sortable._marker);
-  },
-
-  mark: function(dropon, position) {
-    // mark on ghosting only
-    var sortable = Sortable.options(dropon.parentNode);
-    if(sortable && !sortable.ghosting) return; 
-
-    if(!Sortable._marker) {
-      Sortable._marker = $PR('dropmarker') || document.createElement('DIV');
-      Element.hide(Sortable._marker);
-      Element.addClassName(Sortable._marker, 'dropmarker');
-      Sortable._marker.style.position = 'absolute';
-      document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
-    }    
-    var offsets = Position.cumulativeOffset(dropon);
-    Sortable._marker.style.left = offsets[0] + 'px';
-    Sortable._marker.style.top = offsets[1] + 'px';
-    
-    if(position=='after')
-      if(sortable.overlap == 'horizontal') 
-        Sortable._marker.style.left = (offsets[0]+dropon.clientWidth) + 'px';
-      else
-        Sortable._marker.style.top = (offsets[1]+dropon.clientHeight) + 'px';
-    
-    Element.show(Sortable._marker);
-  },
-  
-  _tree: function(element, options, parent) {
-    var children = Sortable.findElements(element, options) || [];
-  
-    for (var i = 0; i < children.length; ++i) {
-      var match = children[i].id.match(options.format);
-
-      if (!match) continue;
-      
-      var child = {
-        id: encodeURIComponent(match ? match[1] : null),
-        element: element,
-        parent: parent,
-        children: new Array,
-        position: parent.children.length,
-        container: Sortable._findChildrenElement(children[i], options.treeTag.toUpperCase())
-      }
-      
-      /* Get the element containing the children and recurse over it */
-      if (child.container)
-        this._tree(child.container, options, child)
-      
-      parent.children.push (child);
-    }
-
-    return parent; 
-  },
-
-  /* Finds the first element of the given tag type within a parent element.
-    Used for finding the first LI[ST] within a L[IST]I[TEM].*/
-  _findChildrenElement: function (element, containerTag) {
-    if (element && element.hasChildNodes)
-      for (var i = 0; i < element.childNodes.length; ++i)
-        if (element.childNodes[i].tagName == containerTag)
-          return element.childNodes[i];
-  
-    return null;
-  },
-
-  tree: function(element) {
-    element = $PR(element);
-    var sortableOptions = this.options(element);
-    var options = Object.extend({
-      tag: sortableOptions.tag,
-      treeTag: sortableOptions.treeTag,
-      only: sortableOptions.only,
-      name: element.id,
-      format: sortableOptions.format
-    }, arguments[1] || {});
-    
-    var root = {
-      id: null,
-      parent: null,
-      children: new Array,
-      container: element,
-      position: 0
-    }
-    
-    return Sortable._tree (element, options, root);
-  },
-
-  /* Construct a [i] index for a particular node */
-  _constructIndex: function(node) {
-    var index = '';
-    do {
-      if (node.id) index = '[' + node.position + ']' + index;
-    } while ((node = node.parent) != null);
-    return index;
-  },
-
-  sequence: function(element) {
-    element = $PR(element);
-    var options = Object.extend(this.options(element), arguments[1] || {});
-    
-    return $PR(this.findElements(element, options) || []).map( function(item) {
-      return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
-    });
-  },
-
-  setSequence: function(element, new_sequence) {
-    element = $PR(element);
-    var options = Object.extend(this.options(element), arguments[2] || {});
-    
-    var nodeMap = {};
-    this.findElements(element, options).each( function(n) {
-        if (n.id.match(options.format))
-            nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
-        n.parentNode.removeChild(n);
-    });
-   
-    new_sequence.each(function(ident) {
-      var n = nodeMap[ident];
-      if (n) {
-        n[1].appendChild(n[0]);
-        delete nodeMap[ident];
-      }
-    });
-  },
-  
-  serialize: function(element) {
-    element = $PR(element);
-    var options = Object.extend(Sortable.options(element), arguments[1] || {});
-    var name = encodeURIComponent(
-      (arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
-    
-    if (options.tree) {
-      return Sortable.tree(element, arguments[1]).children.map( function (item) {
-        return [name + Sortable._constructIndex(item) + "=" + 
-                encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
-      }).flatten().join('&');
-    } else {
-      return Sortable.sequence(element, arguments[1]).map( function(item) {
-        return name + "[]=" + encodeURIComponent(item);
-      }).join('&');
-    }
-  }
-}
-
-/* Returns true if child is contained within element */
-Element.isParent = function(child, element) {
-  if (!child.parentNode || child == element) return false;
-
-  if (child.parentNode == element) return true;
-
-  return Element.isParent(child.parentNode, element);
-}
-
-Element.findChildren = function(element, only, recursive, tagName) {    
-  if(!element.hasChildNodes()) return null;
-  tagName = tagName.toUpperCase();
-  if(only) only = [only].flatten();
-  var elements = [];
-  $A(element.childNodes).each( function(e) {
-    if(e.tagName && e.tagName.toUpperCase()==tagName &&
-      (!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
-        elements.push(e);
-    if(recursive) {
-      var grandchildren = Element.findChildren(e, only, recursive, tagName);
-      if(grandchildren) elements.push(grandchildren);
-    }
-  });
-
-  return (elements.length>0 ? elements.flatten() : []);
-}
-
-Element.offsetSize = function (element, type) {
-  if (type == 'vertical' || type == 'height')
-    return element.offsetHeight;
-  else
-    return element.offsetWidth;
-}
diff --git a/library/cropper/lib/effects.js b/library/cropper/lib/effects.js
deleted file mode 100644 (file)
index 7e0407d..0000000
+++ /dev/null
@@ -1,958 +0,0 @@
-// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
-// Contributors:
-//  Justin Palmer (http://encytemedia.com/)
-//  Mark Pilgrim (http://diveintomark.org/)
-//  Martin Bialasinki
-// 
-// See scriptaculous.js for full license.  
-
-// converts rgb() and #xxx to #xxxxxx format,  
-// returns self (or first argument) if not convertable  
-String.prototype.parseColor = function() {  
-  var color = '#';  
-  if(this.slice(0,4) == 'rgb(') {  
-    var cols = this.slice(4,this.length-1).split(',');  
-    var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);  
-  } else {  
-    if(this.slice(0,1) == '#') {  
-      if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();  
-      if(this.length==7) color = this.toLowerCase();  
-    }  
-  }  
-  return(color.length==7 ? color : (arguments[0] || this));  
-}
-
-/*--------------------------------------------------------------------------*/
-
-Element.collectTextNodes = function(element) {  
-  return $A($PR(element).childNodes).collect( function(node) {
-    return (node.nodeType==3 ? node.nodeValue : 
-      (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
-  }).flatten().join('');
-}
-
-Element.collectTextNodesIgnoreClass = function(element, className) {  
-  return $A($PR(element).childNodes).collect( function(node) {
-    return (node.nodeType==3 ? node.nodeValue : 
-      ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? 
-        Element.collectTextNodesIgnoreClass(node, className) : ''));
-  }).flatten().join('');
-}
-
-Element.setContentZoom = function(element, percent) {
-  element = $PR(element);  
-  Element.setStyle(element, {fontSize: (percent/100) + 'em'});   
-  if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
-}
-
-Element.getOpacity = function(element){  
-  var opacity;
-  if (opacity = Element.getStyle(element, 'opacity'))  
-    return parseFloat(opacity);  
-  if (opacity = (Element.getStyle(element, 'filter') || '').match(/alpha\(opacity=(.*)\)/))  
-    if(opacity[1]) return parseFloat(opacity[1]) / 100;  
-  return 1.0;  
-}
-
-Element.setOpacity = function(element, value){  
-  element= $PR(element);  
-  if (value == 1){
-    Element.setStyle(element, { opacity: 
-      (/Gecko/.test(navigator.userAgent) && !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 
-      0.999999 : null });
-    if(/MSIE/.test(navigator.userAgent))  
-      Element.setStyle(element, {filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});  
-  } else {  
-    if(value < 0.00001) value = 0;  
-    Element.setStyle(element, {opacity: value});
-    if(/MSIE/.test(navigator.userAgent))  
-     Element.setStyle(element, 
-       { filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'') +
-                 'alpha(opacity='+value*100+')' });  
-  }
-}  
-Element.getInlineOpacity = function(element){  
-  return $PR(element).style.opacity || '';
-}  
-
-Element.childrenWithClassName = function(element, className, findFirst) {
-  var classNameRegExp = new RegExp("(^|\\s)" + className + "(\\s|$)");
-  var results = $A($PR(element).getElementsByTagName('*'))[findFirst ? 'detect' : 'select']( function(c) { 
-    return (c.className && c.className.match(classNameRegExp));
-  });
-  if(!results) results = [];
-  return results;
-}
-
-Element.forceRerendering = function(element) {
-  try {
-    element = $PR(element);
-    var n = document.createTextNode(' ');
-    element.appendChild(n);
-    element.removeChild(n);
-  } catch(e) { }
-};
-
-/*--------------------------------------------------------------------------*/
-
-Array.prototype.call = function() {
-  var args = arguments;
-  this.each(function(f){ f.apply(this, args) });
-}
-
-/*--------------------------------------------------------------------------*/
-
-var Effect = {
-  tagifyText: function(element) {
-    var tagifyStyle = 'position:relative';
-    if(/MSIE/.test(navigator.userAgent)) tagifyStyle += ';zoom:1';
-    element = $PR(element);
-    $A(element.childNodes).each( function(child) {
-      if(child.nodeType==3) {
-        child.nodeValue.toArray().each( function(character) {
-          element.insertBefore(
-            Builder.node('span',{style: tagifyStyle},
-              character == ' ' ? String.fromCharCode(160) : character), 
-              child);
-        });
-        Element.remove(child);
-      }
-    });
-  },
-  multiple: function(element, effect) {
-    var elements;
-    if(((typeof element == 'object') || 
-        (typeof element == 'function')) && 
-       (element.length))
-      elements = element;
-    else
-      elements = $PR(element).childNodes;
-      
-    var options = Object.extend({
-      speed: 0.1,
-      delay: 0.0
-    }, arguments[2] || {});
-    var masterDelay = options.delay;
-
-    $A(elements).each( function(element, index) {
-      new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
-    });
-  },
-  PAIRS: {
-    'slide':  ['SlideDown','SlideUp'],
-    'blind':  ['BlindDown','BlindUp'],
-    'appear': ['Appear','Fade']
-  },
-  toggle: function(element, effect) {
-    element = $PR(element);
-    effect = (effect || 'appear').toLowerCase();
-    var options = Object.extend({
-      queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
-    }, arguments[2] || {});
-    Effect[element.visible() ? 
-      Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
-  }
-};
-
-var Effect2 = Effect; // deprecated
-
-/* ------------- transitions ------------- */
-
-Effect.Transitions = {}
-
-Effect.Transitions.linear = function(pos) {
-  return pos;
-}
-Effect.Transitions.sinoidal = function(pos) {
-  return (-Math.cos(pos*Math.PI)/2) + 0.5;
-}
-Effect.Transitions.reverse  = function(pos) {
-  return 1-pos;
-}
-Effect.Transitions.flicker = function(pos) {
-  return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
-}
-Effect.Transitions.wobble = function(pos) {
-  return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
-}
-Effect.Transitions.pulse = function(pos) {
-  return (Math.floor(pos*10) % 2 == 0 ? 
-    (pos*10-Math.floor(pos*10)) : 1-(pos*10-Math.floor(pos*10)));
-}
-Effect.Transitions.none = function(pos) {
-  return 0;
-}
-Effect.Transitions.full = function(pos) {
-  return 1;
-}
-
-/* ------------- core effects ------------- */
-
-Effect.ScopedQueue = Class.create();
-Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
-  initialize: function() {
-    this.effects  = [];
-    this.interval = null;
-  },
-  _each: function(iterator) {
-    this.effects._each(iterator);
-  },
-  add: function(effect) {
-    var timestamp = new Date().getTime();
-    
-    var position = (typeof effect.options.queue == 'string') ? 
-      effect.options.queue : effect.options.queue.position;
-    
-    switch(position) {
-      case 'front':
-        // move unstarted effects after this effect  
-        this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
-            e.startOn  += effect.finishOn;
-            e.finishOn += effect.finishOn;
-          });
-        break;
-      case 'end':
-        // start effect after last queued effect has finished
-        timestamp = this.effects.pluck('finishOn').max() || timestamp;
-        break;
-    }
-    
-    effect.startOn  += timestamp;
-    effect.finishOn += timestamp;
-
-    if(!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
-      this.effects.push(effect);
-    
-    if(!this.interval) 
-      this.interval = setInterval(this.loop.bind(this), 40);
-  },
-  remove: function(effect) {
-    this.effects = this.effects.reject(function(e) { return e==effect });
-    if(this.effects.length == 0) {
-      clearInterval(this.interval);
-      this.interval = null;
-    }
-  },
-  loop: function() {
-    var timePos = new Date().getTime();
-    this.effects.invoke('loop', timePos);
-  }
-});
-
-Effect.Queues = {
-  instances: $H(),
-  get: function(queueName) {
-    if(typeof queueName != 'string') return queueName;
-    
-    if(!this.instances[queueName])
-      this.instances[queueName] = new Effect.ScopedQueue();
-      
-    return this.instances[queueName];
-  }
-}
-Effect.Queue = Effect.Queues.get('global');
-
-Effect.DefaultOptions = {
-  transition: Effect.Transitions.sinoidal,
-  duration:   1.0,   // seconds
-  fps:        25.0,  // max. 25fps due to Effect.Queue implementation
-  sync:       false, // true for combining
-  from:       0.0,
-  to:         1.0,
-  delay:      0.0,
-  queue:      'parallel'
-}
-
-Effect.Base = function() {};
-Effect.Base.prototype = {
-  position: null,
-  start: function(options) {
-    this.options      = Object.extend(Object.extend({},Effect.DefaultOptions), options || {});
-    this.currentFrame = 0;
-    this.state        = 'idle';
-    this.startOn      = this.options.delay*1000;
-    this.finishOn     = this.startOn + (this.options.duration*1000);
-    this.event('beforeStart');
-    if(!this.options.sync)
-      Effect.Queues.get(typeof this.options.queue == 'string' ? 
-        'global' : this.options.queue.scope).add(this);
-  },
-  loop: function(timePos) {
-    if(timePos >= this.startOn) {
-      if(timePos >= this.finishOn) {
-        this.render(1.0);
-        this.cancel();
-        this.event('beforeFinish');
-        if(this.finish) this.finish(); 
-        this.event('afterFinish');
-        return;  
-      }
-      var pos   = (timePos - this.startOn) / (this.finishOn - this.startOn);
-      var frame = Math.round(pos * this.options.fps * this.options.duration);
-      if(frame > this.currentFrame) {
-        this.render(pos);
-        this.currentFrame = frame;
-      }
-    }
-  },
-  render: function(pos) {
-    if(this.state == 'idle') {
-      this.state = 'running';
-      this.event('beforeSetup');
-      if(this.setup) this.setup();
-      this.event('afterSetup');
-    }
-    if(this.state == 'running') {
-      if(this.options.transition) pos = this.options.transition(pos);
-      pos *= (this.options.to-this.options.from);
-      pos += this.options.from;
-      this.position = pos;
-      this.event('beforeUpdate');
-      if(this.update) this.update(pos);
-      this.event('afterUpdate');
-    }
-  },
-  cancel: function() {
-    if(!this.options.sync)
-      Effect.Queues.get(typeof this.options.queue == 'string' ? 
-        'global' : this.options.queue.scope).remove(this);
-    this.state = 'finished';
-  },
-  event: function(eventName) {
-    if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
-    if(this.options[eventName]) this.options[eventName](this);
-  },
-  inspect: function() {
-    return '#<Effect:' + $H(this).inspect() + ',options:' + $H(this.options).inspect() + '>';
-  }
-}
-
-Effect.Parallel = Class.create();
-Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), {
-  initialize: function(effects) {
-    this.effects = effects || [];
-    this.start(arguments[1]);
-  },
-  update: function(position) {
-    this.effects.invoke('render', position);
-  },
-  finish: function(position) {
-    this.effects.each( function(effect) {
-      effect.render(1.0);
-      effect.cancel();
-      effect.event('beforeFinish');
-      if(effect.finish) effect.finish(position);
-      effect.event('afterFinish');
-    });
-  }
-});
-
-Effect.Opacity = Class.create();
-Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
-  initialize: function(element) {
-    this.element = $PR(element);
-    // make this work on IE on elements without 'layout'
-    if(/MSIE/.test(navigator.userAgent) && (!this.element.hasLayout))
-      this.element.setStyle({zoom: 1});
-    var options = Object.extend({
-      from: this.element.getOpacity() || 0.0,
-      to:   1.0
-    }, arguments[1] || {});
-    this.start(options);
-  },
-  update: function(position) {
-    this.element.setOpacity(position);
-  }
-});
-
-Effect.Move = Class.create();
-Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), {
-  initialize: function(element) {
-    this.element = $PR(element);
-    var options = Object.extend({
-      x:    0,
-      y:    0,
-      mode: 'relative'
-    }, arguments[1] || {});
-    this.start(options);
-  },
-  setup: function() {
-    // Bug in Opera: Opera returns the "real" position of a static element or
-    // relative element that does not have top/left explicitly set.
-    // ==> Always set top and left for position relative elements in your stylesheets 
-    // (to 0 if you do not need them) 
-    this.element.makePositioned();
-    this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
-    this.originalTop  = parseFloat(this.element.getStyle('top')  || '0');
-    if(this.options.mode == 'absolute') {
-      // absolute movement, so we need to calc deltaX and deltaY
-      this.options.x = this.options.x - this.originalLeft;
-      this.options.y = this.options.y - this.originalTop;
-    }
-  },
-  update: function(position) {
-    this.element.setStyle({
-      left: this.options.x  * position + this.originalLeft + 'px',
-      top:  this.options.y  * position + this.originalTop  + 'px'
-    });
-  }
-});
-
-// for backwards compatibility
-Effect.MoveBy = function(element, toTop, toLeft) {
-  return new Effect.Move(element, 
-    Object.extend({ x: toLeft, y: toTop }, arguments[3] || {}));
-};
-
-Effect.Scale = Class.create();
-Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
-  initialize: function(element, percent) {
-    this.element = $PR(element)
-    var options = Object.extend({
-      scaleX: true,
-      scaleY: true,
-      scaleContent: true,
-      scaleFromCenter: false,
-      scaleMode: 'box',        // 'box' or 'contents' or {} with provided values
-      scaleFrom: 100.0,
-      scaleTo:   percent
-    }, arguments[2] || {});
-    this.start(options);
-  },
-  setup: function() {
-    this.restoreAfterFinish = this.options.restoreAfterFinish || false;
-    this.elementPositioning = this.element.getStyle('position');
-    
-    this.originalStyle = {};
-    ['top','left','width','height','fontSize'].each( function(k) {
-      this.originalStyle[k] = this.element.style[k];
-    }.bind(this));
-      
-    this.originalTop  = this.element.offsetTop;
-    this.originalLeft = this.element.offsetLeft;
-    
-    var fontSize = this.element.getStyle('font-size') || '100%';
-    ['em','px','%'].each( function(fontSizeType) {
-      if(fontSize.indexOf(fontSizeType)>0) {
-        this.fontSize     = parseFloat(fontSize);
-        this.fontSizeType = fontSizeType;
-      }
-    }.bind(this));
-    
-    this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
-    
-    this.dims = null;
-    if(this.options.scaleMode=='box')
-      this.dims = [this.element.offsetHeight, this.element.offsetWidth];
-    if(/^content/.test(this.options.scaleMode))
-      this.dims = [this.element.scrollHeight, this.element.scrollWidth];
-    if(!this.dims)
-      this.dims = [this.options.scaleMode.originalHeight,
-                   this.options.scaleMode.originalWidth];
-  },
-  update: function(position) {
-    var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
-    if(this.options.scaleContent && this.fontSize)
-      this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
-    this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
-  },
-  finish: function(position) {
-    if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
-  },
-  setDimensions: function(height, width) {
-    var d = {};
-    if(this.options.scaleX) d.width = width + 'px';
-    if(this.options.scaleY) d.height = height + 'px';
-    if(this.options.scaleFromCenter) {
-      var topd  = (height - this.dims[0])/2;
-      var leftd = (width  - this.dims[1])/2;
-      if(this.elementPositioning == 'absolute') {
-        if(this.options.scaleY) d.top = this.originalTop-topd + 'px';
-        if(this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
-      } else {
-        if(this.options.scaleY) d.top = -topd + 'px';
-        if(this.options.scaleX) d.left = -leftd + 'px';
-      }
-    }
-    this.element.setStyle(d);
-  }
-});
-
-Effect.Highlight = Class.create();
-Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), {
-  initialize: function(element) {
-    this.element = $PR(element);
-    var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {});
-    this.start(options);
-  },
-  setup: function() {
-    // Prevent executing on elements not in the layout flow
-    if(this.element.getStyle('display')=='none') { this.cancel(); return; }
-    // Disable background image during the effect
-    this.oldStyle = {
-      backgroundImage: this.element.getStyle('background-image') };
-    this.element.setStyle({backgroundImage: 'none'});
-    if(!this.options.endcolor)
-      this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
-    if(!this.options.restorecolor)
-      this.options.restorecolor = this.element.getStyle('background-color');
-    // init color calculations
-    this._base  = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
-    this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
-  },
-  update: function(position) {
-    this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
-      return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) });
-  },
-  finish: function() {
-    this.element.setStyle(Object.extend(this.oldStyle, {
-      backgroundColor: this.options.restorecolor
-    }));
-  }
-});
-
-Effect.ScrollTo = Class.create();
-Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), {
-  initialize: function(element) {
-    this.element = $PR(element);
-    this.start(arguments[1] || {});
-  },
-  setup: function() {
-    Position.prepare();
-    var offsets = Position.cumulativeOffset(this.element);
-    if(this.options.offset) offsets[1] += this.options.offset;
-    var max = window.innerHeight ? 
-      window.height - window.innerHeight :
-      document.body.scrollHeight - 
-        (document.documentElement.clientHeight ? 
-          document.documentElement.clientHeight : document.body.clientHeight);
-    this.scrollStart = Position.deltaY;
-    this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart;
-  },
-  update: function(position) {
-    Position.prepare();
-    window.scrollTo(Position.deltaX, 
-      this.scrollStart + (position*this.delta));
-  }
-});
-
-/* ------------- combination effects ------------- */
-
-Effect.Fade = function(element) {
-  element = $PR(element);
-  var oldOpacity = element.getInlineOpacity();
-  var options = Object.extend({
-  from: element.getOpacity() || 1.0,
-  to:   0.0,
-  afterFinishInternal: function(effect) { 
-    if(effect.options.to!=0) return;
-    effect.element.hide();
-    effect.element.setStyle({opacity: oldOpacity}); 
-  }}, arguments[1] || {});
-  return new Effect.Opacity(element,options);
-}
-
-Effect.Appear = function(element) {
-  element = $PR(element);
-  var options = Object.extend({
-  from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),
-  to:   1.0,
-  // force Safari to render floated elements properly
-  afterFinishInternal: function(effect) {
-    effect.element.forceRerendering();
-  },
-  beforeSetup: function(effect) {
-    effect.element.setOpacity(effect.options.from);
-    effect.element.show(); 
-  }}, arguments[1] || {});
-  return new Effect.Opacity(element,options);
-}
-
-Effect.Puff = function(element) {
-  element = $PR(element);
-  var oldStyle = { opacity: element.getInlineOpacity(), position: element.getStyle('position') };
-  return new Effect.Parallel(
-   [ new Effect.Scale(element, 200, 
-      { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), 
-     new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], 
-     Object.extend({ duration: 1.0, 
-      beforeSetupInternal: function(effect) {
-        effect.effects[0].element.setStyle({position: 'absolute'}); },
-      afterFinishInternal: function(effect) {
-         effect.effects[0].element.hide();
-         effect.effects[0].element.setStyle(oldStyle); }
-     }, arguments[1] || {})
-   );
-}
-
-Effect.BlindUp = function(element) {
-  element = $PR(element);
-  element.makeClipping();
-  return new Effect.Scale(element, 0, 
-    Object.extend({ scaleContent: false, 
-      scaleX: false, 
-      restoreAfterFinish: true,
-      afterFinishInternal: function(effect) {
-        effect.element.hide();
-        effect.element.undoClipping();
-      } 
-    }, arguments[1] || {})
-  );
-}
-
-Effect.BlindDown = function(element) {
-  element = $PR(element);
-  var elementDimensions = element.getDimensions();
-  return new Effect.Scale(element, 100, 
-    Object.extend({ scaleContent: false, 
-      scaleX: false,
-      scaleFrom: 0,
-      scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
-      restoreAfterFinish: true,
-      afterSetup: function(effect) {
-        effect.element.makeClipping();
-        effect.element.setStyle({height: '0px'});
-        effect.element.show(); 
-      },  
-      afterFinishInternal: function(effect) {
-        effect.element.undoClipping();
-      }
-    }, arguments[1] || {})
-  );
-}
-
-Effect.SwitchOff = function(element) {
-  element = $PR(element);
-  var oldOpacity = element.getInlineOpacity();
-  return new Effect.Appear(element, { 
-    duration: 0.4,
-    from: 0,
-    transition: Effect.Transitions.flicker,
-    afterFinishInternal: function(effect) {
-      new Effect.Scale(effect.element, 1, { 
-        duration: 0.3, scaleFromCenter: true,
-        scaleX: false, scaleContent: false, restoreAfterFinish: true,
-        beforeSetup: function(effect) { 
-          effect.element.makePositioned();
-          effect.element.makeClipping();
-        },
-        afterFinishInternal: function(effect) {
-          effect.element.hide();
-          effect.element.undoClipping();
-          effect.element.undoPositioned();
-          effect.element.setStyle({opacity: oldOpacity});
-        }
-      })
-    }
-  });
-}
-
-Effect.DropOut = function(element) {
-  element = $PR(element);
-  var oldStyle = {
-    top: element.getStyle('top'),
-    left: element.getStyle('left'),
-    opacity: element.getInlineOpacity() };
-  return new Effect.Parallel(
-    [ new Effect.Move(element, {x: 0, y: 100, sync: true }), 
-      new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
-    Object.extend(
-      { duration: 0.5,
-        beforeSetup: function(effect) {
-          effect.effects[0].element.makePositioned(); 
-        },
-        afterFinishInternal: function(effect) {
-          effect.effects[0].element.hide();
-          effect.effects[0].element.undoPositioned();
-          effect.effects[0].element.setStyle(oldStyle);
-        } 
-      }, arguments[1] || {}));
-}
-
-Effect.Shake = function(element) {
-  element = $PR(element);
-  var oldStyle = {
-    top: element.getStyle('top'),
-    left: element.getStyle('left') };
-    return new Effect.Move(element, 
-      { x:  20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
-    new Effect.Move(effect.element,
-      { x: -40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
-    new Effect.Move(effect.element,
-      { x:  40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
-    new Effect.Move(effect.element,
-      { x: -40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
-    new Effect.Move(effect.element,
-      { x:  40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
-    new Effect.Move(effect.element,
-      { x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
-        effect.element.undoPositioned();
-        effect.element.setStyle(oldStyle);
-  }}) }}) }}) }}) }}) }});
-}
-
-Effect.SlideDown = function(element) {
-  element = $PR(element);
-  element.cleanWhitespace();
-  // SlideDown need to have the content of the element wrapped in a container element with fixed height!
-  var oldInnerBottom = $PR(element.firstChild).getStyle('bottom');
-  var elementDimensions = element.getDimensions();
-  return new Effect.Scale(element, 100, Object.extend({ 
-    scaleContent: false, 
-    scaleX: false, 
-    scaleFrom: window.opera ? 0 : 1,
-    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
-    restoreAfterFinish: true,
-    afterSetup: function(effect) {
-      effect.element.makePositioned();
-      effect.element.firstChild.makePositioned();
-      if(window.opera) effect.element.setStyle({top: ''});
-      effect.element.makeClipping();
-      effect.element.setStyle({height: '0px'});
-      effect.element.show(); },
-    afterUpdateInternal: function(effect) {
-      effect.element.firstChild.setStyle({bottom:
-        (effect.dims[0] - effect.element.clientHeight) + 'px' }); 
-    },
-    afterFinishInternal: function(effect) {
-      effect.element.undoClipping(); 
-      // IE will crash if child is undoPositioned first
-      if(/MSIE/.test(navigator.userAgent)){
-        effect.element.undoPositioned();
-        effect.element.firstChild.undoPositioned();
-      }else{
-        effect.element.firstChild.undoPositioned();
-        effect.element.undoPositioned();
-      }
-      effect.element.firstChild.setStyle({bottom: oldInnerBottom}); }
-    }, arguments[1] || {})
-  );
-}
-  
-Effect.SlideUp = function(element) {
-  element = $PR(element);
-  element.cleanWhitespace();
-  var oldInnerBottom = $PR(element.firstChild).getStyle('bottom');
-  return new Effect.Scale(element, window.opera ? 0 : 1,
-   Object.extend({ scaleContent: false, 
-    scaleX: false, 
-    scaleMode: 'box',
-    scaleFrom: 100,
-    restoreAfterFinish: true,
-    beforeStartInternal: function(effect) {
-      effect.element.makePositioned();
-      effect.element.firstChild.makePositioned();
-      if(window.opera) effect.element.setStyle({top: ''});
-      effect.element.makeClipping();
-      effect.element.show(); },  
-    afterUpdateInternal: function(effect) {
-      effect.element.firstChild.setStyle({bottom:
-        (effect.dims[0] - effect.element.clientHeight) + 'px' }); },
-    afterFinishInternal: function(effect) {
-      effect.element.hide();
-      effect.element.undoClipping();
-      effect.element.firstChild.undoPositioned();
-      effect.element.undoPositioned();
-      effect.element.setStyle({bottom: oldInnerBottom}); }
-   }, arguments[1] || {})
-  );
-}
-
-// Bug in opera makes the TD containing this element expand for a instance after finish 
-Effect.Squish = function(element) {
-  return new Effect.Scale(element, window.opera ? 1 : 0, 
-    { restoreAfterFinish: true,
-      beforeSetup: function(effect) {
-        effect.element.makeClipping(effect.element); },  
-      afterFinishInternal: function(effect) {
-        effect.element.hide(effect.element); 
-        effect.element.undoClipping(effect.element); }
-  });
-}
-
-Effect.Grow = function(element) {
-  element = $PR(element);
-  var options = Object.extend({
-    direction: 'center',
-    moveTransition: Effect.Transitions.sinoidal,
-    scaleTransition: Effect.Transitions.sinoidal,
-    opacityTransition: Effect.Transitions.full
-  }, arguments[1] || {});
-  var oldStyle = {
-    top: element.style.top,
-    left: element.style.left,
-    height: element.style.height,
-    width: element.style.width,
-    opacity: element.getInlineOpacity() };
-
-  var dims = element.getDimensions();    
-  var initialMoveX, initialMoveY;
-  var moveX, moveY;
-  
-  switch (options.direction) {
-    case 'top-left':
-      initialMoveX = initialMoveY = moveX = moveY = 0; 
-      break;
-    case 'top-right':
-      initialMoveX = dims.width;
-      initialMoveY = moveY = 0;
-      moveX = -dims.width;
-      break;
-    case 'bottom-left':
-      initialMoveX = moveX = 0;
-      initialMoveY = dims.height;
-      moveY = -dims.height;
-      break;
-    case 'bottom-right':
-      initialMoveX = dims.width;
-      initialMoveY = dims.height;
-      moveX = -dims.width;
-      moveY = -dims.height;
-      break;
-    case 'center':
-      initialMoveX = dims.width / 2;
-      initialMoveY = dims.height / 2;
-      moveX = -dims.width / 2;
-      moveY = -dims.height / 2;
-      break;
-  }
-  
-  return new Effect.Move(element, {
-    x: initialMoveX,
-    y: initialMoveY,
-    duration: 0.01, 
-    beforeSetup: function(effect) {
-      effect.element.hide();
-      effect.element.makeClipping();
-      effect.element.makePositioned();
-    },
-    afterFinishInternal: function(effect) {
-      new Effect.Parallel(
-        [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
-          new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
-          new Effect.Scale(effect.element, 100, {
-            scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, 
-            sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
-        ], Object.extend({
-             beforeSetup: function(effect) {
-               effect.effects[0].element.setStyle({height: '0px'});
-               effect.effects[0].element.show(); 
-             },
-             afterFinishInternal: function(effect) {
-               effect.effects[0].element.undoClipping();
-               effect.effects[0].element.undoPositioned();
-               effect.effects[0].element.setStyle(oldStyle); 
-             }
-           }, options)
-      )
-    }
-  });
-}
-
-Effect.Shrink = function(element) {
-  element = $PR(element);
-  var options = Object.extend({
-    direction: 'center',
-    moveTransition: Effect.Transitions.sinoidal,
-    scaleTransition: Effect.Transitions.sinoidal,
-    opacityTransition: Effect.Transitions.none
-  }, arguments[1] || {});
-  var oldStyle = {
-    top: element.style.top,
-    left: element.style.left,
-    height: element.style.height,
-    width: element.style.width,
-    opacity: element.getInlineOpacity() };
-
-  var dims = element.getDimensions();
-  var moveX, moveY;
-  
-  switch (options.direction) {
-    case 'top-left':
-      moveX = moveY = 0;
-      break;
-    case 'top-right':
-      moveX = dims.width;
-      moveY = 0;
-      break;
-    case 'bottom-left':
-      moveX = 0;
-      moveY = dims.height;
-      break;
-    case 'bottom-right':
-      moveX = dims.width;
-      moveY = dims.height;
-      break;
-    case 'center':  
-      moveX = dims.width / 2;
-      moveY = dims.height / 2;
-      break;
-  }
-  
-  return new Effect.Parallel(
-    [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
-      new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
-      new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
-    ], Object.extend({            
-         beforeStartInternal: function(effect) {
-           effect.effects[0].element.makePositioned();
-           effect.effects[0].element.makeClipping(); },
-         afterFinishInternal: function(effect) {
-           effect.effects[0].element.hide();
-           effect.effects[0].element.undoClipping();
-           effect.effects[0].element.undoPositioned();
-           effect.effects[0].element.setStyle(oldStyle); }
-       }, options)
-  );
-}
-
-Effect.Pulsate = function(element) {
-  element = $PR(element);
-  var options    = arguments[1] || {};
-  var oldOpacity = element.getInlineOpacity();
-  var transition = options.transition || Effect.Transitions.sinoidal;
-  var reverser   = function(pos){ return transition(1-Effect.Transitions.pulse(pos)) };
-  reverser.bind(transition);
-  return new Effect.Opacity(element, 
-    Object.extend(Object.extend({  duration: 3.0, from: 0,
-      afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
-    }, options), {transition: reverser}));
-}
-
-Effect.Fold = function(element) {
-  element = $PR(element);
-  var oldStyle = {
-    top: element.style.top,
-    left: element.style.left,
-    width: element.style.width,
-    height: element.style.height };
-  Element.makeClipping(element);
-  return new Effect.Scale(element, 5, Object.extend({   
-    scaleContent: false,
-    scaleX: false,
-    afterFinishInternal: function(effect) {
-    new Effect.Scale(element, 1, { 
-      scaleContent: false, 
-      scaleY: false,
-      afterFinishInternal: function(effect) {
-        effect.element.hide();
-        effect.element.undoClipping(); 
-        effect.element.setStyle(oldStyle);
-      } });
-  }}, arguments[1] || {}));
-};
-
-['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom',
- 'collectTextNodes','collectTextNodesIgnoreClass','childrenWithClassName'].each( 
-  function(f) { Element.Methods[f] = Element[f]; }
-);
-
-Element.Methods.visualEffect = function(element, effect, options) {
-  s = effect.gsub(/_/, '-').camelize();
-  effect_class = s.charAt(0).toUpperCase() + s.substring(1);
-  new Effect[effect_class](element, options);
-  return $PR(element);
-};
-
-Element.addMethods();
diff --git a/library/cropper/lib/prototype.js b/library/cropper/lib/prototype.js
deleted file mode 100644 (file)
index 6682065..0000000
+++ /dev/null
@@ -1,2006 +0,0 @@
-/*  Prototype JavaScript framework, version 1.5.0_rc0
- *  (c) 2005 Sam Stephenson <sam@conio.net>
- *
- *  Prototype is freely distributable under the terms of an MIT-style license.
- *  For details, see the Prototype web site: http://prototype.conio.net/
- *
-/*--------------------------------------------------------------------------*/
-
-var Prototype = {
-  Version: '1.5.0_rc0',
-  ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
-
-  emptyFunction: function() {},
-  K: function(x) {return x}
-}
-
-var Class = {
-  create: function() {
-    return function() {
-      this.initialize.apply(this, arguments);
-    }
-  }
-}
-
-var Abstract = new Object();
-
-Object.extend = function(destination, source) {
-  for (var property in source) {
-    destination[property] = source[property];
-  }
-  return destination;
-}
-
-Object.inspect = function(object) {
-  try {
-    if (object == undefined) return 'undefined';
-    if (object == null) return 'null';
-    return object.inspect ? object.inspect() : object.toString();
-  } catch (e) {
-    if (e instanceof RangeError) return '...';
-    throw e;
-  }
-}
-
-Function.prototype.bind = function() {
-  var __method = this, args = $A(arguments), object = args.shift();
-  return function() {
-    return __method.apply(object, args.concat($A(arguments)));
-  }
-}
-
-Function.prototype.bindAsEventListener = function(object) {
-  var __method = this;
-  return function(event) {
-    return __method.call(object, event || window.event);
-  }
-}
-
-Object.extend(Number.prototype, {
-  toColorPart: function() {
-    var digits = this.toString(16);
-    if (this < 16) return '0' + digits;
-    return digits;
-  },
-
-  succ: function() {
-    return this + 1;
-  },
-
-  times: function(iterator) {
-    $R(0, this, true).each(iterator);
-    return this;
-  }
-});
-
-var Try = {
-  these: function() {
-    var returnValue;
-
-    for (var i = 0; i < arguments.length; i++) {
-      var lambda = arguments[i];
-      try {
-        returnValue = lambda();
-        break;
-      } catch (e) {}
-    }
-
-    return returnValue;
-  }
-}
-
-/*--------------------------------------------------------------------------*/
-
-var PeriodicalExecuter = Class.create();
-PeriodicalExecuter.prototype = {
-  initialize: function(callback, frequency) {
-    this.callback = callback;
-    this.frequency = frequency;
-    this.currentlyExecuting = false;
-
-    this.registerCallback();
-  },
-
-  registerCallback: function() {
-    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
-  },
-
-  onTimerEvent: function() {
-    if (!this.currentlyExecuting) {
-      try {
-        this.currentlyExecuting = true;
-        this.callback();
-      } finally {
-        this.currentlyExecuting = false;
-      }
-    }
-  }
-}
-Object.extend(String.prototype, {
-  gsub: function(pattern, replacement) {
-    var result = '', source = this, match;
-    replacement = arguments.callee.prepareReplacement(replacement);
-
-    while (source.length > 0) {
-      if (match = source.match(pattern)) {
-        result += source.slice(0, match.index);
-        result += (replacement(match) || '').toString();
-        source  = source.slice(match.index + match[0].length);
-      } else {
-        result += source, source = '';
-      }
-    }
-    return result;
-  },
-
-  sub: function(pattern, replacement, count) {
-    replacement = this.gsub.prepareReplacement(replacement);
-    count = count === undefined ? 1 : count;
-
-    return this.gsub(pattern, function(match) {
-      if (--count < 0) return match[0];
-      return replacement(match);
-    });
-  },
-
-  scan: function(pattern, iterator) {
-    this.gsub(pattern, iterator);
-    return this;
-  },
-
-  truncate: function(length, truncation) {
-    length = length || 30;
-    truncation = truncation === undefined ? '...' : truncation;
-    return this.length > length ?
-      this.slice(0, length - truncation.length) + truncation : this;
-  },
-
-  strip: function() {
-    return this.replace(/^\s+/, '').replace(/\s+$/, '');
-  },
-
-  stripTags: function() {
-    return this.replace(/<\/?[^>]+>/gi, '');
-  },
-
-  stripScripts: function() {
-    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
-  },
-
-  extractScripts: function() {
-    var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
-    var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
-    return (this.match(matchAll) || []).map(function(scriptTag) {
-      return (scriptTag.match(matchOne) || ['', ''])[1];
-    });
-  },
-
-  evalScripts: function() {
-    return this.extractScripts().map(function(script) { return eval(script) });
-  },
-
-  escapeHTML: function() {
-    var div = document.createElement('div');
-    var text = document.createTextNode(this);
-    div.appendChild(text);
-    return div.innerHTML;
-  },
-
-  unescapeHTML: function() {
-    var div = document.createElement('div');
-    div.innerHTML = this.stripTags();
-    return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
-  },
-
-  toQueryParams: function() {
-    var pairs = this.match(/^\??(.*)$/)[1].split('&');
-    return pairs.inject({}, function(params, pairString) {
-      var pair = pairString.split('=');
-      params[pair[0]] = pair[1];
-      return params;
-    });
-  },
-
-  toArray: function() {
-    return this.split('');
-  },
-
-  camelize: function() {
-    var oStringList = this.split('-');
-    if (oStringList.length == 1) return oStringList[0];
-
-    var camelizedString = this.indexOf('-') == 0
-      ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
-      : oStringList[0];
-
-    for (var i = 1, len = oStringList.length; i < len; i++) {
-      var s = oStringList[i];
-      camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
-    }
-
-    return camelizedString;
-  },
-
-  inspect: function() {
-    return "'" + this.replace(/\\/g, '\\\\').replace(/'/g, '\\\'') + "'";
-  }
-});
-
-String.prototype.gsub.prepareReplacement = function(replacement) {
-  if (typeof replacement == 'function') return replacement;
-  var template = new Template(replacement);
-  return function(match) { return template.evaluate(match) };
-}
-
-String.prototype.parseQuery = String.prototype.toQueryParams;
-
-var Template = Class.create();
-Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
-Template.prototype = {
-  initialize: function(template, pattern) {
-    this.template = template.toString();
-    this.pattern  = pattern || Template.Pattern;
-  },
-
-  evaluate: function(object) {
-    return this.template.gsub(this.pattern, function(match) {
-      var before = match[1];
-      if (before == '\\') return match[2];
-      return before + (object[match[3]] || '').toString();
-    });
-  }
-}
-
-var $break    = new Object();
-var $continue = new Object();
-
-var Enumerable = {
-  each: function(iterator) {
-    var index = 0;
-    try {
-      this._each(function(value) {
-        try {
-          iterator(value, index++);
-        } catch (e) {
-          if (e != $continue) throw e;
-        }
-      });
-    } catch (e) {
-      if (e != $break) throw e;
-    }
-  },
-
-  all: function(iterator) {
-    var result = true;
-    this.each(function(value, index) {
-      result = result && !!(iterator || Prototype.K)(value, index);
-      if (!result) throw $break;
-    });
-    return result;
-  },
-
-  any: function(iterator) {
-    var result = true;
-    this.each(function(value, index) {
-      if (result = !!(iterator || Prototype.K)(value, index))
-        throw $break;
-    });
-    return result;
-  },
-
-  collect: function(iterator) {
-    var results = [];
-    this.each(function(value, index) {
-      results.push(iterator(value, index));
-    });
-    return results;
-  },
-
-  detect: function (iterator) {
-    var result;
-    this.each(function(value, index) {
-      if (iterator(value, index)) {
-        result = value;
-        throw $break;
-      }
-    });
-    return result;
-  },
-
-  findAll: function(iterator) {
-    var results = [];
-    this.each(function(value, index) {
-      if (iterator(value, index))
-        results.push(value);
-    });
-    return results;
-  },
-
-  grep: function(pattern, iterator) {
-    var results = [];
-    this.each(function(value, index) {
-      var stringValue = value.toString();
-      if (stringValue.match(pattern))
-        results.push((iterator || Prototype.K)(value, index));
-    })
-    return results;
-  },
-
-  include: function(object) {
-    var found = false;
-    this.each(function(value) {
-      if (value == object) {
-        found = true;
-        throw $break;
-      }
-    });
-    return found;
-  },
-
-  inject: function(memo, iterator) {
-    this.each(function(value, index) {
-      memo = iterator(memo, value, index);
-    });
-    return memo;
-  },
-
-  invoke: function(method) {
-    var args = $A(arguments).slice(1);
-    return this.collect(function(value) {
-      return value[method].apply(value, args);
-    });
-  },
-
-  max: function(iterator) {
-    var result;
-    this.each(function(value, index) {
-      value = (iterator || Prototype.K)(value, index);
-      if (result == undefined || value >= result)
-        result = value;
-    });
-    return result;
-  },
-
-  min: function(iterator) {
-    var result;
-    this.each(function(value, index) {
-      value = (iterator || Prototype.K)(value, index);
-      if (result == undefined || value < result)
-        result = value;
-    });
-    return result;
-  },
-
-  partition: function(iterator) {
-    var trues = [], falses = [];
-    this.each(function(value, index) {
-      ((iterator || Prototype.K)(value, index) ?
-        trues : falses).push(value);
-    });
-    return [trues, falses];
-  },
-
-  pluck: function(property) {
-    var results = [];
-    this.each(function(value, index) {
-      results.push(value[property]);
-    });
-    return results;
-  },
-
-  reject: function(iterator) {
-    var results = [];
-    this.each(function(value, index) {
-      if (!iterator(value, index))
-        results.push(value);
-    });
-    return results;
-  },
-
-  sortBy: function(iterator) {
-    return this.collect(function(value, index) {
-      return {value: value, criteria: iterator(value, index)};
-    }).sort(function(left, right) {
-      var a = left.criteria, b = right.criteria;
-      return a < b ? -1 : a > b ? 1 : 0;
-    }).pluck('value');
-  },
-
-  toArray: function() {
-    return this.collect(Prototype.K);
-  },
-
-  zip: function() {
-    var iterator = Prototype.K, args = $A(arguments);
-    if (typeof args.last() == 'function')
-      iterator = args.pop();
-
-    var collections = [this].concat(args).map($A);
-    return this.map(function(value, index) {
-      return iterator(collections.pluck(index));
-    });
-  },
-
-  inspect: function() {
-    return '#<Enumerable:' + this.toArray().inspect() + '>';
-  }
-}
-
-Object.extend(Enumerable, {
-  map:     Enumerable.collect,
-  find:    Enumerable.detect,
-  select:  Enumerable.findAll,
-  member:  Enumerable.include,
-  entries: Enumerable.toArray
-});
-var $A = Array.from = function(iterable) {
-  if (!iterable) return [];
-  if (iterable.toArray) {
-    return iterable.toArray();
-  } else {
-    var results = [];
-    for (var i = 0; i < iterable.length; i++)
-      results.push(iterable[i]);
-    return results;
-  }
-}
-
-Object.extend(Array.prototype, Enumerable);
-
-if (!Array.prototype._reverse)
-  Array.prototype._reverse = Array.prototype.reverse;
-
-Object.extend(Array.prototype, {
-  _each: function(iterator) {
-    for (var i = 0; i < this.length; i++)
-      iterator(this[i]);
-  },
-
-  clear: function() {
-    this.length = 0;
-    return this;
-  },
-
-  first: function() {
-    return this[0];
-  },
-
-  last: function() {
-    return this[this.length - 1];
-  },
-
-  compact: function() {
-    return this.select(function(value) {
-      return value != undefined || value != null;
-    });
-  },
-
-  flatten: function() {
-    return this.inject([], function(array, value) {
-      return array.concat(value && value.constructor == Array ?
-        value.flatten() : [value]);
-    });
-  },
-
-  without: function() {
-    var values = $A(arguments);
-    return this.select(function(value) {
-      return !values.include(value);
-    });
-  },
-
-  indexOf: function(object) {
-    for (var i = 0; i < this.length; i++)
-      if (this[i] == object) return i;
-    return -1;
-  },
-
-  reverse: function(inline) {
-    return (inline !== false ? this : this.toArray())._reverse();
-  },
-
-  inspect: function() {
-    return '[' + this.map(Object.inspect).join(', ') + ']';
-  }
-});
-var Hash = {
-  _each: function(iterator) {
-    for (var key in this) {
-      var value = this[key];
-      if (typeof value == 'function') continue;
-
-      var pair = [key, value];
-      pair.key = key;
-      pair.value = value;
-      iterator(pair);
-    }
-  },
-
-  keys: function() {
-    return this.pluck('key');
-  },
-
-  values: function() {
-    return this.pluck('value');
-  },
-
-  merge: function(hash) {
-    return $H(hash).inject($H(this), function(mergedHash, pair) {
-      mergedHash[pair.key] = pair.value;
-      return mergedHash;
-    });
-  },
-
-  toQueryString: function() {
-    return this.map(function(pair) {
-      return pair.map(encodeURIComponent).join('=');
-    }).join('&');
-  },
-
-  inspect: function() {
-    return '#<Hash:{' + this.map(function(pair) {
-      return pair.map(Object.inspect).join(': ');
-    }).join(', ') + '}>';
-  }
-}
-
-function $H(object) {
-  var hash = Object.extend({}, object || {});
-  Object.extend(hash, Enumerable);
-  Object.extend(hash, Hash);
-  return hash;
-}
-ObjectRange = Class.create();
-Object.extend(ObjectRange.prototype, Enumerable);
-Object.extend(ObjectRange.prototype, {
-  initialize: function(start, end, exclusive) {
-    this.start = start;
-    this.end = end;
-    this.exclusive = exclusive;
-  },
-
-  _each: function(iterator) {
-    var value = this.start;
-    do {
-      iterator(value);
-      value = value.succ();
-    } while (this.include(value));
-  },
-
-  include: function(value) {
-    if (value < this.start)
-      return false;
-    if (this.exclusive)
-      return value < this.end;
-    return value <= this.end;
-  }
-});
-
-var $R = function(start, end, exclusive) {
-  return new ObjectRange(start, end, exclusive);
-}
-
-var Ajax = {
-  getTransport: function() {
-    return Try.these(
-      function() {return new XMLHttpRequest()},
-      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
-      function() {return new ActiveXObject('Microsoft.XMLHTTP')}
-    ) || false;
-  },
-
-  activeRequestCount: 0
-}
-
-Ajax.Responders = {
-  responders: [],
-
-  _each: function(iterator) {
-    this.responders._each(iterator);
-  },
-
-  register: function(responderToAdd) {
-    if (!this.include(responderToAdd))
-      this.responders.push(responderToAdd);
-  },
-
-  unregister: function(responderToRemove) {
-    this.responders = this.responders.without(responderToRemove);
-  },
-
-  dispatch: function(callback, request, transport, json) {
-    this.each(function(responder) {
-      if (responder[callback] && typeof responder[callback] == 'function') {
-        try {
-          responder[callback].apply(responder, [request, transport, json]);
-        } catch (e) {}
-      }
-    });
-  }
-};
-
-Object.extend(Ajax.Responders, Enumerable);
-
-Ajax.Responders.register({
-  onCreate: function() {
-    Ajax.activeRequestCount++;
-  },
-
-  onComplete: function() {
-    Ajax.activeRequestCount--;
-  }
-});
-
-Ajax.Base = function() {};
-Ajax.Base.prototype = {
-  setOptions: function(options) {
-    this.options = {
-      method:       'post',
-      asynchronous: true,
-      contentType:  'application/x-www-form-urlencoded',
-      parameters:   ''
-    }
-    Object.extend(this.options, options || {});
-  },
-
-  responseIsSuccess: function() {
-    return this.transport.status == undefined
-        || this.transport.status == 0
-        || (this.transport.status >= 200 && this.transport.status < 300);
-  },
-
-  responseIsFailure: function() {
-    return !this.responseIsSuccess();
-  }
-}
-
-Ajax.Request = Class.create();
-Ajax.Request.Events =
-  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
-
-Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
-  initialize: function(url, options) {
-    this.transport = Ajax.getTransport();
-    this.setOptions(options);
-    this.request(url);
-  },
-
-  request: function(url) {
-    var parameters = this.options.parameters || '';
-    if (parameters.length > 0) parameters += '&_=';
-
-    try {
-      this.url = url;
-      if (this.options.method == 'get' && parameters.length > 0)
-        this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;
-
-      Ajax.Responders.dispatch('onCreate', this, this.transport);
-
-      this.transport.open(this.options.method, this.url,
-        this.options.asynchronous);
-
-      if (this.options.asynchronous) {
-        this.transport.onreadystatechange = this.onStateChange.bind(this);
-        setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
-      }
-
-      this.setRequestHeaders();
-
-      var body = this.options.postBody ? this.options.postBody : parameters;
-      this.transport.send(this.options.method == 'post' ? body : null);
-
-    } catch (e) {
-      this.dispatchException(e);
-    }
-  },
-
-  setRequestHeaders: function() {
-    var requestHeaders =
-      ['X-Requested-With', 'XMLHttpRequest',
-       'X-Prototype-Version', Prototype.Version,
-       'Accept', 'text/javascript, text/html, application/xml, text/xml, */*'];
-
-    if (this.options.method == 'post') {
-      requestHeaders.push('Content-type', this.options.contentType);
-
-      /* Force "Connection: close" for Mozilla browsers to work around
-       * a bug where XMLHttpReqeuest sends an incorrect Content-length
-       * header. See Mozilla Bugzilla #246651.
-       */
-      if (this.transport.overrideMimeType)
-        requestHeaders.push('Connection', 'close');
-    }
-
-    if (this.options.requestHeaders)
-      requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);
-
-    for (var i = 0; i < requestHeaders.length; i += 2)
-      this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);
-  },
-
-  onStateChange: function() {
-    var readyState = this.transport.readyState;
-    if (readyState != 1)
-      this.respondToReadyState(this.transport.readyState);
-  },
-
-  header: function(name) {
-    try {
-      return this.transport.getResponseHeader(name);
-    } catch (e) {}
-  },
-
-  evalJSON: function() {
-    try {
-      return eval('(' + this.header('X-JSON') + ')');
-    } catch (e) {}
-  },
-
-  evalResponse: function() {
-    try {
-      return eval(this.transport.responseText);
-    } catch (e) {
-      this.dispatchException(e);
-    }
-  },
-
-  respondToReadyState: function(readyState) {
-    var event = Ajax.Request.Events[readyState];
-    var transport = this.transport, json = this.evalJSON();
-
-    if (event == 'Complete') {
-      try {
-        (this.options['on' + this.transport.status]
-         || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
-         || Prototype.emptyFunction)(transport, json);
-      } catch (e) {
-        this.dispatchException(e);
-      }
-
-      if ((this.header('Content-type') || '').match(/^text\/javascript/i))
-        this.evalResponse();
-    }
-
-    try {
-      (this.options['on' + event] || Prototype.emptyFunction)(transport, json);
-      Ajax.Responders.dispatch('on' + event, this, transport, json);
-    } catch (e) {
-      this.dispatchException(e);
-    }
-
-    /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
-    if (event == 'Complete')
-      this.transport.onreadystatechange = Prototype.emptyFunction;
-  },
-
-  dispatchException: function(exception) {
-    (this.options.onException || Prototype.emptyFunction)(this, exception);
-    Ajax.Responders.dispatch('onException', this, exception);
-  }
-});
-
-Ajax.Updater = Class.create();
-
-Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
-  initialize: function(container, url, options) {
-    this.containers = {
-      success: container.success ? $PR(container.success) : $PR(container),
-      failure: container.failure ? $PR(container.failure) :
-        (container.success ? null : $PR(container))
-    }
-
-    this.transport = Ajax.getTransport();
-    this.setOptions(options);
-
-    var onComplete = this.options.onComplete || Prototype.emptyFunction;
-    this.options.onComplete = (function(transport, object) {
-      this.updateContent();
-      onComplete(transport, object);
-    }).bind(this);
-
-    this.request(url);
-  },
-
-  updateContent: function() {
-    var receiver = this.responseIsSuccess() ?
-      this.containers.success : this.containers.failure;
-    var response = this.transport.responseText;
-
-    if (!this.options.evalScripts)
-      response = response.stripScripts();
-
-    if (receiver) {
-      if (this.options.insertion) {
-        new this.options.insertion(receiver, response);
-      } else {
-        Element.update(receiver, response);
-      }
-    }
-
-    if (this.responseIsSuccess()) {
-      if (this.onComplete)
-        setTimeout(this.onComplete.bind(this), 10);
-    }
-  }
-});
-
-Ajax.PeriodicalUpdater = Class.create();
-Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
-  initialize: function(container, url, options) {
-    this.setOptions(options);
-    this.onComplete = this.options.onComplete;
-
-    this.frequency = (this.options.frequency || 2);
-    this.decay = (this.options.decay || 1);
-
-    this.updater = {};
-    this.container = container;
-    this.url = url;
-
-    this.start();
-  },
-
-  start: function() {
-    this.options.onComplete = this.updateComplete.bind(this);
-    this.onTimerEvent();
-  },
-
-  stop: function() {
-    this.updater.onComplete = undefined;
-    clearTimeout(this.timer);
-    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
-  },
-
-  updateComplete: function(request) {
-    if (this.options.decay) {
-      this.decay = (request.responseText == this.lastText ?
-        this.decay * this.options.decay : 1);
-
-      this.lastText = request.responseText;
-    }
-    this.timer = setTimeout(this.onTimerEvent.bind(this),
-      this.decay * this.frequency * 1000);
-  },
-
-  onTimerEvent: function() {
-    this.updater = new Ajax.Updater(this.container, this.url, this.options);
-  }
-});
-function $PR() {
-  var results = [], element;
-  for (var i = 0; i < arguments.length; i++) {
-    element = arguments[i];
-    if (typeof element == 'string')
-      element = document.getElementById(element);
-    results.push(Element.extend(element));
-  }
-  return results.length < 2 ? results[0] : results;
-}
-
-document.getElementsByClassName = function(className, parentElement) {
-  var children = ($PR(parentElement) || document.body).getElementsByTagName('*');
-  return $A(children).inject([], function(elements, child) {
-    if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
-      elements.push(Element.extend(child));
-    return elements;
-  });
-}
-
-/*--------------------------------------------------------------------------*/
-
-if (!window.Element)
-  var Element = new Object();
-
-Element.extend = function(element) {
-  if (!element) return;
-  if (_nativeExtensions) return element;
-
-  if (!element._extended && element.tagName && element != window) {
-    var methods = Element.Methods, cache = Element.extend.cache;
-    for (property in methods) {
-      var value = methods[property];
-      if (typeof value == 'function')
-        element[property] = cache.findOrStore(value);
-    }
-  }
-
-  element._extended = true;
-  return element;
-}
-
-Element.extend.cache = {
-  findOrStore: function(value) {
-    return this[value] = this[value] || function() {
-      return value.apply(null, [this].concat($A(arguments)));
-    }
-  }
-}
-
-Element.Methods = {
-  visible: function(element) {
-    return $PR(element).style.display != 'none';
-  },
-
-  toggle: function() {
-    for (var i = 0; i < arguments.length; i++) {
-      var element = $PR(arguments[i]);
-      Element[Element.visible(element) ? 'hide' : 'show'](element);
-    }
-  },
-
-  hide: function() {
-    for (var i = 0; i < arguments.length; i++) {
-      var element = $PR(arguments[i]);
-      element.style.display = 'none';
-    }
-  },
-
-  show: function() {
-    for (var i = 0; i < arguments.length; i++) {
-      var element = $PR(arguments[i]);
-      element.style.display = '';
-    }
-  },
-
-  remove: function(element) {
-    element = $PR(element);
-    element.parentNode.removeChild(element);
-  },
-
-  update: function(element, html) {
-    $PR(element).innerHTML = html.stripScripts();
-    setTimeout(function() {html.evalScripts()}, 10);
-  },
-
-  replace: function(element, html) {
-    element = $PR(element);
-    if (element.outerHTML) {
-      element.outerHTML = html.stripScripts();
-    } else {
-      var range = element.ownerDocument.createRange();
-      range.selectNodeContents(element);
-      element.parentNode.replaceChild(
-        range.createContextualFragment(html.stripScripts()), element);
-    }
-    setTimeout(function() {html.evalScripts()}, 10);
-  },
-
-  getHeight: function(element) {
-    element = $PR(element);
-    return element.offsetHeight;
-  },
-
-  classNames: function(element) {
-    return new Element.ClassNames(element);
-  },
-
-  hasClassName: function(element, className) {
-    if (!(element = $PR(element))) return;
-    return Element.classNames(element).include(className);
-  },
-
-  addClassName: function(element, className) {
-    if (!(element = $PR(element))) return;
-    return Element.classNames(element).add(className);
-  },
-
-  removeClassName: function(element, className) {
-    if (!(element = $PR(element))) return;
-    return Element.classNames(element).remove(className);
-  },
-
-  // removes whitespace-only text node children
-  cleanWhitespace: function(element) {
-    element = $PR(element);
-    for (var i = 0; i < element.childNodes.length; i++) {
-      var node = element.childNodes[i];
-      if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
-        Element.remove(node);
-    }
-  },
-
-  empty: function(element) {
-    return $PR(element).innerHTML.match(/^\s*$/);
-  },
-
-  childOf: function(element, ancestor) {
-    element = $PR(element), ancestor = $PR(ancestor);
-    while (element = element.parentNode)
-      if (element == ancestor) return true;
-    return false;
-  },
-
-  scrollTo: function(element) {
-    element = $PR(element);
-    var x = element.x ? element.x : element.offsetLeft,
-        y = element.y ? element.y : element.offsetTop;
-    window.scrollTo(x, y);
-  },
-
-  getStyle: function(element, style) {
-    element = $PR(element);
-    var value = element.style[style.camelize()];
-    if (!value) {
-      if (document.defaultView && document.defaultView.getComputedStyle) {
-        var css = document.defaultView.getComputedStyle(element, null);
-        value = css ? css.getPropertyValue(style) : null;
-      } else if (element.currentStyle) {
-        value = element.currentStyle[style.camelize()];
-      }
-    }
-
-    if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
-      if (Element.getStyle(element, 'position') == 'static') value = 'auto';
-
-    return value == 'auto' ? null : value;
-  },
-
-  setStyle: function(element, style) {
-    element = $PR(element);
-    for (var name in style)
-      element.style[name.camelize()] = style[name];
-  },
-
-  getDimensions: function(element) {
-    element = $PR(element);
-    if (Element.getStyle(element, 'display') != 'none')
-      return {width: element.offsetWidth, height: element.offsetHeight};
-
-    // All *Width and *Height properties give 0 on elements with display none,
-    // so enable the element temporarily
-    var els = element.style;
-    var originalVisibility = els.visibility;
-    var originalPosition = els.position;
-    els.visibility = 'hidden';
-    els.position = 'absolute';
-    els.display = '';
-    var originalWidth = element.clientWidth;
-    var originalHeight = element.clientHeight;
-    els.display = 'none';
-    els.position = originalPosition;
-    els.visibility = originalVisibility;
-    return {width: originalWidth, height: originalHeight};
-  },
-
-  makePositioned: function(element) {
-    element = $PR(element);
-    var pos = Element.getStyle(element, 'position');
-    if (pos == 'static' || !pos) {
-      element._madePositioned = true;
-      element.style.position = 'relative';
-      // Opera returns the offset relative to the positioning context, when an
-      // element is position relative but top and left have not been defined
-      if (window.opera) {
-        element.style.top = 0;
-        element.style.left = 0;
-      }
-    }
-  },
-
-  undoPositioned: function(element) {
-    element = $PR(element);
-    if (element._madePositioned) {
-      element._madePositioned = undefined;
-      element.style.position =
-        element.style.top =
-        element.style.left =
-        element.style.bottom =
-        element.style.right = '';
-    }
-  },
-
-  makeClipping: function(element) {
-    element = $PR(element);
-    if (element._overflow) return;
-    element._overflow = element.style.overflow;
-    if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
-      element.style.overflow = 'hidden';
-  },
-
-  undoClipping: function(element) {
-    element = $PR(element);
-    if (element._overflow) return;
-    element.style.overflow = element._overflow;
-    element._overflow = undefined;
-  }
-}
-
-Object.extend(Element, Element.Methods);
-
-var _nativeExtensions = false;
-
-if(!HTMLElement && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
-  var HTMLElement = {}
-  HTMLElement.prototype = document.createElement('div').__proto__;
-}
-
-Element.addMethods = function(methods) {
-  Object.extend(Element.Methods, methods || {});
-
-  if(typeof HTMLElement != 'undefined') {
-    var methods = Element.Methods, cache = Element.extend.cache;
-    for (property in methods) {
-      var value = methods[property];
-      if (typeof value == 'function')
-        HTMLElement.prototype[property] = cache.findOrStore(value);
-    }
-    _nativeExtensions = true;
-  }
-}
-
-Element.addMethods();
-
-var Toggle = new Object();
-Toggle.display = Element.toggle;
-
-/*--------------------------------------------------------------------------*/
-
-Abstract.Insertion = function(adjacency) {
-  this.adjacency = adjacency;
-}
-
-Abstract.Insertion.prototype = {
-  initialize: function(element, content) {
-    this.element = $PR(element);
-    this.content = content.stripScripts();
-
-    if (this.adjacency && this.element.insertAdjacentHTML) {
-      try {
-        this.element.insertAdjacentHTML(this.adjacency, this.content);
-      } catch (e) {
-        var tagName = this.element.tagName.toLowerCase();
-        if (tagName == 'tbody' || tagName == 'tr') {
-          this.insertContent(this.contentFromAnonymousTable());
-        } else {
-          throw e;
-        }
-      }
-    } else {
-      this.range = this.element.ownerDocument.createRange();
-      if (this.initializeRange) this.initializeRange();
-      this.insertContent([this.range.createContextualFragment(this.content)]);
-    }
-
-    setTimeout(function() {content.evalScripts()}, 10);
-  },
-
-  contentFromAnonymousTable: function() {
-    var div = document.createElement('div');
-    div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
-    return $A(div.childNodes[0].childNodes[0].childNodes);
-  }
-}
-
-var Insertion = new Object();
-
-Insertion.Before = Class.create();
-Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
-  initializeRange: function() {
-    this.range.setStartBefore(this.element);
-  },
-
-  insertContent: function(fragments) {
-    fragments.each((function(fragment) {
-      this.element.parentNode.insertBefore(fragment, this.element);
-    }).bind(this));
-  }
-});
-
-Insertion.Top = Class.create();
-Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
-  initializeRange: function() {
-    this.range.selectNodeContents(this.element);
-    this.range.collapse(true);
-  },
-
-  insertContent: function(fragments) {
-    fragments.reverse(false).each((function(fragment) {
-      this.element.insertBefore(fragment, this.element.firstChild);
-    }).bind(this));
-  }
-});
-
-Insertion.Bottom = Class.create();
-Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
-  initializeRange: function() {
-    this.range.selectNodeContents(this.element);
-    this.range.collapse(this.element);
-  },
-
-  insertContent: function(fragments) {
-    fragments.each((function(fragment) {
-      this.element.appendChild(fragment);
-    }).bind(this));
-  }
-});
-
-Insertion.After = Class.create();
-Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
-  initializeRange: function() {
-    this.range.setStartAfter(this.element);
-  },
-
-  insertContent: function(fragments) {
-    fragments.each((function(fragment) {
-      this.element.parentNode.insertBefore(fragment,
-        this.element.nextSibling);
-    }).bind(this));
-  }
-});
-
-/*--------------------------------------------------------------------------*/
-
-Element.ClassNames = Class.create();
-Element.ClassNames.prototype = {
-  initialize: function(element) {
-    this.element = $PR(element);
-  },
-
-  _each: function(iterator) {
-    this.element.className.split(/\s+/).select(function(name) {
-      return name.length > 0;
-    })._each(iterator);
-  },
-
-  set: function(className) {
-    this.element.className = className;
-  },
-
-  add: function(classNameToAdd) {
-    if (this.include(classNameToAdd)) return;
-    this.set(this.toArray().concat(classNameToAdd).join(' '));
-  },
-
-  remove: function(classNameToRemove) {
-    if (!this.include(classNameToRemove)) return;
-    this.set(this.select(function(className) {
-      return className != classNameToRemove;
-    }).join(' '));
-  },
-
-  toString: function() {
-    return this.toArray().join(' ');
-  }
-}
-
-Object.extend(Element.ClassNames.prototype, Enumerable);
-var Selector = Class.create();
-Selector.prototype = {
-  initialize: function(expression) {
-    this.params = {classNames: []};
-    this.expression = expression.toString().strip();
-    this.parseExpression();
-    this.compileMatcher();
-  },
-
-  parseExpression: function() {
-    function abort(message) { throw 'Parse error in selector: ' + message; }
-
-    if (this.expression == '')  abort('empty expression');
-
-    var params = this.params, expr = this.expression, match, modifier, clause, rest;
-    while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)) {
-      params.attributes = params.attributes || [];
-      params.attributes.push({name: match[2], operator: match[3], value: match[4] || match[5] || ''});
-      expr = match[1];
-    }
-
-    if (expr == '*') return this.params.wildcard = true;
-
-    while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)) {
-      modifier = match[1], clause = match[2], rest = match[3];
-      switch (modifier) {
-        case '#':       params.id = clause; break;
-        case '.':       params.classNames.push(clause); break;
-        case '':
-        case undefined: params.tagName = clause.toUpperCase(); break;
-        default:        abort(expr.inspect());
-      }
-      expr = rest;
-    }
-
-    if (expr.length > 0) abort(expr.inspect());
-  },
-
-  buildMatchExpression: function() {
-    var params = this.params, conditions = [], clause;
-
-    if (params.wildcard)
-      conditions.push('true');
-    if (clause = params.id)
-      conditions.push('element.id == ' + clause.inspect());
-    if (clause = params.tagName)
-      conditions.push('element.tagName.toUpperCase() == ' + clause.inspect());
-    if ((clause = params.classNames).length > 0)
-      for (var i = 0; i < clause.length; i++)
-        conditions.push('Element.hasClassName(element, ' + clause[i].inspect() + ')');
-    if (clause = params.attributes) {
-      clause.each(function(attribute) {
-        var value = 'element.getAttribute(' + attribute.name.inspect() + ')';
-        var splitValueBy = function(delimiter) {
-          return value + ' && ' + value + '.split(' + delimiter.inspect() + ')';
-        }
-
-        switch (attribute.operator) {
-          case '=':       conditions.push(value + ' == ' + attribute.value.inspect()); break;
-          case '~=':      conditions.push(splitValueBy(' ') + '.include(' + attribute.value.inspect() + ')'); break;
-          case '|=':      conditions.push(
-                            splitValueBy('-') + '.first().toUpperCase() == ' + attribute.value.toUpperCase().inspect()
-                          ); break;
-          case '!=':      conditions.push(value + ' != ' + attribute.value.inspect()); break;
-          case '':
-          case undefined: conditions.push(value + ' != null'); break;
-          default:        throw 'Unknown operator ' + attribute.operator + ' in selector';
-        }
-      });
-    }
-
-    return conditions.join(' && ');
-  },
-
-  compileMatcher: function() {
-    this.match = new Function('element', 'if (!element.tagName) return false; \
-      return ' + this.buildMatchExpression());
-  },
-
-  findElements: function(scope) {
-    var element;
-
-    if (element = $PR(this.params.id))
-      if (this.match(element))
-        if (!scope || Element.childOf(element, scope))
-          return [element];
-
-    scope = (scope || document).getElementsByTagName(this.params.tagName || '*');
-
-    var results = [];
-    for (var i = 0; i < scope.length; i++)
-      if (this.match(element = scope[i]))
-        results.push(Element.extend(element));
-
-    return results;
-  },
-
-  toString: function() {
-    return this.expression;
-  }
-}
-
-function $$() {
-  return $A(arguments).map(function(expression) {
-    return expression.strip().split(/\s+/).inject([null], function(results, expr) {
-      var selector = new Selector(expr);
-      return results.map(selector.findElements.bind(selector)).flatten();
-    });
-  }).flatten();
-}
-var Field = {
-  clear: function() {
-    for (var i = 0; i < arguments.length; i++)
-      $PR(arguments[i]).value = '';
-  },
-
-  focus: function(element) {
-    $PR(element).focus();
-  },
-
-  present: function() {
-    for (var i = 0; i < arguments.length; i++)
-      if ($PR(arguments[i]).value == '') return false;
-    return true;
-  },
-
-  select: function(element) {
-    $PR(element).select();
-  },
-
-  activate: function(element) {
-    element = $PR(element);
-    element.focus();
-    if (element.select)
-      element.select();
-  }
-}
-
-/*--------------------------------------------------------------------------*/
-
-var Form = {
-  serialize: function(form) {
-    var elements = Form.getElements($PR(form));
-    var queryComponents = new Array();
-
-    for (var i = 0; i < elements.length; i++) {
-      var queryComponent = Form.Element.serialize(elements[i]);
-      if (queryComponent)
-        queryComponents.push(queryComponent);
-    }
-
-    return queryComponents.join('&');
-  },
-
-  getElements: function(form) {
-    form = $PR(form);
-    var elements = new Array();
-
-    for (var tagName in Form.Element.Serializers) {
-      var tagElements = form.getElementsByTagName(tagName);
-      for (var j = 0; j < tagElements.length; j++)
-        elements.push(tagElements[j]);
-    }
-    return elements;
-  },
-
-  getInputs: function(form, typeName, name) {
-    form = $PR(form);
-    var inputs = form.getElementsByTagName('input');
-
-    if (!typeName && !name)
-      return inputs;
-
-    var matchingInputs = new Array();
-    for (var i = 0; i < inputs.length; i++) {
-      var input = inputs[i];
-      if ((typeName && input.type != typeName) ||
-          (name && input.name != name))
-        continue;
-      matchingInputs.push(input);
-    }
-
-    return matchingInputs;
-  },
-
-  disable: function(form) {
-    var elements = Form.getElements(form);
-    for (var i = 0; i < elements.length; i++) {
-      var element = elements[i];
-      element.blur();
-      element.disabled = 'true';
-    }
-  },
-
-  enable: function(form) {
-    var elements = Form.getElements(form);
-    for (var i = 0; i < elements.length; i++) {
-      var element = elements[i];
-      element.disabled = '';
-    }
-  },
-
-  findFirstElement: function(form) {
-    return Form.getElements(form).find(function(element) {
-      return element.type != 'hidden' && !element.disabled &&
-        ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
-    });
-  },
-
-  focusFirstElement: function(form) {
-    Field.activate(Form.findFirstElement(form));
-  },
-
-  reset: function(form) {
-    $PR(form).reset();
-  }
-}
-
-Form.Element = {
-  serialize: function(element) {
-    element = $PR(element);
-    var method = element.tagName.toLowerCase();
-    var parameter = Form.Element.Serializers[method](element);
-
-    if (parameter) {
-      var key = encodeURIComponent(parameter[0]);
-      if (key.length == 0) return;
-
-      if (parameter[1].constructor != Array)
-        parameter[1] = [parameter[1]];
-
-      return parameter[1].map(function(value) {
-        return key + '=' + encodeURIComponent(value);
-      }).join('&');
-    }
-  },
-
-  getValue: function(element) {
-    element = $PR(element);
-    var method = element.tagName.toLowerCase();
-    var parameter = Form.Element.Serializers[method](element);
-
-    if (parameter)
-      return parameter[1];
-  }
-}
-
-Form.Element.Serializers = {
-  input: function(element) {
-    switch (element.type.toLowerCase()) {
-      case 'submit':
-      case 'hidden':
-      case 'password':
-      case 'text':
-        return Form.Element.Serializers.textarea(element);
-      case 'checkbox':
-      case 'radio':
-        return Form.Element.Serializers.inputSelector(element);
-    }
-    return false;
-  },
-
-  inputSelector: function(element) {
-    if (element.checked)
-      return [element.name, element.value];
-  },
-
-  textarea: function(element) {
-    return [element.name, element.value];
-  },
-
-  select: function(element) {
-    return Form.Element.Serializers[element.type == 'select-one' ?
-      'selectOne' : 'selectMany'](element);
-  },
-
-  selectOne: function(element) {
-    var value = '', opt, index = element.selectedIndex;
-    if (index >= 0) {
-      opt = element.options[index];
-      value = opt.value || opt.text;
-    }
-    return [element.name, value];
-  },
-
-  selectMany: function(element) {
-    var value = [];
-    for (var i = 0; i < element.length; i++) {
-      var opt = element.options[i];
-      if (opt.selected)
-        value.push(opt.value || opt.text);
-    }
-    return [element.name, value];
-  }
-}
-
-/*--------------------------------------------------------------------------*/
-
-var $F = Form.Element.getValue;
-
-/*--------------------------------------------------------------------------*/
-
-Abstract.TimedObserver = function() {}
-Abstract.TimedObserver.prototype = {
-  initialize: function(element, frequency, callback) {
-    this.frequency = frequency;
-    this.element   = $PR(element);
-    this.callback  = callback;
-
-    this.lastValue = this.getValue();
-    this.registerCallback();
-  },
-
-  registerCallback: function() {
-    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
-  },
-
-  onTimerEvent: function() {
-    var value = this.getValue();
-    if (this.lastValue != value) {
-      this.callback(this.element, value);
-      this.lastValue = value;
-    }
-  }
-}
-
-Form.Element.Observer = Class.create();
-Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
-  getValue: function() {
-    return Form.Element.getValue(this.element);
-  }
-});
-
-Form.Observer = Class.create();
-Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
-  getValue: function() {
-    return Form.serialize(this.element);
-  }
-});
-
-/*--------------------------------------------------------------------------*/
-
-Abstract.EventObserver = function() {}
-Abstract.EventObserver.prototype = {
-  initialize: function(element, callback) {
-    this.element  = $PR(element);
-    this.callback = callback;
-
-    this.lastValue = this.getValue();
-    if (this.element.tagName.toLowerCase() == 'form')
-      this.registerFormCallbacks();
-    else
-      this.registerCallback(this.element);
-  },
-
-  onElementEvent: function() {
-    var value = this.getValue();
-    if (this.lastValue != value) {
-      this.callback(this.element, value);
-      this.lastValue = value;
-    }
-  },
-
-  registerFormCallbacks: function() {
-    var elements = Form.getElements(this.element);
-    for (var i = 0; i < elements.length; i++)
-      this.registerCallback(elements[i]);
-  },
-
-  registerCallback: function(element) {
-    if (element.type) {
-      switch (element.type.toLowerCase()) {
-        case 'checkbox':
-        case 'radio':
-          Event.observe(element, 'click', this.onElementEvent.bind(this));
-          break;
-        case 'password':
-        case 'text':
-        case 'textarea':
-        case 'select-one':
-        case 'select-multiple':
-          Event.observe(element, 'change', this.onElementEvent.bind(this));
-          break;
-      }
-    }
-  }
-}
-
-Form.Element.EventObserver = Class.create();
-Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
-  getValue: function() {
-    return Form.Element.getValue(this.element);
-  }
-});
-
-Form.EventObserver = Class.create();
-Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
-  getValue: function() {
-    return Form.serialize(this.element);
-  }
-});
-if (!window.Event) {
-  var Event = new Object();
-}
-
-Object.extend(Event, {
-  KEY_BACKSPACE: 8,
-  KEY_TAB:       9,
-  KEY_RETURN:   13,
-  KEY_ESC:      27,
-  KEY_LEFT:     37,
-  KEY_UP:       38,
-  KEY_RIGHT:    39,
-  KEY_DOWN:     40,
-  KEY_DELETE:   46,
-
-  element: function(event) {
-    return event.target || event.srcElement;
-  },
-
-  isLeftClick: function(event) {
-    return (((event.which) && (event.which == 1)) ||
-            ((event.button) && (event.button == 1)));
-  },
-
-  pointerX: function(event) {
-    return event.pageX || (event.clientX +
-      (document.documentElement.scrollLeft || document.body.scrollLeft));
-  },
-
-  pointerY: function(event) {
-    return event.pageY || (event.clientY +
-      (document.documentElement.scrollTop || document.body.scrollTop));
-  },
-
-  stop: function(event) {
-    if (event.preventDefault) {
-      event.preventDefault();
-      event.stopPropagation();
-    } else {
-      event.returnValue = false;
-      event.cancelBubble = true;
-    }
-  },
-
-  // find the first node with the given tagName, starting from the
-  // node the event was triggered on; traverses the DOM upwards
-  findElement: function(event, tagName) {
-    var element = Event.element(event);
-    while (element.parentNode && (!element.tagName ||
-        (element.tagName.toUpperCase() != tagName.toUpperCase())))
-      element = element.parentNode;
-    return element;
-  },
-
-  observers: false,
-
-  _observeAndCache: function(element, name, observer, useCapture) {
-    if (!this.observers) this.observers = [];
-    if (element.addEventListener) {
-      this.observers.push([element, name, observer, useCapture]);
-      element.addEventListener(name, observer, useCapture);
-    } else if (element.attachEvent) {
-      this.observers.push([element, name, observer, useCapture]);
-      element.attachEvent('on' + name, observer);
-    }
-  },
-
-  unloadCache: function() {
-    if (!Event.observers) return;
-    for (var i = 0; i < Event.observers.length; i++) {
-      Event.stopObserving.apply(this, Event.observers[i]);
-      Event.observers[i][0] = null;
-    }
-    Event.observers = false;
-  },
-
-  observe: function(element, name, observer, useCapture) {
-    var element = $PR(element);
-    useCapture = useCapture || false;
-
-    if (name == 'keypress' &&
-        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
-        || element.attachEvent))
-      name = 'keydown';
-
-    this._observeAndCache(element, name, observer, useCapture);
-  },
-
-  stopObserving: function(element, name, observer, useCapture) {
-    var element = $PR(element);
-    useCapture = useCapture || false;
-
-    if (name == 'keypress' &&
-        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
-        || element.detachEvent))
-      name = 'keydown';
-
-    if (element.removeEventListener) {
-      element.removeEventListener(name, observer, useCapture);
-    } else if (element.detachEvent) {
-      element.detachEvent('on' + name, observer);
-    }
-  }
-});
-
-/* prevent memory leaks in IE */
-if (navigator.appVersion.match(/\bMSIE\b/))
-  Event.observe(window, 'unload', Event.unloadCache, false);
-var Position = {
-  // set to true if needed, warning: firefox performance problems
-  // NOT neeeded for page scrolling, only if draggable contained in
-  // scrollable elements
-  includeScrollOffsets: false,
-
-  // must be called before calling withinIncludingScrolloffset, every time the
-  // page is scrolled
-  prepare: function() {
-    this.deltaX =  window.pageXOffset
-                || document.documentElement.scrollLeft
-                || document.body.scrollLeft
-                || 0;
-    this.deltaY =  window.pageYOffset
-                || document.documentElement.scrollTop
-                || document.body.scrollTop
-                || 0;
-  },
-
-  realOffset: function(element) {
-    var valueT = 0, valueL = 0;
-    do {
-      valueT += element.scrollTop  || 0;
-      valueL += element.scrollLeft || 0;
-      element = element.parentNode;
-    } while (element);
-    return [valueL, valueT];
-  },
-
-  cumulativeOffset: function(element) {
-    var valueT = 0, valueL = 0;
-    do {
-      valueT += element.offsetTop  || 0;
-      valueL += element.offsetLeft || 0;
-      element = element.offsetParent;
-    } while (element);
-    return [valueL, valueT];
-  },
-
-  positionedOffset: function(element) {
-    var valueT = 0, valueL = 0;
-    do {
-      valueT += element.offsetTop  || 0;
-      valueL += element.offsetLeft || 0;
-      element = element.offsetParent;
-      if (element) {
-        p = Element.getStyle(element, 'position');
-        if (p == 'relative' || p == 'absolute') break;
-      }
-    } while (element);
-    return [valueL, valueT];
-  },
-
-  offsetParent: function(element) {
-    if (element.offsetParent) return element.offsetParent;
-    if (element == document.body) return element;
-
-    while ((element = element.parentNode) && element != document.body)
-      if (Element.getStyle(element, 'position') != 'static')
-        return element;
-
-    return document.body;
-  },
-
-  // caches x/y coordinate pair to use with overlap
-  within: function(element, x, y) {
-    if (this.includeScrollOffsets)
-      return this.withinIncludingScrolloffsets(element, x, y);
-    this.xcomp = x;
-    this.ycomp = y;
-    this.offset = this.cumulativeOffset(element);
-
-    return (y >= this.offset[1] &&
-            y <  this.offset[1] + element.offsetHeight &&
-            x >= this.offset[0] &&
-            x <  this.offset[0] + element.offsetWidth);
-  },
-
-  withinIncludingScrolloffsets: function(element, x, y) {
-    var offsetcache = this.realOffset(element);
-
-    this.xcomp = x + offsetcache[0] - this.deltaX;
-    this.ycomp = y + offsetcache[1] - this.deltaY;
-    this.offset = this.cumulativeOffset(element);
-
-    return (this.ycomp >= this.offset[1] &&
-            this.ycomp <  this.offset[1] + element.offsetHeight &&
-            this.xcomp >= this.offset[0] &&
-            this.xcomp <  this.offset[0] + element.offsetWidth);
-  },
-
-  // within must be called directly before
-  overlap: function(mode, element) {
-    if (!mode) return 0;
-    if (mode == 'vertical')
-      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
-        element.offsetHeight;
-    if (mode == 'horizontal')
-      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
-        element.offsetWidth;
-  },
-
-  clone: function(source, target) {
-    source = $PR(source);
-    target = $PR(target);
-    target.style.position = 'absolute';
-    var offsets = this.cumulativeOffset(source);
-    target.style.top    = offsets[1] + 'px';
-    target.style.left   = offsets[0] + 'px';
-    target.style.width  = source.offsetWidth + 'px';
-    target.style.height = source.offsetHeight + 'px';
-  },
-
-  page: function(forElement) {
-    var valueT = 0, valueL = 0;
-
-    var element = forElement;
-    do {
-      valueT += element.offsetTop  || 0;
-      valueL += element.offsetLeft || 0;
-
-      // Safari fix
-      if (element.offsetParent==document.body)
-        if (Element.getStyle(element,'position')=='absolute') break;
-
-    } while (element = element.offsetParent);
-
-    element = forElement;
-    do {
-      valueT -= element.scrollTop  || 0;
-      valueL -= element.scrollLeft || 0;
-    } while (element = element.parentNode);
-
-    return [valueL, valueT];
-  },
-
-  clone: function(source, target) {
-    var options = Object.extend({
-      setLeft:    true,
-      setTop:     true,
-      setWidth:   true,
-      setHeight:  true,
-      offsetTop:  0,
-      offsetLeft: 0
-    }, arguments[2] || {})
-
-    // find page position of source
-    source = $PR(source);
-    var p = Position.page(source);
-
-    // find coordinate system to use
-    target = $PR(target);
-    var delta = [0, 0];
-    var parent = null;
-    // delta [0,0] will do fine with position: fixed elements,
-    // position:absolute needs offsetParent deltas
-    if (Element.getStyle(target,'position') == 'absolute') {
-      parent = Position.offsetParent(target);
-      delta = Position.page(parent);
-    }
-
-    // correct by body offsets (fixes Safari)
-    if (parent == document.body) {
-      delta[0] -= document.body.offsetLeft;
-      delta[1] -= document.body.offsetTop;
-    }
-
-    // set position
-    if(options.setLeft)   target.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
-    if(options.setTop)    target.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
-    if(options.setWidth)  target.style.width = source.offsetWidth + 'px';
-    if(options.setHeight) target.style.height = source.offsetHeight + 'px';
-  },
-
-  absolutize: function(element) {
-    element = $PR(element);
-    if (element.style.position == 'absolute') return;
-    Position.prepare();
-
-    var offsets = Position.positionedOffset(element);
-    var top     = offsets[1];
-    var left    = offsets[0];
-    var width   = element.clientWidth;
-    var height  = element.clientHeight;
-
-    element._originalLeft   = left - parseFloat(element.style.left  || 0);
-    element._originalTop    = top  - parseFloat(element.style.top || 0);
-    element._originalWidth  = element.style.width;
-    element._originalHeight = element.style.height;
-
-    element.style.position = 'absolute';
-    element.style.top    = top + 'px';;
-    element.style.left   = left + 'px';;
-    element.style.width  = width + 'px';;
-    element.style.height = height + 'px';;
-  },
-
-  relativize: function(element) {
-    element = $PR(element);
-    if (element.style.position == 'relative') return;
-    Position.prepare();
-
-    element.style.position = 'relative';
-    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
-    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
-
-    element.style.top    = top + 'px';
-    element.style.left   = left + 'px';
-    element.style.height = element._originalHeight;
-    element.style.width  = element._originalWidth;
-  }
-}
-
-// Safari returns margins on body which is incorrect if the child is absolutely
-// positioned.  For performance reasons, redefine Position.cumulativeOffset for
-// KHTML/WebKit only.
-if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
-  Position.cumulativeOffset = function(element) {
-    var valueT = 0, valueL = 0;
-    do {
-      valueT += element.offsetTop  || 0;
-      valueL += element.offsetLeft || 0;
-      if (element.offsetParent == document.body)
-        if (Element.getStyle(element, 'position') == 'absolute') break;
-
-      element = element.offsetParent;
-    } while (element);
-
-    return [valueL, valueT];
-  }
-}
diff --git a/library/cropper/lib/scriptaculous.js b/library/cropper/lib/scriptaculous.js
deleted file mode 100644 (file)
index f61fc57..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
-// 
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-// 
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-var Scriptaculous = {
-  Version: '1.6.1',
-  require: function(libraryName) {
-    // inserting via DOM fails in Safari 2.0, so brute force approach
-    document.write('<script type="text/javascript" src="'+libraryName+'"></script>');
-  },
-  load: function() {
-    if((typeof Prototype=='undefined') || 
-       (typeof Element == 'undefined') || 
-       (typeof Element.Methods=='undefined') ||
-       parseFloat(Prototype.Version.split(".")[0] + "." +
-                  Prototype.Version.split(".")[1]) < 1.5)
-       throw("script.aculo.us requires the Prototype JavaScript framework >= 1.5.0");
-    
-    $A(document.getElementsByTagName("script")).findAll( function(s) {
-      return (s.src && s.src.match(/scriptaculous\.js(\?.*)?$/))
-    }).each( function(s) {
-      var path = s.src.replace(/scriptaculous\.js(\?.*)?$/,'');
-      var includes = s.src.match(/\?.*load=([a-z,]*)/);
-      (includes ? includes[1] : 'builder,effects,dragdrop,controls,slider').split(',').each(
-       function(include) { Scriptaculous.require(path+include+'.js') });
-    });
-  }
-}
-
-Scriptaculous.load();
\ No newline at end of file
diff --git a/library/cropper/lib/slider.js b/library/cropper/lib/slider.js
deleted file mode 100644 (file)
index cd16b69..0000000
+++ /dev/null
@@ -1,283 +0,0 @@
-// Copyright (c) 2005 Marty Haught, Thomas Fuchs 
-//
-// See http://script.aculo.us for more info
-// 
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-// 
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-if(!Control) var Control = {};
-Control.Slider = Class.create();
-
-// options:
-//  axis: 'vertical', or 'horizontal' (default)
-//
-// callbacks:
-//  onChange(value)
-//  onSlide(value)
-Control.Slider.prototype = {
-  initialize: function(handle, track, options) {
-    var slider = this;
-    
-    if(handle instanceof Array) {
-      this.handles = handle.collect( function(e) { return $PR(e) });
-    } else {
-      this.handles = [$PR(handle)];
-    }
-    
-    this.track   = $PR(track);
-    this.options = options || {};
-
-    this.axis      = this.options.axis || 'horizontal';
-    this.increment = this.options.increment || 1;
-    this.step      = parseInt(this.options.step || '1');
-    this.range     = this.options.range || $R(0,1);
-    
-    this.value     = 0; // assure backwards compat
-    this.values    = this.handles.map( function() { return 0 });
-    this.spans     = this.options.spans ? this.options.spans.map(function(s){ return $PR(s) }) : false;
-    this.options.startSpan = $PR(this.options.startSpan || null);
-    this.options.endSpan   = $PR(this.options.endSpan || null);
-
-    this.restricted = this.options.restricted || false;
-
-    this.maximum   = this.options.maximum || this.range.end;
-    this.minimum   = this.options.minimum || this.range.start;
-
-    // Will be used to align the handle onto the track, if necessary
-    this.alignX = parseInt(this.options.alignX || '0');
-    this.alignY = parseInt(this.options.alignY || '0');
-    
-    this.trackLength = this.maximumOffset() - this.minimumOffset();
-    this.handleLength = this.isVertical() ? this.handles[0].offsetHeight : this.handles[0].offsetWidth;
-
-    this.active   = false;
-    this.dragging = false;
-    this.disabled = false;
-
-    if(this.options.disabled) this.setDisabled();
-
-    // Allowed values array
-    this.allowedValues = this.options.values ? this.options.values.sortBy(Prototype.K) : false;
-    if(this.allowedValues) {
-      this.minimum = this.allowedValues.min();
-      this.maximum = this.allowedValues.max();
-    }
-
-    this.eventMouseDown = this.startDrag.bindAsEventListener(this);
-    this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
-    this.eventMouseMove = this.update.bindAsEventListener(this);
-
-    // Initialize handles in reverse (make sure first handle is active)
-    this.handles.each( function(h,i) {
-      i = slider.handles.length-1-i;
-      slider.setValue(parseFloat(
-        (slider.options.sliderValue instanceof Array ? 
-          slider.options.sliderValue[i] : slider.options.sliderValue) || 
-         slider.range.start), i);
-      Element.makePositioned(h); // fix IE
-      Event.observe(h, "mousedown", slider.eventMouseDown);
-    });
-    
-    Event.observe(this.track, "mousedown", this.eventMouseDown);
-    Event.observe(document, "mouseup", this.eventMouseUp);
-    Event.observe(document, "mousemove", this.eventMouseMove);
-    
-    this.initialized = true;
-  },
-  dispose: function() {
-    var slider = this;    
-    Event.stopObserving(this.track, "mousedown", this.eventMouseDown);
-    Event.stopObserving(document, "mouseup", this.eventMouseUp);
-    Event.stopObserving(document, "mousemove", this.eventMouseMove);
-    this.handles.each( function(h) {
-      Event.stopObserving(h, "mousedown", slider.eventMouseDown);
-    });
-  },
-  setDisabled: function(){
-    this.disabled = true;
-  },
-  setEnabled: function(){
-    this.disabled = false;
-  },  
-  getNearestValue: function(value){
-    if(this.allowedValues){
-      if(value >= this.allowedValues.max()) return(this.allowedValues.max());
-      if(value <= this.allowedValues.min()) return(this.allowedValues.min());
-      
-      var offset = Math.abs(this.allowedValues[0] - value);
-      var newValue = this.allowedValues[0];
-      this.allowedValues.each( function(v) {
-        var currentOffset = Math.abs(v - value);
-        if(currentOffset <= offset){
-          newValue = v;
-          offset = currentOffset;
-        } 
-      });
-      return newValue;
-    }
-    if(value > this.range.end) return this.range.end;
-    if(value < this.range.start) return this.range.start;
-    return value;
-  },
-  setValue: function(sliderValue, handleIdx){
-    if(!this.active) {
-      this.activeHandle    = this.handles[handleIdx];
-      this.activeHandleIdx = handleIdx;
-      this.updateStyles();
-    }
-    handleIdx = handleIdx || this.activeHandleIdx || 0;
-    if(this.initialized && this.restricted) {
-      if((handleIdx>0) && (sliderValue<this.values[handleIdx-1]))
-        sliderValue = this.values[handleIdx-1];
-      if((handleIdx < (this.handles.length-1)) && (sliderValue>this.values[handleIdx+1]))
-        sliderValue = this.values[handleIdx+1];
-    }
-    sliderValue = this.getNearestValue(sliderValue);
-    this.values[handleIdx] = sliderValue;
-    this.value = this.values[0]; // assure backwards compat
-    
-    this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] = 
-      this.translateToPx(sliderValue);
-    
-    this.drawSpans();
-    if(!this.dragging || !this.event) this.updateFinished();
-  },
-  setValueBy: function(delta, handleIdx) {
-    this.setValue(this.values[handleIdx || this.activeHandleIdx || 0] + delta, 
-      handleIdx || this.activeHandleIdx || 0);
-  },
-  translateToPx: function(value) {
-    return Math.round(
-      ((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) * 
-      (value - this.range.start)) + "px";
-  },
-  translateToValue: function(offset) {
-    return ((offset/(this.trackLength-this.handleLength) * 
-      (this.range.end-this.range.start)) + this.range.start);
-  },
-  getRange: function(range) {
-    var v = this.values.sortBy(Prototype.K); 
-    range = range || 0;
-    return $R(v[range],v[range+1]);
-  },
-  minimumOffset: function(){
-    return(this.isVertical() ? this.alignY : this.alignX);
-  },
-  maximumOffset: function(){
-    return(this.isVertical() ?
-      this.track.offsetHeight - this.alignY : this.track.offsetWidth - this.alignX);
-  },  
-  isVertical:  function(){
-    return (this.axis == 'vertical');
-  },
-  drawSpans: function() {
-    var slider = this;
-    if(this.spans)
-      $R(0, this.spans.length-1).each(function(r) { slider.setSpan(slider.spans[r], slider.getRange(r)) });
-    if(this.options.startSpan)
-      this.setSpan(this.options.startSpan,
-        $R(0, this.values.length>1 ? this.getRange(0).min() : this.value ));
-    if(this.options.endSpan)
-      this.setSpan(this.options.endSpan, 
-        $R(this.values.length>1 ? this.getRange(this.spans.length-1).max() : this.value, this.maximum));
-  },
-  setSpan: function(span, range) {
-    if(this.isVertical()) {
-      span.style.top = this.translateToPx(range.start);
-      span.style.height = this.translateToPx(range.end - range.start + this.range.start);
-    } else {
-      span.style.left = this.translateToPx(range.start);
-      span.style.width = this.translateToPx(range.end - range.start + this.range.start);
-    }
-  },
-  updateStyles: function() {
-    this.handles.each( function(h){ Element.removeClassName(h, 'selected') });
-    Element.addClassName(this.activeHandle, 'selected');
-  },
-  startDrag: function(event) {
-    if(Event.isLeftClick(event)) {
-      if(!this.disabled){
-        this.active = true;
-        
-        var handle = Event.element(event);
-        var pointer  = [Event.pointerX(event), Event.pointerY(event)];
-        if(handle==this.track) {
-          var offsets  = Position.cumulativeOffset(this.track); 
-          this.event = event;
-          this.setValue(this.translateToValue( 
-           (this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2)
-          ));
-          var offsets  = Position.cumulativeOffset(this.activeHandle);
-          this.offsetX = (pointer[0] - offsets[0]);
-          this.offsetY = (pointer[1] - offsets[1]);
-        } else {
-          // find the handle (prevents issues with Safari)
-          while((this.handles.indexOf(handle) == -1) && handle.parentNode) 
-            handle = handle.parentNode;
-        
-          this.activeHandle    = handle;
-          this.activeHandleIdx = this.handles.indexOf(this.activeHandle);
-          this.updateStyles();
-        
-          var offsets  = Position.cumulativeOffset(this.activeHandle);
-          this.offsetX = (pointer[0] - offsets[0]);
-          this.offsetY = (pointer[1] - offsets[1]);
-        }
-      }
-      Event.stop(event);
-    }
-  },
-  update: function(event) {
-   if(this.active) {
-      if(!this.dragging) this.dragging = true;
-      this.draw(event);
-      // fix AppleWebKit rendering
-      if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
-      Event.stop(event);
-   }
-  },
-  draw: function(event) {
-    var pointer = [Event.pointerX(event), Event.pointerY(event)];
-    var offsets = Position.cumulativeOffset(this.track);
-    pointer[0] -= this.offsetX + offsets[0];
-    pointer[1] -= this.offsetY + offsets[1];
-    this.event = event;
-    this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] ));
-    if(this.initialized && this.options.onSlide)
-      this.options.onSlide(this.values.length>1 ? this.values : this.value, this);
-  },
-  endDrag: function(event) {
-    if(this.active && this.dragging) {
-      this.finishDrag(event, true);
-      Event.stop(event);
-    }
-    this.active = false;
-    this.dragging = false;
-  },  
-  finishDrag: function(event, success) {
-    this.active = false;
-    this.dragging = false;
-    this.updateFinished();
-  },
-  updateFinished: function() {
-    if(this.initialized && this.options.onChange) 
-      this.options.onChange(this.values.length>1 ? this.values : this.value, this);
-    this.event = null;
-  }
-}
diff --git a/library/cropper/lib/unittest.js b/library/cropper/lib/unittest.js
deleted file mode 100644 (file)
index be0d252..0000000
+++ /dev/null
@@ -1,383 +0,0 @@
-// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
-//           (c) 2005 Jon Tirsen (http://www.tirsen.com)
-//           (c) 2005 Michael Schuerig (http://www.schuerig.de/michael/)
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-// 
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-// 
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-
-// experimental, Firefox-only
-Event.simulateMouse = function(element, eventName) {
-  var options = Object.extend({
-    pointerX: 0,
-    pointerY: 0,
-    buttons: 0
-  }, arguments[2] || {});
-  var oEvent = document.createEvent("MouseEvents");
-  oEvent.initMouseEvent(eventName, true, true, document.defaultView, 
-    options.buttons, options.pointerX, options.pointerY, options.pointerX, options.pointerY, 
-    false, false, false, false, 0, $PR(element));
-  
-  if(this.mark) Element.remove(this.mark);
-  this.mark = document.createElement('div');
-  this.mark.appendChild(document.createTextNode(" "));
-  document.body.appendChild(this.mark);
-  this.mark.style.position = 'absolute';
-  this.mark.style.top = options.pointerY + "px";
-  this.mark.style.left = options.pointerX + "px";
-  this.mark.style.width = "5px";
-  this.mark.style.height = "5px;";
-  this.mark.style.borderTop = "1px solid red;"
-  this.mark.style.borderLeft = "1px solid red;"
-  
-  if(this.step)
-    alert('['+new Date().getTime().toString()+'] '+eventName+'/'+Test.Unit.inspect(options));
-  
-  $PR(element).dispatchEvent(oEvent);
-};
-
-// Note: Due to a fix in Firefox 1.0.5/6 that probably fixed "too much", this doesn't work in 1.0.6 or DP2.
-// You need to downgrade to 1.0.4 for now to get this working
-// See https://bugzilla.mozilla.org/show_bug.cgi?id=289940 for the fix that fixed too much
-Event.simulateKey = function(element, eventName) {
-  var options = Object.extend({
-    ctrlKey: false,
-    altKey: false,
-    shiftKey: false,
-    metaKey: false,
-    keyCode: 0,
-    charCode: 0
-  }, arguments[2] || {});
-
-  var oEvent = document.createEvent("KeyEvents");
-  oEvent.initKeyEvent(eventName, true, true, window, 
-    options.ctrlKey, options.altKey, options.shiftKey, options.metaKey,
-    options.keyCode, options.charCode );
-  $PR(element).dispatchEvent(oEvent);
-};
-
-Event.simulateKeys = function(element, command) {
-  for(var i=0; i<command.length; i++) {
-    Event.simulateKey(element,'keypress',{charCode:command.charCodeAt(i)});
-  }
-};
-
-var Test = {}
-Test.Unit = {};
-
-// security exception workaround
-Test.Unit.inspect = Object.inspect;
-
-Test.Unit.Logger = Class.create();
-Test.Unit.Logger.prototype = {
-  initialize: function(log) {
-    this.log = $PR(log);
-    if (this.log) {
-      this._createLogTable();
-    }
-  },
-  start: function(testName) {
-    if (!this.log) return;
-    this.testName = testName;
-    this.lastLogLine = document.createElement('tr');
-    this.statusCell = document.createElement('td');
-    this.nameCell = document.createElement('td');
-    this.nameCell.appendChild(document.createTextNode(testName));
-    this.messageCell = document.createElement('td');
-    this.lastLogLine.appendChild(this.statusCell);
-    this.lastLogLine.appendChild(this.nameCell);
-    this.lastLogLine.appendChild(this.messageCell);
-    this.loglines.appendChild(this.lastLogLine);
-  },
-  finish: function(status, summary) {
-    if (!this.log) return;
-    this.lastLogLine.className = status;
-    this.statusCell.innerHTML = status;
-    this.messageCell.innerHTML = this._toHTML(summary);
-  },
-  message: function(message) {
-    if (!this.log) return;
-    this.messageCell.innerHTML = this._toHTML(message);
-  },
-  summary: function(summary) {
-    if (!this.log) return;
-    this.logsummary.innerHTML = this._toHTML(summary);
-  },
-  _createLogTable: function() {
-    this.log.innerHTML =
-    '<div id="logsummary"></div>' +
-    '<table id="logtable">' +
-    '<thead><tr><th>Status</th><th>Test</th><th>Message</th></tr></thead>' +
-    '<tbody id="loglines"></tbody>' +
-    '</table>';
-    this.logsummary = $PR('logsummary')
-    this.loglines = $PR('loglines');
-  },
-  _toHTML: function(txt) {
-    return txt.escapeHTML().replace(/\n/g,"<br/>");
-  }
-}
-
-Test.Unit.Runner = Class.create();
-Test.Unit.Runner.prototype = {
-  initialize: function(testcases) {
-    this.options = Object.extend({
-      testLog: 'testlog'
-    }, arguments[1] || {});
-    this.options.resultsURL = this.parseResultsURLQueryParameter();
-    if (this.options.testLog) {
-      this.options.testLog = $PR(this.options.testLog) || null;
-    }
-    if(this.options.tests) {
-      this.tests = [];
-      for(var i = 0; i < this.options.tests.length; i++) {
-        if(/^test/.test(this.options.tests[i])) {
-          this.tests.push(new Test.Unit.Testcase(this.options.tests[i], testcases[this.options.tests[i]], testcases["setup"], testcases["teardown"]));
-        }
-      }
-    } else {
-      if (this.options.test) {
-        this.tests = [new Test.Unit.Testcase(this.options.test, testcases[this.options.test], testcases["setup"], testcases["teardown"])];
-      } else {
-        this.tests = [];
-        for(var testcase in testcases) {
-          if(/^test/.test(testcase)) {
-            this.tests.push(new Test.Unit.Testcase(testcase, testcases[testcase], testcases["setup"], testcases["teardown"]));
-          }
-        }
-      }
-    }
-    this.currentTest = 0;
-    this.logger = new Test.Unit.Logger(this.options.testLog);
-    setTimeout(this.runTests.bind(this), 1000);
-  },
-  parseResultsURLQueryParameter: function() {
-    return window.location.search.parseQuery()["resultsURL"];
-  },
-  // Returns:
-  //  "ERROR" if there was an error,
-  //  "FAILURE" if there was a failure, or
-  //  "SUCCESS" if there was neither
-  getResult: function() {
-    var hasFailure = false;
-    for(var i=0;i<this.tests.length;i++) {
-      if (this.tests[i].errors > 0) {
-        return "ERROR";
-      }
-      if (this.tests[i].failures > 0) {
-        hasFailure = true;
-      }
-    }
-    if (hasFailure) {
-      return "FAILURE";
-    } else {
-      return "SUCCESS";
-    }
-  },
-  postResults: function() {
-    if (this.options.resultsURL) {
-      new Ajax.Request(this.options.resultsURL, 
-        { method: 'get', parameters: 'result=' + this.getResult(), asynchronous: false });
-    }
-  },
-  runTests: function() {
-    var test = this.tests[this.currentTest];
-    if (!test) {
-      // finished!
-      this.postResults();
-      this.logger.summary(this.summary());
-      return;
-    }
-    if(!test.isWaiting) {
-      this.logger.start(test.name);
-    }
-    test.run();
-    if(test.isWaiting) {
-      this.logger.message("Waiting for " + test.timeToWait + "ms");
-      setTimeout(this.runTests.bind(this), test.timeToWait || 1000);
-    } else {
-      this.logger.finish(test.status(), test.summary());
-      this.currentTest++;
-      // tail recursive, hopefully the browser will skip the stackframe
-      this.runTests();
-    }
-  },
-  summary: function() {
-    var assertions = 0;
-    var failures = 0;
-    var errors = 0;
-    var messages = [];
-    for(var i=0;i<this.tests.length;i++) {
-      assertions +=   this.tests[i].assertions;
-      failures   +=   this.tests[i].failures;
-      errors     +=   this.tests[i].errors;
-    }
-    return (
-      this.tests.length + " tests, " + 
-      assertions + " assertions, " + 
-      failures   + " failures, " +
-      errors     + " errors");
-  }
-}
-
-Test.Unit.Assertions = Class.create();
-Test.Unit.Assertions.prototype = {
-  initialize: function() {
-    this.assertions = 0;
-    this.failures   = 0;
-    this.errors     = 0;
-    this.messages   = [];
-  },
-  summary: function() {
-    return (
-      this.assertions + " assertions, " + 
-      this.failures   + " failures, " +
-      this.errors     + " errors" + "\n" +
-      this.messages.join("\n"));
-  },
-  pass: function() {
-    this.assertions++;
-  },
-  fail: function(message) {
-    this.failures++;
-    this.messages.push("Failure: " + message);
-  },
-  info: function(message) {
-    this.messages.push("Info: " + message);
-  },
-  error: function(error) {
-    this.errors++;
-    this.messages.push(error.name + ": "+ error.message + "(" + Test.Unit.inspect(error) +")");
-  },
-  status: function() {
-    if (this.failures > 0) return 'failed';
-    if (this.errors > 0) return 'error';
-    return 'passed';
-  },
-  assert: function(expression) {
-    var message = arguments[1] || 'assert: got "' + Test.Unit.inspect(expression) + '"';
-    try { expression ? this.pass() : 
-      this.fail(message); }
-    catch(e) { this.error(e); }
-  },
-  assertEqual: function(expected, actual) {
-    var message = arguments[2] || "assertEqual";
-    try { (expected == actual) ? this.pass() :
-      this.fail(message + ': expected "' + Test.Unit.inspect(expected) + 
-        '", actual "' + Test.Unit.inspect(actual) + '"'); }
-    catch(e) { this.error(e); }
-  },
-  assertEnumEqual: function(expected, actual) {
-    var message = arguments[2] || "assertEnumEqual";
-    try { $A(expected).length == $A(actual).length && 
-      expected.zip(actual).all(function(pair) { return pair[0] == pair[1] }) ?
-        this.pass() : this.fail(message + ': expected ' + Test.Unit.inspect(expected) + 
-          ', actual ' + Test.Unit.inspect(actual)); }
-    catch(e) { this.error(e); }
-  },
-  assertNotEqual: function(expected, actual) {
-    var message = arguments[2] || "assertNotEqual";
-    try { (expected != actual) ? this.pass() : 
-      this.fail(message + ': got "' + Test.Unit.inspect(actual) + '"'); }
-    catch(e) { this.error(e); }
-  },
-  assertNull: function(obj) {
-    var message = arguments[1] || 'assertNull'
-    try { (obj==null) ? this.pass() : 
-      this.fail(message + ': got "' + Test.Unit.inspect(obj) + '"'); }
-    catch(e) { this.error(e); }
-  },
-  assertHidden: function(element) {
-    var message = arguments[1] || 'assertHidden';
-    this.assertEqual("none", element.style.display, message);
-  },
-  assertNotNull: function(object) {
-    var message = arguments[1] || 'assertNotNull';
-    this.assert(object != null, message);
-  },
-  assertInstanceOf: function(expected, actual) {
-    var message = arguments[2] || 'assertInstanceOf';
-    try { 
-      (actual instanceof expected) ? this.pass() : 
-      this.fail(message + ": object was not an instance of the expected type"); }
-    catch(e) { this.error(e); } 
-  },
-  assertNotInstanceOf: function(expected, actual) {
-    var message = arguments[2] || 'assertNotInstanceOf';
-    try { 
-      !(actual instanceof expected) ? this.pass() : 
-      this.fail(message + ": object was an instance of the not expected type"); }
-    catch(e) { this.error(e); } 
-  },
-  _isVisible: function(element) {
-    element = $PR(element);
-    if(!element.parentNode) return true;
-    this.assertNotNull(element);
-    if(element.style && Element.getStyle(element, 'display') == 'none')
-      return false;
-    
-    return this._isVisible(element.parentNode);
-  },
-  assertNotVisible: function(element) {
-    this.assert(!this._isVisible(element), Test.Unit.inspect(element) + " was not hidden and didn't have a hidden parent either. " + ("" || arguments[1]));
-  },
-  assertVisible: function(element) {
-    this.assert(this._isVisible(element), Test.Unit.inspect(element) + " was not visible. " + ("" || arguments[1]));
-  },
-  benchmark: function(operation, iterations) {
-    var startAt = new Date();
-    (iterations || 1).times(operation);
-    var timeTaken = ((new Date())-startAt);
-    this.info((arguments[2] || 'Operation') + ' finished ' + 
-       iterations + ' iterations in ' + (timeTaken/1000)+'s' );
-    return timeTaken;
-  }
-}
-
-Test.Unit.Testcase = Class.create();
-Object.extend(Object.extend(Test.Unit.Testcase.prototype, Test.Unit.Assertions.prototype), {
-  initialize: function(name, test, setup, teardown) {
-    Test.Unit.Assertions.prototype.initialize.bind(this)();
-    this.name           = name;
-    this.test           = test || function() {};
-    this.setup          = setup || function() {};
-    this.teardown       = teardown || function() {};
-    this.isWaiting      = false;
-    this.timeToWait     = 1000;
-  },
-  wait: function(time, nextPart) {
-    this.isWaiting = true;
-    this.test = nextPart;
-    this.timeToWait = time;
-  },
-  run: function() {
-    try {
-      try {
-        if (!this.isWaiting) this.setup.bind(this)();
-        this.isWaiting = false;
-        this.test.bind(this)();
-      } finally {
-        if(!this.isWaiting) {
-          this.teardown.bind(this)();
-        }
-      }
-    }
-    catch(e) { this.error(e); }
-  }
-});
diff --git a/library/cropper/licence.txt b/library/cropper/licence.txt
deleted file mode 100644 (file)
index b59e029..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-Copyright (c) 2006, David Spurr (www.defusion.org.uk)\r
-All rights reserved.\r
-\r
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\r
-\r
-    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\r
-    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\r
-    * Neither the name of the David Spurr nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\r
-\r
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
-\r
-http://www.opensource.org/licenses/bsd-license.php
\ No newline at end of file
diff --git a/library/cropper/marqueeHoriz.gif b/library/cropper/marqueeHoriz.gif
deleted file mode 100644 (file)
index 25317e5..0000000
Binary files a/library/cropper/marqueeHoriz.gif and /dev/null differ
diff --git a/library/cropper/marqueeVert.gif b/library/cropper/marqueeVert.gif
deleted file mode 100644 (file)
index 354070b..0000000
Binary files a/library/cropper/marqueeVert.gif and /dev/null differ
diff --git a/library/cropper/tests/castle.jpg b/library/cropper/tests/castle.jpg
deleted file mode 100644 (file)
index e40b7e4..0000000
Binary files a/library/cropper/tests/castle.jpg and /dev/null differ
diff --git a/library/cropper/tests/castleMed.jpg b/library/cropper/tests/castleMed.jpg
deleted file mode 100644 (file)
index c35a6f5..0000000
Binary files a/library/cropper/tests/castleMed.jpg and /dev/null differ
diff --git a/library/cropper/tests/example-Basic.htm b/library/cropper/tests/example-Basic.htm
deleted file mode 100644 (file)
index 2a55eca..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\r
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\r
-<head>\r
-       <meta http-equiv="Content-type" content="text/html; charset=utf-8" />\r
-       <meta http-equiv="Content-Language" content="en-us" />\r
-       <title>Basic cropper test</title>\r
-       <script src="../lib/prototype.js" type="text/javascript"></script>      \r
-       <script src="../lib/scriptaculous.js?load=builder,dragdrop" type="text/javascript"></script>\r
-       <script src="../cropper.js" type="text/javascript"></script>\r
-       \r
-       \r
-       <script type="text/javascript" charset="utf-8">\r
-               \r
-               // setup the callback function\r
-               function onEndCrop( coords, dimensions ) {\r
-                       $PR( 'x1' ).value = coords.x1;\r
-                       $PR( 'y1' ).value = coords.y1;\r
-                       $PR( 'x2' ).value = coords.x2;\r
-                       $PR( 'y2' ).value = coords.y2;\r
-                       $PR( 'width' ).value = dimensions.width;\r
-                       $PR( 'height' ).value = dimensions.height;\r
-               }\r
-               \r
-               // basic example\r
-               Event.observe( \r
-                       window, \r
-                       'load', \r
-                       function() { \r
-                               new Cropper.Img( \r
-                                       'testImage',\r
-                                       {\r
-                                               onEndCrop: onEndCrop \r
-                                       }\r
-                               ) \r
-                       }\r
-               );              \r
-               \r
-               \r
-               if( typeof(dump) != 'function' ) {\r
-                       Debug.init(true, '/');\r
-                       \r
-                       function dump( msg ) {\r
-                               Debug.raise( msg );\r
-                       };\r
-               } else dump( '---------------------------------------\n' );\r
-               \r
-               \r
-       </script>\r
-       <link rel="stylesheet" type="text/css" href="debug.css" media="all" />\r
-       <style type="text/css">\r
-               label { \r
-                       clear: left;\r
-                       margin-left: 50px;\r
-                       float: left;\r
-                       width: 5em;\r
-               }\r
-               \r
-               html, body { \r
-                       margin: 0;\r
-               }\r
-               \r
-               #testWrap {\r
-                       margin: 20px 0 0 50px; /* Just while testing, to make sure we return the correct positions for the image & not the window */\r
-               }\r
-       </style>\r
-</head>\r
-<body>\r
-       <h2>Basic cropper test</h2>\r
-       <p>\r
-               Some test content before the image\r
-       </p>\r
-       \r
-       <div id="testWrap">\r
-               <img src="castle.jpg" alt="test image" id="testImage" width="500" height="333" />\r
-       </div>\r
-       \r
-       \r
-       <p>\r
-               <label for="x1">x1:</label>\r
-               <input type="text" name="x1" id="x1" />\r
-       </p>\r
-       <p>\r
-               <label for="y1">y1:</label>\r
-               <input type="text" name="y1" id="y1" />\r
-       </p>\r
-       <p>\r
-               <label for="x2">x2:</label>\r
-               <input type="text" name="x2" id="x2" />\r
-       </p>\r
-       <p>\r
-               <label for="y2">y2:</label>\r
-               <input type="text" name="y2" id="y2" />\r
-       </p>\r
-       <p>\r
-               <label for="width">width:</label>\r
-               <input type="text" name="width" id="width" />\r
-       </p>\r
-       <p>\r
-               <label for="height">height</label>\r
-               <input type="text" name="height" id="height" />\r
-       </p>  \r
-       \r
-</body>\r
-</html>\r
-\r
-\r
diff --git a/library/cropper/tests/example-CSS-Absolute.htm b/library/cropper/tests/example-CSS-Absolute.htm
deleted file mode 100644 (file)
index b605fd3..0000000
+++ /dev/null
@@ -1,162 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\r
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\r
-<head>\r
-       <meta http-equiv="Content-type" content="text/html; charset=utf-8" />\r
-       <meta http-equiv="Content-Language" content="en-us" />\r
-       <title>CSS - Absolute positioned (and draggable) test</title>\r
-       <script src="../lib/prototype.js" type="text/javascript"></script>      \r
-       <script src="../lib/scriptaculous.js?load=builder,dragdrop,effects" type="text/javascript"></script>\r
-       <script src="../cropper.js" type="text/javascript"></script>\r
-       \r
-       \r
-       <script type="text/javascript" charset="utf-8">\r
-               \r
-               // setup the callback function\r
-               function onEndCrop( coords, dimensions ) {\r
-                       $PR( 'x1' ).value = coords.x1;\r
-                       $PR( 'y1' ).value = coords.y1;\r
-                       $PR( 'x2' ).value = coords.x2;\r
-                       $PR( 'y2' ).value = coords.y2;\r
-                       $PR( 'width' ).value = dimensions.width;\r
-                       $PR( 'height' ).value = dimensions.height;\r
-               }\r
-                               \r
-               // Absolute positioned example\r
-               Event.observe(\r
-                       window,\r
-                       'load',\r
-                       function() {\r
-                               new Cropper.Img( 'testAbsImage', { onEndCrop: onEndCrop } );\r
-                               new Draggable( 'test-abs' );\r
-                       }\r
-               );              \r
-               \r
-               if( typeof(dump) != 'function' ) {\r
-                       Debug.init(true, '/');\r
-                       \r
-                       function dump( msg ) {\r
-                               Debug.raise( msg );\r
-                       };\r
-               } else dump( '---------------------------------------\n' );\r
-               \r
-               \r
-       </script>\r
-       <link rel="stylesheet" type="text/css" href="debug.css" media="all" />\r
-       <style type="text/css">\r
-               label { \r
-                       clear: left;\r
-                       margin-left: 50px;\r
-                       float: left;\r
-                       width: 5em;\r
-               }\r
-               \r
-               html, body { \r
-                       margin: 0;\r
-               }\r
-               \r
-               #testWrap {\r
-                       margin: 20px 0 0 50px; /* Just while testing, to make sure we return the correct positions for the image & not the window */\r
-               }\r
-               \r
-               #test-abs {\r
-                       width: 510px;\r
-                       position: absolute;\r
-                       top: 50px;\r
-                       left: 25%;\r
-                       background-color: #dee;\r
-                       border: 3px solid #ccc;\r
-                       z-index: 10;\r
-               }\r
-       </style>\r
-</head>\r
-<body>\r
-       <h2>CSS - Absolute positioned (and draggable) test</h2>\r
-       <p>\r
-               Some test content before the image\r
-       </p>\r
-       <p>\r
-       Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Pellentesque consequat risus cursus ipsum. Etiam libero. Integer vel mauris. Donec vulputate. In ut augue vitae nibh lobortis tempor. Aliquam hendrerit quam. Phasellus sed orci. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Ut sed urna. Donec nunc urna, porttitor a, feugiat pellentesque, varius id, justo. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla facilisi. Sed sollicitudin. Integer enim. Aenean sollicitudin.\r
-       </p>\r
-       <p>\r
-       Integer lorem turpis, dapibus sed, vulputate nec, volutpat a, sem. Sed malesuada laoreet lorem. Duis mauris ipsum, fringilla nec, tristique vel, imperdiet vel, neque. Nulla vel purus. Fusce non lectus. Mauris pulvinar. Curabitur eget eros. Nunc ultrices, risus vitae adipiscing scelerisque, quam mi auctor lacus, non pellentesque augue sapien a magna. Etiam rutrum posuere tortor. Mauris rhoncus sagittis dolor. Donec sed quam. Vivamus vel diam id massa adipiscing bibendum. Suspendisse potenti. Integer arcu est, adipiscing sit amet, convallis eu, sollicitudin tincidunt, quam.\r
-       </p>\r
-       <p>\r
-       Etiam ligula lorem, imperdiet ac, luctus eget, ultrices at, odio. Vivamus malesuada, justo eu adipiscing semper, nisi dui tempus magna, quis ultrices nunc tellus id massa. Nullam lobortis auctor sapien. Quisque non nulla. Donec lobortis pellentesque nisl. Sed lacus sapien, viverra vitae, blandit ut, fermentum quis, leo. Morbi augue turpis, hendrerit non, feugiat vel, laoreet sed, est. Nunc velit. Praesent lobortis. Integer enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Curabitur faucibus lacus ac ante. Donec odio odio, tincidunt a, egestas nec, scelerisque nec, dui. Cras sollicitudin. Donec lacus enim, mollis sit amet, interdum quis, euismod et, nulla. Nunc sit amet dui eu magna dapibus mollis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nulla facilisi.\r
-       </p>\r
-       <p>\r
-       In hac habitasse platea dictumst. Nunc neque urna, dapibus ut, tristique ut, bibendum ac, felis. Donec dictum est ut dolor. Etiam accumsan, velit sit amet blandit vestibulum, turpis quam hendrerit risus, vel interdum eros orci in nunc. Curabitur tellus sapien, rutrum ac, euismod ac, malesuada nec, pede. Proin sit amet ipsum. Praesent quam nisl, adipiscing nec, tristique eget, fermentum sed, est. Praesent ac est sit amet orci facilisis placerat. Sed consequat, est sit amet consectetuer viverra, risus urna porttitor tellus, ut convallis nibh libero in lectus. Pellentesque molestie, erat non vehicula pretium, turpis nisi eleifend eros, sed scelerisque tortor odio non tellus. Nunc leo tellus, faucibus vitae, placerat a, accumsan vel, arcu. In et orci. Ut tristique euismod nibh. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Sed nulla nunc, placerat vitae, pellentesque non, interdum non, sapien. Quisque faucibus, eros sed venenatis sagittis, leo risus rhoncus risus, in pretium sem purus a lacus. Aliquam aliquam leo et diam.\r
-       \r
-       </p>\r
-       <p>\r
-       Nulla sagittis diam. Phasellus vitae enim tristique libero molestie tristique. Nam mauris sem, elementum nec, cursus in, fringilla ac, neque. Nunc metus nisi, dictum vel, vulputate quis, porttitor bibendum, tortor. Vestibulum vehicula. Nulla facilisi. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla ac magna sed purus ultricies euismod. Aliquam dictum. Sed mauris. Suspendisse justo. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Morbi purus lorem, auctor non, porta ac, vehicula vel, orci. Morbi pharetra massa nec leo. Maecenas et mauris. Aliquam porttitor tincidunt nulla. Vestibulum pede.\r
-       </p>\r
-       <p>\r
-       Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Pellentesque consequat risus cursus ipsum. Etiam libero. Integer vel mauris. Donec vulputate. In ut augue vitae nibh lobortis tempor. Aliquam hendrerit quam. Phasellus sed orci. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Ut sed urna. Donec nunc urna, porttitor a, feugiat pellentesque, varius id, justo. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla facilisi. Sed sollicitudin. Integer enim. Aenean sollicitudin.\r
-       </p>\r
-       <p>\r
-       Integer lorem turpis, dapibus sed, vulputate nec, volutpat a, sem. Sed malesuada laoreet lorem. Duis mauris ipsum, fringilla nec, tristique vel, imperdiet vel, neque. Nulla vel purus. Fusce non lectus. Mauris pulvinar. Curabitur eget eros. Nunc ultrices, risus vitae adipiscing scelerisque, quam mi auctor lacus, non pellentesque augue sapien a magna. Etiam rutrum posuere tortor. Mauris rhoncus sagittis dolor. Donec sed quam. Vivamus vel diam id massa adipiscing bibendum. Suspendisse potenti. Integer arcu est, adipiscing sit amet, convallis eu, sollicitudin tincidunt, quam.\r
-       </p>\r
-       <p>\r
-       Etiam ligula lorem, imperdiet ac, luctus eget, ultrices at, odio. Vivamus malesuada, justo eu adipiscing semper, nisi dui tempus magna, quis ultrices nunc tellus id massa. Nullam lobortis auctor sapien. Quisque non nulla. Donec lobortis pellentesque nisl. Sed lacus sapien, viverra vitae, blandit ut, fermentum quis, leo. Morbi augue turpis, hendrerit non, feugiat vel, laoreet sed, est. Nunc velit. Praesent lobortis. Integer enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Curabitur faucibus lacus ac ante. Donec odio odio, tincidunt a, egestas nec, scelerisque nec, dui. Cras sollicitudin. Donec lacus enim, mollis sit amet, interdum quis, euismod et, nulla. Nunc sit amet dui eu magna dapibus mollis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nulla facilisi.\r
-       </p>\r
-       <p>\r
-       In hac habitasse platea dictumst. Nunc neque urna, dapibus ut, tristique ut, bibendum ac, felis. Donec dictum est ut dolor. Etiam accumsan, velit sit amet blandit vestibulum, turpis quam hendrerit risus, vel interdum eros orci in nunc. Curabitur tellus sapien, rutrum ac, euismod ac, malesuada nec, pede. Proin sit amet ipsum. Praesent quam nisl, adipiscing nec, tristique eget, fermentum sed, est. Praesent ac est sit amet orci facilisis placerat. Sed consequat, est sit amet consectetuer viverra, risus urna porttitor tellus, ut convallis nibh libero in lectus. Pellentesque molestie, erat non vehicula pretium, turpis nisi eleifend eros, sed scelerisque tortor odio non tellus. Nunc leo tellus, faucibus vitae, placerat a, accumsan vel, arcu. In et orci. Ut tristique euismod nibh. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Sed nulla nunc, placerat vitae, pellentesque non, interdum non, sapien. Quisque faucibus, eros sed venenatis sagittis, leo risus rhoncus risus, in pretium sem purus a lacus. Aliquam aliquam leo et diam.\r
-       \r
-       </p>\r
-       <p>\r
-       Nulla sagittis diam. Phasellus vitae enim tristique libero molestie tristique. Nam mauris sem, elementum nec, cursus in, fringilla ac, neque. Nunc metus nisi, dictum vel, vulputate quis, porttitor bibendum, tortor. Vestibulum vehicula. Nulla facilisi. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla ac magna sed purus ultricies euismod. Aliquam dictum. Sed mauris. Suspendisse justo. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Morbi purus lorem, auctor non, porta ac, vehicula vel, orci. Morbi pharetra massa nec leo. Maecenas et mauris. Aliquam porttitor tincidunt nulla. Vestibulum pede.\r
-       </p>\r
-       <p>\r
-       Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Pellentesque consequat risus cursus ipsum. Etiam libero. Integer vel mauris. Donec vulputate. In ut augue vitae nibh lobortis tempor. Aliquam hendrerit quam. Phasellus sed orci. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Ut sed urna. Donec nunc urna, porttitor a, feugiat pellentesque, varius id, justo. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla facilisi. Sed sollicitudin. Integer enim. Aenean sollicitudin.\r
-       </p>\r
-       <p>\r
-       Integer lorem turpis, dapibus sed, vulputate nec, volutpat a, sem. Sed malesuada laoreet lorem. Duis mauris ipsum, fringilla nec, tristique vel, imperdiet vel, neque. Nulla vel purus. Fusce non lectus. Mauris pulvinar. Curabitur eget eros. Nunc ultrices, risus vitae adipiscing scelerisque, quam mi auctor lacus, non pellentesque augue sapien a magna. Etiam rutrum posuere tortor. Mauris rhoncus sagittis dolor. Donec sed quam. Vivamus vel diam id massa adipiscing bibendum. Suspendisse potenti. Integer arcu est, adipiscing sit amet, convallis eu, sollicitudin tincidunt, quam.\r
-       </p>\r
-       <p>\r
-       Etiam ligula lorem, imperdiet ac, luctus eget, ultrices at, odio. Vivamus malesuada, justo eu adipiscing semper, nisi dui tempus magna, quis ultrices nunc tellus id massa. Nullam lobortis auctor sapien. Quisque non nulla. Donec lobortis pellentesque nisl. Sed lacus sapien, viverra vitae, blandit ut, fermentum quis, leo. Morbi augue turpis, hendrerit non, feugiat vel, laoreet sed, est. Nunc velit. Praesent lobortis. Integer enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Curabitur faucibus lacus ac ante. Donec odio odio, tincidunt a, egestas nec, scelerisque nec, dui. Cras sollicitudin. Donec lacus enim, mollis sit amet, interdum quis, euismod et, nulla. Nunc sit amet dui eu magna dapibus mollis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nulla facilisi.\r
-       </p>\r
-       <p>\r
-       In hac habitasse platea dictumst. Nunc neque urna, dapibus ut, tristique ut, bibendum ac, felis. Donec dictum est ut dolor. Etiam accumsan, velit sit amet blandit vestibulum, turpis quam hendrerit risus, vel interdum eros orci in nunc. Curabitur tellus sapien, rutrum ac, euismod ac, malesuada nec, pede. Proin sit amet ipsum. Praesent quam nisl, adipiscing nec, tristique eget, fermentum sed, est. Praesent ac est sit amet orci facilisis placerat. Sed consequat, est sit amet consectetuer viverra, risus urna porttitor tellus, ut convallis nibh libero in lectus. Pellentesque molestie, erat non vehicula pretium, turpis nisi eleifend eros, sed scelerisque tortor odio non tellus. Nunc leo tellus, faucibus vitae, placerat a, accumsan vel, arcu. In et orci. Ut tristique euismod nibh. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Sed nulla nunc, placerat vitae, pellentesque non, interdum non, sapien. Quisque faucibus, eros sed venenatis sagittis, leo risus rhoncus risus, in pretium sem purus a lacus. Aliquam aliquam leo et diam.\r
-       \r
-       </p>\r
-       <p>\r
-       Nulla sagittis diam. Phasellus vitae enim tristique libero molestie tristique. Nam mauris sem, elementum nec, cursus in, fringilla ac, neque. Nunc metus nisi, dictum vel, vulputate quis, porttitor bibendum, tortor. Vestibulum vehicula. Nulla facilisi. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla ac magna sed purus ultricies euismod. Aliquam dictum. Sed mauris. Suspendisse justo. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Morbi purus lorem, auctor non, porta ac, vehicula vel, orci. Morbi pharetra massa nec leo. Maecenas et mauris. Aliquam porttitor tincidunt nulla. Vestibulum pede.\r
-       </p>\r
-\r
-\r
-<div id="test-abs">\r
-       <h2>Absolute test</h2>\r
-       <div id="testAbsWrap">\r
-               <img src="castle.jpg" alt="test image" id="testAbsImage" width="500" height="333" />\r
-       </div>\r
-       \r
-       <p>\r
-               <label for="x1">x1:</label>\r
-               <input type="text" name="x1" id="x1" />\r
-       </p>\r
-       <p>\r
-               <label for="y1">y1:</label>\r
-               <input type="text" name="y1" id="y1" />\r
-       </p>\r
-       <p>\r
-               <label for="x2">x2:</label>\r
-               <input type="text" name="x2" id="x2" />\r
-       </p>\r
-       <p>\r
-               <label for="y2">y2:</label>\r
-               <input type="text" name="y2" id="y2" />\r
-       </p>\r
-       <p>\r
-               <label for="width">width:</label>\r
-               <input type="text" name="width" id="width" />\r
-       </p>\r
-       <p>\r
-               <label for="height">height</label>\r
-               <input type="text" name="height" id="height" />\r
-       </p>  \r
-</div>\r
-\r
-</body>\r
-</html>\r
-\r
-\r
diff --git a/library/cropper/tests/example-CSS-Float.htm b/library/cropper/tests/example-CSS-Float.htm
deleted file mode 100644 (file)
index 3dbeeab..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\r
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\r
-<head>\r
-       <meta http-equiv="Content-type" content="text/html; charset=utf-8" />\r
-       <meta http-equiv="Content-Language" content="en-us" />\r
-       <title>CSS - Float test</title>\r
-       <script src="../lib/prototype.js" type="text/javascript"></script>      \r
-       <script src="../lib/scriptaculous.js?load=builder,dragdrop" type="text/javascript"></script>\r
-       <script src="../cropper.js" type="text/javascript"></script>\r
-       \r
-       \r
-       <script type="text/javascript" charset="utf-8">\r
-               \r
-               // setup the callback function\r
-               function onEndCrop( coords, dimensions ) {\r
-                       $PR( 'x1' ).value = coords.x1;\r
-                       $PR( 'y1' ).value = coords.y1;\r
-                       $PR( 'x2' ).value = coords.x2;\r
-                       $PR( 'y2' ).value = coords.y2;\r
-                       $PR( 'width' ).value = dimensions.width;\r
-                       $PR( 'height' ).value = dimensions.height;\r
-               }\r
-               \r
-               // float example\r
-               Event.observe(\r
-                       window,\r
-                       'load',\r
-                       function() {\r
-                               new Cropper.Img(\r
-                                       'testFloatImage',\r
-                                       { \r
-                                               onEndCrop: function( coords, dimensions ) {\r
-                                                       $PR( 'floatX1' ).value = coords.x1;\r
-                                                       $PR( 'floatY1' ).value = coords.y1;\r
-                                                       $PR( 'floatX2' ).value = coords.x2;\r
-                                                       $PR( 'floatY2' ).value = coords.y2;\r
-                                                       $PR( 'floatWidth' ).value = dimensions.width;\r
-                                                       $PR( 'floatHeight' ).value = dimensions.height;\r
-                                               }\r
-                                       }\r
-                               );\r
-                       }\r
-               );              \r
-               \r
-               if( typeof(dump) != 'function' ) {\r
-                       Debug.init(true, '/');\r
-                       \r
-                       function dump( msg ) {\r
-                               Debug.raise( msg );\r
-                       };\r
-               } else dump( '---------------------------------------\n' );\r
-               \r
-               \r
-       </script>\r
-       <link rel="stylesheet" type="text/css" href="debug.css" media="all" />\r
-       <style type="text/css">\r
-               label { \r
-                       clear: left;\r
-                       margin-left: 50px;\r
-                       float: left;\r
-                       width: 5em;\r
-               }\r
-               \r
-               html, body { \r
-                       margin: 0;\r
-               }\r
-               \r
-               #testWrap {\r
-                       margin: 20px 0 0 50px; /* Just while testing, to make sure we return the correct positions for the image & not the window */\r
-               }\r
-               \r
-               #test-float {\r
-                       width: 600px;\r
-                       float: right;\r
-                       background-color: #eee;\r
-                       border: 3px solid #000;\r
-                       margin: 10px;\r
-                       padding: 5px;\r
-               }\r
-\r
-       </style>\r
-</head>\r
-<body>\r
-       <h2>Test page with floating wrapper</h2>\r
-       <p>\r
-               Some test content before the image\r
-       </p>\r
-\r
-<div id="test-float">\r
-       <h2>Float test</h2>\r
-       <div id="testFloatWrap">\r
-               <img src="castle.jpg" alt="test image" id="testFloatImage" width="500" height="333" />\r
-       </div>\r
-       \r
-       <p>\r
-               <label for="floatX1">x1:</label>\r
-               <input type="text" name="floatX1" id="floatX1" />\r
-       </p>\r
-       <p>\r
-               <label for="floatY1">y1:</label>\r
-               <input type="text" name="floatY1" id="floatY1" />\r
-       </p>\r
-       <p>\r
-               <label for="floatX2">x2:</label>\r
-               <input type="text" name="floatX2" id="floatX2" />\r
-       </p>\r
-       <p>\r
-               <label for="floatY2">y2:</label>\r
-               <input type="text" name="floatY2" id="floatY2" />\r
-       </p>\r
-       <p>\r
-               <label for="floatWidth">width:</label>\r
-               <input type="text" name="floatWidth" id="floatWidth" />\r
-       </p>\r
-       <p>\r
-               <label for="floatHeight">height</label>\r
-               <input type="text" name="floatHeight" id="floatHeight" />\r
-       </p>  \r
-</div>\r
-\r
-</body>\r
-</html>\r
-\r
-\r
diff --git a/library/cropper/tests/example-CSS-Relative.htm b/library/cropper/tests/example-CSS-Relative.htm
deleted file mode 100644 (file)
index ecad134..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\r
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\r
-<head>\r
-       <meta http-equiv="Content-type" content="text/html; charset=utf-8" />\r
-       <meta http-equiv="Content-Language" content="en-us" />\r
-       <title>CSS - Relative test</title>\r
-       <script src="../lib/prototype.js" type="text/javascript"></script>      \r
-       <script src="../lib/scriptaculous.js?load=builder,dragdrop" type="text/javascript"></script>\r
-       <script src="../cropper.js" type="text/javascript"></script>\r
-       \r
-       \r
-       <script type="text/javascript" charset="utf-8">\r
-               \r
-               // setup the callback function\r
-               function onEndCrop( coords, dimensions ) {\r
-                       $PR( 'x1' ).value = coords.x1;\r
-                       $PR( 'y1' ).value = coords.y1;\r
-                       $PR( 'x2' ).value = coords.x2;\r
-                       $PR( 'y2' ).value = coords.y2;\r
-                       $PR( 'width' ).value = dimensions.width;\r
-                       $PR( 'height' ).value = dimensions.height;\r
-               }\r
-               \r
-               // relative example\r
-               Event.observe( \r
-                       window, \r
-                       'load', \r
-                       function() { \r
-                               new Cropper.Img( \r
-                                       'testImage',\r
-                                       {\r
-                                               onEndCrop: onEndCrop \r
-                                       }\r
-                               ) \r
-                       }\r
-               );      \r
-                               \r
-               if( typeof(dump) != 'function' ) {\r
-                       Debug.init(true, '/');\r
-                       \r
-                       function dump( msg ) {\r
-                               Debug.raise( msg );\r
-                       };\r
-               } else dump( '---------------------------------------\n' );\r
-               \r
-               \r
-       </script>\r
-       <link rel="stylesheet" type="text/css" href="debug.css" media="all" />\r
-       <style type="text/css">\r
-               label { \r
-                       clear: left;\r
-                       margin-left: 50px;\r
-                       float: left;\r
-                       width: 5em;\r
-               }\r
-               \r
-               html, body { \r
-                       margin: 0;\r
-               }\r
-               \r
-               #testWrap {\r
-                       margin: 20px 0 0 50px; /* Just while testing, to make sure we return the correct positions for the image & not the window */\r
-               }\r
-               \r
-               #test-relative {\r
-                       background-color: #ccc;\r
-                       border: 3px solid #ddd;\r
-                       position: relative;\r
-                       top: 25px;\r
-                       left: 25px;\r
-               }\r
-       </style>\r
-</head>\r
-<body>\r
-       <h2>Test page with relatively positioned wrapper</h2>\r
-       <p>\r
-               Some test content before the image\r
-       </p>\r
-\r
-<div id="test-relative">\r
-       <h2>Relative test</h2>\r
-       <div id="testWrap">\r
-               <img src="castle.jpg" alt="test image" id="testImage" width="500" height="333" />\r
-       </div>\r
-       \r
-       \r
-       <p>\r
-               <label for="x1">x1:</label>\r
-               <input type="text" name="x1" id="x1" />\r
-       </p>\r
-       <p>\r
-               <label for="y1">y1:</label>\r
-               <input type="text" name="y1" id="y1" />\r
-       </p>\r
-       <p>\r
-               <label for="x2">x2:</label>\r
-               <input type="text" name="x2" id="x2" />\r
-       </p>\r
-       <p>\r
-               <label for="y2">y2:</label>\r
-               <input type="text" name="y2" id="y2" />\r
-       </p>\r
-       <p>\r
-               <label for="width">width:</label>\r
-               <input type="text" name="width" id="width" />\r
-       </p>\r
-       <p>\r
-               <label for="height">height</label>\r
-               <input type="text" name="height" id="height" />\r
-       </p>  \r
-</div> \r
-\r
-</body>\r
-</html>\r
-\r
-\r
diff --git a/library/cropper/tests/example-CoordsOnLoad.htm b/library/cropper/tests/example-CoordsOnLoad.htm
deleted file mode 100644 (file)
index c14289c..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\r
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\r
-<head>\r
-       <meta http-equiv="Content-type" content="text/html; charset=utf-8" />\r
-       <meta http-equiv="Content-Language" content="en-us" />\r
-       <title>Loading &amp; displaying co-ordinates of crop area on attachment test</title>\r
-       <script src="../lib/prototype.js" type="text/javascript"></script>      \r
-       <script src="../lib/scriptaculous.js?load=builder,dragdrop" type="text/javascript"></script>\r
-       <script src="../cropper.js" type="text/javascript"></script>\r
-       \r
-       \r
-       <script type="text/javascript" charset="utf-8">\r
-               \r
-               // setup the callback function\r
-               function onEndCrop( coords, dimensions ) {\r
-                       $PR( 'x1' ).value = coords.x1;\r
-                       $PR( 'y1' ).value = coords.y1;\r
-                       $PR( 'x2' ).value = coords.x2;\r
-                       $PR( 'y2' ).value = coords.y2;\r
-                       $PR( 'width' ).value = dimensions.width;\r
-                       $PR( 'height' ).value = dimensions.height;\r
-               }\r
-               \r
-               // basic example\r
-               Event.observe( \r
-                       window, \r
-                       'load', \r
-                       function() { \r
-                               new Cropper.Img( \r
-                                       'testImage',\r
-                                       {\r
-                                               onEndCrop: onEndCrop,\r
-                                               displayOnInit: true,\r
-                                               onloadCoords: { x1: 10, y1: 10, x2: 250, y2: 100 }\r
-                                       }\r
-                               ) \r
-                       }\r
-               );              \r
-               \r
-               \r
-               if( typeof(dump) != 'function' ) {\r
-                       Debug.init(true, '/');\r
-                       \r
-                       function dump( msg ) {\r
-                               Debug.raise( msg );\r
-                       };\r
-               } else dump( '---------------------------------------\n' );\r
-               \r
-               \r
-       </script>\r
-       <link rel="stylesheet" type="text/css" href="debug.css" media="all" />\r
-       <style type="text/css">\r
-               label { \r
-                       clear: left;\r
-                       margin-left: 50px;\r
-                       float: left;\r
-                       width: 5em;\r
-               }\r
-               \r
-               html, body { \r
-                       margin: 0;\r
-               }\r
-               \r
-               #testWrap {\r
-                       margin: 20px 0 0 50px; /* Just while testing, to make sure we return the correct positions for the image & not the window */\r
-               }\r
-       </style>\r
-</head>\r
-<body>\r
-       <h2>Loading &amp; displaying co-ordinates of crop area on attachment test</h2>\r
-       <p>\r
-               Some test content before the image\r
-       </p>\r
-       \r
-       <div id="testWrap">\r
-               <img src="castle.jpg" alt="test image" id="testImage" width="500" height="333" />\r
-       </div>\r
-       \r
-       \r
-       <p>\r
-               <label for="x1">x1:</label>\r
-               <input type="text" name="x1" id="x1" />\r
-       </p>\r
-       <p>\r
-               <label for="y1">y1:</label>\r
-               <input type="text" name="y1" id="y1" />\r
-       </p>\r
-       <p>\r
-               <label for="x2">x2:</label>\r
-               <input type="text" name="x2" id="x2" />\r
-       </p>\r
-       <p>\r
-               <label for="y2">y2:</label>\r
-               <input type="text" name="y2" id="y2" />\r
-       </p>\r
-       <p>\r
-               <label for="width">width:</label>\r
-               <input type="text" name="width" id="width" />\r
-       </p>\r
-       <p>\r
-               <label for="height">height</label>\r
-               <input type="text" name="height" id="height" />\r
-       </p>  \r
-       \r
-</body>\r
-</html>\r
-\r
-\r
diff --git a/library/cropper/tests/example-CoordsOnLoadWithRatio.htm b/library/cropper/tests/example-CoordsOnLoadWithRatio.htm
deleted file mode 100644 (file)
index 9ba02da..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\r
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\r
-<head>\r
-       <meta http-equiv="Content-type" content="text/html; charset=utf-8" />\r
-       <meta http-equiv="Content-Language" content="en-us" />\r
-       <title>Loading &amp; displaying co-ordinates (with ratio) of crop area on attachment test<</title>\r
-       <script src="../lib/prototype.js" type="text/javascript"></script>      \r
-       <script src="../lib/scriptaculous.js?load=builder,dragdrop" type="text/javascript"></script>\r
-       <script src="../cropper.js" type="text/javascript"></script>\r
-       \r
-       \r
-       <script type="text/javascript" charset="utf-8">\r
-               \r
-               // setup the callback function\r
-               function onEndCrop( coords, dimensions ) {\r
-                       $PR( 'x1' ).value = coords.x1;\r
-                       $PR( 'y1' ).value = coords.y1;\r
-                       $PR( 'x2' ).value = coords.x2;\r
-                       $PR( 'y2' ).value = coords.y2;\r
-                       $PR( 'width' ).value = dimensions.width;\r
-                       $PR( 'height' ).value = dimensions.height;\r
-               }\r
-               \r
-               // basic example\r
-               Event.observe( \r
-                       window, \r
-                       'load', \r
-                       function() { \r
-                               new Cropper.Img( \r
-                                       'testImage',\r
-                                       {\r
-                                               onEndCrop: onEndCrop,\r
-                                               displayOnInit: true,\r
-                                               onloadCoords: { x1: 10, y1: 10, x2: 210, y2: 110 },\r
-                                               ratioDim: { x: 200, y: 100 }\r
-                                       }\r
-                               ) \r
-                       }\r
-               );              \r
-               \r
-               \r
-               if( typeof(dump) != 'function' ) {\r
-                       Debug.init(true, '/');\r
-                       \r
-                       function dump( msg ) {\r
-                               Debug.raise( msg );\r
-                       };\r
-               } else dump( '---------------------------------------\n' );\r
-               \r
-               \r
-       </script>\r
-       <link rel="stylesheet" type="text/css" href="debug.css" media="all" />\r
-       <style type="text/css">\r
-               label { \r
-                       clear: left;\r
-                       margin-left: 50px;\r
-                       float: left;\r
-                       width: 5em;\r
-               }\r
-               \r
-               html, body { \r
-                       margin: 0;\r
-               }\r
-               \r
-               #testWrap {\r
-                       margin: 20px 0 0 50px; /* Just while testing, to make sure we return the correct positions for the image & not the window */\r
-               }\r
-       </style>\r
-</head>\r
-<body>\r
-       <h2>Loading &amp; displaying co-ordinates (with ratio) of crop area on attachment test</h2>\r
-       <p>\r
-               Some test content before the image\r
-       </p>\r
-       \r
-       <div id="testWrap">\r
-               <img src="castle.jpg" alt="test image" id="testImage" width="500" height="333" />\r
-       </div>\r
-       \r
-       \r
-       <p>\r
-               <label for="x1">x1:</label>\r
-               <input type="text" name="x1" id="x1" />\r
-       </p>\r
-       <p>\r
-               <label for="y1">y1:</label>\r
-               <input type="text" name="y1" id="y1" />\r
-       </p>\r
-       <p>\r
-               <label for="x2">x2:</label>\r
-               <input type="text" name="x2" id="x2" />\r
-       </p>\r
-       <p>\r
-               <label for="y2">y2:</label>\r
-               <input type="text" name="y2" id="y2" />\r
-       </p>\r
-       <p>\r
-               <label for="width">width:</label>\r
-               <input type="text" name="width" id="width" />\r
-       </p>\r
-       <p>\r
-               <label for="height">height</label>\r
-               <input type="text" name="height" id="height" />\r
-       </p>  \r
-       \r
-</body>\r
-</html>\r
-\r
-\r
diff --git a/library/cropper/tests/example-Dimensions.htm b/library/cropper/tests/example-Dimensions.htm
deleted file mode 100644 (file)
index 10e5ba2..0000000
+++ /dev/null
@@ -1,225 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-<head>
-       <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
-       <meta http-equiv="Content-Language" content="en-us" />
-       <title>Different dimensions test</title>
-       <script src="../lib/prototype.js" type="text/javascript"></script>      
-       <script src="../lib/scriptaculous.js?load=builder,dragdrop" type="text/javascript"></script>
-       <script src="../cropper.js" type="text/javascript"></script>
-       
-       
-       <script type="text/javascript" charset="utf-8">
-               
-               function onEndCrop( coords, dimensions ) {
-                       $PR( 'x1' ).value = coords.x1;
-                       $PR( 'y1' ).value = coords.y1;
-                       $PR( 'x2' ).value = coords.x2;
-                       $PR( 'y2' ).value = coords.y2;
-                       $PR( 'width' ).value = dimensions.width;
-                       $PR( 'height' ).value = dimensions.height;
-               }
-               
-               /*
-               
-               // example with minimum dimensions
-               Event.observe( 
-                       window, 
-                       'load', 
-                       function() { 
-                               new Cropper.Img( 
-                                       'testImage', 
-                                       { 
-                                               minWidth: 200, 
-                                               minHeight: 120,
-                                               maxWidth: 200,
-                                               //maxHeight: 120,
-                                               displayOnInit: true, 
-                                               onEndCrop: onEndCrop 
-                                       } 
-                               ) 
-                       } 
-               );
-               */
-               
-               Event.observe( window, 'load',
-                       function() {
-                               Event.observe( 'dimensionsForm', 'submit', CropManager.attachCropper.bindAsEventListener( CropManager ) );
-                               CropManager.attachCropper();
-                       }
-               );
-               
-               /**
-                * A little manager that allows us to reset the options dynamically
-                */
-               var CropManager = {
-                       /**
-                        * Holds the current Cropper.Img object
-                        * @var obj
-                        */
-                       curCrop: null,
-                       
-                       /**
-                        * Gets a min/max parameter from the form 
-                        * 
-                        * @access private
-                        * @param string Form element ID
-                        * @return int
-                        */
-                       getParam: function( name ) {
-                               var val = $F( name );
-                               console.log( name + ' :: ' + val );
-                               return parseInt( val );
-                       },
-                                                                       
-                       /** 
-                        * Attaches/resets the image cropper
-                        *
-                        * @access private
-                        * @param obj Event object
-                        * @return void
-                        */
-                       attachCropper: function( e ) {
-                               if( this.curCrop == null ) {
-                                       this.curCrop = new Cropper.Img( 
-                                               'testImage', 
-                                               { 
-                                                       minWidth: this.getParam( 'minWidth' ),
-                                                       minHeight: this.getParam( 'minHeight' ),
-                                                       maxWidth: this.getParam( 'maxWidth' ),
-                                                       maxHeight: this.getParam( 'maxHeight' ),
-                                                       onEndCrop: onEndCrop 
-                                               } 
-                                       );
-                               } else {
-                                       this.removeCropper();
-                                       this.curCrop.initialize( 
-                                               'testImage', 
-                                               { 
-                                                       minWidth: this.getParam( 'minWidth' ),
-                                                       minHeight: this.getParam( 'minHeight' ),
-                                                       maxWidth: this.getParam( 'maxWidth' ),
-                                                       maxHeight: this.getParam( 'maxHeight' ),
-                                                       onEndCrop: onEndCrop 
-                                               } 
-                                       );
-                               }
-                               if( e != null ) Event.stop( e );
-                       },
-                       
-                       /**
-                        * Removes the cropper
-                        *
-                        * @access public
-                        * @return void
-                        */
-                       removeCropper: function() {
-                               if( this.curCrop != null ) {
-                                       this.curCrop.remove();
-                               }
-                       },
-                       
-                       /**
-                        * Resets the cropper, either re-setting or re-applying
-                        *
-                        * @access public
-                        * @return void
-                        */
-                       resetCropper: function() {
-                               this.attachCropper();
-                       }
-               };              
-               
-               /*
-               if( typeof(dump) != 'function' ) {
-                       Debug.init(true, '/');
-                       
-                       function dump( msg ) {
-                               // Debug.raise( msg );
-                       };
-               } else dump( '---------------------------------------\n' );
-               */
-               
-       </script>
-       <link rel="stylesheet" type="text/css" href="debug.css" media="all" />
-       <style type="text/css">
-               label { 
-                       clear: left;
-                       margin-left: 50px;
-                       float: left;
-                       width: 5em;
-               }
-               
-               #testWrap {
-                       margin: 20px 0 0 50px; /* Just while testing, to make sure we return the correct positions for the image & not the window */
-               }
-               
-               #dimensionsForm {
-                       float: right;
-                       width: 350px;
-               }
-       </style>
-</head>
-<body> 
-       <h2>Multiple dimensions tests</h2>
-       <p>
-               Test of applying different dimension restrictions to the cropper
-       </p>
-       
-       <form action="#" id="dimensionsForm">
-               <fieldset>
-                       Set the cropper with the following dimension restrictions:
-                       <p>
-                               <label for="minWidth">Min Width</label>
-                               <input type="text" size="10" maxlength="3" value="200" id="minWidth" name="minWidth" />
-                       </p>    
-                       <p>
-                               <label for="maxWidth">Max Width</label>
-                               <input type="text" size="10" maxlength="3" value="200" id="maxWidth" name="maxWidth" />
-                       </p>    
-                       <p>
-                               <label for="minHeight">Min Height</label>
-                               <input type="text" size="10" maxlength="3" value="120" id="minHeight" name="minHeight" />
-                       </p>    
-                       <p>
-                               <label for="maxHeight">Max Height</label>
-                               <input type="text" size="10" maxlength="3" value="120" id="maxHeight" name="maxHeight" />
-                       </p>    
-                       <input type="submit" value="Set Cropper" />
-               </fieldset>
-       </form>
-       
-       <div id="testWrap">
-               <img src="castle.jpg" alt="test image" id="testImage" width="500" height="333" />
-       </div>
-       
-       
-       <p>
-               <label for="x1">x1:</label>
-               <input type="text" name="x1" id="x1" />
-       </p>
-       <p>
-               <label for="y1">y1:</label>
-               <input type="text" name="y1" id="y1" />
-       </p>
-       <p>
-               <label for="x2">x2:</label>
-               <input type="text" name="x2" id="x2" />
-       </p>
-       <p>
-               <label for="y2">y2:</label>
-               <input type="text" name="y2" id="y2" />
-       </p>
-       <p>
-               <label for="width">width:</label>
-               <input type="text" name="width" id="width" />
-       </p>
-       <p>
-               <label for="height">height</label>
-               <input type="text" name="height" id="height" />
-       </p>  
-       
-</body>
-</html>
-
-
diff --git a/library/cropper/tests/example-DynamicImage.htm b/library/cropper/tests/example-DynamicImage.htm
deleted file mode 100644 (file)
index 08240bb..0000000
+++ /dev/null
@@ -1,203 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\r
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\r
-<head>\r
-       <meta http-equiv="Content-type" content="text/html; charset=utf-8" />\r
-       <meta http-equiv="Content-Language" content="en-us" />\r
-       <title>Dynamic image test</title>\r
-       <script src="../lib/prototype.js" type="text/javascript"></script>      \r
-       <script src="../lib/scriptaculous.js?load=builder,dragdrop" type="text/javascript"></script>\r
-       <script src="../cropper.js" type="text/javascript"></script>\r
-       \r
-       \r
-       <script type="text/javascript" charset="utf-8">\r
-               \r
-               /**\r
-                * A little manager that allows us to swap the image dynamically\r
-                *\r
-                */\r
-               var CropImageManager = {\r
-                       /**\r
-                        * Holds the current Cropper.Img object\r
-                        * @var obj\r
-                        */\r
-                       curCrop: null,\r
-                       \r
-                       /**\r
-                        * Initialises the cropImageManager\r
-                        *\r
-                        * @access public\r
-                        * @return void\r
-                        */\r
-                       init: function() {\r
-                               this.attachCropper();\r
-                       },\r
-                       \r
-                       /**\r
-                        * Handles the changing of the select to change the image, the option value\r
-                        * is a pipe seperated list of imgSrc|width|height\r
-                        * \r
-                        * @access public\r
-                        * @param obj event\r
-                        * @return void\r
-                        */\r
-                       onChange: function( e ) {\r
-                               var vals = $F( Event.element( e ) ).split('|');\r
-                               this.setImage( vals[0], vals[1], vals[2] ); \r
-                       },\r
-                       \r
-                       /**\r
-                        * Sets the image within the element & attaches/resets the image cropper\r
-                        *\r
-                        * @access private\r
-                        * @param string Source path of new image\r
-                        * @param int Width of new image in pixels\r
-                        * @param int Height of new image in pixels\r
-                        * @return void\r
-                        */\r
-                       setImage: function( imgSrc, w, h ) {\r
-                               $PR( 'testImage' ).src = imgSrc;\r
-                               $PR( 'testImage' ).width = w;\r
-                               $PR( 'testImage' ).height = h;\r
-                               this.attachCropper();\r
-                       },\r
-                       \r
-                       /** \r
-                        * Attaches/resets the image cropper\r
-                        *\r
-                        * @access private\r
-                        * @return void\r
-                        */\r
-                       attachCropper: function() {\r
-                               if( this.curCrop == null ) this.curCrop = new Cropper.Img( 'testImage', { onEndCrop: onEndCrop } );\r
-                               else this.curCrop.reset();\r
-                       },\r
-                       \r
-                       /**\r
-                        * Removes the cropper\r
-                        *\r
-                        * @access public\r
-                        * @return void\r
-                        */\r
-                       removeCropper: function() {\r
-                               if( this.curCrop != null ) {\r
-                                       this.curCrop.remove();\r
-                               }\r
-                       },\r
-                       \r
-                       /**\r
-                        * Resets the cropper, either re-setting or re-applying\r
-                        *\r
-                        * @access public\r
-                        * @return void\r
-                        */\r
-                       resetCropper: function() {\r
-                               this.attachCropper();\r
-                       }\r
-               };\r
-               \r
-               \r
-               // setup the callback function\r
-               function onEndCrop( coords, dimensions ) {\r
-                       $PR( 'x1' ).value = coords.x1;\r
-                       $PR( 'y1' ).value = coords.y1;\r
-                       $PR( 'x2' ).value = coords.x2;\r
-                       $PR( 'y2' ).value = coords.y2;\r
-                       $PR( 'width' ).value = dimensions.width;\r
-                       $PR( 'height' ).value = dimensions.height;\r
-               }\r
-               \r
-               // basic example\r
-               Event.observe( \r
-                       window, \r
-                       'load', \r
-                       function() { \r
-                               CropImageManager.init();\r
-                               Event.observe( $PR('removeCropper'), 'click', CropImageManager.removeCropper.bindAsEventListener( CropImageManager ), false );\r
-                               Event.observe( $PR('resetCropper'), 'click', CropImageManager.resetCropper.bindAsEventListener( CropImageManager ), false );\r
-                               Event.observe( $PR('imageChoice'), 'change', CropImageManager.onChange.bindAsEventListener( CropImageManager ), false );\r
-                       }\r
-               );              \r
-               \r
-               \r
-               /*\r
-               if( typeof(dump) != 'function' ) {\r
-                       Debug.init(true, '/');\r
-                       \r
-                       function dump( msg ) {\r
-                               Debug.raise( msg );\r
-                       };\r
-               } else dump( '---------------------------------------\n' );\r
-               */\r
-               \r
-       </script>\r
-       <link rel="stylesheet" type="text/css" href="debug.css" media="all" />\r
-       <style type="text/css">\r
-               label { \r
-                       clear: left;\r
-                       margin-left: 50px;\r
-                       float: left;\r
-                       width: 5em;\r
-               }\r
-               \r
-               html, body { \r
-                       margin: 0;\r
-               }\r
-               \r
-               #testWrap {\r
-                       margin: 20px 0 0 50px; /* Just while testing, to make sure we return the correct positions for the image & not the window */\r
-               }\r
-       </style>\r
-</head>\r
-<body>\r
-       <h2>Dynamic image test</h2>\r
-       <p>\r
-               Test of dynamically changing images or removing & re-applying the cropper\r
-       </p>\r
-       \r
-       <div id="testWrap">\r
-               <img src="castle.jpg" alt="test image" id="testImage" width="500" height="333" />\r
-       </div>\r
-       \r
-       <p>\r
-               <label for="imageChoice">image:</label>\r
-               <select name="imageChoice" id="imageChoice">\r
-                       <option value="castle.jpg|500|333">Castle</option>\r
-                       <option value="poppy.jpg|311|466">Flower</option>\r
-               </select>\r
-       </p>\r
-       \r
-       <p>\r
-               <input type="button" id="removeCropper" value="Remove Cropper" />\r
-               <input type="button" id="resetCropper" value="Reset Cropper" />\r
-       </p>\r
-       \r
-       \r
-       <p>\r
-               <label for="x1">x1:</label>\r
-               <input type="text" name="x1" id="x1" />\r
-       </p>\r
-       <p>\r
-               <label for="y1">y1:</label>\r
-               <input type="text" name="y1" id="y1" />\r
-       </p>\r
-       <p>\r
-               <label for="x2">x2:</label>\r
-               <input type="text" name="x2" id="x2" />\r
-       </p>\r
-       <p>\r
-               <label for="y2">y2:</label>\r
-               <input type="text" name="y2" id="y2" />\r
-       </p>\r
-       <p>\r
-               <label for="width">width:</label>\r
-               <input type="text" name="width" id="width" />\r
-       </p>\r
-       <p>\r
-               <label for="height">height</label>\r
-               <input type="text" name="height" id="height" />\r
-       </p>  \r
-       \r
-</body>\r
-</html>\r
-\r
-\r
diff --git a/library/cropper/tests/example-FixedRatio.htm b/library/cropper/tests/example-FixedRatio.htm
deleted file mode 100644 (file)
index 8d196c1..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\r
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\r
-<head>\r
-       <meta http-equiv="Content-type" content="text/html; charset=utf-8" />\r
-       <meta http-equiv="Content-Language" content="en-us" />\r
-       <title>Fixed ratio test</title>\r
-       <script src="../lib/prototype.js" type="text/javascript"></script>      \r
-       <script src="../lib/scriptaculous.js?load=builder,dragdrop" type="text/javascript"></script>\r
-       <script src="../cropper.js" type="text/javascript"></script>\r
-       \r
-       \r
-       <script type="text/javascript" charset="utf-8">\r
-               \r
-               function onEndCrop( coords, dimensions ) {\r
-                       $PR( 'x1' ).value = coords.x1;\r
-                       $PR( 'y1' ).value = coords.y1;\r
-                       $PR( 'x2' ).value = coords.x2;\r
-                       $PR( 'y2' ).value = coords.y2;\r
-                       $PR( 'width' ).value = dimensions.width;\r
-                       $PR( 'height' ).value = dimensions.height;\r
-               }\r
-               \r
-               // with a supplied ratio\r
-               Event.observe( \r
-                       window, \r
-                       'load', \r
-                       function() { \r
-                               new Cropper.Img( \r
-                                       'testImage', \r
-                                       { \r
-                                               ratioDim: { x: 220, y: 124 }, \r
-                                               displayOnInit: true, \r
-                                               onEndCrop: onEndCrop \r
-                                       } \r
-                               ) \r
-                       } \r
-               );\r
-               \r
-               /*\r
-               if( typeof(dump) != 'function' ) {\r
-                       Debug.init(true, '/');\r
-                       \r
-                       function dump( msg ) {\r
-                               // Debug.raise( msg );\r
-                       };\r
-               } else dump( '---------------------------------------\n' );\r
-               */\r
-               \r
-       </script>\r
-       <link rel="stylesheet" type="text/css" href="debug.css" media="all" />\r
-       <style type="text/css">\r
-               label { \r
-                       clear: left;\r
-                       margin-left: 50px;\r
-                       float: left;\r
-                       width: 5em;\r
-               }\r
-               \r
-               #testWrap {\r
-                       margin: 20px 0 0 50px; /* Just while testing, to make sure we return the correct positions for the image & not the window */\r
-               }\r
-       </style>\r
-</head>\r
-<body> \r
-       <h2>Fixed ratio test</h2>\r
-       <p>\r
-               Test of applying a fixed ratio to the cropper\r
-       </p>\r
-       <br />\r
-       \r
-       <div id="testWrap">\r
-               <img src="castle.jpg" alt="test image" id="testImage" width="500" height="333" />\r
-       </div>\r
-       \r
-       \r
-       <p>\r
-               <label for="x1">x1:</label>\r
-               <input type="text" name="x1" id="x1" />\r
-       </p>\r
-       <p>\r
-               <label for="y1">y1:</label>\r
-               <input type="text" name="y1" id="y1" />\r
-       </p>\r
-       <p>\r
-               <label for="x2">x2:</label>\r
-               <input type="text" name="x2" id="x2" />\r
-       </p>\r
-       <p>\r
-               <label for="y2">y2:</label>\r
-               <input type="text" name="y2" id="y2" />\r
-       </p>\r
-       <p>\r
-               <label for="width">width:</label>\r
-               <input type="text" name="width" id="width" />\r
-       </p>\r
-       <p>\r
-               <label for="height">height</label>\r
-               <input type="text" name="height" id="height" />\r
-       </p>  \r
-       \r
-</body>\r
-</html>\r
-\r
-\r
diff --git a/library/cropper/tests/example-MinimumDimensions.htm b/library/cropper/tests/example-MinimumDimensions.htm
deleted file mode 100644 (file)
index e6d96b3..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\r
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\r
-<head>\r
-       <meta http-equiv="Content-type" content="text/html; charset=utf-8" />\r
-       <meta http-equiv="Content-Language" content="en-us" />\r
-       <title>Min dimensions test</title>\r
-       <script src="../lib/prototype.js" type="text/javascript"></script>      \r
-       <script src="../lib/scriptaculous.js?load=builder,dragdrop" type="text/javascript"></script>\r
-       <script src="../cropper.js" type="text/javascript"></script>\r
-       \r
-       \r
-       <script type="text/javascript" charset="utf-8">\r
-               \r
-               function onEndCrop( coords, dimensions ) {\r
-                       $PR( 'x1' ).value = coords.x1;\r
-                       $PR( 'y1' ).value = coords.y1;\r
-                       $PR( 'x2' ).value = coords.x2;\r
-                       $PR( 'y2' ).value = coords.y2;\r
-                       $PR( 'width' ).value = dimensions.width;\r
-                       $PR( 'height' ).value = dimensions.height;\r
-               }\r
-               \r
-               // example with minimum dimensions\r
-               Event.observe( \r
-                       window, \r
-                       'load', \r
-                       function() { \r
-                               new Cropper.Img( \r
-                                       'testImage', \r
-                                       { \r
-                                               minWidth: 200, \r
-                                               minHeight: 120, \r
-                                               displayOnInit: true, \r
-                                               onEndCrop: onEndCrop \r
-                                       } \r
-                               ) \r
-                       } \r
-               );\r
-               \r
-               /*\r
-               if( typeof(dump) != 'function' ) {\r
-                       Debug.init(true, '/');\r
-                       \r
-                       function dump( msg ) {\r
-                               // Debug.raise( msg );\r
-                       };\r
-               } else dump( '---------------------------------------\n' );\r
-               */\r
-               \r
-       </script>\r
-       <link rel="stylesheet" type="text/css" href="debug.css" media="all" />\r
-       <style type="text/css">\r
-               label { \r
-                       clear: left;\r
-                       margin-left: 50px;\r
-                       float: left;\r
-                       width: 5em;\r
-               }\r
-               \r
-               #testWrap {\r
-                       margin: 20px 0 0 50px; /* Just while testing, to make sure we return the correct positions for the image & not the window */\r
-               }\r
-       </style>\r
-</head>\r
-<body> \r
-       <h2>Minimum (both axes ) dimension test</h2>\r
-       <p>\r
-               Test of applying a minimum dimension to both axes to the cropper\r
-       </p>\r
-       <br />\r
-       \r
-       <div id="testWrap">\r
-               <img src="castle.jpg" alt="test image" id="testImage" width="500" height="333" />\r
-       </div>\r
-       \r
-       \r
-       <p>\r
-               <label for="x1">x1:</label>\r
-               <input type="text" name="x1" id="x1" />\r
-       </p>\r
-       <p>\r
-               <label for="y1">y1:</label>\r
-               <input type="text" name="y1" id="y1" />\r
-       </p>\r
-       <p>\r
-               <label for="x2">x2:</label>\r
-               <input type="text" name="x2" id="x2" />\r
-       </p>\r
-       <p>\r
-               <label for="y2">y2:</label>\r
-               <input type="text" name="y2" id="y2" />\r
-       </p>\r
-       <p>\r
-               <label for="width">width:</label>\r
-               <input type="text" name="width" id="width" />\r
-       </p>\r
-       <p>\r
-               <label for="height">height</label>\r
-               <input type="text" name="height" id="height" />\r
-       </p>  \r
-       \r
-</body>\r
-</html>\r
-\r
-\r
diff --git a/library/cropper/tests/example-MinimumWidth.htm b/library/cropper/tests/example-MinimumWidth.htm
deleted file mode 100644 (file)
index ec5d696..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\r
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\r
-<head>\r
-       <meta http-equiv="Content-type" content="text/html; charset=utf-8" />\r
-       <meta http-equiv="Content-Language" content="en-us" />\r
-       <title>Min (single axis) dimensions test</title>\r
-       <script src="../lib/prototype.js" type="text/javascript"></script>      \r
-       <script src="../lib/scriptaculous.js?load=builder,dragdrop" type="text/javascript"></script>\r
-       <script src="../cropper.js" type="text/javascript"></script>\r
-       \r
-       \r
-       <script type="text/javascript" charset="utf-8">\r
-               \r
-               function onEndCrop( coords, dimensions ) {\r
-                       $PR( 'x1' ).value = coords.x1;\r
-                       $PR( 'y1' ).value = coords.y1;\r
-                       $PR( 'x2' ).value = coords.x2;\r
-                       $PR( 'y2' ).value = coords.y2;\r
-                       $PR( 'width' ).value = dimensions.width;\r
-                       $PR( 'height' ).value = dimensions.height;\r
-               }\r
-               \r
-               // example with minimum dimensions\r
-               Event.observe( \r
-                       window, \r
-                       'load', \r
-                       function() { \r
-                               new Cropper.Img( \r
-                                       'testImage', \r
-                                       { \r
-                                               minWidth: 200, \r
-                                               displayOnInit: true, \r
-                                               onEndCrop: onEndCrop \r
-                                       } \r
-                               ) \r
-                       } \r
-               );\r
-               \r
-               /*\r
-               if( typeof(dump) != 'function' ) {\r
-                       Debug.init(true, '/');\r
-                       \r
-                       function dump( msg ) {\r
-                               // Debug.raise( msg );\r
-                       };\r
-               } else dump( '---------------------------------------\n' );\r
-               */\r
-               \r
-       </script>\r
-       <link rel="stylesheet" type="text/css" href="debug.css" media="all" />\r
-       <style type="text/css">\r
-               label { \r
-                       clear: left;\r
-                       margin-left: 50px;\r
-                       float: left;\r
-                       width: 5em;\r
-               }\r
-               \r
-               #testWrap {\r
-                       margin: 20px 0 0 50px; /* Just while testing, to make sure we return the correct positions for the image & not the window */\r
-               }\r
-       </style>\r
-</head>\r
-<body> \r
-       <h2>Minimum (single axis) dimension test</h2>\r
-       <p>\r
-               Test of applying a minimum dimension to only one axis (width in this case) to the cropper\r
-       </p>\r
-       <br />\r
-       <br /><br />\r
-       \r
-       <div id="testWrap">\r
-               <img src="castle.jpg" alt="test image" id="testImage" width="500" height="333" />\r
-       </div>\r
-       \r
-       \r
-       <p>\r
-               <label for="x1">x1:</label>\r
-               <input type="text" name="x1" id="x1" />\r
-       </p>\r
-       <p>\r
-               <label for="y1">y1:</label>\r
-               <input type="text" name="y1" id="y1" />\r
-       </p>\r
-       <p>\r
-               <label for="x2">x2:</label>\r
-               <input type="text" name="x2" id="x2" />\r
-       </p>\r
-       <p>\r
-               <label for="y2">y2:</label>\r
-               <input type="text" name="y2" id="y2" />\r
-       </p>\r
-       <p>\r
-               <label for="width">width:</label>\r
-               <input type="text" name="width" id="width" />\r
-       </p>\r
-       <p>\r
-               <label for="height">height</label>\r
-               <input type="text" name="height" id="height" />\r
-       </p>  \r
-       \r
-</body>\r
-</html>\r
-\r
-\r
diff --git a/library/cropper/tests/example-Preview.htm b/library/cropper/tests/example-Preview.htm
deleted file mode 100644 (file)
index 51bf260..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\r
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\r
-<head>\r
-       <meta http-equiv="Content-type" content="text/html; charset=utf-8" />\r
-       <meta http-equiv="Content-Language" content="en-us" />\r
-       <title></title>\r
-       <script src="../lib/prototype.js" type="text/javascript"></script>      \r
-       <script src="../lib/scriptaculous.js?load=builder,dragdrop" type="text/javascript"></script>\r
-       <script src="../cropper.js" type="text/javascript"></script>\r
-       \r
-       \r
-       <script type="text/javascript" charset="utf-8">\r
-               \r
-               function onEndCrop( coords, dimensions ) {\r
-                       $PR( 'x1' ).value = coords.x1;\r
-                       $PR( 'y1' ).value = coords.y1;\r
-                       $PR( 'x2' ).value = coords.x2;\r
-                       $PR( 'y2' ).value = coords.y2;\r
-                       $PR( 'width' ).value = dimensions.width;\r
-                       $PR( 'height' ).value = dimensions.height;\r
-               }\r
-               \r
-               // example with a preview of crop results, must have minimumm dimensions\r
-               Event.observe( \r
-                       window, \r
-                       'load', \r
-                       function() { \r
-                               new Cropper.ImgWithPreview( \r
-                                       'testImage',\r
-                                       { \r
-                                               minWidth: 200, \r
-                                               minHeight: 120,\r
-                                               ratioDim: { x: 200, y: 120 },\r
-                                               displayOnInit: true, \r
-                                               onEndCrop: onEndCrop,\r
-                                               previewWrap: 'previewArea'\r
-                                       } \r
-                               ) \r
-                       } \r
-               );\r
-               \r
-               /*\r
-               if( typeof(dump) != 'function' ) {\r
-                       Debug.init(true, '/');\r
-                       \r
-                       function dump( msg ) {\r
-                               // Debug.raise( msg );\r
-                       };\r
-               } else dump( '---------------------------------------\n' );\r
-               */\r
-               \r
-       </script>\r
-       <link rel="stylesheet" type="text/css" href="debug.css" media="all" />\r
-       <style type="text/css">\r
-               label { \r
-                       clear: left;\r
-                       margin-left: 50px;\r
-                       float: left;\r
-                       width: 5em;\r
-               }\r
-               \r
-               #testWrap {\r
-                       width: 500px;\r
-                       float: left;\r
-                       margin: 20px 0 0 50px; /* Just while testing, to make sure we return the correct positions for the image & not the window */\r
-               }\r
-               \r
-               #previewArea {\r
-                       margin: 20px; 0 0 20px;\r
-                       float: left;\r
-               }\r
-               \r
-               #results {\r
-                       clear: both;\r
-               }\r
-       </style>\r
-</head>\r
-<body> \r
-       <br /><br />\r
-       \r
-       <div id="testWrap">\r
-               <img src="castle.jpg" alt="test image" id="testImage" width="500" height="333" />\r
-       </div>\r
-       \r
-       <div id="previewArea"></div>\r
-       \r
-       <div id="results">\r
-               <p>\r
-                       <label for="x1">x1:</label>\r
-                       <input type="text" name="x1" id="x1" />\r
-               </p>\r
-               <p>\r
-                       <label for="y1">y1:</label>\r
-                       <input type="text" name="y1" id="y1" />\r
-               </p>\r
-               <p>\r
-                       <label for="x2">x2:</label>\r
-                       <input type="text" name="x2" id="x2" />\r
-               </p>\r
-               <p>\r
-                       <label for="y2">y2:</label>\r
-                       <input type="text" name="y2" id="y2" />\r
-               </p>\r
-               <p>\r
-                       <label for="width">width:</label>\r
-                       <input type="text" name="width" id="width" />\r
-               </p>\r
-               <p>\r
-                       <label for="height">height</label>\r
-                       <input type="text" name="height" id="height" />\r
-               </p>\r
-       </div> \r
-       \r
-</body>\r
-</html>\r
-\r
-\r
diff --git a/library/cropper/tests/poppy.jpg b/library/cropper/tests/poppy.jpg
deleted file mode 100644 (file)
index 1f64985..0000000
Binary files a/library/cropper/tests/poppy.jpg and /dev/null differ
diff --git a/library/cropper/tests/staticHTMLStructure.htm b/library/cropper/tests/staticHTMLStructure.htm
deleted file mode 100644 (file)
index ddb9927..0000000
+++ /dev/null
@@ -1,236 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\r
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\r
-<head>\r
-       <meta http-equiv="Content-type" content="text/html; charset=utf-8" />\r
-       <meta http-equiv="Content-Language" content="en-us" />\r
-       <title></title>\r
-</head>\r
-<body>\r
-       <!--\r
-               \r
-               This is a static test file for the HTML & CSS structure employed, tested in \r
-               the following browsers:\r
-               \r
-                       PC:\r
-                               IE 6:          working\r
-                               IE 5.5:        working\r
-                               IE 5.0:        opacity issues\r
-                               FF 1.5:        working\r
-                               Opera 9:       working\r
-                       MAC:\r
-                               Camino 1.0:    working\r
-                               FF 1.5:        working\r
-                               Safari 2.0:    working\r
-                               \r
-       -->     \r
-       <style type="text/css">         \r
-               .imgCrop_wrap {\r
-                       width: 500px;   /* @TODO IN JS */\r
-                       height: 333px;  /* @TODO IN JS */\r
-                       position: relative;\r
-                       cursor: crosshair;\r
-               }\r
-               \r
-               /* fix for IE displaying all boxes at line-height by default */\r
-               .imgCrop_wrap,\r
-               .imgCrop_wrap * {\r
-                       font-size: 0;\r
-               }\r
-               \r
-               .imgCrop_overlay {\r
-                       background-color: #000;\r
-                       opacity: 0.5;\r
-                       filter:alpha(opacity=50);\r
-                       position: absolute;\r
-                       width: 100%;\r
-                       height: 100%;\r
-               }\r
-               \r
-               .imgCrop_selArea {\r
-                       position: absolute;\r
-                       cursor: move;\r
-                       /* @TODO: rest to be done via JS when selecting areas */\r
-                       top: 110px;\r
-                       left: 210px;\r
-                       width: 200px;\r
-                       height: 200px;\r
-                       z-index: 2;\r
-                       background: transparent url(castle.jpg) no-repeat  -210px -110px;\r
-               }\r
-               \r
-               /* imgCrop_clickArea is all a fix for IE 5.5 & 6 to allow the user to click on the given area */\r
-               .imgCrop_clickArea {\r
-                       width: 100%;\r
-                       height: 100%;\r
-                       background-color: #FFF;\r
-                       opacity: 0.01;\r
-                       filter:alpha(opacity=01);\r
-               }\r
-                               \r
-               .imgCrop_marqueeHoriz {\r
-                       position: absolute;\r
-                       width: 100%;\r
-                       height: 1px;\r
-                       background: transparent url(marqueeHoriz.gif) repeat-x 0 0;\r
-               }\r
-               \r
-               .imgCrop_marqueeVert {\r
-                       position: absolute;\r
-                       height: 100%;\r
-                       width: 1px;\r
-                       background: transparent url(marqueeVert.gif) repeat-y 0 0;\r
-               }\r
-                               \r
-               .imgCrop_marqueeNorth { top: 0; left: 0; }\r
-               .imgCrop_marqueeEast  { top: 0; right: 0; }\r
-               .imgCrop_marqueeSouth { bottom: 0px; left: 0; }\r
-               .imgCrop_marqueeWest  { top: 0; left: 0; }\r
-               \r
-               \r
-               .imgCrop_handle {\r
-                       position: absolute;\r
-                       border: 1px solid #333;\r
-                       width: 6px;\r
-                       height: 6px;\r
-                       background: #FFF;\r
-                       opacity: 0.5;\r
-                       filter:alpha(opacity=50);\r
-                       z-index: 3;\r
-               }\r
-               \r
-               .imgCrop_handleN {\r
-                       top: -3px;\r
-                       left: 0;\r
-                       margin-left: 49%;    /* @TODO : in JS */\r
-                       cursor: n-resize;\r
-               }\r
-               \r
-               .imgCrop_handleNE { \r
-                       top: -3px;\r
-                       right: -3px;\r
-                       cursor: ne-resize;\r
-               }\r
-               \r
-               .imgCrop_handleE {\r
-                       top: 0;\r
-                       right: -3px;\r
-                       margin-top: 49%;    /* @TODO : in JS */\r
-                       cursor: e-resize;\r
-               }\r
-               \r
-               .imgCrop_handleSE {\r
-                       right: -3px;\r
-                       bottom: -3px;\r
-                       cursor: se-resize;\r
-               }\r
-               \r
-               .imgCrop_handleS {\r
-                       right: 0;\r
-                       bottom: -3px;\r
-                       margin-right: 49%; /* @TODO : in JS */\r
-                       cursor: s-resize;\r
-               }\r
-               \r
-               .imgCrop_handleSW {\r
-                       left: -3px;\r
-                       bottom: -3px;\r
-                       cursor: sw-resize;\r
-               }\r
-               \r
-               .imgCrop_handleW {\r
-                       top: 0;\r
-                       left: -3px;\r
-                       margin-top: 49%;  /* @TODO : in JS */\r
-                       cursor: e-resize;\r
-               }\r
-               \r
-               .imgCrop_handleNW {\r
-                       top: -3px;\r
-                       left: -3px;\r
-                       cursor: nw-resize;\r
-               }\r
-               \r
-               /**\r
-                * Create an area to click & drag around on as the default browser behaviour is to let you drag the image \r
-                */\r
-               .imgCrop_dragArea {\r
-                       width: 100%;\r
-                       height: 100%;\r
-                       z-index: 200;\r
-                       position: absolute;\r
-                       top: 0;\r
-                       left: 0;\r
-               }\r
-               \r
-               \r
-               .imgCrop_previewWrap {\r
-                       width: 200px;  /* @TODO : in JS */\r
-                       height: 200px; /* @TODO : in JS */\r
-                       overflow: hidden;\r
-                       position: relative;\r
-               }\r
-               \r
-               /* @TODO : all in JS */\r
-               .imgCrop_previewWrap img {\r
-                       position: absolute;\r
-                       width: 500px;\r
-                       height: 333px;\r
-                       left: -210px;\r
-                       top: -110px;\r
-               }\r
-               \r
-               /**\r
-                * These are just for the static test\r
-                */\r
-               .imgCrop_wrap {\r
-                       margin: 20px 0 0 50px;\r
-                       float: left;\r
-               }\r
-               \r
-               #previewWrapper {\r
-                       float: left;\r
-                       margin-left: 20px;\r
-               }\r
-               \r
-                       \r
-       </style>\r
-       \r
-       <br /><br />\r
-       \r
-       <!-- This is all attached to the image dynamically -->\r
-       <div class="imgCrop_wrap">\r
-               <img src="castle.jpg" alt="test image" id="testImage" width="500" height="333" />\r
-               <div class="imgCrop_dragArea">\r
-                       <div class="imgCrop_overlay"></div>\r
-                       <div class="imgCrop_selArea">\r
-                               <!-- marquees -->\r
-                               <!-- the inner spans are only required for IE to stop it making the divs 1px high/wide -->\r
-                               <div class="imgCrop_marqueeHoriz imgCrop_marqueeNorth"><span></span></div>\r
-                               <div class="imgCrop_marqueeVert imgCrop_marqueeEast"><span></span></div>\r
-                               <div class="imgCrop_marqueeHoriz imgCrop_marqueeSouth"><span></span></div>\r
-                               <div class="imgCrop_marqueeVert imgCrop_marqueeWest"><span></span></div>                        \r
-                               <!-- handles -->\r
-                               <div class="imgCrop_handle imgCrop_handleN"></div>\r
-                               <div class="imgCrop_handle imgCrop_handleNE"></div>\r
-                               <div class="imgCrop_handle imgCrop_handleE"></div>\r
-                               <div class="imgCrop_handle imgCrop_handleSE"></div>\r
-                               <div class="imgCrop_handle imgCrop_handleS"></div>\r
-                               <div class="imgCrop_handle imgCrop_handleSW"></div>\r
-                               <div class="imgCrop_handle imgCrop_handleW"></div>\r
-                               <div class="imgCrop_handle imgCrop_handleNW"></div>\r
-                               <div class="imgCrop_clickArea"></div>\r
-                       </div>  \r
-                       <div class="imgCrop_clickArea"></div>   \r
-               </div>  \r
-       </div>\r
-       \r
-       <div id="previewWrapper">\r
-               <h3>Preview:</h3>\r
-               <div class="imgCrop_previewWrap">\r
-                       <img src="castle.jpg" alt="test image" id="previewImage" />\r
-               </div>\r
-       </div>\r
-</body>\r
-</html>\r
-\r
-\r
diff --git a/library/jquery-textcomplete/CHANGELOG.md b/library/jquery-textcomplete/CHANGELOG.md
deleted file mode 100644 (file)
index e115bf9..0000000
+++ /dev/null
@@ -1,340 +0,0 @@
-# Change Log
-
-All notable changes to this project will be documented in this file.
-
-This project adheres to [Semantic Versioning](http://semver.org/) by version 1.0.0.
-
-This change log adheres to [keepachangelog.com](http://keepachangelog.com).
-
-## [Unreleased]
-
-## [1.3.4] - 2016-04-20
-### Fixed
-- Fix endless loop when RTL ([#247](https://github.com/yuku-t/jquery-textcomplete/pull/247))
-
-## [1.3.3] - 2016-04-04
-### Fixed
-- Fix uncaught TypeError.
-
-## [1.3.2] - 2016-03-27
-### Fixed
-- Fix dropdown position problem with `line-height: normal`.
-
-## [1.3.1] - 2016-03-23
-### Fixed
-- Fix `input[type=search]` support.
-
-## [1.3.0] - 2016-03-20
-### Added
-- Add optional "id" strategy parameter.
-
-## [1.2.2] - 2016-03-19
-### Fixed
-- Remove dropdown element after `textcomplete('destroy')`.
-- Skip search after pressing tab.
-- Fix dropdown-menu positioning problem using textarea-caret package.
-
-## [1.2.1] - 2016-03-14
-### Fixed
-- Build dist files.
-
-## [1.2.0] - 2016-03-14
-### Added
-- Support `input[type=search]` ([#236](https://github.com/yuku-t/jquery-textcomplete/pull/236))
-
-## [1.1.0] - 2016-03-10
-### Added
-- Add the ability to insert HTML into a "contenteditable" field. ([#217](https://github.com/yuku-t/jquery-textcomplete/pull/217))
-
-### Fixed
-- Position relative to appendTo element. ([#234](https://github.com/yuku-t/jquery-textcomplete/pull/234))
-- Avoid dropdown bumping into right edge of window. ([#235](https://github.com/yuku-t/jquery-textcomplete/pull/235))
-- Fix top position issue when window is scrolled up and parents has fix position. ([#229](https://github.com/yuku-t/jquery-textcomplete/pull/229))
-
-## [1.0.0] - 2016-02-29
-### Changed
-- Adheres keepachangelog.com.
-
-## [0.8.2] - 2016-02-29
-### Added
-- Add deactivate method to Completer. ([#233](https://github.com/yuku-t/jquery-textcomplete/pull/233))
-
-## [0.8.1] - 2015-10-22
-### Added
-- Add condition to ignore skipUnchangedTerm for empty text. ([#210](https://github.com/yuku-t/jquery-textcomplete/pull/210))
-
-## [0.8.0] - 2015-08-31
-### Changed
-- If undefined is returned from a replace callback dont replace the text. ([#204](https://github.com/yuku-t/jquery-textcomplete/pull/204))
-
-## [0.7.3] - 2015-08-27
-### Added
-- Add `Strategy#el` and `Strategy#$el` which returns current input/textarea element and corresponding jquery object respectively.
-
-## [0.7.2] - 2015-08-26
-### Fixed
-- Reset \_term after selected ([#170](https://github.com/yuku-t/jquery-textcomplete/pull/170))
-
-## [0.7.1] - 2015-08-19
-### Changed
-- Remove RTL support because of some bugs.
-
-## [0.7.0] - 2015-07-02
-### Add
-- Add support for a "no results" message like the header/footer. ([#179](https://github.com/yuku-t/jquery-textcomplete/pull/179))
-- Yield the search term to the template function. ([#177](https://github.com/yuku-t/jquery-textcomplete/pull/177))
-- Add amd wrapper. ([#167](https://github.com/yuku-t/jquery-textcomplete/pull/167))
-- Add touch devices support. ([#163](https://github.com/yuku-t/jquery-textcomplete/pull/163))
-
-### Changed
-- Stop sharing a dropdown element.
-
-## [0.6.1] - 2015-06-30
-### Fixed
-- Fix bug that Dropdown.\_fitToBottom does not consider window scroll
-
-## [0.6.0] - 2015-06-30
-### Added
-- Now dropdown elements have "textcomplete-dropdown" class.
-
-## [0.5.2] - 2015-06-29
-### Fixed
-- Keep dropdown list in browser window. ([#172](https://github.com/yuku-t/jquery-textcomplete/pull/172))
-
-## [0.5.1] - 2015-06-08
-### Changed
-- Now a replace function is invoked with a user event.
-
-## [0.5.0] - 2015-06-08
-### Added
-- Support `onKeydown` option.
-
-## [0.4.0] - 2015-03-10
-### Added
-- Publish to [npmjs](https://www.npmjs.com/package/jquery-textcomplete).
-- Support giving a function which returns a regexp to `match` option for dynamic matching.
-
-## [0.3.9] - 2015-03-03
-### Fixed
-- Deactivate dropdown on escape. ([#155](https://github.com/yuku-t/jquery-textcomplete/pull/155))
-
-## [0.3.8] - 2015-02-26
-### Fixed
-- Fix completion with enter key. ([#154](https://github.com/yuku-t/jquery-textcomplete/pull/154))
-- Fix empty span node is inserted. ([#153](https://github.com/yuku-t/jquery-textcomplete/pull/153))
-
-## [0.3.7] - 2015-01-21
-### Added
-- Support input([type=text]. [#149](https://github.com/yuku-t/jquery-textcomplete/pull/149))
-
-## [0.3.6] - 2014-12-11
-### Added
-- Support element.contentEditable compatibility check. ([#147](https://github.com/yuku-t/jquery-textcomplete/pull/147))
-
-### Fixed
-- Fixes the fire function for events with additional parameters. ([#145](https://github.com/yuku-t/jquery-textcomplete/pull/145))
-
-## [0.3.5] - 2014-12-11
-### Added
-- Adds functionality to complete selection on space key. ([#141](https://github.com/yuku-t/jquery-textcomplete/pull/141))
-
-### Fixed
-- Loading script in head and destroy method bugfixes. ([#143](https://github.com/yuku-t/jquery-textcomplete/pull/143))
-
-## [0.3.4] - 2014-12-03
-### Fixed
-- Fix error when destroy is called before the field is focused. ([#138](https://github.com/yuku-t/jquery-textcomplete/pull/138))
-- Fix IE bug where it would only trigger when tha carrot was at the end of the line. ([#133](https://github.com/yuku-t/jquery-textcomplete/pull/133))
-
-## [0.3.3] - 2014-09-25
-### Added
-- Add `className` option.
-- Add `match` as the third argument of a search function.
-
-### Fixed
-- Ignore `.textcomplete('destory')` on non-initialized elements. ([#118](https://github.com/yuku-t/jquery-textcomplete/pull/118))
-- Trigger completer with the current text by default. ([#119](https://github.com/yuku-t/jquery-textcomplete/pull/119))
-- Hide dropdown before destroying it. ([#120](https://github.com/yuku-t/jquery-textcomplete/pull/120))
-- Don't throw an exception even if a jquery click event is manually triggered. ([#121](https://github.com/yuku-t/jquery-textcomplete/pull/121))
-
-## [0.3.2] - 2014-09-16
-### Added
-- Add `IETextarea` adapter which supports IE8
-- Add `idProperty` option.
-- Add `adapter` option.
-
-### Changed
-- Rename `Input` as `Adapter`.
-
-## [0.3.1] - 2014-09-10
-### Added
-- Add `context` strategy option.
-- Add `debounce` option.
-
-### Changed
-- Recycle `.dropdown-menu` element if available.
-
-## [0.3.0] - 2014-09-10
-### Added
-- Consider the `tab-size` of textarea.
-- Add `zIndex` option.
-
-### Fixed
-- Revive `header` and `footer` options.
-- Revive `height` option.
-
-## [0.3.0-beta2] - 2014-09-09
-### Fixed
-- Make sure that all demos work fine.
-
-## [0.3.0-beta1] - 2014-08-31
-### Fixed
-- Huge refactoring.
-
-## [0.2.6] - 2014-08-16
-### Fixed
-- Repair contenteditable.
-
-## [0.2.5] - 2014-08-07
-### Added
-- Enhance contenteditable support. ([#98](https://github.com/yuku-t/jquery-textcomplete/pull/98))
-- Support absolute left/right placement. ([#96](https://github.com/yuku-t/jquery-textcomplete/pull/96))
-- Support absolute height, scrollbar, pageup and pagedown. ([#87](https://github.com/yuku-t/jquery-textcomplete/pull/87))
-
-## [0.2.4] - 2014-07-02
-### Fixed
-- Fix horizonal position on contentEditable elements. ([#92](https://github.com/yuku-t/jquery-textcomplete/pull/92))
-
-## [0.2.3] - 2014-06-24
-### Added
-- Option to supply list view position function. ([#88](https://github.com/yuku-t/jquery-textcomplete/pull/88))
-
-## [0.2.2] - 2014-06-08
-### Added
-- Append dropdown element to body element by default.
-- Tiny refactoring. [#84]
-- Ignore tab key when modifier keys are being pushed. ([#85](https://github.com/yuku-t/jquery-textcomplete/pull/85))
-- Manual triggering.
-
-## [0.2.1] - 2014-05-15
-### Added
-- Support `appendTo` option.
-- `header` and `footer` supports a function.
-
-### Changed
-- Remove textcomplate-wrapper element.
-
-## [0.2.0] - 2014-05-02
-### Added
-- Contenteditable support.
-- Several bugfixes.
-- Support `header` and `footer` setting.
-
-## [0.1.4.1] - 2014-04-04
-### Added
-- Support placement option.
-- Emacs-style prev/next keybindings.
-- Replay searchFunc for the last term on slow network env.
-
-### Fixed
-- Several bugfixes.
-
-## [0.1.3] - 2014-04-07
-### Added
-- Support RTL positioning.
-
-### Fixed
-- Several bugfixes.
-
-## [0.1.2] - 2014-02-08
-### Added
-- Enable to append strategies on the fly.
-- Enable to stop autocompleting.
-- Enable to apply multiple textareas at once.
-- Don't show popup on pressing arrow up and down keys.
-- Hide dropdown by pressing ESC key.
-- Prevent showing a dropdown when it just autocompleted.
-
-## [0.1.1] - 2014-02-02
-### Added
-- Introduce `textComplete:show`, `textComplete:hide` and `textComplete:select` events.
-
-## [0.1.0] - 2013-10-28
-### Added
-- Now strategies argument is an Array of strategy objects.
-
-## [0.0.4] - 2013-10-28
-### Added
-- Up and Down arrows cycle instead of exit.
-- Support Zepto.
-- Support jQuery.overlay.
-
-### Fixed
-- Several bugfixes.
-
-## [0.0.3] - 2013-09-11
-### Added
-- Some performance improvement.
-- Implement lazy callbacking on search function.
-
-## [0.0.2] - 2013-09-08
-### Added
-- Support IE8.
-- Some performance improvement.
-- Implement cache option.
-
-## 0.0.1 - 2013-09-02
-### Added
-- Initial release.
-
-[Unreleased]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.3.4...HEAD
-[1.3.4]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.3.3...v1.3.4
-[1.3.3]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.3.2...v1.3.3
-[1.3.2]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.3.1...v1.3.2
-[1.3.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.3.0...v1.3.1
-[1.3.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.2.2...v1.3.0
-[1.2.2]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.2.1...v1.2.2
-[1.2.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.2.0...v1.2.1
-[1.2.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.1.0...v1.2.0
-[1.1.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.0.0...v1.1.0
-[1.0.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.8.2...v1.0.0
-[0.8.2]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.8.1...v0.8.2
-[0.8.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.8.0...v0.8.1
-[0.8.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.7.3...v0.8.0
-[0.7.3]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.7.2...v0.7.3
-[0.7.2]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.7.1...v0.7.2
-[0.7.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.7.0...v0.7.1
-[0.7.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.6.1...v0.7.0
-[0.6.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.6.0...v0.6.1
-[0.6.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.5.2...v0.6.0
-[0.5.2]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.5.1...v0.5.2
-[0.5.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.5.0...v0.5.1
-[0.5.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.4.0...v0.5.0
-[0.4.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.9...v0.4.0
-[0.3.9]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.8...v0.3.9
-[0.3.8]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.7...v0.3.8
-[0.3.7]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.6...v0.3.7
-[0.3.6]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.5...v0.3.6
-[0.3.5]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.4...v0.3.5
-[0.3.4]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.3...v0.3.4
-[0.3.3]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.2...v0.3.3
-[0.3.2]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.1...v0.3.2
-[0.3.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.0...v0.3.1
-[0.3.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.0-beta2...v0.3.0
-[0.3.0-beta2]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.0-beta1...v0.3.0-beta2
-[0.3.0-beta1]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.2.6...v0.3.0-beta1
-[0.2.6]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.2.5...v0.2.6
-[0.2.5]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.2.4...v0.2.5
-[0.2.4]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.2.3...v0.2.4
-[0.2.3]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.2.2...v0.2.3
-[0.2.2]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.2.1...v0.2.2
-[0.2.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.2.0...v0.2.1
-[0.2.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.1.4.1...v0.2.0
-[0.1.4.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.1.3...v0.1.4.1
-[0.1.3]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.1.2...v0.1.3
-[0.1.2]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.1.1...v0.1.2
-[0.1.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.1.0...v0.1.1
-[0.1.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.0.4...v0.1.0
-[0.0.4]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.0.3...v0.0.4
-[0.0.3]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.0.2...v0.0.3
-[0.0.2]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.0.1...v0.0.2
diff --git a/library/jquery-textcomplete/LICENSE b/library/jquery-textcomplete/LICENSE
deleted file mode 100644 (file)
index 4848bd6..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2013-2014 Yuku Takahashi
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
diff --git a/library/jquery-textcomplete/README.md b/library/jquery-textcomplete/README.md
deleted file mode 100644 (file)
index d74dfbd..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-# Autocomplete for Textarea
-
-[![npm version](https://badge.fury.io/js/jquery-textcomplete.svg)](http://badge.fury.io/js/jquery-textcomplete)
-[![Bower version](https://badge.fury.io/bo/jquery-textcomplete.svg)](http://badge.fury.io/bo/jquery-textcomplete)
-[![Analytics](https://ga-beacon.appspot.com/UA-4932407-14/jquery-textcomplete/readme)](https://github.com/igrigorik/ga-beacon)
-
-Introduces autocompleting power to textareas, like a GitHub comment form has.
-
-![Demo](http://yuku-t.com/jquery-textcomplete/media/images/demo.gif)
-
-[Demo](http://yuku-t.com/jquery-textcomplete/).
-
-## Synopsis
-
-```js
-$('textarea').textcomplete([{
-    match: /(^|\b)(\w{2,})$/,
-    search: function (term, callback) {
-        var words = ['google', 'facebook', 'github', 'microsoft', 'yahoo'];
-        callback($.map(words, function (word) {
-            return word.indexOf(term) === 0 ? word : null;
-        }));
-    },
-    replace: function (word) {
-        return word + ' ';
-    }
-}]);
-```
-
-## Dependencies
-
-- jQuery (>= 1.7.0) OR Zepto (>= 1.0)
-
-## Documents
-
-See [doc](https://github.com/yuku-t/jquery-textcomplete/tree/master/doc) dir.
-
-## License
-
-Licensed under the MIT License.
-
-## Contributors
-
-Patches and code improvements were contributed by:
-
-https://github.com/yuku-t/jquery-textcomplete/graphs/contributors
diff --git a/library/jquery-textcomplete/jquery.textcomplete.css b/library/jquery-textcomplete/jquery.textcomplete.css
deleted file mode 100644 (file)
index 37a761b..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/* Sample */
-
-.dropdown-menu {
-    border: 1px solid #ddd;
-    background-color: white;
-}
-
-.dropdown-menu li {
-    border-top: 1px solid #ddd;
-    padding: 2px 5px;
-}
-
-.dropdown-menu li:first-child {
-    border-top: none;
-}
-
-.dropdown-menu li:hover,
-.dropdown-menu .active {
-    background-color: rgb(110, 183, 219);
-}
-
-
-/* SHOULD not modify */
-
-.dropdown-menu {
-    list-style: none;
-    padding: 0;
-    margin: 0;
-}
-
-.dropdown-menu a:hover {
-    cursor: pointer;
-}
diff --git a/library/jquery-textcomplete/jquery.textcomplete.js b/library/jquery-textcomplete/jquery.textcomplete.js
deleted file mode 100644 (file)
index 95e7514..0000000
+++ /dev/null
@@ -1,1401 +0,0 @@
-(function (factory) {
-  if (typeof define === 'function' && define.amd) {
-    // AMD. Register as an anonymous module.
-    define(['jquery'], factory);
-  } else if (typeof module === "object" && module.exports) {
-    var $ = require('jquery');
-    module.exports = factory($);
-  } else {
-    // Browser globals
-    factory(jQuery);
-  }
-}(function (jQuery) {
-
-/*!
- * jQuery.textcomplete
- *
- * Repository: https://github.com/yuku-t/jquery-textcomplete
- * License:    MIT (https://github.com/yuku-t/jquery-textcomplete/blob/master/LICENSE)
- * Author:     Yuku Takahashi
- */
-
-if (typeof jQuery === 'undefined') {
-  throw new Error('jQuery.textcomplete requires jQuery');
-}
-
-+function ($) {
-  'use strict';
-
-  var warn = function (message) {
-    if (console.warn) { console.warn(message); }
-  };
-
-  var id = 1;
-
-  $.fn.textcomplete = function (strategies, option) {
-    var args = Array.prototype.slice.call(arguments);
-    return this.each(function () {
-      var self = this;
-      var $this = $(this);
-      var completer = $this.data('textComplete');
-      if (!completer) {
-        option || (option = {});
-        option._oid = id++;  // unique object id
-        completer = new $.fn.textcomplete.Completer(this, option);
-        $this.data('textComplete', completer);
-      }
-      if (typeof strategies === 'string') {
-        if (!completer) return;
-        args.shift()
-        completer[strategies].apply(completer, args);
-        if (strategies === 'destroy') {
-          $this.removeData('textComplete');
-        }
-      } else {
-        // For backward compatibility.
-        // TODO: Remove at v0.4
-        $.each(strategies, function (obj) {
-          $.each(['header', 'footer', 'placement', 'maxCount'], function (name) {
-            if (obj[name]) {
-              completer.option[name] = obj[name];
-              warn(name + 'as a strategy param is deprecated. Use option.');
-              delete obj[name];
-            }
-          });
-        });
-        completer.register($.fn.textcomplete.Strategy.parse(strategies, {
-          el: self,
-          $el: $this
-        }));
-      }
-    });
-  };
-
-}(jQuery);
-
-+function ($) {
-  'use strict';
-
-  // Exclusive execution control utility.
-  //
-  // func - The function to be locked. It is executed with a function named
-  //        `free` as the first argument. Once it is called, additional
-  //        execution are ignored until the free is invoked. Then the last
-  //        ignored execution will be replayed immediately.
-  //
-  // Examples
-  //
-  //   var lockedFunc = lock(function (free) {
-  //     setTimeout(function { free(); }, 1000); // It will be free in 1 sec.
-  //     console.log('Hello, world');
-  //   });
-  //   lockedFunc();  // => 'Hello, world'
-  //   lockedFunc();  // none
-  //   lockedFunc();  // none
-  //   // 1 sec past then
-  //   // => 'Hello, world'
-  //   lockedFunc();  // => 'Hello, world'
-  //   lockedFunc();  // none
-  //
-  // Returns a wrapped function.
-  var lock = function (func) {
-    var locked, queuedArgsToReplay;
-
-    return function () {
-      // Convert arguments into a real array.
-      var args = Array.prototype.slice.call(arguments);
-      if (locked) {
-        // Keep a copy of this argument list to replay later.
-        // OK to overwrite a previous value because we only replay
-        // the last one.
-        queuedArgsToReplay = args;
-        return;
-      }
-      locked = true;
-      var self = this;
-      args.unshift(function replayOrFree() {
-        if (queuedArgsToReplay) {
-          // Other request(s) arrived while we were locked.
-          // Now that the lock is becoming available, replay
-          // the latest such request, then call back here to
-          // unlock (or replay another request that arrived
-          // while this one was in flight).
-          var replayArgs = queuedArgsToReplay;
-          queuedArgsToReplay = undefined;
-          replayArgs.unshift(replayOrFree);
-          func.apply(self, replayArgs);
-        } else {
-          locked = false;
-        }
-      });
-      func.apply(this, args);
-    };
-  };
-
-  var isString = function (obj) {
-    return Object.prototype.toString.call(obj) === '[object String]';
-  };
-
-  var isFunction = function (obj) {
-    return Object.prototype.toString.call(obj) === '[object Function]';
-  };
-
-  var uniqueId = 0;
-
-  function Completer(element, option) {
-    this.$el        = $(element);
-    this.id         = 'textcomplete' + uniqueId++;
-    this.strategies = [];
-    this.views      = [];
-    this.option     = $.extend({}, Completer._getDefaults(), option);
-
-    if (!this.$el.is('input[type=text]') && !this.$el.is('input[type=search]') && !this.$el.is('textarea') && !element.isContentEditable && element.contentEditable != 'true') {
-      throw new Error('textcomplete must be called on a Textarea or a ContentEditable.');
-    }
-
-    if (element === document.activeElement) {
-      // element has already been focused. Initialize view objects immediately.
-      this.initialize()
-    } else {
-      // Initialize view objects lazily.
-      var self = this;
-      this.$el.one('focus.' + this.id, function () { self.initialize(); });
-    }
-  }
-
-  Completer._getDefaults = function () {
-    if (!Completer.DEFAULTS) {
-      Completer.DEFAULTS = {
-        appendTo: $('body'),
-        zIndex: '100'
-      };
-    }
-
-    return Completer.DEFAULTS;
-  }
-
-  $.extend(Completer.prototype, {
-    // Public properties
-    // -----------------
-
-    id:         null,
-    option:     null,
-    strategies: null,
-    adapter:    null,
-    dropdown:   null,
-    $el:        null,
-
-    // Public methods
-    // --------------
-
-    initialize: function () {
-      var element = this.$el.get(0);
-      // Initialize view objects.
-      this.dropdown = new $.fn.textcomplete.Dropdown(element, this, this.option);
-      var Adapter, viewName;
-      if (this.option.adapter) {
-        Adapter = this.option.adapter;
-      } else {
-        if (this.$el.is('textarea') || this.$el.is('input[type=text]') || this.$el.is('input[type=search]')) {
-          viewName = typeof element.selectionEnd === 'number' ? 'Textarea' : 'IETextarea';
-        } else {
-          viewName = 'ContentEditable';
-        }
-        Adapter = $.fn.textcomplete[viewName];
-      }
-      this.adapter = new Adapter(element, this, this.option);
-    },
-
-    destroy: function () {
-      this.$el.off('.' + this.id);
-      if (this.adapter) {
-        this.adapter.destroy();
-      }
-      if (this.dropdown) {
-        this.dropdown.destroy();
-      }
-      this.$el = this.adapter = this.dropdown = null;
-    },
-
-    deactivate: function () {
-      if (this.dropdown) {
-        this.dropdown.deactivate();
-      }
-    },
-
-    // Invoke textcomplete.
-    trigger: function (text, skipUnchangedTerm) {
-      if (!this.dropdown) { this.initialize(); }
-      text != null || (text = this.adapter.getTextFromHeadToCaret());
-      var searchQuery = this._extractSearchQuery(text);
-      if (searchQuery.length) {
-        var term = searchQuery[1];
-        // Ignore shift-key, ctrl-key and so on.
-        if (skipUnchangedTerm && this._term === term && term !== "") { return; }
-        this._term = term;
-        this._search.apply(this, searchQuery);
-      } else {
-        this._term = null;
-        this.dropdown.deactivate();
-      }
-    },
-
-    fire: function (eventName) {
-      var args = Array.prototype.slice.call(arguments, 1);
-      this.$el.trigger(eventName, args);
-      return this;
-    },
-
-    register: function (strategies) {
-      Array.prototype.push.apply(this.strategies, strategies);
-    },
-
-    // Insert the value into adapter view. It is called when the dropdown is clicked
-    // or selected.
-    //
-    // value    - The selected element of the array callbacked from search func.
-    // strategy - The Strategy object.
-    // e        - Click or keydown event object.
-    select: function (value, strategy, e) {
-      this._term = null;
-      this.adapter.select(value, strategy, e);
-      this.fire('change').fire('textComplete:select', value, strategy);
-      this.adapter.focus();
-    },
-
-    // Private properties
-    // ------------------
-
-    _clearAtNext: true,
-    _term:        null,
-
-    // Private methods
-    // ---------------
-
-    // Parse the given text and extract the first matching strategy.
-    //
-    // Returns an array including the strategy, the query term and the match
-    // object if the text matches an strategy; otherwise returns an empty array.
-    _extractSearchQuery: function (text) {
-      for (var i = 0; i < this.strategies.length; i++) {
-        var strategy = this.strategies[i];
-        var context = strategy.context(text);
-        if (context || context === '') {
-          var matchRegexp = isFunction(strategy.match) ? strategy.match(text) : strategy.match;
-          if (isString(context)) { text = context; }
-          var match = text.match(matchRegexp);
-          if (match) { return [strategy, match[strategy.index], match]; }
-        }
-      }
-      return []
-    },
-
-    // Call the search method of selected strategy..
-    _search: lock(function (free, strategy, term, match) {
-      var self = this;
-      strategy.search(term, function (data, stillSearching) {
-        if (!self.dropdown.shown) {
-          self.dropdown.activate();
-        }
-        if (self._clearAtNext) {
-          // The first callback in the current lock.
-          self.dropdown.clear();
-          self._clearAtNext = false;
-        }
-        self.dropdown.setPosition(self.adapter.getCaretPosition());
-        self.dropdown.render(self._zip(data, strategy, term));
-        if (!stillSearching) {
-          // The last callback in the current lock.
-          free();
-          self._clearAtNext = true; // Call dropdown.clear at the next time.
-        }
-      }, match);
-    }),
-
-    // Build a parameter for Dropdown#render.
-    //
-    // Examples
-    //
-    //  this._zip(['a', 'b'], 's');
-    //  //=> [{ value: 'a', strategy: 's' }, { value: 'b', strategy: 's' }]
-    _zip: function (data, strategy, term) {
-      return $.map(data, function (value) {
-        return { value: value, strategy: strategy, term: term };
-      });
-    }
-  });
-
-  $.fn.textcomplete.Completer = Completer;
-}(jQuery);
-
-+function ($) {
-  'use strict';
-
-  var $window = $(window);
-
-  var include = function (zippedData, datum) {
-    var i, elem;
-    var idProperty = datum.strategy.idProperty
-    for (i = 0; i < zippedData.length; i++) {
-      elem = zippedData[i];
-      if (elem.strategy !== datum.strategy) continue;
-      if (idProperty) {
-        if (elem.value[idProperty] === datum.value[idProperty]) return true;
-      } else {
-        if (elem.value === datum.value) return true;
-      }
-    }
-    return false;
-  };
-
-  var dropdownViews = {};
-  $(document).on('click', function (e) {
-    var id = e.originalEvent && e.originalEvent.keepTextCompleteDropdown;
-    $.each(dropdownViews, function (key, view) {
-      if (key !== id) { view.deactivate(); }
-    });
-  });
-
-  var commands = {
-    SKIP_DEFAULT: 0,
-    KEY_UP: 1,
-    KEY_DOWN: 2,
-    KEY_ENTER: 3,
-    KEY_PAGEUP: 4,
-    KEY_PAGEDOWN: 5,
-    KEY_ESCAPE: 6
-  };
-
-  // Dropdown view
-  // =============
-
-  // Construct Dropdown object.
-  //
-  // element - Textarea or contenteditable element.
-  function Dropdown(element, completer, option) {
-    this.$el       = Dropdown.createElement(option);
-    this.completer = completer;
-    this.id        = completer.id + 'dropdown';
-    this._data     = []; // zipped data.
-    this.$inputEl  = $(element);
-    this.option    = option;
-
-    // Override setPosition method.
-    if (option.listPosition) { this.setPosition = option.listPosition; }
-    if (option.height) { this.$el.height(option.height); }
-    var self = this;
-    $.each(['maxCount', 'placement', 'footer', 'header', 'noResultsMessage', 'className'], function (_i, name) {
-      if (option[name] != null) { self[name] = option[name]; }
-    });
-    this._bindEvents(element);
-    dropdownViews[this.id] = this;
-  }
-
-  $.extend(Dropdown, {
-    // Class methods
-    // -------------
-
-    createElement: function (option) {
-      var $parent = option.appendTo;
-      if (!($parent instanceof $)) { $parent = $($parent); }
-      var $el = $('<ul></ul>')
-        .addClass('dropdown-menu textcomplete-dropdown')
-        .attr('id', 'textcomplete-dropdown-' + option._oid)
-        .css({
-          display: 'none',
-          left: 0,
-          position: 'absolute',
-          zIndex: option.zIndex
-        })
-        .appendTo($parent);
-      return $el;
-    }
-  });
-
-  $.extend(Dropdown.prototype, {
-    // Public properties
-    // -----------------
-
-    $el:       null,  // jQuery object of ul.dropdown-menu element.
-    $inputEl:  null,  // jQuery object of target textarea.
-    completer: null,
-    footer:    null,
-    header:    null,
-    id:        null,
-    maxCount:  10,
-    placement: '',
-    shown:     false,
-    data:      [],     // Shown zipped data.
-    className: '',
-
-    // Public methods
-    // --------------
-
-    destroy: function () {
-      // Don't remove $el because it may be shared by several textcompletes.
-      this.deactivate();
-
-      this.$el.off('.' + this.id);
-      this.$inputEl.off('.' + this.id);
-      this.clear();
-      this.$el.remove();
-      this.$el = this.$inputEl = this.completer = null;
-      delete dropdownViews[this.id]
-    },
-
-    render: function (zippedData) {
-      var contentsHtml = this._buildContents(zippedData);
-      var unzippedData = $.map(this.data, function (d) { return d.value; });
-      if (this.data.length) {
-        var strategy = zippedData[0].strategy;
-        if (strategy.id) {
-          this.$el.attr('data-strategy', strategy.id);
-        } else {
-          this.$el.removeAttr('data-strategy');
-        }
-        this._renderHeader(unzippedData);
-        this._renderFooter(unzippedData);
-        if (contentsHtml) {
-          this._renderContents(contentsHtml);
-          this._fitToBottom();
-          this._fitToRight();
-          this._activateIndexedItem();
-        }
-        this._setScroll();
-      } else if (this.noResultsMessage) {
-        this._renderNoResultsMessage(unzippedData);
-      } else if (this.shown) {
-        this.deactivate();
-      }
-    },
-
-    setPosition: function (pos) {
-      // Make the dropdown fixed if the input is also fixed
-      // This can't be done during init, as textcomplete may be used on multiple elements on the same page
-      // Because the same dropdown is reused behind the scenes, we need to recheck every time the dropdown is showed
-      var position = 'absolute';
-      // Check if input or one of its parents has positioning we need to care about
-      this.$inputEl.add(this.$inputEl.parents()).each(function() {
-        if($(this).css('position') === 'absolute') // The element has absolute positioning, so it's all OK
-          return false;
-        if($(this).css('position') === 'fixed') {
-          pos.top -= $window.scrollTop();
-          pos.left -= $window.scrollLeft();                                    
-          position = 'fixed';
-          return false;
-        }
-      });
-      this.$el.css(this._applyPlacement(pos));
-      this.$el.css({ position: position }); // Update positioning
-
-      return this;
-    },
-
-    clear: function () {
-      this.$el.html('');
-      this.data = [];
-      this._index = 0;
-      this._$header = this._$footer = this._$noResultsMessage = null;
-    },
-
-    activate: function () {
-      if (!this.shown) {
-        this.clear();
-        this.$el.show();
-        if (this.className) { this.$el.addClass(this.className); }
-        this.completer.fire('textComplete:show');
-        this.shown = true;
-      }
-      return this;
-    },
-
-    deactivate: function () {
-      if (this.shown) {
-        this.$el.hide();
-        if (this.className) { this.$el.removeClass(this.className); }
-        this.completer.fire('textComplete:hide');
-        this.shown = false;
-      }
-      return this;
-    },
-
-    isUp: function (e) {
-      return e.keyCode === 38 || (e.ctrlKey && e.keyCode === 80);  // UP, Ctrl-P
-    },
-
-    isDown: function (e) {
-      return e.keyCode === 40 || (e.ctrlKey && e.keyCode === 78);  // DOWN, Ctrl-N
-    },
-
-    isEnter: function (e) {
-      var modifiers = e.ctrlKey || e.altKey || e.metaKey || e.shiftKey;
-      return !modifiers && (e.keyCode === 13 || e.keyCode === 9 || (this.option.completeOnSpace === true && e.keyCode === 32))  // ENTER, TAB
-    },
-
-    isPageup: function (e) {
-      return e.keyCode === 33;  // PAGEUP
-    },
-
-    isPagedown: function (e) {
-      return e.keyCode === 34;  // PAGEDOWN
-    },
-
-    isEscape: function (e) {
-      return e.keyCode === 27;  // ESCAPE
-    },
-
-    // Private properties
-    // ------------------
-
-    _data:    null,  // Currently shown zipped data.
-    _index:   null,
-    _$header: null,
-    _$noResultsMessage: null,
-    _$footer: null,
-
-    // Private methods
-    // ---------------
-
-    _bindEvents: function () {
-      this.$el.on('mousedown.' + this.id, '.textcomplete-item', $.proxy(this._onClick, this));
-      this.$el.on('touchstart.' + this.id, '.textcomplete-item', $.proxy(this._onClick, this));
-      this.$el.on('mouseover.' + this.id, '.textcomplete-item', $.proxy(this._onMouseover, this));
-      this.$inputEl.on('keydown.' + this.id, $.proxy(this._onKeydown, this));
-    },
-
-    _onClick: function (e) {
-      var $el = $(e.target);
-      e.preventDefault();
-      e.originalEvent.keepTextCompleteDropdown = this.id;
-      if (!$el.hasClass('textcomplete-item')) {
-        $el = $el.closest('.textcomplete-item');
-      }
-      var datum = this.data[parseInt($el.data('index'), 10)];
-      this.completer.select(datum.value, datum.strategy, e);
-      var self = this;
-      // Deactive at next tick to allow other event handlers to know whether
-      // the dropdown has been shown or not.
-      setTimeout(function () {
-        self.deactivate();
-        if (e.type === 'touchstart') {
-          self.$inputEl.focus();
-        }
-      }, 0);
-    },
-
-    // Activate hovered item.
-    _onMouseover: function (e) {
-      var $el = $(e.target);
-      e.preventDefault();
-      if (!$el.hasClass('textcomplete-item')) {
-        $el = $el.closest('.textcomplete-item');
-      }
-      this._index = parseInt($el.data('index'), 10);
-      this._activateIndexedItem();
-    },
-
-    _onKeydown: function (e) {
-      if (!this.shown) { return; }
-
-      var command;
-
-      if ($.isFunction(this.option.onKeydown)) {
-        command = this.option.onKeydown(e, commands);
-      }
-
-      if (command == null) {
-        command = this._defaultKeydown(e);
-      }
-
-      switch (command) {
-        case commands.KEY_UP:
-          e.preventDefault();
-          this._up();
-          break;
-        case commands.KEY_DOWN:
-          e.preventDefault();
-          this._down();
-          break;
-        case commands.KEY_ENTER:
-          e.preventDefault();
-          this._enter(e);
-          break;
-        case commands.KEY_PAGEUP:
-          e.preventDefault();
-          this._pageup();
-          break;
-        case commands.KEY_PAGEDOWN:
-          e.preventDefault();
-          this._pagedown();
-          break;
-        case commands.KEY_ESCAPE:
-          e.preventDefault();
-          this.deactivate();
-          break;
-      }
-    },
-
-    _defaultKeydown: function (e) {
-      if (this.isUp(e)) {
-        return commands.KEY_UP;
-      } else if (this.isDown(e)) {
-        return commands.KEY_DOWN;
-      } else if (this.isEnter(e)) {
-        return commands.KEY_ENTER;
-      } else if (this.isPageup(e)) {
-        return commands.KEY_PAGEUP;
-      } else if (this.isPagedown(e)) {
-        return commands.KEY_PAGEDOWN;
-      } else if (this.isEscape(e)) {
-        return commands.KEY_ESCAPE;
-      }
-    },
-
-    _up: function () {
-      if (this._index === 0) {
-        this._index = this.data.length - 1;
-      } else {
-        this._index -= 1;
-      }
-      this._activateIndexedItem();
-      this._setScroll();
-    },
-
-    _down: function () {
-      if (this._index === this.data.length - 1) {
-        this._index = 0;
-      } else {
-        this._index += 1;
-      }
-      this._activateIndexedItem();
-      this._setScroll();
-    },
-
-    _enter: function (e) {
-      var datum = this.data[parseInt(this._getActiveElement().data('index'), 10)];
-      this.completer.select(datum.value, datum.strategy, e);
-      this.deactivate();
-    },
-
-    _pageup: function () {
-      var target = 0;
-      var threshold = this._getActiveElement().position().top - this.$el.innerHeight();
-      this.$el.children().each(function (i) {
-        if ($(this).position().top + $(this).outerHeight() > threshold) {
-          target = i;
-          return false;
-        }
-      });
-      this._index = target;
-      this._activateIndexedItem();
-      this._setScroll();
-    },
-
-    _pagedown: function () {
-      var target = this.data.length - 1;
-      var threshold = this._getActiveElement().position().top + this.$el.innerHeight();
-      this.$el.children().each(function (i) {
-        if ($(this).position().top > threshold) {
-          target = i;
-          return false
-        }
-      });
-      this._index = target;
-      this._activateIndexedItem();
-      this._setScroll();
-    },
-
-    _activateIndexedItem: function () {
-      this.$el.find('.textcomplete-item.active').removeClass('active');
-      this._getActiveElement().addClass('active');
-    },
-
-    _getActiveElement: function () {
-      return this.$el.children('.textcomplete-item:nth(' + this._index + ')');
-    },
-
-    _setScroll: function () {
-      var $activeEl = this._getActiveElement();
-      var itemTop = $activeEl.position().top;
-      var itemHeight = $activeEl.outerHeight();
-      var visibleHeight = this.$el.innerHeight();
-      var visibleTop = this.$el.scrollTop();
-      if (this._index === 0 || this._index == this.data.length - 1 || itemTop < 0) {
-        this.$el.scrollTop(itemTop + visibleTop);
-      } else if (itemTop + itemHeight > visibleHeight) {
-        this.$el.scrollTop(itemTop + itemHeight + visibleTop - visibleHeight);
-      }
-    },
-
-    _buildContents: function (zippedData) {
-      var datum, i, index;
-      var html = '';
-      for (i = 0; i < zippedData.length; i++) {
-        if (this.data.length === this.maxCount) break;
-        datum = zippedData[i];
-        if (include(this.data, datum)) { continue; }
-        index = this.data.length;
-        this.data.push(datum);
-        html += '<li class="textcomplete-item" data-index="' + index + '"><a>';
-        html +=   datum.strategy.template(datum.value, datum.term);
-        html += '</a></li>';
-      }
-      return html;
-    },
-
-    _renderHeader: function (unzippedData) {
-      if (this.header) {
-        if (!this._$header) {
-          this._$header = $('<li class="textcomplete-header"></li>').prependTo(this.$el);
-        }
-        var html = $.isFunction(this.header) ? this.header(unzippedData) : this.header;
-        this._$header.html(html);
-      }
-    },
-
-    _renderFooter: function (unzippedData) {
-      if (this.footer) {
-        if (!this._$footer) {
-          this._$footer = $('<li class="textcomplete-footer"></li>').appendTo(this.$el);
-        }
-        var html = $.isFunction(this.footer) ? this.footer(unzippedData) : this.footer;
-        this._$footer.html(html);
-      }
-    },
-
-    _renderNoResultsMessage: function (unzippedData) {
-      if (this.noResultsMessage) {
-        if (!this._$noResultsMessage) {
-          this._$noResultsMessage = $('<li class="textcomplete-no-results-message"></li>').appendTo(this.$el);
-        }
-        var html = $.isFunction(this.noResultsMessage) ? this.noResultsMessage(unzippedData) : this.noResultsMessage;
-        this._$noResultsMessage.html(html);
-      }
-    },
-
-    _renderContents: function (html) {
-      if (this._$footer) {
-        this._$footer.before(html);
-      } else {
-        this.$el.append(html);
-      }
-    },
-
-    _fitToBottom: function() {
-      var windowScrollBottom = $window.scrollTop() + $window.height();
-      var height = this.$el.height();
-      if ((this.$el.position().top + height) > windowScrollBottom) {
-        this.$el.offset({top: windowScrollBottom - height});
-      }
-    },
-
-    _fitToRight: function() {
-      // We don't know how wide our content is until the browser positions us, and at that point it clips us
-      // to the document width so we don't know if we would have overrun it. As a heuristic to avoid that clipping
-      // (which makes our elements wrap onto the next line and corrupt the next item), if we're close to the right
-      // edge, move left. We don't know how far to move left, so just keep nudging a bit.
-      var tolerance = 30; // pixels. Make wider than vertical scrollbar because we might not be able to use that space.
-      var lastOffset = this.$el.offset().left, offset;
-      var width = this.$el.width();
-      var maxLeft = $window.width() - tolerance;
-      while (lastOffset + width > maxLeft) {
-        this.$el.offset({left: lastOffset - tolerance});
-        offset = this.$el.offset().left;
-        if (offset >= lastOffset) { break; }
-        lastOffset = offset;
-      }
-    },
-
-    _applyPlacement: function (position) {
-      // If the 'placement' option set to 'top', move the position above the element.
-      if (this.placement.indexOf('top') !== -1) {
-        // Overwrite the position object to set the 'bottom' property instead of the top.
-        position = {
-          top: 'auto',
-          bottom: this.$el.parent().height() - position.top + position.lineHeight,
-          left: position.left
-        };
-      } else {
-        position.bottom = 'auto';
-        delete position.lineHeight;
-      }
-      if (this.placement.indexOf('absleft') !== -1) {
-        position.left = 0;
-      } else if (this.placement.indexOf('absright') !== -1) {
-        position.right = 0;
-        position.left = 'auto';
-      }
-      return position;
-    }
-  });
-
-  $.fn.textcomplete.Dropdown = Dropdown;
-  $.extend($.fn.textcomplete, commands);
-}(jQuery);
-
-+function ($) {
-  'use strict';
-
-  // Memoize a search function.
-  var memoize = function (func) {
-    var memo = {};
-    return function (term, callback) {
-      if (memo[term]) {
-        callback(memo[term]);
-      } else {
-        func.call(this, term, function (data) {
-          memo[term] = (memo[term] || []).concat(data);
-          callback.apply(null, arguments);
-        });
-      }
-    };
-  };
-
-  function Strategy(options) {
-    $.extend(this, options);
-    if (this.cache) { this.search = memoize(this.search); }
-  }
-
-  Strategy.parse = function (strategiesArray, params) {
-    return $.map(strategiesArray, function (strategy) {
-      var strategyObj = new Strategy(strategy);
-      strategyObj.el = params.el;
-      strategyObj.$el = params.$el;
-      return strategyObj;
-    });
-  };
-
-  $.extend(Strategy.prototype, {
-    // Public properties
-    // -----------------
-
-    // Required
-    match:      null,
-    replace:    null,
-    search:     null,
-
-    // Optional
-    id:         null,
-    cache:      false,
-    context:    function () { return true; },
-    index:      2,
-    template:   function (obj) { return obj; },
-    idProperty: null
-  });
-
-  $.fn.textcomplete.Strategy = Strategy;
-
-}(jQuery);
-
-+function ($) {
-  'use strict';
-
-  var now = Date.now || function () { return new Date().getTime(); };
-
-  // Returns a function, that, as long as it continues to be invoked, will not
-  // be triggered. The function will be called after it stops being called for
-  // `wait` msec.
-  //
-  // This utility function was originally implemented at Underscore.js.
-  var debounce = function (func, wait) {
-    var timeout, args, context, timestamp, result;
-    var later = function () {
-      var last = now() - timestamp;
-      if (last < wait) {
-        timeout = setTimeout(later, wait - last);
-      } else {
-        timeout = null;
-        result = func.apply(context, args);
-        context = args = null;
-      }
-    };
-
-    return function () {
-      context = this;
-      args = arguments;
-      timestamp = now();
-      if (!timeout) {
-        timeout = setTimeout(later, wait);
-      }
-      return result;
-    };
-  };
-
-  function Adapter () {}
-
-  $.extend(Adapter.prototype, {
-    // Public properties
-    // -----------------
-
-    id:        null, // Identity.
-    completer: null, // Completer object which creates it.
-    el:        null, // Textarea element.
-    $el:       null, // jQuery object of the textarea.
-    option:    null,
-
-    // Public methods
-    // --------------
-
-    initialize: function (element, completer, option) {
-      this.el        = element;
-      this.$el       = $(element);
-      this.id        = completer.id + this.constructor.name;
-      this.completer = completer;
-      this.option    = option;
-
-      if (this.option.debounce) {
-        this._onKeyup = debounce(this._onKeyup, this.option.debounce);
-      }
-
-      this._bindEvents();
-    },
-
-    destroy: function () {
-      this.$el.off('.' + this.id); // Remove all event handlers.
-      this.$el = this.el = this.completer = null;
-    },
-
-    // Update the element with the given value and strategy.
-    //
-    // value    - The selected object. It is one of the item of the array
-    //            which was callbacked from the search function.
-    // strategy - The Strategy associated with the selected value.
-    select: function (/* value, strategy */) {
-      throw new Error('Not implemented');
-    },
-
-    // Returns the caret's relative coordinates from body's left top corner.
-    getCaretPosition: function () {
-      var position = this._getCaretRelativePosition();
-      var offset = this.$el.offset();
-
-      // Calculate the left top corner of `this.option.appendTo` element.
-      var $parent = this.option.appendTo;
-      if ($parent) {
-         if (!($parent instanceof $)) { $parent = $($parent); }
-         var parentOffset = $parent.offsetParent().offset();
-         offset.top -= parentOffset.top;
-         offset.left -= parentOffset.left;
-      }
-
-      position.top += offset.top;
-      position.left += offset.left;
-      return position;
-    },
-
-    // Focus on the element.
-    focus: function () {
-      this.$el.focus();
-    },
-
-    // Private methods
-    // ---------------
-
-    _bindEvents: function () {
-      this.$el.on('keyup.' + this.id, $.proxy(this._onKeyup, this));
-    },
-
-    _onKeyup: function (e) {
-      if (this._skipSearch(e)) { return; }
-      this.completer.trigger(this.getTextFromHeadToCaret(), true);
-    },
-
-    // Suppress searching if it returns true.
-    _skipSearch: function (clickEvent) {
-      switch (clickEvent.keyCode) {
-        case 9:  // TAB
-        case 13: // ENTER
-        case 40: // DOWN
-        case 38: // UP
-          return true;
-      }
-      if (clickEvent.ctrlKey) switch (clickEvent.keyCode) {
-        case 78: // Ctrl-N
-        case 80: // Ctrl-P
-          return true;
-      }
-    }
-  });
-
-  $.fn.textcomplete.Adapter = Adapter;
-}(jQuery);
-
-+function ($) {
-  'use strict';
-
-  // Textarea adapter
-  // ================
-  //
-  // Managing a textarea. It doesn't know a Dropdown.
-  function Textarea(element, completer, option) {
-    this.initialize(element, completer, option);
-  }
-
-  $.extend(Textarea.prototype, $.fn.textcomplete.Adapter.prototype, {
-    // Public methods
-    // --------------
-
-    // Update the textarea with the given value and strategy.
-    select: function (value, strategy, e) {
-      var pre = this.getTextFromHeadToCaret();
-      var post = this.el.value.substring(this.el.selectionEnd);
-      var newSubstr = strategy.replace(value, e);
-      if (typeof newSubstr !== 'undefined') {
-        if ($.isArray(newSubstr)) {
-          post = newSubstr[1] + post;
-          newSubstr = newSubstr[0];
-        }
-        pre = pre.replace(strategy.match, newSubstr);
-        this.$el.val(pre + post);
-        this.el.selectionStart = this.el.selectionEnd = pre.length;
-      }
-    },
-
-    getTextFromHeadToCaret: function () {
-      return this.el.value.substring(0, this.el.selectionEnd);
-    },
-
-    // Private methods
-    // ---------------
-
-    _getCaretRelativePosition: function () {
-      var p = $.fn.textcomplete.getCaretCoordinates(this.el, this.el.selectionStart);
-      return {
-        top: p.top + this._calculateLineHeight() - this.$el.scrollTop(),
-        left: p.left - this.$el.scrollLeft()
-      };
-    },
-
-    _calculateLineHeight: function () {
-      var lineHeight = parseInt(this.$el.css('line-height'), 10);
-      if (isNaN(lineHeight)) {
-        // http://stackoverflow.com/a/4515470/1297336
-        var parentNode = this.el.parentNode;
-        var temp = document.createElement(this.el.nodeName);
-        var style = this.el.style;
-        temp.setAttribute(
-          'style',
-          'margin:0px;padding:0px;font-family:' + style.fontFamily + ';font-size:' + style.fontSize
-        );
-        temp.innerHTML = 'test';
-        parentNode.appendChild(temp);
-        lineHeight = temp.clientHeight;
-        parentNode.removeChild(temp);
-      }
-      return lineHeight;
-    }
-  });
-
-  $.fn.textcomplete.Textarea = Textarea;
-}(jQuery);
-
-+function ($) {
-  'use strict';
-
-  var sentinelChar = '吶';
-
-  function IETextarea(element, completer, option) {
-    this.initialize(element, completer, option);
-    $('<span>' + sentinelChar + '</span>').css({
-      position: 'absolute',
-      top: -9999,
-      left: -9999
-    }).insertBefore(element);
-  }
-
-  $.extend(IETextarea.prototype, $.fn.textcomplete.Textarea.prototype, {
-    // Public methods
-    // --------------
-
-    select: function (value, strategy, e) {
-      var pre = this.getTextFromHeadToCaret();
-      var post = this.el.value.substring(pre.length);
-      var newSubstr = strategy.replace(value, e);
-      if (typeof newSubstr !== 'undefined') {
-        if ($.isArray(newSubstr)) {
-          post = newSubstr[1] + post;
-          newSubstr = newSubstr[0];
-        }
-        pre = pre.replace(strategy.match, newSubstr);
-        this.$el.val(pre + post);
-        this.el.focus();
-        var range = this.el.createTextRange();
-        range.collapse(true);
-        range.moveEnd('character', pre.length);
-        range.moveStart('character', pre.length);
-        range.select();
-      }
-    },
-
-    getTextFromHeadToCaret: function () {
-      this.el.focus();
-      var range = document.selection.createRange();
-      range.moveStart('character', -this.el.value.length);
-      var arr = range.text.split(sentinelChar)
-      return arr.length === 1 ? arr[0] : arr[1];
-    }
-  });
-
-  $.fn.textcomplete.IETextarea = IETextarea;
-}(jQuery);
-
-// NOTE: TextComplete plugin has contenteditable support but it does not work
-//       fine especially on old IEs.
-//       Any pull requests are REALLY welcome.
-
-+function ($) {
-  'use strict';
-
-  // ContentEditable adapter
-  // =======================
-  //
-  // Adapter for contenteditable elements.
-  function ContentEditable (element, completer, option) {
-    this.initialize(element, completer, option);
-  }
-
-  $.extend(ContentEditable.prototype, $.fn.textcomplete.Adapter.prototype, {
-    // Public methods
-    // --------------
-
-    // Update the content with the given value and strategy.
-    // When an dropdown item is selected, it is executed.
-    select: function (value, strategy, e) {
-      var pre = this.getTextFromHeadToCaret();
-      var sel = window.getSelection()
-      var range = sel.getRangeAt(0);
-      var selection = range.cloneRange();
-      selection.selectNodeContents(range.startContainer);
-      var content = selection.toString();
-      var post = content.substring(range.startOffset);
-      var newSubstr = strategy.replace(value, e);
-      if (typeof newSubstr !== 'undefined') {
-        if ($.isArray(newSubstr)) {
-          post = newSubstr[1] + post;
-          newSubstr = newSubstr[0];
-        }
-        pre = pre.replace(strategy.match, newSubstr);
-        range.selectNodeContents(range.startContainer);
-        range.deleteContents();
-        
-        // create temporary elements
-        var preWrapper = document.createElement("div");
-        preWrapper.innerHTML = pre;
-        var postWrapper = document.createElement("div");
-        postWrapper.innerHTML = post;
-        
-        // create the fragment thats inserted
-        var fragment = document.createDocumentFragment();
-        var childNode;
-        var lastOfPre;
-        while (childNode = preWrapper.firstChild) {
-               lastOfPre = fragment.appendChild(childNode);
-        }
-        while (childNode = postWrapper.firstChild) {
-               fragment.appendChild(childNode);
-        }
-        
-        // insert the fragment & jump behind the last node in "pre"
-        range.insertNode(fragment);
-        range.setStartAfter(lastOfPre);
-        
-        range.collapse(true);
-        sel.removeAllRanges();
-        sel.addRange(range);
-      }
-    },
-
-    // Private methods
-    // ---------------
-
-    // Returns the caret's relative position from the contenteditable's
-    // left top corner.
-    //
-    // Examples
-    //
-    //   this._getCaretRelativePosition()
-    //   //=> { top: 18, left: 200, lineHeight: 16 }
-    //
-    // Dropdown's position will be decided using the result.
-    _getCaretRelativePosition: function () {
-      var range = window.getSelection().getRangeAt(0).cloneRange();
-      var node = document.createElement('span');
-      range.insertNode(node);
-      range.selectNodeContents(node);
-      range.deleteContents();
-      var $node = $(node);
-      var position = $node.offset();
-      position.left -= this.$el.offset().left;
-      position.top += $node.height() - this.$el.offset().top;
-      position.lineHeight = $node.height();
-      $node.remove();
-      return position;
-    },
-
-    // Returns the string between the first character and the caret.
-    // Completer will be triggered with the result for start autocompleting.
-    //
-    // Example
-    //
-    //   // Suppose the html is '<b>hello</b> wor|ld' and | is the caret.
-    //   this.getTextFromHeadToCaret()
-    //   // => ' wor'  // not '<b>hello</b> wor'
-    getTextFromHeadToCaret: function () {
-      var range = window.getSelection().getRangeAt(0);
-      var selection = range.cloneRange();
-      selection.selectNodeContents(range.startContainer);
-      return selection.toString().substring(0, range.startOffset);
-    }
-  });
-
-  $.fn.textcomplete.ContentEditable = ContentEditable;
-}(jQuery);
-
-// The MIT License (MIT)
-// 
-// Copyright (c) 2015 Jonathan Ong me@jongleberry.com
-// 
-// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
-// associated documentation files (the "Software"), to deal in the Software without restriction,
-// including without limitation the rights to use, copy, modify, merge, publish, distribute,
-// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-// 
-// The above copyright notice and this permission notice shall be included in all copies or
-// substantial portions of the Software.
-// 
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
-// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-// https://github.com/component/textarea-caret-position
-
-(function ($) {
-
-// The properties that we copy into a mirrored div.
-// Note that some browsers, such as Firefox,
-// do not concatenate properties, i.e. padding-top, bottom etc. -> padding,
-// so we have to do every single property specifically.
-var properties = [
-  'direction',  // RTL support
-  'boxSizing',
-  'width',  // on Chrome and IE, exclude the scrollbar, so the mirror div wraps exactly as the textarea does
-  'height',
-  'overflowX',
-  'overflowY',  // copy the scrollbar for IE
-
-  'borderTopWidth',
-  'borderRightWidth',
-  'borderBottomWidth',
-  'borderLeftWidth',
-  'borderStyle',
-
-  'paddingTop',
-  'paddingRight',
-  'paddingBottom',
-  'paddingLeft',
-
-  // https://developer.mozilla.org/en-US/docs/Web/CSS/font
-  'fontStyle',
-  'fontVariant',
-  'fontWeight',
-  'fontStretch',
-  'fontSize',
-  'fontSizeAdjust',
-  'lineHeight',
-  'fontFamily',
-
-  'textAlign',
-  'textTransform',
-  'textIndent',
-  'textDecoration',  // might not make a difference, but better be safe
-
-  'letterSpacing',
-  'wordSpacing',
-
-  'tabSize',
-  'MozTabSize'
-
-];
-
-var isBrowser = (typeof window !== 'undefined');
-var isFirefox = (isBrowser && window.mozInnerScreenX != null);
-
-function getCaretCoordinates(element, position, options) {
-  if(!isBrowser) {
-    throw new Error('textarea-caret-position#getCaretCoordinates should only be called in a browser');
-  }
-
-  var debug = options && options.debug || false;
-  if (debug) {
-    var el = document.querySelector('#input-textarea-caret-position-mirror-div');
-    if ( el ) { el.parentNode.removeChild(el); }
-  }
-
-  // mirrored div
-  var div = document.createElement('div');
-  div.id = 'input-textarea-caret-position-mirror-div';
-  document.body.appendChild(div);
-
-  var style = div.style;
-  var computed = window.getComputedStyle? getComputedStyle(element) : element.currentStyle;  // currentStyle for IE < 9
-
-  // default textarea styles
-  style.whiteSpace = 'pre-wrap';
-  if (element.nodeName !== 'INPUT')
-    style.wordWrap = 'break-word';  // only for textarea-s
-
-  // position off-screen
-  style.position = 'absolute';  // required to return coordinates properly
-  if (!debug)
-    style.visibility = 'hidden';  // not 'display: none' because we want rendering
-
-  // transfer the element's properties to the div
-  properties.forEach(function (prop) {
-    style[prop] = computed[prop];
-  });
-
-  if (isFirefox) {
-    // Firefox lies about the overflow property for textareas: https://bugzilla.mozilla.org/show_bug.cgi?id=984275
-    if (element.scrollHeight > parseInt(computed.height))
-      style.overflowY = 'scroll';
-  } else {
-    style.overflow = 'hidden';  // for Chrome to not render a scrollbar; IE keeps overflowY = 'scroll'
-  }
-
-  div.textContent = element.value.substring(0, position);
-  // the second special handling for input type="text" vs textarea: spaces need to be replaced with non-breaking spaces - http://stackoverflow.com/a/13402035/1269037
-  if (element.nodeName === 'INPUT')
-    div.textContent = div.textContent.replace(/\s/g, '\u00a0');
-
-  var span = document.createElement('span');
-  // Wrapping must be replicated *exactly*, including when a long word gets
-  // onto the next line, with whitespace at the end of the line before (#7).
-  // The  *only* reliable way to do that is to copy the *entire* rest of the
-  // textarea's content into the <span> created at the caret position.
-  // for inputs, just '.' would be enough, but why bother?
-  span.textContent = element.value.substring(position) || '.';  // || because a completely empty faux span doesn't render at all
-  div.appendChild(span);
-
-  var coordinates = {
-    top: span.offsetTop + parseInt(computed['borderTopWidth']),
-    left: span.offsetLeft + parseInt(computed['borderLeftWidth'])
-  };
-
-  if (debug) {
-    span.style.backgroundColor = '#aaa';
-  } else {
-    document.body.removeChild(div);
-  }
-
-  return coordinates;
-}
-
-$.fn.textcomplete.getCaretCoordinates = getCaretCoordinates;
-
-}(jQuery));
-
-return jQuery;
-}));
diff --git a/library/jquery-textcomplete/jquery.textcomplete.min.js b/library/jquery-textcomplete/jquery.textcomplete.min.js
deleted file mode 100644 (file)
index d3a427f..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-/*! jquery-textcomplete - v1.3.4 - 2016-04-19 */
-!function(a){if("function"==typeof define&&define.amd)define(["jquery"],a);else if("object"==typeof module&&module.exports){var b=require("jquery");module.exports=a(b)}else a(jQuery)}(function(a){if("undefined"==typeof a)throw new Error("jQuery.textcomplete requires jQuery");return+function(a){"use strict";var b=function(a){console.warn&&console.warn(a)},c=1;a.fn.textcomplete=function(d,e){var f=Array.prototype.slice.call(arguments);return this.each(function(){var g=this,h=a(this),i=h.data("textComplete");if(i||(e||(e={}),e._oid=c++,i=new a.fn.textcomplete.Completer(this,e),h.data("textComplete",i)),"string"==typeof d){if(!i)return;f.shift(),i[d].apply(i,f),"destroy"===d&&h.removeData("textComplete")}else a.each(d,function(c){a.each(["header","footer","placement","maxCount"],function(a){c[a]&&(i.option[a]=c[a],b(a+"as a strategy param is deprecated. Use option."),delete c[a])})}),i.register(a.fn.textcomplete.Strategy.parse(d,{el:g,$el:h}))})}}(a),+function(a){"use strict";function b(c,d){if(this.$el=a(c),this.id="textcomplete"+f++,this.strategies=[],this.views=[],this.option=a.extend({},b._getDefaults(),d),!(this.$el.is("input[type=text]")||this.$el.is("input[type=search]")||this.$el.is("textarea")||c.isContentEditable||"true"==c.contentEditable))throw new Error("textcomplete must be called on a Textarea or a ContentEditable.");if(c===document.activeElement)this.initialize();else{var e=this;this.$el.one("focus."+this.id,function(){e.initialize()})}}var c=function(a){var b,c;return function(){var d=Array.prototype.slice.call(arguments);if(b)return void(c=d);b=!0;var e=this;d.unshift(function f(){if(c){var d=c;c=void 0,d.unshift(f),a.apply(e,d)}else b=!1}),a.apply(this,d)}},d=function(a){return"[object String]"===Object.prototype.toString.call(a)},e=function(a){return"[object Function]"===Object.prototype.toString.call(a)},f=0;b._getDefaults=function(){return b.DEFAULTS||(b.DEFAULTS={appendTo:a("body"),zIndex:"100"}),b.DEFAULTS},a.extend(b.prototype,{id:null,option:null,strategies:null,adapter:null,dropdown:null,$el:null,initialize:function(){var b=this.$el.get(0);this.dropdown=new a.fn.textcomplete.Dropdown(b,this,this.option);var c,d;this.option.adapter?c=this.option.adapter:(d=this.$el.is("textarea")||this.$el.is("input[type=text]")||this.$el.is("input[type=search]")?"number"==typeof b.selectionEnd?"Textarea":"IETextarea":"ContentEditable",c=a.fn.textcomplete[d]),this.adapter=new c(b,this,this.option)},destroy:function(){this.$el.off("."+this.id),this.adapter&&this.adapter.destroy(),this.dropdown&&this.dropdown.destroy(),this.$el=this.adapter=this.dropdown=null},deactivate:function(){this.dropdown&&this.dropdown.deactivate()},trigger:function(a,b){this.dropdown||this.initialize(),null!=a||(a=this.adapter.getTextFromHeadToCaret());var c=this._extractSearchQuery(a);if(c.length){var d=c[1];if(b&&this._term===d&&""!==d)return;this._term=d,this._search.apply(this,c)}else this._term=null,this.dropdown.deactivate()},fire:function(a){var b=Array.prototype.slice.call(arguments,1);return this.$el.trigger(a,b),this},register:function(a){Array.prototype.push.apply(this.strategies,a)},select:function(a,b,c){this._term=null,this.adapter.select(a,b,c),this.fire("change").fire("textComplete:select",a,b),this.adapter.focus()},_clearAtNext:!0,_term:null,_extractSearchQuery:function(a){for(var b=0;b<this.strategies.length;b++){var c=this.strategies[b],f=c.context(a);if(f||""===f){var g=e(c.match)?c.match(a):c.match;d(f)&&(a=f);var h=a.match(g);if(h)return[c,h[c.index],h]}}return[]},_search:c(function(a,b,c,d){var e=this;b.search(c,function(d,f){e.dropdown.shown||e.dropdown.activate(),e._clearAtNext&&(e.dropdown.clear(),e._clearAtNext=!1),e.dropdown.setPosition(e.adapter.getCaretPosition()),e.dropdown.render(e._zip(d,b,c)),f||(a(),e._clearAtNext=!0)},d)}),_zip:function(b,c,d){return a.map(b,function(a){return{value:a,strategy:c,term:d}})}}),a.fn.textcomplete.Completer=b}(a),+function(a){"use strict";function b(c,d,f){this.$el=b.createElement(f),this.completer=d,this.id=d.id+"dropdown",this._data=[],this.$inputEl=a(c),this.option=f,f.listPosition&&(this.setPosition=f.listPosition),f.height&&this.$el.height(f.height);var g=this;a.each(["maxCount","placement","footer","header","noResultsMessage","className"],function(a,b){null!=f[b]&&(g[b]=f[b])}),this._bindEvents(c),e[this.id]=this}var c=a(window),d=function(a,b){var c,d,e=b.strategy.idProperty;for(c=0;c<a.length;c++)if(d=a[c],d.strategy===b.strategy)if(e){if(d.value[e]===b.value[e])return!0}else if(d.value===b.value)return!0;return!1},e={};a(document).on("click",function(b){var c=b.originalEvent&&b.originalEvent.keepTextCompleteDropdown;a.each(e,function(a,b){a!==c&&b.deactivate()})});var f={SKIP_DEFAULT:0,KEY_UP:1,KEY_DOWN:2,KEY_ENTER:3,KEY_PAGEUP:4,KEY_PAGEDOWN:5,KEY_ESCAPE:6};a.extend(b,{createElement:function(b){var c=b.appendTo;c instanceof a||(c=a(c));var d=a("<ul></ul>").addClass("dropdown-menu textcomplete-dropdown").attr("id","textcomplete-dropdown-"+b._oid).css({display:"none",left:0,position:"absolute",zIndex:b.zIndex}).appendTo(c);return d}}),a.extend(b.prototype,{$el:null,$inputEl:null,completer:null,footer:null,header:null,id:null,maxCount:10,placement:"",shown:!1,data:[],className:"",destroy:function(){this.deactivate(),this.$el.off("."+this.id),this.$inputEl.off("."+this.id),this.clear(),this.$el.remove(),this.$el=this.$inputEl=this.completer=null,delete e[this.id]},render:function(b){var c=this._buildContents(b),d=a.map(this.data,function(a){return a.value});if(this.data.length){var e=b[0].strategy;e.id?this.$el.attr("data-strategy",e.id):this.$el.removeAttr("data-strategy"),this._renderHeader(d),this._renderFooter(d),c&&(this._renderContents(c),this._fitToBottom(),this._fitToRight(),this._activateIndexedItem()),this._setScroll()}else this.noResultsMessage?this._renderNoResultsMessage(d):this.shown&&this.deactivate()},setPosition:function(b){var d="absolute";return this.$inputEl.add(this.$inputEl.parents()).each(function(){return"absolute"===a(this).css("position")?!1:"fixed"===a(this).css("position")?(b.top-=c.scrollTop(),b.left-=c.scrollLeft(),d="fixed",!1):void 0}),this.$el.css(this._applyPlacement(b)),this.$el.css({position:d}),this},clear:function(){this.$el.html(""),this.data=[],this._index=0,this._$header=this._$footer=this._$noResultsMessage=null},activate:function(){return this.shown||(this.clear(),this.$el.show(),this.className&&this.$el.addClass(this.className),this.completer.fire("textComplete:show"),this.shown=!0),this},deactivate:function(){return this.shown&&(this.$el.hide(),this.className&&this.$el.removeClass(this.className),this.completer.fire("textComplete:hide"),this.shown=!1),this},isUp:function(a){return 38===a.keyCode||a.ctrlKey&&80===a.keyCode},isDown:function(a){return 40===a.keyCode||a.ctrlKey&&78===a.keyCode},isEnter:function(a){var b=a.ctrlKey||a.altKey||a.metaKey||a.shiftKey;return!b&&(13===a.keyCode||9===a.keyCode||this.option.completeOnSpace===!0&&32===a.keyCode)},isPageup:function(a){return 33===a.keyCode},isPagedown:function(a){return 34===a.keyCode},isEscape:function(a){return 27===a.keyCode},_data:null,_index:null,_$header:null,_$noResultsMessage:null,_$footer:null,_bindEvents:function(){this.$el.on("mousedown."+this.id,".textcomplete-item",a.proxy(this._onClick,this)),this.$el.on("touchstart."+this.id,".textcomplete-item",a.proxy(this._onClick,this)),this.$el.on("mouseover."+this.id,".textcomplete-item",a.proxy(this._onMouseover,this)),this.$inputEl.on("keydown."+this.id,a.proxy(this._onKeydown,this))},_onClick:function(b){var c=a(b.target);b.preventDefault(),b.originalEvent.keepTextCompleteDropdown=this.id,c.hasClass("textcomplete-item")||(c=c.closest(".textcomplete-item"));var d=this.data[parseInt(c.data("index"),10)];this.completer.select(d.value,d.strategy,b);var e=this;setTimeout(function(){e.deactivate(),"touchstart"===b.type&&e.$inputEl.focus()},0)},_onMouseover:function(b){var c=a(b.target);b.preventDefault(),c.hasClass("textcomplete-item")||(c=c.closest(".textcomplete-item")),this._index=parseInt(c.data("index"),10),this._activateIndexedItem()},_onKeydown:function(b){if(this.shown){var c;switch(a.isFunction(this.option.onKeydown)&&(c=this.option.onKeydown(b,f)),null==c&&(c=this._defaultKeydown(b)),c){case f.KEY_UP:b.preventDefault(),this._up();break;case f.KEY_DOWN:b.preventDefault(),this._down();break;case f.KEY_ENTER:b.preventDefault(),this._enter(b);break;case f.KEY_PAGEUP:b.preventDefault(),this._pageup();break;case f.KEY_PAGEDOWN:b.preventDefault(),this._pagedown();break;case f.KEY_ESCAPE:b.preventDefault(),this.deactivate()}}},_defaultKeydown:function(a){return this.isUp(a)?f.KEY_UP:this.isDown(a)?f.KEY_DOWN:this.isEnter(a)?f.KEY_ENTER:this.isPageup(a)?f.KEY_PAGEUP:this.isPagedown(a)?f.KEY_PAGEDOWN:this.isEscape(a)?f.KEY_ESCAPE:void 0},_up:function(){0===this._index?this._index=this.data.length-1:this._index-=1,this._activateIndexedItem(),this._setScroll()},_down:function(){this._index===this.data.length-1?this._index=0:this._index+=1,this._activateIndexedItem(),this._setScroll()},_enter:function(a){var b=this.data[parseInt(this._getActiveElement().data("index"),10)];this.completer.select(b.value,b.strategy,a),this.deactivate()},_pageup:function(){var b=0,c=this._getActiveElement().position().top-this.$el.innerHeight();this.$el.children().each(function(d){return a(this).position().top+a(this).outerHeight()>c?(b=d,!1):void 0}),this._index=b,this._activateIndexedItem(),this._setScroll()},_pagedown:function(){var b=this.data.length-1,c=this._getActiveElement().position().top+this.$el.innerHeight();this.$el.children().each(function(d){return a(this).position().top>c?(b=d,!1):void 0}),this._index=b,this._activateIndexedItem(),this._setScroll()},_activateIndexedItem:function(){this.$el.find(".textcomplete-item.active").removeClass("active"),this._getActiveElement().addClass("active")},_getActiveElement:function(){return this.$el.children(".textcomplete-item:nth("+this._index+")")},_setScroll:function(){var a=this._getActiveElement(),b=a.position().top,c=a.outerHeight(),d=this.$el.innerHeight(),e=this.$el.scrollTop();0===this._index||this._index==this.data.length-1||0>b?this.$el.scrollTop(b+e):b+c>d&&this.$el.scrollTop(b+c+e-d)},_buildContents:function(a){var b,c,e,f="";for(c=0;c<a.length&&this.data.length!==this.maxCount;c++)b=a[c],d(this.data,b)||(e=this.data.length,this.data.push(b),f+='<li class="textcomplete-item" data-index="'+e+'"><a>',f+=b.strategy.template(b.value,b.term),f+="</a></li>");return f},_renderHeader:function(b){if(this.header){this._$header||(this._$header=a('<li class="textcomplete-header"></li>').prependTo(this.$el));var c=a.isFunction(this.header)?this.header(b):this.header;this._$header.html(c)}},_renderFooter:function(b){if(this.footer){this._$footer||(this._$footer=a('<li class="textcomplete-footer"></li>').appendTo(this.$el));var c=a.isFunction(this.footer)?this.footer(b):this.footer;this._$footer.html(c)}},_renderNoResultsMessage:function(b){if(this.noResultsMessage){this._$noResultsMessage||(this._$noResultsMessage=a('<li class="textcomplete-no-results-message"></li>').appendTo(this.$el));var c=a.isFunction(this.noResultsMessage)?this.noResultsMessage(b):this.noResultsMessage;this._$noResultsMessage.html(c)}},_renderContents:function(a){this._$footer?this._$footer.before(a):this.$el.append(a)},_fitToBottom:function(){var a=c.scrollTop()+c.height(),b=this.$el.height();this.$el.position().top+b>a&&this.$el.offset({top:a-b})},_fitToRight:function(){for(var a,b=30,d=this.$el.offset().left,e=this.$el.width(),f=c.width()-b;d+e>f&&(this.$el.offset({left:d-b}),a=this.$el.offset().left,!(a>=d));)d=a},_applyPlacement:function(a){return-1!==this.placement.indexOf("top")?a={top:"auto",bottom:this.$el.parent().height()-a.top+a.lineHeight,left:a.left}:(a.bottom="auto",delete a.lineHeight),-1!==this.placement.indexOf("absleft")?a.left=0:-1!==this.placement.indexOf("absright")&&(a.right=0,a.left="auto"),a}}),a.fn.textcomplete.Dropdown=b,a.extend(a.fn.textcomplete,f)}(a),+function(a){"use strict";function b(b){a.extend(this,b),this.cache&&(this.search=c(this.search))}var c=function(a){var b={};return function(c,d){b[c]?d(b[c]):a.call(this,c,function(a){b[c]=(b[c]||[]).concat(a),d.apply(null,arguments)})}};b.parse=function(c,d){return a.map(c,function(a){var c=new b(a);return c.el=d.el,c.$el=d.$el,c})},a.extend(b.prototype,{match:null,replace:null,search:null,id:null,cache:!1,context:function(){return!0},index:2,template:function(a){return a},idProperty:null}),a.fn.textcomplete.Strategy=b}(a),+function(a){"use strict";function b(){}var c=Date.now||function(){return(new Date).getTime()},d=function(a,b){var d,e,f,g,h,i=function(){var j=c()-g;b>j?d=setTimeout(i,b-j):(d=null,h=a.apply(f,e),f=e=null)};return function(){return f=this,e=arguments,g=c(),d||(d=setTimeout(i,b)),h}};a.extend(b.prototype,{id:null,completer:null,el:null,$el:null,option:null,initialize:function(b,c,e){this.el=b,this.$el=a(b),this.id=c.id+this.constructor.name,this.completer=c,this.option=e,this.option.debounce&&(this._onKeyup=d(this._onKeyup,this.option.debounce)),this._bindEvents()},destroy:function(){this.$el.off("."+this.id),this.$el=this.el=this.completer=null},select:function(){throw new Error("Not implemented")},getCaretPosition:function(){var b=this._getCaretRelativePosition(),c=this.$el.offset(),d=this.option.appendTo;if(d){d instanceof a||(d=a(d));var e=d.offsetParent().offset();c.top-=e.top,c.left-=e.left}return b.top+=c.top,b.left+=c.left,b},focus:function(){this.$el.focus()},_bindEvents:function(){this.$el.on("keyup."+this.id,a.proxy(this._onKeyup,this))},_onKeyup:function(a){this._skipSearch(a)||this.completer.trigger(this.getTextFromHeadToCaret(),!0)},_skipSearch:function(a){switch(a.keyCode){case 9:case 13:case 40:case 38:return!0}if(a.ctrlKey)switch(a.keyCode){case 78:case 80:return!0}}}),a.fn.textcomplete.Adapter=b}(a),+function(a){"use strict";function b(a,b,c){this.initialize(a,b,c)}a.extend(b.prototype,a.fn.textcomplete.Adapter.prototype,{select:function(b,c,d){var e=this.getTextFromHeadToCaret(),f=this.el.value.substring(this.el.selectionEnd),g=c.replace(b,d);"undefined"!=typeof g&&(a.isArray(g)&&(f=g[1]+f,g=g[0]),e=e.replace(c.match,g),this.$el.val(e+f),this.el.selectionStart=this.el.selectionEnd=e.length)},getTextFromHeadToCaret:function(){return this.el.value.substring(0,this.el.selectionEnd)},_getCaretRelativePosition:function(){var b=a.fn.textcomplete.getCaretCoordinates(this.el,this.el.selectionStart);return{top:b.top+this._calculateLineHeight()-this.$el.scrollTop(),left:b.left-this.$el.scrollLeft()}},_calculateLineHeight:function(){var a=parseInt(this.$el.css("line-height"),10);if(isNaN(a)){var b=this.el.parentNode,c=document.createElement(this.el.nodeName),d=this.el.style;c.setAttribute("style","margin:0px;padding:0px;font-family:"+d.fontFamily+";font-size:"+d.fontSize),c.innerHTML="test",b.appendChild(c),a=c.clientHeight,b.removeChild(c)}return a}}),a.fn.textcomplete.Textarea=b}(a),+function(a){"use strict";function b(b,d,e){this.initialize(b,d,e),a("<span>"+c+"</span>").css({position:"absolute",top:-9999,left:-9999}).insertBefore(b)}var c="吶";a.extend(b.prototype,a.fn.textcomplete.Textarea.prototype,{select:function(b,c,d){var e=this.getTextFromHeadToCaret(),f=this.el.value.substring(e.length),g=c.replace(b,d);if("undefined"!=typeof g){a.isArray(g)&&(f=g[1]+f,g=g[0]),e=e.replace(c.match,g),this.$el.val(e+f),this.el.focus();var h=this.el.createTextRange();h.collapse(!0),h.moveEnd("character",e.length),h.moveStart("character",e.length),h.select()}},getTextFromHeadToCaret:function(){this.el.focus();var a=document.selection.createRange();a.moveStart("character",-this.el.value.length);var b=a.text.split(c);return 1===b.length?b[0]:b[1]}}),a.fn.textcomplete.IETextarea=b}(a),+function(a){"use strict";function b(a,b,c){this.initialize(a,b,c)}a.extend(b.prototype,a.fn.textcomplete.Adapter.prototype,{select:function(b,c,d){var e=this.getTextFromHeadToCaret(),f=window.getSelection(),g=f.getRangeAt(0),h=g.cloneRange();h.selectNodeContents(g.startContainer);var i=h.toString(),j=i.substring(g.startOffset),k=c.replace(b,d);if("undefined"!=typeof k){a.isArray(k)&&(j=k[1]+j,k=k[0]),e=e.replace(c.match,k),g.selectNodeContents(g.startContainer),g.deleteContents();var l=document.createElement("div");l.innerHTML=e;var m=document.createElement("div");m.innerHTML=j;for(var n,o,p=document.createDocumentFragment();n=l.firstChild;)o=p.appendChild(n);for(;n=m.firstChild;)p.appendChild(n);g.insertNode(p),g.setStartAfter(o),g.collapse(!0),f.removeAllRanges(),f.addRange(g)}},_getCaretRelativePosition:function(){var b=window.getSelection().getRangeAt(0).cloneRange(),c=document.createElement("span");b.insertNode(c),b.selectNodeContents(c),b.deleteContents();var d=a(c),e=d.offset();return e.left-=this.$el.offset().left,e.top+=d.height()-this.$el.offset().top,e.lineHeight=d.height(),d.remove(),e},getTextFromHeadToCaret:function(){var a=window.getSelection().getRangeAt(0),b=a.cloneRange();return b.selectNodeContents(a.startContainer),b.toString().substring(0,a.startOffset)}}),a.fn.textcomplete.ContentEditable=b}(a),function(a){function b(a,b,f){if(!d)throw new Error("textarea-caret-position#getCaretCoordinates should only be called in a browser");var g=f&&f.debug||!1;if(g){var h=document.querySelector("#input-textarea-caret-position-mirror-div");h&&h.parentNode.removeChild(h)}var i=document.createElement("div");i.id="input-textarea-caret-position-mirror-div",document.body.appendChild(i);var j=i.style,k=window.getComputedStyle?getComputedStyle(a):a.currentStyle;j.whiteSpace="pre-wrap","INPUT"!==a.nodeName&&(j.wordWrap="break-word"),j.position="absolute",g||(j.visibility="hidden"),c.forEach(function(a){j[a]=k[a]}),e?a.scrollHeight>parseInt(k.height)&&(j.overflowY="scroll"):j.overflow="hidden",i.textContent=a.value.substring(0,b),"INPUT"===a.nodeName&&(i.textContent=i.textContent.replace(/\s/g," "));var l=document.createElement("span");l.textContent=a.value.substring(b)||".",i.appendChild(l);var m={top:l.offsetTop+parseInt(k.borderTopWidth),left:l.offsetLeft+parseInt(k.borderLeftWidth)};return g?l.style.backgroundColor="#aaa":document.body.removeChild(i),m}var c=["direction","boxSizing","width","height","overflowX","overflowY","borderTopWidth","borderRightWidth","borderBottomWidth","borderLeftWidth","borderStyle","paddingTop","paddingRight","paddingBottom","paddingLeft","fontStyle","fontVariant","fontWeight","fontStretch","fontSize","fontSizeAdjust","lineHeight","fontFamily","textAlign","textTransform","textIndent","textDecoration","letterSpacing","wordSpacing","tabSize","MozTabSize"],d="undefined"!=typeof window,e=d&&null!=window.mozInnerScreenX;a.fn.textcomplete.getCaretCoordinates=b}(a),a});
-//# sourceMappingURL=dist/jquery.textcomplete.min.map
\ No newline at end of file
diff --git a/library/jquery-textcomplete/jquery.textcomplete.min.map b/library/jquery-textcomplete/jquery.textcomplete.min.map
deleted file mode 100644 (file)
index e27ef4d..0000000
+++ /dev/null
@@ -1 +0,0 @@
-{"version":3,"file":"dist/jquery.textcomplete.min.js","sources":["dist/jquery.textcomplete.js"],"names":["factory","define","amd","module","exports","$","require","jQuery","Error","warn","message","console","id","fn","textcomplete","strategies","option","args","Array","prototype","slice","call","arguments","this","each","self","$this","completer","data","_oid","Completer","shift","apply","removeData","obj","name","register","Strategy","parse","el","$el","element","uniqueId","views","extend","_getDefaults","is","isContentEditable","contentEditable","document","activeElement","initialize","one","lock","func","locked","queuedArgsToReplay","unshift","replayOrFree","replayArgs","undefined","isString","Object","toString","isFunction","DEFAULTS","appendTo","zIndex","adapter","dropdown","get","Dropdown","Adapter","viewName","selectionEnd","destroy","off","deactivate","trigger","text","skipUnchangedTerm","getTextFromHeadToCaret","searchQuery","_extractSearchQuery","length","term","_term","_search","fire","eventName","push","select","value","strategy","e","focus","_clearAtNext","i","context","matchRegexp","match","index","free","search","stillSearching","shown","activate","clear","setPosition","getCaretPosition","render","_zip","map","createElement","_data","$inputEl","listPosition","height","_i","_bindEvents","dropdownViews","$window","window","include","zippedData","datum","elem","idProperty","on","originalEvent","keepTextCompleteDropdown","key","view","commands","SKIP_DEFAULT","KEY_UP","KEY_DOWN","KEY_ENTER","KEY_PAGEUP","KEY_PAGEDOWN","KEY_ESCAPE","$parent","addClass","attr","css","display","left","position","footer","header","maxCount","placement","className","remove","contentsHtml","_buildContents","unzippedData","d","removeAttr","_renderHeader","_renderFooter","_renderContents","_fitToBottom","_fitToRight","_activateIndexedItem","_setScroll","noResultsMessage","_renderNoResultsMessage","pos","add","parents","top","scrollTop","scrollLeft","_applyPlacement","html","_index","_$header","_$footer","_$noResultsMessage","show","hide","removeClass","isUp","keyCode","ctrlKey","isDown","isEnter","modifiers","altKey","metaKey","shiftKey","completeOnSpace","isPageup","isPagedown","isEscape","proxy","_onClick","_onMouseover","_onKeydown","target","preventDefault","hasClass","closest","parseInt","setTimeout","type","command","onKeydown","_defaultKeydown","_up","_down","_enter","_pageup","_pagedown","_getActiveElement","threshold","innerHeight","children","outerHeight","find","$activeEl","itemTop","itemHeight","visibleHeight","visibleTop","template","prependTo","before","append","windowScrollBottom","offset","tolerance","lastOffset","width","maxLeft","indexOf","bottom","parent","lineHeight","right","options","cache","memoize","memo","callback","concat","strategiesArray","params","strategyObj","replace","now","Date","getTime","debounce","wait","timeout","timestamp","result","later","last","constructor","_onKeyup","_getCaretRelativePosition","parentOffset","offsetParent","_skipSearch","clickEvent","Textarea","pre","post","substring","newSubstr","isArray","val","selectionStart","p","getCaretCoordinates","_calculateLineHeight","isNaN","parentNode","temp","nodeName","style","setAttribute","fontFamily","fontSize","innerHTML","appendChild","clientHeight","removeChild","IETextarea","sentinelChar","insertBefore","range","createTextRange","collapse","moveEnd","moveStart","selection","createRange","arr","split","ContentEditable","sel","getSelection","getRangeAt","cloneRange","selectNodeContents","startContainer","content","startOffset","deleteContents","preWrapper","postWrapper","childNode","lastOfPre","fragment","createDocumentFragment","firstChild","insertNode","setStartAfter","removeAllRanges","addRange","node","$node","isBrowser","debug","querySelector","div","body","computed","getComputedStyle","currentStyle","whiteSpace","wordWrap","visibility","properties","forEach","prop","isFirefox","scrollHeight","overflowY","overflow","textContent","span","coordinates","offsetTop","offsetLeft","backgroundColor","mozInnerScreenX"],"mappings":";CAAC,SAAUA,GACT,GAAsB,kBAAXC,SAAyBA,OAAOC,IAEzCD,QAAQ,UAAWD,OACd,IAAsB,gBAAXG,SAAuBA,OAAOC,QAAS,CACvD,GAAIC,GAAIC,QAAQ,SAChBH,QAAOC,QAAUJ,EAAQK,OAGzBL,GAAQO,SAEV,SAAUA,GAUZ,GAAsB,mBAAXA,GACT,KAAM,IAAIC,OAAM,sCAi2ClB,QA91CC,SAAUH,GACT,YAEA,IAAII,GAAO,SAAUC,GACfC,QAAQF,MAAQE,QAAQF,KAAKC,IAG/BE,EAAK,CAETP,GAAEQ,GAAGC,aAAe,SAAUC,EAAYC,GACxC,GAAIC,GAAOC,MAAMC,UAAUC,MAAMC,KAAKC,UACtC,OAAOC,MAAKC,KAAK,WACf,GAAIC,GAAOF,KACPG,EAAQrB,EAAEkB,MACVI,EAAYD,EAAME,KAAK,eAO3B,IANKD,IACHX,IAAWA,MACXA,EAAOa,KAAOjB,IACde,EAAY,GAAItB,GAAEQ,GAAGC,aAAagB,UAAUP,KAAMP,GAClDU,EAAME,KAAK,eAAgBD,IAEH,gBAAfZ,GAAyB,CAClC,IAAKY,EAAW,MAChBV,GAAKc,QACLJ,EAAUZ,GAAYiB,MAAML,EAAWV,GACpB,YAAfF,GACFW,EAAMO,WAAW,oBAKnB5B,GAAEmB,KAAKT,EAAY,SAAUmB,GAC3B7B,EAAEmB,MAAM,SAAU,SAAU,YAAa,YAAa,SAAUW,GAC1DD,EAAIC,KACNR,EAAUX,OAAOmB,GAAQD,EAAIC,GAC7B1B,EAAK0B,EAAO,wDACLD,GAAIC,QAIjBR,EAAUS,SAAS/B,EAAEQ,GAAGC,aAAauB,SAASC,MAAMvB,GAClDwB,GAAId,EACJe,IAAKd,SAMbnB,IAED,SAAUF,GACT,YAoEA,SAASyB,GAAUW,EAASzB,GAO1B,GANAO,KAAKiB,IAAanC,EAAEoC,GACpBlB,KAAKX,GAAa,eAAiB8B,IACnCnB,KAAKR,cACLQ,KAAKoB,SACLpB,KAAKP,OAAaX,EAAEuC,UAAWd,EAAUe,eAAgB7B,KAEpDO,KAAKiB,IAAIM,GAAG,qBAAwBvB,KAAKiB,IAAIM,GAAG,uBAA0BvB,KAAKiB,IAAIM,GAAG,aAAgBL,EAAQM,mBAAgD,QAA3BN,EAAQO,iBAC9I,KAAM,IAAIxC,OAAM,kEAGlB,IAAIiC,IAAYQ,SAASC,cAEvB3B,KAAK4B,iBACA,CAEL,GAAI1B,GAAOF,IACXA,MAAKiB,IAAIY,IAAI,SAAW7B,KAAKX,GAAI,WAAca,EAAK0B,gBA7DxD,GAAIE,GAAO,SAAUC,GACnB,GAAIC,GAAQC,CAEZ,OAAO,YAEL,GAAIvC,GAAOC,MAAMC,UAAUC,MAAMC,KAAKC,UACtC,IAAIiC,EAKF,YADAC,EAAqBvC,EAGvBsC,IAAS,CACT,IAAI9B,GAAOF,IACXN,GAAKwC,QAAQ,QAASC,KACpB,GAAIF,EAAoB,CAMtB,GAAIG,GAAaH,CACjBA,GAAqBI,OACrBD,EAAWF,QAAQC,GACnBJ,EAAKtB,MAAMP,EAAMkC,OAEjBJ,IAAS,IAGbD,EAAKtB,MAAMT,KAAMN,KAIjB4C,EAAW,SAAU3B,GACvB,MAA+C,oBAAxC4B,OAAO3C,UAAU4C,SAAS1C,KAAKa,IAGpC8B,EAAa,SAAU9B,GACzB,MAA+C,sBAAxC4B,OAAO3C,UAAU4C,SAAS1C,KAAKa,IAGpCQ,EAAW,CAuBfZ,GAAUe,aAAe,WAQvB,MAPKf,GAAUmC,WACbnC,EAAUmC,UACRC,SAAU7D,EAAE,QACZ8D,OAAQ,QAILrC,EAAUmC,UAGnB5D,EAAEuC,OAAOd,EAAUX,WAIjBP,GAAY,KACZI,OAAY,KACZD,WAAY,KACZqD,QAAY,KACZC,SAAY,KACZ7B,IAAY,KAKZW,WAAY,WACV,GAAIV,GAAUlB,KAAKiB,IAAI8B,IAAI,EAE3B/C,MAAK8C,SAAW,GAAIhE,GAAEQ,GAAGC,aAAayD,SAAS9B,EAASlB,KAAMA,KAAKP,OACnE,IAAIwD,GAASC,CACTlD,MAAKP,OAAOoD,QACdI,EAAUjD,KAAKP,OAAOoD,SAGpBK,EADElD,KAAKiB,IAAIM,GAAG,aAAevB,KAAKiB,IAAIM,GAAG,qBAAuBvB,KAAKiB,IAAIM,GAAG,sBACjC,gBAAzBL,GAAQiC,aAA4B,WAAa,aAExD,kBAEbF,EAAUnE,EAAEQ,GAAGC,aAAa2D,IAE9BlD,KAAK6C,QAAU,GAAII,GAAQ/B,EAASlB,KAAMA,KAAKP,SAGjD2D,QAAS,WACPpD,KAAKiB,IAAIoC,IAAI,IAAMrD,KAAKX,IACpBW,KAAK6C,SACP7C,KAAK6C,QAAQO,UAEXpD,KAAK8C,UACP9C,KAAK8C,SAASM,UAEhBpD,KAAKiB,IAAMjB,KAAK6C,QAAU7C,KAAK8C,SAAW,MAG5CQ,WAAY,WACNtD,KAAK8C,UACP9C,KAAK8C,SAASQ,cAKlBC,QAAS,SAAUC,EAAMC,GAClBzD,KAAK8C,UAAY9C,KAAK4B,aACnB,MAAR4B,IAAiBA,EAAOxD,KAAK6C,QAAQa,yBACrC,IAAIC,GAAc3D,KAAK4D,oBAAoBJ,EAC3C,IAAIG,EAAYE,OAAQ,CACtB,GAAIC,GAAOH,EAAY,EAEvB,IAAIF,GAAqBzD,KAAK+D,QAAUD,GAAiB,KAATA,EAAe,MAC/D9D,MAAK+D,MAAQD,EACb9D,KAAKgE,QAAQvD,MAAMT,KAAM2D,OAEzB3D,MAAK+D,MAAQ,KACb/D,KAAK8C,SAASQ,cAIlBW,KAAM,SAAUC,GACd,GAAIxE,GAAOC,MAAMC,UAAUC,MAAMC,KAAKC,UAAW,EAEjD,OADAC,MAAKiB,IAAIsC,QAAQW,EAAWxE,GACrBM,MAGTa,SAAU,SAAUrB,GAClBG,MAAMC,UAAUuE,KAAK1D,MAAMT,KAAKR,WAAYA,IAS9C4E,OAAQ,SAAUC,EAAOC,EAAUC,GACjCvE,KAAK+D,MAAQ,KACb/D,KAAK6C,QAAQuB,OAAOC,EAAOC,EAAUC,GACrCvE,KAAKiE,KAAK,UAAUA,KAAK,sBAAuBI,EAAOC,GACvDtE,KAAK6C,QAAQ2B,SAMfC,cAAc,EACdV,MAAc,KASdH,oBAAqB,SAAUJ,GAC7B,IAAK,GAAIkB,GAAI,EAAGA,EAAI1E,KAAKR,WAAWqE,OAAQa,IAAK,CAC/C,GAAIJ,GAAWtE,KAAKR,WAAWkF,GAC3BC,EAAUL,EAASK,QAAQnB,EAC/B,IAAImB,GAAuB,KAAZA,EAAgB,CAC7B,GAAIC,GAAcnC,EAAW6B,EAASO,OAASP,EAASO,MAAMrB,GAAQc,EAASO,KAC3EvC,GAASqC,KAAYnB,EAAOmB,EAChC,IAAIE,GAAQrB,EAAKqB,MAAMD,EACvB,IAAIC,EAAS,OAAQP,EAAUO,EAAMP,EAASQ,OAAQD,IAG1D,UAIFb,QAASlC,EAAK,SAAUiD,EAAMT,EAAUR,EAAMe,GAC5C,GAAI3E,GAAOF,IACXsE,GAASU,OAAOlB,EAAM,SAAUzD,EAAM4E,GAC/B/E,EAAK4C,SAASoC,OACjBhF,EAAK4C,SAASqC,WAEZjF,EAAKuE,eAEPvE,EAAK4C,SAASsC,QACdlF,EAAKuE,cAAe,GAEtBvE,EAAK4C,SAASuC,YAAYnF,EAAK2C,QAAQyC,oBACvCpF,EAAK4C,SAASyC,OAAOrF,EAAKsF,KAAKnF,EAAMiE,EAAUR,IAC1CmB,IAEHF,IACA7E,EAAKuE,cAAe,IAErBI,KASLW,KAAM,SAAUnF,EAAMiE,EAAUR,GAC9B,MAAOhF,GAAE2G,IAAIpF,EAAM,SAAUgE,GAC3B,OAASA,MAAOA,EAAOC,SAAUA,EAAUR,KAAMA,QAKvDhF,EAAEQ,GAAGC,aAAagB,UAAYA,GAC9BvB,IAED,SAAUF,GACT,YA2CA,SAASkE,GAAS9B,EAASd,EAAWX,GACpCO,KAAKiB,IAAY+B,EAAS0C,cAAcjG,GACxCO,KAAKI,UAAYA,EACjBJ,KAAKX,GAAYe,EAAUf,GAAK,WAChCW,KAAK2F,SACL3F,KAAK4F,SAAY9G,EAAEoC,GACnBlB,KAAKP,OAAYA,EAGbA,EAAOoG,eAAgB7F,KAAKqF,YAAc5F,EAAOoG,cACjDpG,EAAOqG,QAAU9F,KAAKiB,IAAI6E,OAAOrG,EAAOqG,OAC5C,IAAI5F,GAAOF,IACXlB,GAAEmB,MAAM,WAAY,YAAa,SAAU,SAAU,mBAAoB,aAAc,SAAU8F,EAAInF,GAC/E,MAAhBnB,EAAOmB,KAAiBV,EAAKU,GAAQnB,EAAOmB,MAElDZ,KAAKgG,YAAY9E,GACjB+E,EAAcjG,KAAKX,IAAMW,KAzD3B,GAAIkG,GAAUpH,EAAEqH,QAEZC,EAAU,SAAUC,EAAYC,GAClC,GAAI5B,GAAG6B,EACHC,EAAaF,EAAMhC,SAASkC,UAChC,KAAK9B,EAAI,EAAGA,EAAI2B,EAAWxC,OAAQa,IAEjC,GADA6B,EAAOF,EAAW3B,GACd6B,EAAKjC,WAAagC,EAAMhC,SAC5B,GAAIkC,GACF,GAAID,EAAKlC,MAAMmC,KAAgBF,EAAMjC,MAAMmC,GAAa,OAAO,MAE/D,IAAID,EAAKlC,QAAUiC,EAAMjC,MAAO,OAAO,CAG3C,QAAO,GAGL4B,IACJnH,GAAE4C,UAAU+E,GAAG,QAAS,SAAUlC,GAChC,GAAIlF,GAAKkF,EAAEmC,eAAiBnC,EAAEmC,cAAcC,wBAC5C7H,GAAEmB,KAAKgG,EAAe,SAAUW,EAAKC,GAC/BD,IAAQvH,GAAMwH,EAAKvD,gBAI3B,IAAIwD,IACFC,aAAc,EACdC,OAAQ,EACRC,SAAU,EACVC,UAAW,EACXC,WAAY,EACZC,aAAc,EACdC,WAAY,EA4BdvI,GAAEuC,OAAO2B,GAIP0C,cAAe,SAAUjG,GACvB,GAAI6H,GAAU7H,EAAOkD,QACf2E,aAAmBxI,KAAMwI,EAAUxI,EAAEwI,GAC3C,IAAIrG,GAAMnC,EAAE,aACTyI,SAAS,uCACTC,KAAK,KAAM,yBAA2B/H,EAAOa,MAC7CmH,KACCC,QAAS,OACTC,KAAM,EACNC,SAAU,WACVhF,OAAQnD,EAAOmD,SAEhBD,SAAS2E,EACZ,OAAOrG,MAIXnC,EAAEuC,OAAO2B,EAASpD,WAIhBqB,IAAW,KACX2E,SAAW,KACXxF,UAAW,KACXyH,OAAW,KACXC,OAAW,KACXzI,GAAW,KACX0I,SAAW,GACXC,UAAW,GACX9C,OAAW,EACX7E,QACA4H,UAAW,GAKX7E,QAAS,WAEPpD,KAAKsD,aAELtD,KAAKiB,IAAIoC,IAAI,IAAMrD,KAAKX,IACxBW,KAAK4F,SAASvC,IAAI,IAAMrD,KAAKX,IAC7BW,KAAKoF,QACLpF,KAAKiB,IAAIiH,SACTlI,KAAKiB,IAAMjB,KAAK4F,SAAW5F,KAAKI,UAAY,WACrC6F,GAAcjG,KAAKX,KAG5BkG,OAAQ,SAAUc,GAChB,GAAI8B,GAAenI,KAAKoI,eAAe/B,GACnCgC,EAAevJ,EAAE2G,IAAIzF,KAAKK,KAAM,SAAUiI,GAAK,MAAOA,GAAEjE,OAC5D,IAAIrE,KAAKK,KAAKwD,OAAQ,CACpB,GAAIS,GAAW+B,EAAW,GAAG/B,QACzBA,GAASjF,GACXW,KAAKiB,IAAIuG,KAAK,gBAAiBlD,EAASjF,IAExCW,KAAKiB,IAAIsH,WAAW,iBAEtBvI,KAAKwI,cAAcH,GACnBrI,KAAKyI,cAAcJ,GACfF,IACFnI,KAAK0I,gBAAgBP,GACrBnI,KAAK2I,eACL3I,KAAK4I,cACL5I,KAAK6I,wBAEP7I,KAAK8I,iBACI9I,MAAK+I,iBACd/I,KAAKgJ,wBAAwBX,GACpBrI,KAAKkF,OACdlF,KAAKsD,cAIT+B,YAAa,SAAU4D,GAIrB,GAAIrB,GAAW,UAef,OAbA5H,MAAK4F,SAASsD,IAAIlJ,KAAK4F,SAASuD,WAAWlJ,KAAK,WAC9C,MAA+B,aAA5BnB,EAAEkB,MAAMyH,IAAI,aACN,EACsB,UAA5B3I,EAAEkB,MAAMyH,IAAI,aACbwB,EAAIG,KAAOlD,EAAQmD,YACnBJ,EAAItB,MAAQzB,EAAQoD,aACpB1B,EAAW,SACJ,GAJT,SAOF5H,KAAKiB,IAAIwG,IAAIzH,KAAKuJ,gBAAgBN,IAClCjJ,KAAKiB,IAAIwG,KAAMG,SAAUA,IAElB5H,MAGToF,MAAO,WACLpF,KAAKiB,IAAIuI,KAAK,IACdxJ,KAAKK,QACLL,KAAKyJ,OAAS,EACdzJ,KAAK0J,SAAW1J,KAAK2J,SAAW3J,KAAK4J,mBAAqB,MAG5DzE,SAAU,WAQR,MAPKnF,MAAKkF,QACRlF,KAAKoF,QACLpF,KAAKiB,IAAI4I,OACL7J,KAAKiI,WAAajI,KAAKiB,IAAIsG,SAASvH,KAAKiI,WAC7CjI,KAAKI,UAAU6D,KAAK,qBACpBjE,KAAKkF,OAAQ,GAERlF,MAGTsD,WAAY,WAOV,MANItD,MAAKkF,QACPlF,KAAKiB,IAAI6I,OACL9J,KAAKiI,WAAajI,KAAKiB,IAAI8I,YAAY/J,KAAKiI,WAChDjI,KAAKI,UAAU6D,KAAK,qBACpBjE,KAAKkF,OAAQ,GAERlF,MAGTgK,KAAM,SAAUzF,GACd,MAAqB,MAAdA,EAAE0F,SAAmB1F,EAAE2F,SAAyB,KAAd3F,EAAE0F,SAG7CE,OAAQ,SAAU5F,GAChB,MAAqB,MAAdA,EAAE0F,SAAmB1F,EAAE2F,SAAyB,KAAd3F,EAAE0F,SAG7CG,QAAS,SAAU7F,GACjB,GAAI8F,GAAY9F,EAAE2F,SAAW3F,EAAE+F,QAAU/F,EAAEgG,SAAWhG,EAAEiG,QACxD,QAAQH,IAA4B,KAAd9F,EAAE0F,SAAgC,IAAd1F,EAAE0F,SAAkBjK,KAAKP,OAAOgL,mBAAoB,GAAsB,KAAdlG,EAAE0F,UAG1GS,SAAU,SAAUnG,GAClB,MAAqB,MAAdA,EAAE0F,SAGXU,WAAY,SAAUpG,GACpB,MAAqB,MAAdA,EAAE0F,SAGXW,SAAU,SAAUrG,GAClB,MAAqB,MAAdA,EAAE0F,SAMXtE,MAAU,KACV8D,OAAU,KACVC,SAAU,KACVE,mBAAoB,KACpBD,SAAU,KAKV3D,YAAa,WACXhG,KAAKiB,IAAIwF,GAAG,aAAezG,KAAKX,GAAI,qBAAsBP,EAAE+L,MAAM7K,KAAK8K,SAAU9K,OACjFA,KAAKiB,IAAIwF,GAAG,cAAgBzG,KAAKX,GAAI,qBAAsBP,EAAE+L,MAAM7K,KAAK8K,SAAU9K,OAClFA,KAAKiB,IAAIwF,GAAG,aAAezG,KAAKX,GAAI,qBAAsBP,EAAE+L,MAAM7K,KAAK+K,aAAc/K,OACrFA,KAAK4F,SAASa,GAAG,WAAazG,KAAKX,GAAIP,EAAE+L,MAAM7K,KAAKgL,WAAYhL,QAGlE8K,SAAU,SAAUvG,GAClB,GAAItD,GAAMnC,EAAEyF,EAAE0G,OACd1G,GAAE2G,iBACF3G,EAAEmC,cAAcC,yBAA2B3G,KAAKX,GAC3C4B,EAAIkK,SAAS,uBAChBlK,EAAMA,EAAImK,QAAQ,sBAEpB,IAAI9E,GAAQtG,KAAKK,KAAKgL,SAASpK,EAAIZ,KAAK,SAAU,IAClDL,MAAKI,UAAUgE,OAAOkC,EAAMjC,MAAOiC,EAAMhC,SAAUC,EACnD,IAAIrE,GAAOF,IAGXsL,YAAW,WACTpL,EAAKoD,aACU,eAAXiB,EAAEgH,MACJrL,EAAK0F,SAASpB,SAEf,IAILuG,aAAc,SAAUxG,GACtB,GAAItD,GAAMnC,EAAEyF,EAAE0G,OACd1G,GAAE2G,iBACGjK,EAAIkK,SAAS,uBAChBlK,EAAMA,EAAImK,QAAQ,uBAEpBpL,KAAKyJ,OAAS4B,SAASpK,EAAIZ,KAAK,SAAU,IAC1CL,KAAK6I,wBAGPmC,WAAY,SAAUzG,GACpB,GAAKvE,KAAKkF,MAAV,CAEA,GAAIsG,EAUJ,QARI1M,EAAE2D,WAAWzC,KAAKP,OAAOgM,aAC3BD,EAAUxL,KAAKP,OAAOgM,UAAUlH,EAAGuC,IAGtB,MAAX0E,IACFA,EAAUxL,KAAK0L,gBAAgBnH,IAGzBiH,GACN,IAAK1E,GAASE,OACZzC,EAAE2G,iBACFlL,KAAK2L,KACL,MACF,KAAK7E,GAASG,SACZ1C,EAAE2G,iBACFlL,KAAK4L,OACL,MACF,KAAK9E,GAASI,UACZ3C,EAAE2G,iBACFlL,KAAK6L,OAAOtH,EACZ,MACF,KAAKuC,GAASK,WACZ5C,EAAE2G,iBACFlL,KAAK8L,SACL,MACF,KAAKhF,GAASM,aACZ7C,EAAE2G,iBACFlL,KAAK+L,WACL,MACF,KAAKjF,GAASO,WACZ9C,EAAE2G,iBACFlL,KAAKsD,gBAKXoI,gBAAiB,SAAUnH,GACzB,MAAIvE,MAAKgK,KAAKzF,GACLuC,EAASE,OACPhH,KAAKmK,OAAO5F,GACduC,EAASG,SACPjH,KAAKoK,QAAQ7F,GACfuC,EAASI,UACPlH,KAAK0K,SAASnG,GAChBuC,EAASK,WACPnH,KAAK2K,WAAWpG,GAClBuC,EAASM,aACPpH,KAAK4K,SAASrG,GAChBuC,EAASO,WADX,QAKTsE,IAAK,WACiB,IAAhB3L,KAAKyJ,OACPzJ,KAAKyJ,OAASzJ,KAAKK,KAAKwD,OAAS,EAEjC7D,KAAKyJ,QAAU,EAEjBzJ,KAAK6I,uBACL7I,KAAK8I,cAGP8C,MAAO,WACD5L,KAAKyJ,SAAWzJ,KAAKK,KAAKwD,OAAS,EACrC7D,KAAKyJ,OAAS,EAEdzJ,KAAKyJ,QAAU,EAEjBzJ,KAAK6I,uBACL7I,KAAK8I,cAGP+C,OAAQ,SAAUtH,GAChB,GAAI+B,GAAQtG,KAAKK,KAAKgL,SAASrL,KAAKgM,oBAAoB3L,KAAK,SAAU,IACvEL,MAAKI,UAAUgE,OAAOkC,EAAMjC,MAAOiC,EAAMhC,SAAUC,GACnDvE,KAAKsD,cAGPwI,QAAS,WACP,GAAIb,GAAS,EACTgB,EAAYjM,KAAKgM,oBAAoBpE,WAAWwB,IAAMpJ,KAAKiB,IAAIiL,aACnElM,MAAKiB,IAAIkL,WAAWlM,KAAK,SAAUyE,GACjC,MAAI5F,GAAEkB,MAAM4H,WAAWwB,IAAMtK,EAAEkB,MAAMoM,cAAgBH,GACnDhB,EAASvG,GACF,GAFT,SAKF1E,KAAKyJ,OAASwB,EACdjL,KAAK6I,uBACL7I,KAAK8I,cAGPiD,UAAW,WACT,GAAId,GAASjL,KAAKK,KAAKwD,OAAS,EAC5BoI,EAAYjM,KAAKgM,oBAAoBpE,WAAWwB,IAAMpJ,KAAKiB,IAAIiL,aACnElM,MAAKiB,IAAIkL,WAAWlM,KAAK,SAAUyE,GACjC,MAAI5F,GAAEkB,MAAM4H,WAAWwB,IAAM6C,GAC3BhB,EAASvG,GACF,GAFT,SAKF1E,KAAKyJ,OAASwB,EACdjL,KAAK6I,uBACL7I,KAAK8I,cAGPD,qBAAsB,WACpB7I,KAAKiB,IAAIoL,KAAK,6BAA6BtC,YAAY,UACvD/J,KAAKgM,oBAAoBzE,SAAS,WAGpCyE,kBAAmB,WACjB,MAAOhM,MAAKiB,IAAIkL,SAAS,0BAA4BnM,KAAKyJ,OAAS,MAGrEX,WAAY,WACV,GAAIwD,GAAYtM,KAAKgM,oBACjBO,EAAUD,EAAU1E,WAAWwB,IAC/BoD,EAAaF,EAAUF,cACvBK,EAAgBzM,KAAKiB,IAAIiL,cACzBQ,EAAa1M,KAAKiB,IAAIoI,WACN,KAAhBrJ,KAAKyJ,QAAgBzJ,KAAKyJ,QAAUzJ,KAAKK,KAAKwD,OAAS,GAAe,EAAV0I,EAC9DvM,KAAKiB,IAAIoI,UAAUkD,EAAUG,GACpBH,EAAUC,EAAaC,GAChCzM,KAAKiB,IAAIoI,UAAUkD,EAAUC,EAAaE,EAAaD,IAI3DrE,eAAgB,SAAU/B,GACxB,GAAIC,GAAO5B,EAAGI,EACV0E,EAAO,EACX,KAAK9E,EAAI,EAAGA,EAAI2B,EAAWxC,QACrB7D,KAAKK,KAAKwD,SAAW7D,KAAK+H,SADGrD,IAEjC4B,EAAQD,EAAW3B,GACf0B,EAAQpG,KAAKK,KAAMiG,KACvBxB,EAAQ9E,KAAKK,KAAKwD,OAClB7D,KAAKK,KAAK8D,KAAKmC,GACfkD,GAAQ,6CAA+C1E,EAAQ,QAC/D0E,GAAUlD,EAAMhC,SAASqI,SAASrG,EAAMjC,MAAOiC,EAAMxC,MACrD0F,GAAQ,YAEV,OAAOA,IAGThB,cAAe,SAAUH,GACvB,GAAIrI,KAAK8H,OAAQ,CACV9H,KAAK0J,WACR1J,KAAK0J,SAAW5K,EAAE,yCAAyC8N,UAAU5M,KAAKiB,KAE5E,IAAIuI,GAAO1K,EAAE2D,WAAWzC,KAAK8H,QAAU9H,KAAK8H,OAAOO,GAAgBrI,KAAK8H,MACxE9H,MAAK0J,SAASF,KAAKA,KAIvBf,cAAe,SAAUJ,GACvB,GAAIrI,KAAK6H,OAAQ,CACV7H,KAAK2J,WACR3J,KAAK2J,SAAW7K,EAAE,yCAAyC6D,SAAS3C,KAAKiB,KAE3E,IAAIuI,GAAO1K,EAAE2D,WAAWzC,KAAK6H,QAAU7H,KAAK6H,OAAOQ,GAAgBrI,KAAK6H,MACxE7H,MAAK2J,SAASH,KAAKA,KAIvBR,wBAAyB,SAAUX,GACjC,GAAIrI,KAAK+I,iBAAkB,CACpB/I,KAAK4J,qBACR5J,KAAK4J,mBAAqB9K,EAAE,qDAAqD6D,SAAS3C,KAAKiB,KAEjG,IAAIuI,GAAO1K,EAAE2D,WAAWzC,KAAK+I,kBAAoB/I,KAAK+I,iBAAiBV,GAAgBrI,KAAK+I,gBAC5F/I,MAAK4J,mBAAmBJ,KAAKA,KAIjCd,gBAAiB,SAAUc,GACrBxJ,KAAK2J,SACP3J,KAAK2J,SAASkD,OAAOrD,GAErBxJ,KAAKiB,IAAI6L,OAAOtD,IAIpBb,aAAc,WACZ,GAAIoE,GAAqB7G,EAAQmD,YAAcnD,EAAQJ,SACnDA,EAAS9F,KAAKiB,IAAI6E,QACjB9F,MAAKiB,IAAI2G,WAAWwB,IAAMtD,EAAUiH,GACvC/M,KAAKiB,IAAI+L,QAAQ5D,IAAK2D,EAAqBjH,KAI/C8C,YAAa,WASX,IAJA,GACyCoE,GADrCC,EAAY,GACZC,EAAalN,KAAKiB,IAAI+L,SAASrF,KAC/BwF,EAAQnN,KAAKiB,IAAIkM,QACjBC,EAAUlH,EAAQiH,QAAUF,EACzBC,EAAaC,EAAQC,IAC1BpN,KAAKiB,IAAI+L,QAAQrF,KAAMuF,EAAaD,IACpCD,EAAShN,KAAKiB,IAAI+L,SAASrF,OACvBqF,GAAUE,KACdA,EAAaF,GAIjBzD,gBAAiB,SAAU3B,GAmBzB,MAjBsC,KAAlC5H,KAAKgI,UAAUqF,QAAQ,OAEzBzF,GACEwB,IAAK,OACLkE,OAAQtN,KAAKiB,IAAIsM,SAASzH,SAAW8B,EAASwB,IAAMxB,EAAS4F,WAC7D7F,KAAMC,EAASD,OAGjBC,EAAS0F,OAAS,aACX1F,GAAS4F,YAEwB,KAAtCxN,KAAKgI,UAAUqF,QAAQ,WACzBzF,EAASD,KAAO,EACgC,KAAvC3H,KAAKgI,UAAUqF,QAAQ,cAChCzF,EAAS6F,MAAQ,EACjB7F,EAASD,KAAO,QAEXC,KAIX9I,EAAEQ,GAAGC,aAAayD,SAAWA,EAC7BlE,EAAEuC,OAAOvC,EAAEQ,GAAGC,aAAcuH,IAC5B9H,IAED,SAAUF,GACT,YAiBA,SAASgC,GAAS4M,GAChB5O,EAAEuC,OAAOrB,KAAM0N,GACX1N,KAAK2N,QAAS3N,KAAKgF,OAAS4I,EAAQ5N,KAAKgF,SAhB/C,GAAI4I,GAAU,SAAU7L,GACtB,GAAI8L,KACJ,OAAO,UAAU/J,EAAMgK,GACjBD,EAAK/J,GACPgK,EAASD,EAAK/J,IAEd/B,EAAKjC,KAAKE,KAAM8D,EAAM,SAAUzD,GAC9BwN,EAAK/J,IAAS+J,EAAK/J,QAAaiK,OAAO1N,GACvCyN,EAASrN,MAAM,KAAMV,cAW7Be,GAASC,MAAQ,SAAUiN,EAAiBC,GAC1C,MAAOnP,GAAE2G,IAAIuI,EAAiB,SAAU1J,GACtC,GAAI4J,GAAc,GAAIpN,GAASwD,EAG/B,OAFA4J,GAAYlN,GAAKiN,EAAOjN,GACxBkN,EAAYjN,IAAMgN,EAAOhN,IAClBiN,KAIXpP,EAAEuC,OAAOP,EAASlB,WAKhBiF,MAAY,KACZsJ,QAAY,KACZnJ,OAAY,KAGZ3F,GAAY,KACZsO,OAAY,EACZhJ,QAAY,WAAc,OAAO,GACjCG,MAAY,EACZ6H,SAAY,SAAUhM,GAAO,MAAOA,IACpC6F,WAAY,OAGd1H,EAAEQ,GAAGC,aAAauB,SAAWA,GAE7B9B,IAED,SAAUF,GACT,YAiCA,SAASmE,MA/BT,GAAImL,GAAMC,KAAKD,KAAO,WAAc,OAAO,GAAIC,OAAOC,WAOlDC,EAAW,SAAUxM,EAAMyM,GAC7B,GAAIC,GAAS/O,EAAMiF,EAAS+J,EAAWC,EACnCC,EAAQ,WACV,GAAIC,GAAOT,IAAQM,CACRF,GAAPK,EACFJ,EAAUnD,WAAWsD,EAAOJ,EAAOK,IAEnCJ,EAAU,KACVE,EAAS5M,EAAKtB,MAAMkE,EAASjF,GAC7BiF,EAAUjF,EAAO,MAIrB,OAAO,YAOL,MANAiF,GAAU3E,KACVN,EAAOK,UACP2O,EAAYN,IACPK,IACHA,EAAUnD,WAAWsD,EAAOJ,IAEvBG,GAMX7P,GAAEuC,OAAO4B,EAAQrD,WAIfP,GAAW,KACXe,UAAW,KACXY,GAAW,KACXC,IAAW,KACXxB,OAAW,KAKXmC,WAAY,SAAUV,EAASd,EAAWX,GACxCO,KAAKgB,GAAYE,EACjBlB,KAAKiB,IAAYnC,EAAEoC,GACnBlB,KAAKX,GAAYe,EAAUf,GAAKW,KAAK8O,YAAYlO,KACjDZ,KAAKI,UAAYA,EACjBJ,KAAKP,OAAYA,EAEbO,KAAKP,OAAO8O,WACdvO,KAAK+O,SAAWR,EAASvO,KAAK+O,SAAU/O,KAAKP,OAAO8O,WAGtDvO,KAAKgG,eAGP5C,QAAS,WACPpD,KAAKiB,IAAIoC,IAAI,IAAMrD,KAAKX,IACxBW,KAAKiB,IAAMjB,KAAKgB,GAAKhB,KAAKI,UAAY,MAQxCgE,OAAQ,WACN,KAAM,IAAInF,OAAM,oBAIlBqG,iBAAkB,WAChB,GAAIsC,GAAW5H,KAAKgP,4BAChBhC,EAAShN,KAAKiB,IAAI+L,SAGlB1F,EAAUtH,KAAKP,OAAOkD,QAC1B,IAAI2E,EAAS,CACJA,YAAmBxI,KAAMwI,EAAUxI,EAAEwI,GAC3C,IAAI2H,GAAe3H,EAAQ4H,eAAelC,QAC1CA,GAAO5D,KAAO6F,EAAa7F,IAC3B4D,EAAOrF,MAAQsH,EAAatH,KAK/B,MAFAC,GAASwB,KAAO4D,EAAO5D,IACvBxB,EAASD,MAAQqF,EAAOrF,KACjBC,GAITpD,MAAO,WACLxE,KAAKiB,IAAIuD,SAMXwB,YAAa,WACXhG,KAAKiB,IAAIwF,GAAG,SAAWzG,KAAKX,GAAIP,EAAE+L,MAAM7K,KAAK+O,SAAU/O,QAGzD+O,SAAU,SAAUxK,GACdvE,KAAKmP,YAAY5K,IACrBvE,KAAKI,UAAUmD,QAAQvD,KAAK0D,0BAA0B,IAIxDyL,YAAa,SAAUC,GACrB,OAAQA,EAAWnF,SACjB,IAAK,GACL,IAAK,IACL,IAAK,IACL,IAAK,IACH,OAAO,EAEX,GAAImF,EAAWlF,QAAS,OAAQkF,EAAWnF,SACzC,IAAK,IACL,IAAK,IACH,OAAO,MAKfnL,EAAEQ,GAAGC,aAAa0D,QAAUA,GAC5BjE,IAED,SAAUF,GACT,YAMA,SAASuQ,GAASnO,EAASd,EAAWX,GACpCO,KAAK4B,WAAWV,EAASd,EAAWX,GAGtCX,EAAEuC,OAAOgO,EAASzP,UAAWd,EAAEQ,GAAGC,aAAa0D,QAAQrD,WAKrDwE,OAAQ,SAAUC,EAAOC,EAAUC,GACjC,GAAI+K,GAAMtP,KAAK0D,yBACX6L,EAAOvP,KAAKgB,GAAGqD,MAAMmL,UAAUxP,KAAKgB,GAAGmC,cACvCsM,EAAYnL,EAAS6J,QAAQ9J,EAAOE,EACf,oBAAdkL,KACL3Q,EAAE4Q,QAAQD,KACZF,EAAOE,EAAU,GAAKF,EACtBE,EAAYA,EAAU,IAExBH,EAAMA,EAAInB,QAAQ7J,EAASO,MAAO4K,GAClCzP,KAAKiB,IAAI0O,IAAIL,EAAMC,GACnBvP,KAAKgB,GAAG4O,eAAiB5P,KAAKgB,GAAGmC,aAAemM,EAAIzL,SAIxDH,uBAAwB,WACtB,MAAO1D,MAAKgB,GAAGqD,MAAMmL,UAAU,EAAGxP,KAAKgB,GAAGmC,eAM5C6L,0BAA2B,WACzB,GAAIa,GAAI/Q,EAAEQ,GAAGC,aAAauQ,oBAAoB9P,KAAKgB,GAAIhB,KAAKgB,GAAG4O,eAC/D,QACExG,IAAKyG,EAAEzG,IAAMpJ,KAAK+P,uBAAyB/P,KAAKiB,IAAIoI,YACpD1B,KAAMkI,EAAElI,KAAO3H,KAAKiB,IAAIqI,eAI5ByG,qBAAsB,WACpB,GAAIvC,GAAanC,SAASrL,KAAKiB,IAAIwG,IAAI,eAAgB,GACvD,IAAIuI,MAAMxC,GAAa,CAErB,GAAIyC,GAAajQ,KAAKgB,GAAGiP,WACrBC,EAAOxO,SAASgE,cAAc1F,KAAKgB,GAAGmP,UACtCC,EAAQpQ,KAAKgB,GAAGoP,KACpBF,GAAKG,aACH,QACA,sCAAwCD,EAAME,WAAa,cAAgBF,EAAMG,UAEnFL,EAAKM,UAAY,OACjBP,EAAWQ,YAAYP,GACvB1C,EAAa0C,EAAKQ,aAClBT,EAAWU,YAAYT,GAEzB,MAAO1C,MAIX1O,EAAEQ,GAAGC,aAAa8P,SAAWA,GAC7BrQ,IAED,SAAUF,GACT,YAIA,SAAS8R,GAAW1P,EAASd,EAAWX,GACtCO,KAAK4B,WAAWV,EAASd,EAAWX,GACpCX,EAAE,SAAW+R,EAAe,WAAWpJ,KACrCG,SAAU,WACVwB,IAAK,MACLzB,KAAM,QACLmJ,aAAa5P,GARlB,GAAI2P,GAAe,GAWnB/R,GAAEuC,OAAOuP,EAAWhR,UAAWd,EAAEQ,GAAGC,aAAa8P,SAASzP,WAIxDwE,OAAQ,SAAUC,EAAOC,EAAUC,GACjC,GAAI+K,GAAMtP,KAAK0D,yBACX6L,EAAOvP,KAAKgB,GAAGqD,MAAMmL,UAAUF,EAAIzL,QACnC4L,EAAYnL,EAAS6J,QAAQ9J,EAAOE,EACxC,IAAyB,mBAAdkL,GAA2B,CAChC3Q,EAAE4Q,QAAQD,KACZF,EAAOE,EAAU,GAAKF,EACtBE,EAAYA,EAAU,IAExBH,EAAMA,EAAInB,QAAQ7J,EAASO,MAAO4K,GAClCzP,KAAKiB,IAAI0O,IAAIL,EAAMC,GACnBvP,KAAKgB,GAAGwD,OACR,IAAIuM,GAAQ/Q,KAAKgB,GAAGgQ,iBACpBD,GAAME,UAAS,GACfF,EAAMG,QAAQ,YAAa5B,EAAIzL,QAC/BkN,EAAMI,UAAU,YAAa7B,EAAIzL,QACjCkN,EAAM3M,WAIVV,uBAAwB,WACtB1D,KAAKgB,GAAGwD,OACR,IAAIuM,GAAQrP,SAAS0P,UAAUC,aAC/BN,GAAMI,UAAU,aAAcnR,KAAKgB,GAAGqD,MAAMR,OAC5C,IAAIyN,GAAMP,EAAMvN,KAAK+N,MAAMV,EAC3B,OAAsB,KAAfS,EAAIzN,OAAeyN,EAAI,GAAKA,EAAI,MAI3CxS,EAAEQ,GAAGC,aAAaqR,WAAaA,GAC/B5R,IAMD,SAAUF,GACT,YAMA,SAAS0S,GAAiBtQ,EAASd,EAAWX,GAC5CO,KAAK4B,WAAWV,EAASd,EAAWX,GAGtCX,EAAEuC,OAAOmQ,EAAgB5R,UAAWd,EAAEQ,GAAGC,aAAa0D,QAAQrD,WAM5DwE,OAAQ,SAAUC,EAAOC,EAAUC,GACjC,GAAI+K,GAAMtP,KAAK0D,yBACX+N,EAAMtL,OAAOuL,eACbX,EAAQU,EAAIE,WAAW,GACvBP,EAAYL,EAAMa,YACtBR,GAAUS,mBAAmBd,EAAMe,eACnC,IAAIC,GAAUX,EAAU5O,WACpB+M,EAAOwC,EAAQvC,UAAUuB,EAAMiB,aAC/BvC,EAAYnL,EAAS6J,QAAQ9J,EAAOE,EACxC,IAAyB,mBAAdkL,GAA2B,CAChC3Q,EAAE4Q,QAAQD,KACZF,EAAOE,EAAU,GAAKF,EACtBE,EAAYA,EAAU,IAExBH,EAAMA,EAAInB,QAAQ7J,EAASO,MAAO4K,GAClCsB,EAAMc,mBAAmBd,EAAMe,gBAC/Bf,EAAMkB,gBAGN,IAAIC,GAAaxQ,SAASgE,cAAc,MACxCwM,GAAW1B,UAAYlB,CACvB,IAAI6C,GAAczQ,SAASgE,cAAc,MACzCyM,GAAY3B,UAAYjB,CAMxB,KAHA,GACI6C,GACAC,EAFAC,EAAW5Q,SAAS6Q,yBAGjBH,EAAYF,EAAWM,YAC7BH,EAAYC,EAAS7B,YAAY2B,EAElC,MAAOA,EAAYD,EAAYK,YAC9BF,EAAS7B,YAAY2B,EAItBrB,GAAM0B,WAAWH,GACjBvB,EAAM2B,cAAcL,GAEpBtB,EAAME,UAAS,GACfQ,EAAIkB,kBACJlB,EAAImB,SAAS7B,KAgBjB/B,0BAA2B,WACzB,GAAI+B,GAAQ5K,OAAOuL,eAAeC,WAAW,GAAGC,aAC5CiB,EAAOnR,SAASgE,cAAc,OAClCqL,GAAM0B,WAAWI,GACjB9B,EAAMc,mBAAmBgB,GACzB9B,EAAMkB,gBACN,IAAIa,GAAQhU,EAAE+T,GACVjL,EAAWkL,EAAM9F,QAKrB,OAJApF,GAASD,MAAQ3H,KAAKiB,IAAI+L,SAASrF,KACnCC,EAASwB,KAAO0J,EAAMhN,SAAW9F,KAAKiB,IAAI+L,SAAS5D,IACnDxB,EAAS4F,WAAasF,EAAMhN,SAC5BgN,EAAM5K,SACCN,GAWTlE,uBAAwB,WACtB,GAAIqN,GAAQ5K,OAAOuL,eAAeC,WAAW,GACzCP,EAAYL,EAAMa,YAEtB,OADAR,GAAUS,mBAAmBd,EAAMe,gBAC5BV,EAAU5O,WAAWgN,UAAU,EAAGuB,EAAMiB,gBAInDlT,EAAEQ,GAAGC,aAAaiS,gBAAkBA,GACpCxS,GAuBD,SAAUF,GAmDX,QAASgR,GAAoB5O,EAAS0G,EAAU8F,GAC9C,IAAIqF,EACF,KAAM,IAAI9T,OAAM,iFAGlB,IAAI+T,GAAQtF,GAAWA,EAAQsF,QAAS,CACxC,IAAIA,EAAO,CACT,GAAIhS,GAAKU,SAASuR,cAAc,4CAC3BjS,IAAOA,EAAGiP,WAAWU,YAAY3P,GAIxC,GAAIkS,GAAMxR,SAASgE,cAAc,MACjCwN,GAAI7T,GAAK,2CACTqC,SAASyR,KAAK1C,YAAYyC,EAE1B,IAAI9C,GAAQ8C,EAAI9C,MACZgD,EAAWjN,OAAOkN,iBAAkBA,iBAAiBnS,GAAWA,EAAQoS,YAG5ElD,GAAMmD,WAAa,WACM,UAArBrS,EAAQiP,WACVC,EAAMoD,SAAW,cAGnBpD,EAAMxI,SAAW,WACZoL,IACH5C,EAAMqD,WAAa,UAGrBC,EAAWC,QAAQ,SAAUC,GAC3BxD,EAAMwD,GAAQR,EAASQ,KAGrBC,EAEE3S,EAAQ4S,aAAezI,SAAS+H,EAAStN,UAC3CsK,EAAM2D,UAAY,UAEpB3D,EAAM4D,SAAW,SAGnBd,EAAIe,YAAc/S,EAAQmD,MAAMmL,UAAU,EAAG5H,GAEpB,UAArB1G,EAAQiP,WACV+C,EAAIe,YAAcf,EAAIe,YAAY9F,QAAQ,MAAO,KAEnD,IAAI+F,GAAOxS,SAASgE,cAAc,OAMlCwO,GAAKD,YAAc/S,EAAQmD,MAAMmL,UAAU5H,IAAa,IACxDsL,EAAIzC,YAAYyD,EAEhB,IAAIC,IACF/K,IAAK8K,EAAKE,UAAY/I,SAAS+H,EAAyB,gBACxDzL,KAAMuM,EAAKG,WAAahJ,SAAS+H,EAA0B,iBAS7D,OANIJ,GACFkB,EAAK9D,MAAMkE,gBAAkB,OAE7B5S,SAASyR,KAAKxC,YAAYuC,GAGrBiB,EAhHT,GAAIT,IACF,YACA,YACA,QACA,SACA,YACA,YAEA,iBACA,mBACA,oBACA,kBACA,cAEA,aACA,eACA,gBACA,cAGA,YACA,cACA,aACA,cACA,WACA,iBACA,aACA,aAEA,YACA,gBACA,aACA,iBAEA,gBACA,cAEA,UACA,cAIEX,EAA+B,mBAAX5M,QACpB0N,EAAad,GAAuC,MAA1B5M,OAAOoO,eAwErCzV,GAAEQ,GAAGC,aAAauQ,oBAAsBA,GAEtC9Q,GAEKA"}
\ No newline at end of file
index 6341860b13dc3d868cbe1d52fe0c7fe349bddce3..50a56a45ca52fd79fa8d1a802cdb11b443933745 100755 (executable)
@@ -5,10 +5,10 @@ command -v uglifyjs >/dev/null 2>&1 || { echo >&2 "I require UglifyJS but it's n
 MINIFY_CMD=uglifyjs
 
 JSFILES=(
-       "js/acl.js"
-       "js/ajaxupload.js"
-       "js/country.js"
-       "js/main.js"
+       "view/js/acl.js"
+       "view/js/ajaxupload.js"
+       "view/js/country.js"
+       "view/js/main.js"
        "vendor/asset/base64/base64.min.js"
        "view/theme/frost/js/acl.js"
        "view/theme/frost/js/jquery.divgrow-1.3.1.f1.js"
diff --git a/view/js/acl.js b/view/js/acl.js
new file mode 100644 (file)
index 0000000..257e2c1
--- /dev/null
@@ -0,0 +1,359 @@
+function ACL(backend_url, preset, automention, is_mobile){
+
+       this.url = backend_url;
+       this.automention = automention;
+       this.is_mobile = is_mobile;
+
+
+       this.kp_timer = null;
+
+       if (preset == undefined) {
+               preset = [];
+       }
+       this.allow_cid = (preset[0] || []);
+       this.allow_gid = (preset[1] || []);
+       this.deny_cid  = (preset[2] || []);
+       this.deny_gid  = (preset[3] || []);
+       this.group_uids = [];
+       this.forumCache = null;
+
+       if (this.is_mobile) {
+               this.nw = 1;
+       } else {
+               this.nw = 4;
+       }
+
+
+       this.list_content = $("#acl-list-content");
+       this.item_tpl = unescape($(".acl-list-item[rel=acl-template]").html());
+       this.showall = $("#acl-showall");
+
+       if (preset.length==0) {
+               this.showall.addClass("selected");
+       }
+
+       /*events*/
+       this.showall.click(this.on_showall.bind(this));
+       $(document).on("click", ".acl-button-show", this.on_button_show.bind(this));
+       $(document).on("click", ".acl-button-hide", this.on_button_hide.bind(this));
+       $("#acl-search").keypress(this.on_search.bind(this));
+       $("#acl-wrapper").parents("form").submit(this.on_submit.bind(this));
+
+       /* add/remove mentions  */
+       this.element = $("#profile-jot-text");
+       this.htmlelm = this.element.get()[0];
+
+       /* startup! */
+       this.get(0,100);
+}
+
+ACL.prototype.remove_mention = function(id) {
+       if (!this.automention) {
+               return;
+       }
+       var nick = this.data[id].nick;
+       var addr = this.data[id].addr;
+
+       if (addr != "") {
+               var searchText = "!" + addr + " ";
+       } else {
+               var searchText = "!" + nick + "+" + id + " ";
+       }
+
+       var start = this.element.val().indexOf(searchText);
+       if (start < 0) {
+               return;
+       }
+       var end = start + searchText.length;
+       this.element.setSelection(start, end).replaceSelectedText('').collapseSelection(false);
+};
+
+ACL.prototype.add_mention = function(id) {
+       if (!this.automention) {
+               return;
+       }
+       var nick = this.data[id].nick;
+       var addr = this.data[id].addr;
+
+       if (addr != "") {
+               var searchText = "!" + addr + " ";
+       } else {
+               var searchText = "!" + nick + "+" + id + " ";
+       }
+
+       if (this.element.val().indexOf( searchText) >= 0 ) {
+               return;
+       }
+       this.element.val(searchText + this.element.val()).trigger('change');
+}
+
+ACL.prototype.on_submit = function(){
+       var aclfields = $("#acl-fields").html("");
+       $(this.allow_gid).each(function(i,v){
+               aclfields.append("<input type='hidden' name='group_allow[]' value='"+v+"'>");
+       });
+       $(this.allow_cid).each(function(i,v){
+               aclfields.append("<input type='hidden' name='contact_allow[]' value='"+v+"'>");
+       });
+       $(this.deny_gid).each(function(i,v){
+               aclfields.append("<input type='hidden' name='group_deny[]' value='"+v+"'>");
+       });
+       $(this.deny_cid).each(function(i,v){
+               aclfields.append("<input type='hidden' name='contact_deny[]' value='"+v+"'>");
+       });
+};
+
+ACL.prototype.search = function(){
+       var srcstr = $("#acl-search").val();
+       this.list_content.html("");
+       this.get(0,100, srcstr);
+};
+
+ACL.prototype.on_search = function(event){
+       if (this.kp_timer) clearTimeout(this.kp_timer);
+       this.kp_timer = setTimeout( this.search.bind(this), 1000);
+};
+
+ACL.prototype.on_showall = function(event){
+       event.preventDefault()
+       event.stopPropagation();
+
+       if (this.showall.hasClass("selected")){
+               return false;
+       }
+       this.showall.addClass("selected");
+
+       this.allow_cid = [];
+       this.allow_gid = [];
+       this.deny_cid  = [];
+       this.deny_gid  = [];
+
+       this.update_view();
+
+       return false;
+};
+
+ACL.prototype.on_button_show = function(event){
+       event.preventDefault()
+       event.stopImmediatePropagation()
+       event.stopPropagation();
+
+       this.set_allow($(event.target).parent().attr('id'));
+
+       return false;
+};
+
+ACL.prototype.on_button_hide = function(event){
+       event.preventDefault()
+       event.stopImmediatePropagation()
+       event.stopPropagation();
+
+       this.set_deny($(event.target).parent().attr('id'));
+
+       return false;
+};
+
+ACL.prototype.set_allow = function(itemid) {
+       type = itemid[0];
+       id   = parseInt(itemid.substr(1));
+
+       switch (type){
+               case "g":
+                       if (this.allow_gid.indexOf(id) < 0) {
+                               this.allow_gid.push(id);
+                       }else {
+                               this.allow_gid.remove(id);
+                       }
+                       if (this.deny_gid.indexOf(id) >= 0) {
+                               this.deny_gid.remove(id);
+                       }
+                       break;
+               case "c":
+                       if (this.allow_cid.indexOf(id) < 0){
+                               this.allow_cid.push(id);
+                               if (this.data[id].forum == "1") {
+                                       // If we have select already a forum,
+                                       // we need to remove the old one (because friendica does
+                                       // allow only one forum as receiver).
+                                       if (this.forumCache !== null && this.forumCache !== id) {
+                                               this.deselectCid(this.forumCache);
+                                       }
+                                       // Update the forum cache.
+                                       this.forumCache = id;
+                                       this.add_mention(id);
+                               }
+                       } else {
+                               this.allow_cid.remove(id);
+                               if (this.data[id].forum == "1") {
+                                       this.remove_mention(id);
+                               }
+                       }
+                       if (this.deny_cid.indexOf(id) >=0 ) {
+                               this.deny_cid.remove(id);
+                       }
+                       break;
+       }
+       this.update_view();
+};
+
+ACL.prototype.set_deny = function(itemid){
+       type = itemid[0];
+       id     = parseInt(itemid.substr(1));
+
+       switch(type){
+               case "g":
+                       if (this.deny_gid.indexOf(id)<0){
+                               this.deny_gid.push(id)
+                       } else {
+                               this.deny_gid.remove(id);
+                       }
+                       if (this.allow_gid.indexOf(id)>=0) this.allow_gid.remove(id);
+                       break;
+               case "c":
+                       if (this.data[id].forum=="1") this.remove_mention(id);
+                       if (this.deny_cid.indexOf(id)<0){
+                               this.deny_cid.push(id)
+                       } else {
+                               this.deny_cid.remove(id);
+                       }
+                       if (this.allow_cid.indexOf(id)>=0) this.allow_cid.remove(id);
+                       break;
+       }
+       this.update_view();
+};
+
+ACL.prototype.is_show_all = function() {
+       return (this.allow_gid.length==0 && this.allow_cid.length==0 &&
+               this.deny_gid.length==0 && this.deny_cid.length==0);
+};
+
+ACL.prototype.update_view = function(){
+       if (this.is_show_all()){
+                       this.showall.addClass("selected");
+                       /* jot acl */
+                               $('#jot-perms-icon').removeClass('lock').addClass('unlock');
+                               $('#jot-public').show();
+                               $('.profile-jot-net input').attr('disabled', false);
+                               if(typeof editor != 'undefined' && editor != false) {
+                                       $('#profile-jot-desc').html(ispublic);
+                               }
+
+       } else {
+                       this.showall.removeClass("selected");
+                       /* jot acl */
+                               $('#jot-perms-icon').removeClass('unlock').addClass('lock');
+                               $('#jot-public').hide();
+                               $('.profile-jot-net input').attr('disabled', 'disabled');
+                               $('#profile-jot-desc').html('&nbsp;');
+       }
+       $("#acl-list-content .acl-list-item").each(function(){
+               $(this).removeClass("groupshow grouphide");
+       });
+
+       $("#acl-list-content .acl-list-item").each(function(index, element){
+               itemid = $(element).attr('id');
+               type = itemid[0];
+               id       = parseInt(itemid.substr(1));
+
+               btshow = $(element).children(".acl-button-show").removeClass("selected");
+               bthide = $(element).children(".acl-button-hide").removeClass("selected");
+
+               switch(type){
+                       case "g":
+                               var uclass = "";
+                               if (this.allow_gid.indexOf(id)>=0){
+                                       btshow.addClass("selected");
+                                       bthide.removeClass("selected");
+                                       uclass="groupshow";
+                               }
+                               if (this.deny_gid.indexOf(id)>=0){
+                                       btshow.removeClass("selected");
+                                       bthide.addClass("selected");
+                                       uclass="grouphide";
+                               }
+
+                               $(this.group_uids[id]).each(function(i,v) {
+                                       if(uclass == "grouphide")
+                                               $("#c"+v).removeClass("groupshow");
+                                       if(uclass != "") {
+                                               var cls = $("#c"+v).attr('class');
+                                               if( cls == undefined)
+                                                       return true;
+                                               var hiding = cls.indexOf('grouphide');
+                                               if(hiding == -1)
+                                                       $("#c"+v).addClass(uclass);
+                                       }
+                               });
+
+                               break;
+                       case "c":
+                               if (this.allow_cid.indexOf(id)>=0){
+                                       btshow.addClass("selected");
+                                       bthide.removeClass("selected");
+                               }
+                               if (this.deny_cid.indexOf(id)>=0){
+                                       btshow.removeClass("selected");
+                                       bthide.addClass("selected");
+                               }
+               }
+
+       }.bind(this));
+
+}
+
+ACL.prototype.get = function(start,count, search){
+       var postdata = {
+               start:start,
+               count:count,
+               search:search,
+       }
+
+       $.ajax({
+               type:'POST',
+               url: this.url,
+               data: postdata,
+               dataType: 'json',
+               success:this.populate.bind(this)
+       });
+};
+
+ACL.prototype.populate = function(data){
+       var height = Math.ceil(data.tot / this.nw) * 42;
+       this.list_content.height(height);
+       this.data = {};
+       $(data.items).each(function(index, item) {
+               if (item.separator != undefined) {
+                       html = "<hr class='clear'>";
+               } else {
+                       html = "<div class='acl-list-item {4} {5} type{2}' title='{6}' id='{2}{3}'>"+this.item_tpl+"</div>";
+                       html = html.format(item.photo, item.name, item.type, item.id, (item.forum=='1'?'forum':''), item.network, item.link);
+                       if (item.uids != undefined) {
+                               this.group_uids[item.id] = item.uids;
+                       }
+               }
+               this.list_content.append(html);
+               this.data[item.id] = item;
+       }.bind(this));
+       $(".acl-list-item img[data-src]", this.list_content).each(function(i, el){
+               // Add src attribute for images with a data-src attribute
+               $(el).attr('src', $(el).data("src"));
+       });
+
+       this.update_view();
+};
+
+/**
+ * @brief Deselect previous selected contact.
+ * 
+ * @param {int} id The contact ID.
+ * @returns {void}
+ */
+ACL.prototype.deselectCid = function(id) {
+       if (this.allow_cid.indexOf(id) >= 0) {
+               this.allow_cid.remove(id);
+       }
+       if (this.deny_cid.indexOf(id) >=0 ) {
+               this.deny_cid.remove(id);
+       }
+       this.remove_mention(id);
+};
diff --git a/view/js/ajaxupload.js b/view/js/ajaxupload.js
new file mode 100644 (file)
index 0000000..1c34b11
--- /dev/null
@@ -0,0 +1,703 @@
+/**
+ * AJAX Upload ( http://valums.com/ajax-upload/ ) 
+ * Copyright (c) Andris Valums
+ * Licensed under the MIT license ( http://valums.com/mit-license/ )
+ * Thanks to Gary Haran, David Mark, Corey Burns and others for contributions. 
+ */
+
+(function () {
+    /* global window */
+    /* jslint browser: true, devel: true, undef: true, nomen: true, bitwise: true, regexp: true, newcap: true, immed: true */
+    
+    /**
+     * Wrapper for FireBug's console.log
+     */
+    function log(){
+        if (typeof(console) != 'undefined' && typeof(console.log) == 'function'){            
+            Array.prototype.unshift.call(arguments, '[Ajax Upload]');
+            console.log( Array.prototype.join.call(arguments, ' '));
+        }
+    } 
+
+    /**
+     * Attaches event to a dom element.
+     * @param {Element} el
+     * @param type event name
+     * @param fn callback This refers to the passed element
+     */
+    function addEvent(el, type, fn){
+        if (el.addEventListener) {
+            el.addEventListener(type, fn, false);
+        } else if (el.attachEvent) {
+            el.attachEvent('on' + type, function(){
+                fn.call(el);
+               });
+           } else {
+            throw new Error('not supported or DOM not loaded');
+        }
+    }   
+    
+    /**
+     * Attaches resize event to a window, limiting
+     * number of event fired. Fires only when encounteres
+     * delay of 100 after series of events.
+     * 
+     * Some browsers fire event multiple times when resizing
+     * http://www.quirksmode.org/dom/events/resize.html
+     * 
+     * @param fn callback This refers to the passed element
+     */
+    function addResizeEvent(fn){
+        var timeout;
+               
+           addEvent(window, 'resize', function(){
+            if (timeout){
+                clearTimeout(timeout);
+            }
+            timeout = setTimeout(fn, 100);                        
+        });
+    }    
+
+    // Get offset adding all offsets, slow fall-back method
+    var getOffsetSlow = function(el){
+        var top = 0, left = 0;
+        do {
+            top += el.offsetTop || 0;
+            left += el.offsetLeft || 0;
+            el = el.offsetParent;
+        } while (el);
+        
+        return {
+            left: left,
+            top: top
+        };
+    };
+
+    
+    // Needs more testing, will be rewriten for next version        
+    // getOffset function copied from jQuery lib (http://jquery.com/)
+    if (document.documentElement.getBoundingClientRect){
+        // Get Offset using getBoundingClientRect
+        // http://ejohn.org/blog/getboundingclientrect-is-awesome/
+        var getOffset = function(el){
+            var box = el.getBoundingClientRect();
+            var doc = el.ownerDocument;
+            var body = doc.body;
+            var docElem = doc.documentElement; // for ie 
+            var clientTop = docElem.clientTop || body.clientTop || 0;
+            var clientLeft = docElem.clientLeft || body.clientLeft || 0;
+             
+            // In Internet Explorer 7 getBoundingClientRect property is treated as physical,
+            // while others are logical. Make all logical, like in IE8.        
+            var zoom = 1;            
+            if (body.getBoundingClientRect) {
+                var bound = body.getBoundingClientRect();
+                zoom = (bound.right - bound.left) / body.clientWidth;
+            }
+
+            // some CSS layouts gives 0 width and/or bounding boxes
+            // in this case we fall back to the slow method
+            if (zoom == 0 || body.clientWidth == 0)
+                return getOffsetSlow(el);
+            
+            if (zoom > 1) {
+                clientTop = 0;
+                clientLeft = 0;
+            }
+            
+            var top = box.top / zoom + (window.pageYOffset || docElem && docElem.scrollTop / zoom || body.scrollTop / zoom) - clientTop, left = box.left / zoom + (window.pageXOffset || docElem && docElem.scrollLeft / zoom || body.scrollLeft / zoom) - clientLeft;
+            
+            return {
+                top: top,
+                left: left
+            };
+        };        
+    } else {
+      var getOffset = getOffsetSlow;
+    }
+    
+    /**
+     * Returns left, top, right and bottom properties describing the border-box,
+     * in pixels, with the top-left relative to the body
+     * @param {Element} el
+     * @return {Object} Contains left, top, right,bottom
+     */
+    function getBox(el){
+        var left, right, top, bottom;
+        var offset = getOffset(el);
+        left = offset.left;
+        top = offset.top;
+        
+        right = left + el.offsetWidth;
+        bottom = top + el.offsetHeight;
+        
+        return {
+            left: left,
+            right: right,
+            top: top,
+            bottom: bottom
+        };
+    }
+    
+    /**
+     * Helper that takes object literal
+     * and add all properties to element.style
+     * @param {Element} el
+     * @param {Object} styles
+     */
+    function addStyles(el, styles){
+        for (var name in styles) {
+            if (styles.hasOwnProperty(name)) {
+                el.style[name] = styles[name];
+            }
+        }
+    }
+        
+    /**
+     * Function places an absolutely positioned
+     * element on top of the specified element
+     * copying position and dimentions.
+     * @param {Element} from
+     * @param {Element} to
+     */    
+    function copyLayout(from, to){
+           var box = getBox(from);
+        
+        addStyles(to, {
+               position: 'absolute',                    
+               left : box.left + 'px',
+               top : box.top + 'px',
+               width : from.offsetWidth + 'px',
+               height : from.offsetHeight + 'px'
+           });        
+       to.title = from.title;
+
+    }
+
+    /**
+    * Creates and returns element from html chunk
+    * Uses innerHTML to create an element
+    */
+    var toElement = (function(){
+        var div = document.createElement('div');
+        return function(html){
+            div.innerHTML = html;
+            var el = div.firstChild;
+            return div.removeChild(el);
+        };
+    })();
+            
+    /**
+     * Function generates unique id
+     * @return unique id 
+     */
+    var getUID = (function(){
+        var id = 0;
+        return function(){
+            return 'ValumsAjaxUpload' + id++;
+        };
+    })();        
+    /**
+     * Get file name from path
+     * @param {String} file path to file
+     * @return filename
+     */  
+    function fileFromPath(file){
+        return file.replace(/.*(\/|\\)/, "");
+    }
+    
+    /**
+     * Get file extension lowercase
+     * @param {String} file name
+     * @return file extenstion
+     */    
+    function getExt(file){
+        return (-1 !== file.indexOf('.')) ? file.replace(/.*[.]/, '') : '';
+    }
+
+    function hasClass(el, name){        
+        var re = new RegExp('\\b' + name + '\\b');        
+        return re.test(el.className);
+    }    
+    function addClass(el, name){
+        if ( ! hasClass(el, name)){   
+            el.className += ' ' + name;
+        }
+    }    
+    function removeClass(el, name){
+        var re = new RegExp('\\b' + name + '\\b');                
+        el.className = el.className.replace(re, '');        
+    }
+    
+    function removeNode(el){
+        el.parentNode.removeChild(el);
+    }
+
+    /**
+     * Easy styling and uploading
+     * @constructor
+     * @param button An element you want convert to 
+     * upload button. Tested dimentions up to 500x500px
+     * @param {Object} options See defaults below.
+     */
+    window.AjaxUpload = function(button, options){
+        this._settings = {
+            // Location of the server-side upload script
+            action: 'upload.php',
+            // File upload name
+            name: 'userfile',
+            // Additional data to send
+            data: {},
+            // Submit file as soon as it's selected
+            autoSubmit: true,
+            // The type of data that you're expecting back from the server.
+            // html and xml are detected automatically.
+            // Only useful when you are using json data as a response.
+            // Set to "json" in that case. 
+            responseType: false,
+            // Class applied to button when mouse is hovered
+            hoverClass: 'hover',
+            // Class applied to button when button is focused
+            focusClass: 'focus',
+            // Class applied to button when AU is disabled
+            disabledClass: 'disabled',            
+            // When user selects a file, useful with autoSubmit disabled
+            // You can return false to cancel upload                   
+            onChange: function(file, extension){
+            },
+            // Callback to fire before file is uploaded
+            // You can return false to cancel upload
+            onSubmit: function(file, extension){
+            },
+            // Fired when file upload is completed
+            // WARNING! DO NOT USE "FALSE" STRING AS A RESPONSE!
+            onComplete: function(file, response){
+            }
+        };
+                        
+        // Merge the users options with our defaults
+        for (var i in options) {
+            if (options.hasOwnProperty(i)){
+                this._settings[i] = options[i];
+            }
+        }
+                
+        // button isn't necessary a dom element
+        if (button.jquery){
+            // jQuery object was passed
+            button = button[0];
+        } else if (typeof button == "string") {
+            if (/^#.*/.test(button)){
+                // If jQuery user passes #elementId don't break it                                     
+                button = button.slice(1);                
+            }
+            
+            button = document.getElementById(button);
+        }
+        
+        if ( ! button || button.nodeType !== 1){
+            throw new Error("Please make sure that you're passing a valid element"); 
+        }
+                
+        if ( button.nodeName.toUpperCase() == 'A'){
+            // disable link                       
+            addEvent(button, 'click', function(e){
+                if (e && e.preventDefault){
+                    e.preventDefault();
+                } else if (window.event){
+                    window.event.returnValue = false;
+                }
+            });
+        }
+                    
+        // DOM element
+        this._button = button;        
+        // DOM element                 
+        this._input = null;
+        // If disabled clicking on button won't do anything
+        this._disabled = false;
+        
+        // if the button was disabled before refresh if will remain
+        // disabled in FireFox, let's fix it
+        this.enable();        
+        
+        this._rerouteClicks();
+    };
+    
+    // assigning methods to our class
+    AjaxUpload.prototype = {
+        setData: function(data){
+            this._settings.data = data;
+        },
+        disable: function(){            
+            addClass(this._button, this._settings.disabledClass);
+            this._disabled = true;
+            
+            var nodeName = this._button.nodeName.toUpperCase();            
+            if (nodeName == 'INPUT' || nodeName == 'BUTTON'){
+                this._button.setAttribute('disabled', 'disabled');
+            }            
+            
+            // hide input
+            if (this._input){
+                // We use visibility instead of display to fix problem with Safari 4
+                // The problem is that the value of input doesn't change if it 
+                // has display none when user selects a file           
+                this._input.parentNode.style.visibility = 'hidden';
+            }
+        },
+        enable: function(){
+            removeClass(this._button, this._settings.disabledClass);
+            this._button.removeAttribute('disabled');
+            this._disabled = false;
+            
+        },
+        /**
+         * Creates invisible file input 
+         * that will hover above the button
+         * <div><input type='file' /></div>
+         */
+        _createInput: function(){ 
+            var self = this;
+                        
+            var input = document.createElement("input");
+            input.setAttribute('type', 'file');
+            input.setAttribute('name', this._settings.name);
+
+            addStyles(input, {
+                'position' : 'absolute',
+                // in Opera only 'browse' button
+                // is clickable and it is located at
+                // the right side of the input
+                'right' : 0,
+                'margin' : 0,
+                'padding' : 0,
+                'fontSize' : '480px',
+                // in Firefox if font-family is set to
+                // 'inherit' the input doesn't work
+                'fontFamily' : 'sans-serif',
+                'cursor' : 'pointer'
+            });            
+
+            var div = document.createElement("div");                        
+            addStyles(div, {
+                'display' : 'block',
+                'position' : 'absolute',
+                'overflow' : 'hidden',
+                'margin' : 0,
+                'padding' : 0,                
+                'opacity' : 0,
+                // Make sure browse button is in the right side
+                // in Internet Explorer
+                'direction' : 'ltr',
+                //Max zIndex supported by Opera 9.0-9.2
+                'zIndex': 2147483583,
+                               'cursor' : 'pointer'
+
+            });
+            
+            // Make sure that element opacity exists.
+            // Otherwise use IE filter            
+            if ( div.style.opacity !== "0") {
+                if (typeof(div.filters) == 'undefined'){
+                    throw new Error('Opacity not supported by the browser');
+                }
+                div.style.filter = "alpha(opacity=0)";
+            }            
+            
+            addEvent(input, 'change', function(){
+                 
+                if ( ! input || input.value === ''){                
+                    return;                
+                }
+                            
+                // Get filename from input, required                
+                // as some browsers have path instead of it          
+                var file = fileFromPath(input.value);
+                                
+                if (false === self._settings.onChange.call(self, file, getExt(file))){
+                    self._clearInput();                
+                    return;
+                }
+                
+                // Submit form when value is changed
+                if (self._settings.autoSubmit) {
+                    self.submit();
+                }
+            });            
+
+            addEvent(input, 'mouseover', function(){
+                addClass(self._button, self._settings.hoverClass);
+            });
+            
+            addEvent(input, 'mouseout', function(){
+                removeClass(self._button, self._settings.hoverClass);
+                removeClass(self._button, self._settings.focusClass);
+                
+                // We use visibility instead of display to fix problem with Safari 4
+                // The problem is that the value of input doesn't change if it 
+                // has display none when user selects a file           
+                input.parentNode.style.visibility = 'hidden';
+
+            });   
+                        
+            addEvent(input, 'focus', function(){
+                addClass(self._button, self._settings.focusClass);
+            });
+            
+            addEvent(input, 'blur', function(){
+                removeClass(self._button, self._settings.focusClass);
+            });
+            
+               div.appendChild(input);
+            document.body.appendChild(div);
+              
+            this._input = input;
+        },
+        _clearInput : function(){
+            if (!this._input){
+                return;
+            }            
+                             
+            // this._input.value = ''; Doesn't work in IE6                               
+            removeNode(this._input.parentNode);
+            this._input = null;                                                                   
+            this._createInput();
+            
+            removeClass(this._button, this._settings.hoverClass);
+            removeClass(this._button, this._settings.focusClass);
+        },
+        /**
+         * Function makes sure that when user clicks upload button,
+         * the this._input is clicked instead
+         */
+        _rerouteClicks: function(){
+            var self = this;
+            
+            // IE will later display 'access denied' error
+            // if you use using self._input.click()
+            // other browsers just ignore click()
+
+            addEvent(self._button, 'mouseover', function(){
+                if (self._disabled){
+                    return;
+                }
+                                
+                if ( ! self._input){
+                       self._createInput();
+                }
+                
+                var div = self._input.parentNode;                            
+                copyLayout(self._button, div);
+                div.style.visibility = 'visible';
+                                
+            });
+            
+            
+            // commented because we now hide input on mouseleave
+            /**
+             * When the window is resized the elements 
+             * can be misaligned if button position depends
+             * on window size
+             */
+            //addResizeEvent(function(){
+            //    if (self._input){
+            //        copyLayout(self._button, self._input.parentNode);
+            //    }
+            //});            
+                                         
+        },
+        /**
+         * Creates iframe with unique name
+         * @return {Element} iframe
+         */
+        _createIframe: function(){
+            // We can't use getTime, because it sometimes return
+            // same value in safari :(
+            var id = getUID();            
+             
+            // We can't use following code as the name attribute
+            // won't be properly registered in IE6, and new window
+            // on form submit will open
+            // var iframe = document.createElement('iframe');
+            // iframe.setAttribute('name', id);                        
+            var iframe = toElement('<iframe src="javascript:false;" name="' + id + '" />');
+            // src="javascript:false; was added
+            // because it possibly removes ie6 prompt 
+            // "This page contains both secure and nonsecure items"
+            // Anyway, it doesn't do any harm.            
+            iframe.setAttribute('id', id);
+            
+            iframe.style.display = 'none';
+            document.body.appendChild(iframe);
+            
+            return iframe;
+        },
+        /**
+         * Creates form, that will be submitted to iframe
+         * @param {Element} iframe Where to submit
+         * @return {Element} form
+         */
+        _createForm: function(iframe){
+            var settings = this._settings;
+                        
+            // We can't use the following code in IE6
+            // var form = document.createElement('form');
+            // form.setAttribute('method', 'post');
+            // form.setAttribute('enctype', 'multipart/form-data');
+            // Because in this case file won't be attached to request                    
+            var form = toElement('<form method="post" enctype="multipart/form-data"></form>');
+                        
+            form.setAttribute('action', settings.action);
+            form.setAttribute('target', iframe.name);                                   
+            form.style.display = 'none';
+            document.body.appendChild(form);
+            
+            // Create hidden input element for each data key
+            for (var prop in settings.data) {
+                if (settings.data.hasOwnProperty(prop)){
+                    var el = document.createElement("input");
+                    el.setAttribute('type', 'hidden');
+                    el.setAttribute('name', prop);
+                    el.setAttribute('value', settings.data[prop]);
+                    form.appendChild(el);
+                }
+            }
+            return form;
+        },
+        /**
+         * Gets response from iframe and fires onComplete event when ready
+         * @param iframe
+         * @param file Filename to use in onComplete callback 
+         */
+        _getResponse : function(iframe, file){            
+            // getting response
+            var toDeleteFlag = false, self = this, settings = this._settings;   
+               
+            addEvent(iframe, 'load', function(){                
+                
+                if (// For Safari 
+                    iframe.src == "javascript:'%3Chtml%3E%3C/html%3E';" ||
+                    // For FF, IE
+                    iframe.src == "javascript:'<html></html>';"){                                                                        
+                        // First time around, do not delete.
+                        // We reload to blank page, so that reloading main page
+                        // does not re-submit the post.
+                        
+                        if (toDeleteFlag) {
+                            // Fix busy state in FF3
+                            setTimeout(function(){
+                                removeNode(iframe);
+                            }, 0);
+                        }
+                                                
+                        return;
+                }
+                
+                var doc = iframe.contentDocument ? iframe.contentDocument : window.frames[iframe.id].document;
+                
+                // fixing Opera 9.26,10.00
+                if (doc.readyState && doc.readyState != 'complete') {
+                   // Opera fires load event multiple times
+                   // Even when the DOM is not ready yet
+                   // this fix should not affect other browsers
+                   return;
+                }
+                
+                // fixing Opera 9.64
+                if (doc.body && doc.body.innerHTML == "false") {
+                    // In Opera 9.64 event was fired second time
+                    // when body.innerHTML changed from false 
+                    // to server response approx. after 1 sec
+                    return;
+                }
+                
+                var response;
+                
+                if (doc.XMLDocument) {
+                    // response is a xml document Internet Explorer property
+                    response = doc.XMLDocument;
+                } else if (doc.body){
+                    // response is html document or plain text
+                    response = doc.body.innerHTML;
+                    
+                    if (settings.responseType && settings.responseType.toLowerCase() == 'json') {
+                        // If the document was sent as 'application/javascript' or
+                        // 'text/javascript', then the browser wraps the text in a <pre>
+                        // tag and performs html encoding on the contents.  In this case,
+                        // we need to pull the original text content from the text node's
+                        // nodeValue property to retrieve the unmangled content.
+                        // Note that IE6 only understands text/html
+                        if (doc.body.firstChild && doc.body.firstChild.nodeName.toUpperCase() == 'PRE') {
+                            doc.normalize();
+                            response = doc.body.firstChild.firstChild.nodeValue;
+                        }
+                        
+                        if (response) {
+                            response = eval("(" + response + ")");
+                        } else {
+                            response = {};
+                        }
+                    }
+                } else {
+                    // response is a xml document
+                    response = doc;
+                }
+                
+                settings.onComplete.call(self, file, response);
+                
+                // Reload blank page, so that reloading main page
+                // does not re-submit the post. Also, remember to
+                // delete the frame
+                toDeleteFlag = true;
+                
+                // Fix IE mixed content issue
+                iframe.src = "javascript:'<html></html>';";
+            });            
+        },        
+        /**
+         * Upload file contained in this._input
+         */
+        submit: function(){                        
+            var self = this, settings = this._settings;
+            
+            if ( ! this._input || this._input.value === ''){                
+                return;                
+            }
+                                    
+            var file = fileFromPath(this._input.value);
+            
+            // user returned false to cancel upload
+            if (false === settings.onSubmit.call(this, file, getExt(file))){
+                this._clearInput();                
+                return;
+            }
+            
+            // sending request    
+            var iframe = this._createIframe();
+            var form = this._createForm(iframe);
+            
+            // assuming following structure
+            // div -> input type='file'
+            removeNode(this._input.parentNode);            
+            removeClass(self._button, self._settings.hoverClass);
+            removeClass(self._button, self._settings.focusClass);
+                        
+            form.appendChild(this._input);
+                        
+            form.submit();
+
+            // request set, clean up                
+            removeNode(form); form = null;                          
+            removeNode(this._input); this._input = null;            
+            
+            // Get response from iframe and fire onComplete event when ready
+            this._getResponse(iframe, file);            
+
+            // get ready for next request            
+            this._createInput();
+        }
+    };
+})(); 
diff --git a/view/js/autocomplete.js b/view/js/autocomplete.js
new file mode 100644 (file)
index 0000000..219ad79
--- /dev/null
@@ -0,0 +1,490 @@
+/**
+ * @brief Friendica people autocomplete
+ *
+ * require jQuery, jquery.textcomplete
+ *
+ * for further documentation look at:
+ * http://yuku-t.com/jquery-textcomplete/
+ *
+ * https://github.com/yuku-t/jquery-textcomplete/blob/master/doc/how_to_use.md
+ */
+
+
+function contact_search(term, callback, backend_url, type, mode) {
+
+       // Check if there is a conversation id to include the unkonwn contacts of the conversation
+       var conv_id = document.activeElement.id.match(/\d+$/);
+
+       // Check if there is a cached result that contains the same information we would get with a full server-side search
+       var bt = backend_url+type;
+       if(!(bt in contact_search.cache)) contact_search.cache[bt] = {};
+
+       var lterm = term.toLowerCase(); // Ignore case
+       for(var t in contact_search.cache[bt]) {
+               if(lterm.indexOf(t) >= 0) { // A more broad search has been performed already, so use those results
+                       // Filter old results locally
+                       var matching = contact_search.cache[bt][t].filter(function (x) { return (x.name.toLowerCase().indexOf(lterm) >= 0 || (typeof x.nick !== 'undefined' && x.nick.toLowerCase().indexOf(lterm) >= 0)); }); // Need to check that nick exists because groups don't have one
+                       matching.unshift({forum:false, text: term, replace: term});
+                       setTimeout(function() { callback(matching); } , 1); // Use "pseudo-thread" to avoid some problems
+                       return;
+               }
+       }
+
+       var postdata = {
+               start:0,
+               count:100,
+               search:term,
+               type:type,
+       };
+
+       if(conv_id !== null)
+               postdata['conversation'] = conv_id[0];
+
+       if(mode !== null)
+               postdata['smode'] = mode;
+
+
+       $.ajax({
+               type:'POST',
+               url: backend_url,
+               data: postdata,
+               dataType: 'json',
+               success: function(data){
+                       // Cache results if we got them all (more information would not improve results)
+                       // data.count represents the maximum number of items
+                       if(data.items.length -1 < data.count) {
+                               contact_search.cache[bt][lterm] = data.items;
+                       }
+                       var items = data.items.slice(0);
+                       items.unshift({taggable:false, text: term, replace: term});
+                       callback(items);
+               },
+       }).fail(function () {callback([]); }); // Callback must be invoked even if something went wrong.
+}
+contact_search.cache = {};
+
+
+function contact_format(item) {
+       // Show contact information if not explicitly told to show something else
+       if(typeof item.text === 'undefined') {
+               var desc = ((item.label) ? item.nick + ' ' + item.label : item.nick);
+               var forum = ((item.forum) ? 'forum' : '');
+               if(typeof desc === 'undefined') desc = '';
+               if(desc) desc = ' ('+desc+')';
+               return "<div class='{0}' title='{4}'><img class='acpopup-img' src='{1}'><span class='acpopup-contactname'>{2}</span><span class='acpopup-sub-text'>{3}</span><div class='clear'></div></div>".format(forum, item.photo, item.name, desc, item.link);
+       }
+       else
+               return "<div>" + item.text + "</div>";
+}
+
+function editor_replace(item) {
+       if (typeof item.replace !== 'undefined') {
+               return '$1$2' + item.replace;
+       }
+
+       if (typeof item.addr !== 'undefined') {
+               return '$1$2' + item.addr + ' ';
+       }
+
+       // $2 ensures that prefix (@,@!) is preserved
+       var id = item.id;
+
+       // don't add the id if it is empty (the id empty eg. if there are unknow contacts in thread)
+       if (id.length < 1) {
+               return '$1$2' + item.nick.replace(' ', '') + ' ';
+       }
+       // 16 chars of hash should be enough. Full hash could be used if it can be done in a visually appealing way.
+       // 16 chars is also the minimum length in the backend (otherwise it's interpreted as a local id).
+       if (id.length > 16) {
+               id = item.id.substring(0,16);
+       }
+       return '$1$2' + item.nick.replace(' ', '') + '+' + id + ' ';
+}
+
+function basic_replace(item) {
+       if(typeof item.replace !== 'undefined')
+               return '$1'+item.replace;
+
+       return '$1'+item.name+' ';
+}
+
+function webbie_replace(item) {
+       if(typeof item.replace !== 'undefined')
+               return '$1'+item.replace;
+
+       return '$1'+item.nick+' ';
+}
+
+function trim_replace(item) {
+       if(typeof item.replace !== 'undefined')
+               return '$1'+item.replace;
+
+       return '$1'+item.name;
+}
+
+
+function submit_form(e) {
+       $(e).parents('form').submit();
+}
+
+function getWord(text, caretPos) {
+       var index = text.indexOf(caretPos);
+       var postText = text.substring(caretPos, caretPos+8);
+       if ((postText.indexOf("[/list]") > 0) || postText.indexOf("[/ul]") > 0 || postText.indexOf("[/ol]") > 0) {
+               return postText;
+       }
+}
+
+function getCaretPosition(ctrl) {
+       var CaretPos = 0;   // IE Support
+       if (document.selection) {
+               ctrl.focus();
+               var Sel = document.selection.createRange();
+               Sel.moveStart('character', -ctrl.value.length);
+               CaretPos = Sel.text.length;
+       }
+       // Firefox support
+       else if (ctrl.selectionStart || ctrl.selectionStart == '0')
+               CaretPos = ctrl.selectionStart;
+       return (CaretPos);
+}
+
+function setCaretPosition(ctrl, pos){
+       if(ctrl.setSelectionRange) {
+               ctrl.focus();
+               ctrl.setSelectionRange(pos,pos);
+       }
+       else if (ctrl.createTextRange) {
+               var range = ctrl.createTextRange();
+               range.collapse(true);
+               range.moveEnd('character', pos);
+               range.moveStart('character', pos);
+               range.select();
+       }
+}
+
+function listNewLineAutocomplete(id) {
+       var text = document.getElementById(id);
+       var caretPos = getCaretPosition(text)
+       var word = getWord(text.value, caretPos);
+       if (word != null) {
+               var textBefore = text.value.substring(0, caretPos);
+               var textAfter  = text.value.substring(caretPos, text.length);
+               $('#' + id).val(textBefore + '\r\n[*] ' + textAfter).trigger('change');
+               setCaretPosition(text, caretPos + 5);
+               return true;
+       }
+       else {
+               return false;
+       }
+}
+
+function string2bb(element) {
+       if(element == 'bold') return 'b';
+       else if(element == 'italic') return 'i';
+       else if(element == 'underline') return 'u';
+       else if(element == 'overline') return 'o';
+       else if(element == 'strike') return 's';
+       else return element;
+}
+
+/**
+ * jQuery plugin 'editor_autocomplete'
+ */
+(function( $ ) {
+       $.fn.editor_autocomplete = function(backend_url) {
+
+               // Autocomplete contacts
+               contacts = {
+                       match: /(^|\s)(@\!*)([^ \n]+)$/,
+                       index: 3,
+                       search: function(term, callback) { contact_search(term, callback, backend_url, 'c'); },
+                       replace: editor_replace,
+                       template: contact_format,
+               };
+
+               // Autocomplete forums
+               forums = {
+                       match: /(^|\s)(!\!*)([^ \n]+)$/,
+                       index: 3,
+                       search: function(term, callback) { contact_search(term, callback, backend_url, 'f'); },
+                       replace: editor_replace,
+                       template: contact_format,
+               };
+
+               // Autocomplete smilies e.g. ":like"
+               smilies = {
+                       match: /(^|\s)(:[a-z]{2,})$/,
+                       index: 2,
+                       search: function(term, callback) { $.getJSON('smilies/json').done(function(data) { callback($.map(data, function(entry) { return entry.text.indexOf(term) === 0 ? entry : null; })); }); },
+                       template: function(item) { return item.icon + ' ' + item.text; },
+                       replace: function(item) { return "$1" + item.text + ' '; },
+               };
+
+               this.attr('autocomplete','off');
+               this.textcomplete([contacts, forums, smilies], {className:'acpopup', zIndex:10000});
+       };
+})( jQuery );
+
+/**
+ * jQuery plugin 'search_autocomplete'
+ */
+(function( $ ) {
+       $.fn.search_autocomplete = function(backend_url) {
+               // Autocomplete contacts
+               contacts = {
+                       match: /(^@)([^\n]{2,})$/,
+                       index: 2,
+                       search: function(term, callback) { contact_search(term, callback, backend_url, 'x', 'contact'); },
+                       replace: webbie_replace,
+                       template: contact_format,
+               };
+
+               // Autocomplete forum accounts
+               community = {
+                       match: /(^!)([^\n]{2,})$/,
+                       index: 2,
+                       search: function(term, callback) { contact_search(term, callback, backend_url, 'x', 'community'); },
+                       replace: webbie_replace,
+                       template: contact_format,
+               };
+               this.attr('autocomplete', 'off');
+               var a = this.textcomplete([contacts, community], {className:'acpopup', maxCount:100, zIndex: 10000, appendTo:'nav'});
+               a.on('textComplete:select', function(e, value, strategy) { submit_form(this); });
+       };
+})( jQuery );
+
+(function( $ ) {
+       $.fn.contact_autocomplete = function(backend_url, typ, autosubmit, onselect) {
+               if(typeof typ === 'undefined') typ = '';
+               if(typeof autosubmit === 'undefined') autosubmit = false;
+
+               // Autocomplete contacts
+               contacts = {
+                       match: /(^)([^\n]+)$/,
+                       index: 2,
+                       search: function(term, callback) { contact_search(term, callback, backend_url, typ); },
+                       replace: basic_replace,
+                       template: contact_format,
+               };
+
+               this.attr('autocomplete','off');
+               var a = this.textcomplete([contacts], {className:'acpopup', zIndex:10000});
+
+               if(autosubmit)
+                       a.on('textComplete:select', function(e,value,strategy) { submit_form(this); });
+
+               if(typeof onselect !== 'undefined')
+                       a.on('textComplete:select', function(e, value, strategy) { onselect(value); });
+       };
+})( jQuery );
+
+
+(function( $ ) {
+       $.fn.name_autocomplete = function(backend_url, typ, autosubmit, onselect) {
+               if(typeof typ === 'undefined') typ = '';
+               if(typeof autosubmit === 'undefined') autosubmit = false;
+
+               // Autocomplete contacts
+               names = {
+                       match: /(^)([^\n]+)$/,
+                       index: 2,
+                       search: function(term, callback) { contact_search(term, callback, backend_url, typ); },
+                       replace: trim_replace,
+                       template: contact_format,
+               };
+
+               this.attr('autocomplete','off');
+               var a = this.textcomplete([names], {className:'acpopup', zIndex:10000});
+
+               if(autosubmit)
+                       a.on('textComplete:select', function(e,value,strategy) { submit_form(this); });
+
+               if(typeof onselect !== 'undefined')
+                       a.on('textComplete:select', function(e, value, strategy) { onselect(value); });
+       };
+})( jQuery );
+
+(function( $ ) {
+       $.fn.bbco_autocomplete = function(type) {
+
+               if(type=='bbcode') {
+                       var open_close_elements = ['bold', 'italic', 'underline', 'overline', 'strike', 'quote', 'code', 'spoiler', 'map', 'img', 'url', 'audio', 'video', 'embed', 'youtube', 'vimeo', 'list', 'ul', 'ol', 'li', 'table', 'tr', 'th', 'td', 'center', 'color', 'font', 'size', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'nobb', 'noparse', 'pre', 'abstract'];
+                       var open_elements = ['*', 'hr'];
+
+                       var elements = open_close_elements.concat(open_elements);
+               }
+
+               bbco = {
+                       match: /\[(\w*\**)$/,
+                       search: function (term, callback) {
+                               callback($.map(elements, function (element) {
+                                       return element.indexOf(term) === 0 ? element : null;
+                               }));
+                       },
+                       index: 1,
+                       replace: function (element) {
+                               element = string2bb(element);
+                               if(open_elements.indexOf(element) < 0) {
+                                       if(element === 'list' || element === 'ol' || element === 'ul') {
+                                               return ['\[' + element + '\]' + '\n\[*\] ', '\n\[/' + element + '\]'];
+                                       }
+                                       else if(element === 'table') {
+                                               return ['\[' + element + '\]' + '\n\[tr\]', '\[/tr\]\n\[/' + element + '\]'];
+                                       }
+                                       else {
+                                               return ['\[' + element + '\]', '\[/' + element + '\]'];
+                                       }
+                               }
+                               else {
+                                       return '\[' + element + '\] ';
+                               }
+                       }
+               };
+
+               this.attr('autocomplete','off');
+               var a = this.textcomplete([bbco], {className:'acpopup', zIndex:10000});
+
+               a.on('textComplete:select', function(e, value, strategy) { value; });
+
+               a.keypress(function(e){
+                       if (e.keyCode == 13) {
+                               var x = listNewLineAutocomplete(this.id);
+                               if(x) {
+                                       e.stopImmediatePropagation();
+                                       e.preventDefault();
+                               }
+                       }
+               });
+       };
+})( jQuery );
+
+/**
+ * Friendica people autocomplete legacy code
+ *
+ * require jQuery, jquery.textareas
+ */
+function ACPopup(elm, backend_url){
+       this.idsel = -1;
+       this.element = elm;
+       this.searchText = '';
+       this.ready = true;
+       this.kp_timer = false;
+       this.url = backend_url;
+
+       this.conversation_id = null;
+       var conv_id = this.element.id.match(/\d+$/);
+       if (conv_id) {
+               this.conversation_id = conv_id[0];
+       }
+
+       var w = $(elm).width();
+       var h = $(elm).height();
+
+       var style = $(elm).offset();
+       style.top = style.top + h;
+       style.width = w;
+       style.position = 'absolute';
+       style.display = 'none';
+
+       this.cont = $('<div class="acpopup-mce"></div>');
+       this.cont.css(style);
+
+       $('body').append(this.cont);
+}
+
+ACPopup.prototype.close = function(){
+       $(this.cont).remove();
+       this.ready=false;
+}
+ACPopup.prototype.search = function(text){
+       var that = this;
+       this.searchText=text;
+       if (this.kp_timer) clearTimeout(this.kp_timer);
+       this.kp_timer = setTimeout( function(){that._search();}, 500);
+}
+
+ACPopup.prototype._search = function(){
+       console.log("_search");
+       var that = this;
+       var postdata = {
+               start:0,
+               count:100,
+               search:this.searchText,
+               type:'c',
+               conversation: this.conversation_id,
+       }
+
+       $.ajax({
+               type:'POST',
+               url: this.url,
+               data: postdata,
+               dataType: 'json',
+               success:function(data){
+                       that.cont.html("");
+                       if (data.tot>0){
+                               that.cont.show();
+                               $(data.items).each(function(){
+                                       var html = "<img src='{0}' height='16px' width='16px'>{1} ({2})".format(this.photo, this.name, this.nick);
+                                       var nick = this.nick.replace(' ','');
+                                       if (this.id!=='')  nick += '+' + this.id;
+                                       that.add(html, nick + ' - ' + this.link);
+                               });
+                       } else {
+                               that.cont.hide();
+                       }
+               }
+       });
+
+}
+
+ACPopup.prototype.add = function(label, value){
+       var that = this;
+       var elm = $('<div class="acpopupitem" title="' + value + '">' + label + '</div>');
+       elm.click(function(e){
+               t = $(this).attr('title').replace(new RegExp(' \- .*'), '');
+               el = $(that.element);
+               sel = el.getSelection();
+               sel.start = sel.start - that.searchText.length;
+               el.setSelection(sel.start, sel.end).replaceSelectedText(t + ' ').collapseSelection(false);
+               that.close();
+       });
+       $(this.cont).append(elm);
+}
+
+ACPopup.prototype.onkey = function(event){
+       if (event.keyCode == '13') {
+               if(this.idsel > -1) {
+                       this.cont.children()[this.idsel].click();
+                       event.preventDefault();
+               } else {
+                       this.close();
+               }
+       }
+       if (event.keyCode == '38') { //cursor up
+               var cmax = this.cont.children().size() - 1;
+               this.idsel--;
+               if (this.idsel < 0) {
+                       this.idsel = cmax;
+               }
+               event.preventDefault();
+       }
+       if (event.keyCode == '40' || event.keyCode == '9') { //cursor down
+               var cmax = this.cont.children().size() - 1;
+               this.idsel++;
+               if (this.idsel > cmax) {
+                       this.idsel = 0;
+               }
+               event.preventDefault();
+       }
+
+       if (event.keyCode == '38' || event.keyCode == '40' || event.keyCode == '9') {
+               this.cont.children().removeClass('selected');
+               $(this.cont.children()[this.idsel]).addClass('selected');
+       }
+
+       if (event.keyCode == '27') { //ESC
+               this.close();
+       }
+}
+
diff --git a/view/js/country.js b/view/js/country.js
new file mode 100644 (file)
index 0000000..c6384a9
--- /dev/null
@@ -0,0 +1,438 @@
+//<!--  Docs at: http://www.microcosmotalk.com/tech/scripts/\r
+// NOTE:\r
+// This code is placed into the public domain and may be used in any manner desired.\r
+// \r
+// Jim Carlock obtained the list of country names and state names from an HTML\r
+// document at Microsoft's website.\r
+//\r
+// Thursday, January 13, 2005, 7:00:52 PM\r
+// \r
+var gLngMaxStateLength=0;\r
+var gLngMaxCountryLength=0;\r
+var gLngNumberCountries=253;\r
+var gLngNumberStates=0;\r
+var gLngSelectedCountry=0;\r
+var gLngSelectedState=0;\r
+var gArCountryInfo;\r
+var gArStateInfo;\r
+// NOTE:\r
+// Some editors may exhibit problems viewing 2803 characters...\r
+var sCountryString = "|Afghanistan|Albania|Algeria|American Samoa|Angola|Anguilla|Antartica|Antigua and Barbuda|Argentina|Armenia|Aruba|Ashmore and Cartier Island|Australia|Austria|Azerbaijan|Bahamas|Bahrain|Bangladesh|Barbados|Belarus|Belgium|Belize|Benin|Bermuda|Bhutan|Bolivia|Bosnia and Herzegovina|Botswana|Brazil|British Virgin Islands|Brunei|Bulgaria|Burkina Faso|Burma|Burundi|Cambodia|Cameroon|Canada|Cape Verde|Cayman Islands|Central African Republic|Chad|Chile|China|Christmas Island|Clipperton Island|Cocos (Keeling) Islands|Colombia|Comoros|Congo, Democratic Republic of the|Congo, Republic of the|Cook Islands|Costa Rica|Cote d'Ivoire|Croatia|Cuba|Cyprus|Czech Republic|Denmark|Djibouti|Dominica|Dominican Republic|Ecuador|Egypt|El Salvador|Equatorial Guinea|Eritrea|Estonia|Ethiopia|Europa Island|Falkland Islands (Islas Malvinas)|Faroe Islands|Fiji|Finland|France|French Guiana|French Polynesia|French Southern and Antarctic Lands|Gabon|Gambia, The|Gaza Strip|Georgia|Germany|Ghana|Gibraltar|Glorioso Islands|Greece|Greenland|Grenada|Guadeloupe|Guam|Guatemala|Guernsey|Guinea|Guinea-Bissau|Guyana|Haiti|Heard Island and McDonald Islands|Holy See (Vatican City)|Honduras|Hong Kong|Howland Island|Hungary|Iceland|India|Indonesia|Iran|Iraq|Ireland|Ireland, Northern|Israel|Italy|Jamaica|Jan Mayen|Japan|Jarvis Island|Jersey|Johnston Atoll|Jordan|Juan de Nova Island|Kazakhstan|Kenya|Kiribati|Korea, North|Korea, South|Kuwait|Kyrgyzstan|Laos|Latvia|Lebanon|Lesotho|Liberia|Libya|Liechtenstein|Lithuania|Luxembourg|Macau|Macedonia, Former Yugoslav Republic of|Madagascar|Malawi|Malaysia|Maldives|Mali|Malta|Man, Isle of|Marshall Islands|Martinique|Mauritania|Mauritius|Mayotte|Mexico|Micronesia, Federated States of|Midway Islands|Moldova|Monaco|Mongolia|Montserrat|Morocco|Mozambique|Namibia|Nauru|Nepal|Netherlands|Netherlands Antilles|New Caledonia|New Zealand|Nicaragua|Niger|Nigeria|Niue|Norfolk Island|Northern Mariana Islands|Norway|Oman|Pakistan|Palau|Panama|Papua New Guinea|Paraguay|Peru|Philippines|Pitcaim Islands|Poland|Portugal|Puerto Rico|Qatar|Reunion|Romania|Russia|Rwanda|Saint Helena|Saint Kitts and Nevis|Saint Lucia|Saint Pierre and Miquelon|Saint Vincent and the Grenadines|Samoa|San Marino|Sao Tome and Principe|Saudi Arabia|Scotland|Senegal|Seychelles|Sierra Leone|Singapore|Slovakia|Slovenia|Solomon Islands|Somalia|South Africa|South Georgia and South Sandwich Islands|Spain|Spratly Islands|Sri Lanka|Sudan|Suriname|Svalbard|Swaziland|Sweden|Switzerland|Syria|Taiwan|Tajikistan|Tanzania|Thailand|Tobago|Toga|Tokelau|Tonga|Trinidad|Tunisia|Turkey|Turkmenistan|Tuvalu|Uganda|Ukraine|United Arab Emirates|United Kingdom|Uruguay|USA|Uzbekistan|Vanuatu|Venezuela|Vietnam|Virgin Islands|Wales|Wallis and Futuna|West Bank|Western Sahara|Yemen|Yugoslavia|Zambia|Zimbabwe|Friendicaland"\r
+var aStates = new Array();\r
+\r
+aStates[0]="";\r
+aStates[1]="|Badakhshan|Badghis|Baghlan|Balkh|Bamian|Farah|Faryab|Ghazni|Ghowr|Helmand|Herat|Jowzjan|Kabol|Kandahar|Kapisa|Konar|Kondoz|Laghman|Lowgar|Nangarhar|Nimruz|Oruzgan|Paktia|Paktika|Parvan|Samangan|Sar-e Pol|Takhar|Vardak|Zabol";\r
+aStates[2]="|Berat|Bulqize|Delvine|Devoll (Bilisht)|Diber (Peshkopi)|Durres|Elbasan|Fier|Gjirokaster|Gramsh|Has (Krume)|Kavaje|Kolonje (Erseke)|Korce|Kruje|Kucove|Kukes|Kurbin|Lezhe|Librazhd|Lushnje|Malesi e Madhe (Koplik)|Mallakaster (Ballsh)|Mat (Burrel)|Mirdite (Rreshen)|Peqin|Permet|Pogradec|Puke|Sarande|Shkoder|Skrapar (Corovode)|Tepelene|Tirane (Tirana)|Tirane (Tirana)|Tropoje (Bajram Curri)|Vlore";\r
+aStates[3]="|Adrar|Ain Defla|Ain Temouchent|Alger|Annaba|Batna|Bechar|Bejaia|Biskra|Blida|Bordj Bou Arreridj|Bouira|Boumerdes|Chlef|Constantine|Djelfa|El Bayadh|El Oued|El Tarf|Ghardaia|Guelma|Illizi|Jijel|Khenchela|Laghouat|M'Sila|Mascara|Medea|Mila|Mostaganem|Naama|Oran|Ouargla|Oum el Bouaghi|Relizane|Saida|Setif|Sidi Bel Abbes|Skikda|Souk Ahras|Tamanghasset|Tebessa|Tiaret|Tindouf|Tipaza|Tissemsilt|Tizi Ouzou|Tlemcen";\r
+aStates[4]="|Eastern|Manu'a|Rose Island|Swains Island|Western";\r
+aStates[5]="|Andorra la Vella|Bengo|Benguela|Bie|Cabinda|Canillo|Cuando Cubango|Cuanza Norte|Cuanza Sul|Cunene|Encamp|Escaldes-Engordany|Huambo|Huila|La Massana|Luanda|Lunda Norte|Lunda Sul|Malanje|Moxico|Namibe|Ordino|Sant Julia de Loria|Uige|Zaire";\r
+aStates[6]="|Anguilla";\r
+aStates[7]="|Antartica";\r
+aStates[8]="|Barbuda|Redonda|Saint George|Saint John|Saint Mary|Saint Paul|Saint Peter|Saint Philip";\r
+aStates[9]="|Antartica e Islas del Atlantico Sur|Buenos Aires|Buenos Aires Capital Federal|Catamarca|Chaco|Chubut|Cordoba|Corrientes|Entre Rios|Formosa|Jujuy|La Pampa|La Rioja|Mendoza|Misiones|Neuquen|Rio Negro|Salta|San Juan|San Luis|Santa Cruz|Santa Fe|Santiago del Estero|Tierra del Fuego|Tucuman";\r
+aStates[10]="|Aragatsotn|Ararat|Armavir|Geghark'unik'|Kotayk'|Lorri|Shirak|Syunik'|Tavush|Vayots' Dzor|Yerevan";\r
+aStates[11]="|Aruba";\r
+aStates[12]="|Ashmore and Cartier Island";\r
+aStates[13]="|Australian Capital Territory|New South Wales|Northern Territory|Queensland|South Australia|Tasmania|Victoria|Western Australia";\r
+aStates[14]="|Burgenland|Kaernten|Niederoesterreich|Oberoesterreich|Salzburg|Steiermark|Tirol|Vorarlberg|Wien";\r
+aStates[15]="|Abseron Rayonu|Agcabadi Rayonu|Agdam Rayonu|Agdas Rayonu|Agstafa Rayonu|Agsu Rayonu|Ali Bayramli Sahari|Astara Rayonu|Baki Sahari|Balakan Rayonu|Barda Rayonu|Beylaqan Rayonu|Bilasuvar Rayonu|Cabrayil Rayonu|Calilabad Rayonu|Daskasan Rayonu|Davaci Rayonu|Fuzuli Rayonu|Gadabay Rayonu|Ganca Sahari|Goranboy Rayonu|Goycay Rayonu|Haciqabul Rayonu|Imisli Rayonu|Ismayilli Rayonu|Kalbacar Rayonu|Kurdamir Rayonu|Lacin Rayonu|Lankaran Rayonu|Lankaran Sahari|Lerik Rayonu|Masalli Rayonu|Mingacevir Sahari|Naftalan Sahari|Naxcivan Muxtar Respublikasi|Neftcala Rayonu|Oguz Rayonu|Qabala Rayonu|Qax Rayonu|Qazax Rayonu|Qobustan Rayonu|Quba Rayonu|Qubadli Rayonu|Qusar Rayonu|Saatli Rayonu|Sabirabad Rayonu|Saki Rayonu|Saki Sahari|Salyan Rayonu|Samaxi Rayonu|Samkir Rayonu|Samux Rayonu|Siyazan Rayonu|Sumqayit Sahari|Susa Rayonu|Susa Sahari|Tartar Rayonu|Tovuz Rayonu|Ucar Rayonu|Xacmaz Rayonu|Xankandi Sahari|Xanlar Rayonu|Xizi Rayonu|Xocali Rayonu|Xocavand Rayonu|Yardimli Rayonu|Yevlax Rayonu|Yevlax Sahari|Zangilan Rayonu|Zaqatala Rayonu|Zardab Rayonu";\r
+aStates[16]="|Acklins and Crooked Islands|Bimini|Cat Island|Exuma|Freeport|Fresh Creek|Governor's Harbour|Green Turtle Cay|Harbour Island|High Rock|Inagua|Kemps Bay|Long Island|Marsh Harbour|Mayaguana|New Providence|Nicholls Town and Berry Islands|Ragged Island|Rock Sound|San Salvador and Rum Cay|Sandy Point";\r
+aStates[17]="|Al Hadd|Al Manamah|Al Mintaqah al Gharbiyah|Al Mintaqah al Wusta|Al Mintaqah ash Shamaliyah|Al Muharraq|Ar Rifa' wa al Mintaqah al Janubiyah|Jidd Hafs|Juzur Hawar|Madinat 'Isa|Madinat Hamad|Sitrah";\r
+aStates[18]="|Barisal|Chittagong|Dhaka|Khulna|Rajshahi|Sylhet";\r
+aStates[19]="|Bridgetown|Christ Church|Saint Andrew|Saint George|Saint James|Saint John|Saint Joseph|Saint Lucy|Saint Michael|Saint Peter|Saint Philip|Saint Thomas";\r
+aStates[20]="|Brestskaya (Brest)|Homyel'skaya (Homyel')|Horad Minsk|Hrodzyenskaya (Hrodna)|Mahilyowskaya (Mahilyow)|Minskaya|Vitsyebskaya (Vitsyebsk)";\r
+aStates[21]="|Antwerpen|Brabant Wallon|Brussels Capitol Region|Hainaut|Liege|Limburg|Luxembourg|Namur|Oost-Vlaanderen|Vlaams Brabant|West-Vlaanderen";\r
+aStates[22]="|Belize|Cayo|Corozal|Orange Walk|Stann Creek|Toledo";\r
+aStates[23]="|Alibori|Atakora|Atlantique|Borgou|Collines|Couffo|Donga|Littoral|Mono|Oueme|Plateau|Zou";\r
+aStates[24]="|Devonshire|Hamilton (City)|Hamilton|Paget|Pembroke|Saint George|Saint Georges|Sandys|Smiths|Southampton|Warwick";\r
+aStates[25]="|Bumthang|Chhukha|Chirang|Daga|Geylegphug|Ha|Lhuntshi|Mongar|Paro|Pemagatsel|Punakha|Samchi|Samdrup Jongkhar|Shemgang|Tashigang|Thimphu|Tongsa|Wangdi Phodrang";\r
+aStates[26]="|Beni|Chuquisaca|Cochabamba|La Paz|Oruro|Pando|Potosi|Santa Cruz|Tarija";\r
+aStates[27]="|Federation of Bosnia and Herzegovina|Republika Srpska";\r
+aStates[28]="|Central|Chobe|Francistown|Gaborone|Ghanzi|Kgalagadi|Kgatleng|Kweneng|Lobatse|Ngamiland|North-East|Selebi-Pikwe|South-East|Southern";\r
+aStates[29]="|Acre|Alagoas|Amapa|Amazonas|Bahia|Ceara|Distrito Federal|Espirito Santo|Goias|Maranhao|Mato Grosso|Mato Grosso do Sul|Minas Gerais|Para|Paraiba|Parana|Pernambuco|Piaui|Rio de Janeiro|Rio Grande do Norte|Rio Grande do Sul|Rondonia|Roraima|Santa Catarina|Sao Paulo|Sergipe|Tocantins";\r
+aStates[30]="|Anegada|Jost Van Dyke|Tortola|Virgin Gorda";\r
+aStates[31]="|Belait|Brunei and Muara|Temburong|Tutong";\r
+aStates[32]="|Blagoevgrad|Burgas|Dobrich|Gabrovo|Haskovo|Kardzhali|Kyustendil|Lovech|Montana|Pazardzhik|Pernik|Pleven|Plovdiv|Razgrad|Rousse|Shumen|Silistra|Sliven|Smolyan|Sofia|Stara Zagora|Turgovishte|Varna|Veliko Turnovo|Vidin|Vratsa|Yambol";\r
+aStates[33]="|Bale|Bam|Banwa|Bazega|Bougouriba|Boulgou|Boulkiemde|Comoe|Ganzourgou|Gnagna|Gourma|Houet|Ioba|Kadiogo|Kenedougou|Komandjari|Kompienga|Kossi|Koupelogo|Kouritenga|Kourweogo|Leraba|Loroum|Mouhoun|Nahouri|Namentenga|Naumbiel|Nayala|Oubritenga|Oudalan|Passore|Poni|Samentenga|Sanguie|Seno|Sissili|Soum|Sourou|Tapoa|Tuy|Yagha|Yatenga|Ziro|Zondomo|Zoundweogo";\r
+aStates[34]="|Ayeyarwady|Bago|Chin State|Kachin State|Kayah State|Kayin State|Magway|Mandalay|Mon State|Rakhine State|Sagaing|Shan State|Tanintharyi|Yangon";\r
+aStates[35]="|Bubanza|Bujumbura|Bururi|Cankuzo|Cibitoke|Gitega|Karuzi|Kayanza|Kirundo|Makamba|Muramvya|Muyinga|Mwaro|Ngozi|Rutana|Ruyigi";\r
+aStates[36]="|Banteay Mean Cheay|Batdambang|Kampong Cham|Kampong Chhnang|Kampong Spoe|Kampong Thum|Kampot|Kandal|Kaoh Kong|Keb|Kracheh|Mondol Kiri|Otdar Mean Cheay|Pailin|Phnum Penh|Pouthisat|Preah Seihanu (Sihanoukville)|Preah Vihear|Prey Veng|Rotanah Kiri|Siem Reab|Stoeng Treng|Svay Rieng|Takev";\r
+aStates[37]="|Adamaoua|Centre|Est|Extreme-Nord|Littoral|Nord|Nord-Ouest|Ouest|Sud|Sud-Ouest";\r
+aStates[38]="|Alberta|British Columbia|Manitoba|New Brunswick|Newfoundland|Northwest Territories|Nova Scotia|Nunavut|Ontario|Prince Edward Island|Quebec|Saskatchewan|Yukon Territory";\r
+aStates[39]="|Boa Vista|Brava|Maio|Mosteiros|Paul|Porto Novo|Praia|Ribeira Grande|Sal|Santa Catarina|Santa Cruz|Sao Domingos|Sao Filipe|Sao Nicolau|Sao Vicente|Tarrafal";\r
+aStates[40]="|Creek|Eastern|Midland|South Town|Spot Bay|Stake Bay|West End|Western";\r
+aStates[41]="|Bamingui-Bangoran|Bangui|Basse-Kotto|Gribingui|Haut-Mbomou|Haute-Kotto|Haute-Sangha|Kemo-Gribingui|Lobaye|Mbomou|Nana-Mambere|Ombella-Mpoko|Ouaka|Ouham|Ouham-Pende|Sangha|Vakaga";\r
+aStates[42]="|Batha|Biltine|Borkou-Ennedi-Tibesti|Chari-Baguirmi|Guera|Kanem|Lac|Logone Occidental|Logone Oriental|Mayo-Kebbi|Moyen-Chari|Ouaddai|Salamat|Tandjile";\r
+aStates[43]="|Aisen del General Carlos Ibanez del Campo|Antofagasta|Araucania|Atacama|Bio-Bio|Coquimbo|Libertador General Bernardo O'Higgins|Los Lagos|Magallanes y de la Antartica Chilena|Maule|Region Metropolitana (Santiago)|Tarapaca|Valparaiso";\r
+aStates[44]="|Anhui|Beijing|Chongqing|Fujian|Gansu|Guangdong|Guangxi|Guizhou|Hainan|Hebei|Heilongjiang|Henan|Hubei|Hunan|Jiangsu|Jiangxi|Jilin|Liaoning|Nei Mongol|Ningxia|Qinghai|Shaanxi|Shandong|Shanghai|Shanxi|Sichuan|Tianjin|Xinjiang|Xizang (Tibet)|Yunnan|Zhejiang";\r
+aStates[45]="|Christmas Island";\r
+aStates[46]="|Clipperton Island";\r
+aStates[47]="|Direction Island|Home Island|Horsburgh Island|North Keeling Island|South Island|West Island";\r
+aStates[48]="|Amazonas|Antioquia|Arauca|Atlantico|Bolivar|Boyaca|Caldas|Caqueta|Casanare|Cauca|Cesar|Choco|Cordoba|Cundinamarca|Distrito Capital de Santa Fe de Bogota|Guainia|Guaviare|Huila|La Guajira|Magdalena|Meta|Narino|Norte de Santander|Putumayo|Quindio|Risaralda|San Andres y Providencia|Santander|Sucre|Tolima|Valle del Cauca|Vaupes|Vichada";\r
+// <!-- -->\r
+aStates[49]="|Anjouan (Nzwani)|Domoni|Fomboni|Grande Comore (Njazidja)|Moheli (Mwali)|Moroni|Moutsamoudou";\r
+aStates[50]="|Bandundu|Bas-Congo|Equateur|Kasai-Occidental|Kasai-Oriental|Katanga|Kinshasa|Maniema|Nord-Kivu|Orientale|Sud-Kivu";\r
+aStates[51]="|Bouenza|Brazzaville|Cuvette|Kouilou|Lekoumou|Likouala|Niari|Plateaux|Pool|Sangha";\r
+aStates[52]="|Aitutaki|Atiu|Avarua|Mangaia|Manihiki|Manuae|Mauke|Mitiaro|Nassau Island|Palmerston|Penrhyn|Pukapuka|Rakahanga|Rarotonga|Suwarrow|Takutea";\r
+aStates[53]="|Alajuela|Cartago|Guanacaste|Heredia|Limon|Puntarenas|San Jose";\r
+aStates[54]="|Abengourou|Abidjan|Aboisso|Adiake'|Adzope|Agboville|Agnibilekrou|Ale'pe'|Bangolo|Beoumi|Biankouma|Bocanda|Bondoukou|Bongouanou|Bouafle|Bouake|Bouna|Boundiali|Dabakala|Dabon|Daloa|Danane|Daoukro|Dimbokro|Divo|Duekoue|Ferkessedougou|Gagnoa|Grand Bassam|Grand-Lahou|Guiglo|Issia|Jacqueville|Katiola|Korhogo|Lakota|Man|Mankono|Mbahiakro|Odienne|Oume|Sakassou|San-Pedro|Sassandra|Seguela|Sinfra|Soubre|Tabou|Tanda|Tiassale|Tiebissou|Tingrela|Touba|Toulepleu|Toumodi|Vavoua|Yamoussoukro|Zuenoula";\r
+aStates[55]="|Bjelovarsko-Bilogorska Zupanija|Brodsko-Posavska Zupanija|Dubrovacko-Neretvanska Zupanija|Istarska Zupanija|Karlovacka Zupanija|Koprivnicko-Krizevacka Zupanija|Krapinsko-Zagorska Zupanija|Licko-Senjska Zupanija|Medimurska Zupanija|Osjecko-Baranjska Zupanija|Pozesko-Slavonska Zupanija|Primorsko-Goranska Zupanija|Sibensko-Kninska Zupanija|Sisacko-Moslavacka Zupanija|Splitsko-Dalmatinska Zupanija|Varazdinska Zupanija|Viroviticko-Podravska Zupanija|Vukovarsko-Srijemska Zupanija|Zadarska Zupanija|Zagreb|Zagrebacka Zupanija";\r
+aStates[56]="|Camaguey|Ciego de Avila|Cienfuegos|Ciudad de La Habana|Granma|Guantanamo|Holguin|Isla de la Juventud|La Habana|Las Tunas|Matanzas|Pinar del Rio|Sancti Spiritus|Santiago de Cuba|Villa Clara";\r
+aStates[57]="|Famagusta|Kyrenia|Larnaca|Limassol|Nicosia|Paphos";\r
+aStates[58]="|Brnensky|Budejovicky|Jihlavsky|Karlovarsky|Kralovehradecky|Liberecky|Olomoucky|Ostravsky|Pardubicky|Plzensky|Praha|Stredocesky|Ustecky|Zlinsky";\r
+aStates[59]="|Aalborg|Aarhus|Bornholm|Esbjerg|Falster|Frederiksberg|Fyn|Hillerød|Horsens|Kolding|København|Als|Langeland|Lolland|Midtjylland|Nordjylland|Nordsjælland|Odense|Randers|Roskilde|Syddanmark|Morsø|Møn|Sydsjælland|Vejle|Vestsjælland|Viborg";\r
+aStates[60]="|'Ali Sabih|Dikhil|Djibouti|Obock|Tadjoura";\r
+aStates[61]="|Saint Andrew|Saint David|Saint George|Saint John|Saint Joseph|Saint Luke|Saint Mark|Saint Patrick|Saint Paul|Saint Peter";\r
+aStates[62]="|Azua|Baoruco|Barahona|Dajabon|Distrito Nacional|Duarte|El Seibo|Elias Pina|Espaillat|Hato Mayor|Independencia|La Altagracia|La Romana|La Vega|Maria Trinidad Sanchez|Monsenor Nouel|Monte Cristi|Monte Plata|Pedernales|Peravia|Puerto Plata|Salcedo|Samana|San Cristobal|San Juan|San Pedro de Macoris|Sanchez Ramirez|Santiago|Santiago Rodriguez|Valverde";\r
+// <!-- -->\r
+aStates[63]="|Azuay|Bolivar|Canar|Carchi|Chimborazo|Cotopaxi|El Oro|Esmeraldas|Galapagos|Guayas|Imbabura|Loja|Los Rios|Manabi|Morona-Santiago|Napo|Orellana|Pastaza|Pichincha|Sucumbios|Tungurahua|Zamora-Chinchipe";\r
+aStates[64]="|Ad Daqahliyah|Al Bahr al Ahmar|Al Buhayrah|Al Fayyum|Al Gharbiyah|Al Iskandariyah|Al Isma'iliyah|Al Jizah|Al Minufiyah|Al Minya|Al Qahirah|Al Qalyubiyah|Al Wadi al Jadid|As Suways|Ash Sharqiyah|Aswan|Asyut|Bani Suwayf|Bur Sa'id|Dumyat|Janub Sina'|Kafr ash Shaykh|Matruh|Qina|Shamal Sina'|Suhaj";\r
+aStates[65]="|Ahuachapan|Cabanas|Chalatenango|Cuscatlan|La Libertad|La Paz|La Union|Morazan|San Miguel|San Salvador|San Vicente|Santa Ana|Sonsonate|Usulutan";\r
+aStates[66]="|Annobon|Bioko Norte|Bioko Sur|Centro Sur|Kie-Ntem|Litoral|Wele-Nzas";\r
+aStates[67]="|Akale Guzay|Barka|Denkel|Hamasen|Sahil|Semhar|Senhit|Seraye";\r
+aStates[68]="|Harjumaa (Tallinn)|Hiiumaa (Kardla)|Ida-Virumaa (Johvi)|Jarvamaa (Paide)|Jogevamaa (Jogeva)|Laane-Virumaa (Rakvere)|Laanemaa (Haapsalu)|Parnumaa (Parnu)|Polvamaa (Polva)|Raplamaa (Rapla)|Saaremaa (Kuessaare)|Tartumaa (Tartu)|Valgamaa (Valga)|Viljandimaa (Viljandi)|Vorumaa (Voru)"\r
+aStates[69]="|Adis Abeba (Addis Ababa)|Afar|Amara|Dire Dawa|Gambela Hizboch|Hareri Hizb|Oromiya|Sumale|Tigray|YeDebub Biheroch Bihereseboch na Hizboch";\r
+aStates[70]="|Europa Island";\r
+aStates[71]="|Falkland Islands (Islas Malvinas)"\r
+aStates[72]="|Bordoy|Eysturoy|Mykines|Sandoy|Skuvoy|Streymoy|Suduroy|Tvoroyri|Vagar";\r
+aStates[73]="|Central|Eastern|Northern|Rotuma|Western";\r
+aStates[74]="|Aland|Etela-Suomen Laani|Ita-Suomen Laani|Lansi-Suomen Laani|Lappi|Oulun Laani";\r
+aStates[75]="|Alsace|Aquitaine|Auvergne|Basse-Normandie|Bourgogne|Bretagne|Centre|Champagne-Ardenne|Corse|Franche-Comté|Haute-Normandie|Île-de-France|Languedoc-Roussillon|Limousin|Lorraine|Midi-Pyrénées|Nord-Pas-de-Calais|Pays de la Loire|Picardie|Poitou-Charentes|Provence-Alpes-Côte d’Azur|Rhône-Alpes";\r
+aStates[76]="|French Guiana";\r
+aStates[77]="|Archipel des Marquises|Archipel des Tuamotu|Archipel des Tubuai|Iles du Vent|Iles Sous-le-Vent";\r
+aStates[78]="|Adelie Land|Ile Crozet|Iles Kerguelen|Iles Saint-Paul et Amsterdam";\r
+aStates[79]="|Estuaire|Haut-Ogooue|Moyen-Ogooue|Ngounie|Nyanga|Ogooue-Ivindo|Ogooue-Lolo|Ogooue-Maritime|Woleu-Ntem";\r
+aStates[80]="|Banjul|Central River|Lower River|North Bank|Upper River|Western";\r
+aStates[81]="|Gaza Strip";\r
+aStates[82]="|Abashis|Abkhazia or Ap'khazet'is Avtonomiuri Respublika (Sokhumi)|Adigenis|Ajaria or Acharis Avtonomiuri Respublika (Bat'umi)|Akhalgoris|Akhalk'alak'is|Akhalts'ikhis|Akhmetis|Ambrolauris|Aspindzis|Baghdat'is|Bolnisis|Borjomis|Ch'khorotsqus|Ch'okhatauris|Chiat'ura|Dedop'listsqaros|Dmanisis|Dushet'is|Gardabanis|Gori|Goris|Gurjaanis|Javis|K'arelis|K'ut'aisi|Kaspis|Kharagaulis|Khashuris|Khobis|Khonis|Lagodekhis|Lanch'khut'is|Lentekhis|Marneulis|Martvilis|Mestiis|Mts'khet'is|Ninotsmindis|Onis|Ozurget'is|P'ot'i|Qazbegis|Qvarlis|Rust'avi|Sach'kheris|Sagarejos|Samtrediis|Senakis|Sighnaghis|T'bilisi|T'elavis|T'erjolis|T'et'ritsqaros|T'ianet'is|Tqibuli|Ts'ageris|Tsalenjikhis|Tsalkis|Tsqaltubo|Vanis|Zestap'onis|Zugdidi|Zugdidis";\r
+aStates[83]="|Baden-Württemberg|Bayern|Berlin|Brandenburg|Bremen|Hamburg|Hessen|Mecklenburg-Vorpommern|Niedersachsen|Nordrhein-Westfalen|Rheinland-Pfalz|Saarland|Sachsen|Sachsen-Anhalt|Schleswig-Holstein|Thüringen";\r
+aStates[84]="|Ashanti|Brong-Ahafo|Central|Eastern|Greater Accra|Northern|Upper East|Upper West|Volta|Western";\r
+aStates[85]="|Gibraltar";\r
+aStates[86]="|Ile du Lys|Ile Glorieuse";\r
+aStates[87]="|Aitolia kai Akarnania|Akhaia|Argolis|Arkadhia|Arta|Attiki|Ayion Oros (Mt. Athos)|Dhodhekanisos|Drama|Evritania|Evros|Evvoia|Florina|Fokis|Fthiotis|Grevena|Ilia|Imathia|Ioannina|Irakleion|Kardhitsa|Kastoria|Kavala|Kefallinia|Kerkyra|Khalkidhiki|Khania|Khios|Kikladhes|Kilkis|Korinthia|Kozani|Lakonia|Larisa|Lasithi|Lesvos|Levkas|Magnisia|Messinia|Pella|Pieria|Preveza|Rethimni|Rodhopi|Samos|Serrai|Thesprotia|Thessaloniki|Trikala|Voiotia|Xanthi|Zakinthos";\r
+aStates[88]="|Avannaa (Nordgronland)|Kitaa (Vestgronland)|Tunu (Ostgronland)"\r
+aStates[89]="|Carriacou and Petit Martinique|Saint Andrew|Saint David|Saint George|Saint John|Saint Mark|Saint Patrick";\r
+aStates[90]="|Basse-Terre|Grande-Terre|Îles de la Petite Terre|Îles des Saintes|Marie-Galante";\r
+aStates[91]="|Guam";\r
+aStates[92]="|Alta Verapaz|Baja Verapaz|Chimaltenango|Chiquimula|El Progreso|Escuintla|Guatemala|Huehuetenango|Izabal|Jalapa|Jutiapa|Peten|Quetzaltenango|Quiche|Retalhuleu|Sacatepequez|San Marcos|Santa Rosa|Solola|Suchitepequez|Totonicapan|Zacapa";\r
+aStates[93]="|Castel|Forest|St. Andrew|St. Martin|St. Peter Port|St. Pierre du Bois|St. Sampson|St. Saviour|Torteval|Vale";\r
+aStates[94]="|Beyla|Boffa|Boke|Conakry|Coyah|Dabola|Dalaba|Dinguiraye|Dubreka|Faranah|Forecariah|Fria|Gaoual|Gueckedou|Kankan|Kerouane|Kindia|Kissidougou|Koubia|Koundara|Kouroussa|Labe|Lelouma|Lola|Macenta|Mali|Mamou|Mandiana|Nzerekore|Pita|Siguiri|Telimele|Tougue|Yomou";\r
+aStates[95]="|Bafata|Biombo|Bissau|Bolama-Bijagos|Cacheu|Gabu|Oio|Quinara|Tombali";\r
+aStates[96]="|Barima-Waini|Cuyuni-Mazaruni|Demerara-Mahaica|East Berbice-Corentyne|Essequibo Islands-West Demerara|Mahaica-Berbice|Pomeroon-Supenaam|Potaro-Siparuni|Upper Demerara-Berbice|Upper Takutu-Upper Essequibo";\r
+aStates[97]="|Artibonite|Centre|Grand'Anse|Nord|Nord-Est|Nord-Ouest|Ouest|Sud|Sud-Est";\r
+aStates[98]="|Heard Island and McDonald Islands";\r
+aStates[99]="|Holy See (Vatican City)"\r
+aStates[100]="|Atlantida|Choluteca|Colon|Comayagua|Copan|Cortes|El Paraiso|Francisco Morazan|Gracias a Dios|Intibuca|Islas de la Bahia|La Paz|Lempira|Ocotepeque|Olancho|Santa Barbara|Valle|Yoro";\r
+aStates[101]="|Hong Kong";\r
+aStates[102]="|Howland Island";\r
+aStates[103]="|Bacs-Kiskun|Baranya|Bekes|Bekescsaba|Borsod-Abauj-Zemplen|Budapest|Csongrad|Debrecen|Dunaujvaros|Eger|Fejer|Gyor|Gyor-Moson-Sopron|Hajdu-Bihar|Heves|Hodmezovasarhely|Jasz-Nagykun-Szolnok|Kaposvar|Kecskemet|Komarom-Esztergom|Miskolc|Nagykanizsa|Nograd|Nyiregyhaza|Pecs|Pest|Somogy|Sopron|Szabolcs-Szatmar-Bereg|Szeged|Szekesfehervar|Szolnok|Szombathely|Tatabanya|Tolna|Vas|Veszprem|Veszprem (City)|Zala|Zalaegerszeg";\r
+aStates[104]="|Akranes|Akureyri|Arnessysla|Austur-Bardhastrandarsysla|Austur-Hunavatnssysla|Austur-Skaftafellssysla|Borgarfjardharsysla|Dalasysla|Eyjafjardharsysla|Gullbringusysla|Hafnarfjordhur|Husavik|Isafjordhur|Keflavik|Kjosarsysla|Kopavogur|Myrasysla|Neskaupstadhur|Nordhur-Isafjardharsysla|Nordhur-Mulasys-la|Nordhur-Thingeyjarsysla|Olafsfjordhur|Rangarvallasysla|Reykjavik|Saudharkrokur|Seydhisfjordhur|Siglufjordhur|Skagafjardharsysla|Snaefellsnes-og Hnappadalssysla|Strandasysla|Sudhur-Mulasysla|Sudhur-Thingeyjarsysla|Vesttmannaeyjar|Vestur-Bardhastrandarsysla|Vestur-Hunavatnssysla|Vestur-Isafjardharsysla|Vestur-Skaftafellssysla";\r
+aStates[105]="|Andaman and Nicobar Islands|Andhra Pradesh|Arunachal Pradesh|Assam|Bihar|Chandigarh|Chhattisgarh|Dadra and Nagar Haveli|Daman and Diu|Delhi|Goa|Gujarat|Haryana|Himachal Pradesh|Jammu and Kashmir|Jharkhand|Karnataka|Kerala|Lakshadweep|Madhya Pradesh|Maharashtra|Manipur|Meghalaya|Mizoram|Nagaland|Orissa|Pondicherry|Punjab|Rajasthan|Sikkim|Tamil Nadu|Tripura|Uttar Pradesh|Uttaranchal|West Bengal";\r
+aStates[106]="|Aceh|Bali|Banten|Bengkulu|East Timor|Gorontalo|Irian Jaya|Jakarta Raya|Jambi|Jawa Barat|Jawa Tengah|Jawa Timur|Kalimantan Barat|Kalimantan Selatan|Kalimantan Tengah|Kalimantan Timur|Kepulauan Bangka Belitung|Lampung|Maluku|Maluku Utara|Nusa Tenggara Barat|Nusa Tenggara Timur|Riau|Sulawesi Selatan|Sulawesi Tengah|Sulawesi Tenggara|Sulawesi Utara|Sumatera Barat|Sumatera Selatan|Sumatera Utara|Yogyakarta";\r
+aStates[107]="|Ardabil|Azarbayjan-e Gharbi|Azarbayjan-e Sharqi|Bushehr|Chahar Mahall va Bakhtiari|Esfahan|Fars|Gilan|Golestan|Hamadan|Hormozgan|Ilam|Kerman|Kermanshah|Khorasan|Khuzestan|Kohgiluyeh va Buyer Ahmad|Kordestan|Lorestan|Markazi|Mazandaran|Qazvin|Qom|Semnan|Sistan va Baluchestan|Tehran|Yazd|Zanjan";\r
+aStates[108]="|Al Anbar|Al Basrah|Al Muthanna|Al Qadisiyah|An Najaf|Arbil|As Sulaymaniyah|At Ta'mim|Babil|Baghdad|Dahuk|Dhi Qar|Diyala|Karbala'|Maysan|Ninawa|Salah ad Din|Wasit";\r
+aStates[109]="|Antrim|Armargh|Carlow|Cavan|Clare|Cork|Derry|Donegal|Down|Dún Laoghaire–Rathdown|Fermanagh|Dublin|Fingal|Galway|Kerry|Kildare|Kilkenny|Laois|Leitrim|Limerick|Longford|Louth|Mayo|Meath|Monaghan|Offaly|Roscommon|Sligo|Tipperary|Tyrone|Waterford|Westmeath|Wexford|Wicklow";\r
+aStates[110]="|Antrim|Armagh|Belfast|Down|Fermanagh|Londonderry|Tyrone";\r
+aStates[111]="|Central|Haifa|Jerusalem|Northern|Southern|Tel Aviv";\r
+aStates[112]="|Abruzzi|Basilicata|Calabria|Campania|Emilia-Romagna|Friuli-Venezia Giulia|Lazio|Liguria|Lombardia|Marche|Molise|Piemonte|Puglia|Sardegna|Sicilia|Toscana|Trentino-Alto Adige|Umbria|Valle d'Aosta|Veneto";\r
+aStates[113]="|Clarendon|Hanover|Kingston|Manchester|Portland|Saint Andrew|Saint Ann|Saint Catherine|Saint Elizabeth|Saint James|Saint Mary|Saint Thomas|Trelawny|Westmoreland";\r
+aStates[114]="|Jan Mayen";\r
+aStates[115]="|Aichi|Akita|Aomori|Chiba|Ehime|Fukui|Fukuoka|Fukushima|Gifu|Gumma|Hiroshima|Hokkaido|Hyogo|Ibaraki|Ishikawa|Iwate|Kagawa|Kagoshima|Kanagawa|Kochi|Kumamoto|Kyoto|Mie|Miyagi|Miyazaki|Nagano|Nagasaki|Nara|Niigata|Oita|Okayama|Okinawa|Osaka|Saga|Saitama|Shiga|Shimane|Shizuoka|Tochigi|Tokushima|Tokyo|Tottori|Toyama|Wakayama|Yamagata|Yamaguchi|Yamanashi";\r
+aStates[116]="|Jarvis Island";\r
+aStates[117]="|Jersey";\r
+aStates[118]="|Johnston Atoll";\r
+aStates[119]="|'Amman|Ajlun|Al 'Aqabah|Al Balqa'|Al Karak|Al Mafraq|At Tafilah|Az Zarqa'|Irbid|Jarash|Ma'an|Madaba";\r
+aStates[120]="|Juan de Nova Island";\r
+aStates[121]="|Almaty|Aqmola|Aqtobe|Astana|Atyrau|Batys Qazaqstan|Bayqongyr|Mangghystau|Ongtustik Qazaqstan|Pavlodar|Qaraghandy|Qostanay|Qyzylorda|Shyghys Qazaqstan|Soltustik Qazaqstan|Zhambyl";\r
+aStates[122]="|Central|Coast|Eastern|Nairobi Area|North Eastern|Nyanza|Rift Valley|Western";\r
+aStates[123]="|Abaiang|Abemama|Aranuka|Arorae|Banaba (District)|Banaba|Beru|Butaritari|Central Gilberts (District)|Gilbert Islands (Unit)|Kanton|Kiritimati|Kuria|Line Islands (District)|Line Islands (Unit)|Maiana|Makin|Marakei|Nikunau|Nonouti|Northern Gilberts (District)|Onotoa|Phoenix Islands (Unit)|Southern Gilberts (District)|Tabiteuea|Tabuaeran|Tamana|Tarawa (District)|Tarawa|Teraina";\r
+aStates[124]="|Chagang-do (Chagang Province)|Hamgyong-bukto (North Hamgyong Province)|Hamgyong-namdo (South Hamgyong Province)|Hwanghae-bukto (North Hwanghae Province)|Hwanghae-namdo (South Hwanghae Province)|Kaesong-si (Kaesong City)|Kangwon-do (Kangwon Province)|Namp'o-si (Namp'o City)|P'yongan-bukto (North P'yongan Province)|P'yongan-namdo (South P'yongan Province)|P'yongyang-si (P'yongyang City)|Yanggang-do (Yanggang Province)"\r
+aStates[125]="|Ch'ungch'ong-bukto|Ch'ungch'ong-namdo|Cheju-do|Cholla-bukto|Cholla-namdo|Inch'on-gwangyoksi|Kangwon-do|Kwangju-gwangyoksi|Kyonggi-do|Kyongsang-bukto|Kyongsang-namdo|Pusan-gwangyoksi|Soul-t'ukpyolsi|Taegu-gwangyoksi|Taejon-gwangyoksi|Ulsan-gwangyoksi";\r
+aStates[126]="|Al 'Asimah|Al Ahmadi|Al Farwaniyah|Al Jahra'|Hawalli";\r
+aStates[127]="|Batken Oblasty|Bishkek Shaary|Chuy Oblasty (Bishkek)|Jalal-Abad Oblasty|Naryn Oblasty|Osh Oblasty|Talas Oblasty|Ysyk-Kol Oblasty (Karakol)"\r
+aStates[128]="|Attapu|Bokeo|Bolikhamxai|Champasak|Houaphan|Khammouan|Louangnamtha|Louangphabang|Oudomxai|Phongsali|Salavan|Savannakhet|Viangchan City|Viangchan|Xaignabouli|Xaisomboun|Xekong|Xiangkhoang";\r
+aStates[129]="|Aizkraukles Rajons|Aluksnes Rajons|Balvu Rajons|Bauskas Rajons|Cesu Rajons|Daugavpils|Daugavpils Rajons|Dobeles Rajons|Gulbenes Rajons|Jekabpils Rajons|Jelgava|Jelgavas Rajons|Jurmala|Kraslavas Rajons|Kuldigas Rajons|Leipaja|Liepajas Rajons|Limbazu Rajons|Ludzas Rajons|Madonas Rajons|Ogres Rajons|Preilu Rajons|Rezekne|Rezeknes Rajons|Riga|Rigas Rajons|Saldus Rajons|Talsu Rajons|Tukuma Rajons|Valkas Rajons|Valmieras Rajons|Ventspils|Ventspils Rajons";\r
+aStates[130]="|Beyrouth|Ech Chimal|Ej Jnoub|El Bekaa|Jabal Loubnane";\r
+aStates[131]="|Berea|Butha-Buthe|Leribe|Mafeteng|Maseru|Mohales Hoek|Mokhotlong|Qacha's Nek|Quthing|Thaba-Tseka";\r
+aStates[132]="|Bomi|Bong|Grand Bassa|Grand Cape Mount|Grand Gedeh|Grand Kru|Lofa|Margibi|Maryland|Montserrado|Nimba|River Cess|Sinoe";\r
+aStates[133]="|Ajdabiya|Al 'Aziziyah|Al Fatih|Al Jabal al Akhdar|Al Jufrah|Al Khums|Al Kufrah|An Nuqat al Khams|Ash Shati'|Awbari|Az Zawiyah|Banghazi|Darnah|Ghadamis|Gharyan|Misratah|Murzuq|Sabha|Sawfajjin|Surt|Tarabulus|Tarhunah|Tubruq|Yafran|Zlitan";\r
+aStates[134]="|Balzers|Eschen|Gamprin|Mauren|Planken|Ruggell|Schaan|Schellenberg|Triesen|Triesenberg|Vaduz";\r
+aStates[135]="|Akmenes Rajonas|Alytaus Rajonas|Alytus|Anyksciu Rajonas|Birstonas|Birzu Rajonas|Druskininkai|Ignalinos Rajonas|Jonavos Rajonas|Joniskio Rajonas|Jurbarko Rajonas|Kaisiadoriu Rajonas|Kaunas|Kauno Rajonas|Kedainiu Rajonas|Kelmes Rajonas|Klaipeda|Klaipedos Rajonas|Kretingos Rajonas|Kupiskio Rajonas|Lazdiju Rajonas|Marijampole|Marijampoles Rajonas|Mazeikiu Rajonas|Moletu Rajonas|Neringa Pakruojo Rajonas|Palanga|Panevezio Rajonas|Panevezys|Pasvalio Rajonas|Plunges Rajonas|Prienu Rajonas|Radviliskio Rajonas|Raseiniu Rajonas|Rokiskio Rajonas|Sakiu Rajonas|Salcininku Rajonas|Siauliai|Siauliu Rajonas|Silales Rajonas|Silutes Rajonas|Sirvintu Rajonas|Skuodo Rajonas|Svencioniu Rajonas|Taurages Rajonas|Telsiu Rajonas|Traku Rajonas|Ukmerges Rajonas|Utenos Rajonas|Varenos Rajonas|Vilkaviskio Rajonas|Vilniaus Rajonas|Vilnius|Zarasu Rajonas";\r
+aStates[136]="|Diekirch|Grevenmacher|Luxembourg";\r
+aStates[137]="|Macau";\r
+aStates[138]="|Aracinovo|Bac|Belcista|Berovo|Bistrica|Bitola|Blatec|Bogdanci|Bogomila|Bogovinje|Bosilovo|Brvenica|Cair (Skopje)|Capari|Caska|Cegrane|Centar (Skopje)|Centar Zupa|Cesinovo|Cucer-Sandevo|Debar|Delcevo|Delogozdi|Demir Hisar|Demir Kapija|Dobrusevo|Dolna Banjica|Dolneni|Dorce Petrov (Skopje)|Drugovo|Dzepciste|Gazi Baba (Skopje)|Gevgelija|Gostivar|Gradsko|Ilinden|Izvor|Jegunovce|Kamenjane|Karbinci|Karpos (Skopje)|Kavadarci|Kicevo|Kisela Voda (Skopje)|Klecevce|Kocani|Konce|Kondovo|Konopiste|Kosel|Kratovo|Kriva Palanka|Krivogastani|Krusevo|Kuklis|Kukurecani|Kumanovo|Labunista|Lipkovo|Lozovo|Lukovo|Makedonska Kamenica|Makedonski Brod|Mavrovi Anovi|Meseista|Miravci|Mogila|Murtino|Negotino|Negotino-Poloska|Novaci|Novo Selo|Oblesevo|Ohrid|Orasac|Orizari|Oslomej|Pehcevo|Petrovec|Plasnia|Podares|Prilep|Probistip|Radovis|Rankovce|Resen|Rosoman|Rostusa|Samokov|Saraj|Sipkovica|Sopiste|Sopotnika|Srbinovo|Star Dojran|Staravina|Staro Nagoricane|Stip|Struga|Strumica|Studenicani|Suto Orizari (Skopje)|Sveti Nikole|Tearce|Tetovo|Topolcani|Valandovo|Vasilevo|Veles|Velesta|Vevcani|Vinica|Vitoliste|Vranestica|Vrapciste|Vratnica|Vrutok|Zajas|Zelenikovo|Zileno|Zitose|Zletovo|Zrnovci";\r
+aStates[139]="|Antananarivo|Antsiranana|Fianarantsoa|Mahajanga|Toamasina|Toliara";\r
+aStates[140]="|Balaka|Blantyre|Chikwawa|Chiradzulu|Chitipa|Dedza|Dowa|Karonga|Kasungu|Likoma|Lilongwe|Machinga (Kasupe)|Mangochi|Mchinji|Mulanje|Mwanza|Mzimba|Nkhata Bay|Nkhotakota|Nsanje|Ntcheu|Ntchisi|Phalombe|Rumphi|Salima|Thyolo|Zomba";\r
+aStates[141]="|Johor|Kedah|Kelantan|Labuan|Melaka|Negeri Sembilan|Pahang|Perak|Perlis|Pulau Pinang|Sabah|Sarawak|Selangor|Terengganu|Wilayah Persekutuan";\r
+aStates[142]="|Alifu|Baa|Dhaalu|Faafu|Gaafu Alifu|Gaafu Dhaalu|Gnaviyani|Haa Alifu|Haa Dhaalu|Kaafu|Laamu|Lhaviyani|Maale|Meemu|Noonu|Raa|Seenu|Shaviyani|Thaa|Vaavu";\r
+aStates[143]="|Gao|Kayes|Kidal|Koulikoro|Mopti|Segou|Sikasso|Tombouctou";\r
+aStates[144]="|Valletta";\r
+aStates[145]="|Man, Isle of";\r
+aStates[146]="|Ailinginae|Ailinglaplap|Ailuk|Arno|Aur|Bikar|Bikini|Bokak|Ebon|Enewetak|Erikub|Jabat|Jaluit|Jemo|Kili|Kwajalein|Lae|Lib|Likiep|Majuro|Maloelap|Mejit|Mili|Namorik|Namu|Rongelap|Rongrik|Toke|Ujae|Ujelang|Utirik|Wotho|Wotje";\r
+aStates[147]="|Martinique";\r
+aStates[148]="|Adrar|Assaba|Brakna|Dakhlet Nouadhibou|Gorgol|Guidimaka|Hodh Ech Chargui|Hodh El Gharbi|Inchiri|Nouakchott|Tagant|Tiris Zemmour|Trarza";\r
+aStates[149]="|Agalega Islands|Black River|Cargados Carajos Shoals|Flacq|Grand Port|Moka|Pamplemousses|Plaines Wilhems|Port Louis|Riviere du Rempart|Rodrigues|Savanne";\r
+aStates[150]="|Mayotte";\r
+aStates[151]="|Aguascalientes|Baja California|Baja California Sur|Campeche|Chiapas|Chihuahua|Coahuila de Zaragoza|Colima|Distrito Federal|Durango|Guanajuato|Guerrero|Hidalgo|Jalisco|Mexico|Michoacan de Ocampo|Morelos|Nayarit|Nuevo Leon|Oaxaca|Puebla|Queretaro de Arteaga|Quintana Roo|San Luis Potosi|Sinaloa|Sonora|Tabasco|Tamaulipas|Tlaxcala|Veracruz-Llave|Yucatan|Zacatecas";\r
+aStates[152]="|Chuuk (Truk)|Kosrae|Pohnpei|Yap";\r
+aStates[153]="|Midway Islands";\r
+aStates[154]="|Balti|Cahul|Chisinau (City)|Chisinau|Dubasari|Edinet|Gagauzia|Lapusna|Orhei|Soroca|Tighina|Ungheni";\r
+aStates[155]="|Fontvieille|La Condamine|Monaco-Ville|Monte-Carlo";\r
+aStates[156]="|Arhangay|Bayan-Olgiy|Bayanhongor|Bulgan|Darhan|Dornod|Dornogovi|Dundgovi|Dzavhan|Erdenet|Govi-Altay|Hentiy|Hovd|Hovsgol|Omnogovi|Ovorhangay|Selenge|Suhbaatar|Tov|Ulaanbaatar|Uvs";\r
+aStates[157]="|Saint Anthony|Saint Georges|Saint Peter's";\r
+aStates[158]="|Agadir|Al Hoceima|Azilal|Ben Slimane|Beni Mellal|Boulemane|Casablanca|Chaouen|El Jadida|El Kelaa des Srarhna|Er Rachidia|Essaouira|Fes|Figuig|Guelmim|Ifrane|Kenitra|Khemisset|Khenifra|Khouribga|Laayoune|Larache|Marrakech|Meknes|Nador|Ouarzazate|Oujda|Rabat-Sale|Safi|Settat|Sidi Kacem|Tan-Tan|Tanger|Taounate|Taroudannt|Tata|Taza|Tetouan|Tiznit";\r
+aStates[159]="|Cabo Delgado|Gaza|Inhambane|Manica|Maputo|Nampula|Niassa|Sofala|Tete|Zambezia";\r
+aStates[160]="|Caprivi|Erongo|Hardap|Karas|Khomas|Kunene|Ohangwena|Okavango|Omaheke|Omusati|Oshana|Oshikoto|Otjozondjupa";\r
+aStates[161]="|Aiwo|Anabar|Anetan|Anibare|Baiti|Boe|Buada|Denigomodu|Ewa|Ijuw|Meneng|Nibok|Uaboe|Yaren";\r
+aStates[162]="|Bagmati|Bheri|Dhawalagiri|Gandaki|Janakpur|Karnali|Kosi|Lumbini|Mahakali|Mechi|Narayani|Rapti|Sagarmatha|Seti";\r
+aStates[163]="|Drenthe|Flevoland|Friesland|Gelderland|Groningen|Limburg|Noord-Brabant|Noord-Holland|Overijssel|Utrecht|Zeeland|Zuid-Holland";\r
+aStates[164]="|Netherlands Antilles";\r
+aStates[165]="|Iles Loyaute|Nord|Sud";\r
+aStates[166]="|Akaroa|Amuri|Ashburton|Bay of Islands|Bruce|Buller|Chatham Islands|Cheviot|Clifton|Clutha|Cook|Dannevirke|Egmont|Eketahuna|Ellesmere|Eltham|Eyre|Featherston|Franklin|Golden Bay|Great Barrier Island|Grey|Hauraki Plains|Hawera|Hawke's Bay|Heathcote|Hikurangi|Hobson|Hokianga|Horowhenua|Hurunui|Hutt|Inangahua|Inglewood|Kaikoura|Kairanga|Kiwitea|Lake|Mackenzie|Malvern|Manaia|Manawatu|Mangonui|Maniototo|Marlborough|Masterton|Matamata|Mount Herbert|Ohinemuri|Opotiki|Oroua|Otamatea|Otorohanga|Oxford|Pahiatua|Paparua|Patea|Piako|Pohangina|Raglan|Rangiora|Rangitikei|Rodney|Rotorua|Runanga|Saint Kilda|Silverpeaks|Southland|Stewart Island|Stratford|Strathallan|Taranaki|Taumarunui|Taupo|Tauranga|Thames-Coromandel|Tuapeka|Vincent|Waiapu|Waiheke|Waihemo|Waikato|Waikohu|Waimairi|Waimarino|Waimate|Waimate West|Waimea|Waipa|Waipawa|Waipukurau|Wairarapa South|Wairewa|Wairoa|Waitaki|Waitomo|Waitotara|Wallace|Wanganui|Waverley|Westland|Whakatane|Whangarei|Whangaroa|Woodville";\r
+aStates[167]="|Atlantico Norte|Atlantico Sur|Boaco|Carazo|Chinandega|Chontales|Esteli|Granada|Jinotega|Leon|Madriz|Managua|Masaya|Matagalpa|Nueva Segovia|Rio San Juan|Rivas";\r
+aStates[168]="|Agadez|Diffa|Dosso|Maradi|Niamey|Tahoua|Tillaberi|Zinder";\r
+aStates[169]="|Abia|Abuja Federal Capital Territory|Adamawa|Akwa Ibom|Anambra|Bauchi|Bayelsa|Benue|Borno|Cross River|Delta|Ebonyi|Edo|Ekiti|Enugu|Gombe|Imo|Jigawa|Kaduna|Kano|Katsina|Kebbi|Kogi|Kwara|Lagos|Nassarawa|Niger|Ogun|Ondo|Osun|Oyo|Plateau|Rivers|Sokoto|Taraba|Yobe|Zamfara";\r
+aStates[170]="|Niue";\r
+aStates[171]="|Norfolk Island";\r
+aStates[172]="|Northern Islands|Rota|Saipan|Tinian";\r
+aStates[173]="|Akershus|Aust-Agder|Buskerud|Finnmark|Hedmark|Hordaland|More og Romsdal|Nord-Trondelag|Nordland|Oppland|Oslo|Ostfold|Rogaland|Sogn og Fjordane|Sor-Trondelag|Telemark|Troms|Vest-Agder|Vestfold";\r
+aStates[174]="|Ad Dakhiliyah|Al Batinah|Al Wusta|Ash Sharqiyah|Az Zahirah|Masqat|Musandam|Zufar";\r
+aStates[175]="|Balochistan|Federally Administered Tribal Areas|Islamabad Capital Territory|North-West Frontier Province|Punjab|Sindh";\r
+aStates[176]="|Aimeliik|Airai|Angaur|Hatobohei|Kayangel|Koror|Melekeok|Ngaraard|Ngarchelong|Ngardmau|Ngatpang|Ngchesar|Ngeremlengui|Ngiwal|Palau Island|Peleliu|Sonsoral|Tobi";\r
+aStates[177]="|Bocas del Toro|Chiriqui|Cocle|Colon|Darien|Herrera|Los Santos|Panama|San Blas|Veraguas";\r
+aStates[178]="|Bougainville|Central|Chimbu|East New Britain|East Sepik|Eastern Highlands|Enga|Gulf|Madang|Manus|Milne Bay|Morobe|National Capital|New Ireland|Northern|Sandaun|Southern Highlands|West New Britain|Western|Western Highlands";\r
+aStates[179]="|Alto Paraguay|Alto Parana|Amambay|Asuncion (city)|Boqueron|Caaguazu|Caazapa|Canindeyu|Central|Concepcion|Cordillera|Guaira|Itapua|Misiones|Neembucu|Paraguari|Presidente Hayes|San Pedro";\r
+aStates[180]="|Amazonas|Ancash|Apurimac|Arequipa|Ayacucho|Cajamarca|Callao|Cusco|Huancavelica|Huanuco|Ica|Junin|La Libertad|Lambayeque|Lima|Loreto|Madre de Dios|Moquegua|Pasco|Piura|Puno|San Martin|Tacna|Tumbes|Ucayali";\r
+aStates[181]="|Abra|Agusan del Norte|Agusan del Sur|Aklan|Albay|Angeles|Antique|Aurora|Bacolod|Bago|Baguio|Bais|Basilan|Basilan City|Bataan|Batanes|Batangas|Batangas City|Benguet|Bohol|Bukidnon|Bulacan|Butuan|Cabanatuan|Cadiz|Cagayan|Cagayan de Oro|Calbayog|Caloocan|Camarines Norte|Camarines Sur|Camiguin|Canlaon|Capiz|Catanduanes|Cavite|Cavite City|Cebu|Cebu City|Cotabato|Dagupan|Danao|Dapitan|Davao City Davao|Davao del Sur|Davao Oriental|Dipolog|Dumaguete|Eastern Samar|General Santos|Gingoog|Ifugao|Iligan|Ilocos Norte|Ilocos Sur|Iloilo|Iloilo City|Iriga|Isabela|Kalinga-Apayao|La Carlota|La Union|Laguna|Lanao del Norte|Lanao del Sur|Laoag|Lapu-Lapu|Legaspi|Leyte|Lipa|Lucena|Maguindanao|Mandaue|Manila|Marawi|Marinduque|Masbate|Mindoro Occidental|Mindoro Oriental|Misamis Occidental|Misamis Oriental|Mountain|Naga|Negros Occidental|Negros Oriental|North Cotabato|Northern Samar|Nueva Ecija|Nueva Vizcaya|Olongapo|Ormoc|Oroquieta|Ozamis|Pagadian|Palawan|Palayan|Pampanga|Pangasinan|Pasay|Puerto Princesa|Quezon|Quezon City|Quirino|Rizal|Romblon|Roxas|Samar|San Carlos (in Negros Occidental)|San Carlos (in Pangasinan)|San Jose|San Pablo|Silay|Siquijor|Sorsogon|South Cotabato|Southern Leyte|Sultan Kudarat|Sulu|Surigao|Surigao del Norte|Surigao del Sur|Tacloban|Tagaytay|Tagbilaran|Tangub|Tarlac|Tawitawi|Toledo|Trece Martires|Zambales|Zamboanga|Zamboanga del Norte|Zamboanga del Sur";\r
+aStates[182]="|Pitcaim Islands";\r
+aStates[183]="|Dolnoslaskie|Kujawsko-Pomorskie|Lodzkie|Lubelskie|Lubuskie|Malopolskie|Mazowieckie|Opolskie|Podkarpackie|Podlaskie|Pomorskie|Slaskie|Swietokrzyskie|Warminsko-Mazurskie|Wielkopolskie|Zachodniopomorskie";\r
+aStates[184]="|Acores (Azores)|Aveiro|Beja|Braga|Braganca|Castelo Branco|Coimbra|Evora|Faro|Guarda|Leiria|Lisboa|Madeira|Portalegre|Porto|Santarem|Setubal|Viana do Castelo|Vila Real|Viseu";\r
+aStates[185]="|Adjuntas|Aguada|Aguadilla|Aguas Buenas|Aibonito|Anasco|Arecibo|Arroyo|Barceloneta|Barranquitas|Bayamon|Cabo Rojo|Caguas|Camuy|Canovanas|Carolina|Catano|Cayey|Ceiba|Ciales|Cidra|Coamo|Comerio|Corozal|Culebra|Dorado|Fajardo|Florida|Guanica|Guayama|Guayanilla|Guaynabo|Gurabo|Hatillo|Hormigueros|Humacao|Isabela|Jayuya|Juana Diaz|Juncos|Lajas|Lares|Las Marias|Las Piedras|Loiza|Luquillo|Manati|Maricao|Maunabo|Mayaguez|Moca|Morovis|Naguabo|Naranjito|Orocovis|Patillas|Penuelas|Ponce|Quebradillas|Rincon|Rio Grande|Sabana Grande|Salinas|San German|San Juan|San Lorenzo|San Sebastian|Santa Isabel|Toa Alta|Toa Baja|Trujillo Alto|Utuado|Vega Alta|Vega Baja|Vieques|Villalba|Yabucoa|Yauco";\r
+aStates[186]="|Ad Dawhah|Al Ghuwayriyah|Al Jumayliyah|Al Khawr|Al Wakrah|Ar Rayyan|Jarayan al Batinah|Madinat ash Shamal|Umm Salal";\r
+aStates[187]="|Réunion";\r
+aStates[188]="|Alba|Arad|Arges|Bacau|Bihor|Bistrita-Nasaud|Botosani|Braila|Brasov|Bucuresti|Buzau|Calarasi|Caras-Severin|Cluj|Constanta|Covasna|Dimbovita|Dolj|Galati|Giurgiu|Gorj|Harghita|Hunedoara|Ialomita|Iasi|Maramures|Mehedinti|Mures|Neamt|Olt|Prahova|Salaj|Satu Mare|Sibiu|Suceava|Teleorman|Timis|Tulcea|Vaslui|Vilcea|Vrancea";\r
+aStates[189]="|Adygeya (Maykop)|Aginskiy Buryatskiy (Aginskoye)|Altay (Gorno-Altaysk)|Altayskiy (Barnaul)|Amurskaya (Blagoveshchensk)|Arkhangel'skaya|Astrakhanskaya|Bashkortostan (Ufa)|Belgorodskaya|Bryanskaya|Buryatiya (Ulan-Ude)|Chechnya (Groznyy)|Chelyabinskaya|Chitinskaya|Chukotskiy (Anadyr')|Chuvashiya (Cheboksary)|Dagestan (Makhachkala)|Evenkiyskiy (Tura)|Ingushetiya (Nazran')|Irkutskaya|Ivanovskaya|Kabardino-Balkariya (Nal'chik)|Kaliningradskaya|Kalmykiya (Elista)|Kaluzhskaya|Kamchatskaya (Petropavlovsk-Kamchatskiy)|Karachayevo-Cherkesiya (Cherkessk)|Kareliya (Petrozavodsk)|Kemerovskaya|Khabarovskiy|Khakasiya (Abakan)|Khanty-Mansiyskiy (Khanty-Mansiysk)|Kirovskaya|Komi (Syktyvkar)|Komi-Permyatskiy (Kudymkar)|Koryakskiy (Palana)|Kostromskaya|Krasnodarskiy|Krasnoyarskiy|Kurganskaya|Kurskaya|Leningradskaya|Lipetskaya|Magadanskaya|Mariy-El (Yoshkar-Ola)|Mordoviya (Saransk)|Moskovskaya|Moskva (Moscow)|Murmanskaya|Nenetskiy (Nar'yan-Mar)|Nizhegorodskaya|Novgorodskaya|Novosibirskaya|Omskaya|Orenburgskaya|Orlovskaya (Orel)|Penzenskaya|Permskaya|Primorskiy (Vladivostok)|Pskovskaya|Rostovskaya|Ryazanskaya|Sakha (Yakutsk)|Sakhalinskaya (Yuzhno-Sakhalinsk)|Samarskaya|Sankt-Peterburg (Saint Petersburg)|Saratovskaya|Severnaya Osetiya-Alaniya [North Ossetia] (Vladikavkaz)|Smolenskaya|Stavropol'skiy|Sverdlovskaya (Yekaterinburg)|Tambovskaya|Tatarstan (Kazan')|Taymyrskiy (Dudinka)|Tomskaya|Tul'skaya|Tverskaya|Tyumenskaya|Tyva (Kyzyl)|Udmurtiya (Izhevsk)|Ul'yanovskaya|Ust'-Ordynskiy Buryatskiy (Ust'-Ordynskiy)|Vladimirskaya|Volgogradskaya|Vologodskaya|Voronezhskaya|Yamalo-Nenetskiy (Salekhard)|Yaroslavskaya|Yevreyskaya";\r
+aStates[190]="|Butare|Byumba|Cyangugu|Gikongoro|Gisenyi|Gitarama|Kibungo|Kibuye|Kigali Rurale|Kigali-ville|Ruhengeri|Umutara";\r
+aStates[191]="|Ascension|Saint Helena|Tristan da Cunha";\r
+aStates[192]="|Christ Church Nichola Town|Saint Anne Sandy Point|Saint George Basseterre|Saint George Gingerland|Saint James Windward|Saint John Capisterre|Saint John Figtree|Saint Mary Cayon|Saint Paul Capisterre|Saint Paul Charlestown|Saint Peter Basseterre|Saint Thomas Lowland|Saint Thomas Middle Island|Trinity Palmetto Point";\r
+aStates[193]="|Anse-la-Raye|Castries|Choiseul|Dauphin|Dennery|Gros Islet|Laborie|Micoud|Praslin|Soufriere|Vieux Fort";\r
+aStates[194]="|Miquelon|Saint Pierre";\r
+aStates[195]="|Charlotte|Grenadines|Saint Andrew|Saint David|Saint George|Saint Patrick";\r
+aStates[196]="|A'ana|Aiga-i-le-Tai|Atua|Fa'asaleleaga|Gaga'emauga|Gagaifomauga|Palauli|Satupa'itea|Tuamasaga|Va'a-o-Fonoti|Vaisigano";\r
+aStates[197]="|Acquaviva|Borgo Maggiore|Chiesanuova|Domagnano|Faetano|Fiorentino|Monte Giardino|San Marino|Serravalle";\r
+aStates[198]="|Principe|Sao Tome";\r
+aStates[199]="|'Asir|Al Bahah|Al Hudud ash Shamaliyah|Al Jawf|Al Madinah|Al Qasim|Ar Riyad|Ash Sharqiyah (Eastern Province)|Ha'il|Jizan|Makkah|Najran|Tabuk";\r
+aStates[200]="|Aberdeenshire|Angus|Argyll|Ayrshire|Banffshire|Berwickshire|Bute|Caithness|Clackmannanshire|Cromartyshire|Dumfriesshire|Dunbartonshire|Dundee City|East Lothian|Edinburgh|Fife|Glasgow City|Inverness-shire|Kincardineshire|Kinross-shire|Kirkcudbrightshire|Lanarkshire|Midlothian|Moray|Nairnshire|Orkney Islands|Peeblesshire|Perthshire|Renfrewshire|Ross and Cromarty|Ross-shire|Roxburghshire|Selkirkshire|Shetland Islands|Stirlingshire|Sutherland|West Lothian|Wigtownshire";\r
+aStates[201]="|Dakar|Diourbel|Fatick|Kaolack|Kolda|Louga|Saint-Louis|Tambacounda|Thies|Ziguinchor";\r
+aStates[202]="|Anse aux Pins|Anse Boileau|Anse Etoile|Anse Louis|Anse Royale|Baie Lazare|Baie Sainte Anne|Beau Vallon|Bel Air|Bel Ombre|Cascade|Glacis|Grand' Anse (on Mahe)|Grand' Anse (on Praslin)|La Digue|La Riviere Anglaise|Mont Buxton|Mont Fleuri|Plaisance|Pointe La Rue|Port Glaud|Saint Louis|Takamaka";\r
+aStates[203]="|Eastern|Northern|Southern|Western";\r
+aStates[204]="|Singapore";\r
+aStates[205]="|Banskobystricky|Bratislavsky|Kosicky|Nitriansky|Presovsky|Trenciansky|Trnavsky|Zilinsky";\r
+aStates[206]="|Ajdovscina|Beltinci|Bled|Bohinj|Borovnica|Bovec|Brda|Brezice|Brezovica|Cankova-Tisina|Celje|Cerklje na Gorenjskem|Cerknica|Cerkno|Crensovci|Crna na Koroskem|Crnomelj|Destrnik-Trnovska Vas|Divaca|Dobrepolje|Dobrova-Horjul-Polhov Gradec|Dol pri Ljubljani|Domzale|Dornava|Dravograd|Duplek|Gorenja Vas-Poljane|Gorisnica|Gornja Radgona|Gornji Grad|Gornji Petrovci|Grosuplje|Hodos Salovci|Hrastnik|Hrpelje-Kozina|Idrija|Ig|Ilirska Bistrica|Ivancna Gorica|Izola|Jesenice|Jursinci|Kamnik|Kanal|Kidricevo|Kobarid|Kobilje|Kocevje|Komen|Koper|Kozje|Kranj|Kranjska Gora|Krsko|Kungota|Kuzma|Lasko|Lenart|Lendava|Litija|Ljubljana|Ljubno|Ljutomer|Logatec|Loska Dolina|Loski Potok|Luce|Lukovica|Majsperk|Maribor|Medvode|Menges|Metlika|Mezica|Miren-Kostanjevica|Mislinja|Moravce|Moravske Toplice|Mozirje|Murska Sobota|Muta|Naklo|Nazarje|Nova Gorica|Novo Mesto|Odranci|Ormoz|Osilnica|Pesnica|Piran|Pivka|Podcetrtek|Podvelka-Ribnica|Postojna|Preddvor|Ptuj|Puconci|Race-Fram|Radece|Radenci|Radlje ob Dravi|Radovljica|Ravne-Prevalje|Ribnica|Rogasevci|Rogaska Slatina|Rogatec|Ruse|Semic|Sencur|Sentilj|Sentjernej|Sentjur pri Celju|Sevnica|Sezana|Skocjan|Skofja Loka|Skofljica|Slovenj Gradec|Slovenska Bistrica|Slovenske Konjice|Smarje pri Jelsah|Smartno ob Paki|Sostanj|Starse|Store|Sveti Jurij|Tolmin|Trbovlje|Trebnje|Trzic|Turnisce|Velenje|Velike Lasce|Videm|Vipava|Vitanje|Vodice|Vojnik|Vrhnika|Vuzenica|Zagorje ob Savi|Zalec|Zavrc|Zelezniki|Ziri|Zrece";\r
+aStates[207]="|Bellona|Central|Choiseul (Lauru)|Guadalcanal|Honiara|Isabel|Makira|Malaita|Rennell|Temotu|Western";\r
+aStates[208]="|Awdal|Bakool|Banaadir|Bari|Bay|Galguduud|Gedo|Hiiraan|Jubbada Dhexe|Jubbada Hoose|Mudug|Nugaal|Sanaag|Shabeellaha Dhexe|Shabeellaha Hoose|Sool|Togdheer|Woqooyi Galbeed";\r
+aStates[209]="|Eastern Cape|Free State|Gauteng|KwaZulu-Natal|Mpumalanga|North-West|Northern Cape|Northern Province|Western Cape";\r
+aStates[210]="|Bird Island|Bristol Island|Clerke Rocks|Montagu Island|Saunders Island|South Georgia|Southern Thule|Traversay Islands";\r
+aStates[211]="|Andalucia|Aragon|Asturias|Ceuta|Islas Baleares|Islas Chafarinas|Islas Canarias|Cantabria|Castilla y Leon|Castilla-La Mancha|Catalunya|Extremadura|Galicia|La Rioja|Madrid|Melilla|Murcia|Navarra|Pais Vasco|Peñón de Alhucemas|Peñón de Vélez de la Gomera|Valencia";\r
+aStates[212]="|Spratly Islands";\r
+aStates[213]="|Central|Eastern|North Central|North Eastern|North Western|Northern|Sabaragamuwa|Southern|Uva|Western";\r
+aStates[214]="|A'ali an Nil|Al Bahr al Ahmar|Al Buhayrat|Al Jazirah|Al Khartum|Al Qadarif|Al Wahdah|An Nil al Abyad|An Nil al Azraq|Ash Shamaliyah|Bahr al Jabal|Gharb al Istiwa'iyah|Gharb Bahr al Ghazal|Gharb Darfur|Gharb Kurdufan|Janub Darfur|Janub Kurdufan|Junqali|Kassala|Nahr an Nil|Shamal Bahr al Ghazal|Shamal Darfur|Shamal Kurdufan|Sharq al Istiwa'iyah|Sinnar|Warab";\r
+aStates[215]="|Brokopondo|Commewijne|Coronie|Marowijne|Nickerie|Para|Paramaribo|Saramacca|Sipaliwini|Wanica";\r
+aStates[216]="|Barentsoya|Bjornoya|Edgeoya|Hopen|Kvitoya|Nordaustandet|Prins Karls Forland|Spitsbergen";\r
+aStates[217]="|Hhohho|Lubombo|Manzini|Shiselweni";\r
+aStates[218]="|Blekinge|Dalarnas|Gavleborgs|Gotlands|Hallands|Jamtlands|Jonkopings|Kalmar|Kronobergs|Norrbottens|Orebro|Ostergotlands|Skane|Sodermanlands|Stockholms|Uppsala|Varmlands|Vasterbottens|Vasternorrlands|Vastmanlands|Vastra Gotalands";\r
+aStates[219]="|Aargau|Ausser-Rhoden|Basel-Landschaft|Basel-Stadt|Bern|Fribourg|Geneve|Glarus|Graubunden|Inner-Rhoden|Jura|Luzern|Neuchatel|Nidwalden|Obwalden|Sankt Gallen|Schaffhausen|Schwyz|Solothurn|Thurgau|Ticino|Uri|Valais|Vaud|Zug|Zurich";\r
+aStates[220]="|Al Hasakah|Al Ladhiqiyah|Al Qunaytirah|Ar Raqqah|As Suwayda'|Dar'a|Dayr az Zawr|Dimashq|Halab|Hamah|Hims|Idlib|Rif Dimashq|Tartus";\r
+aStates[221]="|Chang-hua|Chi-lung|Chia-i (City)|Chia-i|Chung-hsing-hsin-ts'un|Hsin-chu (City)|Hsin-chu|Hua-lien|I-lan|Kao-hsiung (City)|Kao-hsiung|Miao-li|Nan-t'ou|P'eng-hu|P'ing-tung|T'ai-chung (City)|T'ai-chung|T'ai-nan (City)|T'ai-nan|T'ai-pei (City)|T'ai-pei|T'ai-tung|T'ao-yuan|Yun-lin";\r
+aStates[222]="|Viloyati Khatlon|Viloyati Leninobod|Viloyati Mukhtori Kuhistoni Badakhshon";\r
+aStates[223]="|Arusha|Dar es Salaam|Dodoma|Iringa|Kagera|Kigoma|Kilimanjaro|Lindi|Mara|Mbeya|Morogoro|Mtwara|Mwanza|Pemba North|Pemba South|Pwani|Rukwa|Ruvuma|Shinyanga|Singida|Tabora|Tanga|Zanzibar Central/South|Zanzibar North|Zanzibar Urban/West";\r
+aStates[224]="|Amnat Charoen|Ang Thong|Buriram|Chachoengsao|Chai Nat|Chaiyaphum|Chanthaburi|Chiang Mai|Chiang Rai|Chon Buri|Chumphon|Kalasin|Kamphaeng Phet|Kanchanaburi|Khon Kaen|Krabi|Krung Thep Mahanakhon (Bangkok)|Lampang|Lamphun|Loei|Lop Buri|Mae Hong Son|Maha Sarakham|Mukdahan|Nakhon Nayok|Nakhon Pathom|Nakhon Phanom|Nakhon Ratchasima|Nakhon Sawan|Nakhon Si Thammarat|Nan|Narathiwat|Nong Bua Lamphu|Nong Khai|Nonthaburi|Pathum Thani|Pattani|Phangnga|Phatthalung|Phayao|Phetchabun|Phetchaburi|Phichit|Phitsanulok|Phra Nakhon Si Ayutthaya|Phrae|Phuket|Prachin Buri|Prachuap Khiri Khan|Ranong|Ratchaburi|Rayong|Roi Et|Sa Kaeo|Sakon Nakhon|Samut Prakan|Samut Sakhon|Samut Songkhram|Sara Buri|Satun|Sing Buri|Sisaket|Songkhla|Sukhothai|Suphan Buri|Surat Thani|Surin|Tak|Trang|Trat|Ubon Ratchathani|Udon Thani|Uthai Thani|Uttaradit|Yala|Yasothon";\r
+aStates[225]="|Tobago";\r
+aStates[226]="|De La Kara|Des Plateaux|Des Savanes|Du Centre|Maritime";\r
+aStates[227]="|Atafu|Fakaofo|Nukunonu";\r
+aStates[228]="|Ha'apai|Tongatapu|Vava'u";\r
+aStates[229]="|Arima|Caroni|Mayaro|Nariva|Port-of-Spain|Saint Andrew|Saint David|Saint George|Saint Patrick|San Fernando|Victoria";\r
+aStates[230]="|Ariana|Beja|Ben Arous|Bizerte|El Kef|Gabes|Gafsa|Jendouba|Kairouan|Kasserine|Kebili|Mahdia|Medenine|Monastir|Nabeul|Sfax|Sidi Bou Zid|Siliana|Sousse|Tataouine|Tozeur|Tunis|Zaghouan";\r
+aStates[231]="|Adana|Adiyaman|Afyon|Agri|Aksaray|Amasya|Ankara|Antalya|Ardahan|Artvin|Aydin|Balikesir|Bartin|Batman|Bayburt|Bilecik|Bingol|Bitlis|Bolu|Burdur|Bursa|Canakkale|Cankiri|Corum|Denizli|Diyarbakir|Duzce|Edirne|Elazig|Erzincan|Erzurum|Eskisehir|Gaziantep|Giresun|Gumushane|Hakkari|Hatay|Icel|Igdir|Isparta|Istanbul|Izmir|Kahramanmaras|Karabuk|Karaman|Kars|Kastamonu|Kayseri|Kilis|Kirikkale|Kirklareli|Kirsehir|Kocaeli|Konya|Kutahya|Malatya|Manisa|Mardin|Mugla|Mus|Nevsehir|Nigde|Ordu|Osmaniye|Rize|Sakarya|Samsun|Sanliurfa|Siirt|Sinop|Sirnak|Sivas|Tekirdag|Tokat|Trabzon|Tunceli|Usak|Van|Yalova|Yozgat|Zonguldak";\r
+aStates[232]="|Ahal Welayaty|Balkan Welayaty|Dashhowuz Welayaty|Lebap Welayaty|Mary Welayaty";\r
+aStates[233]="|Tuvalu";\r
+aStates[234]="|Adjumani|Apac|Arua|Bugiri|Bundibugyo|Bushenyi|Busia|Gulu|Hoima|Iganga|Jinja|Kabale|Kabarole|Kalangala|Kampala|Kamuli|Kapchorwa|Kasese|Katakwi|Kibale|Kiboga|Kisoro|Kitgum|Kotido|Kumi|Lira|Luwero|Masaka|Masindi|Mbale|Mbarara|Moroto|Moyo|Mpigi|Mubende|Mukono|Nakasongola|Nebbi|Ntungamo|Pallisa|Rakai|Rukungiri|Sembabule|Soroti|Tororo";\r
+aStates[235]="|Avtonomna Respublika Krym (Simferopol')|Cherkas'ka (Cherkasy)|Chernihivs'ka (Chernihiv)|Chernivets'ka (Chernivtsi)|Dnipropetrovs'ka (Dnipropetrovs'k)|Donets'ka (Donets'k)|Ivano-Frankivs'ka (Ivano-Frankivs'k)|Kharkivs'ka (Kharkiv)|Khersons'ka (Kherson)|Khmel'nyts'ka (Khmel'nyts'kyy)|Kirovohrads'ka (Kirovohrad)|Kyyiv|Kyyivs'ka (Kiev)|L'vivs'ka (L'viv)|Luhans'ka (Luhans'k)|Mykolayivs'ka (Mykolayiv)|Odes'ka (Odesa)|Poltavs'ka (Poltava)|Rivnens'ka (Rivne)|Sevastopol'|Sums'ka (Sumy)|Ternopil's'ka (Ternopil')|Vinnyts'ka (Vinnytsya)|Volyns'ka (Luts'k)|Zakarpats'ka (Uzhhorod)|Zaporiz'ka (Zaporizhzhya)|Zhytomyrs'ka (Zhytomyr)"\r
+aStates[236]="|'Ajman|Abu Zaby (Abu Dhabi)|Al Fujayrah|Ash Shariqah (Sharjah)|Dubayy (Dubai)|Ra's al Khaymah|Umm al Qaywayn";\r
+aStates[237]="|Aberdeen|Aberdeenshire|Anglesey|Angus|Antrim|Argyl|Armagh|Avon|Ayrshire|Banffshire|Bedfordshire|Belfast|Berwickshire|Brecknockshire|Bristol|Buckinghamshire|Bute|Caernarfonshire|Cardiganshire|Caithness|Cambridgeshire|Carmarthenshire|Chesire|Clackmannashire|Cleveland|Clwyd|Cornwall|Cromartyshire|Cumberland|Cumbria|Denbighshire|Derbyshire|Devon|Dfyed|Dorset|Down|Dumfriesshire|Dunbartonshire|Dundee|Durham|East Lothian|East Suffolk|Derry|East Sussex|Edinburgh|Essex|Fermanagh|Fife|Flintshire|Glasgow|Glamorgan|Gloucestershire|Greater London|Greater Manchester|Gwent|Gwynedd|Hampshire|Hereford and Worcester|Herefordshire|Inverness-shire|Hertfordshire|Humberside|Huntingdon and Peterborough|Huntingdonshire|Isle of Ely|Isle of Wight|Kent|Kincardineshire|Kincross-shire|Kirkcudbrightshire|Lanarkshire|Lancashire|Leicestershire|Lincolnshire|London|Londonderry|Merionethshire|Merseyside|Middlesex|Mid Glamorgan|Midlothian|Monmouthshire|Montgomeryshire|Moray|Nairnshire|Norfolk|Northamptonshire|Northumberland|North Humberside|North Yorkshire|Nottinghamshire|Orkney|Oxfordshire|Peeblesshire|Pembrokeshire|Perthshire|Powys|Radnorshire|Renfrewshire|Ross And Cromarty|Ross-shire|Roxburghshire|Selkirkshire|Shetland|Stirlingshire|Sutherland|Soke of Peterborough|Rutland|Shropshire|Somerset|South Glamorgan|South Humberside|South Yorkshire|Staffordshite|Suffolk|Surrey|Sussex|Tyne and Wear|Tyrone|Warwickshire|West Glamorgan|West Lothian|West Midlands|Westmorland|West Suffolk|West Sussex|West Yorkshire|Wigtownshire|Wiltshire|Worcestershire|Yorkshire";\r
+aStates[238]="|Artigas|Canelones|Cerro Largo|Colonia|Durazno|Flores|Florida|Lavalleja|Maldonado|Montevideo|Paysandu|Rio Negro|Rivera|Rocha|Salto|San Jose|Soriano|Tacuarembo|Treinta y Tres";\r
+aStates[239]="|Alabama|Alaska|Arizona|Arkansas|California|Colorado|Connecticut|Delaware|District of Columbia|Florida|Georgia|Hawaii|Idaho|Illinois|Indiana|Iowa|Kansas|Kentucky|Louisiana|Maine|Maryland|Massachusetts|Michigan|Minnesota|Mississippi|Missouri|Montana|Nebraska|Nevada|New Hampshire|New Jersey|New Mexico|New York|North Carolina|North Dakota|Ohio|Oklahoma|Oregon|Pennsylvania|Rhode Island|South Carolina|South Dakota|Tennessee|Texas|Utah|Vermont|Virginia|Washington|West Virginia|Wisconsin|Wyoming";\r
+aStates[240]="|Andijon Wiloyati|Bukhoro Wiloyati|Farghona Wiloyati|Jizzakh Wiloyati|Khorazm Wiloyati (Urganch)|Namangan Wiloyati|Nawoiy Wiloyati|Qashqadaryo Wiloyati (Qarshi)|Qoraqalpoghiston (Nukus)|Samarqand Wiloyati|Sirdaryo Wiloyati (Guliston)|Surkhondaryo Wiloyati (Termiz)|Toshkent Shahri|Toshkent Wiloyati";\r
+aStates[241]="|Malampa|Penama|Sanma|Shefa|Tafea|Torba";\r
+aStates[242]="|Amazonas|Anzoategui|Apure|Aragua|Barinas|Bolivar|Carabobo|Cojedes|Delta Amacuro|Dependencias Federales|Distrito Federal|Falcon|Guarico|Lara|Merida|Miranda|Monagas|Nueva Esparta|Portuguesa|Sucre|Tachira|Trujillo|Vargas|Yaracuy|Zulia";\r
+aStates[243]="|An Giang|Ba Ria-Vung Tau|Bac Giang|Bac Kan|Bac Lieu|Bac Ninh|Ben Tre|Binh Dinh|Binh Duong|Binh Phuoc|Binh Thuan|Ca Mau|Can Tho|Cao Bang|Da Nang|Dac Lak|Dong Nai|Dong Thap|Gia Lai|Ha Giang|Ha Nam|Ha Noi|Ha Tay|Ha Tinh|Hai Duong|Hai Phong|Ho Chi Minh|Hoa Binh|Hung Yen|Khanh Hoa|Kien Giang|Kon Tum|Lai Chau|Lam Dong|Lang Son|Lao Cai|Long An|Nam Dinh|Nghe An|Ninh Binh|Ninh Thuan|Phu Tho|Phu Yen|Quang Binh|Quang Nam|Quang Ngai|Quang Ninh|Quang Tri|Soc Trang|Son La|Tay Ninh|Thai Binh|Thai Nguyen|Thanh Hoa|Thua Thien-Hue|Tien Giang|Tra Vinh|Tuyen Quang|Vinh Long|Vinh Phuc|Yen Bai";\r
+aStates[244]="|Saint Croix|Saint John|Saint Thomas";\r
+aStates[245]="|Anglesey|Brecknockshire|Caernfonshire|Cardiganshire|Carmarthenshire|Clwyd|Denbighshire|Dyfed|Flintshire|Glamorgan|Gwent|Gwynedd|Merionethshire|Mid Glamorgan|Monmouthsire|Montgomeryshire|Pembrokeshire|Powys|Radnorshire|South Glamorgan|West Glamorgan";\r
+aStates[246]="|Alo|Sigave|Wallis";\r
+aStates[247]="|West Bank";\r
+aStates[248]="|Western Sahara";\r
+aStates[249]="|'Adan|'Ataq|Abyan|Al Bayda'|Al Hudaydah|Al Jawf|Al Mahrah|Al Mahwit|Dhamar|Hadhramawt|Hajjah|Ibb|Lahij|Ma'rib|Sa'dah|San'a'|Ta'izz";\r
+aStates[250]="|Kosovo|Montenegro|Serbia|Vojvodina";\r
+aStates[251]="|Central|Copperbelt|Eastern|Luapula|Lusaka|North-Western|Northern|Southern|Western";\r
+aStates[252]="|Bulawayo|Harare|ManicalandMashonaland Central|Mashonaland East|Mashonaland West|Masvingo|Matabeleland North|Matabeleland South|Midlands";\r
+aStates[253]="|Self Hosted|Private Server|Architects Of Sleep|Chaos Friends|DFRN|Distributed Friend Network|ErrLock|Free-Beer.ch|Foojbook|Free-Haven|Friendica.eu|Friendika.me.4.it|Friendika - I Ask Questions|Frndc.com|Hikado|Hipatia|Hungerfreunde|Kaluguran Community|Kak Ste|Karl.Markx.pm|Loozah Social Club|MyFriendica.net|MyFriendNetwork|Oi!|OpenMindSpace|Optimistisch|Pplsnet|Recolutionari.es|Repatr.de|SPRACI|Styliztique|Sysfu Social Club|theshi.re|Tumpambae|Uzmiac|Other";\r
+/* \r
+ * gArCountryInfo\r
+ * (0) Country name\r
+ * (1) Name length\r
+ * (2) Number of states\r
+ * (3) Max state length\r
+ */\r
+gLngNumberCountries = sCountryString.split("|").length;\r
+//gArCountryInfo = new Array(gLngNumberCountries);\r
+gArCountryInfo=sCountryString.split("|");\r
+/* \r
+ * gArStateInfo[country][statenames][namelengths]\r
+ * (0) Country\r
+ * (1) States (Multi-sized Array)\r
+ *    (0) name\r
+ *    (1) nameLength\r
+ */\r
+gArStateInfo   = new Array(gLngNumberCountries);\r
+\r
+/* \r
+ * fInitgArStateInfo()\r
+ * purpose: build gArStateInfo array\r
+ * gArStateInfo[Country][States][1]\r
+ *   (0) State name\r
+ *   (1) State name length\r
+ * gArStateInfo[i] is an array of state names\r
+ * gArStateInfo[i][j]=state name, name length\r
+ */\r
+function fInitgArStateInfo() {\r
+ var i=0, j=0, sStateName="", iNumberOfStates=0;\r
+ var iMaxLength=0, iLength=0;\r
+ var oldNumber=0;\r
\r
+ for (i=0;i<gLngNumberCountries;i++) {\r
+  // i is selected country\r
+  iNumberOfStates=aStates[i].split("|").length+1;\r
+  gLngNumberStates=gLngNumberStates+iNumberOfStates;\r
+  gArStateInfo[i]=new Array(iNumberOfStates);\r
+  iMaxLength=0;\r
+  \r
+  // Add the additional information\r
+  for (j=0;j<iNumberOfStates;j++) {\r
+   if (iLength>iMaxLength) {\r
+    iMaxLength=iLength;\r
+    gArStateInfo[i][j][0]=sStateName;\r
+   }\r
+  }\r
+  gArCountryInfo[i][3]=parseInt(iMaxLength);\r
+ }\r
+ Update_Globals();\r
+ return;\r
+}\r
+\r
+/* \r
+ * Working on this one.\r
+ * Fills in region from the arrays\r
+ * \r
+ */\r
+function xFillState() {\r
+ var i=0;\r
\r
+ // reset region\r
+ document.form1.region.options.length=0;\r
\r
+ // get selected country\r
+ gLngSelectedCountry=document.form1.country_name.selectedIndex;\r
\r
+ // get number of states for selected country\r
+ gLngNumberStates=gArCountryInfo[[gLngSelectedCountry][2]];\r
\r
+ // update options in region\r
+ for (i=0;i<gLngNumberStates;i++) {\r
+  document.form1.region.options[i]=new\r
+    Option(gArStateInfo[[gLngSelectedCountry][i][0]]);\r
+ }\r
+ gLngSelectedState=\r
+  document.form1.region.options.selectedIndex;\r
\r
+ return;\r
+}\r
+\r
+/* \r
+ * FillStates() function works.\r
+ * Fills region from aStates\r
+ */\r
+function Fill_States(current) {\r
+       var i=0, iLen=0, iNumStates=0;\r
\r
+       // reset region\r
+       document.form1.region.options.length=0;\r
+       // get selected country\r
+       gLngSelectedCountry=document.form1.country_name.selectedIndex;\r
+       iNumStates = aStates[gLngSelectedCountry].split("|").length;\r
\r
+       // update the text boxes\r
+\r
+       // fill the state combobox with the list of states\r
+       for (i=0;i<iNumStates;i++) {\r
+               document.form1.region.options[i]=new \r
+                       Option(aStates[document.form1.country_name.selectedIndex].split("|")[i]);\r
+\r
+               sRegionName=document.form1.region.options[i];\r
+               if(sRegionName.text == current) {\r
+                       document.form1.region.selectedIndex = i;\r
+               }\r
+\r
+       }\r
+\r
+       return;\r
+}\r
+\r
+/*\r
+ * FillCountry()\r
+ * gArCountryInfo matrix holds the following information:\r
+ * (0) Country name\r
+ * (1) Name length\r
+ * (2) Number of states\r
+ * (3) Max state length\r
+ */\r
+function Fill_Country(current) {\r
+       var i=0;\r
+       var sCountryName="";\r
\r
+       // reset country_name.options\r
+       document.form1.country_name.options.length=0;\r
+       // get number of countries from the string\r
\r
+       // ----------------------------------------------------\r
+       // gArCountryInfo = new Array(gLngNumberCountries, 4);\r
+       // ----------------------------------------------------\r
+       for (i=0;i<gLngNumberCountries;i++) {\r
+               gArCountryInfo[i]=new Array(4);\r
+       }\r
\r
+       for (i=0;i<gLngNumberCountries;i++) {\r
+               document.form1.country_name.options[i]=new Option(sCountryString.split("|")[i]);\r
+               sCountryName=document.form1.country_name.options[i];\r
+               if(sCountryName.text == current) {\r
+                       document.form1.country_name.selectedIndex = i;\r
+               }\r
+               gArCountryInfo[i]=\r
+                       [sCountryName, \r
+                               parseInt(sCountryName.length),\r
+                               aStates,\r
+                       0];\r
+       }\r
+\r
+       fInitgArStateInfo();\r
+\r
+       return;\r
+}\r
+\r
+function Update_Globals() {\r
+       gLngSelectedCountry=parseInt(document.form1.country_name.selectedIndex);\r
+       gLngSelectedState=parseInt(document.form1.region.selectedIndex);\r
+       return;\r
+}\r
+\r
+\r
+//-->\r
diff --git a/view/js/cropper/cropper.css b/view/js/cropper/cropper.css
new file mode 100644 (file)
index 0000000..c2e7598
--- /dev/null
@@ -0,0 +1,182 @@
+.imgCrop_wrap {\r
+       /* width: 500px;   @done_in_js */\r
+       /* height: 375px;  @done_in_js */\r
+       position: relative;\r
+       cursor: crosshair;\r
+}\r
+\r
+/* an extra classname is applied for Opera < 9.0 to fix it's lack of opacity support */\r
+.imgCrop_wrap.opera8 .imgCrop_overlay,\r
+.imgCrop_wrap.opera8 .imgCrop_clickArea { \r
+       background-color: transparent;\r
+}\r
+\r
+/* fix for IE displaying all boxes at line-height by default, although they are still 1 pixel high until we combine them with the pointless span */\r
+.imgCrop_wrap,\r
+.imgCrop_wrap * {\r
+       font-size: 0;\r
+}\r
+\r
+.imgCrop_overlay {\r
+       background-color: #000;\r
+       opacity: 0.5;\r
+       filter:alpha(opacity=50);\r
+       position: absolute;\r
+       width: 100%;\r
+       height: 100%;\r
+}\r
+\r
+.imgCrop_selArea {\r
+       position: absolute;\r
+       /* @done_in_js \r
+       top: 20px;\r
+       left: 20px;\r
+       width: 200px;\r
+       height: 200px;\r
+       background: transparent url(castle.jpg) no-repeat  -210px -110px;\r
+       */\r
+       cursor: move;\r
+       z-index: 2;\r
+}\r
+\r
+/* clickArea is all a fix for IE 5.5 & 6 to allow the user to click on the given area */\r
+.imgCrop_clickArea {\r
+       width: 100%;\r
+       height: 100%;\r
+       background-color: #FFF;\r
+       opacity: 0.01;\r
+       filter:alpha(opacity=01);\r
+}\r
+\r
+.imgCrop_marqueeHoriz {\r
+       position: absolute;\r
+       width: 100%;\r
+       height: 1px;\r
+       background: transparent url(marqueeHoriz.gif) repeat-x 0 0;\r
+       z-index: 3;\r
+}\r
+\r
+.imgCrop_marqueeVert {\r
+       position: absolute;\r
+       height: 100%;\r
+       width: 1px;\r
+       background: transparent url(marqueeVert.gif) repeat-y 0 0;\r
+       z-index: 3;\r
+}\r
+\r
+/* \r
+ *  FIX MARCHING ANTS IN IE\r
+ *     As IE <6 tries to load background images we can uncomment the follwoing hack \r
+ *  to remove that issue, not as pretty - but is anything in IE?\r
+ *  And yes I do know that 'filter' is evil, but it will make it look semi decent in IE\r
+ *\r
+* html .imgCrop_marqueeHoriz,\r
+* html .imgCrop_marqueeVert {\r
+       background: transparent;\r
+       filter: Invert; \r
+}\r
+* html .imgCrop_marqueeNorth { border-top: 1px dashed #000; }\r
+* html .imgCrop_marqueeEast  { border-right: 1px dashed #000; }\r
+* html .imgCrop_marqueeSouth { border-bottom: 1px dashed #000; }\r
+* html .imgCrop_marqueeWest  { border-left: 1px dashed #000; }\r
+*/\r
+\r
+.imgCrop_marqueeNorth { top: 0; left: 0; }\r
+.imgCrop_marqueeEast  { top: 0; right: 0; }\r
+.imgCrop_marqueeSouth { bottom: 0px; left: 0; }\r
+.imgCrop_marqueeWest  { top: 0; left: 0; }\r
+\r
+\r
+.imgCrop_handle {\r
+       position: absolute;\r
+       border: 1px solid #333;\r
+       width: 6px;\r
+       height: 6px;\r
+       background: #FFF;\r
+       opacity: 0.5;\r
+       filter:alpha(opacity=50);\r
+       z-index: 4;\r
+}\r
+\r
+/* fix IE 5 box model */\r
+* html .imgCrop_handle {\r
+       width: 8px;\r
+       height: 8px;\r
+       wid\th: 6px;\r
+       hei\ght: 6px;\r
+}\r
+\r
+.imgCrop_handleN {\r
+       top: -3px;\r
+       left: 0;\r
+       /* margin-left: 49%;    @done_in_js */\r
+       cursor: n-resize;\r
+}\r
+\r
+.imgCrop_handleNE { \r
+       top: -3px;\r
+       right: -3px;\r
+       cursor: ne-resize;\r
+}\r
+\r
+.imgCrop_handleE {\r
+       top: 0;\r
+       right: -3px;\r
+       /* margin-top: 49%;    @done_in_js */\r
+       cursor: e-resize;\r
+}\r
+\r
+.imgCrop_handleSE {\r
+       right: -3px;\r
+       bottom: -3px;\r
+       cursor: se-resize;\r
+}\r
+\r
+.imgCrop_handleS {\r
+       right: 0;\r
+       bottom: -3px;\r
+       /* margin-right: 49%; @done_in_js */\r
+       cursor: s-resize;\r
+}\r
+\r
+.imgCrop_handleSW {\r
+       left: -3px;\r
+       bottom: -3px;\r
+       cursor: sw-resize;\r
+}\r
+\r
+.imgCrop_handleW {\r
+       top: 0;\r
+       left: -3px;\r
+       /* margin-top: 49%;  @done_in_js */\r
+       cursor: w-resize;\r
+}\r
+\r
+.imgCrop_handleNW {\r
+       top: -3px;\r
+       left: -3px;\r
+       cursor: nw-resize;\r
+}\r
+\r
+/**\r
+ * Create an area to click & drag around on as the default browser behaviour is to let you drag the image \r
+ */\r
+.imgCrop_dragArea {\r
+       width: 100%;\r
+       height: 100%;\r
+       z-index: 200;\r
+       position: absolute;\r
+       top: 0;\r
+       left: 0;\r
+}\r
+\r
+.imgCrop_previewWrap {\r
+       /* width: 200px;  @done_in_js */\r
+       /* height: 200px; @done_in_js */\r
+       overflow: hidden;\r
+       position: relative;\r
+}\r
+\r
+.imgCrop_previewWrap img {\r
+       position: absolute;\r
+}
\ No newline at end of file
diff --git a/view/js/cropper/cropper.html b/view/js/cropper/cropper.html
new file mode 100644 (file)
index 0000000..ebdf1ff
--- /dev/null
@@ -0,0 +1,228 @@
+   1.
+      <script type="text/javascript" src="scripts/cropper/lib/prototype.js" language="javascript"></script>
+   2.
+      <script type="text/javascript" src="scripts/cropper/lib/scriptaculous.js?load=builder,dragdrop" language="javascript"></script>
+   3.
+      <script type="text/javascript" src="scripts/cropper/cropper.js" language="javascript"></script>
+
+Options
+
+ratioDim obj
+    The pixel dimensions to apply as a restrictive ratio, with properties x & y.
+minWidth int
+    The minimum width for the select area in pixels.
+minHeight int
+    The mimimum height for the select area in pixels.
+maxWidth int
+    The maximum width for the select areas in pixels (if both minWidth & maxWidth set to same the width of the cropper will be fixed)
+maxHeight int
+    The maximum height for the select areas in pixels (if both minHeight & maxHeight set to same the height of the cropper will be fixed)
+displayOnInit int
+    Whether to display the select area on initialisation, only used when providing minimum width & height or ratio.
+onEndCrop func
+    The callback function to provide the crop details to on end of a crop.
+captureKeys boolean
+    Whether to capture the keys for moving the select area, as these can cause some problems at the moment.
+onloadCoords obj
+    A coordinates object with properties x1, y1, x2 & y2; for the coordinates of the select area to display onload
+
+The callback function
+
+The callback function is a function that allows you to capture the crop co-ordinates when the user finished a crop movement, it is passed two arguments:
+
+    * coords, obj, coordinates object with properties x1, y1, x2 & y2; for the coordinates of the select area.
+    * dimensions, obj, dimensions object with properities width & height; for the dimensions of the select area.
+
+An example function which outputs the crop values to form fields:
+Display code as plain text
+JavaScript:
+
+   1.
+      function onEndCrop( coords, dimensions ) {
+   2.
+          $PR( 'x1' ).value = coords.x1;
+   3.
+          $PR( 'y1' ).value = coords.y1;
+   4.
+          $PR( 'x2' ).value = coords.x2;
+   5.
+          $PR( 'y2' ).value = coords.y2;
+   6.
+          $PR( 'width' ).value = dimensions.width;
+   7.
+          $PR( 'height' ).value = dimensions.height;
+   8.
+      }
+
+Basic interface
+
+This basic example will attach the cropper UI to the test image and return crop results to the provided callback function.
+Display code as plain text
+HTML:
+
+   1.
+      <img src="test.jpg" alt="Test image" id="testImage" width="500" height="333" />
+   2.
+       
+   3.
+          <script type="text/javascript" language="javascript">
+   4.
+          Event.observe( window, 'load', function() {
+   5.
+              new Cropper.Img(
+   6.
+                  'testImage',
+   7.
+                  { onEndCrop: onEndCrop }
+   8.
+              );
+   9.
+          } );
+  10.
+      </script>
+
+Minimum dimensions
+
+You can apply minimum dimensions to a single axis or both, this example applies minimum dimensions to both axis.
+Display code as plain text
+HTML:
+
+   1.
+      <img src="test.jpg" alt="Test image" id="testImage" width="500" height="333" />
+   2.
+       
+   3.
+      <script type="text/javascript" language="javascript">
+   4.
+          Event.observe( window, 'load', function() {
+   5.
+              new Cropper.Img(
+   6.
+                  'testImage',
+   7.
+                  {
+   8.
+                      minWidth: 220,
+   9.
+                      minHeight: 120,
+  10.
+                      onEndCrop: onEndCrop
+  11.
+                  }
+  12.
+              );
+  13.
+          } );
+  14.
+      </script>
+
+Select area ratio
+
+You can apply a ratio to the selection area, this example applies a 4:3 ratio to the select area.
+Display code as plain text
+HTML:
+
+   1.
+      <img src="test.jpg" alt="Test image" id="testImage" width="500" height="333" />
+   2.
+       
+   3.
+      <script type="text/javascript" language="javascript">
+   4.
+          Event.observe( window, 'load', function() {
+   5.
+              new Cropper.Img(
+   6.
+                  'testImage',
+   7.
+                  {
+   8.
+                      ratioDim: {
+   9.
+                          x: 220,
+  10.
+                          y: 165
+  11.
+                      },
+  12.
+                      displayOnInit: true,
+  13.
+                      onEndCrop: onEndCrop
+  14.
+                  }
+  15.
+              );
+  16.
+          } );
+  17.
+      </script>
+
+With crop preview
+
+You can display a dynamically prouced preview of the resulting crop by using the ImgWithPreview subclass, a preview can only be displayed when we have a fixed size (set via minWidth & minHeight options). Note that the displayOnInit option is not required as this is the default behaviour when displaying a crop preview.
+Display code as plain text
+HTML:
+
+   1.
+      <img src="test.jpg" alt="Test image" id="testImage" width="500" height="333" />
+   2.
+      <div id="previewWrap"></div>
+   3.
+       
+   4.
+      <script type="text/javascript" language="javascript">
+   5.
+          Event.observe( window, 'load', function() {
+   6.
+              new Cropper.ImgWithPreview(
+   7.
+                  'testImage',
+   8.
+                  {
+   9.
+                      previewWrap: 'previewWrap',
+  10.
+                      minWidth: 120,
+  11.
+                      minHeight: 120,
+  12.
+                      ratioDim: { x: 200, y: 120 },
+  13.
+                      onEndCrop: onEndCrop
+  14.
+                  }
+  15.
+              );
+  16.
+          } );
+  17.
+      </script>
+
+Known Issues
+
+    * Safari animated gifs, only one of each will animate, this seems to be a known Safari issue.
+    * After drawing an area and then clicking to start a new drag in IE 5.5 the rendered height appears as the last height until the user drags, this appears to be the related to another IE error (which has been fixed) where IE does not always redraw the select area properly.
+    * Lack of CSS opacity support in Opera before version 9 mean we disable those style rules, if Opera 8 support is important you & you want the overlay to work then you can use the Opera rules in the CSS to apply a black PNG with 50% alpha transparency to replicate the effect.
+    * Styling & borders on image, any CSS styling applied directly to the image itself (floats, borders, padding, margin, etc.) will cause problems with the cropper. The use of a wrapper element to apply these styles to is recommended.
+    * overflow: auto or overflow: scroll on parent will cause cropper to burst out of parent in IE and Opera when applied (maybe Mac browsers too) I'm not sure why yet.
+
+If you use CakePHP you will notice that including this in your script will break the CSS layout. This is due to the CSS rule
+
+form div{
+vertical-align: text-top;
+margin-left: 1em;
+margin-bottom:2em;
+overflow: auto;
+}
+
+A simple workaround is to add another rule directly after this like so:
+
+form div.no_cake, form div.no_cake div {
+margin:0;
+overflow:hidden;
+}
+
+and then in your code surround the img tag with a div with the class name of no_cake.
+
+Cheers
+
diff --git a/view/js/cropper/cropper.js b/view/js/cropper/cropper.js
new file mode 100644 (file)
index 0000000..427a9ba
--- /dev/null
@@ -0,0 +1,568 @@
+/** \r
+ * Copyright (c) 2006, David Spurr (http://www.defusion.org.uk/)\r
+ * All rights reserved.\r
+ * \r
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\r
+ * \r
+ *     * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\r
+ *     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\r
+ *     * Neither the name of the David Spurr nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\r
+ * \r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ * \r
+ * http://www.opensource.org/licenses/bsd-license.php\r
+ * \r
+ * See scriptaculous.js for full scriptaculous licence\r
+ *\r
+ * Modified 2013/06/01 Zach P to change $() to $PR() for eliminating conflicts with jQuery\r
+ */\r
+\r
+var CropDraggable=Class.create();\r
+Object.extend(Object.extend(CropDraggable.prototype,Draggable.prototype),{initialize:function(_1){\r
+this.options=Object.extend({drawMethod:function(){\r
+}},arguments[1]||{});\r
+this.element=$PR(_1);\r
+this.handle=this.element;\r
+this.delta=this.currentDelta();\r
+this.dragging=false;\r
+this.eventMouseDown=this.initDrag.bindAsEventListener(this);\r
+Event.observe(this.handle,"mousedown",this.eventMouseDown);\r
+Draggables.register(this);\r
+},draw:function(_2){\r
+var _3=Position.cumulativeOffset(this.element);\r
+var d=this.currentDelta();\r
+_3[0]-=d[0];\r
+_3[1]-=d[1];\r
+var p=[0,1].map(function(i){\r
+return (_2[i]-_3[i]-this.offset[i]);\r
+}.bind(this));\r
+this.options.drawMethod(p);\r
+}});\r
+var Cropper={};\r
+Cropper.Img=Class.create();\r
+Cropper.Img.prototype={initialize:function(_7,_8){\r
+this.options=Object.extend({ratioDim:{x:0,y:0},minWidth:0,minHeight:0,displayOnInit:false,onEndCrop:Prototype.emptyFunction,captureKeys:true,onloadCoords:null,maxWidth:0,maxHeight:0},_8||{});\r
+this.img=$PR(_7);\r
+this.clickCoords={x:0,y:0};\r
+this.dragging=false;\r
+this.resizing=false;\r
+this.isWebKit=/Konqueror|Safari|KHTML/.test(navigator.userAgent);\r
+this.isIE=/MSIE/.test(navigator.userAgent);\r
+this.isOpera8=/Opera\s[1-8]/.test(navigator.userAgent);\r
+this.ratioX=0;\r
+this.ratioY=0;\r
+this.attached=false;\r
+this.fixedWidth=(this.options.maxWidth>0&&(this.options.minWidth>=this.options.maxWidth));\r
+this.fixedHeight=(this.options.maxHeight>0&&(this.options.minHeight>=this.options.maxHeight));\r
+if(typeof this.img=="undefined"){\r
+return;\r
+}\r
+$A(document.getElementsByTagName("script")).each(function(s){\r
+if(s.src.match(/cropper\.js/)){\r
+var _a=s.src.replace(/cropper\.js(.*)?/,"");\r
+var _b=document.createElement("link");\r
+_b.rel="stylesheet";\r
+_b.type="text/css";\r
+_b.href=_a+"cropper.css";\r
+_b.media="screen";\r
+document.getElementsByTagName("head")[0].appendChild(_b);\r
+}\r
+});\r
+if(this.options.ratioDim.x>0&&this.options.ratioDim.y>0){\r
+var _c=this.getGCD(this.options.ratioDim.x,this.options.ratioDim.y);\r
+this.ratioX=this.options.ratioDim.x/_c;\r
+this.ratioY=this.options.ratioDim.y/_c;\r
+}\r
+this.subInitialize();\r
+if(this.img.complete||this.isWebKit){\r
+this.onLoad();\r
+}else{\r
+Event.observe(this.img,"load",this.onLoad.bindAsEventListener(this));\r
+}\r
+},getGCD:function(a,b){\r
+if(b==0){\r
+return a;\r
+}\r
+return this.getGCD(b,a%b);\r
+},onLoad:function(){\r
+var _f="imgCrop_";\r
+var _10=this.img.parentNode;\r
+var _11="";\r
+if(this.isOpera8){\r
+_11=" opera8";\r
+}\r
+this.imgWrap=Builder.node("div",{"class":_f+"wrap"+_11});\r
+this.north=Builder.node("div",{"class":_f+"overlay "+_f+"north"},[Builder.node("span")]);\r
+this.east=Builder.node("div",{"class":_f+"overlay "+_f+"east"},[Builder.node("span")]);\r
+this.south=Builder.node("div",{"class":_f+"overlay "+_f+"south"},[Builder.node("span")]);\r
+this.west=Builder.node("div",{"class":_f+"overlay "+_f+"west"},[Builder.node("span")]);\r
+var _12=[this.north,this.east,this.south,this.west];\r
+this.dragArea=Builder.node("div",{"class":_f+"dragArea"},_12);\r
+this.handleN=Builder.node("div",{"class":_f+"handle "+_f+"handleN"});\r
+this.handleNE=Builder.node("div",{"class":_f+"handle "+_f+"handleNE"});\r
+this.handleE=Builder.node("div",{"class":_f+"handle "+_f+"handleE"});\r
+this.handleSE=Builder.node("div",{"class":_f+"handle "+_f+"handleSE"});\r
+this.handleS=Builder.node("div",{"class":_f+"handle "+_f+"handleS"});\r
+this.handleSW=Builder.node("div",{"class":_f+"handle "+_f+"handleSW"});\r
+this.handleW=Builder.node("div",{"class":_f+"handle "+_f+"handleW"});\r
+this.handleNW=Builder.node("div",{"class":_f+"handle "+_f+"handleNW"});\r
+this.selArea=Builder.node("div",{"class":_f+"selArea"},[Builder.node("div",{"class":_f+"marqueeHoriz "+_f+"marqueeNorth"},[Builder.node("span")]),Builder.node("div",{"class":_f+"marqueeVert "+_f+"marqueeEast"},[Builder.node("span")]),Builder.node("div",{"class":_f+"marqueeHoriz "+_f+"marqueeSouth"},[Builder.node("span")]),Builder.node("div",{"class":_f+"marqueeVert "+_f+"marqueeWest"},[Builder.node("span")]),this.handleN,this.handleNE,this.handleE,this.handleSE,this.handleS,this.handleSW,this.handleW,this.handleNW,Builder.node("div",{"class":_f+"clickArea"})]);\r
+this.imgWrap.appendChild(this.img);\r
+this.imgWrap.appendChild(this.dragArea);\r
+this.dragArea.appendChild(this.selArea);\r
+this.dragArea.appendChild(Builder.node("div",{"class":_f+"clickArea"}));\r
+_10.appendChild(this.imgWrap);\r
+this.startDragBind=this.startDrag.bindAsEventListener(this);\r
+Event.observe(this.dragArea,"mousedown",this.startDragBind);\r
+this.onDragBind=this.onDrag.bindAsEventListener(this);\r
+Event.observe(document,"mousemove",this.onDragBind);\r
+this.endCropBind=this.endCrop.bindAsEventListener(this);\r
+Event.observe(document,"mouseup",this.endCropBind);\r
+this.resizeBind=this.startResize.bindAsEventListener(this);\r
+this.handles=[this.handleN,this.handleNE,this.handleE,this.handleSE,this.handleS,this.handleSW,this.handleW,this.handleNW];\r
+this.registerHandles(true);\r
+if(this.options.captureKeys){\r
+this.keysBind=this.handleKeys.bindAsEventListener(this);\r
+Event.observe(document,"keypress",this.keysBind);\r
+}\r
+new CropDraggable(this.selArea,{drawMethod:this.moveArea.bindAsEventListener(this)});\r
+this.setParams();\r
+},registerHandles:function(_13){\r
+for(var i=0;i<this.handles.length;i++){\r
+var _15=$PR(this.handles[i]);\r
+if(_13){\r
+var _16=false;\r
+if(this.fixedWidth&&this.fixedHeight){\r
+_16=true;\r
+}else{\r
+if(this.fixedWidth||this.fixedHeight){\r
+var _17=_15.className.match(/([S|N][E|W])$/);\r
+var _18=_15.className.match(/(E|W)$/);\r
+var _19=_15.className.match(/(N|S)$/);\r
+if(_17){\r
+_16=true;\r
+}else{\r
+if(this.fixedWidth&&_18){\r
+_16=true;\r
+}else{\r
+if(this.fixedHeight&&_19){\r
+_16=true;\r
+}\r
+}\r
+}\r
+}\r
+}\r
+if(_16){\r
+_15.hide();\r
+}else{\r
+Event.observe(_15,"mousedown",this.resizeBind);\r
+}\r
+}else{\r
+_15.show();\r
+Event.stopObserving(_15,"mousedown",this.resizeBind);\r
+}\r
+}\r
+},setParams:function(){\r
+this.imgW=this.img.width;\r
+this.imgH=this.img.height;\r
+$PR(this.north).setStyle({height:0});\r
+$PR(this.east).setStyle({width:0,height:0});\r
+$PR(this.south).setStyle({height:0});\r
+$PR(this.west).setStyle({width:0,height:0});\r
+$PR(this.imgWrap).setStyle({"width":this.imgW+"px","height":this.imgH+"px"});\r
+$PR(this.selArea).hide();\r
+var _1a={x1:0,y1:0,x2:0,y2:0};\r
+var _1b=false;\r
+if(this.options.onloadCoords!=null){\r
+_1a=this.cloneCoords(this.options.onloadCoords);\r
+_1b=true;\r
+}else{\r
+if(this.options.ratioDim.x>0&&this.options.ratioDim.y>0){\r
+_1a.x1=Math.ceil((this.imgW-this.options.ratioDim.x)/2);\r
+_1a.y1=Math.ceil((this.imgH-this.options.ratioDim.y)/2);\r
+_1a.x2=_1a.x1+this.options.ratioDim.x;\r
+_1a.y2=_1a.y1+this.options.ratioDim.y;\r
+_1b=true;\r
+}\r
+}\r
+this.setAreaCoords(_1a,false,false,1);\r
+if(this.options.displayOnInit&&_1b){\r
+this.selArea.show();\r
+this.drawArea();\r
+this.endCrop();\r
+}\r
+this.attached=true;\r
+},remove:function(){\r
+if(this.attached){\r
+this.attached=false;\r
+this.imgWrap.parentNode.insertBefore(this.img,this.imgWrap);\r
+this.imgWrap.parentNode.removeChild(this.imgWrap);\r
+Event.stopObserving(this.dragArea,"mousedown",this.startDragBind);\r
+Event.stopObserving(document,"mousemove",this.onDragBind);\r
+Event.stopObserving(document,"mouseup",this.endCropBind);\r
+this.registerHandles(false);\r
+if(this.options.captureKeys){\r
+Event.stopObserving(document,"keypress",this.keysBind);\r
+}\r
+}\r
+},reset:function(){\r
+if(!this.attached){\r
+this.onLoad();\r
+}else{\r
+this.setParams();\r
+}\r
+this.endCrop();\r
+},handleKeys:function(e){\r
+var dir={x:0,y:0};\r
+if(!this.dragging){\r
+switch(e.keyCode){\r
+case (37):\r
+dir.x=-1;\r
+break;\r
+case (38):\r
+dir.y=-1;\r
+break;\r
+case (39):\r
+dir.x=1;\r
+break;\r
+case (40):\r
+dir.y=1;\r
+break;\r
+}\r
+if(dir.x!=0||dir.y!=0){\r
+if(e.shiftKey){\r
+dir.x*=10;\r
+dir.y*=10;\r
+}\r
+this.moveArea([this.areaCoords.x1+dir.x,this.areaCoords.y1+dir.y]);\r
+Event.stop(e);\r
+}\r
+}\r
+},calcW:function(){\r
+return (this.areaCoords.x2-this.areaCoords.x1);\r
+},calcH:function(){\r
+return (this.areaCoords.y2-this.areaCoords.y1);\r
+},moveArea:function(_1e){\r
+this.setAreaCoords({x1:_1e[0],y1:_1e[1],x2:_1e[0]+this.calcW(),y2:_1e[1]+this.calcH()},true,false);\r
+this.drawArea();\r
+},cloneCoords:function(_1f){\r
+return {x1:_1f.x1,y1:_1f.y1,x2:_1f.x2,y2:_1f.y2};\r
+},setAreaCoords:function(_20,_21,_22,_23,_24){\r
+if(_21){\r
+var _25=_20.x2-_20.x1;\r
+var _26=_20.y2-_20.y1;\r
+if(_20.x1<0){\r
+_20.x1=0;\r
+_20.x2=_25;\r
+}\r
+if(_20.y1<0){\r
+_20.y1=0;\r
+_20.y2=_26;\r
+}\r
+if(_20.x2>this.imgW){\r
+_20.x2=this.imgW;\r
+_20.x1=this.imgW-_25;\r
+}\r
+if(_20.y2>this.imgH){\r
+_20.y2=this.imgH;\r
+_20.y1=this.imgH-_26;\r
+}\r
+}else{\r
+if(_20.x1<0){\r
+_20.x1=0;\r
+}\r
+if(_20.y1<0){\r
+_20.y1=0;\r
+}\r
+if(_20.x2>this.imgW){\r
+_20.x2=this.imgW;\r
+}\r
+if(_20.y2>this.imgH){\r
+_20.y2=this.imgH;\r
+}\r
+if(_23!=null){\r
+if(this.ratioX>0){\r
+this.applyRatio(_20,{x:this.ratioX,y:this.ratioY},_23,_24);\r
+}else{\r
+if(_22){\r
+this.applyRatio(_20,{x:1,y:1},_23,_24);\r
+}\r
+}\r
+var _27=[this.options.minWidth,this.options.minHeight];\r
+var _28=[this.options.maxWidth,this.options.maxHeight];\r
+if(_27[0]>0||_27[1]>0||_28[0]>0||_28[1]>0){\r
+var _29={a1:_20.x1,a2:_20.x2};\r
+var _2a={a1:_20.y1,a2:_20.y2};\r
+var _2b={min:0,max:this.imgW};\r
+var _2c={min:0,max:this.imgH};\r
+if((_27[0]!=0||_27[1]!=0)&&_22){\r
+if(_27[0]>0){\r
+_27[1]=_27[0];\r
+}else{\r
+if(_27[1]>0){\r
+_27[0]=_27[1];\r
+}\r
+}\r
+}\r
+if((_28[0]!=0||_28[0]!=0)&&_22){\r
+if(_28[0]>0&&_28[0]<=_28[1]){\r
+_28[1]=_28[0];\r
+}else{\r
+if(_28[1]>0&&_28[1]<=_28[0]){\r
+_28[0]=_28[1];\r
+}\r
+}\r
+}\r
+if(_27[0]>0){\r
+this.applyDimRestriction(_29,_27[0],_23.x,_2b,"min");\r
+}\r
+if(_27[1]>1){\r
+this.applyDimRestriction(_2a,_27[1],_23.y,_2c,"min");\r
+}\r
+if(_28[0]>0){\r
+this.applyDimRestriction(_29,_28[0],_23.x,_2b,"max");\r
+}\r
+if(_28[1]>1){\r
+this.applyDimRestriction(_2a,_28[1],_23.y,_2c,"max");\r
+}\r
+_20={x1:_29.a1,y1:_2a.a1,x2:_29.a2,y2:_2a.a2};\r
+}\r
+}\r
+}\r
+this.areaCoords=_20;\r
+},applyDimRestriction:function(_2d,val,_2f,_30,_31){\r
+var _32;\r
+if(_31=="min"){\r
+_32=((_2d.a2-_2d.a1)<val);\r
+}else{\r
+_32=((_2d.a2-_2d.a1)>val);\r
+}\r
+if(_32){\r
+if(_2f==1){\r
+_2d.a2=_2d.a1+val;\r
+}else{\r
+_2d.a1=_2d.a2-val;\r
+}\r
+if(_2d.a1<_30.min){\r
+_2d.a1=_30.min;\r
+_2d.a2=val;\r
+}else{\r
+if(_2d.a2>_30.max){\r
+_2d.a1=_30.max-val;\r
+_2d.a2=_30.max;\r
+}\r
+}\r
+}\r
+},applyRatio:function(_33,_34,_35,_36){\r
+var _37;\r
+if(_36=="N"||_36=="S"){\r
+_37=this.applyRatioToAxis({a1:_33.y1,b1:_33.x1,a2:_33.y2,b2:_33.x2},{a:_34.y,b:_34.x},{a:_35.y,b:_35.x},{min:0,max:this.imgW});\r
+_33.x1=_37.b1;\r
+_33.y1=_37.a1;\r
+_33.x2=_37.b2;\r
+_33.y2=_37.a2;\r
+}else{\r
+_37=this.applyRatioToAxis({a1:_33.x1,b1:_33.y1,a2:_33.x2,b2:_33.y2},{a:_34.x,b:_34.y},{a:_35.x,b:_35.y},{min:0,max:this.imgH});\r
+_33.x1=_37.a1;\r
+_33.y1=_37.b1;\r
+_33.x2=_37.a2;\r
+_33.y2=_37.b2;\r
+}\r
+},applyRatioToAxis:function(_38,_39,_3a,_3b){\r
+var _3c=Object.extend(_38,{});\r
+var _3d=_3c.a2-_3c.a1;\r
+var _3e=Math.floor(_3d*_39.b/_39.a);\r
+var _3f;\r
+var _40;\r
+var _41=null;\r
+if(_3a.b==1){\r
+_3f=_3c.b1+_3e;\r
+if(_3f>_3b.max){\r
+_3f=_3b.max;\r
+_41=_3f-_3c.b1;\r
+}\r
+_3c.b2=_3f;\r
+}else{\r
+_3f=_3c.b2-_3e;\r
+if(_3f<_3b.min){\r
+_3f=_3b.min;\r
+_41=_3f+_3c.b2;\r
+}\r
+_3c.b1=_3f;\r
+}\r
+if(_41!=null){\r
+_40=Math.floor(_41*_39.a/_39.b);\r
+if(_3a.a==1){\r
+_3c.a2=_3c.a1+_40;\r
+}else{\r
+_3c.a1=_3c.a1=_3c.a2-_40;\r
+}\r
+}\r
+return _3c;\r
+},drawArea:function(){\r
+var _42=this.calcW();\r
+var _43=this.calcH();\r
+var px="px";\r
+var _45=[this.areaCoords.x1+px,this.areaCoords.y1+px,_42+px,_43+px,this.areaCoords.x2+px,this.areaCoords.y2+px,(this.img.width-this.areaCoords.x2)+px,(this.img.height-this.areaCoords.y2)+px];\r
+var _46=this.selArea.style;\r
+_46.left=_45[0];\r
+_46.top=_45[1];\r
+_46.width=_45[2];\r
+_46.height=_45[3];\r
+var _47=Math.ceil((_42-6)/2)+px;\r
+var _48=Math.ceil((_43-6)/2)+px;\r
+this.handleN.style.left=_47;\r
+this.handleE.style.top=_48;\r
+this.handleS.style.left=_47;\r
+this.handleW.style.top=_48;\r
+this.north.style.height=_45[1];\r
+var _49=this.east.style;\r
+_49.top=_45[1];\r
+_49.height=_45[3];\r
+_49.left=_45[4];\r
+_49.width=_45[6];\r
+var _4a=this.south.style;\r
+_4a.top=_45[5];\r
+_4a.height=_45[7];\r
+var _4b=this.west.style;\r
+_4b.top=_45[1];\r
+_4b.height=_45[3];\r
+_4b.width=_45[0];\r
+this.subDrawArea();\r
+this.forceReRender();\r
+},forceReRender:function(){\r
+if(this.isIE||this.isWebKit){\r
+var n=document.createTextNode(" ");\r
+var d,el,fixEL,i;\r
+if(this.isIE){\r
+fixEl=this.selArea;\r
+}else{\r
+if(this.isWebKit){\r
+fixEl=document.getElementsByClassName("imgCrop_marqueeSouth",this.imgWrap)[0];\r
+d=Builder.node("div","");\r
+d.style.visibility="hidden";\r
+var _4e=["SE","S","SW"];\r
+for(i=0;i<_4e.length;i++){\r
+el=document.getElementsByClassName("imgCrop_handle"+_4e[i],this.selArea)[0];\r
+if(el.childNodes.length){\r
+el.removeChild(el.childNodes[0]);\r
+}\r
+el.appendChild(d);\r
+}\r
+}\r
+}\r
+fixEl.appendChild(n);\r
+fixEl.removeChild(n);\r
+}\r
+},startResize:function(e){\r
+this.startCoords=this.cloneCoords(this.areaCoords);\r
+this.resizing=true;\r
+this.resizeHandle=Event.element(e).classNames().toString().replace(/([^N|NE|E|SE|S|SW|W|NW])+/,"");\r
+Event.stop(e);\r
+},startDrag:function(e){\r
+this.selArea.show();\r
+this.clickCoords=this.getCurPos(e);\r
+this.setAreaCoords({x1:this.clickCoords.x,y1:this.clickCoords.y,x2:this.clickCoords.x,y2:this.clickCoords.y},false,false,null);\r
+this.dragging=true;\r
+this.onDrag(e);\r
+Event.stop(e);\r
+},getCurPos:function(e){\r
+var el=this.imgWrap,wrapOffsets=Position.cumulativeOffset(el);\r
+while(el.nodeName!="BODY"){\r
+wrapOffsets[1]-=el.scrollTop||0;\r
+wrapOffsets[0]-=el.scrollLeft||0;\r
+el=el.parentNode;\r
+}\r
+return curPos={x:Event.pointerX(e)-wrapOffsets[0],y:Event.pointerY(e)-wrapOffsets[1]};\r
+},onDrag:function(e){\r
+if(this.dragging||this.resizing){\r
+var _54=null;\r
+var _55=this.getCurPos(e);\r
+var _56=this.cloneCoords(this.areaCoords);\r
+var _57={x:1,y:1};\r
+if(this.dragging){\r
+if(_55.x<this.clickCoords.x){\r
+_57.x=-1;\r
+}\r
+if(_55.y<this.clickCoords.y){\r
+_57.y=-1;\r
+}\r
+this.transformCoords(_55.x,this.clickCoords.x,_56,"x");\r
+this.transformCoords(_55.y,this.clickCoords.y,_56,"y");\r
+}else{\r
+if(this.resizing){\r
+_54=this.resizeHandle;\r
+if(_54.match(/E/)){\r
+this.transformCoords(_55.x,this.startCoords.x1,_56,"x");\r
+if(_55.x<this.startCoords.x1){\r
+_57.x=-1;\r
+}\r
+}else{\r
+if(_54.match(/W/)){\r
+this.transformCoords(_55.x,this.startCoords.x2,_56,"x");\r
+if(_55.x<this.startCoords.x2){\r
+_57.x=-1;\r
+}\r
+}\r
+}\r
+if(_54.match(/N/)){\r
+this.transformCoords(_55.y,this.startCoords.y2,_56,"y");\r
+if(_55.y<this.startCoords.y2){\r
+_57.y=-1;\r
+}\r
+}else{\r
+if(_54.match(/S/)){\r
+this.transformCoords(_55.y,this.startCoords.y1,_56,"y");\r
+if(_55.y<this.startCoords.y1){\r
+_57.y=-1;\r
+}\r
+}\r
+}\r
+}\r
+}\r
+this.setAreaCoords(_56,false,e.shiftKey,_57,_54);\r
+this.drawArea();\r
+Event.stop(e);\r
+}\r
+},transformCoords:function(_58,_59,_5a,_5b){\r
+var _5c=[_58,_59];\r
+if(_58>_59){\r
+_5c.reverse();\r
+}\r
+_5a[_5b+"1"]=_5c[0];\r
+_5a[_5b+"2"]=_5c[1];\r
+},endCrop:function(){\r
+this.dragging=false;\r
+this.resizing=false;\r
+this.options.onEndCrop(this.areaCoords,{width:this.calcW(),height:this.calcH()});\r
+},subInitialize:function(){\r
+},subDrawArea:function(){\r
+}};\r
+Cropper.ImgWithPreview=Class.create();\r
+Object.extend(Object.extend(Cropper.ImgWithPreview.prototype,Cropper.Img.prototype),{subInitialize:function(){\r
+this.hasPreviewImg=false;\r
+if(typeof (this.options.previewWrap)!="undefined"&&this.options.minWidth>0&&this.options.minHeight>0){\r
+this.previewWrap=$PR(this.options.previewWrap);\r
+this.previewImg=this.img.cloneNode(false);\r
+this.previewImg.id="imgCrop_"+this.previewImg.id;\r
+this.options.displayOnInit=true;\r
+this.hasPreviewImg=true;\r
+this.previewWrap.addClassName("imgCrop_previewWrap");\r
+this.previewWrap.setStyle({width:this.options.minWidth+"px",height:this.options.minHeight+"px"});\r
+this.previewWrap.appendChild(this.previewImg);\r
+}\r
+},subDrawArea:function(){\r
+if(this.hasPreviewImg){\r
+var _5d=this.calcW();\r
+var _5e=this.calcH();\r
+var _5f={x:this.imgW/_5d,y:this.imgH/_5e};\r
+var _60={x:_5d/this.options.minWidth,y:_5e/this.options.minHeight};\r
+var _61={w:Math.ceil(this.options.minWidth*_5f.x)+"px",h:Math.ceil(this.options.minHeight*_5f.y)+"px",x:"-"+Math.ceil(this.areaCoords.x1/_60.x)+"px",y:"-"+Math.ceil(this.areaCoords.y1/_60.y)+"px"};\r
+var _62=this.previewImg.style;\r
+_62.width=_61.w;\r
+_62.height=_61.h;\r
+_62.left=_61.x;\r
+_62.top=_61.y;\r
+}\r
+}});\r
+\r
diff --git a/view/js/cropper/cropper.uncompressed.js b/view/js/cropper/cropper.uncompressed.js
new file mode 100644 (file)
index 0000000..0ea0b80
--- /dev/null
@@ -0,0 +1,1333 @@
+/**\r
+ * Image Cropper (v. 1.2.0 - 2006-10-30 )\r
+ * Copyright (c) 2006 David Spurr (http://www.defusion.org.uk/)\r
+ * \r
+ * The image cropper provides a way to draw a crop area on an image and capture\r
+ * the coordinates of the drawn crop area.\r
+ * \r
+ * Features include:\r
+ *             - Based on Prototype and Scriptaculous\r
+ *             - Image editing package styling, the crop area functions and looks \r
+ *               like those found in popular image editing software\r
+ *             - Dynamic inclusion of required styles\r
+ *             - Drag to draw areas\r
+ *             - Shift drag to draw/resize areas as squares\r
+ *             - Selection area can be moved \r
+ *             - Seleciton area can be resized using resize handles\r
+ *             - Allows dimension ratio limited crop areas\r
+ *             - Allows minimum dimension crop areas\r
+ *             - Allows maximum dimesion crop areas\r
+ *             - If both min & max dimension options set to the same value for a single axis,then the cropper will not \r
+ *               display the resize handles as appropriate (when min & max dimensions are passed for both axes this\r
+ *               results in a 'fixed size' crop area)\r
+ *             - Allows dynamic preview of resultant crop ( if minimum width & height are provided ), this is\r
+ *               implemented as a subclass so can be excluded when not required\r
+ *             - Movement of selection area by arrow keys ( shift + arrow key will move selection area by\r
+ *               10 pixels )\r
+ *             - All operations stay within bounds of image\r
+ *             - All functionality & display compatible with most popular browsers supported by Prototype:\r
+ *                     PC:     IE 7, 6 & 5.5, Firefox 1.5, Opera 8.5 (see known issues) & 9.0b\r
+ *                     MAC: Camino 1.0, Firefox 1.5, Safari 2.0\r
+ * \r
+ * Requires:\r
+ *             - Prototype v. 1.5.0_rc0 > (as packaged with Scriptaculous 1.6.1)\r
+ *             - Scriptaculous v. 1.6.1 > modules: builder, dragdrop \r
+ *             \r
+ * Known issues:\r
+ *             - Safari animated gifs, only one of each will animate, this seems to be a known Safari issue\r
+ * \r
+ *             - After drawing an area and then clicking to start a new drag in IE 5.5 the rendered height \r
+ *        appears as the last height until the user drags, this appears to be the related to the error \r
+ *        that the forceReRender() method fixes for IE 6, i.e. IE 5.5 is not redrawing the box properly.\r
+ * \r
+ *             - Lack of CSS opacity support in Opera before version 9 mean we disable those style rules, these \r
+ *               could be fixed by using PNGs with transparency if Opera 8.5 support is high priority for you\r
+ * \r
+ *             - Marching ants keep reloading in IE <6 (not tested in IE7), it is a known issue in IE and I have \r
+ *        found no viable workarounds that can be included in the release. If this really is an issue for you\r
+ *        either try this post: http://mir.aculo.us/articles/2005/08/28/internet-explorer-and-ajax-image-caching-woes\r
+ *        or uncomment the 'FIX MARCHING ANTS IN IE' rules in the CSS file\r
+ *             \r
+ *             - Styling & borders on image, any CSS styling applied directly to the image itself (floats, borders, padding, margin, etc.) will \r
+ *               cause problems with the cropper. The use of a wrapper element to apply these styles to is recommended.\r
+ * \r
+ *             - overflow: auto or overflow: scroll on parent will cause cropper to burst out of parent in IE and Opera (maybe Mac browsers too)\r
+ *               I'm not sure why yet.\r
+ * \r
+ * Usage:\r
+ *             See Cropper.Img & Cropper.ImgWithPreview for usage details\r
+ * \r
+ * Changelog:\r
+ * v1.2.0 - 2006-10-30\r
+ *             + Added id to the preview image element using 'imgCrop_[originalImageID]'\r
+ *      * #00001 - Fixed bug: Doesn't account for scroll offsets\r
+ *      * #00009 - Fixed bug: Placing the cropper inside differently positioned elements causes incorrect co-ordinates and display\r
+ *      * #00013 - Fixed bug: I-bar cursor appears on drag plane\r
+ *      * #00014 - Fixed bug: If ID for image tag is not found in document script throws error\r
+ *      * Fixed bug with drag start co-ordinates if wrapper element has moved in browser (e.g. dragged to a new position)\r
+ *      * Fixed bug with drag start co-ordinates if image contained in a wrapper with scrolling - this may be buggy if image \r
+ *               has other ancestors with scrolling applied (except the body)\r
+ *      * #00015 - Fixed bug: When cropper removed and then reapplied onEndCrop callback gets called multiple times, solution suggestion from Bill Smith\r
+ *      * Various speed increases & code cleanup which meant improved performance in Mac - which allowed removal of different overlay methods for\r
+ *        IE and all other browsers, which led to a fix for:\r
+ *             * #00010 - Fixed bug: Select area doesn't adhere to image size when image resized using img attributes\r
+ *      - #00006 - Removed default behaviour of automatically setting a ratio when both min width & height passed, the ratioDimensions must be passed in\r
+ *             + #00005 - Added ability to set maximum crop dimensions, if both min & max set as the same value then we'll get a fixed cropper size on the axes as appropriate\r
+ *        and the resize handles will not be displayed as appropriate\r
+ *             * Switched keydown for keypress for moving select area with cursor keys (makes for nicer action) - doesn't appear to work in Safari\r
+ * \r
+ * v1.1.3 - 2006-08-21\r
+ *             * Fixed wrong cursor on western handle in CSS\r
+ *             + #00008 & #00003 - Added feature: Allow to set dimensions & position for cropper on load\r
+ *      * #00002 - Fixed bug: Pressing 'remove cropper' twice removes image in IE\r
+ * \r
+ * v1.1.2 - 2006-06-09\r
+ *             * Fixed bugs with ratios when GCD is low (patch submitted by Andy Skelton)\r
+ * \r
+ * v1.1.1 - 2006-06-03\r
+ *             * Fixed bug with rendering issues fix in IE 5.5\r
+ *             * Fixed bug with endCrop callback issues once cropper had been removed & reset in IE\r
+ * \r
+ * v1.1.0 - 2006-06-02\r
+ *             * Fixed bug with IE constantly trying to reload select area background image\r
+ *             * Applied more robust fix to Safari & IE rendering issues\r
+ *             + Added method to reset parameters - useful for when dynamically changing img cropper attached to\r
+ *             + Added method to remove cropper from image\r
+ * \r
+ * v1.0.0 - 2006-05-18 \r
+ *             + Initial verison\r
+ * \r
+ * \r
+ * Copyright (c) 2006, David Spurr (http://www.defusion.org.uk/)\r
+ * All rights reserved.\r
+ * \r
+ * \r
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\r
+ * \r
+ *     * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\r
+ *     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\r
+ *     * Neither the name of the David Spurr nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\r
+ * \r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ * \r
+ * http://www.opensource.org/licenses/bsd-license.php\r
+ * \r
+ * See scriptaculous.js for full scriptaculous licence\r
+ *\r
+ * Modified 2013/06/01 Zach P to change $() to $PR() for eliminating conflicts with jQuery\r
+ */\r
\r
+/**\r
+ * Extend the Draggable class to allow us to pass the rendering\r
+ * down to the Cropper object.\r
+ */\r
+var CropDraggable = Class.create();\r
+\r
+Object.extend( Object.extend( CropDraggable.prototype, Draggable.prototype), {\r
+       \r
+       initialize: function(element) {\r
+               this.options = Object.extend(\r
+                       {\r
+                               /**\r
+                                * The draw method to defer drawing to\r
+                                */\r
+                               drawMethod: function() {}\r
+                       }, \r
+                       arguments[1] || {}\r
+               );\r
+\r
+               this.element = $PR(element);\r
+\r
+               this.handle = this.element;\r
+\r
+               this.delta    = this.currentDelta();\r
+               this.dragging = false;   \r
+\r
+               this.eventMouseDown = this.initDrag.bindAsEventListener(this);\r
+               Event.observe(this.handle, "mousedown", this.eventMouseDown);\r
+\r
+               Draggables.register(this);\r
+       },\r
+       \r
+       /**\r
+        * Defers the drawing of the draggable to the supplied method\r
+        */\r
+       draw: function(point) {\r
+               var pos = Position.cumulativeOffset(this.element);\r
+               var d = this.currentDelta();\r
+               pos[0] -= d[0]; \r
+               pos[1] -= d[1];\r
+                               \r
+               var p = [0,1].map(function(i) { \r
+                       return (point[i]-pos[i]-this.offset[i]) \r
+               }.bind(this));\r
+                               \r
+               this.options.drawMethod( p );\r
+       }\r
+       \r
+});\r
+\r
+\r
+/**\r
+ * The Cropper object, this will attach itself to the provided image by wrapping it with \r
+ * the generated xHTML structure required by the cropper.\r
+ * \r
+ * Usage:\r
+ *     @param obj Image element to attach to\r
+ *     @param obj Optional options:\r
+ *             - ratioDim obj \r
+ *                     The pixel dimensions to apply as a restrictive ratio, with properties x & y\r
+ * \r
+ *             - minWidth int \r
+ *                     The minimum width for the select area in pixels\r
+ * \r
+ *             - minHeight     int \r
+ *                     The mimimum height for the select area in pixels\r
+ * \r
+ *             - maxWidth int\r
+ *                     The maximum width for the select areas in pixels (if both minWidth & maxWidth set to same the width of the cropper will be fixed)\r
+ * \r
+ *             - maxHeight int\r
+ *                     The maximum height for the select areas in pixels (if both minHeight & maxHeight set to same the height of the cropper will be fixed)\r
+ * \r
+ *             - displayOnInit int \r
+ *                     Whether to display the select area on initialisation, only used when providing minimum width & height or ratio\r
+ * \r
+ *             - onEndCrop func\r
+ *                     The callback function to provide the crop details to on end of a crop (see below)\r
+ * \r
+ *             - captureKeys boolean\r
+ *                     Whether to capture the keys for moving the select area, as these can cause some problems at the moment\r
+ * \r
+ *             - onloadCoords obj\r
+ *                     A coordinates object with properties x1, y1, x2 & y2; for the coordinates of the select area to display onload\r
+ *     \r
+ *----------------------------------------------\r
+ * \r
+ * The callback function provided via the onEndCrop option should accept the following parameters:\r
+ *             - coords obj\r
+ *                     The coordinates object with properties x1, y1, x2 & y2; for the coordinates of the select area\r
+ * \r
+ *             - dimensions obj\r
+ *                     The dimensions object with properites width & height; for the dimensions of the select area\r
+ *             \r
+ *\r
+ *             Example:\r
+ *                     function onEndCrop( coords, dimensions ) {\r
+ *                             $PR( 'x1' ).value       = coords.x1;\r
+ *                             $PR( 'y1' ).value       = coords.y1;\r
+ *                             $PR( 'x2' ).value       = coords.x2;\r
+ *                             $PR( 'y2' ).value       = coords.y2;\r
+ *                             $PR( 'width' ).value    = dimensions.width;\r
+ *                             $PR( 'height' ).value   = dimensions.height;\r
+ *                     }\r
+ * \r
+ */\r
+var Cropper = {};\r
+Cropper.Img = Class.create();\r
+Cropper.Img.prototype = {\r
+       \r
+       /**\r
+        * Initialises the class\r
+        * \r
+        * @access public\r
+        * @param obj Image element to attach to\r
+        * @param obj Options\r
+        * @return void\r
+        */\r
+       initialize: function(element, options) {\r
+               this.options = Object.extend(\r
+                       {\r
+                               /**\r
+                                * @var obj\r
+                                * The pixel dimensions to apply as a restrictive ratio\r
+                                */\r
+                               ratioDim: { x: 0, y: 0 },\r
+                               /**\r
+                                * @var int\r
+                                * The minimum pixel width, also used as restrictive ratio if min height passed too\r
+                                */\r
+                               minWidth:               0,\r
+                               /**\r
+                                * @var int\r
+                                * The minimum pixel height, also used as restrictive ratio if min width passed too\r
+                                */\r
+                               minHeight:              0,\r
+                               /**\r
+                                * @var boolean\r
+                                * Whether to display the select area on initialisation, only used when providing minimum width & height or ratio\r
+                                */\r
+                               displayOnInit:  false,\r
+                               /**\r
+                                * @var function\r
+                                * The call back function to pass the final values to\r
+                                */\r
+                               onEndCrop: Prototype.emptyFunction,\r
+                               /**\r
+                                * @var boolean\r
+                                * Whether to capture key presses or not\r
+                                */\r
+                               captureKeys: true,\r
+                               /**\r
+                                * @var obj Coordinate object x1, y1, x2, y2\r
+                                * The coordinates to optionally display the select area at onload\r
+                                */\r
+                               onloadCoords: null,\r
+                               /**\r
+                                * @var int\r
+                                * The maximum width for the select areas in pixels (if both minWidth & maxWidth set to same the width of the cropper will be fixed)\r
+                                */\r
+                               maxWidth: 0,\r
+                               /**\r
+                                * @var int\r
+                                * The maximum height for the select areas in pixels (if both minHeight & maxHeight set to same the height of the cropper will be fixed)\r
+                                */\r
+                               maxHeight: 0\r
+                       }, \r
+                       options || {}\r
+               );                              \r
+               /**\r
+                * @var obj\r
+                * The img node to attach to\r
+                */\r
+               this.img                        = $PR( element );\r
+               /**\r
+                * @var obj\r
+                * The x & y coordinates of the click point\r
+                */\r
+               this.clickCoords        = { x: 0, y: 0 };\r
+               /**\r
+                * @var boolean\r
+                * Whether the user is dragging\r
+                */\r
+               this.dragging           = false;\r
+               /**\r
+                * @var boolean\r
+                * Whether the user is resizing\r
+                */\r
+               this.resizing           = false;\r
+               /**\r
+                * @var boolean\r
+                * Whether the user is on a webKit browser\r
+                */\r
+               this.isWebKit           = /Konqueror|Safari|KHTML/.test( navigator.userAgent );\r
+               /**\r
+                * @var boolean\r
+                * Whether the user is on IE\r
+                */\r
+               this.isIE                       = /MSIE/.test( navigator.userAgent );\r
+               /**\r
+                * @var boolean\r
+                * Whether the user is on Opera below version 9\r
+                */\r
+               this.isOpera8           = /Opera\s[1-8]/.test( navigator.userAgent );\r
+               /**\r
+                * @var int\r
+                * The x ratio \r
+                */\r
+               this.ratioX                     = 0;\r
+               /**\r
+                * @var int\r
+                * The y ratio\r
+                */\r
+               this.ratioY                     = 0;\r
+               /**\r
+                * @var boolean\r
+                * Whether we've attached sucessfully\r
+                */\r
+               this.attached           = false;\r
+               /**\r
+                * @var boolean\r
+                * Whether we've got a fixed width (if minWidth EQ or GT maxWidth then we have a fixed width\r
+                * in the case of minWidth > maxWidth maxWidth wins as the fixed width)\r
+                */\r
+               this.fixedWidth         = ( this.options.maxWidth > 0 && ( this.options.minWidth >= this.options.maxWidth ) );\r
+               /**\r
+                * @var boolean\r
+                * Whether we've got a fixed height (if minHeight EQ or GT maxHeight then we have a fixed height\r
+                * in the case of minHeight > maxHeight maxHeight wins as the fixed height)\r
+                */\r
+               this.fixedHeight        = ( this.options.maxHeight > 0 && ( this.options.minHeight >= this.options.maxHeight ) );\r
+               \r
+               // quit if the image element doesn't exist\r
+               if( typeof this.img == 'undefined' ) return;\r
+                               \r
+               // include the stylesheet               \r
+               $A( document.getElementsByTagName( 'script' ) ).each( \r
+                       function(s) {\r
+                               if( s.src.match( /cropper\.js/ ) ) {\r
+                                       var path        = s.src.replace( /cropper\.js(.*)?/, '' );\r
+                                       // '<link rel="stylesheet" type="text/css" href="' + path + 'cropper.css" media="screen" />';\r
+                                       var style               = document.createElement( 'link' );\r
+                                       style.rel               = 'stylesheet';\r
+                                       style.type              = 'text/css';\r
+                                       style.href              = path + 'cropper.css';\r
+                                       style.media     = 'screen';\r
+                                       document.getElementsByTagName( 'head' )[0].appendChild( style );\r
+                               }\r
+               }\r
+           );   \r
+       \r
+               // calculate the ratio when neccessary\r
+               if( this.options.ratioDim.x > 0 && this.options.ratioDim.y > 0 ) {\r
+                       var gcd = this.getGCD( this.options.ratioDim.x, this.options.ratioDim.y );\r
+                       this.ratioX = this.options.ratioDim.x / gcd;\r
+                       this.ratioY = this.options.ratioDim.y / gcd;\r
+                       // dump( 'RATIO : ' + this.ratioX + ':' + this.ratioY + '\n' );\r
+               }\r
+                                                       \r
+               // initialise sub classes\r
+               this.subInitialize();\r
+\r
+               // only load the event observers etc. once the image is loaded\r
+               // this is done after the subInitialize() call just in case the sub class does anything\r
+               // that will affect the result of the call to onLoad()\r
+               if( this.img.complete || this.isWebKit ) this.onLoad(); // for some reason Safari seems to support img.complete but returns 'undefined' on the this.img object\r
+               else Event.observe( this.img, 'load', this.onLoad.bindAsEventListener( this) );         \r
+       },\r
+       \r
+       /**\r
+        * The Euclidean algorithm used to find the greatest common divisor\r
+        * \r
+        * @acces private\r
+        * @param int Value 1\r
+        * @param int Value 2\r
+        * @return int\r
+        */\r
+       getGCD : function( a , b ) {\r
+               if( b == 0 ) return a;\r
+               return this.getGCD(b, a % b );\r
+       },\r
+       \r
+       /**\r
+        * Attaches the cropper to the image once it has loaded\r
+        * \r
+        * @access private\r
+        * @return void\r
+        */\r
+       onLoad: function( ) {\r
+               /*\r
+                * Build the container and all related elements, will result in the following\r
+                *\r
+                * <div class="imgCrop_wrap">\r
+                *              <img ... this.img ... />\r
+                *              <div class="imgCrop_dragArea">\r
+                *                      <!-- the inner spans are only required for IE to stop it making the divs 1px high/wide -->\r
+                *                      <div class="imgCrop_overlay imageCrop_north"><span></span></div>\r
+                *                      <div class="imgCrop_overlay imageCrop_east"><span></span></div>\r
+                *                      <div class="imgCrop_overlay imageCrop_south"><span></span></div>\r
+                *                      <div class="imgCrop_overlay imageCrop_west"><span></span></div>\r
+                *                      <div class="imgCrop_selArea">\r
+                *                              <!-- marquees -->\r
+                *                              <!-- the inner spans are only required for IE to stop it making the divs 1px high/wide -->\r
+                *                              <div class="imgCrop_marqueeHoriz imgCrop_marqueeNorth"><span></span></div>\r
+                *                              <div class="imgCrop_marqueeVert imgCrop_marqueeEast"><span></span></div>\r
+                *                              <div class="imgCrop_marqueeHoriz imgCrop_marqueeSouth"><span></span></div>\r
+                *                              <div class="imgCrop_marqueeVert imgCrop_marqueeWest"><span></span></div>                        \r
+                *                              <!-- handles -->\r
+                *                              <div class="imgCrop_handle imgCrop_handleN"></div>\r
+                *                              <div class="imgCrop_handle imgCrop_handleNE"></div>\r
+                *                              <div class="imgCrop_handle imgCrop_handleE"></div>\r
+                *                              <div class="imgCrop_handle imgCrop_handleSE"></div>\r
+                *                              <div class="imgCrop_handle imgCrop_handleS"></div>\r
+                *                              <div class="imgCrop_handle imgCrop_handleSW"></div>\r
+                *                              <div class="imgCrop_handle imgCrop_handleW"></div>\r
+                *                              <div class="imgCrop_handle imgCrop_handleNW"></div>\r
+                *                              <div class="imgCrop_clickArea"></div>\r
+                *                      </div>  \r
+                *                      <div class="imgCrop_clickArea"></div>\r
+                *              </div>  \r
+                * </div>\r
+                */\r
+               var cNamePrefix = 'imgCrop_';\r
+               \r
+               // get the point to insert the container\r
+               var insertPoint = this.img.parentNode;\r
+               \r
+               // apply an extra class to the wrapper to fix Opera below version 9\r
+               var fixOperaClass = '';\r
+               if( this.isOpera8 ) fixOperaClass = ' opera8';\r
+               this.imgWrap = Builder.node( 'div', { 'class': cNamePrefix + 'wrap' + fixOperaClass } );\r
+               \r
+               this.north              = Builder.node( 'div', { 'class': cNamePrefix + 'overlay ' + cNamePrefix + 'north' }, [Builder.node( 'span' )] );\r
+               this.east               = Builder.node( 'div', { 'class': cNamePrefix + 'overlay ' + cNamePrefix + 'east' } , [Builder.node( 'span' )] );\r
+               this.south              = Builder.node( 'div', { 'class': cNamePrefix + 'overlay ' + cNamePrefix + 'south' }, [Builder.node( 'span' )] );\r
+               this.west               = Builder.node( 'div', { 'class': cNamePrefix + 'overlay ' + cNamePrefix + 'west' } , [Builder.node( 'span' )] );\r
+               \r
+               var overlays    = [ this.north, this.east, this.south, this.west ];\r
+\r
+               this.dragArea   = Builder.node( 'div', { 'class': cNamePrefix + 'dragArea' }, overlays );\r
+                                               \r
+               this.handleN    = Builder.node( 'div', { 'class': cNamePrefix + 'handle ' + cNamePrefix + 'handleN' } );\r
+               this.handleNE   = Builder.node( 'div', { 'class': cNamePrefix + 'handle ' + cNamePrefix + 'handleNE' } );\r
+               this.handleE    = Builder.node( 'div', { 'class': cNamePrefix + 'handle ' + cNamePrefix + 'handleE' } );\r
+               this.handleSE   = Builder.node( 'div', { 'class': cNamePrefix + 'handle ' + cNamePrefix + 'handleSE' } );\r
+               this.handleS    = Builder.node( 'div', { 'class': cNamePrefix + 'handle ' + cNamePrefix + 'handleS' } );\r
+               this.handleSW   = Builder.node( 'div', { 'class': cNamePrefix + 'handle ' + cNamePrefix + 'handleSW' } );\r
+               this.handleW    = Builder.node( 'div', { 'class': cNamePrefix + 'handle ' + cNamePrefix + 'handleW' } );\r
+               this.handleNW   = Builder.node( 'div', { 'class': cNamePrefix + 'handle ' + cNamePrefix + 'handleNW' } );\r
+                               \r
+               this.selArea    = Builder.node( 'div', { 'class': cNamePrefix + 'selArea' },\r
+                       [\r
+                               Builder.node( 'div', { 'class': cNamePrefix + 'marqueeHoriz ' + cNamePrefix + 'marqueeNorth' }, [Builder.node( 'span' )] ),\r
+                               Builder.node( 'div', { 'class': cNamePrefix + 'marqueeVert ' + cNamePrefix + 'marqueeEast' }  , [Builder.node( 'span' )] ),\r
+                               Builder.node( 'div', { 'class': cNamePrefix + 'marqueeHoriz ' + cNamePrefix + 'marqueeSouth' }, [Builder.node( 'span' )] ),\r
+                               Builder.node( 'div', { 'class': cNamePrefix + 'marqueeVert ' + cNamePrefix + 'marqueeWest' }  , [Builder.node( 'span' )] ),\r
+                               this.handleN,\r
+                               this.handleNE,\r
+                               this.handleE,\r
+                               this.handleSE,\r
+                               this.handleS,\r
+                               this.handleSW,\r
+                               this.handleW,\r
+                               this.handleNW,\r
+                               Builder.node( 'div', { 'class': cNamePrefix + 'clickArea' } )\r
+                       ]\r
+               );\r
+                               \r
+               this.imgWrap.appendChild( this.img );\r
+               this.imgWrap.appendChild( this.dragArea );\r
+               this.dragArea.appendChild( this.selArea );\r
+               this.dragArea.appendChild( Builder.node( 'div', { 'class': cNamePrefix + 'clickArea' } ) );\r
+\r
+               insertPoint.appendChild( this.imgWrap );\r
+\r
+               // add event observers\r
+               this.startDragBind      = this.startDrag.bindAsEventListener( this );\r
+               Event.observe( this.dragArea, 'mousedown', this.startDragBind );\r
+               \r
+               this.onDragBind         = this.onDrag.bindAsEventListener( this );\r
+               Event.observe( document, 'mousemove', this.onDragBind );\r
+               \r
+               this.endCropBind        = this.endCrop.bindAsEventListener( this );\r
+               Event.observe( document, 'mouseup', this.endCropBind );\r
+               \r
+               this.resizeBind         = this.startResize.bindAsEventListener( this );\r
+               this.handles = [ this.handleN, this.handleNE, this.handleE, this.handleSE, this.handleS, this.handleSW, this.handleW, this.handleNW ];\r
+               this.registerHandles( true );\r
+               \r
+               if( this.options.captureKeys ) {\r
+                       this.keysBind = this.handleKeys.bindAsEventListener( this );\r
+                       Event.observe( document, 'keypress', this.keysBind );\r
+               }\r
+\r
+               // attach the dragable to the select area\r
+               new CropDraggable( this.selArea, { drawMethod: this.moveArea.bindAsEventListener( this ) } );\r
+               \r
+               this.setParams();\r
+       },\r
+       \r
+       /**\r
+        * Manages adding or removing the handle event handler and hiding or displaying them as appropriate\r
+        * \r
+        * @access private\r
+        * @param boolean registration true = add, false = remove\r
+        * @return void\r
+        */\r
+       registerHandles: function( registration ) {     \r
+               for( var i = 0; i < this.handles.length; i++ ) {\r
+                       var handle = $PR( this.handles[i] );\r
+                       \r
+                       if( registration ) {\r
+                               var hideHandle  = false;        // whether to hide the handle\r
+                               \r
+                               // disable handles asappropriate if we've got fixed dimensions\r
+                               // if both dimensions are fixed we don't need to do much\r
+                               if( this.fixedWidth && this.fixedHeight ) hideHandle = true;\r
+                               else if( this.fixedWidth || this.fixedHeight ) {\r
+                                       // if one of the dimensions is fixed then just hide those handles\r
+                                       var isCornerHandle      = handle.className.match( /([S|N][E|W])$/ )\r
+                                       var isWidthHandle       = handle.className.match( /(E|W)$/ );\r
+                                       var isHeightHandle      = handle.className.match( /(N|S)$/ );\r
+                                       if( isCornerHandle ) hideHandle = true;\r
+                                       else if( this.fixedWidth && isWidthHandle ) hideHandle = true;\r
+                                       else if( this.fixedHeight && isHeightHandle ) hideHandle = true;\r
+                               }\r
+                               if( hideHandle ) handle.hide();\r
+                               else Event.observe( handle, 'mousedown', this.resizeBind );\r
+                       } else {\r
+                               handle.show();\r
+                               Event.stopObserving( handle, 'mousedown', this.resizeBind );\r
+                       }\r
+               }\r
+       },\r
+               \r
+       /**\r
+        * Sets up all the cropper parameters, this can be used to reset the cropper when dynamically\r
+        * changing the images\r
+        * \r
+        * @access private\r
+        * @return void\r
+        */\r
+       setParams: function() {\r
+               /**\r
+                * @var int\r
+                * The image width\r
+                */\r
+               this.imgW = this.img.width;\r
+               /**\r
+                * @var int\r
+                * The image height\r
+                */\r
+               this.imgH = this.img.height;                    \r
+\r
+               $PR( this.north ).setStyle( { height: 0 } );\r
+               $PR( this.east ).setStyle( { width: 0, height: 0 } );\r
+               $PR( this.south ).setStyle( { height: 0 } );\r
+               $PR( this.west ).setStyle( { width: 0, height: 0 } );\r
+               \r
+               // resize the container to fit the image\r
+               $PR( this.imgWrap ).setStyle( { 'width': this.imgW + 'px', 'height': this.imgH + 'px' } );\r
+               \r
+               // hide the select area\r
+               $PR( this.selArea ).hide();\r
+                                               \r
+               // setup the starting position of the select area\r
+               var startCoords = { x1: 0, y1: 0, x2: 0, y2: 0 };\r
+               var validCoordsSet = false;\r
+               \r
+               // display the select area \r
+               if( this.options.onloadCoords != null ) {\r
+                       // if we've being given some coordinates to \r
+                       startCoords = this.cloneCoords( this.options.onloadCoords );\r
+                       validCoordsSet = true;\r
+               } else if( this.options.ratioDim.x > 0 && this.options.ratioDim.y > 0 ) {\r
+                       // if there is a ratio limit applied and the then set it to initial ratio\r
+                       startCoords.x1 = Math.ceil( ( this.imgW - this.options.ratioDim.x ) / 2 );\r
+                       startCoords.y1 = Math.ceil( ( this.imgH - this.options.ratioDim.y ) / 2 );\r
+                       startCoords.x2 = startCoords.x1 + this.options.ratioDim.x;\r
+                       startCoords.y2 = startCoords.y1 + this.options.ratioDim.y;\r
+                       validCoordsSet = true;\r
+               }\r
+               \r
+               this.setAreaCoords( startCoords, false, false, 1 );\r
+               \r
+               if( this.options.displayOnInit && validCoordsSet ) {\r
+                       this.selArea.show();\r
+                       this.drawArea();\r
+                       this.endCrop();\r
+               }\r
+               \r
+               this.attached = true;\r
+       },\r
+       \r
+       /**\r
+        * Removes the cropper\r
+        * \r
+        * @access public\r
+        * @return void\r
+        */\r
+       remove: function() {\r
+               if( this.attached ) {\r
+                       this.attached = false;\r
+                       \r
+                       // remove the elements we inserted\r
+                       this.imgWrap.parentNode.insertBefore( this.img, this.imgWrap );\r
+                       this.imgWrap.parentNode.removeChild( this.imgWrap );\r
+                       \r
+                       // remove the event observers\r
+                       Event.stopObserving( this.dragArea, 'mousedown', this.startDragBind );\r
+                       Event.stopObserving( document, 'mousemove', this.onDragBind );          \r
+                       Event.stopObserving( document, 'mouseup', this.endCropBind );\r
+                       this.registerHandles( false );\r
+                       if( this.options.captureKeys ) Event.stopObserving( document, 'keypress', this.keysBind );\r
+               }\r
+       },\r
+       \r
+       /**\r
+        * Resets the cropper, can be used either after being removed or any time you wish\r
+        * \r
+        * @access public\r
+        * @return void\r
+        */\r
+       reset: function() {\r
+               if( !this.attached ) this.onLoad();\r
+               else this.setParams();\r
+               this.endCrop();\r
+       },\r
+       \r
+       /**\r
+        * Handles the key functionality, currently just using arrow keys to move, if the user\r
+        * presses shift then the area will move by 10 pixels\r
+        */\r
+       handleKeys: function( e ) {\r
+               var dir = { x: 0, y: 0 }; // direction to move it in & the amount in pixels\r
+               if( !this.dragging ) {\r
+                       \r
+                       // catch the arrow keys\r
+                       switch( e.keyCode ) {\r
+                               case( 37 ) : // left\r
+                                       dir.x = -1;\r
+                                       break;\r
+                               case( 38 ) : // up\r
+                                       dir.y = -1;\r
+                                       break;\r
+                               case( 39 ) : // right\r
+                                       dir.x = 1;\r
+                                       break\r
+                               case( 40 ) : // down\r
+                                       dir.y = 1;\r
+                                       break;\r
+                       }\r
+                       \r
+                       if( dir.x != 0 || dir.y != 0 ) {\r
+                               // if shift is pressed then move by 10 pixels\r
+                               if( e.shiftKey ) {\r
+                                       dir.x *= 10;\r
+                                       dir.y *= 10;\r
+                               }\r
+                               \r
+                               this.moveArea( [ this.areaCoords.x1 + dir.x, this.areaCoords.y1 + dir.y ] );\r
+                               Event.stop( e ); \r
+                       }\r
+               }\r
+       },\r
+       \r
+       /**\r
+        * Calculates the width from the areaCoords\r
+        * \r
+        * @access private\r
+        * @return int\r
+        */\r
+       calcW: function() {\r
+               return (this.areaCoords.x2 - this.areaCoords.x1)\r
+       },\r
+       \r
+       /**\r
+        * Calculates the height from the areaCoords\r
+        * \r
+        * @access private\r
+        * @return int\r
+        */\r
+       calcH: function() {\r
+               return (this.areaCoords.y2 - this.areaCoords.y1)\r
+       },\r
+       \r
+       /**\r
+        * Moves the select area to the supplied point (assumes the point is x1 & y1 of the select area)\r
+        * \r
+        * @access public\r
+        * @param array Point for x1 & y1 to move select area to\r
+        * @return void\r
+        */\r
+       moveArea: function( point ) {\r
+               // dump( 'moveArea        : ' + point[0] + ',' + point[1] + ',' + ( point[0] + ( this.areaCoords.x2 - this.areaCoords.x1 ) ) + ',' + ( point[1] + ( this.areaCoords.y2 - this.areaCoords.y1 ) ) + '\n' );\r
+               this.setAreaCoords( \r
+                       {\r
+                               x1: point[0], \r
+                               y1: point[1],\r
+                               x2: point[0] + this.calcW(),\r
+                               y2: point[1] + this.calcH()\r
+                       },\r
+                       true,\r
+                       false\r
+               );\r
+               this.drawArea();\r
+       },\r
+\r
+       /**\r
+        * Clones a co-ordinates object, stops problems with handling them by reference\r
+        * \r
+        * @access private\r
+        * @param obj Coordinate object x1, y1, x2, y2\r
+        * @return obj Coordinate object x1, y1, x2, y2\r
+        */\r
+       cloneCoords: function( coords ) {\r
+               return { x1: coords.x1, y1: coords.y1, x2: coords.x2, y2: coords.y2 };\r
+       },\r
+\r
+       /**\r
+        * Sets the select coords to those provided but ensures they don't go\r
+        * outside the bounding box\r
+        * \r
+        * @access private\r
+        * @param obj Coordinates x1, y1, x2, y2\r
+        * @param boolean Whether this is a move\r
+        * @param boolean Whether to apply squaring\r
+        * @param obj Direction of mouse along both axis x, y ( -1 = negative, 1 = positive ) only required when moving etc.\r
+        * @param string The current resize handle || null\r
+        * @return void\r
+        */\r
+       setAreaCoords: function( coords, moving, square, direction, resizeHandle ) {\r
+               // dump( 'setAreaCoords (in) : ' + coords.x1 + ',' + coords.y1 + ',' + coords.x2 + ',' + coords.y2 );\r
+               if( moving ) {\r
+                       // if moving\r
+                       var targW = coords.x2 - coords.x1;\r
+                       var targH = coords.y2 - coords.y1;\r
+                       \r
+                       // ensure we're within the bounds\r
+                       if( coords.x1 < 0 ) {\r
+                               coords.x1 = 0;\r
+                               coords.x2 = targW;\r
+                       }\r
+                       if( coords.y1 < 0 ) {\r
+                               coords.y1 = 0;\r
+                               coords.y2 = targH;\r
+                       }\r
+                       if( coords.x2 > this.imgW ) {\r
+                               coords.x2 = this.imgW;\r
+                               coords.x1 = this.imgW - targW;\r
+                       }\r
+                       if( coords.y2 > this.imgH ) {\r
+                               coords.y2 = this.imgH;\r
+                               coords.y1 = this.imgH - targH;\r
+                       }                       \r
+               } else {\r
+                       // ensure we're within the bounds\r
+                       if( coords.x1 < 0 ) coords.x1 = 0;\r
+                       if( coords.y1 < 0 ) coords.y1 = 0;\r
+                       if( coords.x2 > this.imgW ) coords.x2 = this.imgW;\r
+                       if( coords.y2 > this.imgH ) coords.y2 = this.imgH;\r
+                       \r
+                       // This is passed as null in onload\r
+                       if( direction != null ) {\r
+                                                               \r
+                               // apply the ratio or squaring where appropriate\r
+                               if( this.ratioX > 0 ) this.applyRatio( coords, { x: this.ratioX, y: this.ratioY }, direction, resizeHandle );\r
+                               else if( square ) this.applyRatio( coords, { x: 1, y: 1 }, direction, resizeHandle );\r
+                                                                               \r
+                               var mins = [ this.options.minWidth, this.options.minHeight ]; // minimum dimensions [x,y]                       \r
+                               var maxs = [ this.options.maxWidth, this.options.maxHeight ]; // maximum dimensions [x,y]\r
+               \r
+                               // apply dimensions where appropriate\r
+                               if( mins[0] > 0 || mins[1] > 0 || maxs[0] > 0 || maxs[1] > 0) {\r
+                               \r
+                                       var coordsTransX        = { a1: coords.x1, a2: coords.x2 };\r
+                                       var coordsTransY        = { a1: coords.y1, a2: coords.y2 };\r
+                                       var boundsX                     = { min: 0, max: this.imgW };\r
+                                       var boundsY                     = { min: 0, max: this.imgH };\r
+                                       \r
+                                       // handle squaring properly on single axis minimum dimensions\r
+                                       if( (mins[0] != 0 || mins[1] != 0) && square ) {\r
+                                               if( mins[0] > 0 ) mins[1] = mins[0];\r
+                                               else if( mins[1] > 0 ) mins[0] = mins[1];\r
+                                       }\r
+                                       \r
+                                       if( (maxs[0] != 0 || maxs[0] != 0) && square ) {\r
+                                               // if we have a max x value & it is less than the max y value then we set the y max to the max x (so we don't go over the minimum maximum of one of the axes - if that makes sense)\r
+                                               if( maxs[0] > 0 && maxs[0] <= maxs[1] ) maxs[1] = maxs[0];\r
+                                               else if( maxs[1] > 0 && maxs[1] <= maxs[0] ) maxs[0] = maxs[1];\r
+                                       }\r
+                                       \r
+                                       if( mins[0] > 0 ) this.applyDimRestriction( coordsTransX, mins[0], direction.x, boundsX, 'min' );\r
+                                       if( mins[1] > 1 ) this.applyDimRestriction( coordsTransY, mins[1], direction.y, boundsY, 'min' );\r
+                                       \r
+                                       if( maxs[0] > 0 ) this.applyDimRestriction( coordsTransX, maxs[0], direction.x, boundsX, 'max' );\r
+                                       if( maxs[1] > 1 ) this.applyDimRestriction( coordsTransY, maxs[1], direction.y, boundsY, 'max' );\r
+                                       \r
+                                       coords = { x1: coordsTransX.a1, y1: coordsTransY.a1, x2: coordsTransX.a2, y2: coordsTransY.a2 };\r
+                               }\r
+                               \r
+                       }\r
+               }\r
+               \r
+               // dump( 'setAreaCoords (out) : ' + coords.x1 + ',' + coords.y1 + ',' + coords.x2 + ',' + coords.y2 + '\n' );\r
+               this.areaCoords = coords;\r
+       },\r
+       \r
+       /**\r
+        * Applies the supplied dimension restriction to the supplied coordinates along a single axis\r
+        * \r
+        * @access private\r
+        * @param obj Single axis coordinates, a1, a2 (e.g. for the x axis a1 = x1 & a2 = x2)\r
+        * @param int The restriction value\r
+        * @param int The direction ( -1 = negative, 1 = positive )\r
+        * @param obj The bounds of the image ( for this axis )\r
+        * @param string The dimension restriction type ( 'min' | 'max' )\r
+        * @return void\r
+        */\r
+       applyDimRestriction: function( coords, val, direction, bounds, type ) {\r
+               var check;\r
+               if( type == 'min' ) check = ( ( coords.a2 - coords.a1 ) < val );\r
+               else check = ( ( coords.a2 - coords.a1 ) > val );\r
+               if( check ) {\r
+                       if( direction == 1 ) coords.a2 = coords.a1 + val;\r
+                       else coords.a1 = coords.a2 - val;\r
+                       \r
+                       // make sure we're still in the bounds (not too pretty for the user, but needed)\r
+                       if( coords.a1 < bounds.min ) {\r
+                               coords.a1 = bounds.min;\r
+                               coords.a2 = val;\r
+                       } else if( coords.a2 > bounds.max ) {\r
+                               coords.a1 = bounds.max - val;\r
+                               coords.a2 = bounds.max;\r
+                       }\r
+               }\r
+       },\r
+               \r
+       /**\r
+        * Applies the supplied ratio to the supplied coordinates\r
+        * \r
+        * @access private\r
+        * @param obj Coordinates, x1, y1, x2, y2\r
+        * @param obj Ratio, x, y\r
+        * @param obj Direction of mouse, x & y : -1 == negative 1 == positive\r
+        * @param string The current resize handle || null\r
+        * @return void\r
+        */\r
+       applyRatio : function( coords, ratio, direction, resizeHandle ) {\r
+               // dump( 'direction.y : ' + direction.y + '\n');\r
+               var newCoords;\r
+               if( resizeHandle == 'N' || resizeHandle == 'S' ) {\r
+                       // dump( 'north south \n');\r
+                       // if moving on either the lone north & south handles apply the ratio on the y axis\r
+                       newCoords = this.applyRatioToAxis( \r
+                               { a1: coords.y1, b1: coords.x1, a2: coords.y2, b2: coords.x2 },\r
+                               { a: ratio.y, b: ratio.x },\r
+                               { a: direction.y, b: direction.x },\r
+                               { min: 0, max: this.imgW }\r
+                       );\r
+                       coords.x1 = newCoords.b1;\r
+                       coords.y1 = newCoords.a1;\r
+                       coords.x2 = newCoords.b2;\r
+                       coords.y2 = newCoords.a2;\r
+               } else {\r
+                       // otherwise deal with it as if we're applying the ratio on the x axis\r
+                       newCoords = this.applyRatioToAxis( \r
+                               { a1: coords.x1, b1: coords.y1, a2: coords.x2, b2: coords.y2 },\r
+                               { a: ratio.x, b: ratio.y },\r
+                               { a: direction.x, b: direction.y },\r
+                               { min: 0, max: this.imgH }\r
+                       );\r
+                       coords.x1 = newCoords.a1;\r
+                       coords.y1 = newCoords.b1;\r
+                       coords.x2 = newCoords.a2;\r
+                       coords.y2 = newCoords.b2;\r
+               }\r
+               \r
+       },\r
+       \r
+       /**\r
+        * Applies the provided ratio to the provided coordinates based on provided direction & bounds,\r
+        * use to encapsulate functionality to make it easy to apply to either axis. This is probably\r
+        * quite hard to visualise so see the x axis example within applyRatio()\r
+        * \r
+        * Example in parameter details & comments is for requesting applying ratio to x axis.\r
+        * \r
+        * @access private\r
+        * @param obj Coords object (a1, b1, a2, b2) where a = x & b = y in example\r
+        * @param obj Ratio object (a, b) where a = x & b = y in example\r
+        * @param obj Direction object (a, b) where a = x & b = y in example\r
+        * @param obj Bounds (min, max)\r
+        * @return obj Coords object (a1, b1, a2, b2) where a = x & b = y in example\r
+        */\r
+       applyRatioToAxis: function( coords, ratio, direction, bounds ) {\r
+               var newCoords = Object.extend( coords, {} );\r
+               var calcDimA = newCoords.a2 - newCoords.a1;                     // calculate dimension a (e.g. width)\r
+               var targDimB = Math.floor( calcDimA * ratio.b / ratio.a );      // the target dimension b (e.g. height)\r
+               var targB;                                                                                      // to hold target b (e.g. y value)\r
+               var targDimA;                                           // to hold target dimension a (e.g. width)\r
+               var calcDimB = null;                                                            // to hold calculated dimension b (e.g. height)\r
+               \r
+               // dump( 'newCoords[0]: ' + newCoords.a1 + ',' + newCoords.b1 + ','+ newCoords.a2 + ',' + newCoords.b2 + '\n');\r
+                               \r
+               if( direction.b == 1 ) {                                                        // if travelling in a positive direction\r
+                       // make sure we're not going out of bounds\r
+                       targB = newCoords.b1 + targDimB;\r
+                       if( targB > bounds.max ) {\r
+                               targB = bounds.max;\r
+                               calcDimB = targB - newCoords.b1;                        // calcuate dimension b (e.g. height)\r
+                       }\r
+                       \r
+                       newCoords.b2 = targB;\r
+               } else {                                                                                        // if travelling in a negative direction\r
+                       // make sure we're not going out of bounds\r
+                       targB = newCoords.b2 - targDimB;\r
+                       if( targB < bounds.min ) {\r
+                               targB = bounds.min;\r
+                               calcDimB = targB + newCoords.b2;                        // calcuate dimension b (e.g. height)\r
+                       }\r
+                       newCoords.b1 = targB;\r
+               }\r
+               \r
+               // dump( 'newCoords[1]: ' + newCoords.a1 + ',' + newCoords.b1 + ','+ newCoords.a2 + ',' + newCoords.b2 + '\n');\r
+                       \r
+               // apply the calculated dimensions\r
+               if( calcDimB != null ) {\r
+                       targDimA = Math.floor( calcDimB * ratio.a / ratio.b );\r
+                       \r
+                       if( direction.a == 1 ) newCoords.a2 = newCoords.a1 + targDimA;\r
+                       else newCoords.a1 = newCoords.a1 = newCoords.a2 - targDimA;\r
+               }\r
+               \r
+               // dump( 'newCoords[2]: ' + newCoords.a1 + ',' + newCoords.b1 + ','+ newCoords.a2 + ',' + newCoords.b2 + '\n');\r
+                       \r
+               return newCoords;\r
+       },\r
+       \r
+       /**\r
+        * Draws the select area\r
+        * \r
+        * @access private\r
+        * @return void\r
+        */\r
+       drawArea: function( ) { \r
+               /*\r
+                * NOTE: I'm not using the Element.setStyle() shortcut as they make it \r
+                * quite sluggish on Mac based browsers\r
+                */\r
+               // dump( 'drawArea        : ' + this.areaCoords.x1 + ',' + this.areaCoords.y1 + ',' + this.areaCoords.x2 + ',' + this.areaCoords.y2 + '\n' );\r
+               var areaWidth     = this.calcW();\r
+               var areaHeight    = this.calcH();\r
+               \r
+               /*\r
+                * Calculate all the style strings before we use them, allows reuse & produces quicker\r
+                * rendering (especially noticable in Mac based browsers)\r
+                */\r
+               var px = 'px';\r
+               var params = [\r
+                       this.areaCoords.x1 + px,        // the left of the selArea\r
+                       this.areaCoords.y1 + px,                // the top of the selArea\r
+                       areaWidth + px,                                 // width of the selArea\r
+                       areaHeight + px,                                        // height of the selArea\r
+                       this.areaCoords.x2 + px,                // bottom of the selArea\r
+                       this.areaCoords.y2 + px,                // right of the selArea\r
+                       (this.img.width - this.areaCoords.x2) + px,     // right edge of selArea\r
+                       (this.img.height - this.areaCoords.y2) + px     // bottom edge of selArea\r
+               ];\r
+                               \r
+               // do the select area\r
+               var areaStyle                           = this.selArea.style;\r
+               areaStyle.left                          = params[0];\r
+               areaStyle.top                           = params[1];\r
+               areaStyle.width                         = params[2];\r
+               areaStyle.height                        = params[3];\r
+                               \r
+               // position the north, east, south & west handles\r
+               var horizHandlePos = Math.ceil( (areaWidth - 6) / 2 ) + px;\r
+               var vertHandlePos = Math.ceil( (areaHeight - 6) / 2 ) + px;\r
+               \r
+               this.handleN.style.left         = horizHandlePos;\r
+               this.handleE.style.top          = vertHandlePos;\r
+               this.handleS.style.left         = horizHandlePos;\r
+               this.handleW.style.top          = vertHandlePos;\r
+               \r
+               // draw the four overlays\r
+               this.north.style.height         = params[1];\r
+               \r
+               var eastStyle                           = this.east.style;\r
+               eastStyle.top                           = params[1];\r
+               eastStyle.height                        = params[3];\r
+               eastStyle.left                          = params[4];\r
+           eastStyle.width                             = params[6];\r
+          \r
+               var southStyle                          = this.south.style;\r
+               southStyle.top                          = params[5];\r
+               southStyle.height                       = params[7];\r
+          \r
+           var westStyle                       = this.west.style;\r
+           westStyle.top                               = params[1];\r
+           westStyle.height                    = params[3];\r
+               westStyle.width                         = params[0];\r
+               \r
+               // call the draw method on sub classes\r
+               this.subDrawArea();\r
+               \r
+               this.forceReRender();\r
+       },\r
+       \r
+       /**\r
+        * Force the re-rendering of the selArea element which fixes rendering issues in Safari \r
+        * & IE PC, especially evident when re-sizing perfectly vertical using any of the south handles\r
+        * \r
+        * @access private\r
+        * @return void\r
+        */\r
+       forceReRender: function() {\r
+               if( this.isIE || this.isWebKit) {\r
+                       var n = document.createTextNode(' ');\r
+                       var d,el,fixEL,i;\r
+               \r
+                       if( this.isIE ) fixEl = this.selArea;\r
+                       else if( this.isWebKit ) {\r
+                               fixEl = document.getElementsByClassName( 'imgCrop_marqueeSouth', this.imgWrap )[0];\r
+                               /* we have to be a bit more forceful for Safari, otherwise the the marquee &\r
+                                * the south handles still don't move\r
+                                */ \r
+                               d = Builder.node( 'div', '' );\r
+                               d.style.visibility = 'hidden';\r
+                               \r
+                               var classList = ['SE','S','SW'];\r
+                               for( i = 0; i < classList.length; i++ ) {\r
+                                       el = document.getElementsByClassName( 'imgCrop_handle' + classList[i], this.selArea )[0];\r
+                                       if( el.childNodes.length ) el.removeChild( el.childNodes[0] );\r
+                                       el.appendChild(d);\r
+                               }\r
+                       }\r
+                       fixEl.appendChild(n);\r
+                       fixEl.removeChild(n);\r
+               }\r
+       },\r
+       \r
+       /**\r
+        * Starts the resize\r
+        * \r
+        * @access private\r
+        * @param obj Event\r
+        * @return void\r
+        */\r
+       startResize: function( e ) {\r
+               this.startCoords = this.cloneCoords( this.areaCoords );\r
+               \r
+               this.resizing = true;\r
+               this.resizeHandle = Event.element( e ).classNames().toString().replace(/([^N|NE|E|SE|S|SW|W|NW])+/, '');\r
+               // dump( 'this.resizeHandle : ' + this.resizeHandle + '\n' );\r
+               Event.stop( e );\r
+       },\r
+       \r
+       /**\r
+        * Starts the drag\r
+        * \r
+        * @access private\r
+        * @param obj Event\r
+        * @return void\r
+        */\r
+       startDrag: function( e ) {      \r
+               this.selArea.show();\r
+               this.clickCoords = this.getCurPos( e );\r
+       \r
+       this.setAreaCoords( { x1: this.clickCoords.x, y1: this.clickCoords.y, x2: this.clickCoords.x, y2: this.clickCoords.y }, false, false, null );\r
+       \r
+       this.dragging = true;\r
+       this.onDrag( e ); // incase the user just clicks once after already making a selection\r
+       Event.stop( e );\r
+       },\r
+       \r
+       /**\r
+        * Gets the current cursor position relative to the image\r
+        * \r
+        * @access private\r
+        * @param obj Event\r
+        * @return obj x,y pixels of the cursor\r
+        */\r
+       getCurPos: function( e ) {\r
+               // get the offsets for the wrapper within the document\r
+               var el = this.imgWrap, wrapOffsets = Position.cumulativeOffset( el );\r
+               // remove any scrolling that is applied to the wrapper (this may be buggy) - don't count the scroll on the body as that won't affect us\r
+               while( el.nodeName != 'BODY' ) {\r
+                       wrapOffsets[1] -= el.scrollTop  || 0;\r
+                       wrapOffsets[0] -= el.scrollLeft || 0;\r
+                       el = el.parentNode;\r
+           }           \r
+               return curPos = { \r
+                       x: Event.pointerX(e) - wrapOffsets[0],\r
+                       y: Event.pointerY(e) - wrapOffsets[1]\r
+               }\r
+       },\r
+       \r
+       /**\r
+        * Performs the drag for both resize & inital draw dragging\r
+        * \r
+        * @access private\r
+        * @param obj Event\r
+        * @return void\r
+        */\r
+       onDrag: function( e ) {\r
+               if( this.dragging || this.resizing ) {  \r
+               \r
+                       var resizeHandle = null;\r
+                       var curPos = this.getCurPos( e );                       \r
+                       var newCoords = this.cloneCoords( this.areaCoords );\r
+                       var direction = { x: 1, y: 1 };\r
+                                               \r
+                   if( this.dragging ) {\r
+                       if( curPos.x < this.clickCoords.x ) direction.x = -1;\r
+                       if( curPos.y < this.clickCoords.y ) direction.y = -1;\r
+                       \r
+                               this.transformCoords( curPos.x, this.clickCoords.x, newCoords, 'x' );\r
+                               this.transformCoords( curPos.y, this.clickCoords.y, newCoords, 'y' );\r
+                       } else if( this.resizing ) {\r
+                               resizeHandle = this.resizeHandle;                       \r
+                               // do x movements first\r
+                               if( resizeHandle.match(/E/) ) {\r
+                                       // if we're moving an east handle\r
+                                       this.transformCoords( curPos.x, this.startCoords.x1, newCoords, 'x' );  \r
+                                       if( curPos.x < this.startCoords.x1 ) direction.x = -1;\r
+                               } else if( resizeHandle.match(/W/) ) {\r
+                                       // if we're moving an west handle\r
+                                       this.transformCoords( curPos.x, this.startCoords.x2, newCoords, 'x' );\r
+                                       if( curPos.x < this.startCoords.x2 ) direction.x = -1;\r
+                               }\r
+                                                                       \r
+                               // do y movements second\r
+                               if( resizeHandle.match(/N/) ) {\r
+                                       // if we're moving an north handle      \r
+                                       this.transformCoords( curPos.y, this.startCoords.y2, newCoords, 'y' );\r
+                                       if( curPos.y < this.startCoords.y2 ) direction.y = -1;\r
+                               } else if( resizeHandle.match(/S/) ) {\r
+                                       // if we're moving an south handle\r
+                                       this.transformCoords( curPos.y, this.startCoords.y1, newCoords, 'y' );  \r
+                                       if( curPos.y < this.startCoords.y1 ) direction.y = -1;\r
+                               }       \r
+                                                       \r
+                       }\r
+               \r
+                       this.setAreaCoords( newCoords, false, e.shiftKey, direction, resizeHandle );\r
+                       this.drawArea();\r
+                       Event.stop( e ); // stop the default event (selecting images & text) in Safari & IE PC\r
+               }\r
+       },\r
+       \r
+       /**\r
+        * Applies the appropriate transform to supplied co-ordinates, on the\r
+        * defined axis, depending on the relationship of the supplied values\r
+        * \r
+        * @access private\r
+        * @param int Current value of pointer\r
+        * @param int Base value to compare current pointer val to\r
+        * @param obj Coordinates to apply transformation on x1, x2, y1, y2\r
+        * @param string Axis to apply transformation on 'x' || 'y'\r
+        * @return void\r
+        */\r
+       transformCoords : function( curVal, baseVal, coords, axis ) {\r
+               var newVals = [ curVal, baseVal ];\r
+               if( curVal > baseVal ) newVals.reverse();\r
+               coords[ axis + '1' ] = newVals[0];\r
+               coords[ axis + '2' ] = newVals[1];              \r
+       },\r
+       \r
+       /**\r
+        * Ends the crop & passes the values of the select area on to the appropriate \r
+        * callback function on completion of a crop\r
+        * \r
+        * @access private\r
+        * @return void\r
+        */\r
+       endCrop : function() {\r
+               this.dragging = false;\r
+               this.resizing = false;\r
+               \r
+               this.options.onEndCrop(\r
+                       this.areaCoords,\r
+                       {\r
+                               width: this.calcW(), \r
+                               height: this.calcH() \r
+                       }\r
+               );\r
+       },\r
+       \r
+       /**\r
+        * Abstract method called on the end of initialization\r
+        * \r
+        * @access private\r
+        * @abstract\r
+        * @return void\r
+        */\r
+       subInitialize: function() {},\r
+       \r
+       /**\r
+        * Abstract method called on the end of drawArea()\r
+        * \r
+        * @access private\r
+        * @abstract\r
+        * @return void\r
+        */\r
+       subDrawArea: function() {}\r
+};\r
+\r
+\r
+\r
+\r
+/**\r
+ * Extend the Cropper.Img class to allow for presentation of a preview image of the resulting crop,\r
+ * the option for displayOnInit is always overridden to true when displaying a preview image\r
+ * \r
+ * Usage:\r
+ *     @param obj Image element to attach to\r
+ *     @param obj Optional options:\r
+ *             - see Cropper.Img for base options\r
+ *             \r
+ *             - previewWrap obj\r
+ *                     HTML element that will be used as a container for the preview image             \r
+ */\r
+Cropper.ImgWithPreview = Class.create();\r
+\r
+Object.extend( Object.extend( Cropper.ImgWithPreview.prototype, Cropper.Img.prototype ), {\r
+       \r
+       /**\r
+        * Implements the abstract method from Cropper.Img to initialize preview image settings.\r
+        * Will only attach a preview image is the previewWrap element is defined and the minWidth\r
+        * & minHeight options are set.\r
+        * \r
+        * @see Croper.Img.subInitialize\r
+        */\r
+       subInitialize: function() {\r
+               /**\r
+                * Whether or not we've attached a preview image\r
+                * @var boolean\r
+                */\r
+               this.hasPreviewImg = false;\r
+               if( typeof(this.options.previewWrap) != 'undefined' \r
+                       && this.options.minWidth > 0 \r
+                       && this.options.minHeight > 0\r
+               ) {\r
+                       /**\r
+                        * The preview image wrapper element\r
+                        * @var obj HTML element\r
+                        */\r
+                       this.previewWrap        = $PR( this.options.previewWrap );\r
+                       /**\r
+                        * The preview image element\r
+                        * @var obj HTML IMG element\r
+                        */\r
+                       this.previewImg         = this.img.cloneNode( false );\r
+                       // set the ID of the preview image to be unique\r
+                       this.previewImg.id      = 'imgCrop_' + this.previewImg.id;\r
+                       \r
+                                               \r
+                       // set the displayOnInit option to true so we display the select area at the same time as the thumbnail\r
+                       this.options.displayOnInit = true;\r
+\r
+                       this.hasPreviewImg      = true;\r
+                       \r
+                       this.previewWrap.addClassName( 'imgCrop_previewWrap' );\r
+                       \r
+                       this.previewWrap.setStyle(\r
+                        { \r
+                               width: this.options.minWidth + 'px',\r
+                               height: this.options.minHeight + 'px'\r
+                        }\r
+                       );\r
+                       \r
+                       this.previewWrap.appendChild( this.previewImg );\r
+               }\r
+       },\r
+       \r
+       /**\r
+        * Implements the abstract method from Cropper.Img to draw the preview image\r
+        * \r
+        * @see Croper.Img.subDrawArea\r
+        */\r
+       subDrawArea: function() {\r
+               if( this.hasPreviewImg ) {\r
+                       // get the ratio of the select area to the src image\r
+                       var calcWidth = this.calcW();\r
+                       var calcHeight = this.calcH();\r
+                       // ratios for the dimensions of the preview image\r
+                       var dimRatio = { \r
+                               x: this.imgW / calcWidth, \r
+                               y: this.imgH / calcHeight \r
+                       }; \r
+                       //ratios for the positions within the preview\r
+                       var posRatio = { \r
+                               x: calcWidth / this.options.minWidth, \r
+                               y: calcHeight / this.options.minHeight \r
+                       };\r
+                       \r
+                       // setting the positions in an obj before apply styles for rendering speed increase\r
+                       var calcPos     = {\r
+                               w: Math.ceil( this.options.minWidth * dimRatio.x ) + 'px',\r
+                               h: Math.ceil( this.options.minHeight * dimRatio.y ) + 'px',\r
+                               x: '-' + Math.ceil( this.areaCoords.x1 / posRatio.x )  + 'px',\r
+                               y: '-' + Math.ceil( this.areaCoords.y1 / posRatio.y ) + 'px'\r
+                       }\r
+                       \r
+                       var previewStyle        = this.previewImg.style;\r
+                       previewStyle.width      = calcPos.w;\r
+                       previewStyle.height     = calcPos.h;\r
+                       previewStyle.left       = calcPos.x;\r
+                       previewStyle.top        = calcPos.y;\r
+               }\r
+       }\r
+       \r
+});\r
diff --git a/view/js/cropper/lib/builder.js b/view/js/cropper/lib/builder.js
new file mode 100644 (file)
index 0000000..5b15ba9
--- /dev/null
@@ -0,0 +1,101 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//
+// See scriptaculous.js for full license.
+
+var Builder = {
+  NODEMAP: {
+    AREA: 'map',
+    CAPTION: 'table',
+    COL: 'table',
+    COLGROUP: 'table',
+    LEGEND: 'fieldset',
+    OPTGROUP: 'select',
+    OPTION: 'select',
+    PARAM: 'object',
+    TBODY: 'table',
+    TD: 'table',
+    TFOOT: 'table',
+    TH: 'table',
+    THEAD: 'table',
+    TR: 'table'
+  },
+  // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken,
+  //       due to a Firefox bug
+  node: function(elementName) {
+    elementName = elementName.toUpperCase();
+    
+    // try innerHTML approach
+    var parentTag = this.NODEMAP[elementName] || 'div';
+    var parentElement = document.createElement(parentTag);
+    try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
+      parentElement.innerHTML = "<" + elementName + "></" + elementName + ">";
+    } catch(e) {}
+    var element = parentElement.firstChild || null;
+      
+    // see if browser added wrapping tags
+    if(element && (element.tagName != elementName))
+      element = element.getElementsByTagName(elementName)[0];
+    
+    // fallback to createElement approach
+    if(!element) element = document.createElement(elementName);
+    
+    // abort if nothing could be created
+    if(!element) return;
+
+    // attributes (or text)
+    if(arguments[1])
+      if(this._isStringOrNumber(arguments[1]) ||
+        (arguments[1] instanceof Array)) {
+          this._children(element, arguments[1]);
+        } else {
+          var attrs = this._attributes(arguments[1]);
+          if(attrs.length) {
+            try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
+              parentElement.innerHTML = "<" +elementName + " " +
+                attrs + "></" + elementName + ">";
+            } catch(e) {}
+            element = parentElement.firstChild || null;
+            // workaround firefox 1.0.X bug
+            if(!element) {
+              element = document.createElement(elementName);
+              for(attr in arguments[1]) 
+                element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
+            }
+            if(element.tagName != elementName)
+              element = parentElement.getElementsByTagName(elementName)[0];
+            }
+        } 
+
+    // text, or array of children
+    if(arguments[2])
+      this._children(element, arguments[2]);
+
+     return element;
+  },
+  _text: function(text) {
+     return document.createTextNode(text);
+  },
+  _attributes: function(attributes) {
+    var attrs = [];
+    for(attribute in attributes)
+      attrs.push((attribute=='className' ? 'class' : attribute) +
+          '="' + attributes[attribute].toString().escapeHTML() + '"');
+    return attrs.join(" ");
+  },
+  _children: function(element, children) {
+    if(typeof children=='object') { // array can hold nodes and text
+      children.flatten().each( function(e) {
+        if(typeof e=='object')
+          element.appendChild(e)
+        else
+          if(Builder._isStringOrNumber(e))
+            element.appendChild(Builder._text(e));
+      });
+    } else
+      if(Builder._isStringOrNumber(children)) 
+         element.appendChild(Builder._text(children));
+  },
+  _isStringOrNumber: function(param) {
+    return(typeof param=='string' || typeof param=='number');
+  }
+}
\ No newline at end of file
diff --git a/view/js/cropper/lib/controls.js b/view/js/cropper/lib/controls.js
new file mode 100644 (file)
index 0000000..9606948
--- /dev/null
@@ -0,0 +1,815 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//           (c) 2005 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
+//           (c) 2005 Jon Tirsen (http://www.tirsen.com)
+// Contributors:
+//  Richard Livsey
+//  Rahul Bhargava
+//  Rob Wills
+// 
+// See scriptaculous.js for full license.
+
+// Autocompleter.Base handles all the autocompletion functionality 
+// that's independent of the data source for autocompletion. This
+// includes drawing the autocompletion menu, observing keyboard
+// and mouse events, and similar.
+//
+// Specific autocompleters need to provide, at the very least, 
+// a getUpdatedChoices function that will be invoked every time
+// the text inside the monitored textbox changes. This method 
+// should get the text for which to provide autocompletion by
+// invoking this.getToken(), NOT by directly accessing
+// this.element.value. This is to allow incremental tokenized
+// autocompletion. Specific auto-completion logic (AJAX, etc)
+// belongs in getUpdatedChoices.
+//
+// Tokenized incremental autocompletion is enabled automatically
+// when an autocompleter is instantiated with the 'tokens' option
+// in the options parameter, e.g.:
+// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
+// will incrementally autocomplete with a comma as the token.
+// Additionally, ',' in the above example can be replaced with
+// a token array, e.g. { tokens: [',', '\n'] } which
+// enables autocompletion on multiple tokens. This is most 
+// useful when one of the tokens is \n (a newline), as it 
+// allows smart autocompletion after linebreaks.
+
+var Autocompleter = {}
+Autocompleter.Base = function() {};
+Autocompleter.Base.prototype = {
+  baseInitialize: function(element, update, options) {
+    this.element     = $PR(element); 
+    this.update      = $PR(update);  
+    this.hasFocus    = false; 
+    this.changed     = false; 
+    this.active      = false; 
+    this.index       = 0;     
+    this.entryCount  = 0;
+
+    if (this.setOptions)
+      this.setOptions(options);
+    else
+      this.options = options || {};
+
+    this.options.paramName    = this.options.paramName || this.element.name;
+    this.options.tokens       = this.options.tokens || [];
+    this.options.frequency    = this.options.frequency || 0.4;
+    this.options.minChars     = this.options.minChars || 1;
+    this.options.onShow       = this.options.onShow || 
+    function(element, update){ 
+      if(!update.style.position || update.style.position=='absolute') {
+        update.style.position = 'absolute';
+        Position.clone(element, update, {setHeight: false, offsetTop: element.offsetHeight});
+      }
+      Effect.Appear(update,{duration:0.15});
+    };
+    this.options.onHide = this.options.onHide || 
+    function(element, update){ new Effect.Fade(update,{duration:0.15}) };
+
+    if (typeof(this.options.tokens) == 'string') 
+      this.options.tokens = new Array(this.options.tokens);
+
+    this.observer = null;
+    
+    this.element.setAttribute('autocomplete','off');
+
+    Element.hide(this.update);
+
+    Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this));
+    Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this));
+  },
+
+  show: function() {
+    if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
+    if(!this.iefix && 
+      (navigator.appVersion.indexOf('MSIE')>0) &&
+      (navigator.userAgent.indexOf('Opera')<0) &&
+      (Element.getStyle(this.update, 'position')=='absolute')) {
+      new Insertion.After(this.update, 
+       '<iframe id="' + this.update.id + '_iefix" '+
+       'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
+       'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
+      this.iefix = $PR(this.update.id+'_iefix');
+    }
+    if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
+  },
+  
+  fixIEOverlapping: function() {
+    Position.clone(this.update, this.iefix);
+    this.iefix.style.zIndex = 1;
+    this.update.style.zIndex = 2;
+    Element.show(this.iefix);
+  },
+
+  hide: function() {
+    this.stopIndicator();
+    if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
+    if(this.iefix) Element.hide(this.iefix);
+  },
+
+  startIndicator: function() {
+    if(this.options.indicator) Element.show(this.options.indicator);
+  },
+
+  stopIndicator: function() {
+    if(this.options.indicator) Element.hide(this.options.indicator);
+  },
+
+  onKeyPress: function(event) {
+    if(this.active)
+      switch(event.keyCode) {
+       case Event.KEY_TAB:
+       case Event.KEY_RETURN:
+         this.selectEntry();
+         Event.stop(event);
+       case Event.KEY_ESC:
+         this.hide();
+         this.active = false;
+         Event.stop(event);
+         return;
+       case Event.KEY_LEFT:
+       case Event.KEY_RIGHT:
+         return;
+       case Event.KEY_UP:
+         this.markPrevious();
+         this.render();
+         if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
+         return;
+       case Event.KEY_DOWN:
+         this.markNext();
+         this.render();
+         if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
+         return;
+      }
+     else 
+       if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN || 
+         (navigator.appVersion.indexOf('AppleWebKit') > 0 && event.keyCode == 0)) return;
+
+    this.changed = true;
+    this.hasFocus = true;
+
+    if(this.observer) clearTimeout(this.observer);
+      this.observer = 
+        setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
+  },
+
+  activate: function() {
+    this.changed = false;
+    this.hasFocus = true;
+    this.getUpdatedChoices();
+  },
+
+  onHover: function(event) {
+    var element = Event.findElement(event, 'LI');
+    if(this.index != element.autocompleteIndex) 
+    {
+        this.index = element.autocompleteIndex;
+        this.render();
+    }
+    Event.stop(event);
+  },
+  
+  onClick: function(event) {
+    var element = Event.findElement(event, 'LI');
+    this.index = element.autocompleteIndex;
+    this.selectEntry();
+    this.hide();
+  },
+  
+  onBlur: function(event) {
+    // needed to make click events working
+    setTimeout(this.hide.bind(this), 250);
+    this.hasFocus = false;
+    this.active = false;     
+  }, 
+  
+  render: function() {
+    if(this.entryCount > 0) {
+      for (var i = 0; i < this.entryCount; i++)
+        this.index==i ? 
+          Element.addClassName(this.getEntry(i),"selected") : 
+          Element.removeClassName(this.getEntry(i),"selected");
+        
+      if(this.hasFocus) { 
+        this.show();
+        this.active = true;
+      }
+    } else {
+      this.active = false;
+      this.hide();
+    }
+  },
+  
+  markPrevious: function() {
+    if(this.index > 0) this.index--
+      else this.index = this.entryCount-1;
+  },
+  
+  markNext: function() {
+    if(this.index < this.entryCount-1) this.index++
+      else this.index = 0;
+  },
+  
+  getEntry: function(index) {
+    return this.update.firstChild.childNodes[index];
+  },
+  
+  getCurrentEntry: function() {
+    return this.getEntry(this.index);
+  },
+  
+  selectEntry: function() {
+    this.active = false;
+    this.updateElement(this.getCurrentEntry());
+  },
+
+  updateElement: function(selectedElement) {
+    if (this.options.updateElement) {
+      this.options.updateElement(selectedElement);
+      return;
+    }
+    var value = '';
+    if (this.options.select) {
+      var nodes = document.getElementsByClassName(this.options.select, selectedElement) || [];
+      if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
+    } else
+      value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
+    
+    var lastTokenPos = this.findLastToken();
+    if (lastTokenPos != -1) {
+      var newValue = this.element.value.substr(0, lastTokenPos + 1);
+      var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/);
+      if (whitespace)
+        newValue += whitespace[0];
+      this.element.value = newValue + value;
+    } else {
+      this.element.value = value;
+    }
+    this.element.focus();
+    
+    if (this.options.afterUpdateElement)
+      this.options.afterUpdateElement(this.element, selectedElement);
+  },
+
+  updateChoices: function(choices) {
+    if(!this.changed && this.hasFocus) {
+      this.update.innerHTML = choices;
+      Element.cleanWhitespace(this.update);
+      Element.cleanWhitespace(this.update.firstChild);
+
+      if(this.update.firstChild && this.update.firstChild.childNodes) {
+        this.entryCount = 
+          this.update.firstChild.childNodes.length;
+        for (var i = 0; i < this.entryCount; i++) {
+          var entry = this.getEntry(i);
+          entry.autocompleteIndex = i;
+          this.addObservers(entry);
+        }
+      } else { 
+        this.entryCount = 0;
+      }
+
+      this.stopIndicator();
+
+      this.index = 0;
+      this.render();
+    }
+  },
+
+  addObservers: function(element) {
+    Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
+    Event.observe(element, "click", this.onClick.bindAsEventListener(this));
+  },
+
+  onObserverEvent: function() {
+    this.changed = false;   
+    if(this.getToken().length>=this.options.minChars) {
+      this.startIndicator();
+      this.getUpdatedChoices();
+    } else {
+      this.active = false;
+      this.hide();
+    }
+  },
+
+  getToken: function() {
+    var tokenPos = this.findLastToken();
+    if (tokenPos != -1)
+      var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,'');
+    else
+      var ret = this.element.value;
+
+    return /\n/.test(ret) ? '' : ret;
+  },
+
+  findLastToken: function() {
+    var lastTokenPos = -1;
+
+    for (var i=0; i<this.options.tokens.length; i++) {
+      var thisTokenPos = this.element.value.lastIndexOf(this.options.tokens[i]);
+      if (thisTokenPos > lastTokenPos)
+        lastTokenPos = thisTokenPos;
+    }
+    return lastTokenPos;
+  }
+}
+
+Ajax.Autocompleter = Class.create();
+Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), {
+  initialize: function(element, update, url, options) {
+    this.baseInitialize(element, update, options);
+    this.options.asynchronous  = true;
+    this.options.onComplete    = this.onComplete.bind(this);
+    this.options.defaultParams = this.options.parameters || null;
+    this.url                   = url;
+  },
+
+  getUpdatedChoices: function() {
+    entry = encodeURIComponent(this.options.paramName) + '=' + 
+      encodeURIComponent(this.getToken());
+
+    this.options.parameters = this.options.callback ?
+      this.options.callback(this.element, entry) : entry;
+
+    if(this.options.defaultParams) 
+      this.options.parameters += '&' + this.options.defaultParams;
+
+    new Ajax.Request(this.url, this.options);
+  },
+
+  onComplete: function(request) {
+    this.updateChoices(request.responseText);
+  }
+
+});
+
+// The local array autocompleter. Used when you'd prefer to
+// inject an array of autocompletion options into the page, rather
+// than sending out Ajax queries, which can be quite slow sometimes.
+//
+// The constructor takes four parameters. The first two are, as usual,
+// the id of the monitored textbox, and id of the autocompletion menu.
+// The third is the array you want to autocomplete from, and the fourth
+// is the options block.
+//
+// Extra local autocompletion options:
+// - choices - How many autocompletion choices to offer
+//
+// - partialSearch - If false, the autocompleter will match entered
+//                    text only at the beginning of strings in the 
+//                    autocomplete array. Defaults to true, which will
+//                    match text at the beginning of any *word* in the
+//                    strings in the autocomplete array. If you want to
+//                    search anywhere in the string, additionally set
+//                    the option fullSearch to true (default: off).
+//
+// - fullSsearch - Search anywhere in autocomplete array strings.
+//
+// - partialChars - How many characters to enter before triggering
+//                   a partial match (unlike minChars, which defines
+//                   how many characters are required to do any match
+//                   at all). Defaults to 2.
+//
+// - ignoreCase - Whether to ignore case when autocompleting.
+//                 Defaults to true.
+//
+// It's possible to pass in a custom function as the 'selector' 
+// option, if you prefer to write your own autocompletion logic.
+// In that case, the other options above will not apply unless
+// you support them.
+
+Autocompleter.Local = Class.create();
+Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
+  initialize: function(element, update, array, options) {
+    this.baseInitialize(element, update, options);
+    this.options.array = array;
+  },
+
+  getUpdatedChoices: function() {
+    this.updateChoices(this.options.selector(this));
+  },
+
+  setOptions: function(options) {
+    this.options = Object.extend({
+      choices: 10,
+      partialSearch: true,
+      partialChars: 2,
+      ignoreCase: true,
+      fullSearch: false,
+      selector: function(instance) {
+        var ret       = []; // Beginning matches
+        var partial   = []; // Inside matches
+        var entry     = instance.getToken();
+        var count     = 0;
+
+        for (var i = 0; i < instance.options.array.length &&  
+          ret.length < instance.options.choices ; i++) { 
+
+          var elem = instance.options.array[i];
+          var foundPos = instance.options.ignoreCase ? 
+            elem.toLowerCase().indexOf(entry.toLowerCase()) : 
+            elem.indexOf(entry);
+
+          while (foundPos != -1) {
+            if (foundPos == 0 && elem.length != entry.length) { 
+              ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" + 
+                elem.substr(entry.length) + "</li>");
+              break;
+            } else if (entry.length >= instance.options.partialChars && 
+              instance.options.partialSearch && foundPos != -1) {
+              if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
+                partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
+                  elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
+                  foundPos + entry.length) + "</li>");
+                break;
+              }
+            }
+
+            foundPos = instance.options.ignoreCase ? 
+              elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : 
+              elem.indexOf(entry, foundPos + 1);
+
+          }
+        }
+        if (partial.length)
+          ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
+        return "<ul>" + ret.join('') + "</ul>";
+      }
+    }, options || {});
+  }
+});
+
+// AJAX in-place editor
+//
+// see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor
+
+// Use this if you notice weird scrolling problems on some browsers,
+// the DOM might be a bit confused when this gets called so do this
+// waits 1 ms (with setTimeout) until it does the activation
+Field.scrollFreeActivate = function(field) {
+  setTimeout(function() {
+    Field.activate(field);
+  }, 1);
+}
+
+Ajax.InPlaceEditor = Class.create();
+Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99";
+Ajax.InPlaceEditor.prototype = {
+  initialize: function(element, url, options) {
+    this.url = url;
+    this.element = $PR(element);
+
+    this.options = Object.extend({
+      okButton: true,
+      okText: "ok",
+      cancelLink: true,
+      cancelText: "cancel",
+      savingText: "Saving...",
+      clickToEditText: "Click to edit",
+      okText: "ok",
+      rows: 1,
+      onComplete: function(transport, element) {
+        new Effect.Highlight(element, {startcolor: this.options.highlightcolor});
+      },
+      onFailure: function(transport) {
+        alert("Error communicating with the server: " + transport.responseText.stripTags());
+      },
+      callback: function(form) {
+        return Form.serialize(form);
+      },
+      handleLineBreaks: true,
+      loadingText: 'Loading...',
+      savingClassName: 'inplaceeditor-saving',
+      loadingClassName: 'inplaceeditor-loading',
+      formClassName: 'inplaceeditor-form',
+      highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor,
+      highlightendcolor: "#FFFFFF",
+      externalControl: null,
+      submitOnBlur: false,
+      ajaxOptions: {},
+      evalScripts: false
+    }, options || {});
+
+    if(!this.options.formId && this.element.id) {
+      this.options.formId = this.element.id + "-inplaceeditor";
+      if ($PR(this.options.formId)) {
+        // there's already a form with that name, don't specify an id
+        this.options.formId = null;
+      }
+    }
+    
+    if (this.options.externalControl) {
+      this.options.externalControl = $PR(this.options.externalControl);
+    }
+    
+    this.originalBackground = Element.getStyle(this.element, 'background-color');
+    if (!this.originalBackground) {
+      this.originalBackground = "transparent";
+    }
+    
+    this.element.title = this.options.clickToEditText;
+    
+    this.onclickListener = this.enterEditMode.bindAsEventListener(this);
+    this.mouseoverListener = this.enterHover.bindAsEventListener(this);
+    this.mouseoutListener = this.leaveHover.bindAsEventListener(this);
+    Event.observe(this.element, 'click', this.onclickListener);
+    Event.observe(this.element, 'mouseover', this.mouseoverListener);
+    Event.observe(this.element, 'mouseout', this.mouseoutListener);
+    if (this.options.externalControl) {
+      Event.observe(this.options.externalControl, 'click', this.onclickListener);
+      Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener);
+      Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener);
+    }
+  },
+  enterEditMode: function(evt) {
+    if (this.saving) return;
+    if (this.editing) return;
+    this.editing = true;
+    this.onEnterEditMode();
+    if (this.options.externalControl) {
+      Element.hide(this.options.externalControl);
+    }
+    Element.hide(this.element);
+    this.createForm();
+    this.element.parentNode.insertBefore(this.form, this.element);
+    Field.scrollFreeActivate(this.editField);
+    // stop the event to avoid a page refresh in Safari
+    if (evt) {
+      Event.stop(evt);
+    }
+    return false;
+  },
+  createForm: function() {
+    this.form = document.createElement("form");
+    this.form.id = this.options.formId;
+    Element.addClassName(this.form, this.options.formClassName)
+    this.form.onsubmit = this.onSubmit.bind(this);
+
+    this.createEditField();
+
+    if (this.options.textarea) {
+      var br = document.createElement("br");
+      this.form.appendChild(br);
+    }
+
+    if (this.options.okButton) {
+      okButton = document.createElement("input");
+      okButton.type = "submit";
+      okButton.value = this.options.okText;
+      okButton.className = 'editor_ok_button';
+      this.form.appendChild(okButton);
+    }
+
+    if (this.options.cancelLink) {
+      cancelLink = document.createElement("a");
+      cancelLink.href = "#";
+      cancelLink.appendChild(document.createTextNode(this.options.cancelText));
+      cancelLink.onclick = this.onclickCancel.bind(this);
+      cancelLink.className = 'editor_cancel';      
+      this.form.appendChild(cancelLink);
+    }
+  },
+  hasHTMLLineBreaks: function(string) {
+    if (!this.options.handleLineBreaks) return false;
+    return string.match(/<br/i) || string.match(/<p>/i);
+  },
+  convertHTMLLineBreaks: function(string) {
+    return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, "");
+  },
+  createEditField: function() {
+    var text;
+    if(this.options.loadTextURL) {
+      text = this.options.loadingText;
+    } else {
+      text = this.getText();
+    }
+
+    var obj = this;
+    
+    if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) {
+      this.options.textarea = false;
+      var textField = document.createElement("input");
+      textField.obj = this;
+      textField.type = "text";
+      textField.name = "value";
+      textField.value = text;
+      textField.style.backgroundColor = this.options.highlightcolor;
+      textField.className = 'editor_field';
+      var size = this.options.size || this.options.cols || 0;
+      if (size != 0) textField.size = size;
+      if (this.options.submitOnBlur)
+        textField.onblur = this.onSubmit.bind(this);
+      this.editField = textField;
+    } else {
+      this.options.textarea = true;
+      var textArea = document.createElement("textarea");
+      textArea.obj = this;
+      textArea.name = "value";
+      textArea.value = this.convertHTMLLineBreaks(text);
+      textArea.rows = this.options.rows;
+      textArea.cols = this.options.cols || 40;
+      textArea.className = 'editor_field';      
+      if (this.options.submitOnBlur)
+        textArea.onblur = this.onSubmit.bind(this);
+      this.editField = textArea;
+    }
+    
+    if(this.options.loadTextURL) {
+      this.loadExternalText();
+    }
+    this.form.appendChild(this.editField);
+  },
+  getText: function() {
+    return this.element.innerHTML;
+  },
+  loadExternalText: function() {
+    Element.addClassName(this.form, this.options.loadingClassName);
+    this.editField.disabled = true;
+    new Ajax.Request(
+      this.options.loadTextURL,
+      Object.extend({
+        asynchronous: true,
+        onComplete: this.onLoadedExternalText.bind(this)
+      }, this.options.ajaxOptions)
+    );
+  },
+  onLoadedExternalText: function(transport) {
+    Element.removeClassName(this.form, this.options.loadingClassName);
+    this.editField.disabled = false;
+    this.editField.value = transport.responseText.stripTags();
+  },
+  onclickCancel: function() {
+    this.onComplete();
+    this.leaveEditMode();
+    return false;
+  },
+  onFailure: function(transport) {
+    this.options.onFailure(transport);
+    if (this.oldInnerHTML) {
+      this.element.innerHTML = this.oldInnerHTML;
+      this.oldInnerHTML = null;
+    }
+    return false;
+  },
+  onSubmit: function() {
+    // onLoading resets these so we need to save them away for the Ajax call
+    var form = this.form;
+    var value = this.editField.value;
+    
+    // do this first, sometimes the ajax call returns before we get a chance to switch on Saving...
+    // which means this will actually switch on Saving... *after* we've left edit mode causing Saving...
+    // to be displayed indefinitely
+    this.onLoading();
+    
+    if (this.options.evalScripts) {
+      new Ajax.Request(
+        this.url, Object.extend({
+          parameters: this.options.callback(form, value),
+          onComplete: this.onComplete.bind(this),
+          onFailure: this.onFailure.bind(this),
+          asynchronous:true, 
+          evalScripts:true
+        }, this.options.ajaxOptions));
+    } else  {
+      new Ajax.Updater(
+        { success: this.element,
+          // don't update on failure (this could be an option)
+          failure: null }, 
+        this.url, Object.extend({
+          parameters: this.options.callback(form, value),
+          onComplete: this.onComplete.bind(this),
+          onFailure: this.onFailure.bind(this)
+        }, this.options.ajaxOptions));
+    }
+    // stop the event to avoid a page refresh in Safari
+    if (arguments.length > 1) {
+      Event.stop(arguments[0]);
+    }
+    return false;
+  },
+  onLoading: function() {
+    this.saving = true;
+    this.removeForm();
+    this.leaveHover();
+    this.showSaving();
+  },
+  showSaving: function() {
+    this.oldInnerHTML = this.element.innerHTML;
+    this.element.innerHTML = this.options.savingText;
+    Element.addClassName(this.element, this.options.savingClassName);
+    this.element.style.backgroundColor = this.originalBackground;
+    Element.show(this.element);
+  },
+  removeForm: function() {
+    if(this.form) {
+      if (this.form.parentNode) Element.remove(this.form);
+      this.form = null;
+    }
+  },
+  enterHover: function() {
+    if (this.saving) return;
+    this.element.style.backgroundColor = this.options.highlightcolor;
+    if (this.effect) {
+      this.effect.cancel();
+    }
+    Element.addClassName(this.element, this.options.hoverClassName)
+  },
+  leaveHover: function() {
+    if (this.options.backgroundColor) {
+      this.element.style.backgroundColor = this.oldBackground;
+    }
+    Element.removeClassName(this.element, this.options.hoverClassName)
+    if (this.saving) return;
+    this.effect = new Effect.Highlight(this.element, {
+      startcolor: this.options.highlightcolor,
+      endcolor: this.options.highlightendcolor,
+      restorecolor: this.originalBackground
+    });
+  },
+  leaveEditMode: function() {
+    Element.removeClassName(this.element, this.options.savingClassName);
+    this.removeForm();
+    this.leaveHover();
+    this.element.style.backgroundColor = this.originalBackground;
+    Element.show(this.element);
+    if (this.options.externalControl) {
+      Element.show(this.options.externalControl);
+    }
+    this.editing = false;
+    this.saving = false;
+    this.oldInnerHTML = null;
+    this.onLeaveEditMode();
+  },
+  onComplete: function(transport) {
+    this.leaveEditMode();
+    this.options.onComplete.bind(this)(transport, this.element);
+  },
+  onEnterEditMode: function() {},
+  onLeaveEditMode: function() {},
+  dispose: function() {
+    if (this.oldInnerHTML) {
+      this.element.innerHTML = this.oldInnerHTML;
+    }
+    this.leaveEditMode();
+    Event.stopObserving(this.element, 'click', this.onclickListener);
+    Event.stopObserving(this.element, 'mouseover', this.mouseoverListener);
+    Event.stopObserving(this.element, 'mouseout', this.mouseoutListener);
+    if (this.options.externalControl) {
+      Event.stopObserving(this.options.externalControl, 'click', this.onclickListener);
+      Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener);
+      Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
+    }
+  }
+};
+
+Ajax.InPlaceCollectionEditor = Class.create();
+Object.extend(Ajax.InPlaceCollectionEditor.prototype, Ajax.InPlaceEditor.prototype);
+Object.extend(Ajax.InPlaceCollectionEditor.prototype, {
+  createEditField: function() {
+    if (!this.cached_selectTag) {
+      var selectTag = document.createElement("select");
+      var collection = this.options.collection || [];
+      var optionTag;
+      collection.each(function(e,i) {
+        optionTag = document.createElement("option");
+        optionTag.value = (e instanceof Array) ? e[0] : e;
+        if(this.options.value==optionTag.value) optionTag.selected = true;
+        optionTag.appendChild(document.createTextNode((e instanceof Array) ? e[1] : e));
+        selectTag.appendChild(optionTag);
+      }.bind(this));
+      this.cached_selectTag = selectTag;
+    }
+
+    this.editField = this.cached_selectTag;
+    if(this.options.loadTextURL) this.loadExternalText();
+    this.form.appendChild(this.editField);
+    this.options.callback = function(form, value) {
+      return "value=" + encodeURIComponent(value);
+    }
+  }
+});
+
+// Delayed observer, like Form.Element.Observer, 
+// but waits for delay after last key input
+// Ideal for live-search fields
+
+Form.Element.DelayedObserver = Class.create();
+Form.Element.DelayedObserver.prototype = {
+  initialize: function(element, delay, callback) {
+    this.delay     = delay || 0.5;
+    this.element   = $PR(element);
+    this.callback  = callback;
+    this.timer     = null;
+    this.lastValue = $F(this.element); 
+    Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
+  },
+  delayedListener: function(event) {
+    if(this.lastValue == $F(this.element)) return;
+    if(this.timer) clearTimeout(this.timer);
+    this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
+    this.lastValue = $F(this.element);
+  },
+  onTimerEvent: function() {
+    this.timer = null;
+    this.callback(this.element, $F(this.element));
+  }
+};
diff --git a/view/js/cropper/lib/dragdrop.js b/view/js/cropper/lib/dragdrop.js
new file mode 100644 (file)
index 0000000..baa607c
--- /dev/null
@@ -0,0 +1,915 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//           (c) 2005 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
+// 
+// See scriptaculous.js for full license.
+
+/*--------------------------------------------------------------------------*/
+
+var Droppables = {
+  drops: [],
+
+  remove: function(element) {
+    this.drops = this.drops.reject(function(d) { return d.element==$PR(element) });
+  },
+
+  add: function(element) {
+    element = $PR(element);
+    var options = Object.extend({
+      greedy:     true,
+      hoverclass: null,
+      tree:       false
+    }, arguments[1] || {});
+
+    // cache containers
+    if(options.containment) {
+      options._containers = [];
+      var containment = options.containment;
+      if((typeof containment == 'object') && 
+        (containment.constructor == Array)) {
+        containment.each( function(c) { options._containers.push($PR(c)) });
+      } else {
+        options._containers.push($PR(containment));
+      }
+    }
+    
+    if(options.accept) options.accept = [options.accept].flatten();
+
+    Element.makePositioned(element); // fix IE
+    options.element = element;
+
+    this.drops.push(options);
+  },
+  
+  findDeepestChild: function(drops) {
+    deepest = drops[0];
+      
+    for (i = 1; i < drops.length; ++i)
+      if (Element.isParent(drops[i].element, deepest.element))
+        deepest = drops[i];
+    
+    return deepest;
+  },
+
+  isContained: function(element, drop) {
+    var containmentNode;
+    if(drop.tree) {
+      containmentNode = element.treeNode; 
+    } else {
+      containmentNode = element.parentNode;
+    }
+    return drop._containers.detect(function(c) { return containmentNode == c });
+  },
+  
+  isAffected: function(point, element, drop) {
+    return (
+      (drop.element!=element) &&
+      ((!drop._containers) ||
+        this.isContained(element, drop)) &&
+      ((!drop.accept) ||
+        (Element.classNames(element).detect( 
+          function(v) { return drop.accept.include(v) } ) )) &&
+      Position.within(drop.element, point[0], point[1]) );
+  },
+
+  deactivate: function(drop) {
+    if(drop.hoverclass)
+      Element.removeClassName(drop.element, drop.hoverclass);
+    this.last_active = null;
+  },
+
+  activate: function(drop) {
+    if(drop.hoverclass)
+      Element.addClassName(drop.element, drop.hoverclass);
+    this.last_active = drop;
+  },
+
+  show: function(point, element) {
+    if(!this.drops.length) return;
+    var affected = [];
+    
+    if(this.last_active) this.deactivate(this.last_active);
+    this.drops.each( function(drop) {
+      if(Droppables.isAffected(point, element, drop))
+        affected.push(drop);
+    });
+        
+    if(affected.length>0) {
+      drop = Droppables.findDeepestChild(affected);
+      Position.within(drop.element, point[0], point[1]);
+      if(drop.onHover)
+        drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
+      
+      Droppables.activate(drop);
+    }
+  },
+
+  fire: function(event, element) {
+    if(!this.last_active) return;
+    Position.prepare();
+
+    if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
+      if (this.last_active.onDrop) 
+        this.last_active.onDrop(element, this.last_active.element, event);
+  },
+
+  reset: function() {
+    if(this.last_active)
+      this.deactivate(this.last_active);
+  }
+}
+
+var Draggables = {
+  drags: [],
+  observers: [],
+  
+  register: function(draggable) {
+    if(this.drags.length == 0) {
+      this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
+      this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
+      this.eventKeypress  = this.keyPress.bindAsEventListener(this);
+      
+      Event.observe(document, "mouseup", this.eventMouseUp);
+      Event.observe(document, "mousemove", this.eventMouseMove);
+      Event.observe(document, "keypress", this.eventKeypress);
+    }
+    this.drags.push(draggable);
+  },
+  
+  unregister: function(draggable) {
+    this.drags = this.drags.reject(function(d) { return d==draggable });
+    if(this.drags.length == 0) {
+      Event.stopObserving(document, "mouseup", this.eventMouseUp);
+      Event.stopObserving(document, "mousemove", this.eventMouseMove);
+      Event.stopObserving(document, "keypress", this.eventKeypress);
+    }
+  },
+  
+  activate: function(draggable) {
+    window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
+    this.activeDraggable = draggable;
+  },
+  
+  deactivate: function() {
+    this.activeDraggable = null;
+  },
+  
+  updateDrag: function(event) {
+    if(!this.activeDraggable) return;
+    var pointer = [Event.pointerX(event), Event.pointerY(event)];
+    // Mozilla-based browsers fire successive mousemove events with
+    // the same coordinates, prevent needless redrawing (moz bug?)
+    if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
+    this._lastPointer = pointer;
+    this.activeDraggable.updateDrag(event, pointer);
+  },
+  
+  endDrag: function(event) {
+    if(!this.activeDraggable) return;
+    this._lastPointer = null;
+    this.activeDraggable.endDrag(event);
+    this.activeDraggable = null;
+  },
+  
+  keyPress: function(event) {
+    if(this.activeDraggable)
+      this.activeDraggable.keyPress(event);
+  },
+  
+  addObserver: function(observer) {
+    this.observers.push(observer);
+    this._cacheObserverCallbacks();
+  },
+  
+  removeObserver: function(element) {  // element instead of observer fixes mem leaks
+    this.observers = this.observers.reject( function(o) { return o.element==element });
+    this._cacheObserverCallbacks();
+  },
+  
+  notify: function(eventName, draggable, event) {  // 'onStart', 'onEnd', 'onDrag'
+    if(this[eventName+'Count'] > 0)
+      this.observers.each( function(o) {
+        if(o[eventName]) o[eventName](eventName, draggable, event);
+      });
+  },
+  
+  _cacheObserverCallbacks: function() {
+    ['onStart','onEnd','onDrag'].each( function(eventName) {
+      Draggables[eventName+'Count'] = Draggables.observers.select(
+        function(o) { return o[eventName]; }
+      ).length;
+    });
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Draggable = Class.create();
+Draggable.prototype = {
+  initialize: function(element) {
+    var options = Object.extend({
+      handle: false,
+      starteffect: function(element) {
+        element._opacity = Element.getOpacity(element); 
+        new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7}); 
+      },
+      reverteffect: function(element, top_offset, left_offset) {
+        var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
+        element._revert = new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur});
+      },
+      endeffect: function(element) {
+        var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0
+        new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity}); 
+      },
+      zindex: 1000,
+      revert: false,
+      scroll: false,
+      scrollSensitivity: 20,
+      scrollSpeed: 15,
+      snap: false   // false, or xy or [x,y] or function(x,y){ return [x,y] }
+    }, arguments[1] || {});
+
+    this.element = $PR(element);
+    
+    if(options.handle && (typeof options.handle == 'string')) {
+      var h = Element.childrenWithClassName(this.element, options.handle, true);
+      if(h.length>0) this.handle = h[0];
+    }
+    if(!this.handle) this.handle = $PR(options.handle);
+    if(!this.handle) this.handle = this.element;
+    
+    if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML)
+      options.scroll = $PR(options.scroll);
+
+    Element.makePositioned(this.element); // fix IE    
+
+    this.delta    = this.currentDelta();
+    this.options  = options;
+    this.dragging = false;   
+
+    this.eventMouseDown = this.initDrag.bindAsEventListener(this);
+    Event.observe(this.handle, "mousedown", this.eventMouseDown);
+    
+    Draggables.register(this);
+  },
+  
+  destroy: function() {
+    Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
+    Draggables.unregister(this);
+  },
+  
+  currentDelta: function() {
+    return([
+      parseInt(Element.getStyle(this.element,'left') || '0'),
+      parseInt(Element.getStyle(this.element,'top') || '0')]);
+  },
+  
+  initDrag: function(event) {
+    if(Event.isLeftClick(event)) {    
+      // abort on form elements, fixes a Firefox issue
+      var src = Event.element(event);
+      if(src.tagName && (
+        src.tagName=='INPUT' ||
+        src.tagName=='SELECT' ||
+        src.tagName=='OPTION' ||
+        src.tagName=='BUTTON' ||
+        src.tagName=='TEXTAREA')) return;
+        
+      if(this.element._revert) {
+        this.element._revert.cancel();
+        this.element._revert = null;
+      }
+      
+      var pointer = [Event.pointerX(event), Event.pointerY(event)];
+      var pos     = Position.cumulativeOffset(this.element);
+      this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
+      
+      Draggables.activate(this);
+      Event.stop(event);
+    }
+  },
+  
+  startDrag: function(event) {
+    this.dragging = true;
+    
+    if(this.options.zindex) {
+      this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
+      this.element.style.zIndex = this.options.zindex;
+    }
+    
+    if(this.options.ghosting) {
+      this._clone = this.element.cloneNode(true);
+      Position.absolutize(this.element);
+      this.element.parentNode.insertBefore(this._clone, this.element);
+    }
+    
+    if(this.options.scroll) {
+      if (this.options.scroll == window) {
+        var where = this._getWindowScroll(this.options.scroll);
+        this.originalScrollLeft = where.left;
+        this.originalScrollTop = where.top;
+      } else {
+        this.originalScrollLeft = this.options.scroll.scrollLeft;
+        this.originalScrollTop = this.options.scroll.scrollTop;
+      }
+    }
+    
+    Draggables.notify('onStart', this, event);
+    if(this.options.starteffect) this.options.starteffect(this.element);
+  },
+  
+  updateDrag: function(event, pointer) {
+    if(!this.dragging) this.startDrag(event);
+    Position.prepare();
+    Droppables.show(pointer, this.element);
+    Draggables.notify('onDrag', this, event);
+    this.draw(pointer);
+    if(this.options.change) this.options.change(this);
+    
+    if(this.options.scroll) {
+      this.stopScrolling();
+      
+      var p;
+      if (this.options.scroll == window) {
+        with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
+      } else {
+        p = Position.page(this.options.scroll);
+        p[0] += this.options.scroll.scrollLeft;
+        p[1] += this.options.scroll.scrollTop;
+        p.push(p[0]+this.options.scroll.offsetWidth);
+        p.push(p[1]+this.options.scroll.offsetHeight);
+      }
+      var speed = [0,0];
+      if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
+      if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
+      if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);
+      if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
+      this.startScrolling(speed);
+    }
+    
+    // fix AppleWebKit rendering
+    if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
+    
+    Event.stop(event);
+  },
+  
+  finishDrag: function(event, success) {
+    this.dragging = false;
+
+    if(this.options.ghosting) {
+      Position.relativize(this.element);
+      Element.remove(this._clone);
+      this._clone = null;
+    }
+
+    if(success) Droppables.fire(event, this.element);
+    Draggables.notify('onEnd', this, event);
+
+    var revert = this.options.revert;
+    if(revert && typeof revert == 'function') revert = revert(this.element);
+    
+    var d = this.currentDelta();
+    if(revert && this.options.reverteffect) {
+      this.options.reverteffect(this.element, 
+        d[1]-this.delta[1], d[0]-this.delta[0]);
+    } else {
+      this.delta = d;
+    }
+
+    if(this.options.zindex)
+      this.element.style.zIndex = this.originalZ;
+
+    if(this.options.endeffect) 
+      this.options.endeffect(this.element);
+
+    Draggables.deactivate(this);
+    Droppables.reset();
+  },
+  
+  keyPress: function(event) {
+    if(event.keyCode!=Event.KEY_ESC) return;
+    this.finishDrag(event, false);
+    Event.stop(event);
+  },
+  
+  endDrag: function(event) {
+    if(!this.dragging) return;
+    this.stopScrolling();
+    this.finishDrag(event, true);
+    Event.stop(event);
+  },
+  
+  draw: function(point) {
+    var pos = Position.cumulativeOffset(this.element);
+    var d = this.currentDelta();
+    pos[0] -= d[0]; pos[1] -= d[1];
+    
+    if(this.options.scroll && (this.options.scroll != window)) {
+      pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
+      pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
+    }
+    
+    var p = [0,1].map(function(i){ 
+      return (point[i]-pos[i]-this.offset[i]) 
+    }.bind(this));
+    
+    if(this.options.snap) {
+      if(typeof this.options.snap == 'function') {
+        p = this.options.snap(p[0],p[1],this);
+      } else {
+      if(this.options.snap instanceof Array) {
+        p = p.map( function(v, i) {
+          return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this))
+      } else {
+        p = p.map( function(v) {
+          return Math.round(v/this.options.snap)*this.options.snap }.bind(this))
+      }
+    }}
+    
+    var style = this.element.style;
+    if((!this.options.constraint) || (this.options.constraint=='horizontal'))
+      style.left = p[0] + "px";
+    if((!this.options.constraint) || (this.options.constraint=='vertical'))
+      style.top  = p[1] + "px";
+    if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
+  },
+  
+  stopScrolling: function() {
+    if(this.scrollInterval) {
+      clearInterval(this.scrollInterval);
+      this.scrollInterval = null;
+      Draggables._lastScrollPointer = null;
+    }
+  },
+  
+  startScrolling: function(speed) {
+    this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
+    this.lastScrolled = new Date();
+    this.scrollInterval = setInterval(this.scroll.bind(this), 10);
+  },
+  
+  scroll: function() {
+    var current = new Date();
+    var delta = current - this.lastScrolled;
+    this.lastScrolled = current;
+    if(this.options.scroll == window) {
+      with (this._getWindowScroll(this.options.scroll)) {
+        if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
+          var d = delta / 1000;
+          this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
+        }
+      }
+    } else {
+      this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
+      this.options.scroll.scrollTop  += this.scrollSpeed[1] * delta / 1000;
+    }
+    
+    Position.prepare();
+    Droppables.show(Draggables._lastPointer, this.element);
+    Draggables.notify('onDrag', this);
+    Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
+    Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
+    Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
+    if (Draggables._lastScrollPointer[0] < 0)
+      Draggables._lastScrollPointer[0] = 0;
+    if (Draggables._lastScrollPointer[1] < 0)
+      Draggables._lastScrollPointer[1] = 0;
+    this.draw(Draggables._lastScrollPointer);
+    
+    if(this.options.change) this.options.change(this);
+  },
+  
+  _getWindowScroll: function(w) {
+    var T, L, W, H;
+    with (w.document) {
+      if (w.document.documentElement && documentElement.scrollTop) {
+        T = documentElement.scrollTop;
+        L = documentElement.scrollLeft;
+      } else if (w.document.body) {
+        T = body.scrollTop;
+        L = body.scrollLeft;
+      }
+      if (w.innerWidth) {
+        W = w.innerWidth;
+        H = w.innerHeight;
+      } else if (w.document.documentElement && documentElement.clientWidth) {
+        W = documentElement.clientWidth;
+        H = documentElement.clientHeight;
+      } else {
+        W = body.offsetWidth;
+        H = body.offsetHeight
+      }
+    }
+    return { top: T, left: L, width: W, height: H };
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var SortableObserver = Class.create();
+SortableObserver.prototype = {
+  initialize: function(element, observer) {
+    this.element   = $PR(element);
+    this.observer  = observer;
+    this.lastValue = Sortable.serialize(this.element);
+  },
+  
+  onStart: function() {
+    this.lastValue = Sortable.serialize(this.element);
+  },
+  
+  onEnd: function() {
+    Sortable.unmark();
+    if(this.lastValue != Sortable.serialize(this.element))
+      this.observer(this.element)
+  }
+}
+
+var Sortable = {
+  sortables: {},
+  
+  _findRootElement: function(element) {
+    while (element.tagName != "BODY") {  
+      if(element.id && Sortable.sortables[element.id]) return element;
+      element = element.parentNode;
+    }
+  },
+
+  options: function(element) {
+    element = Sortable._findRootElement($PR(element));
+    if(!element) return;
+    return Sortable.sortables[element.id];
+  },
+  
+  destroy: function(element){
+    var s = Sortable.options(element);
+    
+    if(s) {
+      Draggables.removeObserver(s.element);
+      s.droppables.each(function(d){ Droppables.remove(d) });
+      s.draggables.invoke('destroy');
+      
+      delete Sortable.sortables[s.element.id];
+    }
+  },
+
+  create: function(element) {
+    element = $PR(element);
+    var options = Object.extend({ 
+      element:     element,
+      tag:         'li',       // assumes li children, override with tag: 'tagname'
+      dropOnEmpty: false,
+      tree:        false,
+      treeTag:     'ul',
+      overlap:     'vertical', // one of 'vertical', 'horizontal'
+      constraint:  'vertical', // one of 'vertical', 'horizontal', false
+      containment: element,    // also takes array of elements (or id's); or false
+      handle:      false,      // or a CSS class
+      only:        false,
+      hoverclass:  null,
+      ghosting:    false,
+      scroll:      false,
+      scrollSensitivity: 20,
+      scrollSpeed: 15,
+      format:      /^[^_]*_(.*)$/,
+      onChange:    Prototype.emptyFunction,
+      onUpdate:    Prototype.emptyFunction
+    }, arguments[1] || {});
+
+    // clear any old sortable with same element
+    this.destroy(element);
+
+    // build options for the draggables
+    var options_for_draggable = {
+      revert:      true,
+      scroll:      options.scroll,
+      scrollSpeed: options.scrollSpeed,
+      scrollSensitivity: options.scrollSensitivity,
+      ghosting:    options.ghosting,
+      constraint:  options.constraint,
+      handle:      options.handle };
+
+    if(options.starteffect)
+      options_for_draggable.starteffect = options.starteffect;
+
+    if(options.reverteffect)
+      options_for_draggable.reverteffect = options.reverteffect;
+    else
+      if(options.ghosting) options_for_draggable.reverteffect = function(element) {
+        element.style.top  = 0;
+        element.style.left = 0;
+      };
+
+    if(options.endeffect)
+      options_for_draggable.endeffect = options.endeffect;
+
+    if(options.zindex)
+      options_for_draggable.zindex = options.zindex;
+
+    // build options for the droppables  
+    var options_for_droppable = {
+      overlap:     options.overlap,
+      containment: options.containment,
+      tree:        options.tree,
+      hoverclass:  options.hoverclass,
+      onHover:     Sortable.onHover
+      //greedy:      !options.dropOnEmpty
+    }
+    
+    var options_for_tree = {
+      onHover:      Sortable.onEmptyHover,
+      overlap:      options.overlap,
+      containment:  options.containment,
+      hoverclass:   options.hoverclass
+    }
+
+    // fix for gecko engine
+    Element.cleanWhitespace(element); 
+
+    options.draggables = [];
+    options.droppables = [];
+
+    // drop on empty handling
+    if(options.dropOnEmpty || options.tree) {
+      Droppables.add(element, options_for_tree);
+      options.droppables.push(element);
+    }
+
+    (this.findElements(element, options) || []).each( function(e) {
+      // handles are per-draggable
+      var handle = options.handle ? 
+        Element.childrenWithClassName(e, options.handle)[0] : e;    
+      options.draggables.push(
+        new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
+      Droppables.add(e, options_for_droppable);
+      if(options.tree) e.treeNode = element;
+      options.droppables.push(e);      
+    });
+    
+    if(options.tree) {
+      (Sortable.findTreeElements(element, options) || []).each( function(e) {
+        Droppables.add(e, options_for_tree);
+        e.treeNode = element;
+        options.droppables.push(e);
+      });
+    }
+
+    // keep reference
+    this.sortables[element.id] = options;
+
+    // for onupdate
+    Draggables.addObserver(new SortableObserver(element, options.onUpdate));
+
+  },
+
+  // return all suitable-for-sortable elements in a guaranteed order
+  findElements: function(element, options) {
+    return Element.findChildren(
+      element, options.only, options.tree ? true : false, options.tag);
+  },
+  
+  findTreeElements: function(element, options) {
+    return Element.findChildren(
+      element, options.only, options.tree ? true : false, options.treeTag);
+  },
+
+  onHover: function(element, dropon, overlap) {
+    if(Element.isParent(dropon, element)) return;
+
+    if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
+      return;
+    } else if(overlap>0.5) {
+      Sortable.mark(dropon, 'before');
+      if(dropon.previousSibling != element) {
+        var oldParentNode = element.parentNode;
+        element.style.visibility = "hidden"; // fix gecko rendering
+        dropon.parentNode.insertBefore(element, dropon);
+        if(dropon.parentNode!=oldParentNode) 
+          Sortable.options(oldParentNode).onChange(element);
+        Sortable.options(dropon.parentNode).onChange(element);
+      }
+    } else {
+      Sortable.mark(dropon, 'after');
+      var nextElement = dropon.nextSibling || null;
+      if(nextElement != element) {
+        var oldParentNode = element.parentNode;
+        element.style.visibility = "hidden"; // fix gecko rendering
+        dropon.parentNode.insertBefore(element, nextElement);
+        if(dropon.parentNode!=oldParentNode) 
+          Sortable.options(oldParentNode).onChange(element);
+        Sortable.options(dropon.parentNode).onChange(element);
+      }
+    }
+  },
+  
+  onEmptyHover: function(element, dropon, overlap) {
+    var oldParentNode = element.parentNode;
+    var droponOptions = Sortable.options(dropon);
+        
+    if(!Element.isParent(dropon, element)) {
+      var index;
+      
+      var children = Sortable.findElements(dropon, {tag: droponOptions.tag});
+      var child = null;
+            
+      if(children) {
+        var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
+        
+        for (index = 0; index < children.length; index += 1) {
+          if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
+            offset -= Element.offsetSize (children[index], droponOptions.overlap);
+          } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
+            child = index + 1 < children.length ? children[index + 1] : null;
+            break;
+          } else {
+            child = children[index];
+            break;
+          }
+        }
+      }
+      
+      dropon.insertBefore(element, child);
+      
+      Sortable.options(oldParentNode).onChange(element);
+      droponOptions.onChange(element);
+    }
+  },
+
+  unmark: function() {
+    if(Sortable._marker) Element.hide(Sortable._marker);
+  },
+
+  mark: function(dropon, position) {
+    // mark on ghosting only
+    var sortable = Sortable.options(dropon.parentNode);
+    if(sortable && !sortable.ghosting) return; 
+
+    if(!Sortable._marker) {
+      Sortable._marker = $PR('dropmarker') || document.createElement('DIV');
+      Element.hide(Sortable._marker);
+      Element.addClassName(Sortable._marker, 'dropmarker');
+      Sortable._marker.style.position = 'absolute';
+      document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
+    }    
+    var offsets = Position.cumulativeOffset(dropon);
+    Sortable._marker.style.left = offsets[0] + 'px';
+    Sortable._marker.style.top = offsets[1] + 'px';
+    
+    if(position=='after')
+      if(sortable.overlap == 'horizontal') 
+        Sortable._marker.style.left = (offsets[0]+dropon.clientWidth) + 'px';
+      else
+        Sortable._marker.style.top = (offsets[1]+dropon.clientHeight) + 'px';
+    
+    Element.show(Sortable._marker);
+  },
+  
+  _tree: function(element, options, parent) {
+    var children = Sortable.findElements(element, options) || [];
+  
+    for (var i = 0; i < children.length; ++i) {
+      var match = children[i].id.match(options.format);
+
+      if (!match) continue;
+      
+      var child = {
+        id: encodeURIComponent(match ? match[1] : null),
+        element: element,
+        parent: parent,
+        children: new Array,
+        position: parent.children.length,
+        container: Sortable._findChildrenElement(children[i], options.treeTag.toUpperCase())
+      }
+      
+      /* Get the element containing the children and recurse over it */
+      if (child.container)
+        this._tree(child.container, options, child)
+      
+      parent.children.push (child);
+    }
+
+    return parent; 
+  },
+
+  /* Finds the first element of the given tag type within a parent element.
+    Used for finding the first LI[ST] within a L[IST]I[TEM].*/
+  _findChildrenElement: function (element, containerTag) {
+    if (element && element.hasChildNodes)
+      for (var i = 0; i < element.childNodes.length; ++i)
+        if (element.childNodes[i].tagName == containerTag)
+          return element.childNodes[i];
+  
+    return null;
+  },
+
+  tree: function(element) {
+    element = $PR(element);
+    var sortableOptions = this.options(element);
+    var options = Object.extend({
+      tag: sortableOptions.tag,
+      treeTag: sortableOptions.treeTag,
+      only: sortableOptions.only,
+      name: element.id,
+      format: sortableOptions.format
+    }, arguments[1] || {});
+    
+    var root = {
+      id: null,
+      parent: null,
+      children: new Array,
+      container: element,
+      position: 0
+    }
+    
+    return Sortable._tree (element, options, root);
+  },
+
+  /* Construct a [i] index for a particular node */
+  _constructIndex: function(node) {
+    var index = '';
+    do {
+      if (node.id) index = '[' + node.position + ']' + index;
+    } while ((node = node.parent) != null);
+    return index;
+  },
+
+  sequence: function(element) {
+    element = $PR(element);
+    var options = Object.extend(this.options(element), arguments[1] || {});
+    
+    return $PR(this.findElements(element, options) || []).map( function(item) {
+      return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
+    });
+  },
+
+  setSequence: function(element, new_sequence) {
+    element = $PR(element);
+    var options = Object.extend(this.options(element), arguments[2] || {});
+    
+    var nodeMap = {};
+    this.findElements(element, options).each( function(n) {
+        if (n.id.match(options.format))
+            nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
+        n.parentNode.removeChild(n);
+    });
+   
+    new_sequence.each(function(ident) {
+      var n = nodeMap[ident];
+      if (n) {
+        n[1].appendChild(n[0]);
+        delete nodeMap[ident];
+      }
+    });
+  },
+  
+  serialize: function(element) {
+    element = $PR(element);
+    var options = Object.extend(Sortable.options(element), arguments[1] || {});
+    var name = encodeURIComponent(
+      (arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
+    
+    if (options.tree) {
+      return Sortable.tree(element, arguments[1]).children.map( function (item) {
+        return [name + Sortable._constructIndex(item) + "=" + 
+                encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
+      }).flatten().join('&');
+    } else {
+      return Sortable.sequence(element, arguments[1]).map( function(item) {
+        return name + "[]=" + encodeURIComponent(item);
+      }).join('&');
+    }
+  }
+}
+
+/* Returns true if child is contained within element */
+Element.isParent = function(child, element) {
+  if (!child.parentNode || child == element) return false;
+
+  if (child.parentNode == element) return true;
+
+  return Element.isParent(child.parentNode, element);
+}
+
+Element.findChildren = function(element, only, recursive, tagName) {    
+  if(!element.hasChildNodes()) return null;
+  tagName = tagName.toUpperCase();
+  if(only) only = [only].flatten();
+  var elements = [];
+  $A(element.childNodes).each( function(e) {
+    if(e.tagName && e.tagName.toUpperCase()==tagName &&
+      (!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
+        elements.push(e);
+    if(recursive) {
+      var grandchildren = Element.findChildren(e, only, recursive, tagName);
+      if(grandchildren) elements.push(grandchildren);
+    }
+  });
+
+  return (elements.length>0 ? elements.flatten() : []);
+}
+
+Element.offsetSize = function (element, type) {
+  if (type == 'vertical' || type == 'height')
+    return element.offsetHeight;
+  else
+    return element.offsetWidth;
+}
diff --git a/view/js/cropper/lib/effects.js b/view/js/cropper/lib/effects.js
new file mode 100644 (file)
index 0000000..7e0407d
--- /dev/null
@@ -0,0 +1,958 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// Contributors:
+//  Justin Palmer (http://encytemedia.com/)
+//  Mark Pilgrim (http://diveintomark.org/)
+//  Martin Bialasinki
+// 
+// See scriptaculous.js for full license.  
+
+// converts rgb() and #xxx to #xxxxxx format,  
+// returns self (or first argument) if not convertable  
+String.prototype.parseColor = function() {  
+  var color = '#';  
+  if(this.slice(0,4) == 'rgb(') {  
+    var cols = this.slice(4,this.length-1).split(',');  
+    var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);  
+  } else {  
+    if(this.slice(0,1) == '#') {  
+      if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();  
+      if(this.length==7) color = this.toLowerCase();  
+    }  
+  }  
+  return(color.length==7 ? color : (arguments[0] || this));  
+}
+
+/*--------------------------------------------------------------------------*/
+
+Element.collectTextNodes = function(element) {  
+  return $A($PR(element).childNodes).collect( function(node) {
+    return (node.nodeType==3 ? node.nodeValue : 
+      (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
+  }).flatten().join('');
+}
+
+Element.collectTextNodesIgnoreClass = function(element, className) {  
+  return $A($PR(element).childNodes).collect( function(node) {
+    return (node.nodeType==3 ? node.nodeValue : 
+      ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? 
+        Element.collectTextNodesIgnoreClass(node, className) : ''));
+  }).flatten().join('');
+}
+
+Element.setContentZoom = function(element, percent) {
+  element = $PR(element);  
+  Element.setStyle(element, {fontSize: (percent/100) + 'em'});   
+  if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
+}
+
+Element.getOpacity = function(element){  
+  var opacity;
+  if (opacity = Element.getStyle(element, 'opacity'))  
+    return parseFloat(opacity);  
+  if (opacity = (Element.getStyle(element, 'filter') || '').match(/alpha\(opacity=(.*)\)/))  
+    if(opacity[1]) return parseFloat(opacity[1]) / 100;  
+  return 1.0;  
+}
+
+Element.setOpacity = function(element, value){  
+  element= $PR(element);  
+  if (value == 1){
+    Element.setStyle(element, { opacity: 
+      (/Gecko/.test(navigator.userAgent) && !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 
+      0.999999 : null });
+    if(/MSIE/.test(navigator.userAgent))  
+      Element.setStyle(element, {filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});  
+  } else {  
+    if(value < 0.00001) value = 0;  
+    Element.setStyle(element, {opacity: value});
+    if(/MSIE/.test(navigator.userAgent))  
+     Element.setStyle(element, 
+       { filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'') +
+                 'alpha(opacity='+value*100+')' });  
+  }
+}  
+Element.getInlineOpacity = function(element){  
+  return $PR(element).style.opacity || '';
+}  
+
+Element.childrenWithClassName = function(element, className, findFirst) {
+  var classNameRegExp = new RegExp("(^|\\s)" + className + "(\\s|$)");
+  var results = $A($PR(element).getElementsByTagName('*'))[findFirst ? 'detect' : 'select']( function(c) { 
+    return (c.className && c.className.match(classNameRegExp));
+  });
+  if(!results) results = [];
+  return results;
+}
+
+Element.forceRerendering = function(element) {
+  try {
+    element = $PR(element);
+    var n = document.createTextNode(' ');
+    element.appendChild(n);
+    element.removeChild(n);
+  } catch(e) { }
+};
+
+/*--------------------------------------------------------------------------*/
+
+Array.prototype.call = function() {
+  var args = arguments;
+  this.each(function(f){ f.apply(this, args) });
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Effect = {
+  tagifyText: function(element) {
+    var tagifyStyle = 'position:relative';
+    if(/MSIE/.test(navigator.userAgent)) tagifyStyle += ';zoom:1';
+    element = $PR(element);
+    $A(element.childNodes).each( function(child) {
+      if(child.nodeType==3) {
+        child.nodeValue.toArray().each( function(character) {
+          element.insertBefore(
+            Builder.node('span',{style: tagifyStyle},
+              character == ' ' ? String.fromCharCode(160) : character), 
+              child);
+        });
+        Element.remove(child);
+      }
+    });
+  },
+  multiple: function(element, effect) {
+    var elements;
+    if(((typeof element == 'object') || 
+        (typeof element == 'function')) && 
+       (element.length))
+      elements = element;
+    else
+      elements = $PR(element).childNodes;
+      
+    var options = Object.extend({
+      speed: 0.1,
+      delay: 0.0
+    }, arguments[2] || {});
+    var masterDelay = options.delay;
+
+    $A(elements).each( function(element, index) {
+      new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
+    });
+  },
+  PAIRS: {
+    'slide':  ['SlideDown','SlideUp'],
+    'blind':  ['BlindDown','BlindUp'],
+    'appear': ['Appear','Fade']
+  },
+  toggle: function(element, effect) {
+    element = $PR(element);
+    effect = (effect || 'appear').toLowerCase();
+    var options = Object.extend({
+      queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
+    }, arguments[2] || {});
+    Effect[element.visible() ? 
+      Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
+  }
+};
+
+var Effect2 = Effect; // deprecated
+
+/* ------------- transitions ------------- */
+
+Effect.Transitions = {}
+
+Effect.Transitions.linear = function(pos) {
+  return pos;
+}
+Effect.Transitions.sinoidal = function(pos) {
+  return (-Math.cos(pos*Math.PI)/2) + 0.5;
+}
+Effect.Transitions.reverse  = function(pos) {
+  return 1-pos;
+}
+Effect.Transitions.flicker = function(pos) {
+  return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
+}
+Effect.Transitions.wobble = function(pos) {
+  return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
+}
+Effect.Transitions.pulse = function(pos) {
+  return (Math.floor(pos*10) % 2 == 0 ? 
+    (pos*10-Math.floor(pos*10)) : 1-(pos*10-Math.floor(pos*10)));
+}
+Effect.Transitions.none = function(pos) {
+  return 0;
+}
+Effect.Transitions.full = function(pos) {
+  return 1;
+}
+
+/* ------------- core effects ------------- */
+
+Effect.ScopedQueue = Class.create();
+Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
+  initialize: function() {
+    this.effects  = [];
+    this.interval = null;
+  },
+  _each: function(iterator) {
+    this.effects._each(iterator);
+  },
+  add: function(effect) {
+    var timestamp = new Date().getTime();
+    
+    var position = (typeof effect.options.queue == 'string') ? 
+      effect.options.queue : effect.options.queue.position;
+    
+    switch(position) {
+      case 'front':
+        // move unstarted effects after this effect  
+        this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
+            e.startOn  += effect.finishOn;
+            e.finishOn += effect.finishOn;
+          });
+        break;
+      case 'end':
+        // start effect after last queued effect has finished
+        timestamp = this.effects.pluck('finishOn').max() || timestamp;
+        break;
+    }
+    
+    effect.startOn  += timestamp;
+    effect.finishOn += timestamp;
+
+    if(!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
+      this.effects.push(effect);
+    
+    if(!this.interval) 
+      this.interval = setInterval(this.loop.bind(this), 40);
+  },
+  remove: function(effect) {
+    this.effects = this.effects.reject(function(e) { return e==effect });
+    if(this.effects.length == 0) {
+      clearInterval(this.interval);
+      this.interval = null;
+    }
+  },
+  loop: function() {
+    var timePos = new Date().getTime();
+    this.effects.invoke('loop', timePos);
+  }
+});
+
+Effect.Queues = {
+  instances: $H(),
+  get: function(queueName) {
+    if(typeof queueName != 'string') return queueName;
+    
+    if(!this.instances[queueName])
+      this.instances[queueName] = new Effect.ScopedQueue();
+      
+    return this.instances[queueName];
+  }
+}
+Effect.Queue = Effect.Queues.get('global');
+
+Effect.DefaultOptions = {
+  transition: Effect.Transitions.sinoidal,
+  duration:   1.0,   // seconds
+  fps:        25.0,  // max. 25fps due to Effect.Queue implementation
+  sync:       false, // true for combining
+  from:       0.0,
+  to:         1.0,
+  delay:      0.0,
+  queue:      'parallel'
+}
+
+Effect.Base = function() {};
+Effect.Base.prototype = {
+  position: null,
+  start: function(options) {
+    this.options      = Object.extend(Object.extend({},Effect.DefaultOptions), options || {});
+    this.currentFrame = 0;
+    this.state        = 'idle';
+    this.startOn      = this.options.delay*1000;
+    this.finishOn     = this.startOn + (this.options.duration*1000);
+    this.event('beforeStart');
+    if(!this.options.sync)
+      Effect.Queues.get(typeof this.options.queue == 'string' ? 
+        'global' : this.options.queue.scope).add(this);
+  },
+  loop: function(timePos) {
+    if(timePos >= this.startOn) {
+      if(timePos >= this.finishOn) {
+        this.render(1.0);
+        this.cancel();
+        this.event('beforeFinish');
+        if(this.finish) this.finish(); 
+        this.event('afterFinish');
+        return;  
+      }
+      var pos   = (timePos - this.startOn) / (this.finishOn - this.startOn);
+      var frame = Math.round(pos * this.options.fps * this.options.duration);
+      if(frame > this.currentFrame) {
+        this.render(pos);
+        this.currentFrame = frame;
+      }
+    }
+  },
+  render: function(pos) {
+    if(this.state == 'idle') {
+      this.state = 'running';
+      this.event('beforeSetup');
+      if(this.setup) this.setup();
+      this.event('afterSetup');
+    }
+    if(this.state == 'running') {
+      if(this.options.transition) pos = this.options.transition(pos);
+      pos *= (this.options.to-this.options.from);
+      pos += this.options.from;
+      this.position = pos;
+      this.event('beforeUpdate');
+      if(this.update) this.update(pos);
+      this.event('afterUpdate');
+    }
+  },
+  cancel: function() {
+    if(!this.options.sync)
+      Effect.Queues.get(typeof this.options.queue == 'string' ? 
+        'global' : this.options.queue.scope).remove(this);
+    this.state = 'finished';
+  },
+  event: function(eventName) {
+    if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
+    if(this.options[eventName]) this.options[eventName](this);
+  },
+  inspect: function() {
+    return '#<Effect:' + $H(this).inspect() + ',options:' + $H(this.options).inspect() + '>';
+  }
+}
+
+Effect.Parallel = Class.create();
+Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), {
+  initialize: function(effects) {
+    this.effects = effects || [];
+    this.start(arguments[1]);
+  },
+  update: function(position) {
+    this.effects.invoke('render', position);
+  },
+  finish: function(position) {
+    this.effects.each( function(effect) {
+      effect.render(1.0);
+      effect.cancel();
+      effect.event('beforeFinish');
+      if(effect.finish) effect.finish(position);
+      effect.event('afterFinish');
+    });
+  }
+});
+
+Effect.Opacity = Class.create();
+Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
+  initialize: function(element) {
+    this.element = $PR(element);
+    // make this work on IE on elements without 'layout'
+    if(/MSIE/.test(navigator.userAgent) && (!this.element.hasLayout))
+      this.element.setStyle({zoom: 1});
+    var options = Object.extend({
+      from: this.element.getOpacity() || 0.0,
+      to:   1.0
+    }, arguments[1] || {});
+    this.start(options);
+  },
+  update: function(position) {
+    this.element.setOpacity(position);
+  }
+});
+
+Effect.Move = Class.create();
+Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), {
+  initialize: function(element) {
+    this.element = $PR(element);
+    var options = Object.extend({
+      x:    0,
+      y:    0,
+      mode: 'relative'
+    }, arguments[1] || {});
+    this.start(options);
+  },
+  setup: function() {
+    // Bug in Opera: Opera returns the "real" position of a static element or
+    // relative element that does not have top/left explicitly set.
+    // ==> Always set top and left for position relative elements in your stylesheets 
+    // (to 0 if you do not need them) 
+    this.element.makePositioned();
+    this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
+    this.originalTop  = parseFloat(this.element.getStyle('top')  || '0');
+    if(this.options.mode == 'absolute') {
+      // absolute movement, so we need to calc deltaX and deltaY
+      this.options.x = this.options.x - this.originalLeft;
+      this.options.y = this.options.y - this.originalTop;
+    }
+  },
+  update: function(position) {
+    this.element.setStyle({
+      left: this.options.x  * position + this.originalLeft + 'px',
+      top:  this.options.y  * position + this.originalTop  + 'px'
+    });
+  }
+});
+
+// for backwards compatibility
+Effect.MoveBy = function(element, toTop, toLeft) {
+  return new Effect.Move(element, 
+    Object.extend({ x: toLeft, y: toTop }, arguments[3] || {}));
+};
+
+Effect.Scale = Class.create();
+Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
+  initialize: function(element, percent) {
+    this.element = $PR(element)
+    var options = Object.extend({
+      scaleX: true,
+      scaleY: true,
+      scaleContent: true,
+      scaleFromCenter: false,
+      scaleMode: 'box',        // 'box' or 'contents' or {} with provided values
+      scaleFrom: 100.0,
+      scaleTo:   percent
+    }, arguments[2] || {});
+    this.start(options);
+  },
+  setup: function() {
+    this.restoreAfterFinish = this.options.restoreAfterFinish || false;
+    this.elementPositioning = this.element.getStyle('position');
+    
+    this.originalStyle = {};
+    ['top','left','width','height','fontSize'].each( function(k) {
+      this.originalStyle[k] = this.element.style[k];
+    }.bind(this));
+      
+    this.originalTop  = this.element.offsetTop;
+    this.originalLeft = this.element.offsetLeft;
+    
+    var fontSize = this.element.getStyle('font-size') || '100%';
+    ['em','px','%'].each( function(fontSizeType) {
+      if(fontSize.indexOf(fontSizeType)>0) {
+        this.fontSize     = parseFloat(fontSize);
+        this.fontSizeType = fontSizeType;
+      }
+    }.bind(this));
+    
+    this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
+    
+    this.dims = null;
+    if(this.options.scaleMode=='box')
+      this.dims = [this.element.offsetHeight, this.element.offsetWidth];
+    if(/^content/.test(this.options.scaleMode))
+      this.dims = [this.element.scrollHeight, this.element.scrollWidth];
+    if(!this.dims)
+      this.dims = [this.options.scaleMode.originalHeight,
+                   this.options.scaleMode.originalWidth];
+  },
+  update: function(position) {
+    var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
+    if(this.options.scaleContent && this.fontSize)
+      this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
+    this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
+  },
+  finish: function(position) {
+    if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
+  },
+  setDimensions: function(height, width) {
+    var d = {};
+    if(this.options.scaleX) d.width = width + 'px';
+    if(this.options.scaleY) d.height = height + 'px';
+    if(this.options.scaleFromCenter) {
+      var topd  = (height - this.dims[0])/2;
+      var leftd = (width  - this.dims[1])/2;
+      if(this.elementPositioning == 'absolute') {
+        if(this.options.scaleY) d.top = this.originalTop-topd + 'px';
+        if(this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
+      } else {
+        if(this.options.scaleY) d.top = -topd + 'px';
+        if(this.options.scaleX) d.left = -leftd + 'px';
+      }
+    }
+    this.element.setStyle(d);
+  }
+});
+
+Effect.Highlight = Class.create();
+Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), {
+  initialize: function(element) {
+    this.element = $PR(element);
+    var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {});
+    this.start(options);
+  },
+  setup: function() {
+    // Prevent executing on elements not in the layout flow
+    if(this.element.getStyle('display')=='none') { this.cancel(); return; }
+    // Disable background image during the effect
+    this.oldStyle = {
+      backgroundImage: this.element.getStyle('background-image') };
+    this.element.setStyle({backgroundImage: 'none'});
+    if(!this.options.endcolor)
+      this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
+    if(!this.options.restorecolor)
+      this.options.restorecolor = this.element.getStyle('background-color');
+    // init color calculations
+    this._base  = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
+    this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
+  },
+  update: function(position) {
+    this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
+      return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) });
+  },
+  finish: function() {
+    this.element.setStyle(Object.extend(this.oldStyle, {
+      backgroundColor: this.options.restorecolor
+    }));
+  }
+});
+
+Effect.ScrollTo = Class.create();
+Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), {
+  initialize: function(element) {
+    this.element = $PR(element);
+    this.start(arguments[1] || {});
+  },
+  setup: function() {
+    Position.prepare();
+    var offsets = Position.cumulativeOffset(this.element);
+    if(this.options.offset) offsets[1] += this.options.offset;
+    var max = window.innerHeight ? 
+      window.height - window.innerHeight :
+      document.body.scrollHeight - 
+        (document.documentElement.clientHeight ? 
+          document.documentElement.clientHeight : document.body.clientHeight);
+    this.scrollStart = Position.deltaY;
+    this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart;
+  },
+  update: function(position) {
+    Position.prepare();
+    window.scrollTo(Position.deltaX, 
+      this.scrollStart + (position*this.delta));
+  }
+});
+
+/* ------------- combination effects ------------- */
+
+Effect.Fade = function(element) {
+  element = $PR(element);
+  var oldOpacity = element.getInlineOpacity();
+  var options = Object.extend({
+  from: element.getOpacity() || 1.0,
+  to:   0.0,
+  afterFinishInternal: function(effect) { 
+    if(effect.options.to!=0) return;
+    effect.element.hide();
+    effect.element.setStyle({opacity: oldOpacity}); 
+  }}, arguments[1] || {});
+  return new Effect.Opacity(element,options);
+}
+
+Effect.Appear = function(element) {
+  element = $PR(element);
+  var options = Object.extend({
+  from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),
+  to:   1.0,
+  // force Safari to render floated elements properly
+  afterFinishInternal: function(effect) {
+    effect.element.forceRerendering();
+  },
+  beforeSetup: function(effect) {
+    effect.element.setOpacity(effect.options.from);
+    effect.element.show(); 
+  }}, arguments[1] || {});
+  return new Effect.Opacity(element,options);
+}
+
+Effect.Puff = function(element) {
+  element = $PR(element);
+  var oldStyle = { opacity: element.getInlineOpacity(), position: element.getStyle('position') };
+  return new Effect.Parallel(
+   [ new Effect.Scale(element, 200, 
+      { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), 
+     new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], 
+     Object.extend({ duration: 1.0, 
+      beforeSetupInternal: function(effect) {
+        effect.effects[0].element.setStyle({position: 'absolute'}); },
+      afterFinishInternal: function(effect) {
+         effect.effects[0].element.hide();
+         effect.effects[0].element.setStyle(oldStyle); }
+     }, arguments[1] || {})
+   );
+}
+
+Effect.BlindUp = function(element) {
+  element = $PR(element);
+  element.makeClipping();
+  return new Effect.Scale(element, 0, 
+    Object.extend({ scaleContent: false, 
+      scaleX: false, 
+      restoreAfterFinish: true,
+      afterFinishInternal: function(effect) {
+        effect.element.hide();
+        effect.element.undoClipping();
+      } 
+    }, arguments[1] || {})
+  );
+}
+
+Effect.BlindDown = function(element) {
+  element = $PR(element);
+  var elementDimensions = element.getDimensions();
+  return new Effect.Scale(element, 100, 
+    Object.extend({ scaleContent: false, 
+      scaleX: false,
+      scaleFrom: 0,
+      scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+      restoreAfterFinish: true,
+      afterSetup: function(effect) {
+        effect.element.makeClipping();
+        effect.element.setStyle({height: '0px'});
+        effect.element.show(); 
+      },  
+      afterFinishInternal: function(effect) {
+        effect.element.undoClipping();
+      }
+    }, arguments[1] || {})
+  );
+}
+
+Effect.SwitchOff = function(element) {
+  element = $PR(element);
+  var oldOpacity = element.getInlineOpacity();
+  return new Effect.Appear(element, { 
+    duration: 0.4,
+    from: 0,
+    transition: Effect.Transitions.flicker,
+    afterFinishInternal: function(effect) {
+      new Effect.Scale(effect.element, 1, { 
+        duration: 0.3, scaleFromCenter: true,
+        scaleX: false, scaleContent: false, restoreAfterFinish: true,
+        beforeSetup: function(effect) { 
+          effect.element.makePositioned();
+          effect.element.makeClipping();
+        },
+        afterFinishInternal: function(effect) {
+          effect.element.hide();
+          effect.element.undoClipping();
+          effect.element.undoPositioned();
+          effect.element.setStyle({opacity: oldOpacity});
+        }
+      })
+    }
+  });
+}
+
+Effect.DropOut = function(element) {
+  element = $PR(element);
+  var oldStyle = {
+    top: element.getStyle('top'),
+    left: element.getStyle('left'),
+    opacity: element.getInlineOpacity() };
+  return new Effect.Parallel(
+    [ new Effect.Move(element, {x: 0, y: 100, sync: true }), 
+      new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
+    Object.extend(
+      { duration: 0.5,
+        beforeSetup: function(effect) {
+          effect.effects[0].element.makePositioned(); 
+        },
+        afterFinishInternal: function(effect) {
+          effect.effects[0].element.hide();
+          effect.effects[0].element.undoPositioned();
+          effect.effects[0].element.setStyle(oldStyle);
+        } 
+      }, arguments[1] || {}));
+}
+
+Effect.Shake = function(element) {
+  element = $PR(element);
+  var oldStyle = {
+    top: element.getStyle('top'),
+    left: element.getStyle('left') };
+    return new Effect.Move(element, 
+      { x:  20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x: -40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x:  40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x: -40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x:  40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
+        effect.element.undoPositioned();
+        effect.element.setStyle(oldStyle);
+  }}) }}) }}) }}) }}) }});
+}
+
+Effect.SlideDown = function(element) {
+  element = $PR(element);
+  element.cleanWhitespace();
+  // SlideDown need to have the content of the element wrapped in a container element with fixed height!
+  var oldInnerBottom = $PR(element.firstChild).getStyle('bottom');
+  var elementDimensions = element.getDimensions();
+  return new Effect.Scale(element, 100, Object.extend({ 
+    scaleContent: false, 
+    scaleX: false, 
+    scaleFrom: window.opera ? 0 : 1,
+    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+    restoreAfterFinish: true,
+    afterSetup: function(effect) {
+      effect.element.makePositioned();
+      effect.element.firstChild.makePositioned();
+      if(window.opera) effect.element.setStyle({top: ''});
+      effect.element.makeClipping();
+      effect.element.setStyle({height: '0px'});
+      effect.element.show(); },
+    afterUpdateInternal: function(effect) {
+      effect.element.firstChild.setStyle({bottom:
+        (effect.dims[0] - effect.element.clientHeight) + 'px' }); 
+    },
+    afterFinishInternal: function(effect) {
+      effect.element.undoClipping(); 
+      // IE will crash if child is undoPositioned first
+      if(/MSIE/.test(navigator.userAgent)){
+        effect.element.undoPositioned();
+        effect.element.firstChild.undoPositioned();
+      }else{
+        effect.element.firstChild.undoPositioned();
+        effect.element.undoPositioned();
+      }
+      effect.element.firstChild.setStyle({bottom: oldInnerBottom}); }
+    }, arguments[1] || {})
+  );
+}
+  
+Effect.SlideUp = function(element) {
+  element = $PR(element);
+  element.cleanWhitespace();
+  var oldInnerBottom = $PR(element.firstChild).getStyle('bottom');
+  return new Effect.Scale(element, window.opera ? 0 : 1,
+   Object.extend({ scaleContent: false, 
+    scaleX: false, 
+    scaleMode: 'box',
+    scaleFrom: 100,
+    restoreAfterFinish: true,
+    beforeStartInternal: function(effect) {
+      effect.element.makePositioned();
+      effect.element.firstChild.makePositioned();
+      if(window.opera) effect.element.setStyle({top: ''});
+      effect.element.makeClipping();
+      effect.element.show(); },  
+    afterUpdateInternal: function(effect) {
+      effect.element.firstChild.setStyle({bottom:
+        (effect.dims[0] - effect.element.clientHeight) + 'px' }); },
+    afterFinishInternal: function(effect) {
+      effect.element.hide();
+      effect.element.undoClipping();
+      effect.element.firstChild.undoPositioned();
+      effect.element.undoPositioned();
+      effect.element.setStyle({bottom: oldInnerBottom}); }
+   }, arguments[1] || {})
+  );
+}
+
+// Bug in opera makes the TD containing this element expand for a instance after finish 
+Effect.Squish = function(element) {
+  return new Effect.Scale(element, window.opera ? 1 : 0, 
+    { restoreAfterFinish: true,
+      beforeSetup: function(effect) {
+        effect.element.makeClipping(effect.element); },  
+      afterFinishInternal: function(effect) {
+        effect.element.hide(effect.element); 
+        effect.element.undoClipping(effect.element); }
+  });
+}
+
+Effect.Grow = function(element) {
+  element = $PR(element);
+  var options = Object.extend({
+    direction: 'center',
+    moveTransition: Effect.Transitions.sinoidal,
+    scaleTransition: Effect.Transitions.sinoidal,
+    opacityTransition: Effect.Transitions.full
+  }, arguments[1] || {});
+  var oldStyle = {
+    top: element.style.top,
+    left: element.style.left,
+    height: element.style.height,
+    width: element.style.width,
+    opacity: element.getInlineOpacity() };
+
+  var dims = element.getDimensions();    
+  var initialMoveX, initialMoveY;
+  var moveX, moveY;
+  
+  switch (options.direction) {
+    case 'top-left':
+      initialMoveX = initialMoveY = moveX = moveY = 0; 
+      break;
+    case 'top-right':
+      initialMoveX = dims.width;
+      initialMoveY = moveY = 0;
+      moveX = -dims.width;
+      break;
+    case 'bottom-left':
+      initialMoveX = moveX = 0;
+      initialMoveY = dims.height;
+      moveY = -dims.height;
+      break;
+    case 'bottom-right':
+      initialMoveX = dims.width;
+      initialMoveY = dims.height;
+      moveX = -dims.width;
+      moveY = -dims.height;
+      break;
+    case 'center':
+      initialMoveX = dims.width / 2;
+      initialMoveY = dims.height / 2;
+      moveX = -dims.width / 2;
+      moveY = -dims.height / 2;
+      break;
+  }
+  
+  return new Effect.Move(element, {
+    x: initialMoveX,
+    y: initialMoveY,
+    duration: 0.01, 
+    beforeSetup: function(effect) {
+      effect.element.hide();
+      effect.element.makeClipping();
+      effect.element.makePositioned();
+    },
+    afterFinishInternal: function(effect) {
+      new Effect.Parallel(
+        [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
+          new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
+          new Effect.Scale(effect.element, 100, {
+            scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, 
+            sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
+        ], Object.extend({
+             beforeSetup: function(effect) {
+               effect.effects[0].element.setStyle({height: '0px'});
+               effect.effects[0].element.show(); 
+             },
+             afterFinishInternal: function(effect) {
+               effect.effects[0].element.undoClipping();
+               effect.effects[0].element.undoPositioned();
+               effect.effects[0].element.setStyle(oldStyle); 
+             }
+           }, options)
+      )
+    }
+  });
+}
+
+Effect.Shrink = function(element) {
+  element = $PR(element);
+  var options = Object.extend({
+    direction: 'center',
+    moveTransition: Effect.Transitions.sinoidal,
+    scaleTransition: Effect.Transitions.sinoidal,
+    opacityTransition: Effect.Transitions.none
+  }, arguments[1] || {});
+  var oldStyle = {
+    top: element.style.top,
+    left: element.style.left,
+    height: element.style.height,
+    width: element.style.width,
+    opacity: element.getInlineOpacity() };
+
+  var dims = element.getDimensions();
+  var moveX, moveY;
+  
+  switch (options.direction) {
+    case 'top-left':
+      moveX = moveY = 0;
+      break;
+    case 'top-right':
+      moveX = dims.width;
+      moveY = 0;
+      break;
+    case 'bottom-left':
+      moveX = 0;
+      moveY = dims.height;
+      break;
+    case 'bottom-right':
+      moveX = dims.width;
+      moveY = dims.height;
+      break;
+    case 'center':  
+      moveX = dims.width / 2;
+      moveY = dims.height / 2;
+      break;
+  }
+  
+  return new Effect.Parallel(
+    [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
+      new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
+      new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
+    ], Object.extend({            
+         beforeStartInternal: function(effect) {
+           effect.effects[0].element.makePositioned();
+           effect.effects[0].element.makeClipping(); },
+         afterFinishInternal: function(effect) {
+           effect.effects[0].element.hide();
+           effect.effects[0].element.undoClipping();
+           effect.effects[0].element.undoPositioned();
+           effect.effects[0].element.setStyle(oldStyle); }
+       }, options)
+  );
+}
+
+Effect.Pulsate = function(element) {
+  element = $PR(element);
+  var options    = arguments[1] || {};
+  var oldOpacity = element.getInlineOpacity();
+  var transition = options.transition || Effect.Transitions.sinoidal;
+  var reverser   = function(pos){ return transition(1-Effect.Transitions.pulse(pos)) };
+  reverser.bind(transition);
+  return new Effect.Opacity(element, 
+    Object.extend(Object.extend({  duration: 3.0, from: 0,
+      afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
+    }, options), {transition: reverser}));
+}
+
+Effect.Fold = function(element) {
+  element = $PR(element);
+  var oldStyle = {
+    top: element.style.top,
+    left: element.style.left,
+    width: element.style.width,
+    height: element.style.height };
+  Element.makeClipping(element);
+  return new Effect.Scale(element, 5, Object.extend({   
+    scaleContent: false,
+    scaleX: false,
+    afterFinishInternal: function(effect) {
+    new Effect.Scale(element, 1, { 
+      scaleContent: false, 
+      scaleY: false,
+      afterFinishInternal: function(effect) {
+        effect.element.hide();
+        effect.element.undoClipping(); 
+        effect.element.setStyle(oldStyle);
+      } });
+  }}, arguments[1] || {}));
+};
+
+['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom',
+ 'collectTextNodes','collectTextNodesIgnoreClass','childrenWithClassName'].each( 
+  function(f) { Element.Methods[f] = Element[f]; }
+);
+
+Element.Methods.visualEffect = function(element, effect, options) {
+  s = effect.gsub(/_/, '-').camelize();
+  effect_class = s.charAt(0).toUpperCase() + s.substring(1);
+  new Effect[effect_class](element, options);
+  return $PR(element);
+};
+
+Element.addMethods();
diff --git a/view/js/cropper/lib/prototype.js b/view/js/cropper/lib/prototype.js
new file mode 100644 (file)
index 0000000..6682065
--- /dev/null
@@ -0,0 +1,2006 @@
+/*  Prototype JavaScript framework, version 1.5.0_rc0
+ *  (c) 2005 Sam Stephenson <sam@conio.net>
+ *
+ *  Prototype is freely distributable under the terms of an MIT-style license.
+ *  For details, see the Prototype web site: http://prototype.conio.net/
+ *
+/*--------------------------------------------------------------------------*/
+
+var Prototype = {
+  Version: '1.5.0_rc0',
+  ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
+
+  emptyFunction: function() {},
+  K: function(x) {return x}
+}
+
+var Class = {
+  create: function() {
+    return function() {
+      this.initialize.apply(this, arguments);
+    }
+  }
+}
+
+var Abstract = new Object();
+
+Object.extend = function(destination, source) {
+  for (var property in source) {
+    destination[property] = source[property];
+  }
+  return destination;
+}
+
+Object.inspect = function(object) {
+  try {
+    if (object == undefined) return 'undefined';
+    if (object == null) return 'null';
+    return object.inspect ? object.inspect() : object.toString();
+  } catch (e) {
+    if (e instanceof RangeError) return '...';
+    throw e;
+  }
+}
+
+Function.prototype.bind = function() {
+  var __method = this, args = $A(arguments), object = args.shift();
+  return function() {
+    return __method.apply(object, args.concat($A(arguments)));
+  }
+}
+
+Function.prototype.bindAsEventListener = function(object) {
+  var __method = this;
+  return function(event) {
+    return __method.call(object, event || window.event);
+  }
+}
+
+Object.extend(Number.prototype, {
+  toColorPart: function() {
+    var digits = this.toString(16);
+    if (this < 16) return '0' + digits;
+    return digits;
+  },
+
+  succ: function() {
+    return this + 1;
+  },
+
+  times: function(iterator) {
+    $R(0, this, true).each(iterator);
+    return this;
+  }
+});
+
+var Try = {
+  these: function() {
+    var returnValue;
+
+    for (var i = 0; i < arguments.length; i++) {
+      var lambda = arguments[i];
+      try {
+        returnValue = lambda();
+        break;
+      } catch (e) {}
+    }
+
+    return returnValue;
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var PeriodicalExecuter = Class.create();
+PeriodicalExecuter.prototype = {
+  initialize: function(callback, frequency) {
+    this.callback = callback;
+    this.frequency = frequency;
+    this.currentlyExecuting = false;
+
+    this.registerCallback();
+  },
+
+  registerCallback: function() {
+    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+  },
+
+  onTimerEvent: function() {
+    if (!this.currentlyExecuting) {
+      try {
+        this.currentlyExecuting = true;
+        this.callback();
+      } finally {
+        this.currentlyExecuting = false;
+      }
+    }
+  }
+}
+Object.extend(String.prototype, {
+  gsub: function(pattern, replacement) {
+    var result = '', source = this, match;
+    replacement = arguments.callee.prepareReplacement(replacement);
+
+    while (source.length > 0) {
+      if (match = source.match(pattern)) {
+        result += source.slice(0, match.index);
+        result += (replacement(match) || '').toString();
+        source  = source.slice(match.index + match[0].length);
+      } else {
+        result += source, source = '';
+      }
+    }
+    return result;
+  },
+
+  sub: function(pattern, replacement, count) {
+    replacement = this.gsub.prepareReplacement(replacement);
+    count = count === undefined ? 1 : count;
+
+    return this.gsub(pattern, function(match) {
+      if (--count < 0) return match[0];
+      return replacement(match);
+    });
+  },
+
+  scan: function(pattern, iterator) {
+    this.gsub(pattern, iterator);
+    return this;
+  },
+
+  truncate: function(length, truncation) {
+    length = length || 30;
+    truncation = truncation === undefined ? '...' : truncation;
+    return this.length > length ?
+      this.slice(0, length - truncation.length) + truncation : this;
+  },
+
+  strip: function() {
+    return this.replace(/^\s+/, '').replace(/\s+$/, '');
+  },
+
+  stripTags: function() {
+    return this.replace(/<\/?[^>]+>/gi, '');
+  },
+
+  stripScripts: function() {
+    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
+  },
+
+  extractScripts: function() {
+    var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
+    var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
+    return (this.match(matchAll) || []).map(function(scriptTag) {
+      return (scriptTag.match(matchOne) || ['', ''])[1];
+    });
+  },
+
+  evalScripts: function() {
+    return this.extractScripts().map(function(script) { return eval(script) });
+  },
+
+  escapeHTML: function() {
+    var div = document.createElement('div');
+    var text = document.createTextNode(this);
+    div.appendChild(text);
+    return div.innerHTML;
+  },
+
+  unescapeHTML: function() {
+    var div = document.createElement('div');
+    div.innerHTML = this.stripTags();
+    return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
+  },
+
+  toQueryParams: function() {
+    var pairs = this.match(/^\??(.*)$/)[1].split('&');
+    return pairs.inject({}, function(params, pairString) {
+      var pair = pairString.split('=');
+      params[pair[0]] = pair[1];
+      return params;
+    });
+  },
+
+  toArray: function() {
+    return this.split('');
+  },
+
+  camelize: function() {
+    var oStringList = this.split('-');
+    if (oStringList.length == 1) return oStringList[0];
+
+    var camelizedString = this.indexOf('-') == 0
+      ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
+      : oStringList[0];
+
+    for (var i = 1, len = oStringList.length; i < len; i++) {
+      var s = oStringList[i];
+      camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
+    }
+
+    return camelizedString;
+  },
+
+  inspect: function() {
+    return "'" + this.replace(/\\/g, '\\\\').replace(/'/g, '\\\'') + "'";
+  }
+});
+
+String.prototype.gsub.prepareReplacement = function(replacement) {
+  if (typeof replacement == 'function') return replacement;
+  var template = new Template(replacement);
+  return function(match) { return template.evaluate(match) };
+}
+
+String.prototype.parseQuery = String.prototype.toQueryParams;
+
+var Template = Class.create();
+Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
+Template.prototype = {
+  initialize: function(template, pattern) {
+    this.template = template.toString();
+    this.pattern  = pattern || Template.Pattern;
+  },
+
+  evaluate: function(object) {
+    return this.template.gsub(this.pattern, function(match) {
+      var before = match[1];
+      if (before == '\\') return match[2];
+      return before + (object[match[3]] || '').toString();
+    });
+  }
+}
+
+var $break    = new Object();
+var $continue = new Object();
+
+var Enumerable = {
+  each: function(iterator) {
+    var index = 0;
+    try {
+      this._each(function(value) {
+        try {
+          iterator(value, index++);
+        } catch (e) {
+          if (e != $continue) throw e;
+        }
+      });
+    } catch (e) {
+      if (e != $break) throw e;
+    }
+  },
+
+  all: function(iterator) {
+    var result = true;
+    this.each(function(value, index) {
+      result = result && !!(iterator || Prototype.K)(value, index);
+      if (!result) throw $break;
+    });
+    return result;
+  },
+
+  any: function(iterator) {
+    var result = true;
+    this.each(function(value, index) {
+      if (result = !!(iterator || Prototype.K)(value, index))
+        throw $break;
+    });
+    return result;
+  },
+
+  collect: function(iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      results.push(iterator(value, index));
+    });
+    return results;
+  },
+
+  detect: function (iterator) {
+    var result;
+    this.each(function(value, index) {
+      if (iterator(value, index)) {
+        result = value;
+        throw $break;
+      }
+    });
+    return result;
+  },
+
+  findAll: function(iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      if (iterator(value, index))
+        results.push(value);
+    });
+    return results;
+  },
+
+  grep: function(pattern, iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      var stringValue = value.toString();
+      if (stringValue.match(pattern))
+        results.push((iterator || Prototype.K)(value, index));
+    })
+    return results;
+  },
+
+  include: function(object) {
+    var found = false;
+    this.each(function(value) {
+      if (value == object) {
+        found = true;
+        throw $break;
+      }
+    });
+    return found;
+  },
+
+  inject: function(memo, iterator) {
+    this.each(function(value, index) {
+      memo = iterator(memo, value, index);
+    });
+    return memo;
+  },
+
+  invoke: function(method) {
+    var args = $A(arguments).slice(1);
+    return this.collect(function(value) {
+      return value[method].apply(value, args);
+    });
+  },
+
+  max: function(iterator) {
+    var result;
+    this.each(function(value, index) {
+      value = (iterator || Prototype.K)(value, index);
+      if (result == undefined || value >= result)
+        result = value;
+    });
+    return result;
+  },
+
+  min: function(iterator) {
+    var result;
+    this.each(function(value, index) {
+      value = (iterator || Prototype.K)(value, index);
+      if (result == undefined || value < result)
+        result = value;
+    });
+    return result;
+  },
+
+  partition: function(iterator) {
+    var trues = [], falses = [];
+    this.each(function(value, index) {
+      ((iterator || Prototype.K)(value, index) ?
+        trues : falses).push(value);
+    });
+    return [trues, falses];
+  },
+
+  pluck: function(property) {
+    var results = [];
+    this.each(function(value, index) {
+      results.push(value[property]);
+    });
+    return results;
+  },
+
+  reject: function(iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      if (!iterator(value, index))
+        results.push(value);
+    });
+    return results;
+  },
+
+  sortBy: function(iterator) {
+    return this.collect(function(value, index) {
+      return {value: value, criteria: iterator(value, index)};
+    }).sort(function(left, right) {
+      var a = left.criteria, b = right.criteria;
+      return a < b ? -1 : a > b ? 1 : 0;
+    }).pluck('value');
+  },
+
+  toArray: function() {
+    return this.collect(Prototype.K);
+  },
+
+  zip: function() {
+    var iterator = Prototype.K, args = $A(arguments);
+    if (typeof args.last() == 'function')
+      iterator = args.pop();
+
+    var collections = [this].concat(args).map($A);
+    return this.map(function(value, index) {
+      return iterator(collections.pluck(index));
+    });
+  },
+
+  inspect: function() {
+    return '#<Enumerable:' + this.toArray().inspect() + '>';
+  }
+}
+
+Object.extend(Enumerable, {
+  map:     Enumerable.collect,
+  find:    Enumerable.detect,
+  select:  Enumerable.findAll,
+  member:  Enumerable.include,
+  entries: Enumerable.toArray
+});
+var $A = Array.from = function(iterable) {
+  if (!iterable) return [];
+  if (iterable.toArray) {
+    return iterable.toArray();
+  } else {
+    var results = [];
+    for (var i = 0; i < iterable.length; i++)
+      results.push(iterable[i]);
+    return results;
+  }
+}
+
+Object.extend(Array.prototype, Enumerable);
+
+if (!Array.prototype._reverse)
+  Array.prototype._reverse = Array.prototype.reverse;
+
+Object.extend(Array.prototype, {
+  _each: function(iterator) {
+    for (var i = 0; i < this.length; i++)
+      iterator(this[i]);
+  },
+
+  clear: function() {
+    this.length = 0;
+    return this;
+  },
+
+  first: function() {
+    return this[0];
+  },
+
+  last: function() {
+    return this[this.length - 1];
+  },
+
+  compact: function() {
+    return this.select(function(value) {
+      return value != undefined || value != null;
+    });
+  },
+
+  flatten: function() {
+    return this.inject([], function(array, value) {
+      return array.concat(value && value.constructor == Array ?
+        value.flatten() : [value]);
+    });
+  },
+
+  without: function() {
+    var values = $A(arguments);
+    return this.select(function(value) {
+      return !values.include(value);
+    });
+  },
+
+  indexOf: function(object) {
+    for (var i = 0; i < this.length; i++)
+      if (this[i] == object) return i;
+    return -1;
+  },
+
+  reverse: function(inline) {
+    return (inline !== false ? this : this.toArray())._reverse();
+  },
+
+  inspect: function() {
+    return '[' + this.map(Object.inspect).join(', ') + ']';
+  }
+});
+var Hash = {
+  _each: function(iterator) {
+    for (var key in this) {
+      var value = this[key];
+      if (typeof value == 'function') continue;
+
+      var pair = [key, value];
+      pair.key = key;
+      pair.value = value;
+      iterator(pair);
+    }
+  },
+
+  keys: function() {
+    return this.pluck('key');
+  },
+
+  values: function() {
+    return this.pluck('value');
+  },
+
+  merge: function(hash) {
+    return $H(hash).inject($H(this), function(mergedHash, pair) {
+      mergedHash[pair.key] = pair.value;
+      return mergedHash;
+    });
+  },
+
+  toQueryString: function() {
+    return this.map(function(pair) {
+      return pair.map(encodeURIComponent).join('=');
+    }).join('&');
+  },
+
+  inspect: function() {
+    return '#<Hash:{' + this.map(function(pair) {
+      return pair.map(Object.inspect).join(': ');
+    }).join(', ') + '}>';
+  }
+}
+
+function $H(object) {
+  var hash = Object.extend({}, object || {});
+  Object.extend(hash, Enumerable);
+  Object.extend(hash, Hash);
+  return hash;
+}
+ObjectRange = Class.create();
+Object.extend(ObjectRange.prototype, Enumerable);
+Object.extend(ObjectRange.prototype, {
+  initialize: function(start, end, exclusive) {
+    this.start = start;
+    this.end = end;
+    this.exclusive = exclusive;
+  },
+
+  _each: function(iterator) {
+    var value = this.start;
+    do {
+      iterator(value);
+      value = value.succ();
+    } while (this.include(value));
+  },
+
+  include: function(value) {
+    if (value < this.start)
+      return false;
+    if (this.exclusive)
+      return value < this.end;
+    return value <= this.end;
+  }
+});
+
+var $R = function(start, end, exclusive) {
+  return new ObjectRange(start, end, exclusive);
+}
+
+var Ajax = {
+  getTransport: function() {
+    return Try.these(
+      function() {return new XMLHttpRequest()},
+      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
+      function() {return new ActiveXObject('Microsoft.XMLHTTP')}
+    ) || false;
+  },
+
+  activeRequestCount: 0
+}
+
+Ajax.Responders = {
+  responders: [],
+
+  _each: function(iterator) {
+    this.responders._each(iterator);
+  },
+
+  register: function(responderToAdd) {
+    if (!this.include(responderToAdd))
+      this.responders.push(responderToAdd);
+  },
+
+  unregister: function(responderToRemove) {
+    this.responders = this.responders.without(responderToRemove);
+  },
+
+  dispatch: function(callback, request, transport, json) {
+    this.each(function(responder) {
+      if (responder[callback] && typeof responder[callback] == 'function') {
+        try {
+          responder[callback].apply(responder, [request, transport, json]);
+        } catch (e) {}
+      }
+    });
+  }
+};
+
+Object.extend(Ajax.Responders, Enumerable);
+
+Ajax.Responders.register({
+  onCreate: function() {
+    Ajax.activeRequestCount++;
+  },
+
+  onComplete: function() {
+    Ajax.activeRequestCount--;
+  }
+});
+
+Ajax.Base = function() {};
+Ajax.Base.prototype = {
+  setOptions: function(options) {
+    this.options = {
+      method:       'post',
+      asynchronous: true,
+      contentType:  'application/x-www-form-urlencoded',
+      parameters:   ''
+    }
+    Object.extend(this.options, options || {});
+  },
+
+  responseIsSuccess: function() {
+    return this.transport.status == undefined
+        || this.transport.status == 0
+        || (this.transport.status >= 200 && this.transport.status < 300);
+  },
+
+  responseIsFailure: function() {
+    return !this.responseIsSuccess();
+  }
+}
+
+Ajax.Request = Class.create();
+Ajax.Request.Events =
+  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
+  initialize: function(url, options) {
+    this.transport = Ajax.getTransport();
+    this.setOptions(options);
+    this.request(url);
+  },
+
+  request: function(url) {
+    var parameters = this.options.parameters || '';
+    if (parameters.length > 0) parameters += '&_=';
+
+    try {
+      this.url = url;
+      if (this.options.method == 'get' && parameters.length > 0)
+        this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;
+
+      Ajax.Responders.dispatch('onCreate', this, this.transport);
+
+      this.transport.open(this.options.method, this.url,
+        this.options.asynchronous);
+
+      if (this.options.asynchronous) {
+        this.transport.onreadystatechange = this.onStateChange.bind(this);
+        setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
+      }
+
+      this.setRequestHeaders();
+
+      var body = this.options.postBody ? this.options.postBody : parameters;
+      this.transport.send(this.options.method == 'post' ? body : null);
+
+    } catch (e) {
+      this.dispatchException(e);
+    }
+  },
+
+  setRequestHeaders: function() {
+    var requestHeaders =
+      ['X-Requested-With', 'XMLHttpRequest',
+       'X-Prototype-Version', Prototype.Version,
+       'Accept', 'text/javascript, text/html, application/xml, text/xml, */*'];
+
+    if (this.options.method == 'post') {
+      requestHeaders.push('Content-type', this.options.contentType);
+
+      /* Force "Connection: close" for Mozilla browsers to work around
+       * a bug where XMLHttpReqeuest sends an incorrect Content-length
+       * header. See Mozilla Bugzilla #246651.
+       */
+      if (this.transport.overrideMimeType)
+        requestHeaders.push('Connection', 'close');
+    }
+
+    if (this.options.requestHeaders)
+      requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);
+
+    for (var i = 0; i < requestHeaders.length; i += 2)
+      this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);
+  },
+
+  onStateChange: function() {
+    var readyState = this.transport.readyState;
+    if (readyState != 1)
+      this.respondToReadyState(this.transport.readyState);
+  },
+
+  header: function(name) {
+    try {
+      return this.transport.getResponseHeader(name);
+    } catch (e) {}
+  },
+
+  evalJSON: function() {
+    try {
+      return eval('(' + this.header('X-JSON') + ')');
+    } catch (e) {}
+  },
+
+  evalResponse: function() {
+    try {
+      return eval(this.transport.responseText);
+    } catch (e) {
+      this.dispatchException(e);
+    }
+  },
+
+  respondToReadyState: function(readyState) {
+    var event = Ajax.Request.Events[readyState];
+    var transport = this.transport, json = this.evalJSON();
+
+    if (event == 'Complete') {
+      try {
+        (this.options['on' + this.transport.status]
+         || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
+         || Prototype.emptyFunction)(transport, json);
+      } catch (e) {
+        this.dispatchException(e);
+      }
+
+      if ((this.header('Content-type') || '').match(/^text\/javascript/i))
+        this.evalResponse();
+    }
+
+    try {
+      (this.options['on' + event] || Prototype.emptyFunction)(transport, json);
+      Ajax.Responders.dispatch('on' + event, this, transport, json);
+    } catch (e) {
+      this.dispatchException(e);
+    }
+
+    /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
+    if (event == 'Complete')
+      this.transport.onreadystatechange = Prototype.emptyFunction;
+  },
+
+  dispatchException: function(exception) {
+    (this.options.onException || Prototype.emptyFunction)(this, exception);
+    Ajax.Responders.dispatch('onException', this, exception);
+  }
+});
+
+Ajax.Updater = Class.create();
+
+Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
+  initialize: function(container, url, options) {
+    this.containers = {
+      success: container.success ? $PR(container.success) : $PR(container),
+      failure: container.failure ? $PR(container.failure) :
+        (container.success ? null : $PR(container))
+    }
+
+    this.transport = Ajax.getTransport();
+    this.setOptions(options);
+
+    var onComplete = this.options.onComplete || Prototype.emptyFunction;
+    this.options.onComplete = (function(transport, object) {
+      this.updateContent();
+      onComplete(transport, object);
+    }).bind(this);
+
+    this.request(url);
+  },
+
+  updateContent: function() {
+    var receiver = this.responseIsSuccess() ?
+      this.containers.success : this.containers.failure;
+    var response = this.transport.responseText;
+
+    if (!this.options.evalScripts)
+      response = response.stripScripts();
+
+    if (receiver) {
+      if (this.options.insertion) {
+        new this.options.insertion(receiver, response);
+      } else {
+        Element.update(receiver, response);
+      }
+    }
+
+    if (this.responseIsSuccess()) {
+      if (this.onComplete)
+        setTimeout(this.onComplete.bind(this), 10);
+    }
+  }
+});
+
+Ajax.PeriodicalUpdater = Class.create();
+Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
+  initialize: function(container, url, options) {
+    this.setOptions(options);
+    this.onComplete = this.options.onComplete;
+
+    this.frequency = (this.options.frequency || 2);
+    this.decay = (this.options.decay || 1);
+
+    this.updater = {};
+    this.container = container;
+    this.url = url;
+
+    this.start();
+  },
+
+  start: function() {
+    this.options.onComplete = this.updateComplete.bind(this);
+    this.onTimerEvent();
+  },
+
+  stop: function() {
+    this.updater.onComplete = undefined;
+    clearTimeout(this.timer);
+    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
+  },
+
+  updateComplete: function(request) {
+    if (this.options.decay) {
+      this.decay = (request.responseText == this.lastText ?
+        this.decay * this.options.decay : 1);
+
+      this.lastText = request.responseText;
+    }
+    this.timer = setTimeout(this.onTimerEvent.bind(this),
+      this.decay * this.frequency * 1000);
+  },
+
+  onTimerEvent: function() {
+    this.updater = new Ajax.Updater(this.container, this.url, this.options);
+  }
+});
+function $PR() {
+  var results = [], element;
+  for (var i = 0; i < arguments.length; i++) {
+    element = arguments[i];
+    if (typeof element == 'string')
+      element = document.getElementById(element);
+    results.push(Element.extend(element));
+  }
+  return results.length < 2 ? results[0] : results;
+}
+
+document.getElementsByClassName = function(className, parentElement) {
+  var children = ($PR(parentElement) || document.body).getElementsByTagName('*');
+  return $A(children).inject([], function(elements, child) {
+    if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
+      elements.push(Element.extend(child));
+    return elements;
+  });
+}
+
+/*--------------------------------------------------------------------------*/
+
+if (!window.Element)
+  var Element = new Object();
+
+Element.extend = function(element) {
+  if (!element) return;
+  if (_nativeExtensions) return element;
+
+  if (!element._extended && element.tagName && element != window) {
+    var methods = Element.Methods, cache = Element.extend.cache;
+    for (property in methods) {
+      var value = methods[property];
+      if (typeof value == 'function')
+        element[property] = cache.findOrStore(value);
+    }
+  }
+
+  element._extended = true;
+  return element;
+}
+
+Element.extend.cache = {
+  findOrStore: function(value) {
+    return this[value] = this[value] || function() {
+      return value.apply(null, [this].concat($A(arguments)));
+    }
+  }
+}
+
+Element.Methods = {
+  visible: function(element) {
+    return $PR(element).style.display != 'none';
+  },
+
+  toggle: function() {
+    for (var i = 0; i < arguments.length; i++) {
+      var element = $PR(arguments[i]);
+      Element[Element.visible(element) ? 'hide' : 'show'](element);
+    }
+  },
+
+  hide: function() {
+    for (var i = 0; i < arguments.length; i++) {
+      var element = $PR(arguments[i]);
+      element.style.display = 'none';
+    }
+  },
+
+  show: function() {
+    for (var i = 0; i < arguments.length; i++) {
+      var element = $PR(arguments[i]);
+      element.style.display = '';
+    }
+  },
+
+  remove: function(element) {
+    element = $PR(element);
+    element.parentNode.removeChild(element);
+  },
+
+  update: function(element, html) {
+    $PR(element).innerHTML = html.stripScripts();
+    setTimeout(function() {html.evalScripts()}, 10);
+  },
+
+  replace: function(element, html) {
+    element = $PR(element);
+    if (element.outerHTML) {
+      element.outerHTML = html.stripScripts();
+    } else {
+      var range = element.ownerDocument.createRange();
+      range.selectNodeContents(element);
+      element.parentNode.replaceChild(
+        range.createContextualFragment(html.stripScripts()), element);
+    }
+    setTimeout(function() {html.evalScripts()}, 10);
+  },
+
+  getHeight: function(element) {
+    element = $PR(element);
+    return element.offsetHeight;
+  },
+
+  classNames: function(element) {
+    return new Element.ClassNames(element);
+  },
+
+  hasClassName: function(element, className) {
+    if (!(element = $PR(element))) return;
+    return Element.classNames(element).include(className);
+  },
+
+  addClassName: function(element, className) {
+    if (!(element = $PR(element))) return;
+    return Element.classNames(element).add(className);
+  },
+
+  removeClassName: function(element, className) {
+    if (!(element = $PR(element))) return;
+    return Element.classNames(element).remove(className);
+  },
+
+  // removes whitespace-only text node children
+  cleanWhitespace: function(element) {
+    element = $PR(element);
+    for (var i = 0; i < element.childNodes.length; i++) {
+      var node = element.childNodes[i];
+      if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
+        Element.remove(node);
+    }
+  },
+
+  empty: function(element) {
+    return $PR(element).innerHTML.match(/^\s*$/);
+  },
+
+  childOf: function(element, ancestor) {
+    element = $PR(element), ancestor = $PR(ancestor);
+    while (element = element.parentNode)
+      if (element == ancestor) return true;
+    return false;
+  },
+
+  scrollTo: function(element) {
+    element = $PR(element);
+    var x = element.x ? element.x : element.offsetLeft,
+        y = element.y ? element.y : element.offsetTop;
+    window.scrollTo(x, y);
+  },
+
+  getStyle: function(element, style) {
+    element = $PR(element);
+    var value = element.style[style.camelize()];
+    if (!value) {
+      if (document.defaultView && document.defaultView.getComputedStyle) {
+        var css = document.defaultView.getComputedStyle(element, null);
+        value = css ? css.getPropertyValue(style) : null;
+      } else if (element.currentStyle) {
+        value = element.currentStyle[style.camelize()];
+      }
+    }
+
+    if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
+      if (Element.getStyle(element, 'position') == 'static') value = 'auto';
+
+    return value == 'auto' ? null : value;
+  },
+
+  setStyle: function(element, style) {
+    element = $PR(element);
+    for (var name in style)
+      element.style[name.camelize()] = style[name];
+  },
+
+  getDimensions: function(element) {
+    element = $PR(element);
+    if (Element.getStyle(element, 'display') != 'none')
+      return {width: element.offsetWidth, height: element.offsetHeight};
+
+    // All *Width and *Height properties give 0 on elements with display none,
+    // so enable the element temporarily
+    var els = element.style;
+    var originalVisibility = els.visibility;
+    var originalPosition = els.position;
+    els.visibility = 'hidden';
+    els.position = 'absolute';
+    els.display = '';
+    var originalWidth = element.clientWidth;
+    var originalHeight = element.clientHeight;
+    els.display = 'none';
+    els.position = originalPosition;
+    els.visibility = originalVisibility;
+    return {width: originalWidth, height: originalHeight};
+  },
+
+  makePositioned: function(element) {
+    element = $PR(element);
+    var pos = Element.getStyle(element, 'position');
+    if (pos == 'static' || !pos) {
+      element._madePositioned = true;
+      element.style.position = 'relative';
+      // Opera returns the offset relative to the positioning context, when an
+      // element is position relative but top and left have not been defined
+      if (window.opera) {
+        element.style.top = 0;
+        element.style.left = 0;
+      }
+    }
+  },
+
+  undoPositioned: function(element) {
+    element = $PR(element);
+    if (element._madePositioned) {
+      element._madePositioned = undefined;
+      element.style.position =
+        element.style.top =
+        element.style.left =
+        element.style.bottom =
+        element.style.right = '';
+    }
+  },
+
+  makeClipping: function(element) {
+    element = $PR(element);
+    if (element._overflow) return;
+    element._overflow = element.style.overflow;
+    if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
+      element.style.overflow = 'hidden';
+  },
+
+  undoClipping: function(element) {
+    element = $PR(element);
+    if (element._overflow) return;
+    element.style.overflow = element._overflow;
+    element._overflow = undefined;
+  }
+}
+
+Object.extend(Element, Element.Methods);
+
+var _nativeExtensions = false;
+
+if(!HTMLElement && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
+  var HTMLElement = {}
+  HTMLElement.prototype = document.createElement('div').__proto__;
+}
+
+Element.addMethods = function(methods) {
+  Object.extend(Element.Methods, methods || {});
+
+  if(typeof HTMLElement != 'undefined') {
+    var methods = Element.Methods, cache = Element.extend.cache;
+    for (property in methods) {
+      var value = methods[property];
+      if (typeof value == 'function')
+        HTMLElement.prototype[property] = cache.findOrStore(value);
+    }
+    _nativeExtensions = true;
+  }
+}
+
+Element.addMethods();
+
+var Toggle = new Object();
+Toggle.display = Element.toggle;
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.Insertion = function(adjacency) {
+  this.adjacency = adjacency;
+}
+
+Abstract.Insertion.prototype = {
+  initialize: function(element, content) {
+    this.element = $PR(element);
+    this.content = content.stripScripts();
+
+    if (this.adjacency && this.element.insertAdjacentHTML) {
+      try {
+        this.element.insertAdjacentHTML(this.adjacency, this.content);
+      } catch (e) {
+        var tagName = this.element.tagName.toLowerCase();
+        if (tagName == 'tbody' || tagName == 'tr') {
+          this.insertContent(this.contentFromAnonymousTable());
+        } else {
+          throw e;
+        }
+      }
+    } else {
+      this.range = this.element.ownerDocument.createRange();
+      if (this.initializeRange) this.initializeRange();
+      this.insertContent([this.range.createContextualFragment(this.content)]);
+    }
+
+    setTimeout(function() {content.evalScripts()}, 10);
+  },
+
+  contentFromAnonymousTable: function() {
+    var div = document.createElement('div');
+    div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
+    return $A(div.childNodes[0].childNodes[0].childNodes);
+  }
+}
+
+var Insertion = new Object();
+
+Insertion.Before = Class.create();
+Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
+  initializeRange: function() {
+    this.range.setStartBefore(this.element);
+  },
+
+  insertContent: function(fragments) {
+    fragments.each((function(fragment) {
+      this.element.parentNode.insertBefore(fragment, this.element);
+    }).bind(this));
+  }
+});
+
+Insertion.Top = Class.create();
+Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
+  initializeRange: function() {
+    this.range.selectNodeContents(this.element);
+    this.range.collapse(true);
+  },
+
+  insertContent: function(fragments) {
+    fragments.reverse(false).each((function(fragment) {
+      this.element.insertBefore(fragment, this.element.firstChild);
+    }).bind(this));
+  }
+});
+
+Insertion.Bottom = Class.create();
+Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
+  initializeRange: function() {
+    this.range.selectNodeContents(this.element);
+    this.range.collapse(this.element);
+  },
+
+  insertContent: function(fragments) {
+    fragments.each((function(fragment) {
+      this.element.appendChild(fragment);
+    }).bind(this));
+  }
+});
+
+Insertion.After = Class.create();
+Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
+  initializeRange: function() {
+    this.range.setStartAfter(this.element);
+  },
+
+  insertContent: function(fragments) {
+    fragments.each((function(fragment) {
+      this.element.parentNode.insertBefore(fragment,
+        this.element.nextSibling);
+    }).bind(this));
+  }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Element.ClassNames = Class.create();
+Element.ClassNames.prototype = {
+  initialize: function(element) {
+    this.element = $PR(element);
+  },
+
+  _each: function(iterator) {
+    this.element.className.split(/\s+/).select(function(name) {
+      return name.length > 0;
+    })._each(iterator);
+  },
+
+  set: function(className) {
+    this.element.className = className;
+  },
+
+  add: function(classNameToAdd) {
+    if (this.include(classNameToAdd)) return;
+    this.set(this.toArray().concat(classNameToAdd).join(' '));
+  },
+
+  remove: function(classNameToRemove) {
+    if (!this.include(classNameToRemove)) return;
+    this.set(this.select(function(className) {
+      return className != classNameToRemove;
+    }).join(' '));
+  },
+
+  toString: function() {
+    return this.toArray().join(' ');
+  }
+}
+
+Object.extend(Element.ClassNames.prototype, Enumerable);
+var Selector = Class.create();
+Selector.prototype = {
+  initialize: function(expression) {
+    this.params = {classNames: []};
+    this.expression = expression.toString().strip();
+    this.parseExpression();
+    this.compileMatcher();
+  },
+
+  parseExpression: function() {
+    function abort(message) { throw 'Parse error in selector: ' + message; }
+
+    if (this.expression == '')  abort('empty expression');
+
+    var params = this.params, expr = this.expression, match, modifier, clause, rest;
+    while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)) {
+      params.attributes = params.attributes || [];
+      params.attributes.push({name: match[2], operator: match[3], value: match[4] || match[5] || ''});
+      expr = match[1];
+    }
+
+    if (expr == '*') return this.params.wildcard = true;
+
+    while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)) {
+      modifier = match[1], clause = match[2], rest = match[3];
+      switch (modifier) {
+        case '#':       params.id = clause; break;
+        case '.':       params.classNames.push(clause); break;
+        case '':
+        case undefined: params.tagName = clause.toUpperCase(); break;
+        default:        abort(expr.inspect());
+      }
+      expr = rest;
+    }
+
+    if (expr.length > 0) abort(expr.inspect());
+  },
+
+  buildMatchExpression: function() {
+    var params = this.params, conditions = [], clause;
+
+    if (params.wildcard)
+      conditions.push('true');
+    if (clause = params.id)
+      conditions.push('element.id == ' + clause.inspect());
+    if (clause = params.tagName)
+      conditions.push('element.tagName.toUpperCase() == ' + clause.inspect());
+    if ((clause = params.classNames).length > 0)
+      for (var i = 0; i < clause.length; i++)
+        conditions.push('Element.hasClassName(element, ' + clause[i].inspect() + ')');
+    if (clause = params.attributes) {
+      clause.each(function(attribute) {
+        var value = 'element.getAttribute(' + attribute.name.inspect() + ')';
+        var splitValueBy = function(delimiter) {
+          return value + ' && ' + value + '.split(' + delimiter.inspect() + ')';
+        }
+
+        switch (attribute.operator) {
+          case '=':       conditions.push(value + ' == ' + attribute.value.inspect()); break;
+          case '~=':      conditions.push(splitValueBy(' ') + '.include(' + attribute.value.inspect() + ')'); break;
+          case '|=':      conditions.push(
+                            splitValueBy('-') + '.first().toUpperCase() == ' + attribute.value.toUpperCase().inspect()
+                          ); break;
+          case '!=':      conditions.push(value + ' != ' + attribute.value.inspect()); break;
+          case '':
+          case undefined: conditions.push(value + ' != null'); break;
+          default:        throw 'Unknown operator ' + attribute.operator + ' in selector';
+        }
+      });
+    }
+
+    return conditions.join(' && ');
+  },
+
+  compileMatcher: function() {
+    this.match = new Function('element', 'if (!element.tagName) return false; \
+      return ' + this.buildMatchExpression());
+  },
+
+  findElements: function(scope) {
+    var element;
+
+    if (element = $PR(this.params.id))
+      if (this.match(element))
+        if (!scope || Element.childOf(element, scope))
+          return [element];
+
+    scope = (scope || document).getElementsByTagName(this.params.tagName || '*');
+
+    var results = [];
+    for (var i = 0; i < scope.length; i++)
+      if (this.match(element = scope[i]))
+        results.push(Element.extend(element));
+
+    return results;
+  },
+
+  toString: function() {
+    return this.expression;
+  }
+}
+
+function $$() {
+  return $A(arguments).map(function(expression) {
+    return expression.strip().split(/\s+/).inject([null], function(results, expr) {
+      var selector = new Selector(expr);
+      return results.map(selector.findElements.bind(selector)).flatten();
+    });
+  }).flatten();
+}
+var Field = {
+  clear: function() {
+    for (var i = 0; i < arguments.length; i++)
+      $PR(arguments[i]).value = '';
+  },
+
+  focus: function(element) {
+    $PR(element).focus();
+  },
+
+  present: function() {
+    for (var i = 0; i < arguments.length; i++)
+      if ($PR(arguments[i]).value == '') return false;
+    return true;
+  },
+
+  select: function(element) {
+    $PR(element).select();
+  },
+
+  activate: function(element) {
+    element = $PR(element);
+    element.focus();
+    if (element.select)
+      element.select();
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Form = {
+  serialize: function(form) {
+    var elements = Form.getElements($PR(form));
+    var queryComponents = new Array();
+
+    for (var i = 0; i < elements.length; i++) {
+      var queryComponent = Form.Element.serialize(elements[i]);
+      if (queryComponent)
+        queryComponents.push(queryComponent);
+    }
+
+    return queryComponents.join('&');
+  },
+
+  getElements: function(form) {
+    form = $PR(form);
+    var elements = new Array();
+
+    for (var tagName in Form.Element.Serializers) {
+      var tagElements = form.getElementsByTagName(tagName);
+      for (var j = 0; j < tagElements.length; j++)
+        elements.push(tagElements[j]);
+    }
+    return elements;
+  },
+
+  getInputs: function(form, typeName, name) {
+    form = $PR(form);
+    var inputs = form.getElementsByTagName('input');
+
+    if (!typeName && !name)
+      return inputs;
+
+    var matchingInputs = new Array();
+    for (var i = 0; i < inputs.length; i++) {
+      var input = inputs[i];
+      if ((typeName && input.type != typeName) ||
+          (name && input.name != name))
+        continue;
+      matchingInputs.push(input);
+    }
+
+    return matchingInputs;
+  },
+
+  disable: function(form) {
+    var elements = Form.getElements(form);
+    for (var i = 0; i < elements.length; i++) {
+      var element = elements[i];
+      element.blur();
+      element.disabled = 'true';
+    }
+  },
+
+  enable: function(form) {
+    var elements = Form.getElements(form);
+    for (var i = 0; i < elements.length; i++) {
+      var element = elements[i];
+      element.disabled = '';
+    }
+  },
+
+  findFirstElement: function(form) {
+    return Form.getElements(form).find(function(element) {
+      return element.type != 'hidden' && !element.disabled &&
+        ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
+    });
+  },
+
+  focusFirstElement: function(form) {
+    Field.activate(Form.findFirstElement(form));
+  },
+
+  reset: function(form) {
+    $PR(form).reset();
+  }
+}
+
+Form.Element = {
+  serialize: function(element) {
+    element = $PR(element);
+    var method = element.tagName.toLowerCase();
+    var parameter = Form.Element.Serializers[method](element);
+
+    if (parameter) {
+      var key = encodeURIComponent(parameter[0]);
+      if (key.length == 0) return;
+
+      if (parameter[1].constructor != Array)
+        parameter[1] = [parameter[1]];
+
+      return parameter[1].map(function(value) {
+        return key + '=' + encodeURIComponent(value);
+      }).join('&');
+    }
+  },
+
+  getValue: function(element) {
+    element = $PR(element);
+    var method = element.tagName.toLowerCase();
+    var parameter = Form.Element.Serializers[method](element);
+
+    if (parameter)
+      return parameter[1];
+  }
+}
+
+Form.Element.Serializers = {
+  input: function(element) {
+    switch (element.type.toLowerCase()) {
+      case 'submit':
+      case 'hidden':
+      case 'password':
+      case 'text':
+        return Form.Element.Serializers.textarea(element);
+      case 'checkbox':
+      case 'radio':
+        return Form.Element.Serializers.inputSelector(element);
+    }
+    return false;
+  },
+
+  inputSelector: function(element) {
+    if (element.checked)
+      return [element.name, element.value];
+  },
+
+  textarea: function(element) {
+    return [element.name, element.value];
+  },
+
+  select: function(element) {
+    return Form.Element.Serializers[element.type == 'select-one' ?
+      'selectOne' : 'selectMany'](element);
+  },
+
+  selectOne: function(element) {
+    var value = '', opt, index = element.selectedIndex;
+    if (index >= 0) {
+      opt = element.options[index];
+      value = opt.value || opt.text;
+    }
+    return [element.name, value];
+  },
+
+  selectMany: function(element) {
+    var value = [];
+    for (var i = 0; i < element.length; i++) {
+      var opt = element.options[i];
+      if (opt.selected)
+        value.push(opt.value || opt.text);
+    }
+    return [element.name, value];
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var $F = Form.Element.getValue;
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.TimedObserver = function() {}
+Abstract.TimedObserver.prototype = {
+  initialize: function(element, frequency, callback) {
+    this.frequency = frequency;
+    this.element   = $PR(element);
+    this.callback  = callback;
+
+    this.lastValue = this.getValue();
+    this.registerCallback();
+  },
+
+  registerCallback: function() {
+    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+  },
+
+  onTimerEvent: function() {
+    var value = this.getValue();
+    if (this.lastValue != value) {
+      this.callback(this.element, value);
+      this.lastValue = value;
+    }
+  }
+}
+
+Form.Element.Observer = Class.create();
+Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
+  getValue: function() {
+    return Form.Element.getValue(this.element);
+  }
+});
+
+Form.Observer = Class.create();
+Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
+  getValue: function() {
+    return Form.serialize(this.element);
+  }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.EventObserver = function() {}
+Abstract.EventObserver.prototype = {
+  initialize: function(element, callback) {
+    this.element  = $PR(element);
+    this.callback = callback;
+
+    this.lastValue = this.getValue();
+    if (this.element.tagName.toLowerCase() == 'form')
+      this.registerFormCallbacks();
+    else
+      this.registerCallback(this.element);
+  },
+
+  onElementEvent: function() {
+    var value = this.getValue();
+    if (this.lastValue != value) {
+      this.callback(this.element, value);
+      this.lastValue = value;
+    }
+  },
+
+  registerFormCallbacks: function() {
+    var elements = Form.getElements(this.element);
+    for (var i = 0; i < elements.length; i++)
+      this.registerCallback(elements[i]);
+  },
+
+  registerCallback: function(element) {
+    if (element.type) {
+      switch (element.type.toLowerCase()) {
+        case 'checkbox':
+        case 'radio':
+          Event.observe(element, 'click', this.onElementEvent.bind(this));
+          break;
+        case 'password':
+        case 'text':
+        case 'textarea':
+        case 'select-one':
+        case 'select-multiple':
+          Event.observe(element, 'change', this.onElementEvent.bind(this));
+          break;
+      }
+    }
+  }
+}
+
+Form.Element.EventObserver = Class.create();
+Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+  getValue: function() {
+    return Form.Element.getValue(this.element);
+  }
+});
+
+Form.EventObserver = Class.create();
+Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+  getValue: function() {
+    return Form.serialize(this.element);
+  }
+});
+if (!window.Event) {
+  var Event = new Object();
+}
+
+Object.extend(Event, {
+  KEY_BACKSPACE: 8,
+  KEY_TAB:       9,
+  KEY_RETURN:   13,
+  KEY_ESC:      27,
+  KEY_LEFT:     37,
+  KEY_UP:       38,
+  KEY_RIGHT:    39,
+  KEY_DOWN:     40,
+  KEY_DELETE:   46,
+
+  element: function(event) {
+    return event.target || event.srcElement;
+  },
+
+  isLeftClick: function(event) {
+    return (((event.which) && (event.which == 1)) ||
+            ((event.button) && (event.button == 1)));
+  },
+
+  pointerX: function(event) {
+    return event.pageX || (event.clientX +
+      (document.documentElement.scrollLeft || document.body.scrollLeft));
+  },
+
+  pointerY: function(event) {
+    return event.pageY || (event.clientY +
+      (document.documentElement.scrollTop || document.body.scrollTop));
+  },
+
+  stop: function(event) {
+    if (event.preventDefault) {
+      event.preventDefault();
+      event.stopPropagation();
+    } else {
+      event.returnValue = false;
+      event.cancelBubble = true;
+    }
+  },
+
+  // find the first node with the given tagName, starting from the
+  // node the event was triggered on; traverses the DOM upwards
+  findElement: function(event, tagName) {
+    var element = Event.element(event);
+    while (element.parentNode && (!element.tagName ||
+        (element.tagName.toUpperCase() != tagName.toUpperCase())))
+      element = element.parentNode;
+    return element;
+  },
+
+  observers: false,
+
+  _observeAndCache: function(element, name, observer, useCapture) {
+    if (!this.observers) this.observers = [];
+    if (element.addEventListener) {
+      this.observers.push([element, name, observer, useCapture]);
+      element.addEventListener(name, observer, useCapture);
+    } else if (element.attachEvent) {
+      this.observers.push([element, name, observer, useCapture]);
+      element.attachEvent('on' + name, observer);
+    }
+  },
+
+  unloadCache: function() {
+    if (!Event.observers) return;
+    for (var i = 0; i < Event.observers.length; i++) {
+      Event.stopObserving.apply(this, Event.observers[i]);
+      Event.observers[i][0] = null;
+    }
+    Event.observers = false;
+  },
+
+  observe: function(element, name, observer, useCapture) {
+    var element = $PR(element);
+    useCapture = useCapture || false;
+
+    if (name == 'keypress' &&
+        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
+        || element.attachEvent))
+      name = 'keydown';
+
+    this._observeAndCache(element, name, observer, useCapture);
+  },
+
+  stopObserving: function(element, name, observer, useCapture) {
+    var element = $PR(element);
+    useCapture = useCapture || false;
+
+    if (name == 'keypress' &&
+        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
+        || element.detachEvent))
+      name = 'keydown';
+
+    if (element.removeEventListener) {
+      element.removeEventListener(name, observer, useCapture);
+    } else if (element.detachEvent) {
+      element.detachEvent('on' + name, observer);
+    }
+  }
+});
+
+/* prevent memory leaks in IE */
+if (navigator.appVersion.match(/\bMSIE\b/))
+  Event.observe(window, 'unload', Event.unloadCache, false);
+var Position = {
+  // set to true if needed, warning: firefox performance problems
+  // NOT neeeded for page scrolling, only if draggable contained in
+  // scrollable elements
+  includeScrollOffsets: false,
+
+  // must be called before calling withinIncludingScrolloffset, every time the
+  // page is scrolled
+  prepare: function() {
+    this.deltaX =  window.pageXOffset
+                || document.documentElement.scrollLeft
+                || document.body.scrollLeft
+                || 0;
+    this.deltaY =  window.pageYOffset
+                || document.documentElement.scrollTop
+                || document.body.scrollTop
+                || 0;
+  },
+
+  realOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.scrollTop  || 0;
+      valueL += element.scrollLeft || 0;
+      element = element.parentNode;
+    } while (element);
+    return [valueL, valueT];
+  },
+
+  cumulativeOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      element = element.offsetParent;
+    } while (element);
+    return [valueL, valueT];
+  },
+
+  positionedOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      element = element.offsetParent;
+      if (element) {
+        p = Element.getStyle(element, 'position');
+        if (p == 'relative' || p == 'absolute') break;
+      }
+    } while (element);
+    return [valueL, valueT];
+  },
+
+  offsetParent: function(element) {
+    if (element.offsetParent) return element.offsetParent;
+    if (element == document.body) return element;
+
+    while ((element = element.parentNode) && element != document.body)
+      if (Element.getStyle(element, 'position') != 'static')
+        return element;
+
+    return document.body;
+  },
+
+  // caches x/y coordinate pair to use with overlap
+  within: function(element, x, y) {
+    if (this.includeScrollOffsets)
+      return this.withinIncludingScrolloffsets(element, x, y);
+    this.xcomp = x;
+    this.ycomp = y;
+    this.offset = this.cumulativeOffset(element);
+
+    return (y >= this.offset[1] &&
+            y <  this.offset[1] + element.offsetHeight &&
+            x >= this.offset[0] &&
+            x <  this.offset[0] + element.offsetWidth);
+  },
+
+  withinIncludingScrolloffsets: function(element, x, y) {
+    var offsetcache = this.realOffset(element);
+
+    this.xcomp = x + offsetcache[0] - this.deltaX;
+    this.ycomp = y + offsetcache[1] - this.deltaY;
+    this.offset = this.cumulativeOffset(element);
+
+    return (this.ycomp >= this.offset[1] &&
+            this.ycomp <  this.offset[1] + element.offsetHeight &&
+            this.xcomp >= this.offset[0] &&
+            this.xcomp <  this.offset[0] + element.offsetWidth);
+  },
+
+  // within must be called directly before
+  overlap: function(mode, element) {
+    if (!mode) return 0;
+    if (mode == 'vertical')
+      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
+        element.offsetHeight;
+    if (mode == 'horizontal')
+      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
+        element.offsetWidth;
+  },
+
+  clone: function(source, target) {
+    source = $PR(source);
+    target = $PR(target);
+    target.style.position = 'absolute';
+    var offsets = this.cumulativeOffset(source);
+    target.style.top    = offsets[1] + 'px';
+    target.style.left   = offsets[0] + 'px';
+    target.style.width  = source.offsetWidth + 'px';
+    target.style.height = source.offsetHeight + 'px';
+  },
+
+  page: function(forElement) {
+    var valueT = 0, valueL = 0;
+
+    var element = forElement;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+
+      // Safari fix
+      if (element.offsetParent==document.body)
+        if (Element.getStyle(element,'position')=='absolute') break;
+
+    } while (element = element.offsetParent);
+
+    element = forElement;
+    do {
+      valueT -= element.scrollTop  || 0;
+      valueL -= element.scrollLeft || 0;
+    } while (element = element.parentNode);
+
+    return [valueL, valueT];
+  },
+
+  clone: function(source, target) {
+    var options = Object.extend({
+      setLeft:    true,
+      setTop:     true,
+      setWidth:   true,
+      setHeight:  true,
+      offsetTop:  0,
+      offsetLeft: 0
+    }, arguments[2] || {})
+
+    // find page position of source
+    source = $PR(source);
+    var p = Position.page(source);
+
+    // find coordinate system to use
+    target = $PR(target);
+    var delta = [0, 0];
+    var parent = null;
+    // delta [0,0] will do fine with position: fixed elements,
+    // position:absolute needs offsetParent deltas
+    if (Element.getStyle(target,'position') == 'absolute') {
+      parent = Position.offsetParent(target);
+      delta = Position.page(parent);
+    }
+
+    // correct by body offsets (fixes Safari)
+    if (parent == document.body) {
+      delta[0] -= document.body.offsetLeft;
+      delta[1] -= document.body.offsetTop;
+    }
+
+    // set position
+    if(options.setLeft)   target.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
+    if(options.setTop)    target.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
+    if(options.setWidth)  target.style.width = source.offsetWidth + 'px';
+    if(options.setHeight) target.style.height = source.offsetHeight + 'px';
+  },
+
+  absolutize: function(element) {
+    element = $PR(element);
+    if (element.style.position == 'absolute') return;
+    Position.prepare();
+
+    var offsets = Position.positionedOffset(element);
+    var top     = offsets[1];
+    var left    = offsets[0];
+    var width   = element.clientWidth;
+    var height  = element.clientHeight;
+
+    element._originalLeft   = left - parseFloat(element.style.left  || 0);
+    element._originalTop    = top  - parseFloat(element.style.top || 0);
+    element._originalWidth  = element.style.width;
+    element._originalHeight = element.style.height;
+
+    element.style.position = 'absolute';
+    element.style.top    = top + 'px';;
+    element.style.left   = left + 'px';;
+    element.style.width  = width + 'px';;
+    element.style.height = height + 'px';;
+  },
+
+  relativize: function(element) {
+    element = $PR(element);
+    if (element.style.position == 'relative') return;
+    Position.prepare();
+
+    element.style.position = 'relative';
+    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
+    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
+
+    element.style.top    = top + 'px';
+    element.style.left   = left + 'px';
+    element.style.height = element._originalHeight;
+    element.style.width  = element._originalWidth;
+  }
+}
+
+// Safari returns margins on body which is incorrect if the child is absolutely
+// positioned.  For performance reasons, redefine Position.cumulativeOffset for
+// KHTML/WebKit only.
+if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
+  Position.cumulativeOffset = function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      if (element.offsetParent == document.body)
+        if (Element.getStyle(element, 'position') == 'absolute') break;
+
+      element = element.offsetParent;
+    } while (element);
+
+    return [valueL, valueT];
+  }
+}
diff --git a/view/js/cropper/lib/scriptaculous.js b/view/js/cropper/lib/scriptaculous.js
new file mode 100644 (file)
index 0000000..f61fc57
--- /dev/null
@@ -0,0 +1,47 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// 
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+var Scriptaculous = {
+  Version: '1.6.1',
+  require: function(libraryName) {
+    // inserting via DOM fails in Safari 2.0, so brute force approach
+    document.write('<script type="text/javascript" src="'+libraryName+'"></script>');
+  },
+  load: function() {
+    if((typeof Prototype=='undefined') || 
+       (typeof Element == 'undefined') || 
+       (typeof Element.Methods=='undefined') ||
+       parseFloat(Prototype.Version.split(".")[0] + "." +
+                  Prototype.Version.split(".")[1]) < 1.5)
+       throw("script.aculo.us requires the Prototype JavaScript framework >= 1.5.0");
+    
+    $A(document.getElementsByTagName("script")).findAll( function(s) {
+      return (s.src && s.src.match(/scriptaculous\.js(\?.*)?$/))
+    }).each( function(s) {
+      var path = s.src.replace(/scriptaculous\.js(\?.*)?$/,'');
+      var includes = s.src.match(/\?.*load=([a-z,]*)/);
+      (includes ? includes[1] : 'builder,effects,dragdrop,controls,slider').split(',').each(
+       function(include) { Scriptaculous.require(path+include+'.js') });
+    });
+  }
+}
+
+Scriptaculous.load();
\ No newline at end of file
diff --git a/view/js/cropper/lib/slider.js b/view/js/cropper/lib/slider.js
new file mode 100644 (file)
index 0000000..cd16b69
--- /dev/null
@@ -0,0 +1,283 @@
+// Copyright (c) 2005 Marty Haught, Thomas Fuchs 
+//
+// See http://script.aculo.us for more info
+// 
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+if(!Control) var Control = {};
+Control.Slider = Class.create();
+
+// options:
+//  axis: 'vertical', or 'horizontal' (default)
+//
+// callbacks:
+//  onChange(value)
+//  onSlide(value)
+Control.Slider.prototype = {
+  initialize: function(handle, track, options) {
+    var slider = this;
+    
+    if(handle instanceof Array) {
+      this.handles = handle.collect( function(e) { return $PR(e) });
+    } else {
+      this.handles = [$PR(handle)];
+    }
+    
+    this.track   = $PR(track);
+    this.options = options || {};
+
+    this.axis      = this.options.axis || 'horizontal';
+    this.increment = this.options.increment || 1;
+    this.step      = parseInt(this.options.step || '1');
+    this.range     = this.options.range || $R(0,1);
+    
+    this.value     = 0; // assure backwards compat
+    this.values    = this.handles.map( function() { return 0 });
+    this.spans     = this.options.spans ? this.options.spans.map(function(s){ return $PR(s) }) : false;
+    this.options.startSpan = $PR(this.options.startSpan || null);
+    this.options.endSpan   = $PR(this.options.endSpan || null);
+
+    this.restricted = this.options.restricted || false;
+
+    this.maximum   = this.options.maximum || this.range.end;
+    this.minimum   = this.options.minimum || this.range.start;
+
+    // Will be used to align the handle onto the track, if necessary
+    this.alignX = parseInt(this.options.alignX || '0');
+    this.alignY = parseInt(this.options.alignY || '0');
+    
+    this.trackLength = this.maximumOffset() - this.minimumOffset();
+    this.handleLength = this.isVertical() ? this.handles[0].offsetHeight : this.handles[0].offsetWidth;
+
+    this.active   = false;
+    this.dragging = false;
+    this.disabled = false;
+
+    if(this.options.disabled) this.setDisabled();
+
+    // Allowed values array
+    this.allowedValues = this.options.values ? this.options.values.sortBy(Prototype.K) : false;
+    if(this.allowedValues) {
+      this.minimum = this.allowedValues.min();
+      this.maximum = this.allowedValues.max();
+    }
+
+    this.eventMouseDown = this.startDrag.bindAsEventListener(this);
+    this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
+    this.eventMouseMove = this.update.bindAsEventListener(this);
+
+    // Initialize handles in reverse (make sure first handle is active)
+    this.handles.each( function(h,i) {
+      i = slider.handles.length-1-i;
+      slider.setValue(parseFloat(
+        (slider.options.sliderValue instanceof Array ? 
+          slider.options.sliderValue[i] : slider.options.sliderValue) || 
+         slider.range.start), i);
+      Element.makePositioned(h); // fix IE
+      Event.observe(h, "mousedown", slider.eventMouseDown);
+    });
+    
+    Event.observe(this.track, "mousedown", this.eventMouseDown);
+    Event.observe(document, "mouseup", this.eventMouseUp);
+    Event.observe(document, "mousemove", this.eventMouseMove);
+    
+    this.initialized = true;
+  },
+  dispose: function() {
+    var slider = this;    
+    Event.stopObserving(this.track, "mousedown", this.eventMouseDown);
+    Event.stopObserving(document, "mouseup", this.eventMouseUp);
+    Event.stopObserving(document, "mousemove", this.eventMouseMove);
+    this.handles.each( function(h) {
+      Event.stopObserving(h, "mousedown", slider.eventMouseDown);
+    });
+  },
+  setDisabled: function(){
+    this.disabled = true;
+  },
+  setEnabled: function(){
+    this.disabled = false;
+  },  
+  getNearestValue: function(value){
+    if(this.allowedValues){
+      if(value >= this.allowedValues.max()) return(this.allowedValues.max());
+      if(value <= this.allowedValues.min()) return(this.allowedValues.min());
+      
+      var offset = Math.abs(this.allowedValues[0] - value);
+      var newValue = this.allowedValues[0];
+      this.allowedValues.each( function(v) {
+        var currentOffset = Math.abs(v - value);
+        if(currentOffset <= offset){
+          newValue = v;
+          offset = currentOffset;
+        } 
+      });
+      return newValue;
+    }
+    if(value > this.range.end) return this.range.end;
+    if(value < this.range.start) return this.range.start;
+    return value;
+  },
+  setValue: function(sliderValue, handleIdx){
+    if(!this.active) {
+      this.activeHandle    = this.handles[handleIdx];
+      this.activeHandleIdx = handleIdx;
+      this.updateStyles();
+    }
+    handleIdx = handleIdx || this.activeHandleIdx || 0;
+    if(this.initialized && this.restricted) {
+      if((handleIdx>0) && (sliderValue<this.values[handleIdx-1]))
+        sliderValue = this.values[handleIdx-1];
+      if((handleIdx < (this.handles.length-1)) && (sliderValue>this.values[handleIdx+1]))
+        sliderValue = this.values[handleIdx+1];
+    }
+    sliderValue = this.getNearestValue(sliderValue);
+    this.values[handleIdx] = sliderValue;
+    this.value = this.values[0]; // assure backwards compat
+    
+    this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] = 
+      this.translateToPx(sliderValue);
+    
+    this.drawSpans();
+    if(!this.dragging || !this.event) this.updateFinished();
+  },
+  setValueBy: function(delta, handleIdx) {
+    this.setValue(this.values[handleIdx || this.activeHandleIdx || 0] + delta, 
+      handleIdx || this.activeHandleIdx || 0);
+  },
+  translateToPx: function(value) {
+    return Math.round(
+      ((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) * 
+      (value - this.range.start)) + "px";
+  },
+  translateToValue: function(offset) {
+    return ((offset/(this.trackLength-this.handleLength) * 
+      (this.range.end-this.range.start)) + this.range.start);
+  },
+  getRange: function(range) {
+    var v = this.values.sortBy(Prototype.K); 
+    range = range || 0;
+    return $R(v[range],v[range+1]);
+  },
+  minimumOffset: function(){
+    return(this.isVertical() ? this.alignY : this.alignX);
+  },
+  maximumOffset: function(){
+    return(this.isVertical() ?
+      this.track.offsetHeight - this.alignY : this.track.offsetWidth - this.alignX);
+  },  
+  isVertical:  function(){
+    return (this.axis == 'vertical');
+  },
+  drawSpans: function() {
+    var slider = this;
+    if(this.spans)
+      $R(0, this.spans.length-1).each(function(r) { slider.setSpan(slider.spans[r], slider.getRange(r)) });
+    if(this.options.startSpan)
+      this.setSpan(this.options.startSpan,
+        $R(0, this.values.length>1 ? this.getRange(0).min() : this.value ));
+    if(this.options.endSpan)
+      this.setSpan(this.options.endSpan, 
+        $R(this.values.length>1 ? this.getRange(this.spans.length-1).max() : this.value, this.maximum));
+  },
+  setSpan: function(span, range) {
+    if(this.isVertical()) {
+      span.style.top = this.translateToPx(range.start);
+      span.style.height = this.translateToPx(range.end - range.start + this.range.start);
+    } else {
+      span.style.left = this.translateToPx(range.start);
+      span.style.width = this.translateToPx(range.end - range.start + this.range.start);
+    }
+  },
+  updateStyles: function() {
+    this.handles.each( function(h){ Element.removeClassName(h, 'selected') });
+    Element.addClassName(this.activeHandle, 'selected');
+  },
+  startDrag: function(event) {
+    if(Event.isLeftClick(event)) {
+      if(!this.disabled){
+        this.active = true;
+        
+        var handle = Event.element(event);
+        var pointer  = [Event.pointerX(event), Event.pointerY(event)];
+        if(handle==this.track) {
+          var offsets  = Position.cumulativeOffset(this.track); 
+          this.event = event;
+          this.setValue(this.translateToValue( 
+           (this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2)
+          ));
+          var offsets  = Position.cumulativeOffset(this.activeHandle);
+          this.offsetX = (pointer[0] - offsets[0]);
+          this.offsetY = (pointer[1] - offsets[1]);
+        } else {
+          // find the handle (prevents issues with Safari)
+          while((this.handles.indexOf(handle) == -1) && handle.parentNode) 
+            handle = handle.parentNode;
+        
+          this.activeHandle    = handle;
+          this.activeHandleIdx = this.handles.indexOf(this.activeHandle);
+          this.updateStyles();
+        
+          var offsets  = Position.cumulativeOffset(this.activeHandle);
+          this.offsetX = (pointer[0] - offsets[0]);
+          this.offsetY = (pointer[1] - offsets[1]);
+        }
+      }
+      Event.stop(event);
+    }
+  },
+  update: function(event) {
+   if(this.active) {
+      if(!this.dragging) this.dragging = true;
+      this.draw(event);
+      // fix AppleWebKit rendering
+      if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
+      Event.stop(event);
+   }
+  },
+  draw: function(event) {
+    var pointer = [Event.pointerX(event), Event.pointerY(event)];
+    var offsets = Position.cumulativeOffset(this.track);
+    pointer[0] -= this.offsetX + offsets[0];
+    pointer[1] -= this.offsetY + offsets[1];
+    this.event = event;
+    this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] ));
+    if(this.initialized && this.options.onSlide)
+      this.options.onSlide(this.values.length>1 ? this.values : this.value, this);
+  },
+  endDrag: function(event) {
+    if(this.active && this.dragging) {
+      this.finishDrag(event, true);
+      Event.stop(event);
+    }
+    this.active = false;
+    this.dragging = false;
+  },  
+  finishDrag: function(event, success) {
+    this.active = false;
+    this.dragging = false;
+    this.updateFinished();
+  },
+  updateFinished: function() {
+    if(this.initialized && this.options.onChange) 
+      this.options.onChange(this.values.length>1 ? this.values : this.value, this);
+    this.event = null;
+  }
+}
diff --git a/view/js/cropper/lib/unittest.js b/view/js/cropper/lib/unittest.js
new file mode 100644 (file)
index 0000000..be0d252
--- /dev/null
@@ -0,0 +1,383 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//           (c) 2005 Jon Tirsen (http://www.tirsen.com)
+//           (c) 2005 Michael Schuerig (http://www.schuerig.de/michael/)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+// experimental, Firefox-only
+Event.simulateMouse = function(element, eventName) {
+  var options = Object.extend({
+    pointerX: 0,
+    pointerY: 0,
+    buttons: 0
+  }, arguments[2] || {});
+  var oEvent = document.createEvent("MouseEvents");
+  oEvent.initMouseEvent(eventName, true, true, document.defaultView, 
+    options.buttons, options.pointerX, options.pointerY, options.pointerX, options.pointerY, 
+    false, false, false, false, 0, $PR(element));
+  
+  if(this.mark) Element.remove(this.mark);
+  this.mark = document.createElement('div');
+  this.mark.appendChild(document.createTextNode(" "));
+  document.body.appendChild(this.mark);
+  this.mark.style.position = 'absolute';
+  this.mark.style.top = options.pointerY + "px";
+  this.mark.style.left = options.pointerX + "px";
+  this.mark.style.width = "5px";
+  this.mark.style.height = "5px;";
+  this.mark.style.borderTop = "1px solid red;"
+  this.mark.style.borderLeft = "1px solid red;"
+  
+  if(this.step)
+    alert('['+new Date().getTime().toString()+'] '+eventName+'/'+Test.Unit.inspect(options));
+  
+  $PR(element).dispatchEvent(oEvent);
+};
+
+// Note: Due to a fix in Firefox 1.0.5/6 that probably fixed "too much", this doesn't work in 1.0.6 or DP2.
+// You need to downgrade to 1.0.4 for now to get this working
+// See https://bugzilla.mozilla.org/show_bug.cgi?id=289940 for the fix that fixed too much
+Event.simulateKey = function(element, eventName) {
+  var options = Object.extend({
+    ctrlKey: false,
+    altKey: false,
+    shiftKey: false,
+    metaKey: false,
+    keyCode: 0,
+    charCode: 0
+  }, arguments[2] || {});
+
+  var oEvent = document.createEvent("KeyEvents");
+  oEvent.initKeyEvent(eventName, true, true, window, 
+    options.ctrlKey, options.altKey, options.shiftKey, options.metaKey,
+    options.keyCode, options.charCode );
+  $PR(element).dispatchEvent(oEvent);
+};
+
+Event.simulateKeys = function(element, command) {
+  for(var i=0; i<command.length; i++) {
+    Event.simulateKey(element,'keypress',{charCode:command.charCodeAt(i)});
+  }
+};
+
+var Test = {}
+Test.Unit = {};
+
+// security exception workaround
+Test.Unit.inspect = Object.inspect;
+
+Test.Unit.Logger = Class.create();
+Test.Unit.Logger.prototype = {
+  initialize: function(log) {
+    this.log = $PR(log);
+    if (this.log) {
+      this._createLogTable();
+    }
+  },
+  start: function(testName) {
+    if (!this.log) return;
+    this.testName = testName;
+    this.lastLogLine = document.createElement('tr');
+    this.statusCell = document.createElement('td');
+    this.nameCell = document.createElement('td');
+    this.nameCell.appendChild(document.createTextNode(testName));
+    this.messageCell = document.createElement('td');
+    this.lastLogLine.appendChild(this.statusCell);
+    this.lastLogLine.appendChild(this.nameCell);
+    this.lastLogLine.appendChild(this.messageCell);
+    this.loglines.appendChild(this.lastLogLine);
+  },
+  finish: function(status, summary) {
+    if (!this.log) return;
+    this.lastLogLine.className = status;
+    this.statusCell.innerHTML = status;
+    this.messageCell.innerHTML = this._toHTML(summary);
+  },
+  message: function(message) {
+    if (!this.log) return;
+    this.messageCell.innerHTML = this._toHTML(message);
+  },
+  summary: function(summary) {
+    if (!this.log) return;
+    this.logsummary.innerHTML = this._toHTML(summary);
+  },
+  _createLogTable: function() {
+    this.log.innerHTML =
+    '<div id="logsummary"></div>' +
+    '<table id="logtable">' +
+    '<thead><tr><th>Status</th><th>Test</th><th>Message</th></tr></thead>' +
+    '<tbody id="loglines"></tbody>' +
+    '</table>';
+    this.logsummary = $PR('logsummary')
+    this.loglines = $PR('loglines');
+  },
+  _toHTML: function(txt) {
+    return txt.escapeHTML().replace(/\n/g,"<br/>");
+  }
+}
+
+Test.Unit.Runner = Class.create();
+Test.Unit.Runner.prototype = {
+  initialize: function(testcases) {
+    this.options = Object.extend({
+      testLog: 'testlog'
+    }, arguments[1] || {});
+    this.options.resultsURL = this.parseResultsURLQueryParameter();
+    if (this.options.testLog) {
+      this.options.testLog = $PR(this.options.testLog) || null;
+    }
+    if(this.options.tests) {
+      this.tests = [];
+      for(var i = 0; i < this.options.tests.length; i++) {
+        if(/^test/.test(this.options.tests[i])) {
+          this.tests.push(new Test.Unit.Testcase(this.options.tests[i], testcases[this.options.tests[i]], testcases["setup"], testcases["teardown"]));
+        }
+      }
+    } else {
+      if (this.options.test) {
+        this.tests = [new Test.Unit.Testcase(this.options.test, testcases[this.options.test], testcases["setup"], testcases["teardown"])];
+      } else {
+        this.tests = [];
+        for(var testcase in testcases) {
+          if(/^test/.test(testcase)) {
+            this.tests.push(new Test.Unit.Testcase(testcase, testcases[testcase], testcases["setup"], testcases["teardown"]));
+          }
+        }
+      }
+    }
+    this.currentTest = 0;
+    this.logger = new Test.Unit.Logger(this.options.testLog);
+    setTimeout(this.runTests.bind(this), 1000);
+  },
+  parseResultsURLQueryParameter: function() {
+    return window.location.search.parseQuery()["resultsURL"];
+  },
+  // Returns:
+  //  "ERROR" if there was an error,
+  //  "FAILURE" if there was a failure, or
+  //  "SUCCESS" if there was neither
+  getResult: function() {
+    var hasFailure = false;
+    for(var i=0;i<this.tests.length;i++) {
+      if (this.tests[i].errors > 0) {
+        return "ERROR";
+      }
+      if (this.tests[i].failures > 0) {
+        hasFailure = true;
+      }
+    }
+    if (hasFailure) {
+      return "FAILURE";
+    } else {
+      return "SUCCESS";
+    }
+  },
+  postResults: function() {
+    if (this.options.resultsURL) {
+      new Ajax.Request(this.options.resultsURL, 
+        { method: 'get', parameters: 'result=' + this.getResult(), asynchronous: false });
+    }
+  },
+  runTests: function() {
+    var test = this.tests[this.currentTest];
+    if (!test) {
+      // finished!
+      this.postResults();
+      this.logger.summary(this.summary());
+      return;
+    }
+    if(!test.isWaiting) {
+      this.logger.start(test.name);
+    }
+    test.run();
+    if(test.isWaiting) {
+      this.logger.message("Waiting for " + test.timeToWait + "ms");
+      setTimeout(this.runTests.bind(this), test.timeToWait || 1000);
+    } else {
+      this.logger.finish(test.status(), test.summary());
+      this.currentTest++;
+      // tail recursive, hopefully the browser will skip the stackframe
+      this.runTests();
+    }
+  },
+  summary: function() {
+    var assertions = 0;
+    var failures = 0;
+    var errors = 0;
+    var messages = [];
+    for(var i=0;i<this.tests.length;i++) {
+      assertions +=   this.tests[i].assertions;
+      failures   +=   this.tests[i].failures;
+      errors     +=   this.tests[i].errors;
+    }
+    return (
+      this.tests.length + " tests, " + 
+      assertions + " assertions, " + 
+      failures   + " failures, " +
+      errors     + " errors");
+  }
+}
+
+Test.Unit.Assertions = Class.create();
+Test.Unit.Assertions.prototype = {
+  initialize: function() {
+    this.assertions = 0;
+    this.failures   = 0;
+    this.errors     = 0;
+    this.messages   = [];
+  },
+  summary: function() {
+    return (
+      this.assertions + " assertions, " + 
+      this.failures   + " failures, " +
+      this.errors     + " errors" + "\n" +
+      this.messages.join("\n"));
+  },
+  pass: function() {
+    this.assertions++;
+  },
+  fail: function(message) {
+    this.failures++;
+    this.messages.push("Failure: " + message);
+  },
+  info: function(message) {
+    this.messages.push("Info: " + message);
+  },
+  error: function(error) {
+    this.errors++;
+    this.messages.push(error.name + ": "+ error.message + "(" + Test.Unit.inspect(error) +")");
+  },
+  status: function() {
+    if (this.failures > 0) return 'failed';
+    if (this.errors > 0) return 'error';
+    return 'passed';
+  },
+  assert: function(expression) {
+    var message = arguments[1] || 'assert: got "' + Test.Unit.inspect(expression) + '"';
+    try { expression ? this.pass() : 
+      this.fail(message); }
+    catch(e) { this.error(e); }
+  },
+  assertEqual: function(expected, actual) {
+    var message = arguments[2] || "assertEqual";
+    try { (expected == actual) ? this.pass() :
+      this.fail(message + ': expected "' + Test.Unit.inspect(expected) + 
+        '", actual "' + Test.Unit.inspect(actual) + '"'); }
+    catch(e) { this.error(e); }
+  },
+  assertEnumEqual: function(expected, actual) {
+    var message = arguments[2] || "assertEnumEqual";
+    try { $A(expected).length == $A(actual).length && 
+      expected.zip(actual).all(function(pair) { return pair[0] == pair[1] }) ?
+        this.pass() : this.fail(message + ': expected ' + Test.Unit.inspect(expected) + 
+          ', actual ' + Test.Unit.inspect(actual)); }
+    catch(e) { this.error(e); }
+  },
+  assertNotEqual: function(expected, actual) {
+    var message = arguments[2] || "assertNotEqual";
+    try { (expected != actual) ? this.pass() : 
+      this.fail(message + ': got "' + Test.Unit.inspect(actual) + '"'); }
+    catch(e) { this.error(e); }
+  },
+  assertNull: function(obj) {
+    var message = arguments[1] || 'assertNull'
+    try { (obj==null) ? this.pass() : 
+      this.fail(message + ': got "' + Test.Unit.inspect(obj) + '"'); }
+    catch(e) { this.error(e); }
+  },
+  assertHidden: function(element) {
+    var message = arguments[1] || 'assertHidden';
+    this.assertEqual("none", element.style.display, message);
+  },
+  assertNotNull: function(object) {
+    var message = arguments[1] || 'assertNotNull';
+    this.assert(object != null, message);
+  },
+  assertInstanceOf: function(expected, actual) {
+    var message = arguments[2] || 'assertInstanceOf';
+    try { 
+      (actual instanceof expected) ? this.pass() : 
+      this.fail(message + ": object was not an instance of the expected type"); }
+    catch(e) { this.error(e); } 
+  },
+  assertNotInstanceOf: function(expected, actual) {
+    var message = arguments[2] || 'assertNotInstanceOf';
+    try { 
+      !(actual instanceof expected) ? this.pass() : 
+      this.fail(message + ": object was an instance of the not expected type"); }
+    catch(e) { this.error(e); } 
+  },
+  _isVisible: function(element) {
+    element = $PR(element);
+    if(!element.parentNode) return true;
+    this.assertNotNull(element);
+    if(element.style && Element.getStyle(element, 'display') == 'none')
+      return false;
+    
+    return this._isVisible(element.parentNode);
+  },
+  assertNotVisible: function(element) {
+    this.assert(!this._isVisible(element), Test.Unit.inspect(element) + " was not hidden and didn't have a hidden parent either. " + ("" || arguments[1]));
+  },
+  assertVisible: function(element) {
+    this.assert(this._isVisible(element), Test.Unit.inspect(element) + " was not visible. " + ("" || arguments[1]));
+  },
+  benchmark: function(operation, iterations) {
+    var startAt = new Date();
+    (iterations || 1).times(operation);
+    var timeTaken = ((new Date())-startAt);
+    this.info((arguments[2] || 'Operation') + ' finished ' + 
+       iterations + ' iterations in ' + (timeTaken/1000)+'s' );
+    return timeTaken;
+  }
+}
+
+Test.Unit.Testcase = Class.create();
+Object.extend(Object.extend(Test.Unit.Testcase.prototype, Test.Unit.Assertions.prototype), {
+  initialize: function(name, test, setup, teardown) {
+    Test.Unit.Assertions.prototype.initialize.bind(this)();
+    this.name           = name;
+    this.test           = test || function() {};
+    this.setup          = setup || function() {};
+    this.teardown       = teardown || function() {};
+    this.isWaiting      = false;
+    this.timeToWait     = 1000;
+  },
+  wait: function(time, nextPart) {
+    this.isWaiting = true;
+    this.test = nextPart;
+    this.timeToWait = time;
+  },
+  run: function() {
+    try {
+      try {
+        if (!this.isWaiting) this.setup.bind(this)();
+        this.isWaiting = false;
+        this.test.bind(this)();
+      } finally {
+        if(!this.isWaiting) {
+          this.teardown.bind(this)();
+        }
+      }
+    }
+    catch(e) { this.error(e); }
+  }
+});
diff --git a/view/js/cropper/licence.txt b/view/js/cropper/licence.txt
new file mode 100644 (file)
index 0000000..b59e029
--- /dev/null
@@ -0,0 +1,12 @@
+Copyright (c) 2006, David Spurr (www.defusion.org.uk)\r
+All rights reserved.\r
+\r
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\r
+\r
+    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\r
+    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\r
+    * Neither the name of the David Spurr nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\r
+\r
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+\r
+http://www.opensource.org/licenses/bsd-license.php
\ No newline at end of file
diff --git a/view/js/cropper/marqueeHoriz.gif b/view/js/cropper/marqueeHoriz.gif
new file mode 100644 (file)
index 0000000..25317e5
Binary files /dev/null and b/view/js/cropper/marqueeHoriz.gif differ
diff --git a/view/js/cropper/marqueeVert.gif b/view/js/cropper/marqueeVert.gif
new file mode 100644 (file)
index 0000000..354070b
Binary files /dev/null and b/view/js/cropper/marqueeVert.gif differ
diff --git a/view/js/cropper/tests/castle.jpg b/view/js/cropper/tests/castle.jpg
new file mode 100644 (file)
index 0000000..e40b7e4
Binary files /dev/null and b/view/js/cropper/tests/castle.jpg differ
diff --git a/view/js/cropper/tests/castleMed.jpg b/view/js/cropper/tests/castleMed.jpg
new file mode 100644 (file)
index 0000000..c35a6f5
Binary files /dev/null and b/view/js/cropper/tests/castleMed.jpg differ
diff --git a/view/js/cropper/tests/example-Basic.htm b/view/js/cropper/tests/example-Basic.htm
new file mode 100644 (file)
index 0000000..2a55eca
--- /dev/null
@@ -0,0 +1,106 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\r
+<head>\r
+       <meta http-equiv="Content-type" content="text/html; charset=utf-8" />\r
+       <meta http-equiv="Content-Language" content="en-us" />\r
+       <title>Basic cropper test</title>\r
+       <script src="../lib/prototype.js" type="text/javascript"></script>      \r
+       <script src="../lib/scriptaculous.js?load=builder,dragdrop" type="text/javascript"></script>\r
+       <script src="../cropper.js" type="text/javascript"></script>\r
+       \r
+       \r
+       <script type="text/javascript" charset="utf-8">\r
+               \r
+               // setup the callback function\r
+               function onEndCrop( coords, dimensions ) {\r
+                       $PR( 'x1' ).value = coords.x1;\r
+                       $PR( 'y1' ).value = coords.y1;\r
+                       $PR( 'x2' ).value = coords.x2;\r
+                       $PR( 'y2' ).value = coords.y2;\r
+                       $PR( 'width' ).value = dimensions.width;\r
+                       $PR( 'height' ).value = dimensions.height;\r
+               }\r
+               \r
+               // basic example\r
+               Event.observe( \r
+                       window, \r
+                       'load', \r
+                       function() { \r
+                               new Cropper.Img( \r
+                                       'testImage',\r
+                                       {\r
+                                               onEndCrop: onEndCrop \r
+                                       }\r
+                               ) \r
+                       }\r
+               );              \r
+               \r
+               \r
+               if( typeof(dump) != 'function' ) {\r
+                       Debug.init(true, '/');\r
+                       \r
+                       function dump( msg ) {\r
+                               Debug.raise( msg );\r
+                       };\r
+               } else dump( '---------------------------------------\n' );\r
+               \r
+               \r
+       </script>\r
+       <link rel="stylesheet" type="text/css" href="debug.css" media="all" />\r
+       <style type="text/css">\r
+               label { \r
+                       clear: left;\r
+                       margin-left: 50px;\r
+                       float: left;\r
+                       width: 5em;\r
+               }\r
+               \r
+               html, body { \r
+                       margin: 0;\r
+               }\r
+               \r
+               #testWrap {\r
+                       margin: 20px 0 0 50px; /* Just while testing, to make sure we return the correct positions for the image & not the window */\r
+               }\r
+       </style>\r
+</head>\r
+<body>\r
+       <h2>Basic cropper test</h2>\r
+       <p>\r
+               Some test content before the image\r
+       </p>\r
+       \r
+       <div id="testWrap">\r
+               <img src="castle.jpg" alt="test image" id="testImage" width="500" height="333" />\r
+       </div>\r
+       \r
+       \r
+       <p>\r
+               <label for="x1">x1:</label>\r
+               <input type="text" name="x1" id="x1" />\r
+       </p>\r
+       <p>\r
+               <label for="y1">y1:</label>\r
+               <input type="text" name="y1" id="y1" />\r
+       </p>\r
+       <p>\r
+               <label for="x2">x2:</label>\r
+               <input type="text" name="x2" id="x2" />\r
+       </p>\r
+       <p>\r
+               <label for="y2">y2:</label>\r
+               <input type="text" name="y2" id="y2" />\r
+       </p>\r
+       <p>\r
+               <label for="width">width:</label>\r
+               <input type="text" name="width" id="width" />\r
+       </p>\r
+       <p>\r
+               <label for="height">height</label>\r
+               <input type="text" name="height" id="height" />\r
+       </p>  \r
+       \r
+</body>\r
+</html>\r
+\r
+\r
diff --git a/view/js/cropper/tests/example-CSS-Absolute.htm b/view/js/cropper/tests/example-CSS-Absolute.htm
new file mode 100644 (file)
index 0000000..b605fd3
--- /dev/null
@@ -0,0 +1,162 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\r
+<head>\r
+       <meta http-equiv="Content-type" content="text/html; charset=utf-8" />\r
+       <meta http-equiv="Content-Language" content="en-us" />\r
+       <title>CSS - Absolute positioned (and draggable) test</title>\r
+       <script src="../lib/prototype.js" type="text/javascript"></script>      \r
+       <script src="../lib/scriptaculous.js?load=builder,dragdrop,effects" type="text/javascript"></script>\r
+       <script src="../cropper.js" type="text/javascript"></script>\r
+       \r
+       \r
+       <script type="text/javascript" charset="utf-8">\r
+               \r
+               // setup the callback function\r
+               function onEndCrop( coords, dimensions ) {\r
+                       $PR( 'x1' ).value = coords.x1;\r
+                       $PR( 'y1' ).value = coords.y1;\r
+                       $PR( 'x2' ).value = coords.x2;\r
+                       $PR( 'y2' ).value = coords.y2;\r
+                       $PR( 'width' ).value = dimensions.width;\r
+                       $PR( 'height' ).value = dimensions.height;\r
+               }\r
+                               \r
+               // Absolute positioned example\r
+               Event.observe(\r
+                       window,\r
+                       'load',\r
+                       function() {\r
+                               new Cropper.Img( 'testAbsImage', { onEndCrop: onEndCrop } );\r
+                               new Draggable( 'test-abs' );\r
+                       }\r
+               );              \r
+               \r
+               if( typeof(dump) != 'function' ) {\r
+                       Debug.init(true, '/');\r
+                       \r
+                       function dump( msg ) {\r
+                               Debug.raise( msg );\r
+                       };\r
+               } else dump( '---------------------------------------\n' );\r
+               \r
+               \r
+       </script>\r
+       <link rel="stylesheet" type="text/css" href="debug.css" media="all" />\r
+       <style type="text/css">\r
+               label { \r
+                       clear: left;\r
+                       margin-left: 50px;\r
+                       float: left;\r
+                       width: 5em;\r
+               }\r
+               \r
+               html, body { \r
+                       margin: 0;\r
+               }\r
+               \r
+               #testWrap {\r
+                       margin: 20px 0 0 50px; /* Just while testing, to make sure we return the correct positions for the image & not the window */\r
+               }\r
+               \r
+               #test-abs {\r
+                       width: 510px;\r
+                       position: absolute;\r
+                       top: 50px;\r
+                       left: 25%;\r
+                       background-color: #dee;\r
+                       border: 3px solid #ccc;\r
+                       z-index: 10;\r
+               }\r
+       </style>\r
+</head>\r
+<body>\r
+       <h2>CSS - Absolute positioned (and draggable) test</h2>\r
+       <p>\r
+               Some test content before the image\r
+       </p>\r
+       <p>\r
+       Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Pellentesque consequat risus cursus ipsum. Etiam libero. Integer vel mauris. Donec vulputate. In ut augue vitae nibh lobortis tempor. Aliquam hendrerit quam. Phasellus sed orci. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Ut sed urna. Donec nunc urna, porttitor a, feugiat pellentesque, varius id, justo. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla facilisi. Sed sollicitudin. Integer enim. Aenean sollicitudin.\r
+       </p>\r
+       <p>\r
+       Integer lorem turpis, dapibus sed, vulputate nec, volutpat a, sem. Sed malesuada laoreet lorem. Duis mauris ipsum, fringilla nec, tristique vel, imperdiet vel, neque. Nulla vel purus. Fusce non lectus. Mauris pulvinar. Curabitur eget eros. Nunc ultrices, risus vitae adipiscing scelerisque, quam mi auctor lacus, non pellentesque augue sapien a magna. Etiam rutrum posuere tortor. Mauris rhoncus sagittis dolor. Donec sed quam. Vivamus vel diam id massa adipiscing bibendum. Suspendisse potenti. Integer arcu est, adipiscing sit amet, convallis eu, sollicitudin tincidunt, quam.\r
+       </p>\r
+       <p>\r
+       Etiam ligula lorem, imperdiet ac, luctus eget, ultrices at, odio. Vivamus malesuada, justo eu adipiscing semper, nisi dui tempus magna, quis ultrices nunc tellus id massa. Nullam lobortis auctor sapien. Quisque non nulla. Donec lobortis pellentesque nisl. Sed lacus sapien, viverra vitae, blandit ut, fermentum quis, leo. Morbi augue turpis, hendrerit non, feugiat vel, laoreet sed, est. Nunc velit. Praesent lobortis. Integer enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Curabitur faucibus lacus ac ante. Donec odio odio, tincidunt a, egestas nec, scelerisque nec, dui. Cras sollicitudin. Donec lacus enim, mollis sit amet, interdum quis, euismod et, nulla. Nunc sit amet dui eu magna dapibus mollis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nulla facilisi.\r
+       </p>\r
+       <p>\r
+       In hac habitasse platea dictumst. Nunc neque urna, dapibus ut, tristique ut, bibendum ac, felis. Donec dictum est ut dolor. Etiam accumsan, velit sit amet blandit vestibulum, turpis quam hendrerit risus, vel interdum eros orci in nunc. Curabitur tellus sapien, rutrum ac, euismod ac, malesuada nec, pede. Proin sit amet ipsum. Praesent quam nisl, adipiscing nec, tristique eget, fermentum sed, est. Praesent ac est sit amet orci facilisis placerat. Sed consequat, est sit amet consectetuer viverra, risus urna porttitor tellus, ut convallis nibh libero in lectus. Pellentesque molestie, erat non vehicula pretium, turpis nisi eleifend eros, sed scelerisque tortor odio non tellus. Nunc leo tellus, faucibus vitae, placerat a, accumsan vel, arcu. In et orci. Ut tristique euismod nibh. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Sed nulla nunc, placerat vitae, pellentesque non, interdum non, sapien. Quisque faucibus, eros sed venenatis sagittis, leo risus rhoncus risus, in pretium sem purus a lacus. Aliquam aliquam leo et diam.\r
+       \r
+       </p>\r
+       <p>\r
+       Nulla sagittis diam. Phasellus vitae enim tristique libero molestie tristique. Nam mauris sem, elementum nec, cursus in, fringilla ac, neque. Nunc metus nisi, dictum vel, vulputate quis, porttitor bibendum, tortor. Vestibulum vehicula. Nulla facilisi. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla ac magna sed purus ultricies euismod. Aliquam dictum. Sed mauris. Suspendisse justo. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Morbi purus lorem, auctor non, porta ac, vehicula vel, orci. Morbi pharetra massa nec leo. Maecenas et mauris. Aliquam porttitor tincidunt nulla. Vestibulum pede.\r
+       </p>\r
+       <p>\r
+       Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Pellentesque consequat risus cursus ipsum. Etiam libero. Integer vel mauris. Donec vulputate. In ut augue vitae nibh lobortis tempor. Aliquam hendrerit quam. Phasellus sed orci. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Ut sed urna. Donec nunc urna, porttitor a, feugiat pellentesque, varius id, justo. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla facilisi. Sed sollicitudin. Integer enim. Aenean sollicitudin.\r
+       </p>\r
+       <p>\r
+       Integer lorem turpis, dapibus sed, vulputate nec, volutpat a, sem. Sed malesuada laoreet lorem. Duis mauris ipsum, fringilla nec, tristique vel, imperdiet vel, neque. Nulla vel purus. Fusce non lectus. Mauris pulvinar. Curabitur eget eros. Nunc ultrices, risus vitae adipiscing scelerisque, quam mi auctor lacus, non pellentesque augue sapien a magna. Etiam rutrum posuere tortor. Mauris rhoncus sagittis dolor. Donec sed quam. Vivamus vel diam id massa adipiscing bibendum. Suspendisse potenti. Integer arcu est, adipiscing sit amet, convallis eu, sollicitudin tincidunt, quam.\r
+       </p>\r
+       <p>\r
+       Etiam ligula lorem, imperdiet ac, luctus eget, ultrices at, odio. Vivamus malesuada, justo eu adipiscing semper, nisi dui tempus magna, quis ultrices nunc tellus id massa. Nullam lobortis auctor sapien. Quisque non nulla. Donec lobortis pellentesque nisl. Sed lacus sapien, viverra vitae, blandit ut, fermentum quis, leo. Morbi augue turpis, hendrerit non, feugiat vel, laoreet sed, est. Nunc velit. Praesent lobortis. Integer enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Curabitur faucibus lacus ac ante. Donec odio odio, tincidunt a, egestas nec, scelerisque nec, dui. Cras sollicitudin. Donec lacus enim, mollis sit amet, interdum quis, euismod et, nulla. Nunc sit amet dui eu magna dapibus mollis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nulla facilisi.\r
+       </p>\r
+       <p>\r
+       In hac habitasse platea dictumst. Nunc neque urna, dapibus ut, tristique ut, bibendum ac, felis. Donec dictum est ut dolor. Etiam accumsan, velit sit amet blandit vestibulum, turpis quam hendrerit risus, vel interdum eros orci in nunc. Curabitur tellus sapien, rutrum ac, euismod ac, malesuada nec, pede. Proin sit amet ipsum. Praesent quam nisl, adipiscing nec, tristique eget, fermentum sed, est. Praesent ac est sit amet orci facilisis placerat. Sed consequat, est sit amet consectetuer viverra, risus urna porttitor tellus, ut convallis nibh libero in lectus. Pellentesque molestie, erat non vehicula pretium, turpis nisi eleifend eros, sed scelerisque tortor odio non tellus. Nunc leo tellus, faucibus vitae, placerat a, accumsan vel, arcu. In et orci. Ut tristique euismod nibh. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Sed nulla nunc, placerat vitae, pellentesque non, interdum non, sapien. Quisque faucibus, eros sed venenatis sagittis, leo risus rhoncus risus, in pretium sem purus a lacus. Aliquam aliquam leo et diam.\r
+       \r
+       </p>\r
+       <p>\r
+       Nulla sagittis diam. Phasellus vitae enim tristique libero molestie tristique. Nam mauris sem, elementum nec, cursus in, fringilla ac, neque. Nunc metus nisi, dictum vel, vulputate quis, porttitor bibendum, tortor. Vestibulum vehicula. Nulla facilisi. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla ac magna sed purus ultricies euismod. Aliquam dictum. Sed mauris. Suspendisse justo. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Morbi purus lorem, auctor non, porta ac, vehicula vel, orci. Morbi pharetra massa nec leo. Maecenas et mauris. Aliquam porttitor tincidunt nulla. Vestibulum pede.\r
+       </p>\r
+       <p>\r
+       Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Pellentesque consequat risus cursus ipsum. Etiam libero. Integer vel mauris. Donec vulputate. In ut augue vitae nibh lobortis tempor. Aliquam hendrerit quam. Phasellus sed orci. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Ut sed urna. Donec nunc urna, porttitor a, feugiat pellentesque, varius id, justo. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla facilisi. Sed sollicitudin. Integer enim. Aenean sollicitudin.\r
+       </p>\r
+       <p>\r
+       Integer lorem turpis, dapibus sed, vulputate nec, volutpat a, sem. Sed malesuada laoreet lorem. Duis mauris ipsum, fringilla nec, tristique vel, imperdiet vel, neque. Nulla vel purus. Fusce non lectus. Mauris pulvinar. Curabitur eget eros. Nunc ultrices, risus vitae adipiscing scelerisque, quam mi auctor lacus, non pellentesque augue sapien a magna. Etiam rutrum posuere tortor. Mauris rhoncus sagittis dolor. Donec sed quam. Vivamus vel diam id massa adipiscing bibendum. Suspendisse potenti. Integer arcu est, adipiscing sit amet, convallis eu, sollicitudin tincidunt, quam.\r
+       </p>\r
+       <p>\r
+       Etiam ligula lorem, imperdiet ac, luctus eget, ultrices at, odio. Vivamus malesuada, justo eu adipiscing semper, nisi dui tempus magna, quis ultrices nunc tellus id massa. Nullam lobortis auctor sapien. Quisque non nulla. Donec lobortis pellentesque nisl. Sed lacus sapien, viverra vitae, blandit ut, fermentum quis, leo. Morbi augue turpis, hendrerit non, feugiat vel, laoreet sed, est. Nunc velit. Praesent lobortis. Integer enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Curabitur faucibus lacus ac ante. Donec odio odio, tincidunt a, egestas nec, scelerisque nec, dui. Cras sollicitudin. Donec lacus enim, mollis sit amet, interdum quis, euismod et, nulla. Nunc sit amet dui eu magna dapibus mollis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nulla facilisi.\r
+       </p>\r
+       <p>\r
+       In hac habitasse platea dictumst. Nunc neque urna, dapibus ut, tristique ut, bibendum ac, felis. Donec dictum est ut dolor. Etiam accumsan, velit sit amet blandit vestibulum, turpis quam hendrerit risus, vel interdum eros orci in nunc. Curabitur tellus sapien, rutrum ac, euismod ac, malesuada nec, pede. Proin sit amet ipsum. Praesent quam nisl, adipiscing nec, tristique eget, fermentum sed, est. Praesent ac est sit amet orci facilisis placerat. Sed consequat, est sit amet consectetuer viverra, risus urna porttitor tellus, ut convallis nibh libero in lectus. Pellentesque molestie, erat non vehicula pretium, turpis nisi eleifend eros, sed scelerisque tortor odio non tellus. Nunc leo tellus, faucibus vitae, placerat a, accumsan vel, arcu. In et orci. Ut tristique euismod nibh. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Sed nulla nunc, placerat vitae, pellentesque non, interdum non, sapien. Quisque faucibus, eros sed venenatis sagittis, leo risus rhoncus risus, in pretium sem purus a lacus. Aliquam aliquam leo et diam.\r
+       \r
+       </p>\r
+       <p>\r
+       Nulla sagittis diam. Phasellus vitae enim tristique libero molestie tristique. Nam mauris sem, elementum nec, cursus in, fringilla ac, neque. Nunc metus nisi, dictum vel, vulputate quis, porttitor bibendum, tortor. Vestibulum vehicula. Nulla facilisi. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla ac magna sed purus ultricies euismod. Aliquam dictum. Sed mauris. Suspendisse justo. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Morbi purus lorem, auctor non, porta ac, vehicula vel, orci. Morbi pharetra massa nec leo. Maecenas et mauris. Aliquam porttitor tincidunt nulla. Vestibulum pede.\r
+       </p>\r
+\r
+\r
+<div id="test-abs">\r
+       <h2>Absolute test</h2>\r
+       <div id="testAbsWrap">\r
+               <img src="castle.jpg" alt="test image" id="testAbsImage" width="500" height="333" />\r
+       </div>\r
+       \r
+       <p>\r
+               <label for="x1">x1:</label>\r
+               <input type="text" name="x1" id="x1" />\r
+       </p>\r
+       <p>\r
+               <label for="y1">y1:</label>\r
+               <input type="text" name="y1" id="y1" />\r
+       </p>\r
+       <p>\r
+               <label for="x2">x2:</label>\r
+               <input type="text" name="x2" id="x2" />\r
+       </p>\r
+       <p>\r
+               <label for="y2">y2:</label>\r
+               <input type="text" name="y2" id="y2" />\r
+       </p>\r
+       <p>\r
+               <label for="width">width:</label>\r
+               <input type="text" name="width" id="width" />\r
+       </p>\r
+       <p>\r
+               <label for="height">height</label>\r
+               <input type="text" name="height" id="height" />\r
+       </p>  \r
+</div>\r
+\r
+</body>\r
+</html>\r
+\r
+\r
diff --git a/view/js/cropper/tests/example-CSS-Float.htm b/view/js/cropper/tests/example-CSS-Float.htm
new file mode 100644 (file)
index 0000000..3dbeeab
--- /dev/null
@@ -0,0 +1,124 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\r
+<head>\r
+       <meta http-equiv="Content-type" content="text/html; charset=utf-8" />\r
+       <meta http-equiv="Content-Language" content="en-us" />\r
+       <title>CSS - Float test</title>\r
+       <script src="../lib/prototype.js" type="text/javascript"></script>      \r
+       <script src="../lib/scriptaculous.js?load=builder,dragdrop" type="text/javascript"></script>\r
+       <script src="../cropper.js" type="text/javascript"></script>\r
+       \r
+       \r
+       <script type="text/javascript" charset="utf-8">\r
+               \r
+               // setup the callback function\r
+               function onEndCrop( coords, dimensions ) {\r
+                       $PR( 'x1' ).value = coords.x1;\r
+                       $PR( 'y1' ).value = coords.y1;\r
+                       $PR( 'x2' ).value = coords.x2;\r
+                       $PR( 'y2' ).value = coords.y2;\r
+                       $PR( 'width' ).value = dimensions.width;\r
+                       $PR( 'height' ).value = dimensions.height;\r
+               }\r
+               \r
+               // float example\r
+               Event.observe(\r
+                       window,\r
+                       'load',\r
+                       function() {\r
+                               new Cropper.Img(\r
+                                       'testFloatImage',\r
+                                       { \r
+                                               onEndCrop: function( coords, dimensions ) {\r
+                                                       $PR( 'floatX1' ).value = coords.x1;\r
+                                                       $PR( 'floatY1' ).value = coords.y1;\r
+                                                       $PR( 'floatX2' ).value = coords.x2;\r
+                                                       $PR( 'floatY2' ).value = coords.y2;\r
+                                                       $PR( 'floatWidth' ).value = dimensions.width;\r
+                                                       $PR( 'floatHeight' ).value = dimensions.height;\r
+                                               }\r
+                                       }\r
+                               );\r
+                       }\r
+               );              \r
+               \r
+               if( typeof(dump) != 'function' ) {\r
+                       Debug.init(true, '/');\r
+                       \r
+                       function dump( msg ) {\r
+                               Debug.raise( msg );\r
+                       };\r
+               } else dump( '---------------------------------------\n' );\r
+               \r
+               \r
+       </script>\r
+       <link rel="stylesheet" type="text/css" href="debug.css" media="all" />\r
+       <style type="text/css">\r
+               label { \r
+                       clear: left;\r
+                       margin-left: 50px;\r
+                       float: left;\r
+                       width: 5em;\r
+               }\r
+               \r
+               html, body { \r
+                       margin: 0;\r
+               }\r
+               \r
+               #testWrap {\r
+                       margin: 20px 0 0 50px; /* Just while testing, to make sure we return the correct positions for the image & not the window */\r
+               }\r
+               \r
+               #test-float {\r
+                       width: 600px;\r
+                       float: right;\r
+                       background-color: #eee;\r
+                       border: 3px solid #000;\r
+                       margin: 10px;\r
+                       padding: 5px;\r
+               }\r
+\r
+       </style>\r
+</head>\r
+<body>\r
+       <h2>Test page with floating wrapper</h2>\r
+       <p>\r
+               Some test content before the image\r
+       </p>\r
+\r
+<div id="test-float">\r
+       <h2>Float test</h2>\r
+       <div id="testFloatWrap">\r
+               <img src="castle.jpg" alt="test image" id="testFloatImage" width="500" height="333" />\r
+       </div>\r
+       \r
+       <p>\r
+               <label for="floatX1">x1:</label>\r
+               <input type="text" name="floatX1" id="floatX1" />\r
+       </p>\r
+       <p>\r
+               <label for="floatY1">y1:</label>\r
+               <input type="text" name="floatY1" id="floatY1" />\r
+       </p>\r
+       <p>\r
+               <label for="floatX2">x2:</label>\r
+               <input type="text" name="floatX2" id="floatX2" />\r
+       </p>\r
+       <p>\r
+               <label for="floatY2">y2:</label>\r
+               <input type="text" name="floatY2" id="floatY2" />\r
+       </p>\r
+       <p>\r
+               <label for="floatWidth">width:</label>\r
+               <input type="text" name="floatWidth" id="floatWidth" />\r
+       </p>\r
+       <p>\r
+               <label for="floatHeight">height</label>\r
+               <input type="text" name="floatHeight" id="floatHeight" />\r
+       </p>  \r
+</div>\r
+\r
+</body>\r
+</html>\r
+\r
+\r
diff --git a/view/js/cropper/tests/example-CSS-Relative.htm b/view/js/cropper/tests/example-CSS-Relative.htm
new file mode 100644 (file)
index 0000000..ecad134
--- /dev/null
@@ -0,0 +1,116 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\r
+<head>\r
+       <meta http-equiv="Content-type" content="text/html; charset=utf-8" />\r
+       <meta http-equiv="Content-Language" content="en-us" />\r
+       <title>CSS - Relative test</title>\r
+       <script src="../lib/prototype.js" type="text/javascript"></script>      \r
+       <script src="../lib/scriptaculous.js?load=builder,dragdrop" type="text/javascript"></script>\r
+       <script src="../cropper.js" type="text/javascript"></script>\r
+       \r
+       \r
+       <script type="text/javascript" charset="utf-8">\r
+               \r
+               // setup the callback function\r
+               function onEndCrop( coords, dimensions ) {\r
+                       $PR( 'x1' ).value = coords.x1;\r
+                       $PR( 'y1' ).value = coords.y1;\r
+                       $PR( 'x2' ).value = coords.x2;\r
+                       $PR( 'y2' ).value = coords.y2;\r
+                       $PR( 'width' ).value = dimensions.width;\r
+                       $PR( 'height' ).value = dimensions.height;\r
+               }\r
+               \r
+               // relative example\r
+               Event.observe( \r
+                       window, \r
+                       'load', \r
+                       function() { \r
+                               new Cropper.Img( \r
+                                       'testImage',\r
+                                       {\r
+                                               onEndCrop: onEndCrop \r
+                                       }\r
+                               ) \r
+                       }\r
+               );      \r
+                               \r
+               if( typeof(dump) != 'function' ) {\r
+                       Debug.init(true, '/');\r
+                       \r
+                       function dump( msg ) {\r
+                               Debug.raise( msg );\r
+                       };\r
+               } else dump( '---------------------------------------\n' );\r
+               \r
+               \r
+       </script>\r
+       <link rel="stylesheet" type="text/css" href="debug.css" media="all" />\r
+       <style type="text/css">\r
+               label { \r
+                       clear: left;\r
+                       margin-left: 50px;\r
+                       float: left;\r
+                       width: 5em;\r
+               }\r
+               \r
+               html, body { \r
+                       margin: 0;\r
+               }\r
+               \r
+               #testWrap {\r
+                       margin: 20px 0 0 50px; /* Just while testing, to make sure we return the correct positions for the image & not the window */\r
+               }\r
+               \r
+               #test-relative {\r
+                       background-color: #ccc;\r
+                       border: 3px solid #ddd;\r
+                       position: relative;\r
+                       top: 25px;\r
+                       left: 25px;\r
+               }\r
+       </style>\r
+</head>\r
+<body>\r
+       <h2>Test page with relatively positioned wrapper</h2>\r
+       <p>\r
+               Some test content before the image\r
+       </p>\r
+\r
+<div id="test-relative">\r
+       <h2>Relative test</h2>\r
+       <div id="testWrap">\r
+               <img src="castle.jpg" alt="test image" id="testImage" width="500" height="333" />\r
+       </div>\r
+       \r
+       \r
+       <p>\r
+               <label for="x1">x1:</label>\r
+               <input type="text" name="x1" id="x1" />\r
+       </p>\r
+       <p>\r
+               <label for="y1">y1:</label>\r
+               <input type="text" name="y1" id="y1" />\r
+       </p>\r
+       <p>\r
+               <label for="x2">x2:</label>\r
+               <input type="text" name="x2" id="x2" />\r
+       </p>\r
+       <p>\r
+               <label for="y2">y2:</label>\r
+               <input type="text" name="y2" id="y2" />\r
+       </p>\r
+       <p>\r
+               <label for="width">width:</label>\r
+               <input type="text" name="width" id="width" />\r
+       </p>\r
+       <p>\r
+               <label for="height">height</label>\r
+               <input type="text" name="height" id="height" />\r
+       </p>  \r
+</div> \r
+\r
+</body>\r
+</html>\r
+\r
+\r
diff --git a/view/js/cropper/tests/example-CoordsOnLoad.htm b/view/js/cropper/tests/example-CoordsOnLoad.htm
new file mode 100644 (file)
index 0000000..c14289c
--- /dev/null
@@ -0,0 +1,108 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\r
+<head>\r
+       <meta http-equiv="Content-type" content="text/html; charset=utf-8" />\r
+       <meta http-equiv="Content-Language" content="en-us" />\r
+       <title>Loading &amp; displaying co-ordinates of crop area on attachment test</title>\r
+       <script src="../lib/prototype.js" type="text/javascript"></script>      \r
+       <script src="../lib/scriptaculous.js?load=builder,dragdrop" type="text/javascript"></script>\r
+       <script src="../cropper.js" type="text/javascript"></script>\r
+       \r
+       \r
+       <script type="text/javascript" charset="utf-8">\r
+               \r
+               // setup the callback function\r
+               function onEndCrop( coords, dimensions ) {\r
+                       $PR( 'x1' ).value = coords.x1;\r
+                       $PR( 'y1' ).value = coords.y1;\r
+                       $PR( 'x2' ).value = coords.x2;\r
+                       $PR( 'y2' ).value = coords.y2;\r
+                       $PR( 'width' ).value = dimensions.width;\r
+                       $PR( 'height' ).value = dimensions.height;\r
+               }\r
+               \r
+               // basic example\r
+               Event.observe( \r
+                       window, \r
+                       'load', \r
+                       function() { \r
+                               new Cropper.Img( \r
+                                       'testImage',\r
+                                       {\r
+                                               onEndCrop: onEndCrop,\r
+                                               displayOnInit: true,\r
+                                               onloadCoords: { x1: 10, y1: 10, x2: 250, y2: 100 }\r
+                                       }\r
+                               ) \r
+                       }\r
+               );              \r
+               \r
+               \r
+               if( typeof(dump) != 'function' ) {\r
+                       Debug.init(true, '/');\r
+                       \r
+                       function dump( msg ) {\r
+                               Debug.raise( msg );\r
+                       };\r
+               } else dump( '---------------------------------------\n' );\r
+               \r
+               \r
+       </script>\r
+       <link rel="stylesheet" type="text/css" href="debug.css" media="all" />\r
+       <style type="text/css">\r
+               label { \r
+                       clear: left;\r
+                       margin-left: 50px;\r
+                       float: left;\r
+                       width: 5em;\r
+               }\r
+               \r
+               html, body { \r
+                       margin: 0;\r
+               }\r
+               \r
+               #testWrap {\r
+                       margin: 20px 0 0 50px; /* Just while testing, to make sure we return the correct positions for the image & not the window */\r
+               }\r
+       </style>\r
+</head>\r
+<body>\r
+       <h2>Loading &amp; displaying co-ordinates of crop area on attachment test</h2>\r
+       <p>\r
+               Some test content before the image\r
+       </p>\r
+       \r
+       <div id="testWrap">\r
+               <img src="castle.jpg" alt="test image" id="testImage" width="500" height="333" />\r
+       </div>\r
+       \r
+       \r
+       <p>\r
+               <label for="x1">x1:</label>\r
+               <input type="text" name="x1" id="x1" />\r
+       </p>\r
+       <p>\r
+               <label for="y1">y1:</label>\r
+               <input type="text" name="y1" id="y1" />\r
+       </p>\r
+       <p>\r
+               <label for="x2">x2:</label>\r
+               <input type="text" name="x2" id="x2" />\r
+       </p>\r
+       <p>\r
+               <label for="y2">y2:</label>\r
+               <input type="text" name="y2" id="y2" />\r
+       </p>\r
+       <p>\r
+               <label for="width">width:</label>\r
+               <input type="text" name="width" id="width" />\r
+       </p>\r
+       <p>\r
+               <label for="height">height</label>\r
+               <input type="text" name="height" id="height" />\r
+       </p>  \r
+       \r
+</body>\r
+</html>\r
+\r
+\r
diff --git a/view/js/cropper/tests/example-CoordsOnLoadWithRatio.htm b/view/js/cropper/tests/example-CoordsOnLoadWithRatio.htm
new file mode 100644 (file)
index 0000000..9ba02da
--- /dev/null
@@ -0,0 +1,109 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\r
+<head>\r
+       <meta http-equiv="Content-type" content="text/html; charset=utf-8" />\r
+       <meta http-equiv="Content-Language" content="en-us" />\r
+       <title>Loading &amp; displaying co-ordinates (with ratio) of crop area on attachment test<</title>\r
+       <script src="../lib/prototype.js" type="text/javascript"></script>      \r
+       <script src="../lib/scriptaculous.js?load=builder,dragdrop" type="text/javascript"></script>\r
+       <script src="../cropper.js" type="text/javascript"></script>\r
+       \r
+       \r
+       <script type="text/javascript" charset="utf-8">\r
+               \r
+               // setup the callback function\r
+               function onEndCrop( coords, dimensions ) {\r
+                       $PR( 'x1' ).value = coords.x1;\r
+                       $PR( 'y1' ).value = coords.y1;\r
+                       $PR( 'x2' ).value = coords.x2;\r
+                       $PR( 'y2' ).value = coords.y2;\r
+                       $PR( 'width' ).value = dimensions.width;\r
+                       $PR( 'height' ).value = dimensions.height;\r
+               }\r
+               \r
+               // basic example\r
+               Event.observe( \r
+                       window, \r
+                       'load', \r
+                       function() { \r
+                               new Cropper.Img( \r
+                                       'testImage',\r
+                                       {\r
+                                               onEndCrop: onEndCrop,\r
+                                               displayOnInit: true,\r
+                                               onloadCoords: { x1: 10, y1: 10, x2: 210, y2: 110 },\r
+                                               ratioDim: { x: 200, y: 100 }\r
+                                       }\r
+                               ) \r
+                       }\r
+               );              \r
+               \r
+               \r
+               if( typeof(dump) != 'function' ) {\r
+                       Debug.init(true, '/');\r
+                       \r
+                       function dump( msg ) {\r
+                               Debug.raise( msg );\r
+                       };\r
+               } else dump( '---------------------------------------\n' );\r
+               \r
+               \r
+       </script>\r
+       <link rel="stylesheet" type="text/css" href="debug.css" media="all" />\r
+       <style type="text/css">\r
+               label { \r
+                       clear: left;\r
+                       margin-left: 50px;\r
+                       float: left;\r
+                       width: 5em;\r
+               }\r
+               \r
+               html, body { \r
+                       margin: 0;\r
+               }\r
+               \r
+               #testWrap {\r
+                       margin: 20px 0 0 50px; /* Just while testing, to make sure we return the correct positions for the image & not the window */\r
+               }\r
+       </style>\r
+</head>\r
+<body>\r
+       <h2>Loading &amp; displaying co-ordinates (with ratio) of crop area on attachment test</h2>\r
+       <p>\r
+               Some test content before the image\r
+       </p>\r
+       \r
+       <div id="testWrap">\r
+               <img src="castle.jpg" alt="test image" id="testImage" width="500" height="333" />\r
+       </div>\r
+       \r
+       \r
+       <p>\r
+               <label for="x1">x1:</label>\r
+               <input type="text" name="x1" id="x1" />\r
+       </p>\r
+       <p>\r
+               <label for="y1">y1:</label>\r
+               <input type="text" name="y1" id="y1" />\r
+       </p>\r
+       <p>\r
+               <label for="x2">x2:</label>\r
+               <input type="text" name="x2" id="x2" />\r
+       </p>\r
+       <p>\r
+               <label for="y2">y2:</label>\r
+               <input type="text" name="y2" id="y2" />\r
+       </p>\r
+       <p>\r
+               <label for="width">width:</label>\r
+               <input type="text" name="width" id="width" />\r
+       </p>\r
+       <p>\r
+               <label for="height">height</label>\r
+               <input type="text" name="height" id="height" />\r
+       </p>  \r
+       \r
+</body>\r
+</html>\r
+\r
+\r
diff --git a/view/js/cropper/tests/example-Dimensions.htm b/view/js/cropper/tests/example-Dimensions.htm
new file mode 100644 (file)
index 0000000..10e5ba2
--- /dev/null
@@ -0,0 +1,225 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+       <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+       <meta http-equiv="Content-Language" content="en-us" />
+       <title>Different dimensions test</title>
+       <script src="../lib/prototype.js" type="text/javascript"></script>      
+       <script src="../lib/scriptaculous.js?load=builder,dragdrop" type="text/javascript"></script>
+       <script src="../cropper.js" type="text/javascript"></script>
+       
+       
+       <script type="text/javascript" charset="utf-8">
+               
+               function onEndCrop( coords, dimensions ) {
+                       $PR( 'x1' ).value = coords.x1;
+                       $PR( 'y1' ).value = coords.y1;
+                       $PR( 'x2' ).value = coords.x2;
+                       $PR( 'y2' ).value = coords.y2;
+                       $PR( 'width' ).value = dimensions.width;
+                       $PR( 'height' ).value = dimensions.height;
+               }
+               
+               /*
+               
+               // example with minimum dimensions
+               Event.observe( 
+                       window, 
+                       'load', 
+                       function() { 
+                               new Cropper.Img( 
+                                       'testImage', 
+                                       { 
+                                               minWidth: 200, 
+                                               minHeight: 120,
+                                               maxWidth: 200,
+                                               //maxHeight: 120,
+                                               displayOnInit: true, 
+                                               onEndCrop: onEndCrop 
+                                       } 
+                               ) 
+                       } 
+               );
+               */
+               
+               Event.observe( window, 'load',
+                       function() {
+                               Event.observe( 'dimensionsForm', 'submit', CropManager.attachCropper.bindAsEventListener( CropManager ) );
+                               CropManager.attachCropper();
+                       }
+               );
+               
+               /**
+                * A little manager that allows us to reset the options dynamically
+                */
+               var CropManager = {
+                       /**
+                        * Holds the current Cropper.Img object
+                        * @var obj
+                        */
+                       curCrop: null,
+                       
+                       /**
+                        * Gets a min/max parameter from the form 
+                        * 
+                        * @access private
+                        * @param string Form element ID
+                        * @return int
+                        */
+                       getParam: function( name ) {
+                               var val = $F( name );
+                               console.log( name + ' :: ' + val );
+                               return parseInt( val );
+                       },
+                                                                       
+                       /** 
+                        * Attaches/resets the image cropper
+                        *
+                        * @access private
+                        * @param obj Event object
+                        * @return void
+                        */
+                       attachCropper: function( e ) {
+                               if( this.curCrop == null ) {
+                                       this.curCrop = new Cropper.Img( 
+                                               'testImage', 
+                                               { 
+                                                       minWidth: this.getParam( 'minWidth' ),
+                                                       minHeight: this.getParam( 'minHeight' ),
+                                                       maxWidth: this.getParam( 'maxWidth' ),
+                                                       maxHeight: this.getParam( 'maxHeight' ),
+                                                       onEndCrop: onEndCrop 
+                                               } 
+                                       );
+                               } else {
+                                       this.removeCropper();
+                                       this.curCrop.initialize( 
+                                               'testImage', 
+                                               { 
+                                                       minWidth: this.getParam( 'minWidth' ),
+                                                       minHeight: this.getParam( 'minHeight' ),
+                                                       maxWidth: this.getParam( 'maxWidth' ),
+                                                       maxHeight: this.getParam( 'maxHeight' ),
+                                                       onEndCrop: onEndCrop 
+                                               } 
+                                       );
+                               }
+                               if( e != null ) Event.stop( e );
+                       },
+                       
+                       /**
+                        * Removes the cropper
+                        *
+                        * @access public
+                        * @return void
+                        */
+                       removeCropper: function() {
+                               if( this.curCrop != null ) {
+                                       this.curCrop.remove();
+                               }
+                       },
+                       
+                       /**
+                        * Resets the cropper, either re-setting or re-applying
+                        *
+                        * @access public
+                        * @return void
+                        */
+                       resetCropper: function() {
+                               this.attachCropper();
+                       }
+               };              
+               
+               /*
+               if( typeof(dump) != 'function' ) {
+                       Debug.init(true, '/');
+                       
+                       function dump( msg ) {
+                               // Debug.raise( msg );
+                       };
+               } else dump( '---------------------------------------\n' );
+               */
+               
+       </script>
+       <link rel="stylesheet" type="text/css" href="debug.css" media="all" />
+       <style type="text/css">
+               label { 
+                       clear: left;
+                       margin-left: 50px;
+                       float: left;
+                       width: 5em;
+               }
+               
+               #testWrap {
+                       margin: 20px 0 0 50px; /* Just while testing, to make sure we return the correct positions for the image & not the window */
+               }
+               
+               #dimensionsForm {
+                       float: right;
+                       width: 350px;
+               }
+       </style>
+</head>
+<body> 
+       <h2>Multiple dimensions tests</h2>
+       <p>
+               Test of applying different dimension restrictions to the cropper
+       </p>
+       
+       <form action="#" id="dimensionsForm">
+               <fieldset>
+                       Set the cropper with the following dimension restrictions:
+                       <p>
+                               <label for="minWidth">Min Width</label>
+                               <input type="text" size="10" maxlength="3" value="200" id="minWidth" name="minWidth" />
+                       </p>    
+                       <p>
+                               <label for="maxWidth">Max Width</label>
+                               <input type="text" size="10" maxlength="3" value="200" id="maxWidth" name="maxWidth" />
+                       </p>    
+                       <p>
+                               <label for="minHeight">Min Height</label>
+                               <input type="text" size="10" maxlength="3" value="120" id="minHeight" name="minHeight" />
+                       </p>    
+                       <p>
+                               <label for="maxHeight">Max Height</label>
+                               <input type="text" size="10" maxlength="3" value="120" id="maxHeight" name="maxHeight" />
+                       </p>    
+                       <input type="submit" value="Set Cropper" />
+               </fieldset>
+       </form>
+       
+       <div id="testWrap">
+               <img src="castle.jpg" alt="test image" id="testImage" width="500" height="333" />
+       </div>
+       
+       
+       <p>
+               <label for="x1">x1:</label>
+               <input type="text" name="x1" id="x1" />
+       </p>
+       <p>
+               <label for="y1">y1:</label>
+               <input type="text" name="y1" id="y1" />
+       </p>
+       <p>
+               <label for="x2">x2:</label>
+               <input type="text" name="x2" id="x2" />
+       </p>
+       <p>
+               <label for="y2">y2:</label>
+               <input type="text" name="y2" id="y2" />
+       </p>
+       <p>
+               <label for="width">width:</label>
+               <input type="text" name="width" id="width" />
+       </p>
+       <p>
+               <label for="height">height</label>
+               <input type="text" name="height" id="height" />
+       </p>  
+       
+</body>
+</html>
+
+
diff --git a/view/js/cropper/tests/example-DynamicImage.htm b/view/js/cropper/tests/example-DynamicImage.htm
new file mode 100644 (file)
index 0000000..08240bb
--- /dev/null
@@ -0,0 +1,203 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\r
+<head>\r
+       <meta http-equiv="Content-type" content="text/html; charset=utf-8" />\r
+       <meta http-equiv="Content-Language" content="en-us" />\r
+       <title>Dynamic image test</title>\r
+       <script src="../lib/prototype.js" type="text/javascript"></script>      \r
+       <script src="../lib/scriptaculous.js?load=builder,dragdrop" type="text/javascript"></script>\r
+       <script src="../cropper.js" type="text/javascript"></script>\r
+       \r
+       \r
+       <script type="text/javascript" charset="utf-8">\r
+               \r
+               /**\r
+                * A little manager that allows us to swap the image dynamically\r
+                *\r
+                */\r
+               var CropImageManager = {\r
+                       /**\r
+                        * Holds the current Cropper.Img object\r
+                        * @var obj\r
+                        */\r
+                       curCrop: null,\r
+                       \r
+                       /**\r
+                        * Initialises the cropImageManager\r
+                        *\r
+                        * @access public\r
+                        * @return void\r
+                        */\r
+                       init: function() {\r
+                               this.attachCropper();\r
+                       },\r
+                       \r
+                       /**\r
+                        * Handles the changing of the select to change the image, the option value\r
+                        * is a pipe seperated list of imgSrc|width|height\r
+                        * \r
+                        * @access public\r
+                        * @param obj event\r
+                        * @return void\r
+                        */\r
+                       onChange: function( e ) {\r
+                               var vals = $F( Event.element( e ) ).split('|');\r
+                               this.setImage( vals[0], vals[1], vals[2] ); \r
+                       },\r
+                       \r
+                       /**\r
+                        * Sets the image within the element & attaches/resets the image cropper\r
+                        *\r
+                        * @access private\r
+                        * @param string Source path of new image\r
+                        * @param int Width of new image in pixels\r
+                        * @param int Height of new image in pixels\r
+                        * @return void\r
+                        */\r
+                       setImage: function( imgSrc, w, h ) {\r
+                               $PR( 'testImage' ).src = imgSrc;\r
+                               $PR( 'testImage' ).width = w;\r
+                               $PR( 'testImage' ).height = h;\r
+                               this.attachCropper();\r
+                       },\r
+                       \r
+                       /** \r
+                        * Attaches/resets the image cropper\r
+                        *\r
+                        * @access private\r
+                        * @return void\r
+                        */\r
+                       attachCropper: function() {\r
+                               if( this.curCrop == null ) this.curCrop = new Cropper.Img( 'testImage', { onEndCrop: onEndCrop } );\r
+                               else this.curCrop.reset();\r
+                       },\r
+                       \r
+                       /**\r
+                        * Removes the cropper\r
+                        *\r
+                        * @access public\r
+                        * @return void\r
+                        */\r
+                       removeCropper: function() {\r
+                               if( this.curCrop != null ) {\r
+                                       this.curCrop.remove();\r
+                               }\r
+                       },\r
+                       \r
+                       /**\r
+                        * Resets the cropper, either re-setting or re-applying\r
+                        *\r
+                        * @access public\r
+                        * @return void\r
+                        */\r
+                       resetCropper: function() {\r
+                               this.attachCropper();\r
+                       }\r
+               };\r
+               \r
+               \r
+               // setup the callback function\r
+               function onEndCrop( coords, dimensions ) {\r
+                       $PR( 'x1' ).value = coords.x1;\r
+                       $PR( 'y1' ).value = coords.y1;\r
+                       $PR( 'x2' ).value = coords.x2;\r
+                       $PR( 'y2' ).value = coords.y2;\r
+                       $PR( 'width' ).value = dimensions.width;\r
+                       $PR( 'height' ).value = dimensions.height;\r
+               }\r
+               \r
+               // basic example\r
+               Event.observe( \r
+                       window, \r
+                       'load', \r
+                       function() { \r
+                               CropImageManager.init();\r
+                               Event.observe( $PR('removeCropper'), 'click', CropImageManager.removeCropper.bindAsEventListener( CropImageManager ), false );\r
+                               Event.observe( $PR('resetCropper'), 'click', CropImageManager.resetCropper.bindAsEventListener( CropImageManager ), false );\r
+                               Event.observe( $PR('imageChoice'), 'change', CropImageManager.onChange.bindAsEventListener( CropImageManager ), false );\r
+                       }\r
+               );              \r
+               \r
+               \r
+               /*\r
+               if( typeof(dump) != 'function' ) {\r
+                       Debug.init(true, '/');\r
+                       \r
+                       function dump( msg ) {\r
+                               Debug.raise( msg );\r
+                       };\r
+               } else dump( '---------------------------------------\n' );\r
+               */\r
+               \r
+       </script>\r
+       <link rel="stylesheet" type="text/css" href="debug.css" media="all" />\r
+       <style type="text/css">\r
+               label { \r
+                       clear: left;\r
+                       margin-left: 50px;\r
+                       float: left;\r
+                       width: 5em;\r
+               }\r
+               \r
+               html, body { \r
+                       margin: 0;\r
+               }\r
+               \r
+               #testWrap {\r
+                       margin: 20px 0 0 50px; /* Just while testing, to make sure we return the correct positions for the image & not the window */\r
+               }\r
+       </style>\r
+</head>\r
+<body>\r
+       <h2>Dynamic image test</h2>\r
+       <p>\r
+               Test of dynamically changing images or removing & re-applying the cropper\r
+       </p>\r
+       \r
+       <div id="testWrap">\r
+               <img src="castle.jpg" alt="test image" id="testImage" width="500" height="333" />\r
+       </div>\r
+       \r
+       <p>\r
+               <label for="imageChoice">image:</label>\r
+               <select name="imageChoice" id="imageChoice">\r
+                       <option value="castle.jpg|500|333">Castle</option>\r
+                       <option value="poppy.jpg|311|466">Flower</option>\r
+               </select>\r
+       </p>\r
+       \r
+       <p>\r
+               <input type="button" id="removeCropper" value="Remove Cropper" />\r
+               <input type="button" id="resetCropper" value="Reset Cropper" />\r
+       </p>\r
+       \r
+       \r
+       <p>\r
+               <label for="x1">x1:</label>\r
+               <input type="text" name="x1" id="x1" />\r
+       </p>\r
+       <p>\r
+               <label for="y1">y1:</label>\r
+               <input type="text" name="y1" id="y1" />\r
+       </p>\r
+       <p>\r
+               <label for="x2">x2:</label>\r
+               <input type="text" name="x2" id="x2" />\r
+       </p>\r
+       <p>\r
+               <label for="y2">y2:</label>\r
+               <input type="text" name="y2" id="y2" />\r
+       </p>\r
+       <p>\r
+               <label for="width">width:</label>\r
+               <input type="text" name="width" id="width" />\r
+       </p>\r
+       <p>\r
+               <label for="height">height</label>\r
+               <input type="text" name="height" id="height" />\r
+       </p>  \r
+       \r
+</body>\r
+</html>\r
+\r
+\r
diff --git a/view/js/cropper/tests/example-FixedRatio.htm b/view/js/cropper/tests/example-FixedRatio.htm
new file mode 100644 (file)
index 0000000..8d196c1
--- /dev/null
@@ -0,0 +1,104 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\r
+<head>\r
+       <meta http-equiv="Content-type" content="text/html; charset=utf-8" />\r
+       <meta http-equiv="Content-Language" content="en-us" />\r
+       <title>Fixed ratio test</title>\r
+       <script src="../lib/prototype.js" type="text/javascript"></script>      \r
+       <script src="../lib/scriptaculous.js?load=builder,dragdrop" type="text/javascript"></script>\r
+       <script src="../cropper.js" type="text/javascript"></script>\r
+       \r
+       \r
+       <script type="text/javascript" charset="utf-8">\r
+               \r
+               function onEndCrop( coords, dimensions ) {\r
+                       $PR( 'x1' ).value = coords.x1;\r
+                       $PR( 'y1' ).value = coords.y1;\r
+                       $PR( 'x2' ).value = coords.x2;\r
+                       $PR( 'y2' ).value = coords.y2;\r
+                       $PR( 'width' ).value = dimensions.width;\r
+                       $PR( 'height' ).value = dimensions.height;\r
+               }\r
+               \r
+               // with a supplied ratio\r
+               Event.observe( \r
+                       window, \r
+                       'load', \r
+                       function() { \r
+                               new Cropper.Img( \r
+                                       'testImage', \r
+                                       { \r
+                                               ratioDim: { x: 220, y: 124 }, \r
+                                               displayOnInit: true, \r
+                                               onEndCrop: onEndCrop \r
+                                       } \r
+                               ) \r
+                       } \r
+               );\r
+               \r
+               /*\r
+               if( typeof(dump) != 'function' ) {\r
+                       Debug.init(true, '/');\r
+                       \r
+                       function dump( msg ) {\r
+                               // Debug.raise( msg );\r
+                       };\r
+               } else dump( '---------------------------------------\n' );\r
+               */\r
+               \r
+       </script>\r
+       <link rel="stylesheet" type="text/css" href="debug.css" media="all" />\r
+       <style type="text/css">\r
+               label { \r
+                       clear: left;\r
+                       margin-left: 50px;\r
+                       float: left;\r
+                       width: 5em;\r
+               }\r
+               \r
+               #testWrap {\r
+                       margin: 20px 0 0 50px; /* Just while testing, to make sure we return the correct positions for the image & not the window */\r
+               }\r
+       </style>\r
+</head>\r
+<body> \r
+       <h2>Fixed ratio test</h2>\r
+       <p>\r
+               Test of applying a fixed ratio to the cropper\r
+       </p>\r
+       <br />\r
+       \r
+       <div id="testWrap">\r
+               <img src="castle.jpg" alt="test image" id="testImage" width="500" height="333" />\r
+       </div>\r
+       \r
+       \r
+       <p>\r
+               <label for="x1">x1:</label>\r
+               <input type="text" name="x1" id="x1" />\r
+       </p>\r
+       <p>\r
+               <label for="y1">y1:</label>\r
+               <input type="text" name="y1" id="y1" />\r
+       </p>\r
+       <p>\r
+               <label for="x2">x2:</label>\r
+               <input type="text" name="x2" id="x2" />\r
+       </p>\r
+       <p>\r
+               <label for="y2">y2:</label>\r
+               <input type="text" name="y2" id="y2" />\r
+       </p>\r
+       <p>\r
+               <label for="width">width:</label>\r
+               <input type="text" name="width" id="width" />\r
+       </p>\r
+       <p>\r
+               <label for="height">height</label>\r
+               <input type="text" name="height" id="height" />\r
+       </p>  \r
+       \r
+</body>\r
+</html>\r
+\r
+\r
diff --git a/view/js/cropper/tests/example-MinimumDimensions.htm b/view/js/cropper/tests/example-MinimumDimensions.htm
new file mode 100644 (file)
index 0000000..e6d96b3
--- /dev/null
@@ -0,0 +1,105 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\r
+<head>\r
+       <meta http-equiv="Content-type" content="text/html; charset=utf-8" />\r
+       <meta http-equiv="Content-Language" content="en-us" />\r
+       <title>Min dimensions test</title>\r
+       <script src="../lib/prototype.js" type="text/javascript"></script>      \r
+       <script src="../lib/scriptaculous.js?load=builder,dragdrop" type="text/javascript"></script>\r
+       <script src="../cropper.js" type="text/javascript"></script>\r
+       \r
+       \r
+       <script type="text/javascript" charset="utf-8">\r
+               \r
+               function onEndCrop( coords, dimensions ) {\r
+                       $PR( 'x1' ).value = coords.x1;\r
+                       $PR( 'y1' ).value = coords.y1;\r
+                       $PR( 'x2' ).value = coords.x2;\r
+                       $PR( 'y2' ).value = coords.y2;\r
+                       $PR( 'width' ).value = dimensions.width;\r
+                       $PR( 'height' ).value = dimensions.height;\r
+               }\r
+               \r
+               // example with minimum dimensions\r
+               Event.observe( \r
+                       window, \r
+                       'load', \r
+                       function() { \r
+                               new Cropper.Img( \r
+                                       'testImage', \r
+                                       { \r
+                                               minWidth: 200, \r
+                                               minHeight: 120, \r
+                                               displayOnInit: true, \r
+                                               onEndCrop: onEndCrop \r
+                                       } \r
+                               ) \r
+                       } \r
+               );\r
+               \r
+               /*\r
+               if( typeof(dump) != 'function' ) {\r
+                       Debug.init(true, '/');\r
+                       \r
+                       function dump( msg ) {\r
+                               // Debug.raise( msg );\r
+                       };\r
+               } else dump( '---------------------------------------\n' );\r
+               */\r
+               \r
+       </script>\r
+       <link rel="stylesheet" type="text/css" href="debug.css" media="all" />\r
+       <style type="text/css">\r
+               label { \r
+                       clear: left;\r
+                       margin-left: 50px;\r
+                       float: left;\r
+                       width: 5em;\r
+               }\r
+               \r
+               #testWrap {\r
+                       margin: 20px 0 0 50px; /* Just while testing, to make sure we return the correct positions for the image & not the window */\r
+               }\r
+       </style>\r
+</head>\r
+<body> \r
+       <h2>Minimum (both axes ) dimension test</h2>\r
+       <p>\r
+               Test of applying a minimum dimension to both axes to the cropper\r
+       </p>\r
+       <br />\r
+       \r
+       <div id="testWrap">\r
+               <img src="castle.jpg" alt="test image" id="testImage" width="500" height="333" />\r
+       </div>\r
+       \r
+       \r
+       <p>\r
+               <label for="x1">x1:</label>\r
+               <input type="text" name="x1" id="x1" />\r
+       </p>\r
+       <p>\r
+               <label for="y1">y1:</label>\r
+               <input type="text" name="y1" id="y1" />\r
+       </p>\r
+       <p>\r
+               <label for="x2">x2:</label>\r
+               <input type="text" name="x2" id="x2" />\r
+       </p>\r
+       <p>\r
+               <label for="y2">y2:</label>\r
+               <input type="text" name="y2" id="y2" />\r
+       </p>\r
+       <p>\r
+               <label for="width">width:</label>\r
+               <input type="text" name="width" id="width" />\r
+       </p>\r
+       <p>\r
+               <label for="height">height</label>\r
+               <input type="text" name="height" id="height" />\r
+       </p>  \r
+       \r
+</body>\r
+</html>\r
+\r
+\r
diff --git a/view/js/cropper/tests/example-MinimumWidth.htm b/view/js/cropper/tests/example-MinimumWidth.htm
new file mode 100644 (file)
index 0000000..ec5d696
--- /dev/null
@@ -0,0 +1,105 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\r
+<head>\r
+       <meta http-equiv="Content-type" content="text/html; charset=utf-8" />\r
+       <meta http-equiv="Content-Language" content="en-us" />\r
+       <title>Min (single axis) dimensions test</title>\r
+       <script src="../lib/prototype.js" type="text/javascript"></script>      \r
+       <script src="../lib/scriptaculous.js?load=builder,dragdrop" type="text/javascript"></script>\r
+       <script src="../cropper.js" type="text/javascript"></script>\r
+       \r
+       \r
+       <script type="text/javascript" charset="utf-8">\r
+               \r
+               function onEndCrop( coords, dimensions ) {\r
+                       $PR( 'x1' ).value = coords.x1;\r
+                       $PR( 'y1' ).value = coords.y1;\r
+                       $PR( 'x2' ).value = coords.x2;\r
+                       $PR( 'y2' ).value = coords.y2;\r
+                       $PR( 'width' ).value = dimensions.width;\r
+                       $PR( 'height' ).value = dimensions.height;\r
+               }\r
+               \r
+               // example with minimum dimensions\r
+               Event.observe( \r
+                       window, \r
+                       'load', \r
+                       function() { \r
+                               new Cropper.Img( \r
+                                       'testImage', \r
+                                       { \r
+                                               minWidth: 200, \r
+                                               displayOnInit: true, \r
+                                               onEndCrop: onEndCrop \r
+                                       } \r
+                               ) \r
+                       } \r
+               );\r
+               \r
+               /*\r
+               if( typeof(dump) != 'function' ) {\r
+                       Debug.init(true, '/');\r
+                       \r
+                       function dump( msg ) {\r
+                               // Debug.raise( msg );\r
+                       };\r
+               } else dump( '---------------------------------------\n' );\r
+               */\r
+               \r
+       </script>\r
+       <link rel="stylesheet" type="text/css" href="debug.css" media="all" />\r
+       <style type="text/css">\r
+               label { \r
+                       clear: left;\r
+                       margin-left: 50px;\r
+                       float: left;\r
+                       width: 5em;\r
+               }\r
+               \r
+               #testWrap {\r
+                       margin: 20px 0 0 50px; /* Just while testing, to make sure we return the correct positions for the image & not the window */\r
+               }\r
+       </style>\r
+</head>\r
+<body> \r
+       <h2>Minimum (single axis) dimension test</h2>\r
+       <p>\r
+               Test of applying a minimum dimension to only one axis (width in this case) to the cropper\r
+       </p>\r
+       <br />\r
+       <br /><br />\r
+       \r
+       <div id="testWrap">\r
+               <img src="castle.jpg" alt="test image" id="testImage" width="500" height="333" />\r
+       </div>\r
+       \r
+       \r
+       <p>\r
+               <label for="x1">x1:</label>\r
+               <input type="text" name="x1" id="x1" />\r
+       </p>\r
+       <p>\r
+               <label for="y1">y1:</label>\r
+               <input type="text" name="y1" id="y1" />\r
+       </p>\r
+       <p>\r
+               <label for="x2">x2:</label>\r
+               <input type="text" name="x2" id="x2" />\r
+       </p>\r
+       <p>\r
+               <label for="y2">y2:</label>\r
+               <input type="text" name="y2" id="y2" />\r
+       </p>\r
+       <p>\r
+               <label for="width">width:</label>\r
+               <input type="text" name="width" id="width" />\r
+       </p>\r
+       <p>\r
+               <label for="height">height</label>\r
+               <input type="text" name="height" id="height" />\r
+       </p>  \r
+       \r
+</body>\r
+</html>\r
+\r
+\r
diff --git a/view/js/cropper/tests/example-Preview.htm b/view/js/cropper/tests/example-Preview.htm
new file mode 100644 (file)
index 0000000..51bf260
--- /dev/null
@@ -0,0 +1,117 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\r
+<head>\r
+       <meta http-equiv="Content-type" content="text/html; charset=utf-8" />\r
+       <meta http-equiv="Content-Language" content="en-us" />\r
+       <title></title>\r
+       <script src="../lib/prototype.js" type="text/javascript"></script>      \r
+       <script src="../lib/scriptaculous.js?load=builder,dragdrop" type="text/javascript"></script>\r
+       <script src="../cropper.js" type="text/javascript"></script>\r
+       \r
+       \r
+       <script type="text/javascript" charset="utf-8">\r
+               \r
+               function onEndCrop( coords, dimensions ) {\r
+                       $PR( 'x1' ).value = coords.x1;\r
+                       $PR( 'y1' ).value = coords.y1;\r
+                       $PR( 'x2' ).value = coords.x2;\r
+                       $PR( 'y2' ).value = coords.y2;\r
+                       $PR( 'width' ).value = dimensions.width;\r
+                       $PR( 'height' ).value = dimensions.height;\r
+               }\r
+               \r
+               // example with a preview of crop results, must have minimumm dimensions\r
+               Event.observe( \r
+                       window, \r
+                       'load', \r
+                       function() { \r
+                               new Cropper.ImgWithPreview( \r
+                                       'testImage',\r
+                                       { \r
+                                               minWidth: 200, \r
+                                               minHeight: 120,\r
+                                               ratioDim: { x: 200, y: 120 },\r
+                                               displayOnInit: true, \r
+                                               onEndCrop: onEndCrop,\r
+                                               previewWrap: 'previewArea'\r
+                                       } \r
+                               ) \r
+                       } \r
+               );\r
+               \r
+               /*\r
+               if( typeof(dump) != 'function' ) {\r
+                       Debug.init(true, '/');\r
+                       \r
+                       function dump( msg ) {\r
+                               // Debug.raise( msg );\r
+                       };\r
+               } else dump( '---------------------------------------\n' );\r
+               */\r
+               \r
+       </script>\r
+       <link rel="stylesheet" type="text/css" href="debug.css" media="all" />\r
+       <style type="text/css">\r
+               label { \r
+                       clear: left;\r
+                       margin-left: 50px;\r
+                       float: left;\r
+                       width: 5em;\r
+               }\r
+               \r
+               #testWrap {\r
+                       width: 500px;\r
+                       float: left;\r
+                       margin: 20px 0 0 50px; /* Just while testing, to make sure we return the correct positions for the image & not the window */\r
+               }\r
+               \r
+               #previewArea {\r
+                       margin: 20px; 0 0 20px;\r
+                       float: left;\r
+               }\r
+               \r
+               #results {\r
+                       clear: both;\r
+               }\r
+       </style>\r
+</head>\r
+<body> \r
+       <br /><br />\r
+       \r
+       <div id="testWrap">\r
+               <img src="castle.jpg" alt="test image" id="testImage" width="500" height="333" />\r
+       </div>\r
+       \r
+       <div id="previewArea"></div>\r
+       \r
+       <div id="results">\r
+               <p>\r
+                       <label for="x1">x1:</label>\r
+                       <input type="text" name="x1" id="x1" />\r
+               </p>\r
+               <p>\r
+                       <label for="y1">y1:</label>\r
+                       <input type="text" name="y1" id="y1" />\r
+               </p>\r
+               <p>\r
+                       <label for="x2">x2:</label>\r
+                       <input type="text" name="x2" id="x2" />\r
+               </p>\r
+               <p>\r
+                       <label for="y2">y2:</label>\r
+                       <input type="text" name="y2" id="y2" />\r
+               </p>\r
+               <p>\r
+                       <label for="width">width:</label>\r
+                       <input type="text" name="width" id="width" />\r
+               </p>\r
+               <p>\r
+                       <label for="height">height</label>\r
+                       <input type="text" name="height" id="height" />\r
+               </p>\r
+       </div> \r
+       \r
+</body>\r
+</html>\r
+\r
+\r
diff --git a/view/js/cropper/tests/poppy.jpg b/view/js/cropper/tests/poppy.jpg
new file mode 100644 (file)
index 0000000..1f64985
Binary files /dev/null and b/view/js/cropper/tests/poppy.jpg differ
diff --git a/view/js/cropper/tests/staticHTMLStructure.htm b/view/js/cropper/tests/staticHTMLStructure.htm
new file mode 100644 (file)
index 0000000..ddb9927
--- /dev/null
@@ -0,0 +1,236 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\r
+<head>\r
+       <meta http-equiv="Content-type" content="text/html; charset=utf-8" />\r
+       <meta http-equiv="Content-Language" content="en-us" />\r
+       <title></title>\r
+</head>\r
+<body>\r
+       <!--\r
+               \r
+               This is a static test file for the HTML & CSS structure employed, tested in \r
+               the following browsers:\r
+               \r
+                       PC:\r
+                               IE 6:          working\r
+                               IE 5.5:        working\r
+                               IE 5.0:        opacity issues\r
+                               FF 1.5:        working\r
+                               Opera 9:       working\r
+                       MAC:\r
+                               Camino 1.0:    working\r
+                               FF 1.5:        working\r
+                               Safari 2.0:    working\r
+                               \r
+       -->     \r
+       <style type="text/css">         \r
+               .imgCrop_wrap {\r
+                       width: 500px;   /* @TODO IN JS */\r
+                       height: 333px;  /* @TODO IN JS */\r
+                       position: relative;\r
+                       cursor: crosshair;\r
+               }\r
+               \r
+               /* fix for IE displaying all boxes at line-height by default */\r
+               .imgCrop_wrap,\r
+               .imgCrop_wrap * {\r
+                       font-size: 0;\r
+               }\r
+               \r
+               .imgCrop_overlay {\r
+                       background-color: #000;\r
+                       opacity: 0.5;\r
+                       filter:alpha(opacity=50);\r
+                       position: absolute;\r
+                       width: 100%;\r
+                       height: 100%;\r
+               }\r
+               \r
+               .imgCrop_selArea {\r
+                       position: absolute;\r
+                       cursor: move;\r
+                       /* @TODO: rest to be done via JS when selecting areas */\r
+                       top: 110px;\r
+                       left: 210px;\r
+                       width: 200px;\r
+                       height: 200px;\r
+                       z-index: 2;\r
+                       background: transparent url(castle.jpg) no-repeat  -210px -110px;\r
+               }\r
+               \r
+               /* imgCrop_clickArea is all a fix for IE 5.5 & 6 to allow the user to click on the given area */\r
+               .imgCrop_clickArea {\r
+                       width: 100%;\r
+                       height: 100%;\r
+                       background-color: #FFF;\r
+                       opacity: 0.01;\r
+                       filter:alpha(opacity=01);\r
+               }\r
+                               \r
+               .imgCrop_marqueeHoriz {\r
+                       position: absolute;\r
+                       width: 100%;\r
+                       height: 1px;\r
+                       background: transparent url(marqueeHoriz.gif) repeat-x 0 0;\r
+               }\r
+               \r
+               .imgCrop_marqueeVert {\r
+                       position: absolute;\r
+                       height: 100%;\r
+                       width: 1px;\r
+                       background: transparent url(marqueeVert.gif) repeat-y 0 0;\r
+               }\r
+                               \r
+               .imgCrop_marqueeNorth { top: 0; left: 0; }\r
+               .imgCrop_marqueeEast  { top: 0; right: 0; }\r
+               .imgCrop_marqueeSouth { bottom: 0px; left: 0; }\r
+               .imgCrop_marqueeWest  { top: 0; left: 0; }\r
+               \r
+               \r
+               .imgCrop_handle {\r
+                       position: absolute;\r
+                       border: 1px solid #333;\r
+                       width: 6px;\r
+                       height: 6px;\r
+                       background: #FFF;\r
+                       opacity: 0.5;\r
+                       filter:alpha(opacity=50);\r
+                       z-index: 3;\r
+               }\r
+               \r
+               .imgCrop_handleN {\r
+                       top: -3px;\r
+                       left: 0;\r
+                       margin-left: 49%;    /* @TODO : in JS */\r
+                       cursor: n-resize;\r
+               }\r
+               \r
+               .imgCrop_handleNE { \r
+                       top: -3px;\r
+                       right: -3px;\r
+                       cursor: ne-resize;\r
+               }\r
+               \r
+               .imgCrop_handleE {\r
+                       top: 0;\r
+                       right: -3px;\r
+                       margin-top: 49%;    /* @TODO : in JS */\r
+                       cursor: e-resize;\r
+               }\r
+               \r
+               .imgCrop_handleSE {\r
+                       right: -3px;\r
+                       bottom: -3px;\r
+                       cursor: se-resize;\r
+               }\r
+               \r
+               .imgCrop_handleS {\r
+                       right: 0;\r
+                       bottom: -3px;\r
+                       margin-right: 49%; /* @TODO : in JS */\r
+                       cursor: s-resize;\r
+               }\r
+               \r
+               .imgCrop_handleSW {\r
+                       left: -3px;\r
+                       bottom: -3px;\r
+                       cursor: sw-resize;\r
+               }\r
+               \r
+               .imgCrop_handleW {\r
+                       top: 0;\r
+                       left: -3px;\r
+                       margin-top: 49%;  /* @TODO : in JS */\r
+                       cursor: e-resize;\r
+               }\r
+               \r
+               .imgCrop_handleNW {\r
+                       top: -3px;\r
+                       left: -3px;\r
+                       cursor: nw-resize;\r
+               }\r
+               \r
+               /**\r
+                * Create an area to click & drag around on as the default browser behaviour is to let you drag the image \r
+                */\r
+               .imgCrop_dragArea {\r
+                       width: 100%;\r
+                       height: 100%;\r
+                       z-index: 200;\r
+                       position: absolute;\r
+                       top: 0;\r
+                       left: 0;\r
+               }\r
+               \r
+               \r
+               .imgCrop_previewWrap {\r
+                       width: 200px;  /* @TODO : in JS */\r
+                       height: 200px; /* @TODO : in JS */\r
+                       overflow: hidden;\r
+                       position: relative;\r
+               }\r
+               \r
+               /* @TODO : all in JS */\r
+               .imgCrop_previewWrap img {\r
+                       position: absolute;\r
+                       width: 500px;\r
+                       height: 333px;\r
+                       left: -210px;\r
+                       top: -110px;\r
+               }\r
+               \r
+               /**\r
+                * These are just for the static test\r
+                */\r
+               .imgCrop_wrap {\r
+                       margin: 20px 0 0 50px;\r
+                       float: left;\r
+               }\r
+               \r
+               #previewWrapper {\r
+                       float: left;\r
+                       margin-left: 20px;\r
+               }\r
+               \r
+                       \r
+       </style>\r
+       \r
+       <br /><br />\r
+       \r
+       <!-- This is all attached to the image dynamically -->\r
+       <div class="imgCrop_wrap">\r
+               <img src="castle.jpg" alt="test image" id="testImage" width="500" height="333" />\r
+               <div class="imgCrop_dragArea">\r
+                       <div class="imgCrop_overlay"></div>\r
+                       <div class="imgCrop_selArea">\r
+                               <!-- marquees -->\r
+                               <!-- the inner spans are only required for IE to stop it making the divs 1px high/wide -->\r
+                               <div class="imgCrop_marqueeHoriz imgCrop_marqueeNorth"><span></span></div>\r
+                               <div class="imgCrop_marqueeVert imgCrop_marqueeEast"><span></span></div>\r
+                               <div class="imgCrop_marqueeHoriz imgCrop_marqueeSouth"><span></span></div>\r
+                               <div class="imgCrop_marqueeVert imgCrop_marqueeWest"><span></span></div>                        \r
+                               <!-- handles -->\r
+                               <div class="imgCrop_handle imgCrop_handleN"></div>\r
+                               <div class="imgCrop_handle imgCrop_handleNE"></div>\r
+                               <div class="imgCrop_handle imgCrop_handleE"></div>\r
+                               <div class="imgCrop_handle imgCrop_handleSE"></div>\r
+                               <div class="imgCrop_handle imgCrop_handleS"></div>\r
+                               <div class="imgCrop_handle imgCrop_handleSW"></div>\r
+                               <div class="imgCrop_handle imgCrop_handleW"></div>\r
+                               <div class="imgCrop_handle imgCrop_handleNW"></div>\r
+                               <div class="imgCrop_clickArea"></div>\r
+                       </div>  \r
+                       <div class="imgCrop_clickArea"></div>   \r
+               </div>  \r
+       </div>\r
+       \r
+       <div id="previewWrapper">\r
+               <h3>Preview:</h3>\r
+               <div class="imgCrop_previewWrap">\r
+                       <img src="castle.jpg" alt="test image" id="previewImage" />\r
+               </div>\r
+       </div>\r
+</body>\r
+</html>\r
+\r
+\r
diff --git a/view/js/filebrowser.js b/view/js/filebrowser.js
new file mode 100644 (file)
index 0000000..78cee0e
--- /dev/null
@@ -0,0 +1,143 @@
+/**\r
+ * Filebrowser - Friendica Communications Server\r
+ *\r
+ * Copyright (c) 2010-2015 the Friendica Project\r
+ *\r
+ * This program is free software: you can redistribute it and/or modify\r
+ * it under the terms of the GNU Affero General Public License as published by\r
+ * the Free Software Foundation, either version 3 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This code handle user interaction for image/file upload/browser dialog.\r
+ * Is loaded from filebrowser_plain.tpl\r
+ *\r
+ * To load filebrowser in colorbox, call\r
+ *\r
+ *      Dialog.doImageBrowser(eventname, id);\r
+ *\r
+ * or\r
+ *\r
+ *      Dialog.doFileBrowser(eventname, id);\r
+ *\r
+ * where:\r
+ *\r
+ *             eventname: event name to catch return value\r
+ *             id: id returned to event handler\r
+ *\r
+ * When user select an item, an event in fired in parent page, on body element\r
+ * The event is named\r
+ *\r
+ *             fbrowser.<type>.[<eventname>]\r
+ *\r
+ * <type> will be one of "image" or "file", and the event handler will\r
+ * get the following params:\r
+ *\r
+ *             filemane: filename of item choosed by user\r
+ *             embed: bbcode to embed element into posts\r
+ *             id: id from caller code\r
+ *\r
+ * example:\r
+ *\r
+ *             // open dialog for select an image for a textarea with id "myeditor"\r
+ *             var id="myeditor";\r
+ *             Dialog.doImageBrowser("example", id);\r
+ *\r
+ *             // setup event handler to get user selection\r
+ *             $("body").on("fbrowser.image.example", function(event, filename, bbcode, id) {\r
+ *                     // close colorbox\r
+ *                     $.colorbox.close();\r
+ *                     // replace textxarea text with bbcode\r
+ *                     $(id).value = bbcode;\r
+ *             });\r
+ **/\r
+\r
+var FileBrowser = {\r
+       nickname : "",\r
+       type : "",\r
+       event: "",\r
+       id : null,\r
+\r
+       init: function(nickname, type) {\r
+               FileBrowser.nickname = nickname;\r
+               FileBrowser.type = type;\r
+               FileBrowser.event = "fbrowser."+type;\r
+               if (location['hash']!=="") {\r
+                       var h = location['hash'].replace("#","");\r
+                       FileBrowser.event = FileBrowser.event + "." + h.split("-")[0];\r
+                       FileBrowser.id = h.split("-")[1];\r
+               }\r
+\r
+               console.log("FileBrowser:", nickname, type,FileBrowser.event, FileBrowser.id );\r
+\r
+               $(".error a.close").on("click", function(e) {\r
+                       e.preventDefault();\r
+                       $(".error").addClass("hidden");\r
+               });\r
+\r
+               $(".folders a, .path a").on("click", function(e){\r
+                       e.preventDefault();\r
+                       var url = baseurl + "/fbrowser/" + FileBrowser.type + "/" + this.dataset.folder + "?mode=minimal" + location['hash'];\r
+                       location.href = url;\r
+               });\r
+\r
+               $(".photo-album-photo-link").on('click', function(e){\r
+                       e.preventDefault();\r
+\r
+                       var embed = "";\r
+                       if (FileBrowser.type == "image") {\r
+                               embed = "[url="+this.dataset.link+"][img]"+this.dataset.img+"[/img][/url]";\r
+                       }\r
+                       if (FileBrowser.type=="file") {\r
+                               // attachment links are "baseurl/attach/id"; we need id\r
+                               embed = "[attachment]"+this.dataset.link.split("/").pop()+"[/attachment]";\r
+                       }\r
+                       console.log(FileBrowser.event, this.dataset.filename, embed, FileBrowser.id);\r
+                       parent.$("body").trigger(FileBrowser.event, [\r
+                               this.dataset.filename,\r
+                               embed,\r
+                               FileBrowser.id\r
+                       ]);\r
+\r
+               });\r
+\r
+               if ($("#upload-image").length)\r
+                       var image_uploader = new window.AjaxUpload(\r
+                               'upload-image',\r
+                               { action: 'wall_upload/'+FileBrowser.nickname+'?response=json',\r
+                                       name: 'userfile',\r
+                                       responseType: 'json',\r
+                                       onSubmit: function(file,ext) { $('#profile-rotator').show(); $(".error").addClass('hidden'); },\r
+                                       onComplete: function(file,response) {\r
+                                               if (response['error']!= undefined) {\r
+                                                       $(".error span").html(response['error']);\r
+                                                       $(".error").removeClass('hidden');\r
+                                                       $('#profile-rotator').hide();\r
+                                                       return;\r
+                                               }\r
+                                               location = baseurl + "/fbrowser/image/?mode=minimal"+location['hash'];\r
+                                               location.reload(true);\r
+                                       }\r
+                               }\r
+                       );\r
+\r
+               if ($("#upload-file").length)\r
+                       var file_uploader = new window.AjaxUpload(\r
+                               'upload-file',\r
+                               { action: 'wall_attach/'+FileBrowser.nickname+'?response=json',\r
+                                       name: 'userfile',\r
+                                       onSubmit: function(file,ext) { $('#profile-rotator').show(); $(".error").addClass('hidden'); },\r
+                                       onComplete: function(file,response) {\r
+                                               if (response['error']!= undefined) {\r
+                                                       $(".error span").html(response['error']);\r
+                                                       $(".error").removeClass('hidden');\r
+                                                       $('#profile-rotator').hide();\r
+                                                       return;\r
+                                               }\r
+                                               location = baseurl + "/fbrowser/file/?mode=minimal"+location['hash'];\r
+                                               location.reload(true);\r
+                                       }\r
+                               }\r
+               );\r
+       }\r
+};\r
+\r
diff --git a/view/js/jquery-textcomplete/CHANGELOG.md b/view/js/jquery-textcomplete/CHANGELOG.md
new file mode 100644 (file)
index 0000000..e115bf9
--- /dev/null
@@ -0,0 +1,340 @@
+# Change Log
+
+All notable changes to this project will be documented in this file.
+
+This project adheres to [Semantic Versioning](http://semver.org/) by version 1.0.0.
+
+This change log adheres to [keepachangelog.com](http://keepachangelog.com).
+
+## [Unreleased]
+
+## [1.3.4] - 2016-04-20
+### Fixed
+- Fix endless loop when RTL ([#247](https://github.com/yuku-t/jquery-textcomplete/pull/247))
+
+## [1.3.3] - 2016-04-04
+### Fixed
+- Fix uncaught TypeError.
+
+## [1.3.2] - 2016-03-27
+### Fixed
+- Fix dropdown position problem with `line-height: normal`.
+
+## [1.3.1] - 2016-03-23
+### Fixed
+- Fix `input[type=search]` support.
+
+## [1.3.0] - 2016-03-20
+### Added
+- Add optional "id" strategy parameter.
+
+## [1.2.2] - 2016-03-19
+### Fixed
+- Remove dropdown element after `textcomplete('destroy')`.
+- Skip search after pressing tab.
+- Fix dropdown-menu positioning problem using textarea-caret package.
+
+## [1.2.1] - 2016-03-14
+### Fixed
+- Build dist files.
+
+## [1.2.0] - 2016-03-14
+### Added
+- Support `input[type=search]` ([#236](https://github.com/yuku-t/jquery-textcomplete/pull/236))
+
+## [1.1.0] - 2016-03-10
+### Added
+- Add the ability to insert HTML into a "contenteditable" field. ([#217](https://github.com/yuku-t/jquery-textcomplete/pull/217))
+
+### Fixed
+- Position relative to appendTo element. ([#234](https://github.com/yuku-t/jquery-textcomplete/pull/234))
+- Avoid dropdown bumping into right edge of window. ([#235](https://github.com/yuku-t/jquery-textcomplete/pull/235))
+- Fix top position issue when window is scrolled up and parents has fix position. ([#229](https://github.com/yuku-t/jquery-textcomplete/pull/229))
+
+## [1.0.0] - 2016-02-29
+### Changed
+- Adheres keepachangelog.com.
+
+## [0.8.2] - 2016-02-29
+### Added
+- Add deactivate method to Completer. ([#233](https://github.com/yuku-t/jquery-textcomplete/pull/233))
+
+## [0.8.1] - 2015-10-22
+### Added
+- Add condition to ignore skipUnchangedTerm for empty text. ([#210](https://github.com/yuku-t/jquery-textcomplete/pull/210))
+
+## [0.8.0] - 2015-08-31
+### Changed
+- If undefined is returned from a replace callback dont replace the text. ([#204](https://github.com/yuku-t/jquery-textcomplete/pull/204))
+
+## [0.7.3] - 2015-08-27
+### Added
+- Add `Strategy#el` and `Strategy#$el` which returns current input/textarea element and corresponding jquery object respectively.
+
+## [0.7.2] - 2015-08-26
+### Fixed
+- Reset \_term after selected ([#170](https://github.com/yuku-t/jquery-textcomplete/pull/170))
+
+## [0.7.1] - 2015-08-19
+### Changed
+- Remove RTL support because of some bugs.
+
+## [0.7.0] - 2015-07-02
+### Add
+- Add support for a "no results" message like the header/footer. ([#179](https://github.com/yuku-t/jquery-textcomplete/pull/179))
+- Yield the search term to the template function. ([#177](https://github.com/yuku-t/jquery-textcomplete/pull/177))
+- Add amd wrapper. ([#167](https://github.com/yuku-t/jquery-textcomplete/pull/167))
+- Add touch devices support. ([#163](https://github.com/yuku-t/jquery-textcomplete/pull/163))
+
+### Changed
+- Stop sharing a dropdown element.
+
+## [0.6.1] - 2015-06-30
+### Fixed
+- Fix bug that Dropdown.\_fitToBottom does not consider window scroll
+
+## [0.6.0] - 2015-06-30
+### Added
+- Now dropdown elements have "textcomplete-dropdown" class.
+
+## [0.5.2] - 2015-06-29
+### Fixed
+- Keep dropdown list in browser window. ([#172](https://github.com/yuku-t/jquery-textcomplete/pull/172))
+
+## [0.5.1] - 2015-06-08
+### Changed
+- Now a replace function is invoked with a user event.
+
+## [0.5.0] - 2015-06-08
+### Added
+- Support `onKeydown` option.
+
+## [0.4.0] - 2015-03-10
+### Added
+- Publish to [npmjs](https://www.npmjs.com/package/jquery-textcomplete).
+- Support giving a function which returns a regexp to `match` option for dynamic matching.
+
+## [0.3.9] - 2015-03-03
+### Fixed
+- Deactivate dropdown on escape. ([#155](https://github.com/yuku-t/jquery-textcomplete/pull/155))
+
+## [0.3.8] - 2015-02-26
+### Fixed
+- Fix completion with enter key. ([#154](https://github.com/yuku-t/jquery-textcomplete/pull/154))
+- Fix empty span node is inserted. ([#153](https://github.com/yuku-t/jquery-textcomplete/pull/153))
+
+## [0.3.7] - 2015-01-21
+### Added
+- Support input([type=text]. [#149](https://github.com/yuku-t/jquery-textcomplete/pull/149))
+
+## [0.3.6] - 2014-12-11
+### Added
+- Support element.contentEditable compatibility check. ([#147](https://github.com/yuku-t/jquery-textcomplete/pull/147))
+
+### Fixed
+- Fixes the fire function for events with additional parameters. ([#145](https://github.com/yuku-t/jquery-textcomplete/pull/145))
+
+## [0.3.5] - 2014-12-11
+### Added
+- Adds functionality to complete selection on space key. ([#141](https://github.com/yuku-t/jquery-textcomplete/pull/141))
+
+### Fixed
+- Loading script in head and destroy method bugfixes. ([#143](https://github.com/yuku-t/jquery-textcomplete/pull/143))
+
+## [0.3.4] - 2014-12-03
+### Fixed
+- Fix error when destroy is called before the field is focused. ([#138](https://github.com/yuku-t/jquery-textcomplete/pull/138))
+- Fix IE bug where it would only trigger when tha carrot was at the end of the line. ([#133](https://github.com/yuku-t/jquery-textcomplete/pull/133))
+
+## [0.3.3] - 2014-09-25
+### Added
+- Add `className` option.
+- Add `match` as the third argument of a search function.
+
+### Fixed
+- Ignore `.textcomplete('destory')` on non-initialized elements. ([#118](https://github.com/yuku-t/jquery-textcomplete/pull/118))
+- Trigger completer with the current text by default. ([#119](https://github.com/yuku-t/jquery-textcomplete/pull/119))
+- Hide dropdown before destroying it. ([#120](https://github.com/yuku-t/jquery-textcomplete/pull/120))
+- Don't throw an exception even if a jquery click event is manually triggered. ([#121](https://github.com/yuku-t/jquery-textcomplete/pull/121))
+
+## [0.3.2] - 2014-09-16
+### Added
+- Add `IETextarea` adapter which supports IE8
+- Add `idProperty` option.
+- Add `adapter` option.
+
+### Changed
+- Rename `Input` as `Adapter`.
+
+## [0.3.1] - 2014-09-10
+### Added
+- Add `context` strategy option.
+- Add `debounce` option.
+
+### Changed
+- Recycle `.dropdown-menu` element if available.
+
+## [0.3.0] - 2014-09-10
+### Added
+- Consider the `tab-size` of textarea.
+- Add `zIndex` option.
+
+### Fixed
+- Revive `header` and `footer` options.
+- Revive `height` option.
+
+## [0.3.0-beta2] - 2014-09-09
+### Fixed
+- Make sure that all demos work fine.
+
+## [0.3.0-beta1] - 2014-08-31
+### Fixed
+- Huge refactoring.
+
+## [0.2.6] - 2014-08-16
+### Fixed
+- Repair contenteditable.
+
+## [0.2.5] - 2014-08-07
+### Added
+- Enhance contenteditable support. ([#98](https://github.com/yuku-t/jquery-textcomplete/pull/98))
+- Support absolute left/right placement. ([#96](https://github.com/yuku-t/jquery-textcomplete/pull/96))
+- Support absolute height, scrollbar, pageup and pagedown. ([#87](https://github.com/yuku-t/jquery-textcomplete/pull/87))
+
+## [0.2.4] - 2014-07-02
+### Fixed
+- Fix horizonal position on contentEditable elements. ([#92](https://github.com/yuku-t/jquery-textcomplete/pull/92))
+
+## [0.2.3] - 2014-06-24
+### Added
+- Option to supply list view position function. ([#88](https://github.com/yuku-t/jquery-textcomplete/pull/88))
+
+## [0.2.2] - 2014-06-08
+### Added
+- Append dropdown element to body element by default.
+- Tiny refactoring. [#84]
+- Ignore tab key when modifier keys are being pushed. ([#85](https://github.com/yuku-t/jquery-textcomplete/pull/85))
+- Manual triggering.
+
+## [0.2.1] - 2014-05-15
+### Added
+- Support `appendTo` option.
+- `header` and `footer` supports a function.
+
+### Changed
+- Remove textcomplate-wrapper element.
+
+## [0.2.0] - 2014-05-02
+### Added
+- Contenteditable support.
+- Several bugfixes.
+- Support `header` and `footer` setting.
+
+## [0.1.4.1] - 2014-04-04
+### Added
+- Support placement option.
+- Emacs-style prev/next keybindings.
+- Replay searchFunc for the last term on slow network env.
+
+### Fixed
+- Several bugfixes.
+
+## [0.1.3] - 2014-04-07
+### Added
+- Support RTL positioning.
+
+### Fixed
+- Several bugfixes.
+
+## [0.1.2] - 2014-02-08
+### Added
+- Enable to append strategies on the fly.
+- Enable to stop autocompleting.
+- Enable to apply multiple textareas at once.
+- Don't show popup on pressing arrow up and down keys.
+- Hide dropdown by pressing ESC key.
+- Prevent showing a dropdown when it just autocompleted.
+
+## [0.1.1] - 2014-02-02
+### Added
+- Introduce `textComplete:show`, `textComplete:hide` and `textComplete:select` events.
+
+## [0.1.0] - 2013-10-28
+### Added
+- Now strategies argument is an Array of strategy objects.
+
+## [0.0.4] - 2013-10-28
+### Added
+- Up and Down arrows cycle instead of exit.
+- Support Zepto.
+- Support jQuery.overlay.
+
+### Fixed
+- Several bugfixes.
+
+## [0.0.3] - 2013-09-11
+### Added
+- Some performance improvement.
+- Implement lazy callbacking on search function.
+
+## [0.0.2] - 2013-09-08
+### Added
+- Support IE8.
+- Some performance improvement.
+- Implement cache option.
+
+## 0.0.1 - 2013-09-02
+### Added
+- Initial release.
+
+[Unreleased]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.3.4...HEAD
+[1.3.4]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.3.3...v1.3.4
+[1.3.3]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.3.2...v1.3.3
+[1.3.2]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.3.1...v1.3.2
+[1.3.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.3.0...v1.3.1
+[1.3.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.2.2...v1.3.0
+[1.2.2]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.2.1...v1.2.2
+[1.2.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.2.0...v1.2.1
+[1.2.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.1.0...v1.2.0
+[1.1.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.0.0...v1.1.0
+[1.0.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.8.2...v1.0.0
+[0.8.2]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.8.1...v0.8.2
+[0.8.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.8.0...v0.8.1
+[0.8.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.7.3...v0.8.0
+[0.7.3]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.7.2...v0.7.3
+[0.7.2]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.7.1...v0.7.2
+[0.7.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.7.0...v0.7.1
+[0.7.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.6.1...v0.7.0
+[0.6.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.6.0...v0.6.1
+[0.6.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.5.2...v0.6.0
+[0.5.2]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.5.1...v0.5.2
+[0.5.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.5.0...v0.5.1
+[0.5.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.4.0...v0.5.0
+[0.4.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.9...v0.4.0
+[0.3.9]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.8...v0.3.9
+[0.3.8]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.7...v0.3.8
+[0.3.7]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.6...v0.3.7
+[0.3.6]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.5...v0.3.6
+[0.3.5]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.4...v0.3.5
+[0.3.4]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.3...v0.3.4
+[0.3.3]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.2...v0.3.3
+[0.3.2]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.1...v0.3.2
+[0.3.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.0...v0.3.1
+[0.3.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.0-beta2...v0.3.0
+[0.3.0-beta2]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.0-beta1...v0.3.0-beta2
+[0.3.0-beta1]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.2.6...v0.3.0-beta1
+[0.2.6]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.2.5...v0.2.6
+[0.2.5]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.2.4...v0.2.5
+[0.2.4]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.2.3...v0.2.4
+[0.2.3]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.2.2...v0.2.3
+[0.2.2]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.2.1...v0.2.2
+[0.2.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.2.0...v0.2.1
+[0.2.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.1.4.1...v0.2.0
+[0.1.4.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.1.3...v0.1.4.1
+[0.1.3]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.1.2...v0.1.3
+[0.1.2]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.1.1...v0.1.2
+[0.1.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.1.0...v0.1.1
+[0.1.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.0.4...v0.1.0
+[0.0.4]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.0.3...v0.0.4
+[0.0.3]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.0.2...v0.0.3
+[0.0.2]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.0.1...v0.0.2
diff --git a/view/js/jquery-textcomplete/LICENSE b/view/js/jquery-textcomplete/LICENSE
new file mode 100644 (file)
index 0000000..4848bd6
--- /dev/null
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2013-2014 Yuku Takahashi
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/view/js/jquery-textcomplete/README.md b/view/js/jquery-textcomplete/README.md
new file mode 100644 (file)
index 0000000..d74dfbd
--- /dev/null
@@ -0,0 +1,46 @@
+# Autocomplete for Textarea
+
+[![npm version](https://badge.fury.io/js/jquery-textcomplete.svg)](http://badge.fury.io/js/jquery-textcomplete)
+[![Bower version](https://badge.fury.io/bo/jquery-textcomplete.svg)](http://badge.fury.io/bo/jquery-textcomplete)
+[![Analytics](https://ga-beacon.appspot.com/UA-4932407-14/jquery-textcomplete/readme)](https://github.com/igrigorik/ga-beacon)
+
+Introduces autocompleting power to textareas, like a GitHub comment form has.
+
+![Demo](http://yuku-t.com/jquery-textcomplete/media/images/demo.gif)
+
+[Demo](http://yuku-t.com/jquery-textcomplete/).
+
+## Synopsis
+
+```js
+$('textarea').textcomplete([{
+    match: /(^|\b)(\w{2,})$/,
+    search: function (term, callback) {
+        var words = ['google', 'facebook', 'github', 'microsoft', 'yahoo'];
+        callback($.map(words, function (word) {
+            return word.indexOf(term) === 0 ? word : null;
+        }));
+    },
+    replace: function (word) {
+        return word + ' ';
+    }
+}]);
+```
+
+## Dependencies
+
+- jQuery (>= 1.7.0) OR Zepto (>= 1.0)
+
+## Documents
+
+See [doc](https://github.com/yuku-t/jquery-textcomplete/tree/master/doc) dir.
+
+## License
+
+Licensed under the MIT License.
+
+## Contributors
+
+Patches and code improvements were contributed by:
+
+https://github.com/yuku-t/jquery-textcomplete/graphs/contributors
diff --git a/view/js/jquery-textcomplete/jquery.textcomplete.css b/view/js/jquery-textcomplete/jquery.textcomplete.css
new file mode 100644 (file)
index 0000000..37a761b
--- /dev/null
@@ -0,0 +1,33 @@
+/* Sample */
+
+.dropdown-menu {
+    border: 1px solid #ddd;
+    background-color: white;
+}
+
+.dropdown-menu li {
+    border-top: 1px solid #ddd;
+    padding: 2px 5px;
+}
+
+.dropdown-menu li:first-child {
+    border-top: none;
+}
+
+.dropdown-menu li:hover,
+.dropdown-menu .active {
+    background-color: rgb(110, 183, 219);
+}
+
+
+/* SHOULD not modify */
+
+.dropdown-menu {
+    list-style: none;
+    padding: 0;
+    margin: 0;
+}
+
+.dropdown-menu a:hover {
+    cursor: pointer;
+}
diff --git a/view/js/jquery-textcomplete/jquery.textcomplete.js b/view/js/jquery-textcomplete/jquery.textcomplete.js
new file mode 100644 (file)
index 0000000..95e7514
--- /dev/null
@@ -0,0 +1,1401 @@
+(function (factory) {
+  if (typeof define === 'function' && define.amd) {
+    // AMD. Register as an anonymous module.
+    define(['jquery'], factory);
+  } else if (typeof module === "object" && module.exports) {
+    var $ = require('jquery');
+    module.exports = factory($);
+  } else {
+    // Browser globals
+    factory(jQuery);
+  }
+}(function (jQuery) {
+
+/*!
+ * jQuery.textcomplete
+ *
+ * Repository: https://github.com/yuku-t/jquery-textcomplete
+ * License:    MIT (https://github.com/yuku-t/jquery-textcomplete/blob/master/LICENSE)
+ * Author:     Yuku Takahashi
+ */
+
+if (typeof jQuery === 'undefined') {
+  throw new Error('jQuery.textcomplete requires jQuery');
+}
+
++function ($) {
+  'use strict';
+
+  var warn = function (message) {
+    if (console.warn) { console.warn(message); }
+  };
+
+  var id = 1;
+
+  $.fn.textcomplete = function (strategies, option) {
+    var args = Array.prototype.slice.call(arguments);
+    return this.each(function () {
+      var self = this;
+      var $this = $(this);
+      var completer = $this.data('textComplete');
+      if (!completer) {
+        option || (option = {});
+        option._oid = id++;  // unique object id
+        completer = new $.fn.textcomplete.Completer(this, option);
+        $this.data('textComplete', completer);
+      }
+      if (typeof strategies === 'string') {
+        if (!completer) return;
+        args.shift()
+        completer[strategies].apply(completer, args);
+        if (strategies === 'destroy') {
+          $this.removeData('textComplete');
+        }
+      } else {
+        // For backward compatibility.
+        // TODO: Remove at v0.4
+        $.each(strategies, function (obj) {
+          $.each(['header', 'footer', 'placement', 'maxCount'], function (name) {
+            if (obj[name]) {
+              completer.option[name] = obj[name];
+              warn(name + 'as a strategy param is deprecated. Use option.');
+              delete obj[name];
+            }
+          });
+        });
+        completer.register($.fn.textcomplete.Strategy.parse(strategies, {
+          el: self,
+          $el: $this
+        }));
+      }
+    });
+  };
+
+}(jQuery);
+
++function ($) {
+  'use strict';
+
+  // Exclusive execution control utility.
+  //
+  // func - The function to be locked. It is executed with a function named
+  //        `free` as the first argument. Once it is called, additional
+  //        execution are ignored until the free is invoked. Then the last
+  //        ignored execution will be replayed immediately.
+  //
+  // Examples
+  //
+  //   var lockedFunc = lock(function (free) {
+  //     setTimeout(function { free(); }, 1000); // It will be free in 1 sec.
+  //     console.log('Hello, world');
+  //   });
+  //   lockedFunc();  // => 'Hello, world'
+  //   lockedFunc();  // none
+  //   lockedFunc();  // none
+  //   // 1 sec past then
+  //   // => 'Hello, world'
+  //   lockedFunc();  // => 'Hello, world'
+  //   lockedFunc();  // none
+  //
+  // Returns a wrapped function.
+  var lock = function (func) {
+    var locked, queuedArgsToReplay;
+
+    return function () {
+      // Convert arguments into a real array.
+      var args = Array.prototype.slice.call(arguments);
+      if (locked) {
+        // Keep a copy of this argument list to replay later.
+        // OK to overwrite a previous value because we only replay
+        // the last one.
+        queuedArgsToReplay = args;
+        return;
+      }
+      locked = true;
+      var self = this;
+      args.unshift(function replayOrFree() {
+        if (queuedArgsToReplay) {
+          // Other request(s) arrived while we were locked.
+          // Now that the lock is becoming available, replay
+          // the latest such request, then call back here to
+          // unlock (or replay another request that arrived
+          // while this one was in flight).
+          var replayArgs = queuedArgsToReplay;
+          queuedArgsToReplay = undefined;
+          replayArgs.unshift(replayOrFree);
+          func.apply(self, replayArgs);
+        } else {
+          locked = false;
+        }
+      });
+      func.apply(this, args);
+    };
+  };
+
+  var isString = function (obj) {
+    return Object.prototype.toString.call(obj) === '[object String]';
+  };
+
+  var isFunction = function (obj) {
+    return Object.prototype.toString.call(obj) === '[object Function]';
+  };
+
+  var uniqueId = 0;
+
+  function Completer(element, option) {
+    this.$el        = $(element);
+    this.id         = 'textcomplete' + uniqueId++;
+    this.strategies = [];
+    this.views      = [];
+    this.option     = $.extend({}, Completer._getDefaults(), option);
+
+    if (!this.$el.is('input[type=text]') && !this.$el.is('input[type=search]') && !this.$el.is('textarea') && !element.isContentEditable && element.contentEditable != 'true') {
+      throw new Error('textcomplete must be called on a Textarea or a ContentEditable.');
+    }
+
+    if (element === document.activeElement) {
+      // element has already been focused. Initialize view objects immediately.
+      this.initialize()
+    } else {
+      // Initialize view objects lazily.
+      var self = this;
+      this.$el.one('focus.' + this.id, function () { self.initialize(); });
+    }
+  }
+
+  Completer._getDefaults = function () {
+    if (!Completer.DEFAULTS) {
+      Completer.DEFAULTS = {
+        appendTo: $('body'),
+        zIndex: '100'
+      };
+    }
+
+    return Completer.DEFAULTS;
+  }
+
+  $.extend(Completer.prototype, {
+    // Public properties
+    // -----------------
+
+    id:         null,
+    option:     null,
+    strategies: null,
+    adapter:    null,
+    dropdown:   null,
+    $el:        null,
+
+    // Public methods
+    // --------------
+
+    initialize: function () {
+      var element = this.$el.get(0);
+      // Initialize view objects.
+      this.dropdown = new $.fn.textcomplete.Dropdown(element, this, this.option);
+      var Adapter, viewName;
+      if (this.option.adapter) {
+        Adapter = this.option.adapter;
+      } else {
+        if (this.$el.is('textarea') || this.$el.is('input[type=text]') || this.$el.is('input[type=search]')) {
+          viewName = typeof element.selectionEnd === 'number' ? 'Textarea' : 'IETextarea';
+        } else {
+          viewName = 'ContentEditable';
+        }
+        Adapter = $.fn.textcomplete[viewName];
+      }
+      this.adapter = new Adapter(element, this, this.option);
+    },
+
+    destroy: function () {
+      this.$el.off('.' + this.id);
+      if (this.adapter) {
+        this.adapter.destroy();
+      }
+      if (this.dropdown) {
+        this.dropdown.destroy();
+      }
+      this.$el = this.adapter = this.dropdown = null;
+    },
+
+    deactivate: function () {
+      if (this.dropdown) {
+        this.dropdown.deactivate();
+      }
+    },
+
+    // Invoke textcomplete.
+    trigger: function (text, skipUnchangedTerm) {
+      if (!this.dropdown) { this.initialize(); }
+      text != null || (text = this.adapter.getTextFromHeadToCaret());
+      var searchQuery = this._extractSearchQuery(text);
+      if (searchQuery.length) {
+        var term = searchQuery[1];
+        // Ignore shift-key, ctrl-key and so on.
+        if (skipUnchangedTerm && this._term === term && term !== "") { return; }
+        this._term = term;
+        this._search.apply(this, searchQuery);
+      } else {
+        this._term = null;
+        this.dropdown.deactivate();
+      }
+    },
+
+    fire: function (eventName) {
+      var args = Array.prototype.slice.call(arguments, 1);
+      this.$el.trigger(eventName, args);
+      return this;
+    },
+
+    register: function (strategies) {
+      Array.prototype.push.apply(this.strategies, strategies);
+    },
+
+    // Insert the value into adapter view. It is called when the dropdown is clicked
+    // or selected.
+    //
+    // value    - The selected element of the array callbacked from search func.
+    // strategy - The Strategy object.
+    // e        - Click or keydown event object.
+    select: function (value, strategy, e) {
+      this._term = null;
+      this.adapter.select(value, strategy, e);
+      this.fire('change').fire('textComplete:select', value, strategy);
+      this.adapter.focus();
+    },
+
+    // Private properties
+    // ------------------
+
+    _clearAtNext: true,
+    _term:        null,
+
+    // Private methods
+    // ---------------
+
+    // Parse the given text and extract the first matching strategy.
+    //
+    // Returns an array including the strategy, the query term and the match
+    // object if the text matches an strategy; otherwise returns an empty array.
+    _extractSearchQuery: function (text) {
+      for (var i = 0; i < this.strategies.length; i++) {
+        var strategy = this.strategies[i];
+        var context = strategy.context(text);
+        if (context || context === '') {
+          var matchRegexp = isFunction(strategy.match) ? strategy.match(text) : strategy.match;
+          if (isString(context)) { text = context; }
+          var match = text.match(matchRegexp);
+          if (match) { return [strategy, match[strategy.index], match]; }
+        }
+      }
+      return []
+    },
+
+    // Call the search method of selected strategy..
+    _search: lock(function (free, strategy, term, match) {
+      var self = this;
+      strategy.search(term, function (data, stillSearching) {
+        if (!self.dropdown.shown) {
+          self.dropdown.activate();
+        }
+        if (self._clearAtNext) {
+          // The first callback in the current lock.
+          self.dropdown.clear();
+          self._clearAtNext = false;
+        }
+        self.dropdown.setPosition(self.adapter.getCaretPosition());
+        self.dropdown.render(self._zip(data, strategy, term));
+        if (!stillSearching) {
+          // The last callback in the current lock.
+          free();
+          self._clearAtNext = true; // Call dropdown.clear at the next time.
+        }
+      }, match);
+    }),
+
+    // Build a parameter for Dropdown#render.
+    //
+    // Examples
+    //
+    //  this._zip(['a', 'b'], 's');
+    //  //=> [{ value: 'a', strategy: 's' }, { value: 'b', strategy: 's' }]
+    _zip: function (data, strategy, term) {
+      return $.map(data, function (value) {
+        return { value: value, strategy: strategy, term: term };
+      });
+    }
+  });
+
+  $.fn.textcomplete.Completer = Completer;
+}(jQuery);
+
++function ($) {
+  'use strict';
+
+  var $window = $(window);
+
+  var include = function (zippedData, datum) {
+    var i, elem;
+    var idProperty = datum.strategy.idProperty
+    for (i = 0; i < zippedData.length; i++) {
+      elem = zippedData[i];
+      if (elem.strategy !== datum.strategy) continue;
+      if (idProperty) {
+        if (elem.value[idProperty] === datum.value[idProperty]) return true;
+      } else {
+        if (elem.value === datum.value) return true;
+      }
+    }
+    return false;
+  };
+
+  var dropdownViews = {};
+  $(document).on('click', function (e) {
+    var id = e.originalEvent && e.originalEvent.keepTextCompleteDropdown;
+    $.each(dropdownViews, function (key, view) {
+      if (key !== id) { view.deactivate(); }
+    });
+  });
+
+  var commands = {
+    SKIP_DEFAULT: 0,
+    KEY_UP: 1,
+    KEY_DOWN: 2,
+    KEY_ENTER: 3,
+    KEY_PAGEUP: 4,
+    KEY_PAGEDOWN: 5,
+    KEY_ESCAPE: 6
+  };
+
+  // Dropdown view
+  // =============
+
+  // Construct Dropdown object.
+  //
+  // element - Textarea or contenteditable element.
+  function Dropdown(element, completer, option) {
+    this.$el       = Dropdown.createElement(option);
+    this.completer = completer;
+    this.id        = completer.id + 'dropdown';
+    this._data     = []; // zipped data.
+    this.$inputEl  = $(element);
+    this.option    = option;
+
+    // Override setPosition method.
+    if (option.listPosition) { this.setPosition = option.listPosition; }
+    if (option.height) { this.$el.height(option.height); }
+    var self = this;
+    $.each(['maxCount', 'placement', 'footer', 'header', 'noResultsMessage', 'className'], function (_i, name) {
+      if (option[name] != null) { self[name] = option[name]; }
+    });
+    this._bindEvents(element);
+    dropdownViews[this.id] = this;
+  }
+
+  $.extend(Dropdown, {
+    // Class methods
+    // -------------
+
+    createElement: function (option) {
+      var $parent = option.appendTo;
+      if (!($parent instanceof $)) { $parent = $($parent); }
+      var $el = $('<ul></ul>')
+        .addClass('dropdown-menu textcomplete-dropdown')
+        .attr('id', 'textcomplete-dropdown-' + option._oid)
+        .css({
+          display: 'none',
+          left: 0,
+          position: 'absolute',
+          zIndex: option.zIndex
+        })
+        .appendTo($parent);
+      return $el;
+    }
+  });
+
+  $.extend(Dropdown.prototype, {
+    // Public properties
+    // -----------------
+
+    $el:       null,  // jQuery object of ul.dropdown-menu element.
+    $inputEl:  null,  // jQuery object of target textarea.
+    completer: null,
+    footer:    null,
+    header:    null,
+    id:        null,
+    maxCount:  10,
+    placement: '',
+    shown:     false,
+    data:      [],     // Shown zipped data.
+    className: '',
+
+    // Public methods
+    // --------------
+
+    destroy: function () {
+      // Don't remove $el because it may be shared by several textcompletes.
+      this.deactivate();
+
+      this.$el.off('.' + this.id);
+      this.$inputEl.off('.' + this.id);
+      this.clear();
+      this.$el.remove();
+      this.$el = this.$inputEl = this.completer = null;
+      delete dropdownViews[this.id]
+    },
+
+    render: function (zippedData) {
+      var contentsHtml = this._buildContents(zippedData);
+      var unzippedData = $.map(this.data, function (d) { return d.value; });
+      if (this.data.length) {
+        var strategy = zippedData[0].strategy;
+        if (strategy.id) {
+          this.$el.attr('data-strategy', strategy.id);
+        } else {
+          this.$el.removeAttr('data-strategy');
+        }
+        this._renderHeader(unzippedData);
+        this._renderFooter(unzippedData);
+        if (contentsHtml) {
+          this._renderContents(contentsHtml);
+          this._fitToBottom();
+          this._fitToRight();
+          this._activateIndexedItem();
+        }
+        this._setScroll();
+      } else if (this.noResultsMessage) {
+        this._renderNoResultsMessage(unzippedData);
+      } else if (this.shown) {
+        this.deactivate();
+      }
+    },
+
+    setPosition: function (pos) {
+      // Make the dropdown fixed if the input is also fixed
+      // This can't be done during init, as textcomplete may be used on multiple elements on the same page
+      // Because the same dropdown is reused behind the scenes, we need to recheck every time the dropdown is showed
+      var position = 'absolute';
+      // Check if input or one of its parents has positioning we need to care about
+      this.$inputEl.add(this.$inputEl.parents()).each(function() {
+        if($(this).css('position') === 'absolute') // The element has absolute positioning, so it's all OK
+          return false;
+        if($(this).css('position') === 'fixed') {
+          pos.top -= $window.scrollTop();
+          pos.left -= $window.scrollLeft();                                    
+          position = 'fixed';
+          return false;
+        }
+      });
+      this.$el.css(this._applyPlacement(pos));
+      this.$el.css({ position: position }); // Update positioning
+
+      return this;
+    },
+
+    clear: function () {
+      this.$el.html('');
+      this.data = [];
+      this._index = 0;
+      this._$header = this._$footer = this._$noResultsMessage = null;
+    },
+
+    activate: function () {
+      if (!this.shown) {
+        this.clear();
+        this.$el.show();
+        if (this.className) { this.$el.addClass(this.className); }
+        this.completer.fire('textComplete:show');
+        this.shown = true;
+      }
+      return this;
+    },
+
+    deactivate: function () {
+      if (this.shown) {
+        this.$el.hide();
+        if (this.className) { this.$el.removeClass(this.className); }
+        this.completer.fire('textComplete:hide');
+        this.shown = false;
+      }
+      return this;
+    },
+
+    isUp: function (e) {
+      return e.keyCode === 38 || (e.ctrlKey && e.keyCode === 80);  // UP, Ctrl-P
+    },
+
+    isDown: function (e) {
+      return e.keyCode === 40 || (e.ctrlKey && e.keyCode === 78);  // DOWN, Ctrl-N
+    },
+
+    isEnter: function (e) {
+      var modifiers = e.ctrlKey || e.altKey || e.metaKey || e.shiftKey;
+      return !modifiers && (e.keyCode === 13 || e.keyCode === 9 || (this.option.completeOnSpace === true && e.keyCode === 32))  // ENTER, TAB
+    },
+
+    isPageup: function (e) {
+      return e.keyCode === 33;  // PAGEUP
+    },
+
+    isPagedown: function (e) {
+      return e.keyCode === 34;  // PAGEDOWN
+    },
+
+    isEscape: function (e) {
+      return e.keyCode === 27;  // ESCAPE
+    },
+
+    // Private properties
+    // ------------------
+
+    _data:    null,  // Currently shown zipped data.
+    _index:   null,
+    _$header: null,
+    _$noResultsMessage: null,
+    _$footer: null,
+
+    // Private methods
+    // ---------------
+
+    _bindEvents: function () {
+      this.$el.on('mousedown.' + this.id, '.textcomplete-item', $.proxy(this._onClick, this));
+      this.$el.on('touchstart.' + this.id, '.textcomplete-item', $.proxy(this._onClick, this));
+      this.$el.on('mouseover.' + this.id, '.textcomplete-item', $.proxy(this._onMouseover, this));
+      this.$inputEl.on('keydown.' + this.id, $.proxy(this._onKeydown, this));
+    },
+
+    _onClick: function (e) {
+      var $el = $(e.target);
+      e.preventDefault();
+      e.originalEvent.keepTextCompleteDropdown = this.id;
+      if (!$el.hasClass('textcomplete-item')) {
+        $el = $el.closest('.textcomplete-item');
+      }
+      var datum = this.data[parseInt($el.data('index'), 10)];
+      this.completer.select(datum.value, datum.strategy, e);
+      var self = this;
+      // Deactive at next tick to allow other event handlers to know whether
+      // the dropdown has been shown or not.
+      setTimeout(function () {
+        self.deactivate();
+        if (e.type === 'touchstart') {
+          self.$inputEl.focus();
+        }
+      }, 0);
+    },
+
+    // Activate hovered item.
+    _onMouseover: function (e) {
+      var $el = $(e.target);
+      e.preventDefault();
+      if (!$el.hasClass('textcomplete-item')) {
+        $el = $el.closest('.textcomplete-item');
+      }
+      this._index = parseInt($el.data('index'), 10);
+      this._activateIndexedItem();
+    },
+
+    _onKeydown: function (e) {
+      if (!this.shown) { return; }
+
+      var command;
+
+      if ($.isFunction(this.option.onKeydown)) {
+        command = this.option.onKeydown(e, commands);
+      }
+
+      if (command == null) {
+        command = this._defaultKeydown(e);
+      }
+
+      switch (command) {
+        case commands.KEY_UP:
+          e.preventDefault();
+          this._up();
+          break;
+        case commands.KEY_DOWN:
+          e.preventDefault();
+          this._down();
+          break;
+        case commands.KEY_ENTER:
+          e.preventDefault();
+          this._enter(e);
+          break;
+        case commands.KEY_PAGEUP:
+          e.preventDefault();
+          this._pageup();
+          break;
+        case commands.KEY_PAGEDOWN:
+          e.preventDefault();
+          this._pagedown();
+          break;
+        case commands.KEY_ESCAPE:
+          e.preventDefault();
+          this.deactivate();
+          break;
+      }
+    },
+
+    _defaultKeydown: function (e) {
+      if (this.isUp(e)) {
+        return commands.KEY_UP;
+      } else if (this.isDown(e)) {
+        return commands.KEY_DOWN;
+      } else if (this.isEnter(e)) {
+        return commands.KEY_ENTER;
+      } else if (this.isPageup(e)) {
+        return commands.KEY_PAGEUP;
+      } else if (this.isPagedown(e)) {
+        return commands.KEY_PAGEDOWN;
+      } else if (this.isEscape(e)) {
+        return commands.KEY_ESCAPE;
+      }
+    },
+
+    _up: function () {
+      if (this._index === 0) {
+        this._index = this.data.length - 1;
+      } else {
+        this._index -= 1;
+      }
+      this._activateIndexedItem();
+      this._setScroll();
+    },
+
+    _down: function () {
+      if (this._index === this.data.length - 1) {
+        this._index = 0;
+      } else {
+        this._index += 1;
+      }
+      this._activateIndexedItem();
+      this._setScroll();
+    },
+
+    _enter: function (e) {
+      var datum = this.data[parseInt(this._getActiveElement().data('index'), 10)];
+      this.completer.select(datum.value, datum.strategy, e);
+      this.deactivate();
+    },
+
+    _pageup: function () {
+      var target = 0;
+      var threshold = this._getActiveElement().position().top - this.$el.innerHeight();
+      this.$el.children().each(function (i) {
+        if ($(this).position().top + $(this).outerHeight() > threshold) {
+          target = i;
+          return false;
+        }
+      });
+      this._index = target;
+      this._activateIndexedItem();
+      this._setScroll();
+    },
+
+    _pagedown: function () {
+      var target = this.data.length - 1;
+      var threshold = this._getActiveElement().position().top + this.$el.innerHeight();
+      this.$el.children().each(function (i) {
+        if ($(this).position().top > threshold) {
+          target = i;
+          return false
+        }
+      });
+      this._index = target;
+      this._activateIndexedItem();
+      this._setScroll();
+    },
+
+    _activateIndexedItem: function () {
+      this.$el.find('.textcomplete-item.active').removeClass('active');
+      this._getActiveElement().addClass('active');
+    },
+
+    _getActiveElement: function () {
+      return this.$el.children('.textcomplete-item:nth(' + this._index + ')');
+    },
+
+    _setScroll: function () {
+      var $activeEl = this._getActiveElement();
+      var itemTop = $activeEl.position().top;
+      var itemHeight = $activeEl.outerHeight();
+      var visibleHeight = this.$el.innerHeight();
+      var visibleTop = this.$el.scrollTop();
+      if (this._index === 0 || this._index == this.data.length - 1 || itemTop < 0) {
+        this.$el.scrollTop(itemTop + visibleTop);
+      } else if (itemTop + itemHeight > visibleHeight) {
+        this.$el.scrollTop(itemTop + itemHeight + visibleTop - visibleHeight);
+      }
+    },
+
+    _buildContents: function (zippedData) {
+      var datum, i, index;
+      var html = '';
+      for (i = 0; i < zippedData.length; i++) {
+        if (this.data.length === this.maxCount) break;
+        datum = zippedData[i];
+        if (include(this.data, datum)) { continue; }
+        index = this.data.length;
+        this.data.push(datum);
+        html += '<li class="textcomplete-item" data-index="' + index + '"><a>';
+        html +=   datum.strategy.template(datum.value, datum.term);
+        html += '</a></li>';
+      }
+      return html;
+    },
+
+    _renderHeader: function (unzippedData) {
+      if (this.header) {
+        if (!this._$header) {
+          this._$header = $('<li class="textcomplete-header"></li>').prependTo(this.$el);
+        }
+        var html = $.isFunction(this.header) ? this.header(unzippedData) : this.header;
+        this._$header.html(html);
+      }
+    },
+
+    _renderFooter: function (unzippedData) {
+      if (this.footer) {
+        if (!this._$footer) {
+          this._$footer = $('<li class="textcomplete-footer"></li>').appendTo(this.$el);
+        }
+        var html = $.isFunction(this.footer) ? this.footer(unzippedData) : this.footer;
+        this._$footer.html(html);
+      }
+    },
+
+    _renderNoResultsMessage: function (unzippedData) {
+      if (this.noResultsMessage) {
+        if (!this._$noResultsMessage) {
+          this._$noResultsMessage = $('<li class="textcomplete-no-results-message"></li>').appendTo(this.$el);
+        }
+        var html = $.isFunction(this.noResultsMessage) ? this.noResultsMessage(unzippedData) : this.noResultsMessage;
+        this._$noResultsMessage.html(html);
+      }
+    },
+
+    _renderContents: function (html) {
+      if (this._$footer) {
+        this._$footer.before(html);
+      } else {
+        this.$el.append(html);
+      }
+    },
+
+    _fitToBottom: function() {
+      var windowScrollBottom = $window.scrollTop() + $window.height();
+      var height = this.$el.height();
+      if ((this.$el.position().top + height) > windowScrollBottom) {
+        this.$el.offset({top: windowScrollBottom - height});
+      }
+    },
+
+    _fitToRight: function() {
+      // We don't know how wide our content is until the browser positions us, and at that point it clips us
+      // to the document width so we don't know if we would have overrun it. As a heuristic to avoid that clipping
+      // (which makes our elements wrap onto the next line and corrupt the next item), if we're close to the right
+      // edge, move left. We don't know how far to move left, so just keep nudging a bit.
+      var tolerance = 30; // pixels. Make wider than vertical scrollbar because we might not be able to use that space.
+      var lastOffset = this.$el.offset().left, offset;
+      var width = this.$el.width();
+      var maxLeft = $window.width() - tolerance;
+      while (lastOffset + width > maxLeft) {
+        this.$el.offset({left: lastOffset - tolerance});
+        offset = this.$el.offset().left;
+        if (offset >= lastOffset) { break; }
+        lastOffset = offset;
+      }
+    },
+
+    _applyPlacement: function (position) {
+      // If the 'placement' option set to 'top', move the position above the element.
+      if (this.placement.indexOf('top') !== -1) {
+        // Overwrite the position object to set the 'bottom' property instead of the top.
+        position = {
+          top: 'auto',
+          bottom: this.$el.parent().height() - position.top + position.lineHeight,
+          left: position.left
+        };
+      } else {
+        position.bottom = 'auto';
+        delete position.lineHeight;
+      }
+      if (this.placement.indexOf('absleft') !== -1) {
+        position.left = 0;
+      } else if (this.placement.indexOf('absright') !== -1) {
+        position.right = 0;
+        position.left = 'auto';
+      }
+      return position;
+    }
+  });
+
+  $.fn.textcomplete.Dropdown = Dropdown;
+  $.extend($.fn.textcomplete, commands);
+}(jQuery);
+
++function ($) {
+  'use strict';
+
+  // Memoize a search function.
+  var memoize = function (func) {
+    var memo = {};
+    return function (term, callback) {
+      if (memo[term]) {
+        callback(memo[term]);
+      } else {
+        func.call(this, term, function (data) {
+          memo[term] = (memo[term] || []).concat(data);
+          callback.apply(null, arguments);
+        });
+      }
+    };
+  };
+
+  function Strategy(options) {
+    $.extend(this, options);
+    if (this.cache) { this.search = memoize(this.search); }
+  }
+
+  Strategy.parse = function (strategiesArray, params) {
+    return $.map(strategiesArray, function (strategy) {
+      var strategyObj = new Strategy(strategy);
+      strategyObj.el = params.el;
+      strategyObj.$el = params.$el;
+      return strategyObj;
+    });
+  };
+
+  $.extend(Strategy.prototype, {
+    // Public properties
+    // -----------------
+
+    // Required
+    match:      null,
+    replace:    null,
+    search:     null,
+
+    // Optional
+    id:         null,
+    cache:      false,
+    context:    function () { return true; },
+    index:      2,
+    template:   function (obj) { return obj; },
+    idProperty: null
+  });
+
+  $.fn.textcomplete.Strategy = Strategy;
+
+}(jQuery);
+
++function ($) {
+  'use strict';
+
+  var now = Date.now || function () { return new Date().getTime(); };
+
+  // Returns a function, that, as long as it continues to be invoked, will not
+  // be triggered. The function will be called after it stops being called for
+  // `wait` msec.
+  //
+  // This utility function was originally implemented at Underscore.js.
+  var debounce = function (func, wait) {
+    var timeout, args, context, timestamp, result;
+    var later = function () {
+      var last = now() - timestamp;
+      if (last < wait) {
+        timeout = setTimeout(later, wait - last);
+      } else {
+        timeout = null;
+        result = func.apply(context, args);
+        context = args = null;
+      }
+    };
+
+    return function () {
+      context = this;
+      args = arguments;
+      timestamp = now();
+      if (!timeout) {
+        timeout = setTimeout(later, wait);
+      }
+      return result;
+    };
+  };
+
+  function Adapter () {}
+
+  $.extend(Adapter.prototype, {
+    // Public properties
+    // -----------------
+
+    id:        null, // Identity.
+    completer: null, // Completer object which creates it.
+    el:        null, // Textarea element.
+    $el:       null, // jQuery object of the textarea.
+    option:    null,
+
+    // Public methods
+    // --------------
+
+    initialize: function (element, completer, option) {
+      this.el        = element;
+      this.$el       = $(element);
+      this.id        = completer.id + this.constructor.name;
+      this.completer = completer;
+      this.option    = option;
+
+      if (this.option.debounce) {
+        this._onKeyup = debounce(this._onKeyup, this.option.debounce);
+      }
+
+      this._bindEvents();
+    },
+
+    destroy: function () {
+      this.$el.off('.' + this.id); // Remove all event handlers.
+      this.$el = this.el = this.completer = null;
+    },
+
+    // Update the element with the given value and strategy.
+    //
+    // value    - The selected object. It is one of the item of the array
+    //            which was callbacked from the search function.
+    // strategy - The Strategy associated with the selected value.
+    select: function (/* value, strategy */) {
+      throw new Error('Not implemented');
+    },
+
+    // Returns the caret's relative coordinates from body's left top corner.
+    getCaretPosition: function () {
+      var position = this._getCaretRelativePosition();
+      var offset = this.$el.offset();
+
+      // Calculate the left top corner of `this.option.appendTo` element.
+      var $parent = this.option.appendTo;
+      if ($parent) {
+         if (!($parent instanceof $)) { $parent = $($parent); }
+         var parentOffset = $parent.offsetParent().offset();
+         offset.top -= parentOffset.top;
+         offset.left -= parentOffset.left;
+      }
+
+      position.top += offset.top;
+      position.left += offset.left;
+      return position;
+    },
+
+    // Focus on the element.
+    focus: function () {
+      this.$el.focus();
+    },
+
+    // Private methods
+    // ---------------
+
+    _bindEvents: function () {
+      this.$el.on('keyup.' + this.id, $.proxy(this._onKeyup, this));
+    },
+
+    _onKeyup: function (e) {
+      if (this._skipSearch(e)) { return; }
+      this.completer.trigger(this.getTextFromHeadToCaret(), true);
+    },
+
+    // Suppress searching if it returns true.
+    _skipSearch: function (clickEvent) {
+      switch (clickEvent.keyCode) {
+        case 9:  // TAB
+        case 13: // ENTER
+        case 40: // DOWN
+        case 38: // UP
+          return true;
+      }
+      if (clickEvent.ctrlKey) switch (clickEvent.keyCode) {
+        case 78: // Ctrl-N
+        case 80: // Ctrl-P
+          return true;
+      }
+    }
+  });
+
+  $.fn.textcomplete.Adapter = Adapter;
+}(jQuery);
+
++function ($) {
+  'use strict';
+
+  // Textarea adapter
+  // ================
+  //
+  // Managing a textarea. It doesn't know a Dropdown.
+  function Textarea(element, completer, option) {
+    this.initialize(element, completer, option);
+  }
+
+  $.extend(Textarea.prototype, $.fn.textcomplete.Adapter.prototype, {
+    // Public methods
+    // --------------
+
+    // Update the textarea with the given value and strategy.
+    select: function (value, strategy, e) {
+      var pre = this.getTextFromHeadToCaret();
+      var post = this.el.value.substring(this.el.selectionEnd);
+      var newSubstr = strategy.replace(value, e);
+      if (typeof newSubstr !== 'undefined') {
+        if ($.isArray(newSubstr)) {
+          post = newSubstr[1] + post;
+          newSubstr = newSubstr[0];
+        }
+        pre = pre.replace(strategy.match, newSubstr);
+        this.$el.val(pre + post);
+        this.el.selectionStart = this.el.selectionEnd = pre.length;
+      }
+    },
+
+    getTextFromHeadToCaret: function () {
+      return this.el.value.substring(0, this.el.selectionEnd);
+    },
+
+    // Private methods
+    // ---------------
+
+    _getCaretRelativePosition: function () {
+      var p = $.fn.textcomplete.getCaretCoordinates(this.el, this.el.selectionStart);
+      return {
+        top: p.top + this._calculateLineHeight() - this.$el.scrollTop(),
+        left: p.left - this.$el.scrollLeft()
+      };
+    },
+
+    _calculateLineHeight: function () {
+      var lineHeight = parseInt(this.$el.css('line-height'), 10);
+      if (isNaN(lineHeight)) {
+        // http://stackoverflow.com/a/4515470/1297336
+        var parentNode = this.el.parentNode;
+        var temp = document.createElement(this.el.nodeName);
+        var style = this.el.style;
+        temp.setAttribute(
+          'style',
+          'margin:0px;padding:0px;font-family:' + style.fontFamily + ';font-size:' + style.fontSize
+        );
+        temp.innerHTML = 'test';
+        parentNode.appendChild(temp);
+        lineHeight = temp.clientHeight;
+        parentNode.removeChild(temp);
+      }
+      return lineHeight;
+    }
+  });
+
+  $.fn.textcomplete.Textarea = Textarea;
+}(jQuery);
+
++function ($) {
+  'use strict';
+
+  var sentinelChar = '吶';
+
+  function IETextarea(element, completer, option) {
+    this.initialize(element, completer, option);
+    $('<span>' + sentinelChar + '</span>').css({
+      position: 'absolute',
+      top: -9999,
+      left: -9999
+    }).insertBefore(element);
+  }
+
+  $.extend(IETextarea.prototype, $.fn.textcomplete.Textarea.prototype, {
+    // Public methods
+    // --------------
+
+    select: function (value, strategy, e) {
+      var pre = this.getTextFromHeadToCaret();
+      var post = this.el.value.substring(pre.length);
+      var newSubstr = strategy.replace(value, e);
+      if (typeof newSubstr !== 'undefined') {
+        if ($.isArray(newSubstr)) {
+          post = newSubstr[1] + post;
+          newSubstr = newSubstr[0];
+        }
+        pre = pre.replace(strategy.match, newSubstr);
+        this.$el.val(pre + post);
+        this.el.focus();
+        var range = this.el.createTextRange();
+        range.collapse(true);
+        range.moveEnd('character', pre.length);
+        range.moveStart('character', pre.length);
+        range.select();
+      }
+    },
+
+    getTextFromHeadToCaret: function () {
+      this.el.focus();
+      var range = document.selection.createRange();
+      range.moveStart('character', -this.el.value.length);
+      var arr = range.text.split(sentinelChar)
+      return arr.length === 1 ? arr[0] : arr[1];
+    }
+  });
+
+  $.fn.textcomplete.IETextarea = IETextarea;
+}(jQuery);
+
+// NOTE: TextComplete plugin has contenteditable support but it does not work
+//       fine especially on old IEs.
+//       Any pull requests are REALLY welcome.
+
++function ($) {
+  'use strict';
+
+  // ContentEditable adapter
+  // =======================
+  //
+  // Adapter for contenteditable elements.
+  function ContentEditable (element, completer, option) {
+    this.initialize(element, completer, option);
+  }
+
+  $.extend(ContentEditable.prototype, $.fn.textcomplete.Adapter.prototype, {
+    // Public methods
+    // --------------
+
+    // Update the content with the given value and strategy.
+    // When an dropdown item is selected, it is executed.
+    select: function (value, strategy, e) {
+      var pre = this.getTextFromHeadToCaret();
+      var sel = window.getSelection()
+      var range = sel.getRangeAt(0);
+      var selection = range.cloneRange();
+      selection.selectNodeContents(range.startContainer);
+      var content = selection.toString();
+      var post = content.substring(range.startOffset);
+      var newSubstr = strategy.replace(value, e);
+      if (typeof newSubstr !== 'undefined') {
+        if ($.isArray(newSubstr)) {
+          post = newSubstr[1] + post;
+          newSubstr = newSubstr[0];
+        }
+        pre = pre.replace(strategy.match, newSubstr);
+        range.selectNodeContents(range.startContainer);
+        range.deleteContents();
+        
+        // create temporary elements
+        var preWrapper = document.createElement("div");
+        preWrapper.innerHTML = pre;
+        var postWrapper = document.createElement("div");
+        postWrapper.innerHTML = post;
+        
+        // create the fragment thats inserted
+        var fragment = document.createDocumentFragment();
+        var childNode;
+        var lastOfPre;
+        while (childNode = preWrapper.firstChild) {
+               lastOfPre = fragment.appendChild(childNode);
+        }
+        while (childNode = postWrapper.firstChild) {
+               fragment.appendChild(childNode);
+        }
+        
+        // insert the fragment & jump behind the last node in "pre"
+        range.insertNode(fragment);
+        range.setStartAfter(lastOfPre);
+        
+        range.collapse(true);
+        sel.removeAllRanges();
+        sel.addRange(range);
+      }
+    },
+
+    // Private methods
+    // ---------------
+
+    // Returns the caret's relative position from the contenteditable's
+    // left top corner.
+    //
+    // Examples
+    //
+    //   this._getCaretRelativePosition()
+    //   //=> { top: 18, left: 200, lineHeight: 16 }
+    //
+    // Dropdown's position will be decided using the result.
+    _getCaretRelativePosition: function () {
+      var range = window.getSelection().getRangeAt(0).cloneRange();
+      var node = document.createElement('span');
+      range.insertNode(node);
+      range.selectNodeContents(node);
+      range.deleteContents();
+      var $node = $(node);
+      var position = $node.offset();
+      position.left -= this.$el.offset().left;
+      position.top += $node.height() - this.$el.offset().top;
+      position.lineHeight = $node.height();
+      $node.remove();
+      return position;
+    },
+
+    // Returns the string between the first character and the caret.
+    // Completer will be triggered with the result for start autocompleting.
+    //
+    // Example
+    //
+    //   // Suppose the html is '<b>hello</b> wor|ld' and | is the caret.
+    //   this.getTextFromHeadToCaret()
+    //   // => ' wor'  // not '<b>hello</b> wor'
+    getTextFromHeadToCaret: function () {
+      var range = window.getSelection().getRangeAt(0);
+      var selection = range.cloneRange();
+      selection.selectNodeContents(range.startContainer);
+      return selection.toString().substring(0, range.startOffset);
+    }
+  });
+
+  $.fn.textcomplete.ContentEditable = ContentEditable;
+}(jQuery);
+
+// The MIT License (MIT)
+// 
+// Copyright (c) 2015 Jonathan Ong me@jongleberry.com
+// 
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
+// associated documentation files (the "Software"), to deal in the Software without restriction,
+// including without limitation the rights to use, copy, modify, merge, publish, distribute,
+// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
+// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// https://github.com/component/textarea-caret-position
+
+(function ($) {
+
+// The properties that we copy into a mirrored div.
+// Note that some browsers, such as Firefox,
+// do not concatenate properties, i.e. padding-top, bottom etc. -> padding,
+// so we have to do every single property specifically.
+var properties = [
+  'direction',  // RTL support
+  'boxSizing',
+  'width',  // on Chrome and IE, exclude the scrollbar, so the mirror div wraps exactly as the textarea does
+  'height',
+  'overflowX',
+  'overflowY',  // copy the scrollbar for IE
+
+  'borderTopWidth',
+  'borderRightWidth',
+  'borderBottomWidth',
+  'borderLeftWidth',
+  'borderStyle',
+
+  'paddingTop',
+  'paddingRight',
+  'paddingBottom',
+  'paddingLeft',
+
+  // https://developer.mozilla.org/en-US/docs/Web/CSS/font
+  'fontStyle',
+  'fontVariant',
+  'fontWeight',
+  'fontStretch',
+  'fontSize',
+  'fontSizeAdjust',
+  'lineHeight',
+  'fontFamily',
+
+  'textAlign',
+  'textTransform',
+  'textIndent',
+  'textDecoration',  // might not make a difference, but better be safe
+
+  'letterSpacing',
+  'wordSpacing',
+
+  'tabSize',
+  'MozTabSize'
+
+];
+
+var isBrowser = (typeof window !== 'undefined');
+var isFirefox = (isBrowser && window.mozInnerScreenX != null);
+
+function getCaretCoordinates(element, position, options) {
+  if(!isBrowser) {
+    throw new Error('textarea-caret-position#getCaretCoordinates should only be called in a browser');
+  }
+
+  var debug = options && options.debug || false;
+  if (debug) {
+    var el = document.querySelector('#input-textarea-caret-position-mirror-div');
+    if ( el ) { el.parentNode.removeChild(el); }
+  }
+
+  // mirrored div
+  var div = document.createElement('div');
+  div.id = 'input-textarea-caret-position-mirror-div';
+  document.body.appendChild(div);
+
+  var style = div.style;
+  var computed = window.getComputedStyle? getComputedStyle(element) : element.currentStyle;  // currentStyle for IE < 9
+
+  // default textarea styles
+  style.whiteSpace = 'pre-wrap';
+  if (element.nodeName !== 'INPUT')
+    style.wordWrap = 'break-word';  // only for textarea-s
+
+  // position off-screen
+  style.position = 'absolute';  // required to return coordinates properly
+  if (!debug)
+    style.visibility = 'hidden';  // not 'display: none' because we want rendering
+
+  // transfer the element's properties to the div
+  properties.forEach(function (prop) {
+    style[prop] = computed[prop];
+  });
+
+  if (isFirefox) {
+    // Firefox lies about the overflow property for textareas: https://bugzilla.mozilla.org/show_bug.cgi?id=984275
+    if (element.scrollHeight > parseInt(computed.height))
+      style.overflowY = 'scroll';
+  } else {
+    style.overflow = 'hidden';  // for Chrome to not render a scrollbar; IE keeps overflowY = 'scroll'
+  }
+
+  div.textContent = element.value.substring(0, position);
+  // the second special handling for input type="text" vs textarea: spaces need to be replaced with non-breaking spaces - http://stackoverflow.com/a/13402035/1269037
+  if (element.nodeName === 'INPUT')
+    div.textContent = div.textContent.replace(/\s/g, '\u00a0');
+
+  var span = document.createElement('span');
+  // Wrapping must be replicated *exactly*, including when a long word gets
+  // onto the next line, with whitespace at the end of the line before (#7).
+  // The  *only* reliable way to do that is to copy the *entire* rest of the
+  // textarea's content into the <span> created at the caret position.
+  // for inputs, just '.' would be enough, but why bother?
+  span.textContent = element.value.substring(position) || '.';  // || because a completely empty faux span doesn't render at all
+  div.appendChild(span);
+
+  var coordinates = {
+    top: span.offsetTop + parseInt(computed['borderTopWidth']),
+    left: span.offsetLeft + parseInt(computed['borderLeftWidth'])
+  };
+
+  if (debug) {
+    span.style.backgroundColor = '#aaa';
+  } else {
+    document.body.removeChild(div);
+  }
+
+  return coordinates;
+}
+
+$.fn.textcomplete.getCaretCoordinates = getCaretCoordinates;
+
+}(jQuery));
+
+return jQuery;
+}));
diff --git a/view/js/jquery-textcomplete/jquery.textcomplete.min.js b/view/js/jquery-textcomplete/jquery.textcomplete.min.js
new file mode 100644 (file)
index 0000000..d3a427f
--- /dev/null
@@ -0,0 +1,3 @@
+/*! jquery-textcomplete - v1.3.4 - 2016-04-19 */
+!function(a){if("function"==typeof define&&define.amd)define(["jquery"],a);else if("object"==typeof module&&module.exports){var b=require("jquery");module.exports=a(b)}else a(jQuery)}(function(a){if("undefined"==typeof a)throw new Error("jQuery.textcomplete requires jQuery");return+function(a){"use strict";var b=function(a){console.warn&&console.warn(a)},c=1;a.fn.textcomplete=function(d,e){var f=Array.prototype.slice.call(arguments);return this.each(function(){var g=this,h=a(this),i=h.data("textComplete");if(i||(e||(e={}),e._oid=c++,i=new a.fn.textcomplete.Completer(this,e),h.data("textComplete",i)),"string"==typeof d){if(!i)return;f.shift(),i[d].apply(i,f),"destroy"===d&&h.removeData("textComplete")}else a.each(d,function(c){a.each(["header","footer","placement","maxCount"],function(a){c[a]&&(i.option[a]=c[a],b(a+"as a strategy param is deprecated. Use option."),delete c[a])})}),i.register(a.fn.textcomplete.Strategy.parse(d,{el:g,$el:h}))})}}(a),+function(a){"use strict";function b(c,d){if(this.$el=a(c),this.id="textcomplete"+f++,this.strategies=[],this.views=[],this.option=a.extend({},b._getDefaults(),d),!(this.$el.is("input[type=text]")||this.$el.is("input[type=search]")||this.$el.is("textarea")||c.isContentEditable||"true"==c.contentEditable))throw new Error("textcomplete must be called on a Textarea or a ContentEditable.");if(c===document.activeElement)this.initialize();else{var e=this;this.$el.one("focus."+this.id,function(){e.initialize()})}}var c=function(a){var b,c;return function(){var d=Array.prototype.slice.call(arguments);if(b)return void(c=d);b=!0;var e=this;d.unshift(function f(){if(c){var d=c;c=void 0,d.unshift(f),a.apply(e,d)}else b=!1}),a.apply(this,d)}},d=function(a){return"[object String]"===Object.prototype.toString.call(a)},e=function(a){return"[object Function]"===Object.prototype.toString.call(a)},f=0;b._getDefaults=function(){return b.DEFAULTS||(b.DEFAULTS={appendTo:a("body"),zIndex:"100"}),b.DEFAULTS},a.extend(b.prototype,{id:null,option:null,strategies:null,adapter:null,dropdown:null,$el:null,initialize:function(){var b=this.$el.get(0);this.dropdown=new a.fn.textcomplete.Dropdown(b,this,this.option);var c,d;this.option.adapter?c=this.option.adapter:(d=this.$el.is("textarea")||this.$el.is("input[type=text]")||this.$el.is("input[type=search]")?"number"==typeof b.selectionEnd?"Textarea":"IETextarea":"ContentEditable",c=a.fn.textcomplete[d]),this.adapter=new c(b,this,this.option)},destroy:function(){this.$el.off("."+this.id),this.adapter&&this.adapter.destroy(),this.dropdown&&this.dropdown.destroy(),this.$el=this.adapter=this.dropdown=null},deactivate:function(){this.dropdown&&this.dropdown.deactivate()},trigger:function(a,b){this.dropdown||this.initialize(),null!=a||(a=this.adapter.getTextFromHeadToCaret());var c=this._extractSearchQuery(a);if(c.length){var d=c[1];if(b&&this._term===d&&""!==d)return;this._term=d,this._search.apply(this,c)}else this._term=null,this.dropdown.deactivate()},fire:function(a){var b=Array.prototype.slice.call(arguments,1);return this.$el.trigger(a,b),this},register:function(a){Array.prototype.push.apply(this.strategies,a)},select:function(a,b,c){this._term=null,this.adapter.select(a,b,c),this.fire("change").fire("textComplete:select",a,b),this.adapter.focus()},_clearAtNext:!0,_term:null,_extractSearchQuery:function(a){for(var b=0;b<this.strategies.length;b++){var c=this.strategies[b],f=c.context(a);if(f||""===f){var g=e(c.match)?c.match(a):c.match;d(f)&&(a=f);var h=a.match(g);if(h)return[c,h[c.index],h]}}return[]},_search:c(function(a,b,c,d){var e=this;b.search(c,function(d,f){e.dropdown.shown||e.dropdown.activate(),e._clearAtNext&&(e.dropdown.clear(),e._clearAtNext=!1),e.dropdown.setPosition(e.adapter.getCaretPosition()),e.dropdown.render(e._zip(d,b,c)),f||(a(),e._clearAtNext=!0)},d)}),_zip:function(b,c,d){return a.map(b,function(a){return{value:a,strategy:c,term:d}})}}),a.fn.textcomplete.Completer=b}(a),+function(a){"use strict";function b(c,d,f){this.$el=b.createElement(f),this.completer=d,this.id=d.id+"dropdown",this._data=[],this.$inputEl=a(c),this.option=f,f.listPosition&&(this.setPosition=f.listPosition),f.height&&this.$el.height(f.height);var g=this;a.each(["maxCount","placement","footer","header","noResultsMessage","className"],function(a,b){null!=f[b]&&(g[b]=f[b])}),this._bindEvents(c),e[this.id]=this}var c=a(window),d=function(a,b){var c,d,e=b.strategy.idProperty;for(c=0;c<a.length;c++)if(d=a[c],d.strategy===b.strategy)if(e){if(d.value[e]===b.value[e])return!0}else if(d.value===b.value)return!0;return!1},e={};a(document).on("click",function(b){var c=b.originalEvent&&b.originalEvent.keepTextCompleteDropdown;a.each(e,function(a,b){a!==c&&b.deactivate()})});var f={SKIP_DEFAULT:0,KEY_UP:1,KEY_DOWN:2,KEY_ENTER:3,KEY_PAGEUP:4,KEY_PAGEDOWN:5,KEY_ESCAPE:6};a.extend(b,{createElement:function(b){var c=b.appendTo;c instanceof a||(c=a(c));var d=a("<ul></ul>").addClass("dropdown-menu textcomplete-dropdown").attr("id","textcomplete-dropdown-"+b._oid).css({display:"none",left:0,position:"absolute",zIndex:b.zIndex}).appendTo(c);return d}}),a.extend(b.prototype,{$el:null,$inputEl:null,completer:null,footer:null,header:null,id:null,maxCount:10,placement:"",shown:!1,data:[],className:"",destroy:function(){this.deactivate(),this.$el.off("."+this.id),this.$inputEl.off("."+this.id),this.clear(),this.$el.remove(),this.$el=this.$inputEl=this.completer=null,delete e[this.id]},render:function(b){var c=this._buildContents(b),d=a.map(this.data,function(a){return a.value});if(this.data.length){var e=b[0].strategy;e.id?this.$el.attr("data-strategy",e.id):this.$el.removeAttr("data-strategy"),this._renderHeader(d),this._renderFooter(d),c&&(this._renderContents(c),this._fitToBottom(),this._fitToRight(),this._activateIndexedItem()),this._setScroll()}else this.noResultsMessage?this._renderNoResultsMessage(d):this.shown&&this.deactivate()},setPosition:function(b){var d="absolute";return this.$inputEl.add(this.$inputEl.parents()).each(function(){return"absolute"===a(this).css("position")?!1:"fixed"===a(this).css("position")?(b.top-=c.scrollTop(),b.left-=c.scrollLeft(),d="fixed",!1):void 0}),this.$el.css(this._applyPlacement(b)),this.$el.css({position:d}),this},clear:function(){this.$el.html(""),this.data=[],this._index=0,this._$header=this._$footer=this._$noResultsMessage=null},activate:function(){return this.shown||(this.clear(),this.$el.show(),this.className&&this.$el.addClass(this.className),this.completer.fire("textComplete:show"),this.shown=!0),this},deactivate:function(){return this.shown&&(this.$el.hide(),this.className&&this.$el.removeClass(this.className),this.completer.fire("textComplete:hide"),this.shown=!1),this},isUp:function(a){return 38===a.keyCode||a.ctrlKey&&80===a.keyCode},isDown:function(a){return 40===a.keyCode||a.ctrlKey&&78===a.keyCode},isEnter:function(a){var b=a.ctrlKey||a.altKey||a.metaKey||a.shiftKey;return!b&&(13===a.keyCode||9===a.keyCode||this.option.completeOnSpace===!0&&32===a.keyCode)},isPageup:function(a){return 33===a.keyCode},isPagedown:function(a){return 34===a.keyCode},isEscape:function(a){return 27===a.keyCode},_data:null,_index:null,_$header:null,_$noResultsMessage:null,_$footer:null,_bindEvents:function(){this.$el.on("mousedown."+this.id,".textcomplete-item",a.proxy(this._onClick,this)),this.$el.on("touchstart."+this.id,".textcomplete-item",a.proxy(this._onClick,this)),this.$el.on("mouseover."+this.id,".textcomplete-item",a.proxy(this._onMouseover,this)),this.$inputEl.on("keydown."+this.id,a.proxy(this._onKeydown,this))},_onClick:function(b){var c=a(b.target);b.preventDefault(),b.originalEvent.keepTextCompleteDropdown=this.id,c.hasClass("textcomplete-item")||(c=c.closest(".textcomplete-item"));var d=this.data[parseInt(c.data("index"),10)];this.completer.select(d.value,d.strategy,b);var e=this;setTimeout(function(){e.deactivate(),"touchstart"===b.type&&e.$inputEl.focus()},0)},_onMouseover:function(b){var c=a(b.target);b.preventDefault(),c.hasClass("textcomplete-item")||(c=c.closest(".textcomplete-item")),this._index=parseInt(c.data("index"),10),this._activateIndexedItem()},_onKeydown:function(b){if(this.shown){var c;switch(a.isFunction(this.option.onKeydown)&&(c=this.option.onKeydown(b,f)),null==c&&(c=this._defaultKeydown(b)),c){case f.KEY_UP:b.preventDefault(),this._up();break;case f.KEY_DOWN:b.preventDefault(),this._down();break;case f.KEY_ENTER:b.preventDefault(),this._enter(b);break;case f.KEY_PAGEUP:b.preventDefault(),this._pageup();break;case f.KEY_PAGEDOWN:b.preventDefault(),this._pagedown();break;case f.KEY_ESCAPE:b.preventDefault(),this.deactivate()}}},_defaultKeydown:function(a){return this.isUp(a)?f.KEY_UP:this.isDown(a)?f.KEY_DOWN:this.isEnter(a)?f.KEY_ENTER:this.isPageup(a)?f.KEY_PAGEUP:this.isPagedown(a)?f.KEY_PAGEDOWN:this.isEscape(a)?f.KEY_ESCAPE:void 0},_up:function(){0===this._index?this._index=this.data.length-1:this._index-=1,this._activateIndexedItem(),this._setScroll()},_down:function(){this._index===this.data.length-1?this._index=0:this._index+=1,this._activateIndexedItem(),this._setScroll()},_enter:function(a){var b=this.data[parseInt(this._getActiveElement().data("index"),10)];this.completer.select(b.value,b.strategy,a),this.deactivate()},_pageup:function(){var b=0,c=this._getActiveElement().position().top-this.$el.innerHeight();this.$el.children().each(function(d){return a(this).position().top+a(this).outerHeight()>c?(b=d,!1):void 0}),this._index=b,this._activateIndexedItem(),this._setScroll()},_pagedown:function(){var b=this.data.length-1,c=this._getActiveElement().position().top+this.$el.innerHeight();this.$el.children().each(function(d){return a(this).position().top>c?(b=d,!1):void 0}),this._index=b,this._activateIndexedItem(),this._setScroll()},_activateIndexedItem:function(){this.$el.find(".textcomplete-item.active").removeClass("active"),this._getActiveElement().addClass("active")},_getActiveElement:function(){return this.$el.children(".textcomplete-item:nth("+this._index+")")},_setScroll:function(){var a=this._getActiveElement(),b=a.position().top,c=a.outerHeight(),d=this.$el.innerHeight(),e=this.$el.scrollTop();0===this._index||this._index==this.data.length-1||0>b?this.$el.scrollTop(b+e):b+c>d&&this.$el.scrollTop(b+c+e-d)},_buildContents:function(a){var b,c,e,f="";for(c=0;c<a.length&&this.data.length!==this.maxCount;c++)b=a[c],d(this.data,b)||(e=this.data.length,this.data.push(b),f+='<li class="textcomplete-item" data-index="'+e+'"><a>',f+=b.strategy.template(b.value,b.term),f+="</a></li>");return f},_renderHeader:function(b){if(this.header){this._$header||(this._$header=a('<li class="textcomplete-header"></li>').prependTo(this.$el));var c=a.isFunction(this.header)?this.header(b):this.header;this._$header.html(c)}},_renderFooter:function(b){if(this.footer){this._$footer||(this._$footer=a('<li class="textcomplete-footer"></li>').appendTo(this.$el));var c=a.isFunction(this.footer)?this.footer(b):this.footer;this._$footer.html(c)}},_renderNoResultsMessage:function(b){if(this.noResultsMessage){this._$noResultsMessage||(this._$noResultsMessage=a('<li class="textcomplete-no-results-message"></li>').appendTo(this.$el));var c=a.isFunction(this.noResultsMessage)?this.noResultsMessage(b):this.noResultsMessage;this._$noResultsMessage.html(c)}},_renderContents:function(a){this._$footer?this._$footer.before(a):this.$el.append(a)},_fitToBottom:function(){var a=c.scrollTop()+c.height(),b=this.$el.height();this.$el.position().top+b>a&&this.$el.offset({top:a-b})},_fitToRight:function(){for(var a,b=30,d=this.$el.offset().left,e=this.$el.width(),f=c.width()-b;d+e>f&&(this.$el.offset({left:d-b}),a=this.$el.offset().left,!(a>=d));)d=a},_applyPlacement:function(a){return-1!==this.placement.indexOf("top")?a={top:"auto",bottom:this.$el.parent().height()-a.top+a.lineHeight,left:a.left}:(a.bottom="auto",delete a.lineHeight),-1!==this.placement.indexOf("absleft")?a.left=0:-1!==this.placement.indexOf("absright")&&(a.right=0,a.left="auto"),a}}),a.fn.textcomplete.Dropdown=b,a.extend(a.fn.textcomplete,f)}(a),+function(a){"use strict";function b(b){a.extend(this,b),this.cache&&(this.search=c(this.search))}var c=function(a){var b={};return function(c,d){b[c]?d(b[c]):a.call(this,c,function(a){b[c]=(b[c]||[]).concat(a),d.apply(null,arguments)})}};b.parse=function(c,d){return a.map(c,function(a){var c=new b(a);return c.el=d.el,c.$el=d.$el,c})},a.extend(b.prototype,{match:null,replace:null,search:null,id:null,cache:!1,context:function(){return!0},index:2,template:function(a){return a},idProperty:null}),a.fn.textcomplete.Strategy=b}(a),+function(a){"use strict";function b(){}var c=Date.now||function(){return(new Date).getTime()},d=function(a,b){var d,e,f,g,h,i=function(){var j=c()-g;b>j?d=setTimeout(i,b-j):(d=null,h=a.apply(f,e),f=e=null)};return function(){return f=this,e=arguments,g=c(),d||(d=setTimeout(i,b)),h}};a.extend(b.prototype,{id:null,completer:null,el:null,$el:null,option:null,initialize:function(b,c,e){this.el=b,this.$el=a(b),this.id=c.id+this.constructor.name,this.completer=c,this.option=e,this.option.debounce&&(this._onKeyup=d(this._onKeyup,this.option.debounce)),this._bindEvents()},destroy:function(){this.$el.off("."+this.id),this.$el=this.el=this.completer=null},select:function(){throw new Error("Not implemented")},getCaretPosition:function(){var b=this._getCaretRelativePosition(),c=this.$el.offset(),d=this.option.appendTo;if(d){d instanceof a||(d=a(d));var e=d.offsetParent().offset();c.top-=e.top,c.left-=e.left}return b.top+=c.top,b.left+=c.left,b},focus:function(){this.$el.focus()},_bindEvents:function(){this.$el.on("keyup."+this.id,a.proxy(this._onKeyup,this))},_onKeyup:function(a){this._skipSearch(a)||this.completer.trigger(this.getTextFromHeadToCaret(),!0)},_skipSearch:function(a){switch(a.keyCode){case 9:case 13:case 40:case 38:return!0}if(a.ctrlKey)switch(a.keyCode){case 78:case 80:return!0}}}),a.fn.textcomplete.Adapter=b}(a),+function(a){"use strict";function b(a,b,c){this.initialize(a,b,c)}a.extend(b.prototype,a.fn.textcomplete.Adapter.prototype,{select:function(b,c,d){var e=this.getTextFromHeadToCaret(),f=this.el.value.substring(this.el.selectionEnd),g=c.replace(b,d);"undefined"!=typeof g&&(a.isArray(g)&&(f=g[1]+f,g=g[0]),e=e.replace(c.match,g),this.$el.val(e+f),this.el.selectionStart=this.el.selectionEnd=e.length)},getTextFromHeadToCaret:function(){return this.el.value.substring(0,this.el.selectionEnd)},_getCaretRelativePosition:function(){var b=a.fn.textcomplete.getCaretCoordinates(this.el,this.el.selectionStart);return{top:b.top+this._calculateLineHeight()-this.$el.scrollTop(),left:b.left-this.$el.scrollLeft()}},_calculateLineHeight:function(){var a=parseInt(this.$el.css("line-height"),10);if(isNaN(a)){var b=this.el.parentNode,c=document.createElement(this.el.nodeName),d=this.el.style;c.setAttribute("style","margin:0px;padding:0px;font-family:"+d.fontFamily+";font-size:"+d.fontSize),c.innerHTML="test",b.appendChild(c),a=c.clientHeight,b.removeChild(c)}return a}}),a.fn.textcomplete.Textarea=b}(a),+function(a){"use strict";function b(b,d,e){this.initialize(b,d,e),a("<span>"+c+"</span>").css({position:"absolute",top:-9999,left:-9999}).insertBefore(b)}var c="吶";a.extend(b.prototype,a.fn.textcomplete.Textarea.prototype,{select:function(b,c,d){var e=this.getTextFromHeadToCaret(),f=this.el.value.substring(e.length),g=c.replace(b,d);if("undefined"!=typeof g){a.isArray(g)&&(f=g[1]+f,g=g[0]),e=e.replace(c.match,g),this.$el.val(e+f),this.el.focus();var h=this.el.createTextRange();h.collapse(!0),h.moveEnd("character",e.length),h.moveStart("character",e.length),h.select()}},getTextFromHeadToCaret:function(){this.el.focus();var a=document.selection.createRange();a.moveStart("character",-this.el.value.length);var b=a.text.split(c);return 1===b.length?b[0]:b[1]}}),a.fn.textcomplete.IETextarea=b}(a),+function(a){"use strict";function b(a,b,c){this.initialize(a,b,c)}a.extend(b.prototype,a.fn.textcomplete.Adapter.prototype,{select:function(b,c,d){var e=this.getTextFromHeadToCaret(),f=window.getSelection(),g=f.getRangeAt(0),h=g.cloneRange();h.selectNodeContents(g.startContainer);var i=h.toString(),j=i.substring(g.startOffset),k=c.replace(b,d);if("undefined"!=typeof k){a.isArray(k)&&(j=k[1]+j,k=k[0]),e=e.replace(c.match,k),g.selectNodeContents(g.startContainer),g.deleteContents();var l=document.createElement("div");l.innerHTML=e;var m=document.createElement("div");m.innerHTML=j;for(var n,o,p=document.createDocumentFragment();n=l.firstChild;)o=p.appendChild(n);for(;n=m.firstChild;)p.appendChild(n);g.insertNode(p),g.setStartAfter(o),g.collapse(!0),f.removeAllRanges(),f.addRange(g)}},_getCaretRelativePosition:function(){var b=window.getSelection().getRangeAt(0).cloneRange(),c=document.createElement("span");b.insertNode(c),b.selectNodeContents(c),b.deleteContents();var d=a(c),e=d.offset();return e.left-=this.$el.offset().left,e.top+=d.height()-this.$el.offset().top,e.lineHeight=d.height(),d.remove(),e},getTextFromHeadToCaret:function(){var a=window.getSelection().getRangeAt(0),b=a.cloneRange();return b.selectNodeContents(a.startContainer),b.toString().substring(0,a.startOffset)}}),a.fn.textcomplete.ContentEditable=b}(a),function(a){function b(a,b,f){if(!d)throw new Error("textarea-caret-position#getCaretCoordinates should only be called in a browser");var g=f&&f.debug||!1;if(g){var h=document.querySelector("#input-textarea-caret-position-mirror-div");h&&h.parentNode.removeChild(h)}var i=document.createElement("div");i.id="input-textarea-caret-position-mirror-div",document.body.appendChild(i);var j=i.style,k=window.getComputedStyle?getComputedStyle(a):a.currentStyle;j.whiteSpace="pre-wrap","INPUT"!==a.nodeName&&(j.wordWrap="break-word"),j.position="absolute",g||(j.visibility="hidden"),c.forEach(function(a){j[a]=k[a]}),e?a.scrollHeight>parseInt(k.height)&&(j.overflowY="scroll"):j.overflow="hidden",i.textContent=a.value.substring(0,b),"INPUT"===a.nodeName&&(i.textContent=i.textContent.replace(/\s/g," "));var l=document.createElement("span");l.textContent=a.value.substring(b)||".",i.appendChild(l);var m={top:l.offsetTop+parseInt(k.borderTopWidth),left:l.offsetLeft+parseInt(k.borderLeftWidth)};return g?l.style.backgroundColor="#aaa":document.body.removeChild(i),m}var c=["direction","boxSizing","width","height","overflowX","overflowY","borderTopWidth","borderRightWidth","borderBottomWidth","borderLeftWidth","borderStyle","paddingTop","paddingRight","paddingBottom","paddingLeft","fontStyle","fontVariant","fontWeight","fontStretch","fontSize","fontSizeAdjust","lineHeight","fontFamily","textAlign","textTransform","textIndent","textDecoration","letterSpacing","wordSpacing","tabSize","MozTabSize"],d="undefined"!=typeof window,e=d&&null!=window.mozInnerScreenX;a.fn.textcomplete.getCaretCoordinates=b}(a),a});
+//# sourceMappingURL=dist/jquery.textcomplete.min.map
\ No newline at end of file
diff --git a/view/js/jquery-textcomplete/jquery.textcomplete.min.map b/view/js/jquery-textcomplete/jquery.textcomplete.min.map
new file mode 100644 (file)
index 0000000..e27ef4d
--- /dev/null
@@ -0,0 +1 @@
+{"version":3,"file":"dist/jquery.textcomplete.min.js","sources":["dist/jquery.textcomplete.js"],"names":["factory","define","amd","module","exports","$","require","jQuery","Error","warn","message","console","id","fn","textcomplete","strategies","option","args","Array","prototype","slice","call","arguments","this","each","self","$this","completer","data","_oid","Completer","shift","apply","removeData","obj","name","register","Strategy","parse","el","$el","element","uniqueId","views","extend","_getDefaults","is","isContentEditable","contentEditable","document","activeElement","initialize","one","lock","func","locked","queuedArgsToReplay","unshift","replayOrFree","replayArgs","undefined","isString","Object","toString","isFunction","DEFAULTS","appendTo","zIndex","adapter","dropdown","get","Dropdown","Adapter","viewName","selectionEnd","destroy","off","deactivate","trigger","text","skipUnchangedTerm","getTextFromHeadToCaret","searchQuery","_extractSearchQuery","length","term","_term","_search","fire","eventName","push","select","value","strategy","e","focus","_clearAtNext","i","context","matchRegexp","match","index","free","search","stillSearching","shown","activate","clear","setPosition","getCaretPosition","render","_zip","map","createElement","_data","$inputEl","listPosition","height","_i","_bindEvents","dropdownViews","$window","window","include","zippedData","datum","elem","idProperty","on","originalEvent","keepTextCompleteDropdown","key","view","commands","SKIP_DEFAULT","KEY_UP","KEY_DOWN","KEY_ENTER","KEY_PAGEUP","KEY_PAGEDOWN","KEY_ESCAPE","$parent","addClass","attr","css","display","left","position","footer","header","maxCount","placement","className","remove","contentsHtml","_buildContents","unzippedData","d","removeAttr","_renderHeader","_renderFooter","_renderContents","_fitToBottom","_fitToRight","_activateIndexedItem","_setScroll","noResultsMessage","_renderNoResultsMessage","pos","add","parents","top","scrollTop","scrollLeft","_applyPlacement","html","_index","_$header","_$footer","_$noResultsMessage","show","hide","removeClass","isUp","keyCode","ctrlKey","isDown","isEnter","modifiers","altKey","metaKey","shiftKey","completeOnSpace","isPageup","isPagedown","isEscape","proxy","_onClick","_onMouseover","_onKeydown","target","preventDefault","hasClass","closest","parseInt","setTimeout","type","command","onKeydown","_defaultKeydown","_up","_down","_enter","_pageup","_pagedown","_getActiveElement","threshold","innerHeight","children","outerHeight","find","$activeEl","itemTop","itemHeight","visibleHeight","visibleTop","template","prependTo","before","append","windowScrollBottom","offset","tolerance","lastOffset","width","maxLeft","indexOf","bottom","parent","lineHeight","right","options","cache","memoize","memo","callback","concat","strategiesArray","params","strategyObj","replace","now","Date","getTime","debounce","wait","timeout","timestamp","result","later","last","constructor","_onKeyup","_getCaretRelativePosition","parentOffset","offsetParent","_skipSearch","clickEvent","Textarea","pre","post","substring","newSubstr","isArray","val","selectionStart","p","getCaretCoordinates","_calculateLineHeight","isNaN","parentNode","temp","nodeName","style","setAttribute","fontFamily","fontSize","innerHTML","appendChild","clientHeight","removeChild","IETextarea","sentinelChar","insertBefore","range","createTextRange","collapse","moveEnd","moveStart","selection","createRange","arr","split","ContentEditable","sel","getSelection","getRangeAt","cloneRange","selectNodeContents","startContainer","content","startOffset","deleteContents","preWrapper","postWrapper","childNode","lastOfPre","fragment","createDocumentFragment","firstChild","insertNode","setStartAfter","removeAllRanges","addRange","node","$node","isBrowser","debug","querySelector","div","body","computed","getComputedStyle","currentStyle","whiteSpace","wordWrap","visibility","properties","forEach","prop","isFirefox","scrollHeight","overflowY","overflow","textContent","span","coordinates","offsetTop","offsetLeft","backgroundColor","mozInnerScreenX"],"mappings":";CAAC,SAAUA,GACT,GAAsB,kBAAXC,SAAyBA,OAAOC,IAEzCD,QAAQ,UAAWD,OACd,IAAsB,gBAAXG,SAAuBA,OAAOC,QAAS,CACvD,GAAIC,GAAIC,QAAQ,SAChBH,QAAOC,QAAUJ,EAAQK,OAGzBL,GAAQO,SAEV,SAAUA,GAUZ,GAAsB,mBAAXA,GACT,KAAM,IAAIC,OAAM,sCAi2ClB,QA91CC,SAAUH,GACT,YAEA,IAAII,GAAO,SAAUC,GACfC,QAAQF,MAAQE,QAAQF,KAAKC,IAG/BE,EAAK,CAETP,GAAEQ,GAAGC,aAAe,SAAUC,EAAYC,GACxC,GAAIC,GAAOC,MAAMC,UAAUC,MAAMC,KAAKC,UACtC,OAAOC,MAAKC,KAAK,WACf,GAAIC,GAAOF,KACPG,EAAQrB,EAAEkB,MACVI,EAAYD,EAAME,KAAK,eAO3B,IANKD,IACHX,IAAWA,MACXA,EAAOa,KAAOjB,IACde,EAAY,GAAItB,GAAEQ,GAAGC,aAAagB,UAAUP,KAAMP,GAClDU,EAAME,KAAK,eAAgBD,IAEH,gBAAfZ,GAAyB,CAClC,IAAKY,EAAW,MAChBV,GAAKc,QACLJ,EAAUZ,GAAYiB,MAAML,EAAWV,GACpB,YAAfF,GACFW,EAAMO,WAAW,oBAKnB5B,GAAEmB,KAAKT,EAAY,SAAUmB,GAC3B7B,EAAEmB,MAAM,SAAU,SAAU,YAAa,YAAa,SAAUW,GAC1DD,EAAIC,KACNR,EAAUX,OAAOmB,GAAQD,EAAIC,GAC7B1B,EAAK0B,EAAO,wDACLD,GAAIC,QAIjBR,EAAUS,SAAS/B,EAAEQ,GAAGC,aAAauB,SAASC,MAAMvB,GAClDwB,GAAId,EACJe,IAAKd,SAMbnB,IAED,SAAUF,GACT,YAoEA,SAASyB,GAAUW,EAASzB,GAO1B,GANAO,KAAKiB,IAAanC,EAAEoC,GACpBlB,KAAKX,GAAa,eAAiB8B,IACnCnB,KAAKR,cACLQ,KAAKoB,SACLpB,KAAKP,OAAaX,EAAEuC,UAAWd,EAAUe,eAAgB7B,KAEpDO,KAAKiB,IAAIM,GAAG,qBAAwBvB,KAAKiB,IAAIM,GAAG,uBAA0BvB,KAAKiB,IAAIM,GAAG,aAAgBL,EAAQM,mBAAgD,QAA3BN,EAAQO,iBAC9I,KAAM,IAAIxC,OAAM,kEAGlB,IAAIiC,IAAYQ,SAASC,cAEvB3B,KAAK4B,iBACA,CAEL,GAAI1B,GAAOF,IACXA,MAAKiB,IAAIY,IAAI,SAAW7B,KAAKX,GAAI,WAAca,EAAK0B,gBA7DxD,GAAIE,GAAO,SAAUC,GACnB,GAAIC,GAAQC,CAEZ,OAAO,YAEL,GAAIvC,GAAOC,MAAMC,UAAUC,MAAMC,KAAKC,UACtC,IAAIiC,EAKF,YADAC,EAAqBvC,EAGvBsC,IAAS,CACT,IAAI9B,GAAOF,IACXN,GAAKwC,QAAQ,QAASC,KACpB,GAAIF,EAAoB,CAMtB,GAAIG,GAAaH,CACjBA,GAAqBI,OACrBD,EAAWF,QAAQC,GACnBJ,EAAKtB,MAAMP,EAAMkC,OAEjBJ,IAAS,IAGbD,EAAKtB,MAAMT,KAAMN,KAIjB4C,EAAW,SAAU3B,GACvB,MAA+C,oBAAxC4B,OAAO3C,UAAU4C,SAAS1C,KAAKa,IAGpC8B,EAAa,SAAU9B,GACzB,MAA+C,sBAAxC4B,OAAO3C,UAAU4C,SAAS1C,KAAKa,IAGpCQ,EAAW,CAuBfZ,GAAUe,aAAe,WAQvB,MAPKf,GAAUmC,WACbnC,EAAUmC,UACRC,SAAU7D,EAAE,QACZ8D,OAAQ,QAILrC,EAAUmC,UAGnB5D,EAAEuC,OAAOd,EAAUX,WAIjBP,GAAY,KACZI,OAAY,KACZD,WAAY,KACZqD,QAAY,KACZC,SAAY,KACZ7B,IAAY,KAKZW,WAAY,WACV,GAAIV,GAAUlB,KAAKiB,IAAI8B,IAAI,EAE3B/C,MAAK8C,SAAW,GAAIhE,GAAEQ,GAAGC,aAAayD,SAAS9B,EAASlB,KAAMA,KAAKP,OACnE,IAAIwD,GAASC,CACTlD,MAAKP,OAAOoD,QACdI,EAAUjD,KAAKP,OAAOoD,SAGpBK,EADElD,KAAKiB,IAAIM,GAAG,aAAevB,KAAKiB,IAAIM,GAAG,qBAAuBvB,KAAKiB,IAAIM,GAAG,sBACjC,gBAAzBL,GAAQiC,aAA4B,WAAa,aAExD,kBAEbF,EAAUnE,EAAEQ,GAAGC,aAAa2D,IAE9BlD,KAAK6C,QAAU,GAAII,GAAQ/B,EAASlB,KAAMA,KAAKP,SAGjD2D,QAAS,WACPpD,KAAKiB,IAAIoC,IAAI,IAAMrD,KAAKX,IACpBW,KAAK6C,SACP7C,KAAK6C,QAAQO,UAEXpD,KAAK8C,UACP9C,KAAK8C,SAASM,UAEhBpD,KAAKiB,IAAMjB,KAAK6C,QAAU7C,KAAK8C,SAAW,MAG5CQ,WAAY,WACNtD,KAAK8C,UACP9C,KAAK8C,SAASQ,cAKlBC,QAAS,SAAUC,EAAMC,GAClBzD,KAAK8C,UAAY9C,KAAK4B,aACnB,MAAR4B,IAAiBA,EAAOxD,KAAK6C,QAAQa,yBACrC,IAAIC,GAAc3D,KAAK4D,oBAAoBJ,EAC3C,IAAIG,EAAYE,OAAQ,CACtB,GAAIC,GAAOH,EAAY,EAEvB,IAAIF,GAAqBzD,KAAK+D,QAAUD,GAAiB,KAATA,EAAe,MAC/D9D,MAAK+D,MAAQD,EACb9D,KAAKgE,QAAQvD,MAAMT,KAAM2D,OAEzB3D,MAAK+D,MAAQ,KACb/D,KAAK8C,SAASQ,cAIlBW,KAAM,SAAUC,GACd,GAAIxE,GAAOC,MAAMC,UAAUC,MAAMC,KAAKC,UAAW,EAEjD,OADAC,MAAKiB,IAAIsC,QAAQW,EAAWxE,GACrBM,MAGTa,SAAU,SAAUrB,GAClBG,MAAMC,UAAUuE,KAAK1D,MAAMT,KAAKR,WAAYA,IAS9C4E,OAAQ,SAAUC,EAAOC,EAAUC,GACjCvE,KAAK+D,MAAQ,KACb/D,KAAK6C,QAAQuB,OAAOC,EAAOC,EAAUC,GACrCvE,KAAKiE,KAAK,UAAUA,KAAK,sBAAuBI,EAAOC,GACvDtE,KAAK6C,QAAQ2B,SAMfC,cAAc,EACdV,MAAc,KASdH,oBAAqB,SAAUJ,GAC7B,IAAK,GAAIkB,GAAI,EAAGA,EAAI1E,KAAKR,WAAWqE,OAAQa,IAAK,CAC/C,GAAIJ,GAAWtE,KAAKR,WAAWkF,GAC3BC,EAAUL,EAASK,QAAQnB,EAC/B,IAAImB,GAAuB,KAAZA,EAAgB,CAC7B,GAAIC,GAAcnC,EAAW6B,EAASO,OAASP,EAASO,MAAMrB,GAAQc,EAASO,KAC3EvC,GAASqC,KAAYnB,EAAOmB,EAChC,IAAIE,GAAQrB,EAAKqB,MAAMD,EACvB,IAAIC,EAAS,OAAQP,EAAUO,EAAMP,EAASQ,OAAQD,IAG1D,UAIFb,QAASlC,EAAK,SAAUiD,EAAMT,EAAUR,EAAMe,GAC5C,GAAI3E,GAAOF,IACXsE,GAASU,OAAOlB,EAAM,SAAUzD,EAAM4E,GAC/B/E,EAAK4C,SAASoC,OACjBhF,EAAK4C,SAASqC,WAEZjF,EAAKuE,eAEPvE,EAAK4C,SAASsC,QACdlF,EAAKuE,cAAe,GAEtBvE,EAAK4C,SAASuC,YAAYnF,EAAK2C,QAAQyC,oBACvCpF,EAAK4C,SAASyC,OAAOrF,EAAKsF,KAAKnF,EAAMiE,EAAUR,IAC1CmB,IAEHF,IACA7E,EAAKuE,cAAe,IAErBI,KASLW,KAAM,SAAUnF,EAAMiE,EAAUR,GAC9B,MAAOhF,GAAE2G,IAAIpF,EAAM,SAAUgE,GAC3B,OAASA,MAAOA,EAAOC,SAAUA,EAAUR,KAAMA,QAKvDhF,EAAEQ,GAAGC,aAAagB,UAAYA,GAC9BvB,IAED,SAAUF,GACT,YA2CA,SAASkE,GAAS9B,EAASd,EAAWX,GACpCO,KAAKiB,IAAY+B,EAAS0C,cAAcjG,GACxCO,KAAKI,UAAYA,EACjBJ,KAAKX,GAAYe,EAAUf,GAAK,WAChCW,KAAK2F,SACL3F,KAAK4F,SAAY9G,EAAEoC,GACnBlB,KAAKP,OAAYA,EAGbA,EAAOoG,eAAgB7F,KAAKqF,YAAc5F,EAAOoG,cACjDpG,EAAOqG,QAAU9F,KAAKiB,IAAI6E,OAAOrG,EAAOqG,OAC5C,IAAI5F,GAAOF,IACXlB,GAAEmB,MAAM,WAAY,YAAa,SAAU,SAAU,mBAAoB,aAAc,SAAU8F,EAAInF,GAC/E,MAAhBnB,EAAOmB,KAAiBV,EAAKU,GAAQnB,EAAOmB,MAElDZ,KAAKgG,YAAY9E,GACjB+E,EAAcjG,KAAKX,IAAMW,KAzD3B,GAAIkG,GAAUpH,EAAEqH,QAEZC,EAAU,SAAUC,EAAYC,GAClC,GAAI5B,GAAG6B,EACHC,EAAaF,EAAMhC,SAASkC,UAChC,KAAK9B,EAAI,EAAGA,EAAI2B,EAAWxC,OAAQa,IAEjC,GADA6B,EAAOF,EAAW3B,GACd6B,EAAKjC,WAAagC,EAAMhC,SAC5B,GAAIkC,GACF,GAAID,EAAKlC,MAAMmC,KAAgBF,EAAMjC,MAAMmC,GAAa,OAAO,MAE/D,IAAID,EAAKlC,QAAUiC,EAAMjC,MAAO,OAAO,CAG3C,QAAO,GAGL4B,IACJnH,GAAE4C,UAAU+E,GAAG,QAAS,SAAUlC,GAChC,GAAIlF,GAAKkF,EAAEmC,eAAiBnC,EAAEmC,cAAcC,wBAC5C7H,GAAEmB,KAAKgG,EAAe,SAAUW,EAAKC,GAC/BD,IAAQvH,GAAMwH,EAAKvD,gBAI3B,IAAIwD,IACFC,aAAc,EACdC,OAAQ,EACRC,SAAU,EACVC,UAAW,EACXC,WAAY,EACZC,aAAc,EACdC,WAAY,EA4BdvI,GAAEuC,OAAO2B,GAIP0C,cAAe,SAAUjG,GACvB,GAAI6H,GAAU7H,EAAOkD,QACf2E,aAAmBxI,KAAMwI,EAAUxI,EAAEwI,GAC3C,IAAIrG,GAAMnC,EAAE,aACTyI,SAAS,uCACTC,KAAK,KAAM,yBAA2B/H,EAAOa,MAC7CmH,KACCC,QAAS,OACTC,KAAM,EACNC,SAAU,WACVhF,OAAQnD,EAAOmD,SAEhBD,SAAS2E,EACZ,OAAOrG,MAIXnC,EAAEuC,OAAO2B,EAASpD,WAIhBqB,IAAW,KACX2E,SAAW,KACXxF,UAAW,KACXyH,OAAW,KACXC,OAAW,KACXzI,GAAW,KACX0I,SAAW,GACXC,UAAW,GACX9C,OAAW,EACX7E,QACA4H,UAAW,GAKX7E,QAAS,WAEPpD,KAAKsD,aAELtD,KAAKiB,IAAIoC,IAAI,IAAMrD,KAAKX,IACxBW,KAAK4F,SAASvC,IAAI,IAAMrD,KAAKX,IAC7BW,KAAKoF,QACLpF,KAAKiB,IAAIiH,SACTlI,KAAKiB,IAAMjB,KAAK4F,SAAW5F,KAAKI,UAAY,WACrC6F,GAAcjG,KAAKX,KAG5BkG,OAAQ,SAAUc,GAChB,GAAI8B,GAAenI,KAAKoI,eAAe/B,GACnCgC,EAAevJ,EAAE2G,IAAIzF,KAAKK,KAAM,SAAUiI,GAAK,MAAOA,GAAEjE,OAC5D,IAAIrE,KAAKK,KAAKwD,OAAQ,CACpB,GAAIS,GAAW+B,EAAW,GAAG/B,QACzBA,GAASjF,GACXW,KAAKiB,IAAIuG,KAAK,gBAAiBlD,EAASjF,IAExCW,KAAKiB,IAAIsH,WAAW,iBAEtBvI,KAAKwI,cAAcH,GACnBrI,KAAKyI,cAAcJ,GACfF,IACFnI,KAAK0I,gBAAgBP,GACrBnI,KAAK2I,eACL3I,KAAK4I,cACL5I,KAAK6I,wBAEP7I,KAAK8I,iBACI9I,MAAK+I,iBACd/I,KAAKgJ,wBAAwBX,GACpBrI,KAAKkF,OACdlF,KAAKsD,cAIT+B,YAAa,SAAU4D,GAIrB,GAAIrB,GAAW,UAef,OAbA5H,MAAK4F,SAASsD,IAAIlJ,KAAK4F,SAASuD,WAAWlJ,KAAK,WAC9C,MAA+B,aAA5BnB,EAAEkB,MAAMyH,IAAI,aACN,EACsB,UAA5B3I,EAAEkB,MAAMyH,IAAI,aACbwB,EAAIG,KAAOlD,EAAQmD,YACnBJ,EAAItB,MAAQzB,EAAQoD,aACpB1B,EAAW,SACJ,GAJT,SAOF5H,KAAKiB,IAAIwG,IAAIzH,KAAKuJ,gBAAgBN,IAClCjJ,KAAKiB,IAAIwG,KAAMG,SAAUA,IAElB5H,MAGToF,MAAO,WACLpF,KAAKiB,IAAIuI,KAAK,IACdxJ,KAAKK,QACLL,KAAKyJ,OAAS,EACdzJ,KAAK0J,SAAW1J,KAAK2J,SAAW3J,KAAK4J,mBAAqB,MAG5DzE,SAAU,WAQR,MAPKnF,MAAKkF,QACRlF,KAAKoF,QACLpF,KAAKiB,IAAI4I,OACL7J,KAAKiI,WAAajI,KAAKiB,IAAIsG,SAASvH,KAAKiI,WAC7CjI,KAAKI,UAAU6D,KAAK,qBACpBjE,KAAKkF,OAAQ,GAERlF,MAGTsD,WAAY,WAOV,MANItD,MAAKkF,QACPlF,KAAKiB,IAAI6I,OACL9J,KAAKiI,WAAajI,KAAKiB,IAAI8I,YAAY/J,KAAKiI,WAChDjI,KAAKI,UAAU6D,KAAK,qBACpBjE,KAAKkF,OAAQ,GAERlF,MAGTgK,KAAM,SAAUzF,GACd,MAAqB,MAAdA,EAAE0F,SAAmB1F,EAAE2F,SAAyB,KAAd3F,EAAE0F,SAG7CE,OAAQ,SAAU5F,GAChB,MAAqB,MAAdA,EAAE0F,SAAmB1F,EAAE2F,SAAyB,KAAd3F,EAAE0F,SAG7CG,QAAS,SAAU7F,GACjB,GAAI8F,GAAY9F,EAAE2F,SAAW3F,EAAE+F,QAAU/F,EAAEgG,SAAWhG,EAAEiG,QACxD,QAAQH,IAA4B,KAAd9F,EAAE0F,SAAgC,IAAd1F,EAAE0F,SAAkBjK,KAAKP,OAAOgL,mBAAoB,GAAsB,KAAdlG,EAAE0F,UAG1GS,SAAU,SAAUnG,GAClB,MAAqB,MAAdA,EAAE0F,SAGXU,WAAY,SAAUpG,GACpB,MAAqB,MAAdA,EAAE0F,SAGXW,SAAU,SAAUrG,GAClB,MAAqB,MAAdA,EAAE0F,SAMXtE,MAAU,KACV8D,OAAU,KACVC,SAAU,KACVE,mBAAoB,KACpBD,SAAU,KAKV3D,YAAa,WACXhG,KAAKiB,IAAIwF,GAAG,aAAezG,KAAKX,GAAI,qBAAsBP,EAAE+L,MAAM7K,KAAK8K,SAAU9K,OACjFA,KAAKiB,IAAIwF,GAAG,cAAgBzG,KAAKX,GAAI,qBAAsBP,EAAE+L,MAAM7K,KAAK8K,SAAU9K,OAClFA,KAAKiB,IAAIwF,GAAG,aAAezG,KAAKX,GAAI,qBAAsBP,EAAE+L,MAAM7K,KAAK+K,aAAc/K,OACrFA,KAAK4F,SAASa,GAAG,WAAazG,KAAKX,GAAIP,EAAE+L,MAAM7K,KAAKgL,WAAYhL,QAGlE8K,SAAU,SAAUvG,GAClB,GAAItD,GAAMnC,EAAEyF,EAAE0G,OACd1G,GAAE2G,iBACF3G,EAAEmC,cAAcC,yBAA2B3G,KAAKX,GAC3C4B,EAAIkK,SAAS,uBAChBlK,EAAMA,EAAImK,QAAQ,sBAEpB,IAAI9E,GAAQtG,KAAKK,KAAKgL,SAASpK,EAAIZ,KAAK,SAAU,IAClDL,MAAKI,UAAUgE,OAAOkC,EAAMjC,MAAOiC,EAAMhC,SAAUC,EACnD,IAAIrE,GAAOF,IAGXsL,YAAW,WACTpL,EAAKoD,aACU,eAAXiB,EAAEgH,MACJrL,EAAK0F,SAASpB,SAEf,IAILuG,aAAc,SAAUxG,GACtB,GAAItD,GAAMnC,EAAEyF,EAAE0G,OACd1G,GAAE2G,iBACGjK,EAAIkK,SAAS,uBAChBlK,EAAMA,EAAImK,QAAQ,uBAEpBpL,KAAKyJ,OAAS4B,SAASpK,EAAIZ,KAAK,SAAU,IAC1CL,KAAK6I,wBAGPmC,WAAY,SAAUzG,GACpB,GAAKvE,KAAKkF,MAAV,CAEA,GAAIsG,EAUJ,QARI1M,EAAE2D,WAAWzC,KAAKP,OAAOgM,aAC3BD,EAAUxL,KAAKP,OAAOgM,UAAUlH,EAAGuC,IAGtB,MAAX0E,IACFA,EAAUxL,KAAK0L,gBAAgBnH,IAGzBiH,GACN,IAAK1E,GAASE,OACZzC,EAAE2G,iBACFlL,KAAK2L,KACL,MACF,KAAK7E,GAASG,SACZ1C,EAAE2G,iBACFlL,KAAK4L,OACL,MACF,KAAK9E,GAASI,UACZ3C,EAAE2G,iBACFlL,KAAK6L,OAAOtH,EACZ,MACF,KAAKuC,GAASK,WACZ5C,EAAE2G,iBACFlL,KAAK8L,SACL,MACF,KAAKhF,GAASM,aACZ7C,EAAE2G,iBACFlL,KAAK+L,WACL,MACF,KAAKjF,GAASO,WACZ9C,EAAE2G,iBACFlL,KAAKsD,gBAKXoI,gBAAiB,SAAUnH,GACzB,MAAIvE,MAAKgK,KAAKzF,GACLuC,EAASE,OACPhH,KAAKmK,OAAO5F,GACduC,EAASG,SACPjH,KAAKoK,QAAQ7F,GACfuC,EAASI,UACPlH,KAAK0K,SAASnG,GAChBuC,EAASK,WACPnH,KAAK2K,WAAWpG,GAClBuC,EAASM,aACPpH,KAAK4K,SAASrG,GAChBuC,EAASO,WADX,QAKTsE,IAAK,WACiB,IAAhB3L,KAAKyJ,OACPzJ,KAAKyJ,OAASzJ,KAAKK,KAAKwD,OAAS,EAEjC7D,KAAKyJ,QAAU,EAEjBzJ,KAAK6I,uBACL7I,KAAK8I,cAGP8C,MAAO,WACD5L,KAAKyJ,SAAWzJ,KAAKK,KAAKwD,OAAS,EACrC7D,KAAKyJ,OAAS,EAEdzJ,KAAKyJ,QAAU,EAEjBzJ,KAAK6I,uBACL7I,KAAK8I,cAGP+C,OAAQ,SAAUtH,GAChB,GAAI+B,GAAQtG,KAAKK,KAAKgL,SAASrL,KAAKgM,oBAAoB3L,KAAK,SAAU,IACvEL,MAAKI,UAAUgE,OAAOkC,EAAMjC,MAAOiC,EAAMhC,SAAUC,GACnDvE,KAAKsD,cAGPwI,QAAS,WACP,GAAIb,GAAS,EACTgB,EAAYjM,KAAKgM,oBAAoBpE,WAAWwB,IAAMpJ,KAAKiB,IAAIiL,aACnElM,MAAKiB,IAAIkL,WAAWlM,KAAK,SAAUyE,GACjC,MAAI5F,GAAEkB,MAAM4H,WAAWwB,IAAMtK,EAAEkB,MAAMoM,cAAgBH,GACnDhB,EAASvG,GACF,GAFT,SAKF1E,KAAKyJ,OAASwB,EACdjL,KAAK6I,uBACL7I,KAAK8I,cAGPiD,UAAW,WACT,GAAId,GAASjL,KAAKK,KAAKwD,OAAS,EAC5BoI,EAAYjM,KAAKgM,oBAAoBpE,WAAWwB,IAAMpJ,KAAKiB,IAAIiL,aACnElM,MAAKiB,IAAIkL,WAAWlM,KAAK,SAAUyE,GACjC,MAAI5F,GAAEkB,MAAM4H,WAAWwB,IAAM6C,GAC3BhB,EAASvG,GACF,GAFT,SAKF1E,KAAKyJ,OAASwB,EACdjL,KAAK6I,uBACL7I,KAAK8I,cAGPD,qBAAsB,WACpB7I,KAAKiB,IAAIoL,KAAK,6BAA6BtC,YAAY,UACvD/J,KAAKgM,oBAAoBzE,SAAS,WAGpCyE,kBAAmB,WACjB,MAAOhM,MAAKiB,IAAIkL,SAAS,0BAA4BnM,KAAKyJ,OAAS,MAGrEX,WAAY,WACV,GAAIwD,GAAYtM,KAAKgM,oBACjBO,EAAUD,EAAU1E,WAAWwB,IAC/BoD,EAAaF,EAAUF,cACvBK,EAAgBzM,KAAKiB,IAAIiL,cACzBQ,EAAa1M,KAAKiB,IAAIoI,WACN,KAAhBrJ,KAAKyJ,QAAgBzJ,KAAKyJ,QAAUzJ,KAAKK,KAAKwD,OAAS,GAAe,EAAV0I,EAC9DvM,KAAKiB,IAAIoI,UAAUkD,EAAUG,GACpBH,EAAUC,EAAaC,GAChCzM,KAAKiB,IAAIoI,UAAUkD,EAAUC,EAAaE,EAAaD,IAI3DrE,eAAgB,SAAU/B,GACxB,GAAIC,GAAO5B,EAAGI,EACV0E,EAAO,EACX,KAAK9E,EAAI,EAAGA,EAAI2B,EAAWxC,QACrB7D,KAAKK,KAAKwD,SAAW7D,KAAK+H,SADGrD,IAEjC4B,EAAQD,EAAW3B,GACf0B,EAAQpG,KAAKK,KAAMiG,KACvBxB,EAAQ9E,KAAKK,KAAKwD,OAClB7D,KAAKK,KAAK8D,KAAKmC,GACfkD,GAAQ,6CAA+C1E,EAAQ,QAC/D0E,GAAUlD,EAAMhC,SAASqI,SAASrG,EAAMjC,MAAOiC,EAAMxC,MACrD0F,GAAQ,YAEV,OAAOA,IAGThB,cAAe,SAAUH,GACvB,GAAIrI,KAAK8H,OAAQ,CACV9H,KAAK0J,WACR1J,KAAK0J,SAAW5K,EAAE,yCAAyC8N,UAAU5M,KAAKiB,KAE5E,IAAIuI,GAAO1K,EAAE2D,WAAWzC,KAAK8H,QAAU9H,KAAK8H,OAAOO,GAAgBrI,KAAK8H,MACxE9H,MAAK0J,SAASF,KAAKA,KAIvBf,cAAe,SAAUJ,GACvB,GAAIrI,KAAK6H,OAAQ,CACV7H,KAAK2J,WACR3J,KAAK2J,SAAW7K,EAAE,yCAAyC6D,SAAS3C,KAAKiB,KAE3E,IAAIuI,GAAO1K,EAAE2D,WAAWzC,KAAK6H,QAAU7H,KAAK6H,OAAOQ,GAAgBrI,KAAK6H,MACxE7H,MAAK2J,SAASH,KAAKA,KAIvBR,wBAAyB,SAAUX,GACjC,GAAIrI,KAAK+I,iBAAkB,CACpB/I,KAAK4J,qBACR5J,KAAK4J,mBAAqB9K,EAAE,qDAAqD6D,SAAS3C,KAAKiB,KAEjG,IAAIuI,GAAO1K,EAAE2D,WAAWzC,KAAK+I,kBAAoB/I,KAAK+I,iBAAiBV,GAAgBrI,KAAK+I,gBAC5F/I,MAAK4J,mBAAmBJ,KAAKA,KAIjCd,gBAAiB,SAAUc,GACrBxJ,KAAK2J,SACP3J,KAAK2J,SAASkD,OAAOrD,GAErBxJ,KAAKiB,IAAI6L,OAAOtD,IAIpBb,aAAc,WACZ,GAAIoE,GAAqB7G,EAAQmD,YAAcnD,EAAQJ,SACnDA,EAAS9F,KAAKiB,IAAI6E,QACjB9F,MAAKiB,IAAI2G,WAAWwB,IAAMtD,EAAUiH,GACvC/M,KAAKiB,IAAI+L,QAAQ5D,IAAK2D,EAAqBjH,KAI/C8C,YAAa,WASX,IAJA,GACyCoE,GADrCC,EAAY,GACZC,EAAalN,KAAKiB,IAAI+L,SAASrF,KAC/BwF,EAAQnN,KAAKiB,IAAIkM,QACjBC,EAAUlH,EAAQiH,QAAUF,EACzBC,EAAaC,EAAQC,IAC1BpN,KAAKiB,IAAI+L,QAAQrF,KAAMuF,EAAaD,IACpCD,EAAShN,KAAKiB,IAAI+L,SAASrF,OACvBqF,GAAUE,KACdA,EAAaF,GAIjBzD,gBAAiB,SAAU3B,GAmBzB,MAjBsC,KAAlC5H,KAAKgI,UAAUqF,QAAQ,OAEzBzF,GACEwB,IAAK,OACLkE,OAAQtN,KAAKiB,IAAIsM,SAASzH,SAAW8B,EAASwB,IAAMxB,EAAS4F,WAC7D7F,KAAMC,EAASD,OAGjBC,EAAS0F,OAAS,aACX1F,GAAS4F,YAEwB,KAAtCxN,KAAKgI,UAAUqF,QAAQ,WACzBzF,EAASD,KAAO,EACgC,KAAvC3H,KAAKgI,UAAUqF,QAAQ,cAChCzF,EAAS6F,MAAQ,EACjB7F,EAASD,KAAO,QAEXC,KAIX9I,EAAEQ,GAAGC,aAAayD,SAAWA,EAC7BlE,EAAEuC,OAAOvC,EAAEQ,GAAGC,aAAcuH,IAC5B9H,IAED,SAAUF,GACT,YAiBA,SAASgC,GAAS4M,GAChB5O,EAAEuC,OAAOrB,KAAM0N,GACX1N,KAAK2N,QAAS3N,KAAKgF,OAAS4I,EAAQ5N,KAAKgF,SAhB/C,GAAI4I,GAAU,SAAU7L,GACtB,GAAI8L,KACJ,OAAO,UAAU/J,EAAMgK,GACjBD,EAAK/J,GACPgK,EAASD,EAAK/J,IAEd/B,EAAKjC,KAAKE,KAAM8D,EAAM,SAAUzD,GAC9BwN,EAAK/J,IAAS+J,EAAK/J,QAAaiK,OAAO1N,GACvCyN,EAASrN,MAAM,KAAMV,cAW7Be,GAASC,MAAQ,SAAUiN,EAAiBC,GAC1C,MAAOnP,GAAE2G,IAAIuI,EAAiB,SAAU1J,GACtC,GAAI4J,GAAc,GAAIpN,GAASwD,EAG/B,OAFA4J,GAAYlN,GAAKiN,EAAOjN,GACxBkN,EAAYjN,IAAMgN,EAAOhN,IAClBiN,KAIXpP,EAAEuC,OAAOP,EAASlB,WAKhBiF,MAAY,KACZsJ,QAAY,KACZnJ,OAAY,KAGZ3F,GAAY,KACZsO,OAAY,EACZhJ,QAAY,WAAc,OAAO,GACjCG,MAAY,EACZ6H,SAAY,SAAUhM,GAAO,MAAOA,IACpC6F,WAAY,OAGd1H,EAAEQ,GAAGC,aAAauB,SAAWA,GAE7B9B,IAED,SAAUF,GACT,YAiCA,SAASmE,MA/BT,GAAImL,GAAMC,KAAKD,KAAO,WAAc,OAAO,GAAIC,OAAOC,WAOlDC,EAAW,SAAUxM,EAAMyM,GAC7B,GAAIC,GAAS/O,EAAMiF,EAAS+J,EAAWC,EACnCC,EAAQ,WACV,GAAIC,GAAOT,IAAQM,CACRF,GAAPK,EACFJ,EAAUnD,WAAWsD,EAAOJ,EAAOK,IAEnCJ,EAAU,KACVE,EAAS5M,EAAKtB,MAAMkE,EAASjF,GAC7BiF,EAAUjF,EAAO,MAIrB,OAAO,YAOL,MANAiF,GAAU3E,KACVN,EAAOK,UACP2O,EAAYN,IACPK,IACHA,EAAUnD,WAAWsD,EAAOJ,IAEvBG,GAMX7P,GAAEuC,OAAO4B,EAAQrD,WAIfP,GAAW,KACXe,UAAW,KACXY,GAAW,KACXC,IAAW,KACXxB,OAAW,KAKXmC,WAAY,SAAUV,EAASd,EAAWX,GACxCO,KAAKgB,GAAYE,EACjBlB,KAAKiB,IAAYnC,EAAEoC,GACnBlB,KAAKX,GAAYe,EAAUf,GAAKW,KAAK8O,YAAYlO,KACjDZ,KAAKI,UAAYA,EACjBJ,KAAKP,OAAYA,EAEbO,KAAKP,OAAO8O,WACdvO,KAAK+O,SAAWR,EAASvO,KAAK+O,SAAU/O,KAAKP,OAAO8O,WAGtDvO,KAAKgG,eAGP5C,QAAS,WACPpD,KAAKiB,IAAIoC,IAAI,IAAMrD,KAAKX,IACxBW,KAAKiB,IAAMjB,KAAKgB,GAAKhB,KAAKI,UAAY,MAQxCgE,OAAQ,WACN,KAAM,IAAInF,OAAM,oBAIlBqG,iBAAkB,WAChB,GAAIsC,GAAW5H,KAAKgP,4BAChBhC,EAAShN,KAAKiB,IAAI+L,SAGlB1F,EAAUtH,KAAKP,OAAOkD,QAC1B,IAAI2E,EAAS,CACJA,YAAmBxI,KAAMwI,EAAUxI,EAAEwI,GAC3C,IAAI2H,GAAe3H,EAAQ4H,eAAelC,QAC1CA,GAAO5D,KAAO6F,EAAa7F,IAC3B4D,EAAOrF,MAAQsH,EAAatH,KAK/B,MAFAC,GAASwB,KAAO4D,EAAO5D,IACvBxB,EAASD,MAAQqF,EAAOrF,KACjBC,GAITpD,MAAO,WACLxE,KAAKiB,IAAIuD,SAMXwB,YAAa,WACXhG,KAAKiB,IAAIwF,GAAG,SAAWzG,KAAKX,GAAIP,EAAE+L,MAAM7K,KAAK+O,SAAU/O,QAGzD+O,SAAU,SAAUxK,GACdvE,KAAKmP,YAAY5K,IACrBvE,KAAKI,UAAUmD,QAAQvD,KAAK0D,0BAA0B,IAIxDyL,YAAa,SAAUC,GACrB,OAAQA,EAAWnF,SACjB,IAAK,GACL,IAAK,IACL,IAAK,IACL,IAAK,IACH,OAAO,EAEX,GAAImF,EAAWlF,QAAS,OAAQkF,EAAWnF,SACzC,IAAK,IACL,IAAK,IACH,OAAO,MAKfnL,EAAEQ,GAAGC,aAAa0D,QAAUA,GAC5BjE,IAED,SAAUF,GACT,YAMA,SAASuQ,GAASnO,EAASd,EAAWX,GACpCO,KAAK4B,WAAWV,EAASd,EAAWX,GAGtCX,EAAEuC,OAAOgO,EAASzP,UAAWd,EAAEQ,GAAGC,aAAa0D,QAAQrD,WAKrDwE,OAAQ,SAAUC,EAAOC,EAAUC,GACjC,GAAI+K,GAAMtP,KAAK0D,yBACX6L,EAAOvP,KAAKgB,GAAGqD,MAAMmL,UAAUxP,KAAKgB,GAAGmC,cACvCsM,EAAYnL,EAAS6J,QAAQ9J,EAAOE,EACf,oBAAdkL,KACL3Q,EAAE4Q,QAAQD,KACZF,EAAOE,EAAU,GAAKF,EACtBE,EAAYA,EAAU,IAExBH,EAAMA,EAAInB,QAAQ7J,EAASO,MAAO4K,GAClCzP,KAAKiB,IAAI0O,IAAIL,EAAMC,GACnBvP,KAAKgB,GAAG4O,eAAiB5P,KAAKgB,GAAGmC,aAAemM,EAAIzL,SAIxDH,uBAAwB,WACtB,MAAO1D,MAAKgB,GAAGqD,MAAMmL,UAAU,EAAGxP,KAAKgB,GAAGmC,eAM5C6L,0BAA2B,WACzB,GAAIa,GAAI/Q,EAAEQ,GAAGC,aAAauQ,oBAAoB9P,KAAKgB,GAAIhB,KAAKgB,GAAG4O,eAC/D,QACExG,IAAKyG,EAAEzG,IAAMpJ,KAAK+P,uBAAyB/P,KAAKiB,IAAIoI,YACpD1B,KAAMkI,EAAElI,KAAO3H,KAAKiB,IAAIqI,eAI5ByG,qBAAsB,WACpB,GAAIvC,GAAanC,SAASrL,KAAKiB,IAAIwG,IAAI,eAAgB,GACvD,IAAIuI,MAAMxC,GAAa,CAErB,GAAIyC,GAAajQ,KAAKgB,GAAGiP,WACrBC,EAAOxO,SAASgE,cAAc1F,KAAKgB,GAAGmP,UACtCC,EAAQpQ,KAAKgB,GAAGoP,KACpBF,GAAKG,aACH,QACA,sCAAwCD,EAAME,WAAa,cAAgBF,EAAMG,UAEnFL,EAAKM,UAAY,OACjBP,EAAWQ,YAAYP,GACvB1C,EAAa0C,EAAKQ,aAClBT,EAAWU,YAAYT,GAEzB,MAAO1C,MAIX1O,EAAEQ,GAAGC,aAAa8P,SAAWA,GAC7BrQ,IAED,SAAUF,GACT,YAIA,SAAS8R,GAAW1P,EAASd,EAAWX,GACtCO,KAAK4B,WAAWV,EAASd,EAAWX,GACpCX,EAAE,SAAW+R,EAAe,WAAWpJ,KACrCG,SAAU,WACVwB,IAAK,MACLzB,KAAM,QACLmJ,aAAa5P,GARlB,GAAI2P,GAAe,GAWnB/R,GAAEuC,OAAOuP,EAAWhR,UAAWd,EAAEQ,GAAGC,aAAa8P,SAASzP,WAIxDwE,OAAQ,SAAUC,EAAOC,EAAUC,GACjC,GAAI+K,GAAMtP,KAAK0D,yBACX6L,EAAOvP,KAAKgB,GAAGqD,MAAMmL,UAAUF,EAAIzL,QACnC4L,EAAYnL,EAAS6J,QAAQ9J,EAAOE,EACxC,IAAyB,mBAAdkL,GAA2B,CAChC3Q,EAAE4Q,QAAQD,KACZF,EAAOE,EAAU,GAAKF,EACtBE,EAAYA,EAAU,IAExBH,EAAMA,EAAInB,QAAQ7J,EAASO,MAAO4K,GAClCzP,KAAKiB,IAAI0O,IAAIL,EAAMC,GACnBvP,KAAKgB,GAAGwD,OACR,IAAIuM,GAAQ/Q,KAAKgB,GAAGgQ,iBACpBD,GAAME,UAAS,GACfF,EAAMG,QAAQ,YAAa5B,EAAIzL,QAC/BkN,EAAMI,UAAU,YAAa7B,EAAIzL,QACjCkN,EAAM3M,WAIVV,uBAAwB,WACtB1D,KAAKgB,GAAGwD,OACR,IAAIuM,GAAQrP,SAAS0P,UAAUC,aAC/BN,GAAMI,UAAU,aAAcnR,KAAKgB,GAAGqD,MAAMR,OAC5C,IAAIyN,GAAMP,EAAMvN,KAAK+N,MAAMV,EAC3B,OAAsB,KAAfS,EAAIzN,OAAeyN,EAAI,GAAKA,EAAI,MAI3CxS,EAAEQ,GAAGC,aAAaqR,WAAaA,GAC/B5R,IAMD,SAAUF,GACT,YAMA,SAAS0S,GAAiBtQ,EAASd,EAAWX,GAC5CO,KAAK4B,WAAWV,EAASd,EAAWX,GAGtCX,EAAEuC,OAAOmQ,EAAgB5R,UAAWd,EAAEQ,GAAGC,aAAa0D,QAAQrD,WAM5DwE,OAAQ,SAAUC,EAAOC,EAAUC,GACjC,GAAI+K,GAAMtP,KAAK0D,yBACX+N,EAAMtL,OAAOuL,eACbX,EAAQU,EAAIE,WAAW,GACvBP,EAAYL,EAAMa,YACtBR,GAAUS,mBAAmBd,EAAMe,eACnC,IAAIC,GAAUX,EAAU5O,WACpB+M,EAAOwC,EAAQvC,UAAUuB,EAAMiB,aAC/BvC,EAAYnL,EAAS6J,QAAQ9J,EAAOE,EACxC,IAAyB,mBAAdkL,GAA2B,CAChC3Q,EAAE4Q,QAAQD,KACZF,EAAOE,EAAU,GAAKF,EACtBE,EAAYA,EAAU,IAExBH,EAAMA,EAAInB,QAAQ7J,EAASO,MAAO4K,GAClCsB,EAAMc,mBAAmBd,EAAMe,gBAC/Bf,EAAMkB,gBAGN,IAAIC,GAAaxQ,SAASgE,cAAc,MACxCwM,GAAW1B,UAAYlB,CACvB,IAAI6C,GAAczQ,SAASgE,cAAc,MACzCyM,GAAY3B,UAAYjB,CAMxB,KAHA,GACI6C,GACAC,EAFAC,EAAW5Q,SAAS6Q,yBAGjBH,EAAYF,EAAWM,YAC7BH,EAAYC,EAAS7B,YAAY2B,EAElC,MAAOA,EAAYD,EAAYK,YAC9BF,EAAS7B,YAAY2B,EAItBrB,GAAM0B,WAAWH,GACjBvB,EAAM2B,cAAcL,GAEpBtB,EAAME,UAAS,GACfQ,EAAIkB,kBACJlB,EAAImB,SAAS7B,KAgBjB/B,0BAA2B,WACzB,GAAI+B,GAAQ5K,OAAOuL,eAAeC,WAAW,GAAGC,aAC5CiB,EAAOnR,SAASgE,cAAc,OAClCqL,GAAM0B,WAAWI,GACjB9B,EAAMc,mBAAmBgB,GACzB9B,EAAMkB,gBACN,IAAIa,GAAQhU,EAAE+T,GACVjL,EAAWkL,EAAM9F,QAKrB,OAJApF,GAASD,MAAQ3H,KAAKiB,IAAI+L,SAASrF,KACnCC,EAASwB,KAAO0J,EAAMhN,SAAW9F,KAAKiB,IAAI+L,SAAS5D,IACnDxB,EAAS4F,WAAasF,EAAMhN,SAC5BgN,EAAM5K,SACCN,GAWTlE,uBAAwB,WACtB,GAAIqN,GAAQ5K,OAAOuL,eAAeC,WAAW,GACzCP,EAAYL,EAAMa,YAEtB,OADAR,GAAUS,mBAAmBd,EAAMe,gBAC5BV,EAAU5O,WAAWgN,UAAU,EAAGuB,EAAMiB,gBAInDlT,EAAEQ,GAAGC,aAAaiS,gBAAkBA,GACpCxS,GAuBD,SAAUF,GAmDX,QAASgR,GAAoB5O,EAAS0G,EAAU8F,GAC9C,IAAIqF,EACF,KAAM,IAAI9T,OAAM,iFAGlB,IAAI+T,GAAQtF,GAAWA,EAAQsF,QAAS,CACxC,IAAIA,EAAO,CACT,GAAIhS,GAAKU,SAASuR,cAAc,4CAC3BjS,IAAOA,EAAGiP,WAAWU,YAAY3P,GAIxC,GAAIkS,GAAMxR,SAASgE,cAAc,MACjCwN,GAAI7T,GAAK,2CACTqC,SAASyR,KAAK1C,YAAYyC,EAE1B,IAAI9C,GAAQ8C,EAAI9C,MACZgD,EAAWjN,OAAOkN,iBAAkBA,iBAAiBnS,GAAWA,EAAQoS,YAG5ElD,GAAMmD,WAAa,WACM,UAArBrS,EAAQiP,WACVC,EAAMoD,SAAW,cAGnBpD,EAAMxI,SAAW,WACZoL,IACH5C,EAAMqD,WAAa,UAGrBC,EAAWC,QAAQ,SAAUC,GAC3BxD,EAAMwD,GAAQR,EAASQ,KAGrBC,EAEE3S,EAAQ4S,aAAezI,SAAS+H,EAAStN,UAC3CsK,EAAM2D,UAAY,UAEpB3D,EAAM4D,SAAW,SAGnBd,EAAIe,YAAc/S,EAAQmD,MAAMmL,UAAU,EAAG5H,GAEpB,UAArB1G,EAAQiP,WACV+C,EAAIe,YAAcf,EAAIe,YAAY9F,QAAQ,MAAO,KAEnD,IAAI+F,GAAOxS,SAASgE,cAAc,OAMlCwO,GAAKD,YAAc/S,EAAQmD,MAAMmL,UAAU5H,IAAa,IACxDsL,EAAIzC,YAAYyD,EAEhB,IAAIC,IACF/K,IAAK8K,EAAKE,UAAY/I,SAAS+H,EAAyB,gBACxDzL,KAAMuM,EAAKG,WAAahJ,SAAS+H,EAA0B,iBAS7D,OANIJ,GACFkB,EAAK9D,MAAMkE,gBAAkB,OAE7B5S,SAASyR,KAAKxC,YAAYuC,GAGrBiB,EAhHT,GAAIT,IACF,YACA,YACA,QACA,SACA,YACA,YAEA,iBACA,mBACA,oBACA,kBACA,cAEA,aACA,eACA,gBACA,cAGA,YACA,cACA,aACA,cACA,WACA,iBACA,aACA,aAEA,YACA,gBACA,aACA,iBAEA,gBACA,cAEA,UACA,cAIEX,EAA+B,mBAAX5M,QACpB0N,EAAad,GAAuC,MAA1B5M,OAAOoO,eAwErCzV,GAAEQ,GAAGC,aAAauQ,oBAAsBA,GAEtC9Q,GAEKA"}
\ No newline at end of file
diff --git a/view/js/jquery.textinputs.js b/view/js/jquery.textinputs.js
new file mode 100644 (file)
index 0000000..fd6d145
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ Rangy Text Inputs, a cross-browser textarea and text input library plug-in for jQuery.
+
+ Part of Rangy, a cross-browser JavaScript range and selection library
+ http://code.google.com/p/rangy/
+
+ Depends on jQuery 1.0 or later.
+
+ Copyright 2010, Tim Down
+ Licensed under the MIT license.
+ Version: 0.1.205
+ Build date: 5 November 2010
+*/
+(function(n){function o(e,g){var a=typeof e[g];return a==="function"||!!(a=="object"&&e[g])||a=="unknown"}function p(e,g,a){if(g<0)g+=e.value.length;if(typeof a=="undefined")a=g;if(a<0)a+=e.value.length;return{start:g,end:a}}function k(){return typeof document.body=="object"&&document.body?document.body:document.getElementsByTagName("body")[0]}var i,h,q,l,r,s,t,u,m;n(document).ready(function(){function e(a,b){return function(){var c=this.jquery?this[0]:this,d=c.nodeName.toLowerCase();if(c.nodeType==
+1&&(d=="textarea"||d=="input"&&c.type=="text")){c=[c].concat(Array.prototype.slice.call(arguments));c=a.apply(this,c);if(!b)return c}if(b)return this}}var g=document.createElement("textarea");k().appendChild(g);if(typeof g.selectionStart!="undefined"&&typeof g.selectionEnd!="undefined"){i=function(a){return{start:a.selectionStart,end:a.selectionEnd,length:a.selectionEnd-a.selectionStart,text:a.value.slice(a.selectionStart,a.selectionEnd)}};h=function(a,b,c){b=p(a,b,c);a.selectionStart=b.start;a.selectionEnd=
+b.end};m=function(a,b){if(b)a.selectionEnd=a.selectionStart;else a.selectionStart=a.selectionEnd}}else if(o(g,"createTextRange")&&typeof document.selection=="object"&&document.selection&&o(document.selection,"createRange")){i=function(a){var b=0,c=0,d,f,j;if((j=document.selection.createRange())&&j.parentElement()==a){f=a.value.length;d=a.value.replace(/\r\n/g,"\n");c=a.createTextRange();c.moveToBookmark(j.getBookmark());j=a.createTextRange();j.collapse(false);if(c.compareEndPoints("StartToEnd",j)>
+-1)b=c=f;else{b=-c.moveStart("character",-f);b+=d.slice(0,b).split("\n").length-1;if(c.compareEndPoints("EndToEnd",j)>-1)c=f;else{c=-c.moveEnd("character",-f);c+=d.slice(0,c).split("\n").length-1}}}return{start:b,end:c,length:c-b,text:a.value.slice(b,c)}};h=function(a,b,c){b=p(a,b,c);c=a.createTextRange();var d=b.start-(a.value.slice(0,b.start).split("\r\n").length-1);c.collapse(true);if(b.start==b.end)c.move("character",d);else{c.moveEnd("character",b.end-(a.value.slice(0,b.end).split("\r\n").length-
+1));c.moveStart("character",d)}c.select()};m=function(a,b){var c=document.selection.createRange();c.collapse(b);c.select()}}else{k().removeChild(g);window.console&&window.console.log&&window.console.log("TextInputs module for Rangy not supported in your browser. Reason: No means of finding text input caret position");return}k().removeChild(g);l=function(a,b,c,d){var f;if(b!=c){f=a.value;a.value=f.slice(0,b)+f.slice(c)}d&&h(a,b,b)};q=function(a){var b=i(a);l(a,b.start,b.end,true)};u=function(a){var b=
+i(a),c;if(b.start!=b.end){c=a.value;a.value=c.slice(0,b.start)+c.slice(b.end)}h(a,b.start,b.start);return b.text};r=function(a,b,c,d){var f=a.value;a.value=f.slice(0,c)+b+f.slice(c);if(d){b=c+b.length;h(a,b,b)}};s=function(a,b){var c=i(a),d=a.value;a.value=d.slice(0,c.start)+b+d.slice(c.end);c=c.start+b.length;h(a,c,c)};t=function(a,b,c){var d=i(a),f=a.value;a.value=f.slice(0,d.start)+b+d.text+c+f.slice(d.end);b=d.start+b.length;h(a,b,b+d.length)};n.fn.extend({getSelection:e(i,false),setSelection:e(h,
+true),collapseSelection:e(m,true),deleteSelectedText:e(q,true),deleteText:e(l,true),extractSelectedText:e(u,false),insertText:e(r,true),replaceSelectedText:e(s,true),surroundSelectedText:e(t,true)})})})(jQuery);
\ No newline at end of file
diff --git a/view/js/main.js b/view/js/main.js
new file mode 100644 (file)
index 0000000..77c240f
--- /dev/null
@@ -0,0 +1,922 @@
+       function resizeIframe(obj) {
+               //obj.style.height = 0;
+               _resizeIframe(obj, 0);
+       }
+
+       function _resizeIframe(obj, desth) {
+               var h = obj.style.height;
+               var ch = obj.contentWindow.document.body.scrollHeight;
+               if (h == (ch + 'px')) {
+                       return;
+               }
+               if (desth == ch && ch>0) {
+                       obj.style.height  = ch + 'px';
+               }
+               setTimeout(_resizeIframe, 100, obj, ch);
+       }
+
+       function openClose(theID) {
+               if(document.getElementById(theID).style.display == "block") {
+                       document.getElementById(theID).style.display = "none"
+               }
+               else {
+                       document.getElementById(theID).style.display = "block"
+               }
+       }
+
+       function openMenu(theID) {
+               var el = document.getElementById(theID)
+               if (el) {
+                       el.style.display = "block";
+               }
+       }
+
+       function closeMenu(theID) {
+               var el = document.getElementById(theID)
+               if (el) {
+                       el.style.display = "none";
+               }
+       }
+
+       function decodeHtml(html) {
+               var txt = document.createElement("textarea");
+               txt.innerHTML = html;
+               return txt.value;
+       }
+
+
+       var src = null;
+       var prev = null;
+       var livetime = null;
+       var force_update = false;
+       var stopped = false;
+       var totStopped = false;
+       var timer = null;
+       var pr = 0;
+       var liking = 0;
+       var in_progress = false;
+       var langSelect = false;
+       var commentBusy = false;
+       var last_popup_menu = null;
+       var last_popup_button = null;
+       var lockLoadContent = false;
+
+       $(function() {
+               $.ajaxSetup({cache: false});
+
+               /* setup comment textarea buttons */
+               /* comment textarea buttons needs some "data-*" attributes to work:
+                *              data-role="insert-formatting" : to mark the element as a formatting button
+                *              data-bbcode="<string>" : name of the bbcode element to insert. insertFormatting() will insert it as "[name][/name]"
+                *              data-id="<string>" : id of the comment, used to find other comment-related element, like the textarea
+                * */
+               $('body').on('click','[data-role="insert-formatting"]', function(e) {
+                       e.preventDefault();
+                       var o = $(this);
+                       var bbcode  = o.data('bbcode');
+                       var id = o.data('id');
+                       if (bbcode=="img") {
+                               Dialog.doImageBrowser("comment", id);
+                               return;
+                       }
+                       insertFormatting(bbcode, id);
+               });
+
+               /* event from comment textarea button popups */
+               /* insert returned bbcode at cursor position or replace selected text */
+               $("body").on("fbrowser.image.comment", function(e, filename, bbcode, id) {
+                       $.colorbox.close();
+                       var textarea = document.getElementById("comment-edit-text-" +id);
+                       var start = textarea.selectionStart;
+                       var end = textarea.selectionEnd;
+                       textarea.value = textarea.value.substring(0, start) + bbcode + textarea.value.substring(end, textarea.value.length);
+                       $(textarea).trigger('change');
+               });
+
+
+
+               /* setup onoff widgets */
+               $(".onoff input").each(function(){
+                       val = $(this).val();
+                       id = $(this).attr("id");
+                       $("#"+id+"_onoff ."+ (val==0?"on":"off")).addClass("hidden");
+
+               });
+               $(".onoff > a").click(function(event){
+                       event.preventDefault();
+                       var input = $(this).siblings("input");
+                       var val = 1-input.val();
+                       var id = input.attr("id");
+                       $("#"+id+"_onoff ."+ (val==0?"on":"off")).addClass("hidden");
+                       $("#"+id+"_onoff ."+ (val==1?"on":"off")).removeClass("hidden");
+                       input.val(val);
+               });
+
+               /* popup menus */
+               function close_last_popup_menu() {
+                       if(last_popup_menu) {
+                               last_popup_menu.hide();
+                               last_popup_menu.off('click', function(e) {e.stopPropagation()});
+                               last_popup_button.removeClass("selected");
+                               last_popup_menu = null;
+                               last_popup_button = null;
+                       }
+               }
+               $('a[rel^="#"]').click(function(e){
+                       e.preventDefault();
+                       var parent = $(this).parent();
+                       var isSelected = (last_popup_button && parent.attr('id') == last_popup_button.attr('id'));
+                       close_last_popup_menu();
+                       if(isSelected) return false;
+                       menu = $( $(this).attr('rel') );
+                       e.preventDefault();
+                       e.stopPropagation();
+                       if (menu.attr('popup')=="false") return false;
+                       parent.toggleClass("selected");
+                       menu.toggle();
+                       if (menu.css("display") == "none") {
+                               last_popup_menu = null;
+                               last_popup_button = null;
+                       } else {
+                               last_popup_menu = menu;
+                               last_popup_menu.on('click', function(e) {e.stopPropagation()});
+                               last_popup_button = parent;
+                               $('#nav-notifications-menu').perfectScrollbar('update');
+                       }
+                       return false;
+               });
+               $('html').click(function() {
+                       close_last_popup_menu();
+               });
+
+               // fancyboxes
+               $("a.popupbox").colorbox({
+                       'inline' : true,
+                       'transition' : 'elastic',
+                       'maxWidth' : '100%'
+               });
+               $("a.ajax-popupbox").colorbox({
+                       'transition' : 'elastic',
+                       'maxWidth' : '100%'
+               });
+
+               /* notifications template */
+               var notifications_tpl= unescape($("#nav-notifications-template[rel=template]").html());
+               var notifications_all = unescape($('<div>').append( $("#nav-notifications-see-all").clone() ).html()); //outerHtml hack
+               var notifications_mark = unescape($('<div>').append( $("#nav-notifications-mark-all").clone() ).html()); //outerHtml hack
+               var notifications_empty = unescape($("#nav-notifications-menu").html());
+
+               /* enable perfect-scrollbars for different elements */
+               $('#nav-notifications-menu, aside').perfectScrollbar();
+
+               /* nav update event  */
+               $('nav').bind('nav-update', function(e, data){
+                       var invalid = data.invalid || 0;
+                       if(invalid == 1) { window.location.href=window.location.href }
+
+                       ['net', 'home', 'intro', 'mail', 'events', 'birthdays', 'notify'].forEach(function(type) {
+                               var number = data[type];
+                               if (number == 0) {
+                                       number = '';
+                                       $('#' + type + '-update').removeClass('show');
+                               } else {
+                                       $('#' + type + '-update').addClass('show');
+                               }
+                               $('#' + type + '-update').text(number);
+                       });
+
+                       var intro = data['intro'];
+                       if(intro == 0) { intro = ''; $('#intro-update-li').removeClass('show') } else { $('#intro-update-li').addClass('show') }
+                       $('#intro-update-li').html(intro);
+
+                       var mail = data['mail'];
+                       if(mail == 0) { mail = ''; $('#mail-update-li').removeClass('show') } else { $('#mail-update-li').addClass('show') }
+                       $('#mail-update-li').html(mail);
+
+                       $(".sidebar-group-li .notify").removeClass("show");
+                       $(data.groups).each(function(key, group) {
+                               var gid = group.id;
+                               var gcount = group.count;
+                               $(".group-"+gid+" .notify").addClass("show").text(gcount);
+                       });
+
+                       $(".forum-widget-entry .notify").removeClass("show");
+                       $(data.forums).each(function(key, forum) {
+                               var fid = forum.id;
+                               var fcount = forum.count;
+                               $(".forum-"+fid+" .notify").addClass("show").text(fcount);
+                       });
+
+                       if (data.notifications.length == 0) {
+                               $("#nav-notifications-menu").html(notifications_empty);
+                       } else {
+                               var nnm = $("#nav-notifications-menu");
+                               nnm.html(notifications_all + notifications_mark);
+
+                               var lastItemStorageKey = "notification-lastitem:" + localUser;
+                               var notification_lastitem = parseInt(localStorage.getItem(lastItemStorageKey));
+                               var notification_id = 0;
+
+                               // Insert notifs into the notifications-menu
+                               $(data.notifications).each(function(key, notif){
+                                       var text = notif.message.format('<span class="contactname">' + notif.name + '</span>');
+                                       var contact = ('<a href="' + notif.url + '"><span class="contactname">' + notif.name + '</span></a>');
+                                       var seenclass = (notif.seen == 1) ? "notify-seen" : "notify-unseen";
+                                       var html = notifications_tpl.format(
+                                               notif.href,                     // {0}  // link to the source
+                                               notif.photo,                    // {1}  // photo of the contact
+                                               text,                           // {2}  // preformatted text (autor + text)
+                                               notif.date,                     // {3}  // date of notification (time ago)
+                                               seenclass,                      // {4}  // visited status of the notification
+                                               new Date(notif.timestamp*1000), // {5}  // date of notification
+                                               notif.url,                      // {6}  // profile url of the contact
+                                               notif.message.format(contact),  // {7}  // preformatted html (text including author profile url)
+                                               ''                              // {8}  // Deprecated
+                                       );
+                                       nnm.append(html);
+                               });
+
+                               // Desktop Notifications
+                               $(data.notifications.reverse()).each(function(key, e){
+                                       notification_id = parseInt(e.timestamp);
+                                       if (notification_lastitem !== null && notification_id > notification_lastitem && Number(e.seen) === 0) {
+                                               if (getNotificationPermission() === "granted") {
+                                                       var notification = new Notification(document.title, {
+                                                                                         body: decodeHtml(e.message.replace('&rarr; ', '').format(e.name)),
+                                                                                         icon: e.photo,
+                                                                                        });
+                                                       notification['url'] = e.href;
+                                                       notification.addEventListener("click", function(ev){
+                                                               window.location = ev.target.url;
+                                                       });
+                                               }
+                                       }
+
+                               });
+                               notification_lastitem = notification_id;
+                               localStorage.setItem(lastItemStorageKey, notification_lastitem)
+
+                               $("img[data-src]", nnm).each(function(i, el){
+                                       // Add src attribute for images with a data-src attribute
+                                       // However, don't bother if the data-src attribute is empty, because
+                                       // an empty "src" tag for an image will cause some browsers
+                                       // to prefetch the root page of the Friendica hub, which will
+                                       // unnecessarily load an entire profile/ or network/ page
+                                       if($(el).data("src") != '') $(el).attr('src', $(el).data("src"));
+                               });
+                       }
+
+                       var notif = data['notify'];
+                       if (notif > 0){
+                               $("#nav-notifications-linkmenu").addClass("on");
+                       } else {
+                               $("#nav-notifications-linkmenu").removeClass("on");
+                       }
+
+                       $(data.sysmsgs.notice).each(function(key, message){
+                               $.jGrowl(message, {sticky: true, theme: 'notice'});
+                       });
+                       $(data.sysmsgs.info).each(function(key, message){
+                               $.jGrowl(message, {sticky: false, theme: 'info', life: 5000});
+                       });
+
+                       // Update the js scrollbars
+                       $('#nav-notifications-menu').perfectScrollbar('update');
+
+               });
+
+               NavUpdate();
+               // Allow folks to stop the ajax page updates with the pause/break key
+               $(document).keydown(function(event) {
+                       if(event.keyCode == '8') {
+                               var target = event.target || event.srcElement;
+                               if (!/input|textarea/i.test(target.nodeName)) {
+                                       return false;
+                               }
+                       }
+                       if(event.keyCode == '19' || (event.ctrlKey && event.which == '32')) {
+                               event.preventDefault();
+                               if(stopped == false) {
+                                       stopped = true;
+                                       if (event.ctrlKey) {
+                                               totStopped = true;
+                                       }
+                                       $('#pause').html('<img src="images/pause.gif" alt="pause" style="border: 1px solid black;" />');
+                               } else {
+                                       unpause();
+                               }
+                       } else {
+                               if (!totStopped) {
+                                       unpause();
+                               }
+                       }
+               });
+
+               // Scroll to the next/previous thread when pressing J and K
+               $(document).keydown(function (event) {
+                       var threads = $('.thread_level_1');
+                       if ((event.keyCode === 74 || event.keyCode === 75) && !$(event.target).is('textarea, input')) {
+                               var scrollTop = $(window).scrollTop();
+                               if (event.keyCode === 75) {
+                                       threads = $(threads.get().reverse());
+                               }
+                               threads.each(function(key, item) {
+                                       var comparison;
+                                       var top = $(item).offset().top - 100;
+                                       if (event.keyCode === 74) {
+                                               comparison = top > scrollTop + 1;
+                                       } else if (event.keyCode === 75) {
+                                               comparison = top < scrollTop - 1;
+                                       }
+                                       if (comparison) {
+                                               $('html, body').animate({ scrollTop: top }, 200);
+                                               return false;
+                                       }
+                               });
+                       }
+               });
+
+               // Set an event listener for infinite scroll
+               if(typeof infinite_scroll !== 'undefined') {
+                       $(window).scroll(function(e){
+                               if ($(document).height() != $(window).height()) {
+                                       // First method that is expected to work - but has problems with Chrome
+                                       if ($(window).scrollTop() > ($(document).height() - $(window).height() * 1.5))
+                                               loadScrollContent();
+                               } else {
+                                       // This method works with Chrome - but seems to be much slower in Firefox
+                                       if ($(window).scrollTop() > (($("section").height() + $("header").height() + $("footer").height()) - $(window).height() * 1.5))
+                                               loadScrollContent();
+                               }
+                       });
+               }
+
+
+       });
+
+       function NavUpdate() {
+
+               if (!stopped) {
+                       var pingCmd = 'ping?format=json' + ((localUser != 0) ? '&f=&uid=' + localUser : '');
+                       $.get(pingCmd, function(data) {
+                               if (data.result) {
+                                       // send nav-update event
+                                       $('nav').trigger('nav-update', data.result);
+
+                                       // start live update
+                                       ['network', 'profile', 'community', 'notes', 'display'].forEach(function (src) {
+                                               if ($('#live-' + src).length) {
+                                                       liveUpdate(src);
+                                               }
+                                       });
+                                       if ($('#live-photos').length) {
+                                               if (liking) {
+                                                       liking = 0;
+                                                       window.location.href = window.location.href;
+                                               }
+                                       }
+                               }
+                       }) ;
+               }
+               timer = setTimeout(NavUpdate, updateInterval);
+       }
+
+       function liveUpdate(src) {
+               if((src == null) || (stopped) || (! profile_uid)) { $('.like-rotator').hide(); return; }
+               if(($('.comment-edit-text-full').length) || (in_progress)) {
+                       if(livetime) {
+                               clearTimeout(livetime);
+                       }
+                       livetime = setTimeout(function() {liveUpdate(src)}, 5000);
+                       return;
+               }
+               if(livetime != null)
+                       livetime = null;
+
+               prev = 'live-' + src;
+
+               in_progress = true;
+
+               if ($(document).scrollTop() == 0)
+                       force_update = true;
+
+               var udargs = ((netargs.length) ? '/' + netargs : '');
+               var update_url = 'update_' + src + udargs + '&p=' + profile_uid + '&page=' + profile_page + '&force=' + ((force_update) ? 1 : 0);
+
+               $.get(update_url,function(data) {
+                       in_progress = false;
+                       force_update = false;
+                       //                      $('.collapsed-comments',data).each(function() {
+                       //      var ident = $(this).attr('id');
+                       //      var is_hidden = $('#' + ident).is(':hidden');
+                       //      if($('#' + ident).length) {
+                       //              $('#' + ident).replaceWith($(this));
+                       //              if(is_hidden)
+                       //                      $('#' + ident).hide();
+                       //      }
+                       //});
+
+                       // add a new thread
+                       $('.toplevel_item',data).each(function() {
+                               var ident = $(this).attr('id');
+
+                               if($('#' + ident).length == 0 && profile_page == 1) {
+                                       $('img',this).each(function() {
+                                               $(this).attr('src',$(this).attr('dst'));
+                                       });
+                                       $('#' + prev).after($(this));
+                               }
+                               else {
+                                       // Find out if the hidden comments are open, so we can keep it that way
+                                       // if a new comment has been posted
+                                       var id = $('.hide-comments-total', this).attr('id');
+                                       if(typeof id != 'undefined') {
+                                               id = id.split('-')[3];
+                                               var commentsOpen = $("#collapsed-comments-" + id).is(":visible");
+                                       }
+
+                                       $('img',this).each(function() {
+                                               $(this).attr('src',$(this).attr('dst'));
+                                       });
+                                       //vScroll = $(document).scrollTop();
+                                       $('html').height($('html').height());
+                                       $('#' + ident).replaceWith($(this));
+
+                                       if(typeof id != 'undefined') {
+                                               if(commentsOpen) showHideComments(id);
+                                       }
+                                       $('html').height('auto');
+                                       //$(document).scrollTop(vScroll);
+                               }
+                               prev = ident;
+                       });
+
+                       // reset vars for inserting individual items
+
+                       /*                      prev = 'live-' + src;
+
+                       $('.wall-item-outside-wrapper',data).each(function() {
+                               var ident = $(this).attr('id');
+
+                               if($('#' + ident).length == 0 && prev != 'live-' + src) {
+                                               $('img',this).each(function() {
+                                                       $(this).attr('src',$(this).attr('dst'));
+                                               });
+                                               $('#' + prev).after($(this));
+                               }
+                               else {
+                                       $('#' + ident + ' ' + '.wall-item-ago').replaceWith($(this).find('.wall-item-ago'));
+                                       if($('#' + ident + ' ' + '.comment-edit-text-empty').length)
+                                               $('#' + ident + ' ' + '.wall-item-comment-wrapper').replaceWith($(this).find('.wall-item-comment-wrapper'));
+                                       $('#' + ident + ' ' + '.hide-comments-total').replaceWith($(this).find('.hide-comments-total'));
+                                       $('#' + ident + ' ' + '.wall-item-like').replaceWith($(this).find('.wall-item-like'));
+                                       $('#' + ident + ' ' + '.wall-item-dislike').replaceWith($(this).find('.wall-item-dislike'));
+                                       $('#' + ident + ' ' + '.my-comment-photo').each(function() {
+                                               $(this).attr('src',$(this).attr('dst'));
+                                       });
+                               }
+                               prev = ident;
+                       });
+                       */
+                       $('.like-rotator').hide();
+                       if(commentBusy) {
+                               commentBusy = false;
+                               $('body').css('cursor', 'auto');
+                       }
+                       /* autocomplete @nicknames */
+                       $(".comment-edit-form  textarea").editor_autocomplete(baseurl+"/acl");
+                       /* autocomplete bbcode */
+                       $(".comment-edit-form  textarea").bbco_autocomplete('bbcode');
+               });
+       }
+
+       function imgbright(node) {
+               $(node).removeClass("drophide").addClass("drop");
+       }
+
+       function imgdull(node) {
+               $(node).removeClass("drop").addClass("drophide");
+       }
+
+       // Since our ajax calls are asynchronous, we will give a few
+       // seconds for the first ajax call (setting like/dislike), then
+       // run the updater to pick up any changes and display on the page.
+       // The updater will turn any rotators off when it's done.
+       // This function will have returned long before any of these
+       // events have completed and therefore there won't be any
+       // visible feedback that anything changed without all this
+       // trickery. This still could cause confusion if the "like" ajax call
+       // is delayed and NavUpdate runs before it completes.
+
+       function dolike(ident,verb) {
+               unpause();
+               $('#like-rotator-' + ident.toString()).show();
+               $.get('like/' + ident.toString() + '?verb=' + verb, NavUpdate );
+               liking = 1;
+               force_update = true;
+       }
+
+       function dosubthread(ident) {
+               unpause();
+               $('#like-rotator-' + ident.toString()).show();
+               $.get('subthread/' + ident.toString(), NavUpdate );
+               liking = 1;
+       }
+
+
+       function dostar(ident) {
+               ident = ident.toString();
+               $('#like-rotator-' + ident).show();
+               $.get('starred/' + ident, function(data) {
+                       if(data.match(/1/)) {
+                               $('#starred-' + ident).addClass('starred');
+                               $('#starred-' + ident).removeClass('unstarred');
+                               $('#star-' + ident).addClass('hidden');
+                               $('#unstar-' + ident).removeClass('hidden');
+                       }
+                       else {
+                               $('#starred-' + ident).addClass('unstarred');
+                               $('#starred-' + ident).removeClass('starred');
+                               $('#star-' + ident).removeClass('hidden');
+                               $('#unstar-' + ident).addClass('hidden');
+                       }
+                       $('#like-rotator-' + ident).hide();
+               });
+       }
+
+       function doignore(ident) {
+               ident = ident.toString();
+               $('#like-rotator-' + ident).show();
+               $.get('ignored/' + ident, function(data) {
+                       if(data.match(/1/)) {
+                               $('#ignored-' + ident).addClass('ignored');
+                               $('#ignored-' + ident).removeClass('unignored');
+                               $('#ignore-' + ident).addClass('hidden');
+                               $('#unignore-' + ident).removeClass('hidden');
+                       }
+                       else {
+                               $('#ignored-' + ident).addClass('unignored');
+                               $('#ignored-' + ident).removeClass('ignored');
+                               $('#ignore-' + ident).removeClass('hidden');
+                               $('#unignore-' + ident).addClass('hidden');
+                       }
+                       $('#like-rotator-' + ident).hide();
+               });
+       }
+
+       function getPosition(e) {
+               var cursor = {x:0, y:0};
+               if ( e.pageX || e.pageY  ) {
+                       cursor.x = e.pageX;
+                       cursor.y = e.pageY;
+               }
+               else {
+                       if( e.clientX || e.clientY ) {
+                               cursor.x = e.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft) - document.documentElement.clientLeft;
+                               cursor.y = e.clientY + (document.documentElement.scrollTop  || document.body.scrollTop)  - document.documentElement.clientTop;
+                       }
+                       else {
+                               if( e.x || e.y ) {
+                                       cursor.x = e.x;
+                                       cursor.y = e.y;
+                               }
+                       }
+               }
+               return cursor;
+       }
+
+       var lockvisible = false;
+
+       function lockview(event,id) {
+               event = event || window.event;
+               cursor = getPosition(event);
+               if(lockvisible) {
+                       lockviewhide();
+               }
+               else {
+                       lockvisible = true;
+                       $.get('lockview/' + id, function(data) {
+                               $('#panel').html(data);
+                               $('#panel').css({ 'left': cursor.x + 5 , 'top': cursor.y + 5});
+                               $('#panel').show();
+                       });
+               }
+       }
+
+       function lockviewhide() {
+               lockvisible = false;
+               $('#panel').hide();
+       }
+
+       function post_comment(id) {
+               unpause();
+               commentBusy = true;
+               $('body').css('cursor', 'wait');
+               $("#comment-preview-inp-" + id).val("0");
+               $.post(
+                       "item",
+                       $("#comment-edit-form-" + id).serialize(),
+                       function(data) {
+                               if(data.success) {
+                                       $("#comment-edit-wrapper-" + id).hide();
+                                       $("#comment-edit-text-" + id).val('');
+                                       var tarea = document.getElementById("comment-edit-text-" + id);
+                                       if(tarea)
+                                               commentClose(tarea,id);
+                                       if(timer) clearTimeout(timer);
+                                       timer = setTimeout(NavUpdate,10);
+                                       force_update = true;
+                               }
+                               if(data.reload) {
+                                       window.location.href=data.reload;
+                               }
+                       },
+                       "json"
+               );
+               return false;
+       }
+
+
+       function preview_comment(id) {
+               $("#comment-preview-inp-" + id).val("1");
+               $("#comment-edit-preview-" + id).show();
+               $.post(
+                       "item",
+                       $("#comment-edit-form-" + id).serialize(),
+                       function(data) {
+                               if(data.preview) {
+                                       $("#comment-edit-preview-" + id).html(data.preview);
+                                       $("#comment-edit-preview-" + id + " a").click(function() { return false; });
+                               }
+                       },
+                       "json"
+               );
+               return true;
+       }
+
+
+
+       function showHideComments(id) {
+               if( $("#collapsed-comments-" + id).is(":visible")) {
+                       $("#collapsed-comments-" + id).hide();
+                       $("#hide-comments-" + id).html(window.showMore);
+               }
+               else {
+                       $("#collapsed-comments-" + id).show();
+                       $("#hide-comments-" + id).html(window.showFewer);
+               }
+       }
+
+
+
+       function preview_post() {
+               $("#jot-preview").val("1");
+               $("#jot-preview-content").show();
+               $.post(
+                       "item",
+                       $("#profile-jot-form").serialize(),
+                       function(data) {
+                               if(data.preview) {
+                                       $("#jot-preview-content").html(data.preview);
+                                       $("#jot-preview-content" + " a").click(function() { return false; });
+                               }
+                       },
+                       "json"
+               );
+               $("#jot-preview").val("0");
+               return true;
+       }
+
+
+       function unpause() {
+               // unpause auto reloads if they are currently stopped
+               totStopped = false;
+               stopped = false;
+               $('#pause').html('');
+       }
+
+       // load more network content (used for infinite scroll)
+       function loadScrollContent() {
+               if (lockLoadContent) return;
+               lockLoadContent = true;
+
+               $("#scroll-loader").fadeIn('normal');
+
+               // the page number to load is one higher than the actual
+               // page number
+               infinite_scroll.pageno+=1;
+
+               match = $("span.received").last();
+               if (match.length > 0) {
+                       received = match[0].innerHTML;
+               } else {
+                       received = "0000-00-00 00:00:00";
+               }
+
+               match = $("span.created").last();
+               if (match.length > 0) {
+                       created = match[0].innerHTML;
+               } else {
+                       created = "0000-00-00 00:00:00";
+               }
+
+               match = $("span.commented").last();
+               if (match.length > 0) {
+                       commented = match[0].innerHTML;
+               } else {
+                       commented = "0000-00-00 00:00:00";
+               }
+
+               match = $("span.id").last();
+               if (match.length > 0) {
+                       id = match[0].innerHTML;
+               } else {
+                       id = "0";
+               }
+               // console.log("Received: " + received + " - Commented: " + commented+ " - Created: " + created + " - ID: " + id);
+
+               // get the raw content from the next page and insert this content
+               // right before "#conversation-end"
+               $.get('network?mode=raw' + infinite_scroll.reload_uri + '&last_received=' + received + '&last_commented=' + commented + '&last_created=' + created + '&last_id=' + id + '&page=' + infinite_scroll.pageno, function(data) {
+                       $("#scroll-loader").hide();
+                       if ($(data).length > 0) {
+
+                               $(data).insertBefore('#conversation-end');
+                               lockLoadContent = false;
+                       } else {
+                               $("#scroll-end").fadeIn('normal');
+                       }
+               });
+       }
+
+    function bin2hex(s){
+        // Converts the binary representation of data to hex
+        //
+        // version: 812.316
+        // discuss at: http://phpjs.org/functions/bin2hex
+        // +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+        // +   bugfixed by: Onno Marsman
+        // +   bugfixed by: Linuxworld
+        // *     example 1: bin2hex('Kev');
+        // *     returns 1: '4b6576'
+        // *     example 2: bin2hex(String.fromCharCode(0x00));
+        // *     returns 2: '00'
+        var v,i, f = 0, a = [];
+        s += '';
+        f = s.length;
+
+        for (i = 0; i<f; i++) {
+            a[i] = s.charCodeAt(i).toString(16).replace(/^([\da-f])$/,"0$1");
+        }
+
+        return a.join('');
+    }
+
+       function groupChangeMember(gid, cid, sec_token) {
+               $('body .fakelink').css('cursor', 'wait');
+               $.get('group/' + gid + '/' + cid + "?t=" + sec_token, function(data) {
+                               $('#group-update-wrapper').html(data);
+                               $('body .fakelink').css('cursor', 'auto');
+               });
+       }
+
+       function profChangeMember(gid,cid) {
+               $('body .fakelink').css('cursor', 'wait');
+               $.get('profperm/' + gid + '/' + cid, function(data) {
+                               $('#prof-update-wrapper').html(data);
+                               $('body .fakelink').css('cursor', 'auto');
+               });
+       }
+
+       function contactgroupChangeMember(gid,cid) {
+               $('body').css('cursor', 'wait');
+               $.get('contactgroup/' + gid + '/' + cid, function(data) {
+                               $('body').css('cursor', 'auto');
+               });
+       }
+
+
+function checkboxhighlight(box) {
+  if($(box).is(':checked')) {
+       $(box).addClass('checkeditem');
+  }
+  else {
+       $(box).removeClass('checkeditem');
+  }
+}
+
+function notifyMarkAll() {
+       $.get('notify/mark/all', function(data) {
+               if(timer) clearTimeout(timer);
+               timer = setTimeout(NavUpdate,1000);
+               force_update = true;
+       });
+}
+
+/**
+ * sprintf in javascript
+ *     "{0} and {1}".format('zero','uno');
+ **/
+String.prototype.format = function() {
+    var formatted = this;
+    for (var i = 0; i < arguments.length; i++) {
+        var regexp = new RegExp('\\{'+i+'\\}', 'gi');
+        formatted = formatted.replace(regexp, arguments[i]);
+    }
+    return formatted;
+};
+// Array Remove
+Array.prototype.remove = function(item) {
+  to=undefined; from=this.indexOf(item);
+  var rest = this.slice((to || from) + 1 || this.length);
+  this.length = from < 0 ? this.length + from : from;
+  return this.push.apply(this, rest);
+};
+
+function previewTheme(elm) {
+       theme = $(elm).val();
+       $.getJSON('pretheme?f=&theme=' + theme,function(data) {
+                       $('#theme-preview').html('<div id="theme-desc">' + data.desc + '</div><div id="theme-version">' + data.version + '</div><div id="theme-credits">' + data.credits + '</div><a href="' + data.img + '"><img src="' + data.img + '" width="320" height="240" alt="' + theme + '" /></a>');
+       });
+
+}
+
+// notification permission settings in localstorage
+// set by settings page
+function getNotificationPermission() {
+       if (window["Notification"] === undefined) {
+               return null;
+       }
+    if (Notification.permission === 'granted') {
+        var val = localStorage.getItem('notification-permissions');
+               if (val === null) return 'denied';
+               return val;
+    } else {
+        return Notification.permission;
+    }
+}
+
+/**
+ * Show a dialog loaded from an url
+ * By defaults this load the url in an iframe in colorbox
+ * Themes can overwrite `show()` function to personalize it
+ */
+var Dialog = {
+       /**
+        * Show the dialog
+        *
+        * @param string url
+        * @return object colorbox
+        */
+       show : function (url) {
+               var size = Dialog._get_size();
+               return $.colorbox({href: url, iframe:true,innerWidth: size.width+'px',innerHeight: size.height+'px'})
+       },
+
+       /**
+        * Show the Image browser dialog
+        *
+        * @param string name
+        * @param string id (optional)
+        * @return object
+        *
+        * The name will be used to build the event name
+        * fired by image browser dialog when the user select
+        * an image. The optional id will be passed as argument
+        * to the event handler
+        */
+       doImageBrowser : function (name, id) {
+               var url = Dialog._get_url("image",name,id);
+               return Dialog.show(url);
+       },
+
+       /**
+        * Show the File browser dialog
+        *
+        * @param string name
+        * @param string id (optional)
+        * @return object
+        *
+        * The name will be used to build the event name
+        * fired by file browser dialog when the user select
+        * a file. The optional id will be passed as argument
+        * to the event handler
+        */
+       doFileBrowser : function (name, id) {
+               var url = Dialog._get_url("file",name,id);
+               return Dialog.show(url);
+       },
+
+       _get_url : function(type, name, id) {
+               var hash = name;
+               if (id !== undefined) hash = hash + "-" + id;
+               return baseurl + "/fbrowser/"+type+"/?mode=minimal#"+hash;
+       },
+
+       _get_size: function() {
+               return {
+                       width: window.innerWidth-50,
+                       height: window.innerHeight-100
+               };
+       }
+}
diff --git a/view/js/modernizr.js b/view/js/modernizr.js
new file mode 100644 (file)
index 0000000..f930bca
--- /dev/null
@@ -0,0 +1,4 @@
+/* Modernizr 2.8.3 (Custom Build) | MIT & BSD
+ * Build: http://modernizr.com/download/#-shiv-cssclasses-load
+ */
+;window.Modernizr=function(a,b,c){function u(a){j.cssText=a}function v(a,b){return u(prefixes.join(a+";")+(b||""))}function w(a,b){return typeof a===b}function x(a,b){return!!~(""+a).indexOf(b)}function y(a,b,d){for(var e in a){var f=b[a[e]];if(f!==c)return d===!1?a[e]:w(f,"function")?f.bind(d||b):f}return!1}var d="2.8.3",e={},f=!0,g=b.documentElement,h="modernizr",i=b.createElement(h),j=i.style,k,l={}.toString,m={},n={},o={},p=[],q=p.slice,r,s={}.hasOwnProperty,t;!w(s,"undefined")&&!w(s.call,"undefined")?t=function(a,b){return s.call(a,b)}:t=function(a,b){return b in a&&w(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=q.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(q.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(q.call(arguments)))};return e});for(var z in m)t(m,z)&&(r=z.toLowerCase(),e[r]=m[z](),p.push((e[r]?"":"no-")+r));return e.addTest=function(a,b){if(typeof a=="object")for(var d in a)t(a,d)&&e.addTest(d,a[d]);else{a=a.toLowerCase();if(e[a]!==c)return e;b=typeof b=="function"?b():b,typeof f!="undefined"&&f&&(g.className+=" "+(b?"":"no-")+a),e[a]=b}return e},u(""),i=k=null,function(a,b){function l(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x<style>"+b+"</style>",d.insertBefore(c.lastChild,d.firstChild)}function m(){var a=s.elements;return typeof a=="string"?a.split(" "):a}function n(a){var b=j[a[h]];return b||(b={},i++,a[h]=i,j[i]=b),b}function o(a,c,d){c||(c=b);if(k)return c.createElement(a);d||(d=n(c));var g;return d.cache[a]?g=d.cache[a].cloneNode():f.test(a)?g=(d.cache[a]=d.createElem(a)).cloneNode():g=d.createElem(a),g.canHaveChildren&&!e.test(a)&&!g.tagUrn?d.frag.appendChild(g):g}function p(a,c){a||(a=b);if(k)return a.createDocumentFragment();c=c||n(a);var d=c.frag.cloneNode(),e=0,f=m(),g=f.length;for(;e<g;e++)d.createElement(f[e]);return d}function q(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return s.shivMethods?o(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+m().join().replace(/[\w\-]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(s,b.frag)}function r(a){a||(a=b);var c=n(a);return s.shivCSS&&!g&&!c.hasCSS&&(c.hasCSS=!!l(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),k||q(a,c),a}var c="3.7.0",d=a.html5||{},e=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,f=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,g,h="_html5shiv",i=0,j={},k;(function(){try{var a=b.createElement("a");a.innerHTML="<xyz></xyz>",g="hidden"in a,k=a.childNodes.length==1||function(){b.createElement("a");var a=b.createDocumentFragment();return typeof a.cloneNode=="undefined"||typeof a.createDocumentFragment=="undefined"||typeof a.createElement=="undefined"}()}catch(c){g=!0,k=!0}})();var s={elements:d.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video",version:c,shivCSS:d.shivCSS!==!1,supportsUnknownElements:k,shivMethods:d.shivMethods!==!1,type:"default",shivDocument:r,createElement:o,createDocumentFragment:p};a.html5=s,r(b)}(this,b),e._version=d,g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(f?" js "+p.join(" "):""),e}(this,this.document),function(a,b,c){function d(a){return"[object Function]"==o.call(a)}function e(a){return"string"==typeof a}function f(){}function g(a){return!a||"loaded"==a||"complete"==a||"uninitialized"==a}function h(){var a=p.shift();q=1,a?a.t?m(function(){("c"==a.t?B.injectCss:B.injectJs)(a.s,0,a.a,a.x,a.e,1)},0):(a(),h()):q=0}function i(a,c,d,e,f,i,j){function k(b){if(!o&&g(l.readyState)&&(u.r=o=1,!q&&h(),l.onload=l.onreadystatechange=null,b)){"img"!=a&&m(function(){t.removeChild(l)},50);for(var d in y[c])y[c].hasOwnProperty(d)&&y[c][d].onload()}}var j=j||B.errorTimeout,l=b.createElement(a),o=0,r=0,u={t:d,s:c,e:f,a:i,x:j};1===y[c]&&(r=1,y[c]=[]),"object"==a?l.data=c:(l.src=c,l.type=a),l.width=l.height="0",l.onerror=l.onload=l.onreadystatechange=function(){k.call(this,r)},p.splice(e,0,u),"img"!=a&&(r||2===y[c]?(t.insertBefore(l,s?null:n),m(k,j)):y[c].push(l))}function j(a,b,c,d,f){return q=0,b=b||"j",e(a)?i("c"==b?v:u,a,b,this.i++,c,d,f):(p.splice(this.i++,0,a),1==p.length&&h()),this}function k(){var a=B;return a.loader={load:j,i:0},a}var l=b.documentElement,m=a.setTimeout,n=b.getElementsByTagName("script")[0],o={}.toString,p=[],q=0,r="MozAppearance"in l.style,s=r&&!!b.createRange().compareNode,t=s?l:n.parentNode,l=a.opera&&"[object Opera]"==o.call(a.opera),l=!!b.attachEvent&&!l,u=r?"object":l?"script":"img",v=l?"script":u,w=Array.isArray||function(a){return"[object Array]"==o.call(a)},x=[],y={},z={timeout:function(a,b){return b.length&&(a.timeout=b[0]),a}},A,B;B=function(a){function b(a){var a=a.split("!"),b=x.length,c=a.pop(),d=a.length,c={url:c,origUrl:c,prefixes:a},e,f,g;for(f=0;f<d;f++)g=a[f].split("="),(e=z[g.shift()])&&(c=e(c,g));for(f=0;f<b;f++)c=x[f](c);return c}function g(a,e,f,g,h){var i=b(a),j=i.autoCallback;i.url.split(".").pop().split("?").shift(),i.bypass||(e&&(e=d(e)?e:e[a]||e[g]||e[a.split("/").pop().split("?")[0]]),i.instead?i.instead(a,e,f,g,h):(y[i.url]?i.noexec=!0:y[i.url]=1,f.load(i.url,i.forceCSS||!i.forceJS&&"css"==i.url.split(".").pop().split("?").shift()?"c":c,i.noexec,i.attrs,i.timeout),(d(e)||d(j))&&f.load(function(){k(),e&&e(i.origUrl,h,g),j&&j(i.origUrl,h,g),y[i.url]=2})))}function h(a,b){function c(a,c){if(a){if(e(a))c||(j=function(){var a=[].slice.call(arguments);k.apply(this,a),l()}),g(a,j,b,0,h);else if(Object(a)===a)for(n in m=function(){var b=0,c;for(c in a)a.hasOwnProperty(c)&&b++;return b}(),a)a.hasOwnProperty(n)&&(!c&&!--m&&(d(j)?j=function(){var a=[].slice.call(arguments);k.apply(this,a),l()}:j[n]=function(a){return function(){var b=[].slice.call(arguments);a&&a.apply(this,b),l()}}(k[n])),g(a[n],j,b,n,h))}else!c&&l()}var h=!!a.test,i=a.load||a.both,j=a.callback||f,k=j,l=a.complete||f,m,n;c(h?a.yep:a.nope,!!i),i&&c(i)}var i,j,l=this.yepnope.loader;if(e(a))g(a,0,l,0);else if(w(a))for(i=0;i<a.length;i++)j=a[i],e(j)?g(j,0,l,0):w(j)?B(j):Object(j)===j&&h(j,l);else Object(a)===a&&h(a,l)},B.addPrefix=function(a,b){z[a]=b},B.addFilter=function(a){x.push(a)},B.errorTimeout=1e4,null==b.readyState&&b.addEventListener&&(b.readyState="loading",b.addEventListener("DOMContentLoaded",A=function(){b.removeEventListener("DOMContentLoaded",A,0),b.readyState="complete"},0)),a.yepnope=k(),a.yepnope.executeStack=h,a.yepnope.injectJs=function(a,c,d,e,i,j){var k=b.createElement("script"),l,o,e=e||B.errorTimeout;k.src=a;for(o in d)k.setAttribute(o,d[o]);c=j?h:c||f,k.onreadystatechange=k.onload=function(){!l&&g(k.readyState)&&(l=1,c(),k.onload=k.onreadystatechange=null)},m(function(){l||(l=1,c(1))},e),i?k.onload():n.parentNode.insertBefore(k,n)},a.yepnope.injectCss=function(a,c,d,e,g,i){var e=b.createElement("link"),j,c=i?h:c||f;e.href=a,e.rel="stylesheet",e.type="text/css";for(j in d)e.setAttribute(j,d[j]);g||(n.parentNode.insertBefore(e,n),m(c,0))}}(this,document),Modernizr.load=function(){yepnope.apply(window,[].slice.call(arguments,0))};
\ No newline at end of file
index 48f375426577dab2419400d7274287d395c0b831..033f2e410144df151b5a7853c13ec1cd49f8c19f 100644 (file)
@@ -1,4 +1,4 @@
-      <script type="text/javascript" src="library/cropper/lib/prototype.js" language="javascript"></script>
-      <script type="text/javascript" src="library/cropper/lib/scriptaculous.js?load=effects,builder,dragdrop" language="javascript"></script>
-      <script type="text/javascript" src="library/cropper/cropper.js" language="javascript"></script>
-      <link rel="stylesheet" href="library/cropper/cropper.css" type="text/css" />
+      <script type="text/javascript" src="view/js/cropper/lib/prototype.js" language="javascript"></script>
+      <script type="text/javascript" src="view/js/cropper/lib/scriptaculous.js?load=effects,builder,dragdrop" language="javascript"></script>
+      <script type="text/javascript" src="view/js/cropper/cropper.js" language="javascript"></script>
+      <link rel="stylesheet" href="view/js/cropper/cropper.css" type="text/css" />
index 105e0c7c47f904a11113d60ebbcd2e48a73b52bd..27a73d0398a6201ae3dc741653ebe631c52dfa3d 100644 (file)
@@ -4,8 +4,8 @@
 <style>
        #buglink_wrapper{display:none;} /* hide buglink. only in this page */
 </style>
-<script type="text/javascript" src="{{$baseurl}}/js/ajaxupload.js" ></script>
-<script type="text/javascript" src="{{$baseurl}}/js/filebrowser.js"></script>
+<script type="text/javascript" src="{{$baseurl}}/view/js/ajaxupload.js" ></script>
+<script type="text/javascript" src="{{$baseurl}}/view/js/filebrowser.js"></script>
 <script>
        $(function() {
                FileBrowser.init("{{$nickname}}", "{{$type}}");
index 061e8262dbfc23f5d314b3d91a397e6e78a20233..bf0dd52b9e9fba88e057e3fb770987fc6d012518 100644 (file)
 <!--[if IE]>
 <script type="text/javascript" src="https://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
 <![endif]-->
-<script type="text/javascript" src="js/modernizr.js" ></script>
+<script type="text/javascript" src="view/js/modernizr.js" ></script>
 <script type="text/javascript" src="vendor/asset/jquery/dist/jquery.min.js" ></script>
-<script type="text/javascript" src="js/jquery.textinputs.js" ></script>
-<script type="text/javascript" src="library/jquery-textcomplete/jquery.textcomplete.min.js" ></script>
-<script type="text/javascript" src="js/autocomplete.js" ></script>
+<script type="text/javascript" src="view/js/jquery.textinputs.js" ></script>
+<script type="text/javascript" src="view/js/jquery-textcomplete/jquery.textcomplete.min.js" ></script>
+<script type="text/javascript" src="view/js/autocomplete.js" ></script>
 <script type="text/javascript" src="vendor/asset/jquery-colorbox/jquery.colorbox-min.js"></script>
 <script type="text/javascript" src="vendor/asset/jgrowl/jquery.jgrowl.min.js"></script>
 <script type="text/javascript" src="vendor/asset/jquery-datetimepicker/build/jquery.datetimepicker.full.min.js"></script>
 <script type="text/javascript" src="vendor/asset/perfect-scrollbar/js/perfect-scrollbar.jquery.min.js" ></script>
-<script type="text/javascript" src="js/acl.js" ></script>
+<script type="text/javascript" src="view/js/acl.js" ></script>
 <script type="text/javascript" src="vendor/asset/base64/base64.min.js" ></script>
-<script type="text/javascript" src="js/main.js" ></script>
+<script type="text/javascript" src="view/js/main.js" ></script>
 <script>
 
        var updateInterval = {{$update_interval}};
index 6ddd695450bb182d829d33a22c2a2807ac6a127e..f37b67085ec229055ebc0733d529b75b061012e9 100644 (file)
@@ -40,7 +40,7 @@ function enableOnUser(){
 }
 
 </script>
-<script type="text/javascript" src="{{$baseurl}}/js/ajaxupload.js" ></script>
+<script type="text/javascript" src="{{$baseurl}}/view/js/ajaxupload.js" ></script>
 <script>
        var ispublic = '{{$ispublic}}';
 
index 6e3a33fdca9d0869d01a9b8261f4d2a810ea315d..653b3d56612abc7cf966912d6c8a2db995e18bfe 100644 (file)
@@ -1,7 +1,7 @@
 <script language="javascript" type="text/javascript">
        $("#prvmail-text").editor_autocomplete(baseurl + '/acl');
 </script>
-<script type="text/javascript" src="js/ajaxupload.js" ></script>
+<script type="text/javascript" src="view/js/ajaxupload.js" ></script>
 <script>
        $(document).ready(function() {
                var uploader = new window.AjaxUpload(
index 248e66d7827b463eb6f672878dd34e6bc6ad4515..f7e1fb18a683261e6430cd596e5a291b1d614c97 100644 (file)
@@ -1 +1 @@
-<script type="text/javascript" src="js/country.js" ></script>
\ No newline at end of file
+<script type="text/javascript" src="view/js/country.js" ></script>
\ No newline at end of file
index 3fde6a52021b07b45b9137844d8e03e98bdf7756..dc3c47a66a9ea05dcfec6805c561b7ec2c36596a 100644 (file)
@@ -4,7 +4,7 @@
 <style>
        #buglink_wrapper{display:none;} /* hide buglink. only in this page */
 </style>
-{{*<script type="text/javascript" src="{{$baseurl}}/js/ajaxupload.js" ></script>*}}
+{{*<script type="text/javascript" src="{{$baseurl}}/view/js/ajaxupload.js" ></script>*}}
 {{*<script type="text/javascript" src="view/theme/frio/js/filebrowser.js"></script>*}}
 
 <div class="fbrowser {{$type}}">
index e599a5151bdc6ce33ee8c8a9c218425456440393..fb91af0863e8f28b589f00931592d53dbed4bd77 100644 (file)
 <!--[if IE]>
 <script type="text/javascript" src="https://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
 <![endif]-->
-<script type="text/javascript" src="js/modernizr.js" ></script>
+<script type="text/javascript" src="view/js/modernizr.js" ></script>
 <script type="text/javascript" src="vendor/asset/jquery/dist/jquery.min.js"></script>
-<script type="text/javascript" src="js/jquery.textinputs.js" ></script>
-<script type="text/javascript" src="library/jquery-textcomplete/jquery.textcomplete.min.js" ></script>
-<script type="text/javascript" src="js/autocomplete.js" ></script>
+<script type="text/javascript" src="view/js/jquery.textinputs.js" ></script>
+<script type="text/javascript" src="view/js/jquery-textcomplete/jquery.textcomplete.min.js" ></script>
+<script type="text/javascript" src="view/js/autocomplete.js" ></script>
 <script type="text/javascript" src="vendor/asset/jquery-colorbox/jquery.colorbox-min.js"></script>
 <script type="text/javascript" src="vendor/asset/jgrowl/jquery.jgrowl.min.js"></script>
 <script type="text/javascript" src="vendor/asset/jquery-datetimepicker/build/jquery.datetimepicker.full.min.js"></script>
 <script type="text/javascript" src="vendor/asset/perfect-scrollbar/js/perfect-scrollbar.jquery.min.js" ></script>
-<script type="text/javascript" src="js/acl.js" ></script>
+<script type="text/javascript" src="view/js/acl.js" ></script>
 <script type="text/javascript" src="vendor/asset/base64/base64.min.js" ></script>
-<script type="text/javascript" src="js/main.js" ></script>
+<script type="text/javascript" src="view/js/main.js" ></script>
 
 <script type="text/javascript" src="view/theme/frio/frameworks/bootstrap/js/bootstrap.min.js"></script>
 <script type="text/javascript" src="view/theme/frio/frameworks/jasny/js/jasny-bootstrap.custom.js"></script>
index d52f4b98dec41eac862d5ce3ebff8c0c937166f0..81921598ab30fa40f4a6e26174411b9003c7b270 100644 (file)
@@ -1,5 +1,5 @@
 
-<script type="text/javascript" src="{{$baseurl}}/js/ajaxupload.js" ></script>
+<script type="text/javascript" src="{{$baseurl}}/view/js/ajaxupload.js" ></script>
 
 <script type="text/javascript">
        var editor = false;
index 37b29d9a103716868e407e525dd2bccbd3fa8f55..adc2150cf9f64e204d0ebe245a2252e59a844032 100644 (file)
@@ -2,7 +2,7 @@
 <link rel="stylesheet" href="{{$baseurl}}/view/theme/frio/frameworks/jRange/jquery.range.css" type="text/css" media="screen" />
 <script src="{{$baseurl}}/view/theme/quattro/jquery.tools.min.js"></script>
 <script src="{{$baseurl}}/view/theme/frio/frameworks/jRange/jquery.range.js"></script>
-<script type="text/javascript" src="{{$baseurl}}/js/ajaxupload.js" ></script>
+<script type="text/javascript" src="{{$baseurl}}/view/js/ajaxupload.js" ></script>
 
 {{include file="field_select.tpl" field=$schema}}
 
index a56c71d92e2f1d476407ddf2257a8b1acc6aa87c..a26100e0d4987d2c4ef3b7b5c2fd5bfaededc1aa 100644 (file)
@@ -1,4 +1,4 @@
-      <script type="text/javascript" src="library/cropper/lib/prototype.js" language="javascript"></script>
-      <script type="text/javascript" src="library/cropper/lib/scriptaculous.js?load=effects,builder,dragdrop" language="javascript"></script>
-      <script type="text/javascript" src="library/cropper/cropper.js" language="javascript"></script>
+      <script type="text/javascript" src="view/js/cropper/lib/prototype.js" language="javascript"></script>
+      <script type="text/javascript" src="view/js/cropper/lib/scriptaculous.js?load=effects,builder,dragdrop" language="javascript"></script>
+      <script type="text/javascript" src="view/js/cropper/cropper.js" language="javascript"></script>
       <script type="text/javascript" language="javascript">initCrop();</script>
index 56e941e3ab1a5ecb995a11eba7cf99029114b12c..cbfce8d83fb402754cfeff54fbaa2c16e61866c0 100644 (file)
@@ -1 +1 @@
-      <link rel="stylesheet" href="library/cropper/cropper.css" type="text/css" />
+      <link rel="stylesheet" href="view/js/cropper/cropper.css" type="text/css" />
index 1f47aecee864cc0564a5f60cbd0cabb656850a25..4af3b41c0b87905c92c7127b7f5ee3c2173549e3 100644 (file)
@@ -3,12 +3,12 @@
 <![endif]-->
 <script type="text/javascript" src="{{$baseurl}}/vendor/asset/jquery/dist/jquery.min.js" ></script>
 <script type="text/javascript" src="{{$baseurl}}/view/theme/frost-mobile/js/readmore.min.js" ></script>
-<script type="text/javascript" src="{{$baseurl}}/js/jquery.textinputs.js" ></script>
+<script type="text/javascript" src="{{$baseurl}}/view/js/jquery.textinputs.js" ></script>
 <script type="text/javascript" src="{{$baseurl}}/vendor/asset/jgrowl/jquery.jgrowl.min.js"></script>
 <script type="text/javascript" src="{{$baseurl}}/vendor/asset/jquery-datetimepicker/build/jquery.datetimepicker.full.min.js"></script>
 
-<script type="text/javascript" src="{{$baseurl}}/library/jquery-textcomplete/jquery.textcomplete.min.js" ></script>
-<script type="text/javascript" src="{{$baseurl}}/js/autocomplete.js" ></script>
+<script type="text/javascript" src="{{$baseurl}}/view/js/jquery-textcomplete/jquery.textcomplete.min.js" ></script>
+<script type="text/javascript" src="{{$baseurl}}/view/js/autocomplete.js" ></script>
 <script type="text/javascript" src="{{$baseurl}}/view/theme/frost-mobile/js/acl.js" ></script>
 <script type="text/javascript" src="{{$baseurl}}/vendor/asset/base64/base64.min.js" ></script>
 <script type="text/javascript" src="{{$baseurl}}/view/theme/frost-mobile/js/main.js" ></script>
index 5a38b51ac655124438a57172ae0a04c24ef48f38..7455d355a22d288ef08006f9ce08583106e8fd31 100644 (file)
@@ -1,4 +1,4 @@
-<script type="text/javascript" src="{{$baseurl}}/js/ajaxupload.js" ></script>
+<script type="text/javascript" src="{{$baseurl}}/view/js/ajaxupload.js" ></script>
 
 <script language="javascript" type="text/javascript">if (typeof window.jotInit != 'undefined') initEditor();</script>
 
index fe59efd8804fc5592bb88c0b95f88c8901d88c4a..18aec4e6a689d391e6bec8d1cafd25eedc43013e 100644 (file)
@@ -1,3 +1,3 @@
 
-<script type="text/javascript" src="{{$baseurl}}/js/ajaxupload.js" ></script>
+<script type="text/javascript" src="{{$baseurl}}/view/js/ajaxupload.js" ></script>
 
index 07ec0f7385f157586f22beadb89fefdbf8139d4a..a54fe60ddcd59be4a2f27fc5bcaadf232d3a35b5 100644 (file)
@@ -1,6 +1,6 @@
 
 
-<script type="text/javascript" src="js/country.js" ></script>
+<script type="text/javascript" src="view/js/country.js" ></script>
 
 <script language="javascript" type="text/javascript">
        Fill_Country('{{$country_name}}');
index fe59efd8804fc5592bb88c0b95f88c8901d88c4a..18aec4e6a689d391e6bec8d1cafd25eedc43013e 100644 (file)
@@ -1,3 +1,3 @@
 
-<script type="text/javascript" src="{{$baseurl}}/js/ajaxupload.js" ></script>
+<script type="text/javascript" src="{{$baseurl}}/view/js/ajaxupload.js" ></script>
 
index a56c71d92e2f1d476407ddf2257a8b1acc6aa87c..a26100e0d4987d2c4ef3b7b5c2fd5bfaededc1aa 100644 (file)
@@ -1,4 +1,4 @@
-      <script type="text/javascript" src="library/cropper/lib/prototype.js" language="javascript"></script>
-      <script type="text/javascript" src="library/cropper/lib/scriptaculous.js?load=effects,builder,dragdrop" language="javascript"></script>
-      <script type="text/javascript" src="library/cropper/cropper.js" language="javascript"></script>
+      <script type="text/javascript" src="view/js/cropper/lib/prototype.js" language="javascript"></script>
+      <script type="text/javascript" src="view/js/cropper/lib/scriptaculous.js?load=effects,builder,dragdrop" language="javascript"></script>
+      <script type="text/javascript" src="view/js/cropper/cropper.js" language="javascript"></script>
       <script type="text/javascript" language="javascript">initCrop();</script>
index 56e941e3ab1a5ecb995a11eba7cf99029114b12c..cbfce8d83fb402754cfeff54fbaa2c16e61866c0 100644 (file)
@@ -1 +1 @@
-      <link rel="stylesheet" href="library/cropper/cropper.css" type="text/css" />
+      <link rel="stylesheet" href="view/js/cropper/cropper.css" type="text/css" />
index ac931261bfc943fc82130fbdbce8b8ca9ff2dbe8..b49fc3cd4c324525e71dfc915185f5fd132d5955 100644 (file)
@@ -5,15 +5,15 @@
 
 <script type="text/javascript" src="{{$baseurl}}/vendor/asset/jquery/dist/jquery.min.js" ></script>
 <script type="text/javascript" src="{{$baseurl}}/view/theme/frost/js/jquery.divgrow-1.3.1.f1.min.js" ></script>
-<script type="text/javascript" src="{{$baseurl}}/js/jquery.textinputs.js" ></script>
+<script type="text/javascript" src="{{$baseurl}}/view/js/jquery.textinputs.js" ></script>
 <script type="text/javascript" src="{{$baseurl}}/vendor/asset/jquery-colorbox/jquery.colorbox-min.js"></script>
 <script type="text/javascript" src="{{$baseurl}}/vendor/asset/jgrowl/jquery.jgrowl.min.js"></script>
 <script type="text/javascript" src="{{$baseurl}}/vendor/asset/jquery-datetimepicker/build/jquery.datetimepicker.full.min.js"></script>
 
 <script type="text/javascript" src="{{$baseurl}}/view/theme/frost/js/acl.js" ></script>
 <script type="text/javascript" src="{{$baseurl}}/vendor/asset/base64/base64.min.js" ></script>
-<script type="text/javascript" src="{{$baseurl}}/library/jquery-textcomplete/jquery.textcomplete.min.js" ></script>
-<script type="text/javascript" src="{{$baseurl}}/js/autocomplete.js" ></script>
+<script type="text/javascript" src="{{$baseurl}}/view/js/jquery-textcomplete/jquery.textcomplete.min.js" ></script>
+<script type="text/javascript" src="{{$baseurl}}/view/js/autocomplete.js" ></script>
 <script type="text/javascript" src="{{$baseurl}}/view/theme/frost/js/main.js" ></script>
 <script type="text/javascript" src="{{$baseurl}}/view/theme/frost/js/theme.js"></script>
 
index e44815708639d1b7a88b544a376ce7a7d9c63903..9c04ad631ad0d6a2a5c4b863c0ce1ae787d3985c 100644 (file)
@@ -1,4 +1,4 @@
 
-<script type="text/javascript" src="{{$baseurl}}/js/ajaxupload.js" ></script>
+<script type="text/javascript" src="{{$baseurl}}/view/js/ajaxupload.js" ></script>
 
 <script language="javascript" type="text/javascript">if(typeof window.jotInit != 'undefined') initEditor();</script>
index 90ff83e28aaa686fa7c7cf410adddb4d49fdaa87..628dec2ba47ae414b148982b8b8a1f66125c6384 100644 (file)
@@ -1,4 +1,4 @@
 
-<script type="text/javascript" src="{{$baseurl}}/js/ajaxupload.js" ></script>
+<script type="text/javascript" src="{{$baseurl}}/view/js/ajaxupload.js" ></script>
 
 <script language="javascript" type="text/javascript">msgInitEditor();</script>
index e8aafd1ef1871e1666e58665793d4555317eee55..cac9de97ab2ffa9593a91d97a1db9aff60b83dfa 100644 (file)
@@ -1,5 +1,5 @@
 
-<script type="text/javascript" src="js/country.js" ></script>
+<script type="text/javascript" src="view/js/country.js" ></script>
 
 <script language="javascript" type="text/javascript">
 Fill_Country('{{$country_name}}');
index 9f7a0f88c0bfd78e3575bb29e10f86162df749dd..90f4ed198a531759f55de188ef690de57b69c718 100644 (file)
@@ -1,5 +1,5 @@
 
-<script type="text/javascript" src="{{$baseurl}}/js/ajaxupload.js" ></script>
+<script type="text/javascript" src="{{$baseurl}}/view/js/ajaxupload.js" ></script>
 
 <script language="javascript" type="text/javascript">msgInitEditor();</script>
 
index eb8c13f2174c0a8ac310cfbf2805569ed8497c0d..5eae0ce4bb12637c47082ad5d93eade03e78e27b 100644 (file)
@@ -48,7 +48,7 @@ function enableOnUser(){
 
 </script>
 
-<script type="text/javascript" src="js/ajaxupload.js" >
+<script type="text/javascript" src="view/js/ajaxupload.js" >
 </script>
 
 <script>