Version 1.2 ()
------------------------------------------------------------------------
+ * Moved drag+drop and YahooUI library into templates/default/ to
+ be used in shared installation environments (garvinhicking)
+
* Improve WP importer by only fetching real posts (attachments/static
only optional) and splitting a post into extended/normal entry.
Thanks to jtb!
<div><?php echo BELOW_IS_A_LIST_OF_INSTALLED_PLUGINS ?></div>
<?php
if (!isset($serendipity['eyecandy']) || serendipity_db_bool($serendipity['eyecandy'])) {
- echo '<script src="bundled-libs/dragdrop.js" type="text/javascript"></script>';
+ echo '<script src="' . serendipity_getTemplateFile('dragdrop.js') . '" type="text/javascript"></script>';
echo '<div class="warning js_warning"><em>' . PREFERENCE_USE_JS_WARNING . '</em></div>';
}
--- /dev/null
+/*
+Copyright (c) 2006 Yahoo! Inc. All rights reserved.
+version 0.9.0
+*/
+
+/**
+ * @class The Yahoo global namespace
+ */
+var YAHOO = function() {
+
+ return {
+
+ /**
+ * Yahoo presentation platform utils namespace
+ */
+ util: {},
+
+ /**
+ * Yahoo presentation platform widgets namespace
+ */
+ widget: {},
+
+ /**
+ * Yahoo presentation platform examples namespace
+ */
+ example: {},
+
+ /**
+ * Returns the namespace specified and creates it if it doesn't exist
+ *
+ * YAHOO.namespace("property.package");
+ * YAHOO.namespace("YAHOO.property.package");
+ *
+ * Either of the above would create YAHOO.property, then
+ * YAHOO.property.package
+ *
+ * @param {String} sNameSpace String representation of the desired
+ * namespace
+ * @return {Object} A reference to the namespace object
+ */
+ namespace: function( sNameSpace ) {
+
+ if (!sNameSpace || !sNameSpace.length) {
+ return null;
+ }
+
+ var levels = sNameSpace.split(".");
+
+ var currentNS = YAHOO;
+
+ // YAHOO is implied, so it is ignored if it is included
+ for (var i=(levels[0] == "YAHOO") ? 1 : 0; i<levels.length; ++i) {
+ currentNS[levels[i]] = currentNS[levels[i]] || {};
+ currentNS = currentNS[levels[i]];
+ }
+
+ return currentNS;
+
+ }
+ };
+
+} ();
+
--- /dev/null
+Software License Agreement (BSD License)\r
+\r
+Copyright (c) 2006, Yahoo! Inc.\r
+All rights reserved.\r
+\r
+Redistribution and use of this software in source and binary forms, with \r
+or without modification, are permitted provided that the following \r
+conditions are met:\r
+\r
+* Redistributions of source code must retain the above\r
+ copyright notice, this list of conditions and the\r
+ following disclaimer.\r
+\r
+* Redistributions in binary form must reproduce the above\r
+ copyright notice, this list of conditions and the\r
+ following disclaimer in the documentation and/or other\r
+ materials provided with the distribution.\r
+\r
+* Neither the name of Yahoo! Inc. nor the names of its\r
+ contributors may be used to endorse or promote products\r
+ derived from this software without specific prior\r
+ written permission of Yahoo! Inc.\r
+\r
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS \r
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED \r
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A \r
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER \r
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, \r
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, \r
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR \r
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF \r
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING \r
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS \r
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+\r
--- /dev/null
+/*
+Copyright (c) 2006 Yahoo! Inc. All rights reserved.
+version 0.9.0
+*/
+
+/**
+ * @class Contains the tree view metadata and the root node. This is an
+ * ordered tree; child nodes will be displayed in the order created, and
+ * there currently is no way to change this.
+ *
+ * @constructor
+ * @todo prune, graft, reload, repaint
+ * @param {string} id The id of the element that the tree will be inserted
+ * into.
+ */
+YAHOO.widget.TreeView = function(id) {
+ if (id) { this.init(id); }
+};
+
+YAHOO.widget.TreeView.prototype = {
+
+ /**
+ * The id of tree container element
+ *
+ * @type String
+ */
+ id: null,
+
+ /**
+ * Flat collection of all nodes in this tree
+ *
+ * @type YAHOO.widget.Node[]
+ * @private
+ */
+ _nodes: null,
+
+ /**
+ * We lock the tree control while waiting for the dynamic loader to return
+ *
+ * @type boolean
+ */
+ locked: false,
+
+ /**
+ * The animation to use for expanding children, if any
+ *
+ * @type string
+ * @private
+ */
+ _expandAnim: null,
+
+ /**
+ * The animation to use for collapsing children, if any
+ *
+ * @type string
+ * @private
+ */
+ _collapseAnim: null,
+
+ /**
+ * The current number of animations that are executing
+ *
+ * @type int
+ * @private
+ */
+ _animCount: 0,
+
+ /**
+ * The maximum number of animations to run at one time.
+ *
+ * @type int
+ */
+ _maxAnim: 2,
+
+ /**
+ * Sets up the animation for expanding children
+ *
+ * @param {string} the type of animation (acceptable constants in YAHOO.widget.TVAnim)
+ */
+ setExpandAnim: function(type) {
+ if (YAHOO.widget.TVAnim.isValid(type)) {
+ this._expandAnim = type;
+ }
+ },
+
+ /**
+ * Sets up the animation for collapsing children
+ *
+ * @param {string} the type of animation (acceptable constants in YAHOO.widget.TVAnim)
+ */
+ setCollapseAnim: function(type) {
+ if (YAHOO.widget.TVAnim.isValid(type)) {
+ this._collapseAnim = type;
+ }
+ },
+
+ /**
+ * Perform the expand animation if configured, or just show the
+ * element if not configured or too many animations are in progress
+ *
+ * @param el {HTMLElement} the element to animate
+ * @return {boolean} true if animation could be invoked, false otherwise
+ */
+ animateExpand: function(el) {
+
+ if (this._expandAnim && this._animCount < this._maxAnim) {
+ // this.locked = true;
+ var tree = this;
+ var a = YAHOO.widget.TVAnim.getAnim(this._expandAnim, el,
+ function() { tree.expandComplete(); });
+ if (a) {
+ ++this._animCount;
+ a.animate();
+ }
+
+ return true;
+ }
+
+ return false;
+ },
+
+ /**
+ * Perform the collapse animation if configured, or just show the
+ * element if not configured or too many animations are in progress
+ *
+ * @param el {HTMLElement} the element to animate
+ * @return {boolean} true if animation could be invoked, false otherwise
+ */
+ animateCollapse: function(el) {
+
+ if (this._collapseAnim && this._animCount < this._maxAnim) {
+ // this.locked = true;
+ var tree = this;
+ var a = YAHOO.widget.TVAnim.getAnim(this._collapseAnim, el,
+ function() { tree.collapseComplete(); });
+ if (a) {
+ ++this._animCount;
+ a.animate();
+ }
+
+ return true;
+ }
+
+ return false;
+ },
+
+ /**
+ * Function executed when the expand animation completes
+ */
+ expandComplete: function() {
+ --this._animCount;
+ // this.locked = false;
+ },
+
+ /**
+ * Function executed when the collapse animation completes
+ */
+ collapseComplete: function() {
+ --this._animCount;
+ // this.locked = false;
+ },
+
+ /**
+ * Initializes the tree
+ *
+ * @parm {string} id the id of the element that will hold the tree
+ * @private
+ */
+ init: function(id) {
+
+ this.id = id;
+ this._nodes = new Array();
+
+ // store a global reference
+ YAHOO.widget.TreeView.trees[id] = this;
+
+ // Set up the root node
+ this.root = new YAHOO.widget.RootNode(this);
+
+
+ },
+
+ /**
+ * Renders the tree boilerplate and visible nodes
+ */
+ draw: function() {
+ var html = this.root.getHtml();
+ document.getElementById(this.id).innerHTML = html;
+ this.firstDraw = false;
+ },
+
+ /**
+ * Nodes register themselves with the tree instance when they are created.
+ *
+ * @param node {YAHOO.widget.Node} the node to register
+ * @private
+ */
+ regNode: function(node) {
+ this._nodes[node.index] = node;
+ },
+
+ /**
+ * Returns the root node of this tree
+ *
+ * @return {YAHOO.widget.Node} the root node
+ */
+ getRoot: function() {
+ return this.root;
+ },
+
+ /**
+ * Configures this tree to dynamically load all child data
+ *
+ * @param {function} fnDataLoader the function that will be called to get the data
+ */
+ setDynamicLoad: function(fnDataLoader) {
+ // this.root.dataLoader = fnDataLoader;
+ // this.root._dynLoad = true;
+ this.root.setDynamicLoad(fnDataLoader);
+ },
+
+ /**
+ * Expands all child nodes. Note: this conflicts with the "multiExpand"
+ * node property. If expand all is called in a tree with nodes that
+ * do not allow multiple siblings to be displayed, only the last sibling
+ * will be expanded.
+ */
+ expandAll: function() {
+ if (!this.locked) {
+ this.root.expandAll();
+ }
+ },
+
+ /**
+ * Collapses all expanded child nodes in the entire tree.
+ */
+ collapseAll: function() {
+ if (!this.locked) {
+ this.root.collapseAll();
+ }
+ },
+
+ /**
+ * Returns a node in the tree that has the specified index (this index
+ * is created internally, so this function probably will only be used
+ * in html generated for a given node.)
+ *
+ * @param {int} nodeIndex the index of the node wanted
+ * @return {YAHOO.widget.Node} the node with index=nodeIndex, null if no match
+ */
+ getNodeByIndex: function(nodeIndex) {
+ var n = this._nodes[nodeIndex];
+ return (n) ? n : null;
+ },
+
+ /**
+ * Returns a node that has a matching property and value in the data
+ * object that was passed into its constructor. Provides a flexible
+ * way for the implementer to get a particular node.
+ *
+ * @param {object} property the property to search (usually a string)
+ * @param {object} value the value we want to find (usuall an int or string)
+ * @return {YAHOO.widget.Node} the matching node, null if no match
+ */
+ getNodeByProperty: function(property, value) {
+ for (var i in this._nodes) {
+ var n = this._nodes[i];
+ if (n.data && value == n.data[property]) {
+ return n;
+ }
+ }
+
+ return null;
+ },
+
+ /**
+ * Abstract method that is executed when a node is expanded
+ *
+ * @param node {YAHOO.widget.Node} the node that was expanded
+ */
+ onExpand: function(node) { },
+
+ /**
+ * Abstract method that is executed when a node is collapsed
+ *
+ * @param node {YAHOO.widget.Node} the node that was collapsed.
+ */
+ onCollapse: function(node) { }
+
+};
+
+/**
+ * Global cache of tree instances
+ *
+ * @type Array
+ * @private
+ */
+YAHOO.widget.TreeView.trees = [];
+
+/**
+ * Global method for getting a tree by its id. Used in the generated
+ * tree html.
+ *
+ * @param treeId {String} the id of the tree instance
+ * @return {TreeView} the tree instance requested, null if not found.
+ */
+YAHOO.widget.TreeView.getTree = function(treeId) {
+ var t = YAHOO.widget.TreeView.trees[treeId];
+ return (t) ? t : null;
+};
+
+YAHOO.widget.TreeView.nodeCount = 0;
+
+/**
+ * Global method for getting a node by its id. Used in the generated
+ * tree html.
+ *
+ * @param treeId {String} the id of the tree instance
+ * @param nodeIndex {String} the index of the node to return
+ * @param return {YAHOO.widget.Node} the node instance requested, null if not found
+ */
+YAHOO.widget.TreeView.getNode = function(treeId, nodeIndex) {
+ var t = YAHOO.widget.TreeView.getTree(treeId);
+ return (t) ? t.getNodeByIndex(nodeIndex) : null;
+};
+
+/**
+ * Adds an event. Replace with event manager when available
+ *
+ * @param el the elment to bind the handler to
+ * @param {string} sType the type of event handler
+ * @param {function} fn the callback to invoke
+ * @param {boolean} capture if true event is capture phase, bubble otherwise
+ */
+YAHOO.widget.TreeView.addHandler = function (el, sType, fn, capture) {
+ capture = (capture) ? true : false;
+ if (el.addEventListener) {
+ el.addEventListener(sType, fn, capture);
+ } else if (el.attachEvent) {
+ el.attachEvent("on" + sType, fn);
+ } else {
+ el["on" + sType] = fn;
+ }
+};
+
+/**
+ * Attempts to preload the images defined in the styles used to draw the tree by
+ * rendering off-screen elements that use the styles.
+ */
+YAHOO.widget.TreeView.preload = function() {
+
+ var styles = [
+ "ygtvtn",
+ "ygtvtm",
+ "ygtvtmh",
+ "ygtvtp",
+ "ygtvtph",
+ "ygtvln",
+ "ygtvlm",
+ "ygtvlmh",
+ "ygtvlp",
+ "ygtvlph",
+ "ygtvloading"
+ ];
+
+ var sb = [];
+
+ for (var i = 0; i < styles.length; ++i) {
+ sb[sb.length] = '<span class="' + styles[i] + '"> </span>';
+ }
+
+ var f = document.createElement("div");
+ var s = f.style;
+ s.position = "absolute";
+ s.top = "-1000px";
+ s.left = "-1000px";
+ f.innerHTML = sb.join("");
+
+ document.body.appendChild(f);
+};
+
+YAHOO.widget.TreeView.addHandler(window,
+ "load", YAHOO.widget.TreeView.preload);
+
+/* Copyright (c) 2006 Yahoo! Inc. All rights reserved. */
+
+/**
+ * @class Abstract node class
+ * @constructor
+ * @param oData {object} a string or object containing the data that will
+ * be used to render this node
+ * @param oParent {YAHOO.widget.Node} this node's parent node
+ * @param expanded {boolean} the initial expanded/collapsed state
+ */
+YAHOO.widget.Node = function(oData, oParent, expanded) {
+ if (oParent) { this.init(oData, oParent, expanded); }
+};
+
+YAHOO.widget.Node.prototype = {
+
+ /**
+ * The index for this instance obtained from global counter in YAHOO.widget.TreeView.
+ *
+ * @type int
+ */
+ index: 0,
+
+ /**
+ * This node's child node collection.
+ *
+ * @type YAHOO.widget.Node[]
+ */
+ children: null,
+
+ /**
+ * Tree instance this node is part of
+ *
+ * @type YAHOO.widget.TreeView
+ */
+ tree: null,
+
+ /**
+ * The data linked to this node. This can be any object or primitive
+ * value, and the data can be used in getNodeHtml().
+ *
+ * @type object
+ */
+ data: null,
+
+ /**
+ * Parent node
+ *
+ * @type YAHOO.widget.Node
+ */
+ parent: null,
+
+ /**
+ * The depth of this node. We start at -1 for the root node.
+ *
+ * @type int
+ */
+ depth: -1,
+
+ /**
+ * The href for the node's label. If one is not specified, the href will
+ * be set so that it toggles the node.
+ *
+ * @type string
+ */
+ href: null,
+
+ /**
+ * The label href target, defaults to current window
+ *
+ * @type string
+ */
+ target: "_self",
+
+ /**
+ * The node's expanded/collapsed state
+ *
+ * @type boolean
+ */
+ expanded: false,
+
+ /**
+ * Can multiple children be expanded at once?
+ *
+ * @type boolean
+ */
+ multiExpand: true,
+
+ /**
+ * Should we render children for a collapsed node? It is possible that the
+ * implementer will want to render the hidden data... @todo verify that we
+ * need this, and implement it if we do.
+ *
+ * @type boolean
+ */
+ renderHidden: false,
+
+ /**
+ * Flag that is set to true the first time this node's children are rendered.
+ *
+ * @type boolean
+ */
+ childrenRendered: false,
+
+ /**
+ * This node's previous sibling
+ *
+ * @type YAHOO.widget.Node
+ */
+ previousSibling: null,
+
+ /**
+ * This node's next sibling
+ *
+ * @type YAHOO.widget.Node
+ */
+ nextSibling: null,
+
+ /**
+ * We can set the node up to call an external method to get the child
+ * data dynamically.
+ *
+ * @type boolean
+ * @private
+ */
+ _dynLoad: false,
+
+ /**
+ * Function to execute when we need to get this node's child data.
+ *
+ * @type function
+ */
+ dataLoader: null,
+
+ /**
+ * This is true for dynamically loading nodes while waiting for the
+ * callback to return.
+ *
+ * @type boolean
+ */
+ isLoading: false,
+
+ /**
+ * The toggle/branch icon will not show if this is set to false. This
+ * could be useful if the implementer wants to have the child contain
+ * extra info about the parent, rather than an actual node.
+ *
+ * @type boolean
+ */
+ hasIcon: true,
+
+ /**
+ * Initializes this node, gets some of the properties from the parent
+ *
+ * @param oData {object} a string or object containing the data that will
+ * be used to render this node
+ * @param oParent {YAHOO.widget.Node} this node's parent node
+ * @param expanded {boolean} the initial expanded/collapsed state
+ */
+ init: function(oData, oParent, expanded) {
+ this.data = oData;
+ this.children = [];
+ this.index = YAHOO.widget.TreeView.nodeCount;
+ ++YAHOO.widget.TreeView.nodeCount;
+ this.expanded = expanded;
+
+ // oParent should never be null except when we create the root node.
+ if (oParent) {
+ this.tree = oParent.tree;
+ this.parent = oParent;
+ this.href = "javascript:" + this.getToggleLink();
+ this.depth = oParent.depth + 1;
+ this.multiExpand = oParent.multiExpand;
+
+ oParent.appendChild(this);
+ }
+ },
+
+ /**
+ * Appends a node to the child collection.
+ *
+ * @param node {YAHOO.widget.Node} the new node
+ * @return {YAHOO.widget.Node} the child node
+ * @private
+ */
+ appendChild: function(node) {
+ if (this.hasChildren()) {
+ var sib = this.children[this.children.length - 1];
+ sib.nextSibling = node;
+ node.previousSibling = sib;
+ }
+
+ this.tree.regNode(node);
+ this.children[this.children.length] = node;
+ return node;
+
+ },
+
+ /**
+ * Returns a node array of this node's siblings, null if none.
+ *
+ * @return YAHOO.widget.Node[]
+ */
+ getSiblings: function() {
+ return this.parent.children;
+ },
+
+ /**
+ * Shows this node's children
+ */
+ showChildren: function() {
+ if (!this.tree.animateExpand(this.getChildrenEl())) {
+ if (this.hasChildren()) {
+ this.getChildrenEl().style.display = "";
+ }
+ }
+ },
+
+ /**
+ * Hides this node's children
+ */
+ hideChildren: function() {
+
+ if (!this.tree.animateCollapse(this.getChildrenEl())) {
+ this.getChildrenEl().style.display = "none";
+ }
+ },
+
+ /**
+ * Returns the id for this node's container div
+ *
+ * @return {string} the element id
+ */
+ getElId: function() {
+ return "ygtv" + this.index;
+ },
+
+ /**
+ * Returns the id for this node's children div
+ *
+ * @return {string} the element id for this node's children div
+ */
+ getChildrenElId: function() {
+ return "ygtvc" + this.index;
+ },
+
+ /**
+ * Returns the id for this node's toggle element
+ *
+ * @return {string} the toggel element id
+ */
+ getToggleElId: function() {
+ return "ygtvt" + this.index;
+ },
+
+ /**
+ * Returns this node's container html element
+ *
+ * @return {Object} the container html element
+ */
+ getEl: function() {
+ return document.getElementById(this.getElId());
+ },
+
+ /**
+ * Returns the div that was generated for this node's children
+ *
+ * @return {Object} this node's children div
+ */
+ getChildrenEl: function() {
+ return document.getElementById(this.getChildrenElId());
+ },
+
+ /**
+ * Returns the element that is being used for this node's toggle.
+ *
+ * @return {Object} this node's toggel html element
+ */
+ getToggleEl: function() {
+ return document.getElementById(this.getToggleElId());
+ },
+
+ /**
+ * Generates the link that will invoke this node's toggle method
+ *
+ * @return {string} the javascript url for toggling this node
+ */
+ getToggleLink: function() {
+ return "YAHOO.widget.TreeView.getNode(\'" + this.tree.id + "\'," +
+ this.index + ").toggle()";
+ },
+
+ /**
+ * Hides this nodes children (creating them if necessary), changes the
+ * toggle style.
+ */
+ collapse: function() {
+ // Only collapse if currently expanded
+ if (!this.expanded) { return; }
+
+ if (!this.getEl()) {
+ this.expanded = false;
+ return;
+ }
+
+ // hide the child div
+ this.hideChildren();
+ this.expanded = false;
+
+ if (this.hasIcon) {
+ this.getToggleEl().className = this.getStyle();
+ }
+
+ // fire the collapse event handler
+ this.tree.onCollapse(this);
+ },
+
+ /**
+ * Shows this nodes children (creating them if necessary), changes the
+ * toggle style, and collapses its siblings if multiExpand is not set.
+ */
+ expand: function() {
+ // Only expand if currently collapsed.
+ if (this.expanded) { return; }
+
+ if (!this.getEl()) {
+ this.expanded = true;
+ return;
+ }
+
+ if (! this.childrenRendered) {
+ this.getChildrenEl().innerHTML = this.renderChildren();
+ }
+
+ this.expanded = true;
+ if (this.hasIcon) {
+ this.getToggleEl().className = this.getStyle();
+ }
+
+ // We do an extra check for children here because the lazy
+ // load feature can expose nodes that have no children.
+
+ // if (!this.hasChildren()) {
+ if (this.isLoading) {
+ this.expanded = false;
+ return;
+ }
+
+ if (! this.multiExpand) {
+ var sibs = this.getSiblings();
+ for (var i=0; i<sibs.length; ++i) {
+ if (sibs[i] != this && sibs[i].expanded) {
+ sibs[i].collapse();
+ }
+ }
+ }
+
+ this.showChildren();
+
+ // fire the expand event handler
+ this.tree.onExpand(this);
+ },
+
+ /**
+ * Returns the css style name for the toggle
+ *
+ * @return {string} the css class for this node's toggle
+ */
+ getStyle: function() {
+ if (this.isLoading) {
+ return "ygtvloading";
+ } else {
+ // location top or bottom, middle nodes also get the top style
+ var loc = (this.nextSibling) ? "t" : "l";
+
+ // type p=plus(expand), m=minus(collapase), n=none(no children)
+ var type = "n";
+ if (this.hasChildren(true) || this.isDynamic()) {
+ type = (this.expanded) ? "m" : "p";
+ }
+
+ return "ygtv" + loc + type;
+ }
+ },
+
+ /**
+ * Returns the hover style for the icon
+ * @return {string} the css class hover state
+ */
+ getHoverStyle: function() {
+ var s = this.getStyle();
+ if (this.hasChildren(true) && !this.isLoading) {
+ s += "h";
+ }
+ return s;
+ },
+
+ /**
+ * Recursively expands all of this node's children.
+ */
+ expandAll: function() {
+ for (var i=0;i<this.children.length;++i) {
+ var c = this.children[i];
+ if (c.isDynamic()) {
+ alert("Not supported (lazy load + expand all)");
+ break;
+ } else if (! c.multiExpand) {
+ alert("Not supported (no multi-expand + expand all)");
+ break;
+ } else {
+ c.expand();
+ c.expandAll();
+ }
+ }
+ },
+
+ /**
+ * Recursively collapses all of this node's children.
+ */
+ collapseAll: function() {
+ for (var i=0;i<this.children.length;++i) {
+ this.children[i].collapse();
+ this.children[i].collapseAll();
+ }
+ },
+
+ /**
+ * Configures this node for dynamically obtaining the child data
+ * when the node is first expanded.
+ *
+ * @param fmDataLoader {function} the function that will be used to get the data.
+ */
+ setDynamicLoad: function(fnDataLoader) {
+ this.dataLoader = fnDataLoader;
+ this._dynLoad = true;
+ },
+
+ /**
+ * Evaluates if this node is the root node of the tree
+ *
+ * @return {boolean} true if this is the root node
+ */
+ isRoot: function() {
+ return (this == this.tree.root);
+ },
+
+ /**
+ * Evaluates if this node's children should be loaded dynamically. Looks for
+ * the property both in this instance and the root node. If the tree is
+ * defined to load all children dynamically, the data callback function is
+ * defined in the root node
+ *
+ * @return {boolean} true if this node's children are to be loaded dynamically
+ */
+ isDynamic: function() {
+ var lazy = (!this.isRoot() && (this._dynLoad || this.tree.root._dynLoad));
+ return lazy;
+ },
+
+ /**
+ * Checks if this node has children. If this node is lazy-loading and the
+ * children have not been rendered, we do not know whether or not there
+ * are actual children. In most cases, we need to assume that there are
+ * children (for instance, the toggle needs to show the expandable
+ * presentation state). In other times we want to know if there are rendered
+ * children. For the latter, "checkForLazyLoad" should be false.
+ *
+ * @param checkForLazyLoad {boolean} should we check for unloaded children?
+ * @return {boolean} true if this has children or if it might and we are
+ * checking for this condition.
+ */
+ hasChildren: function(checkForLazyLoad) {
+ return ( this.children.length > 0 ||
+ (checkForLazyLoad && this.isDynamic() && !this.childrenRendered) );
+ },
+
+ /**
+ * Expands if node is collapsed, collapses otherwise.
+ */
+ toggle: function() {
+ if (!this.tree.locked && ( this.hasChildren(true) || this.isDynamic()) ) {
+ if (this.expanded) { this.collapse(); } else { this.expand(); }
+ }
+ },
+
+ /**
+ * Returns the markup for this node and its children.
+ *
+ * @return {string} the markup for this node and its expanded children.
+ */
+ getHtml: function() {
+ var sb = [];
+ sb[sb.length] = '<div class="ygtvitem" id="' + this.getElId() + '">';
+ sb[sb.length] = this.getNodeHtml();
+ sb[sb.length] = this.getChildrenHtml();
+ sb[sb.length] = '</div>';
+ return sb.join("");
+ },
+
+ /**
+ * Called when first rendering the tree. We always build the div that will
+ * contain this nodes children, but we don't render the children themselves
+ * unless this node is expanded.
+ *
+ * @return {string} the children container div html and any expanded children
+ * @private
+ */
+ getChildrenHtml: function() {
+ var sb = [];
+ sb[sb.length] = '<div class="ygtvchildren"';
+ sb[sb.length] = ' id="' + this.getChildrenElId() + '"';
+ if (!this.expanded) {
+ sb[sb.length] = ' style="display:none;"';
+ }
+ sb[sb.length] = '>';
+
+ // Don't render the actual child node HTML unless this node is expanded.
+ if (this.hasChildren(true) && this.expanded) {
+ sb[sb.length] = this.renderChildren();
+ }
+
+ sb[sb.length] = '</div>';
+
+ return sb.join("");
+ },
+
+ /**
+ * Generates the markup for the child nodes. This is not done until the node
+ * is expanded.
+ *
+ * @return {string} the html for this node's children
+ * @private
+ */
+ renderChildren: function() {
+
+
+ var node = this;
+
+ if (this.isDynamic() && !this.childrenRendered) {
+ this.isLoading = true;
+ this.tree.locked = true;
+
+ if (this.dataLoader) {
+ setTimeout(
+ function() {
+ node.dataLoader(node,
+ function() {
+ node.loadComplete();
+ });
+ }, 10);
+
+ } else if (this.tree.root.dataLoader) {
+
+ setTimeout(
+ function() {
+ node.tree.root.dataLoader(node,
+ function() {
+ node.loadComplete();
+ });
+ }, 10);
+
+ } else {
+ return "Error: data loader not found or not specified.";
+ }
+
+ return "";
+
+ } else {
+ return this.completeRender();
+ }
+ },
+
+ /**
+ * Called when we know we have all the child data.
+ * @return {string} children html
+ */
+ completeRender: function() {
+ var sb = [];
+
+ for (var i=0; i < this.children.length; ++i) {
+ sb[sb.length] = this.children[i].getHtml();
+ }
+
+ this.childrenRendered = true;
+
+ return sb.join("");
+ },
+
+ /**
+ * Load complete is the callback function we pass to the data provider
+ * in dynamic load situations.
+ */
+ loadComplete: function() {
+ this.getChildrenEl().innerHTML = this.completeRender();
+ this.isLoading = false;
+ this.expand();
+ this.tree.locked = false;
+ },
+
+ /**
+ * Returns this node's ancestor at the specified depth.
+ *
+ * @param {int} depth the depth of the ancestor.
+ * @return {YAHOO.widget.Node} the ancestor
+ */
+ getAncestor: function(depth) {
+ if (depth >= this.depth || depth < 0) {
+ return null;
+ }
+
+ var p = this.parent;
+
+ while (p.depth > depth) {
+ p = p.parent;
+ }
+
+ return p;
+ },
+
+ /**
+ * Returns the css class for the spacer at the specified depth for
+ * this node. If this node's ancestor at the specified depth
+ * has a next sibling the presentation is different than if it
+ * does not have a next sibling
+ *
+ * @param {int} depth the depth of the ancestor.
+ * @return {string} the css class for the spacer
+ */
+ getDepthStyle: function(depth) {
+ return (this.getAncestor(depth).nextSibling) ?
+ "ygtvdepthcell" : "ygtvblankdepthcell";
+ },
+
+ /**
+ * Get the markup for the node. This is designed to be overrided so that we can
+ * support different types of nodes.
+ *
+ * @return {string} the html for this node
+ */
+ getNodeHtml: function() {
+ return "";
+ }
+
+};
+
+/* Copyright (c) 2006 Yahoo! Inc. All rights reserved. */
+
+/**
+ * @class A custom YAHOO.widget.Node that handles the unique nature of
+ * the virtual, presentationless root node.
+ *
+ * @extends YAHOO.widget.Node
+ * @constructor
+ */
+YAHOO.widget.RootNode = function(oTree) {
+ // Initialize the node with null params. The root node is a
+ // special case where the node has no presentation. So we have
+ // to alter the standard properties a bit.
+ this.init(null, null, true);
+
+ /**
+ * For the root node, we get the tree reference from as a param
+ * to the constructor instead of from the parent element.
+ *
+ * @type YAHOO.widget.TreeView
+ */
+ this.tree = oTree;
+};
+YAHOO.widget.RootNode.prototype = new YAHOO.widget.Node();
+
+// overrides YAHOO.widget.Node
+YAHOO.widget.RootNode.prototype.getNodeHtml = function() {
+ return "";
+};
+
+/* Copyright (c) 2006 Yahoo! Inc. All rights reserved. */
+
+/**
+ * @class The default node presentation. The first parameter should be
+ * either a string that will be used as the node's label, or an object
+ * that has a string propery called label. By default, the clicking the
+ * label will toggle the expanded/collapsed state of the node. By
+ * changing the href property of the instance, this behavior can be
+ * changed so that the label will go to the specified href.
+ *
+ * @extends YAHOO.widget.Node
+ * @constructor
+ * @param oData {object} a string or object containing the data that will
+ * be used to render this node
+ * @param oParent {YAHOO.widget.Node} this node's parent node
+ * @param expanded {boolean} the initial expanded/collapsed state
+ */
+YAHOO.widget.TextNode = function(oData, oParent, expanded) {
+ if (oParent) {
+ this.init(oData, oParent, expanded);
+ this.setUpLabel(oData);
+ }
+};
+
+YAHOO.widget.TextNode.prototype = new YAHOO.widget.Node();
+
+/**
+ * The CSS class for the label href. Defaults to ygtvlabel, but can be
+ * overridden to provide a custom presentation for a specific node.
+ *
+ * @type string
+ */
+YAHOO.widget.TextNode.prototype.labelStyle = "ygtvlabel";
+
+/**
+ * The derived element id of the label for this node
+ *
+ * @type string
+ */
+YAHOO.widget.TextNode.prototype.labelElId = null;
+
+/**
+ * The text for the label. It is assumed that the oData parameter will
+ * either be a string that will be used as the label, or an object that
+ * has a property called "label" that we will use.
+ *
+ * @type string
+ */
+YAHOO.widget.TextNode.prototype.label = null;
+
+/**
+ * Sets up the node label
+ *
+ * @param oData string containing the label, or an object with a label property
+ */
+YAHOO.widget.TextNode.prototype.setUpLabel = function(oData) {
+ if (typeof oData == "string") {
+ oData = { label: oData };
+ }
+ this.label = oData.label;
+
+ // update the link
+ if (oData.href) {
+ this.href = oData.href;
+ }
+
+ // set the target
+ if (oData.target) {
+ this.target = oData.target;
+ }
+
+ this.labelElId = "ygtvlabelel" + this.index;
+};
+
+/**
+ * Returns the label element
+ *
+ * @return {object} the element
+ */
+YAHOO.widget.TextNode.prototype.getLabelEl = function() {
+ return document.getElementById(this.labelElId);
+};
+
+// overrides YAHOO.widget.Node
+YAHOO.widget.TextNode.prototype.getNodeHtml = function() {
+ var sb = new Array();
+
+ sb[sb.length] = '<table border="0" cellpadding="0" cellspacing="0">';
+ sb[sb.length] = '<tr>';
+
+ for (i=0;i<this.depth;++i) {
+ // sb[sb.length] = '<td class="ygtvdepthcell"> </td>';
+ sb[sb.length] = '<td class="' + this.getDepthStyle(i) + '"> </td>';
+ }
+
+ var getNode = 'YAHOO.widget.TreeView.getNode(\'' +
+ this.tree.id + '\',' + this.index + ')';
+
+ sb[sb.length] = '<td';
+ // sb[sb.length] = ' onselectstart="return false"';
+ sb[sb.length] = ' id="' + this.getToggleElId() + '"';
+ sb[sb.length] = ' class="' + this.getStyle() + '"';
+ if (this.hasChildren(true)) {
+ sb[sb.length] = ' onmouseover="this.className=';
+ sb[sb.length] = getNode + '.getHoverStyle()"';
+ sb[sb.length] = ' onmouseout="this.className=';
+ sb[sb.length] = getNode + '.getStyle()"';
+ }
+ sb[sb.length] = ' onclick="javascript:' + this.getToggleLink() + '"> ';
+ sb[sb.length] = '</td>';
+ sb[sb.length] = '<td>';
+ sb[sb.length] = '<a';
+ sb[sb.length] = ' id="' + this.labelElId + '"';
+ sb[sb.length] = ' class="' + this.labelStyle + '"';
+ sb[sb.length] = ' href="' + this.href + '"';
+ sb[sb.length] = ' target="' + this.target + '"';
+ if (this.hasChildren(true)) {
+ sb[sb.length] = ' onmouseover="document.getElementById(\'';
+ sb[sb.length] = this.getToggleElId() + '\').className=';
+ sb[sb.length] = getNode + '.getHoverStyle()"';
+ sb[sb.length] = ' onmouseout="document.getElementById(\'';
+ sb[sb.length] = this.getToggleElId() + '\').className=';
+ sb[sb.length] = getNode + '.getStyle()"';
+ }
+ sb[sb.length] = ' >';
+ sb[sb.length] = this.label;
+ sb[sb.length] = '</a>';
+ sb[sb.length] = '</td>';
+ sb[sb.length] = '</tr>';
+ sb[sb.length] = '</table>';
+
+ return sb.join("");
+};
+
+/* Copyright (c) 2006 Yahoo! Inc. All rights reserved. */
+
+/**
+ * @class menu-specific implementation that differs in that only one sibling
+ * can be expanded at a time.
+ * @extends YAHOO.widget.TextNode
+ * @constructor
+ */
+YAHOO.widget.MenuNode = function(oData, oParent, expanded) {
+ if (oParent) {
+ this.init(oData, oParent, expanded);
+ this.setUpLabel(oData);
+ }
+
+ // Menus usually allow only one branch to be open at a time.
+ this.multiExpand = false;
+
+};
+
+YAHOO.widget.MenuNode.prototype = new YAHOO.widget.TextNode();
+
+/* Copyright (c) 2006 Yahoo! Inc. All rights reserved. */
+
+/**
+ * @class This implementation takes either a string or object for the
+ * oData argument. If is it a string, we will use it for the display
+ * of this node (and it can contain any html code). If the parameter
+ * is an object, we look for a parameter called "html" that will be
+ * used for this node's display.
+ *
+ * @extends YAHOO.widget.Node
+ * @constructor
+ * @param oData {object} a string or object containing the data that will
+ * be used to render this node
+ * @param oParent {YAHOO.widget.Node} this node's parent node
+ * @param expanded {boolean} the initial expanded/collapsed state
+ * @param hasIcon {boolean} specifies whether or not leaf nodes should
+ * have an icon
+ */
+YAHOO.widget.HTMLNode = function(oData, oParent, expanded, hasIcon) {
+ if (oParent) {
+ this.init(oData, oParent, expanded);
+ this.initContent(oData, hasIcon);
+ }
+};
+
+YAHOO.widget.HTMLNode.prototype = new YAHOO.widget.Node();
+
+/**
+ * The CSS class for the label href. Defaults to ygtvlabel, but can be
+ * overridden to provide a custom presentation for a specific node.
+ *
+ * @type string
+ */
+YAHOO.widget.HTMLNode.prototype.contentStyle = "ygtvhtml";
+
+/**
+ * The generated id that will contain the data passed in by the implementer.
+ *
+ * @type string
+ */
+YAHOO.widget.HTMLNode.prototype.contentElId = null;
+
+/**
+ * The HTML content to use for this node's display
+ *
+ * @type string
+ */
+YAHOO.widget.HTMLNode.prototype.content = null;
+
+/**
+ * Sets up the node label
+ *
+ * @param {object} html string or object containing a html field
+ * @param {boolean} hasIcon determines if the node will be rendered with an
+ * icon or not
+ */
+YAHOO.widget.HTMLNode.prototype.initContent = function(oData, hasIcon) {
+ if (typeof oData == "string") {
+ oData = { html: oData };
+ }
+
+ this.html = oData.html;
+ this.contentElId = "ygtvcontentel" + this.index;
+ this.hasIcon = hasIcon;
+};
+
+/**
+ * Returns the outer html element for this node's content
+ *
+ * @return {Object} the element
+ */
+YAHOO.widget.HTMLNode.prototype.getContentEl = function() {
+ return document.getElementById(this.contentElId);
+};
+
+// overrides YAHOO.widget.Node
+YAHOO.widget.HTMLNode.prototype.getNodeHtml = function() {
+ var sb = new Array();
+
+ sb[sb.length] = '<table border="0" cellpadding="0" cellspacing="0">';
+ sb[sb.length] = '<tr>';
+
+ for (i=0;i<this.depth;++i) {
+ sb[sb.length] = '<td class="' + this.getDepthStyle(i) + '"> </td>';
+ }
+
+ if (this.hasIcon) {
+ sb[sb.length] = '<td';
+ sb[sb.length] = ' id="' + this.getToggleElId() + '"';
+ sb[sb.length] = ' class="' + this.getStyle() + '"';
+ sb[sb.length] = ' onclick="javascript:' + this.getToggleLink() + '"> ';
+ if (this.hasChildren(true)) {
+ sb[sb.length] = ' onmouseover="this.className=';
+ sb[sb.length] = 'YAHOO.widget.TreeView.getNode(\'';
+ sb[sb.length] = this.tree.id + '\',' + this.index + ').getHoverStyle()"';
+ sb[sb.length] = ' onmouseout="this.className=';
+ sb[sb.length] = 'YAHOO.widget.TreeView.getNode(\'';
+ sb[sb.length] = this.tree.id + '\',' + this.index + ').getStyle()"';
+ }
+ sb[sb.length] = '</td>';
+ }
+
+ sb[sb.length] = '<td';
+ sb[sb.length] = ' id="' + this.contentElId + '"';
+ sb[sb.length] = ' class="' + this.contentStyle + '"';
+ sb[sb.length] = ' >';
+ sb[sb.length] = this.html;
+ sb[sb.length] = '</td>';
+ sb[sb.length] = '</tr>';
+ sb[sb.length] = '</table>';
+
+ return sb.join("");
+};
+
+/* Copyright (c) 2006 Yahoo! Inc. All rights reserved. */
+
+/**
+ * Static factory class for tree view expand/collapse animations
+ */
+YAHOO.widget.TVAnim = new function() {
+ /**
+ * Constant for the fade in animation
+ *
+ * @type string
+ */
+ this.FADE_IN = "YAHOO.widget.TVFadeIn";
+
+ /**
+ * Constant for the fade out animation
+ *
+ * @type string
+ */
+ this.FADE_OUT = "YAHOO.widget.TVFadeOut";
+
+ /**
+ * Returns a ygAnim instance of the given type
+ *
+ * @param type {string} the type of animation
+ * @param el {HTMLElement} the element to element (probably the children div)
+ * @param callback {function} function to invoke when the animation is done.
+ * @return {ygAnim} the animation instance
+ */
+ this.getAnim = function(type, el, callback) {
+ switch (type) {
+ case this.FADE_IN: return new YAHOO.widget.TVFadeIn(el, callback);
+ case this.FADE_OUT: return new YAHOO.widget.TVFadeOut(el, callback);
+ default: return null;
+ }
+ };
+
+ /**
+ * Returns true if the specified animation class is available
+ *
+ * @param type {string} the type of animation
+ * @return {boolean} true if valid, false if not
+ */
+ this.isValid = function(type) {
+ return ( "undefined" != eval("typeof " + type) );
+ };
+};
+
+/* Copyright (c) 2006 Yahoo! Inc. All rights reserved. */
+
+/**
+ * 1/2 second fade-in
+ *
+ * @constructor
+ * @param el {HTMLElement} the element to animate
+ * @param callback {function} function to invoke when the animation is finished
+ */
+YAHOO.widget.TVFadeIn = function(el, callback) {
+ /**
+ * The animation dom ref
+ */
+ this.el = el;
+
+ /**
+ * the callback to invoke when the animation is complete
+ *
+ * @type function
+ */
+ this.callback = callback;
+
+ /**
+ * @private
+ */
+};
+
+/**
+ * Performs the animation
+ */
+YAHOO.widget.TVFadeIn.prototype = {
+ animate: function() {
+ var tvanim = this;
+
+ var s = this.el.style;
+ s.opacity = 0.1;
+ s.filter = "alpha(opacity=10)";
+ s.display = "";
+
+ // var dur = ( navigator.userAgent.match(/msie/gi) ) ? 0.05 : 0.4;
+ var dur = 0.4;
+ // var a = new ygAnim_Fade(this.el, dur, 1);
+ // a.setStart(0.1);
+ // a.onComplete = function() { tvanim.onComplete(); };
+
+ // var a = new YAHOO.util.Anim(this.el, 'opacity', 0.1, 1);
+ var a = new YAHOO.util.Anim(this.el, {opacity: {from: 0.1, to: 1, unit:""}}, dur);
+ a.onComplete.subscribe( function() { tvanim.onComplete(); } );
+ a.animate();
+ },
+
+ /**
+ * Clean up and invoke callback
+ */
+ onComplete: function() {
+ this.callback();
+ }
+};
+
+/* Copyright (c) 2006 Yahoo! Inc. All rights reserved. */
+
+/**
+ * 1/2 second fade out
+ *
+ * @constructor
+ * @param el {HTMLElement} the element to animate
+ * @param callback {Function} function to invoke when the animation is finished
+ */
+YAHOO.widget.TVFadeOut = function(el, callback) {
+ /**
+ * The animation dom ref
+ */
+ this.el = el;
+
+ /**
+ * the callback to invoke when the animation is complete
+ *
+ * @type function
+ */
+ this.callback = callback;
+
+ /**
+ * @private
+ */
+};
+
+/**
+ * Performs the animation
+ */
+YAHOO.widget.TVFadeOut.prototype = {
+ animate: function() {
+ var tvanim = this;
+ // var dur = ( navigator.userAgent.match(/msie/gi) ) ? 0.05 : 0.4;
+ var dur = 0.4;
+ // var a = new ygAnim_Fade(this.el, dur, 0.1);
+ // a.onComplete = function() { tvanim.onComplete(); };
+
+ // var a = new YAHOO.util.Anim(this.el, 'opacity', 1, 0.1);
+ var a = new YAHOO.util.Anim(this.el, {opacity: {from: 1, to: 0.1, unit:""}}, dur);
+ a.onComplete.subscribe( function() { tvanim.onComplete(); } );
+ a.animate();
+ },
+
+ /**
+ * Clean up and invoke callback
+ */
+ onComplete: function() {
+ var s = this.el.style;
+ s.display = "none";
+ // s.opacity = 1;
+ s.filter = "alpha(opacity=100)";
+ this.callback();
+ }
+};
+
</style>
- <script type="text/javascript" src="bundled-libs/dragdrop.js" ></script>
- <script type="text/javascript" src="bundled-libs/imgedit.js" ></script>
+ <script type="text/javascript" src="{serendipity_getFile file='dragdrop.js'}" ></script>
+ <script type="text/javascript" src="{serendipity_getFile file='imgedit.js'}" ></script>
{/if}
- <script type="text/javascript" src="bundled-libs/YahooUI/treeview/YAHOO.js"></script>
- <script type="text/javascript" src="bundled-libs/YahooUI/treeview/treeview.js" ></script>
+ <script type="text/javascript" src="{serendipity_getFile file='YahooUI/treeview/YAHOO.js'}"></script>
+ <script type="text/javascript" src="{serendipity_getFile file='YahooUI/treeview/treeview.js'}"></script>
</head>
<script type="text/javascript">
--- /dev/null
+/**********************************************************
+ Very minorly modified from the example by Tim Taylor
+ http://tool-man.org/examples/sorting.html
+
+ Added Coordinate.prototype.inside( northwest, southeast );
+
+ Copyright (c) 2005 Tim Taylor Consulting <http://tool-man.org/>
+
+ 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 Coordinates = {
+ ORIGIN : new Coordinate(0, 0),
+
+ northwestPosition : function(element) {
+ var x = parseInt(element.style.left);
+ var y = parseInt(element.style.top);
+
+ return new Coordinate(isNaN(x) ? 0 : x, isNaN(y) ? 0 : y);
+ },
+
+ southeastPosition : function(element) {
+ return Coordinates.northwestPosition(element).plus(
+ new Coordinate(element.offsetWidth, element.offsetHeight));
+ },
+
+ northwestOffset : function(element, isRecursive) {
+ var offset = new Coordinate(element.offsetLeft, element.offsetTop);
+
+ if (!isRecursive) return offset;
+
+ var parent = element.offsetParent;
+ while (parent) {
+ offset = offset.plus(
+ new Coordinate(parent.offsetLeft, parent.offsetTop));
+ parent = parent.offsetParent;
+ }
+ return offset;
+ },
+
+ southeastOffset : function(element, isRecursive) {
+ return Coordinates.northwestOffset(element, isRecursive).plus(
+ new Coordinate(element.offsetWidth, element.offsetHeight));
+ },
+
+ fixEvent : function(event) {
+ event.windowCoordinate = new Coordinate(event.clientX, event.clientY);
+ }
+};
+
+function Coordinate(x, y) {
+ this.x = x;
+ this.y = y;
+}
+
+Coordinate.prototype.toString = function() {
+ return "(" + this.x + "," + this.y + ")";
+}
+
+Coordinate.prototype.plus = function(that) {
+ return new Coordinate(this.x + that.x, this.y + that.y);
+}
+
+Coordinate.prototype.minus = function(that) {
+ return new Coordinate(this.x - that.x, this.y - that.y);
+}
+
+Coordinate.prototype.distance = function(that) {
+ var deltaX = this.x - that.x;
+ var deltaY = this.y - that.y;
+
+ return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2));
+}
+
+Coordinate.prototype.max = function(that) {
+ var x = Math.max(this.x, that.x);
+ var y = Math.max(this.y, that.y);
+ return new Coordinate(x, y);
+}
+
+Coordinate.prototype.constrain = function(min, max) {
+ if (min.x > max.x || min.y > max.y) return this;
+
+ var x = this.x;
+ var y = this.y;
+
+ if (min.x != null) x = Math.max(x, min.x);
+ if (max.x != null) x = Math.min(x, max.x);
+ if (min.y != null) y = Math.max(y, min.y);
+ if (max.y != null) y = Math.min(y, max.y);
+
+ return new Coordinate(x, y);
+}
+
+Coordinate.prototype.reposition = function(element) {
+ element.style["top"] = this.y + "px";
+ element.style["left"] = this.x + "px";
+}
+
+Coordinate.prototype.equals = function(that) {
+ if (this == that) return true;
+ if (!that || that == null) return false;
+
+ return this.x == that.x && this.y == that.y;
+}
+
+// returns true of this point is inside specified box
+Coordinate.prototype.inside = function(northwest, southeast) {
+ if ((this.x >= northwest.x) && (this.x <= southeast.x) &&
+ (this.y >= northwest.y) && (this.y <= southeast.y)) {
+
+ return true;
+ }
+ return false;
+}
+
+/*
+ * drag.js - click & drag DOM elements
+ *
+ * originally based on Youngpup's dom-drag.js, www.youngpup.net
+ */
+
+/**********************************************************
+ Further modified from the example by Tim Taylor
+ http://tool-man.org/examples/sorting.html
+
+ Changed onMouseMove where it calls group.onDrag and then
+ adjusts the offset for changes to the DOM. If the item
+ being moved changed parents it would be off so changed to
+ get the absolute offset (recursive northwestOffset).
+
+ **********************************************************/
+
+var Drag = {
+ BIG_Z_INDEX : 10000,
+ group : null,
+ isDragging : false,
+
+ makeDraggable : function(group) {
+ group.handle = group;
+ group.handle.group = group;
+
+ group.minX = null;
+ group.minY = null;
+ group.maxX = null;
+ group.maxY = null;
+ group.threshold = 0;
+ group.thresholdY = 0;
+ group.thresholdX = 0;
+
+ group.onDragStart = new Function();
+ group.onDragEnd = new Function();
+ group.onDrag = new Function();
+
+ // TODO: use element.prototype.myFunc
+ group.setDragHandle = Drag.setDragHandle;
+ group.setDragThreshold = Drag.setDragThreshold;
+ group.setDragThresholdX = Drag.setDragThresholdX;
+ group.setDragThresholdY = Drag.setDragThresholdY;
+ group.constrain = Drag.constrain;
+ group.constrainVertical = Drag.constrainVertical;
+ group.constrainHorizontal = Drag.constrainHorizontal;
+
+ group.onmousedown = Drag.onMouseDown;
+ },
+
+ constrainVertical : function() {
+ var nwOffset = Coordinates.northwestOffset(this, true);
+ this.minX = nwOffset.x;
+ this.maxX = nwOffset.x;
+ },
+
+ constrainHorizontal : function() {
+ var nwOffset = Coordinates.northwestOffset(this, true);
+ this.minY = nwOffset.y;
+ this.maxY = nwOffset.y;
+ },
+
+ constrain : function(nwPosition, sePosition) {
+ this.minX = nwPosition.x;
+ this.minY = nwPosition.y;
+ this.maxX = sePosition.x;
+ this.maxY = sePosition.y;
+ },
+
+ setDragHandle : function(handle) {
+ if (handle && handle != null) {
+ this.handle = handle;
+ } else {
+ this.handle = this;
+ }
+
+ this.handle.group = this;
+ this.onmousedown = null;
+ // this.onclick = Drag.toggleMe;
+ // this.handle.onclick = Drag.toggleMe;
+ this.handle.onmousedown = Drag.onMouseDown;
+ },
+
+ toggleMe : function(str) {
+ alert('Toggle: ' + str);
+ },
+
+ setDragThreshold : function(threshold) {
+ if (isNaN(parseInt(threshold))) return;
+
+ this.threshold = threshold;
+ },
+
+ setDragThresholdX : function(threshold) {
+ if (isNaN(parseInt(threshold))) return;
+
+ this.thresholdX = threshold;
+ },
+
+ setDragThresholdY : function(threshold) {
+ if (isNaN(parseInt(threshold))) return;
+
+ this.thresholdY = threshold;
+ },
+
+ onMouseDown : function(event) {
+ event = Drag.fixEvent(event);
+ Drag.group = this.group;
+
+ var group = this.group;
+ var mouse = event.windowCoordinate;
+ var nwOffset = Coordinates.northwestOffset(group, true);
+ var nwPosition = Coordinates.northwestPosition(group);
+ var sePosition = Coordinates.southeastPosition(group);
+ var seOffset = Coordinates.southeastOffset(group, true);
+
+ group.originalOpacity = group.style.opacity;
+ group.originalZIndex = group.style.zIndex;
+ group.initialWindowCoordinate = mouse;
+ // TODO: need a better name, but don't yet understand how it
+ // participates in the magic while dragging
+ group.dragCoordinate = mouse;
+
+ Drag.showStatus(mouse, nwPosition, sePosition, nwOffset, seOffset);
+
+ group.onDragStart(nwPosition, sePosition, nwOffset, seOffset);
+
+ // TODO: need better constraint API
+ if (group.minX != null)
+ group.minMouseX = mouse.x - nwPosition.x +
+ group.minX - nwOffset.x;
+ if (group.maxX != null)
+ group.maxMouseX = group.minMouseX + group.maxX - group.minX;
+
+ if (group.minY != null)
+ group.minMouseY = mouse.y - nwPosition.y +
+ group.minY - nwOffset.y;
+ if (group.maxY != null)
+ group.maxMouseY = group.minMouseY + group.maxY - group.minY;
+
+ group.mouseMin = new Coordinate(group.minMouseX, group.minMouseY);
+ group.mouseMax = new Coordinate(group.maxMouseX, group.maxMouseY);
+
+ document.onmousemove = Drag.onMouseMove;
+ document.onmouseup = Drag.onMouseUp;
+
+ return false;
+ },
+
+ showStatus : function(mouse, nwPosition, sePosition, nwOffset, seOffset) {
+ /*window.status =
+ "mouse: " + mouse.toString() + " " +
+ "NW pos: " + nwPosition.toString() + " " +
+ "SE pos: " + sePosition.toString() + " " +
+ "NW offset: " + nwOffset.toString() + " " +
+ "SE offset: " + seOffset.toString();*/
+ },
+
+ onMouseMove : function(event) {
+ event = Drag.fixEvent(event);
+ var group = Drag.group;
+ var mouse = event.windowCoordinate;
+ var nwOffset = Coordinates.northwestOffset(group, true);
+ var nwPosition = Coordinates.northwestPosition(group);
+ var sePosition = Coordinates.southeastPosition(group);
+ var seOffset = Coordinates.southeastOffset(group, true);
+
+ Drag.showStatus(mouse, nwPosition, sePosition, nwOffset, seOffset);
+
+ if (!Drag.isDragging) {
+ if (group.threshold > 0) {
+ var distance = group.initialWindowCoordinate.distance(
+ mouse);
+ if (distance < group.threshold) return true;
+ } else if (group.thresholdY > 0) {
+ var deltaY = Math.abs(group.initialWindowCoordinate.y - mouse.y);
+ if (deltaY < group.thresholdY) return true;
+ } else if (group.thresholdX > 0) {
+ var deltaX = Math.abs(group.initialWindowCoordinate.x - mouse.x);
+ if (deltaX < group.thresholdX) return true;
+ }
+
+ Drag.isDragging = true;
+ group.style["zIndex"] = Drag.BIG_Z_INDEX;
+ group.style["opacity"] = 0.75;
+ }
+
+ // TODO: need better constraint API
+ var adjusted = mouse.constrain(group.mouseMin, group.mouseMax);
+ nwPosition = nwPosition.plus(adjusted.minus(group.dragCoordinate));
+ nwPosition.reposition(group);
+ group.dragCoordinate = adjusted;
+
+ // once dragging has started, the position of the group
+ // relative to the mouse should stay fixed. They can get out
+ // of sync if the DOM is manipulated while dragging, so we
+ // correct the error here
+ //
+ // TODO: what we really want to do is find the offset from
+ // our corner to the mouse coordinate and adjust to keep it
+ // the same
+
+ // changed to be recursive/use absolute offset for corrections
+ var offsetBefore = Coordinates.northwestOffset(group, true);
+ group.onDrag(nwPosition, sePosition, nwOffset, seOffset);
+ var offsetAfter = Coordinates.northwestOffset(group, true);
+
+ if (!offsetBefore.equals(offsetAfter)) {
+ var errorDelta = offsetBefore.minus(offsetAfter);
+ nwPosition = Coordinates.northwestPosition(group).plus(errorDelta);
+ nwPosition.reposition(group);
+ }
+
+ return false;
+ },
+
+ onMouseUp : function(event) {
+ event = Drag.fixEvent(event);
+ var group = Drag.group;
+
+ var mouse = event.windowCoordinate;
+ var nwOffset = Coordinates.northwestOffset(group, true);
+ var nwPosition = Coordinates.northwestPosition(group);
+ var sePosition = Coordinates.southeastPosition(group);
+ var seOffset = Coordinates.southeastOffset(group, true);
+
+ document.onmousemove = null;
+ document.onmouseup = null;
+ group.onDragEnd(nwPosition, sePosition, nwOffset, seOffset);
+
+ if (Drag.isDragging) {
+ // restoring zIndex before opacity avoids visual flicker in Firefox
+ group.style["zIndex"] = group.originalZIndex;
+ group.style["opacity"] = group.originalOpacity;
+ }
+
+ Drag.group = null;
+ Drag.isDragging = false;
+
+ return false;
+ },
+
+ fixEvent : function(event) {
+ if (typeof event == 'undefined') event = window.event;
+ Coordinates.fixEvent(event);
+
+ return event;
+ }
+};
+
+/**********************************************************
+ Adapted from the sortable lists example by Tim Taylor
+ http://tool-man.org/examples/sorting.html
+ Modified by Tom Westcott : http://www.cyberdummy.co.uk
+ **********************************************************/
+
+var DragDrop = {
+ firstContainer : null,
+ lastContainer : null,
+ parent_id : null,
+ parent_group : null,
+ makeListContainer : function(list, group) {
+ // each container becomes a linked list node
+ if (this.firstContainer == null) {
+ this.firstContainer = this.lastContainer = list;
+ list.previousContainer = null;
+ list.nextContainer = null;
+ } else {
+ list.previousContainer = this.lastContainer;
+ list.nextContainer = null;
+ this.lastContainer.nextContainer = list;
+ this.lastContainer = list;
+ }
+
+ // these functions are called when an item is draged over
+ // a container or out of a container bounds. onDragOut
+ // is also called when the drag ends with an item having
+ // been added to the container
+ list.onDragOver = new Function();
+ list.onDragOut = new Function();
+ list.onDragDrop = new Function();
+ list.group = group;
+
+ var items = list.getElementsByTagName( "li" );
+
+ for (var i = 0; i < items.length; i++) {
+ DragDrop.makeItemDragable(items[i]);
+ }
+ },
+
+ serData : function ( group, theid ) {
+ var container = DragDrop.firstContainer;
+ var j = 0;
+ var string = "";
+
+ while (container != null) {
+ if(theid != null && container.id != theid) {
+ container = container.nextContainer;
+ continue;
+ }
+
+ if(group != null && container.group != group) {
+ container = container.nextContainer;
+ continue;
+ }
+
+ j ++;
+ if (j > 1) {
+ string += ":";
+ }
+ string += container.id;
+
+ var items = container.getElementsByTagName( "li" );
+ string += "(";
+ for (var i = 0; i < items.length; i++) {
+ if(i > 0) {
+ string += ",";
+ }
+
+ string += items[i].id;
+ }
+ string += ")";
+
+ container = container.nextContainer;
+ }
+
+ return string;
+ },
+
+ makeItemDragable : function(item) {
+ Drag.makeDraggable(item);
+ item.setDragThreshold(5);
+
+ // tracks if the item is currently outside all containers
+ item.isOutside = false;
+
+ item.onDragStart = DragDrop.onDragStart;
+ item.onDrag = DragDrop.onDrag;
+ item.onDragEnd = DragDrop.onDragEnd;
+
+ item.setDragHandle(document.getElementById('g' + item.id));
+ },
+
+ onDragStart : function(nwPosition, sePosition, nwOffset, seOffset) {
+ // update all container bounds, since they may have changed
+ // on a previous drag
+ //
+ // could be more smart about when to do this
+ var container = DragDrop.firstContainer;
+ while (container != null) {
+ container.northwest = Coordinates.northwestOffset( container, true );
+ container.southeast = Coordinates.southeastOffset( container, true );
+ container = container.nextContainer;
+ }
+
+ // item starts out over current parent
+ this.parentNode.onDragOver();
+ parent_id = this.parentNode.id;
+ parent_group = this.parentNode.group;
+ },
+
+ onDrag : function(nwPosition, sePosition, nwOffset, seOffset) {
+ // check if we were nowhere
+ if (this.isOutside) {
+ // check each container to see if in its bounds
+ var container = DragDrop.firstContainer;
+ while (container != null) {
+
+ if ((nwOffset.inside( container.northwest, container.southeast ) ||
+ seOffset.inside( container.northwest, container.southeast )) && container.group == parent_group) {
+ // we're inside this one
+ container.onDragOver();
+ this.isOutside = false;
+
+ // since isOutside was true, the current parent is a
+ // temporary clone of some previous container node and
+ // it needs to be removed from the document
+ var tempParent = this.parentNode;
+ tempParent.removeChild( this );
+ container.appendChild( this );
+ tempParent.parentNode.removeChild( tempParent );
+ break;
+ }
+ container = container.nextContainer;
+ }
+ // we're still not inside the bounds of any container
+ if (this.isOutside)
+ return;
+
+ // check if we're outside our parent's bounds
+ } else if (!(nwOffset.inside( this.parentNode.northwest, this.parentNode.southeast ) ||
+ seOffset.inside( this.parentNode.northwest, this.parentNode.southeast ))) {
+
+ this.parentNode.onDragOut();
+ this.isOutside = true;
+
+ // check if we're inside a new container's bounds
+ var container = DragDrop.firstContainer;
+ while (container != null) {
+ if ((nwOffset.inside( container.northwest, container.southeast ) ||
+ seOffset.inside( container.northwest, container.southeast )) && container.group == parent_group) {
+ // we're inside this one
+ container.onDragOver();
+ this.isOutside = false;
+ this.parentNode.removeChild( this );
+ container.appendChild( this );
+ break;
+ }
+ container = container.nextContainer;
+ }
+ // if we're not in any container now, make a temporary clone of
+ // the previous container node and add it to the document
+ if (this.isOutside) {
+ var tempParent = this.parentNode.cloneNode( false );
+ this.parentNode.removeChild( this );
+ tempParent.appendChild( this );
+ // body puts a border or item at bottom of page if do not have this
+ tempParent.style.border = 0;
+ document.getElementsByTagName( "body" ).item(0).appendChild( tempParent );
+ return;
+ }
+ }
+
+ // if we get here, we're inside some container bounds, so we do
+ // everything the original dragsort script did to swap us into the
+ // correct position
+
+ var parent = this.parentNode;
+
+ var item = this;
+ var next = DragUtils.nextItem(item);
+ while (next != null && this.offsetTop >= next.offsetTop - 2) {
+ var item = next;
+ var next = DragUtils.nextItem(item);
+ }
+ if (this != item) {
+ DragUtils.swap(this, next);
+ return;
+ }
+
+ var item = this;
+ var previous = DragUtils.previousItem(item);
+ while (previous != null && this.offsetTop <= previous.offsetTop + 2) {
+ var item = previous;
+ var previous = DragUtils.previousItem(item);
+ }
+ if (this != item) {
+ DragUtils.swap(this, item);
+ return;
+ }
+ },
+
+ onDragEnd : function(nwPosition, sePosition, nwOffset, seOffset) {
+ // if the drag ends and we're still outside all containers
+ // it's time to remove ourselves from the document or add
+ // to the trash bin
+ if (this.isOutside) {
+ var container = DragDrop.firstContainer;
+ while (container != null) {
+ if (container.id == parent_id) {
+ break;
+ }
+ container = container.nextContainer;
+ }
+ this.isOutside = false;
+ this.parentNode.removeChild( this );
+ container.appendChild( this );
+ this.style["top"] = "0px";
+ this.style["left"] = "0px";
+ //var container = DragDrop.firstContainer;
+ //container.appendChild( this );
+ return;
+ }
+ this.parentNode.onDragOut();
+ this.parentNode.onDragDrop();
+ this.style["top"] = "0px";
+ this.style["left"] = "0px";
+ }
+};
+
+var DragUtils = {
+ swap : function(item1, item2) {
+ var parent = item1.parentNode;
+ parent.removeChild(item1);
+ parent.insertBefore(item1, item2);
+
+ item1.style["top"] = "0px";
+ item1.style["left"] = "0px";
+ },
+
+ nextItem : function(item) {
+ var sibling = item.nextSibling;
+ while (sibling != null) {
+ if (sibling.nodeName == item.nodeName) return sibling;
+ sibling = sibling.nextSibling;
+ }
+ return null;
+ },
+
+ previousItem : function(item) {
+ var sibling = item.previousSibling;
+ while (sibling != null) {
+ if (sibling.nodeName == item.nodeName) return sibling;
+ sibling = sibling.previousSibling;
+ }
+ return null;
+ }
+};
+
+/*************************
+ * Custom Serendipity code
+ *************************/
+
+function pluginMoverInit() {
+ var list = document.getElementById("left_col");
+ DragDrop.makeListContainer(list, 'g1');
+ list.onDragOver = function() { this.style["border"] = "1px solid #4d759b"; };
+ list.onDragOut = function() { this.style["border"] = "none"; };
+
+ list = document.getElementById("hide_col");
+ DragDrop.makeListContainer(list, 'g1');
+ list.onDragOver = function() { this.style["border"] = "1px solid #4d759b"; };
+ list.onDragOut = function() {this.style["border"] = "none"; };
+
+ list = document.getElementById("right_col");
+ DragDrop.makeListContainer(list, 'g1');
+ list.onDragOver = function() { this.style["border"] = "1px solid #4d759b"; };
+ list.onDragOut = function() {this.style["border"] = "none"; };
+}
+
+function pluginMoverInitEvent() {
+ var list = document.getElementById("event_col");
+ DragDrop.makeListContainer(list, 'g1');
+ list.onDragOver = function() { this.style["border"] = "1px solid #4d759b"; };
+ list.onDragOut = function() { this.style["border"] = "none"; };
+
+ list = document.getElementById("eventh_col");
+ DragDrop.makeListContainer(list, 'g1');
+ list.onDragOver = function() { this.style["border"] = "1px solid #4d759b"; };
+ list.onDragOut = function() {this.style["border"] = "none"; };
+}
+
+function pluginMovergetSort() {
+ order = document.getElementById("order");
+ order.value = DragDrop.serData('g1', null);
+ return order.value;
+}
+
+function pluginMovergetSortEvent() {
+ order = document.getElementById("eventorder");
+ order.value = DragDrop.serData('g1', null);
+ return order.value;
+}
+
+/**************************************************
+ * dom-drag.js
+ * 09.25.2001
+ * www.youngpup.net
+ **************************************************
+ * 10.28.2001 - fixed minor bug where events
+ * sometimes fired off the handle, not the root.
+ **************************************************/
+
+var DOMDrag = {
+
+ obj : null,
+
+ init : function(o, oRoot, minX, maxX, minY, maxY, bSwapHorzRef, bSwapVertRef, fXMapper, fYMapper) {
+ o.onmousedown = DOMDrag.start;
+
+ o.hmode = bSwapHorzRef ? false : true ;
+ o.vmode = bSwapVertRef ? false : true ;
+
+ o.root = oRoot && oRoot != null ? oRoot : o ;
+
+ if (o.hmode && isNaN(parseInt(o.root.style.left ))) o.root.style.left = "0px";
+ if (o.vmode && isNaN(parseInt(o.root.style.top ))) o.root.style.top = "0px";
+ if (!o.hmode && isNaN(parseInt(o.root.style.right ))) o.root.style.right = "0px";
+ if (!o.vmode && isNaN(parseInt(o.root.style.bottom))) o.root.style.bottom = "0px";
+
+ o.minX = typeof minX != 'undefined' ? minX : null;
+ o.minY = typeof minY != 'undefined' ? minY : null;
+ o.max = typeof maxX != 'undefined' ? maxX : null;
+ o.maxY = typeof maxY != 'undefined' ? maxY : null;
+
+ o.xMapper = fXMapper ? fXMapper : null;
+ o.yMapper = fYMapper ? fYMapper : null;
+
+ o.root.onDragStart = new Function();
+ o.root.onDragEnd = new Function();
+ o.root.onDrag = new Function();
+ },
+
+ start : function(e) {
+ var o = DOMDrag.obj = this;
+ e = DOMDrag.fixE(e);
+ var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom);
+ var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right );
+ o.root.onDragStart(x, y);
+
+ o.lastMouseX = e.clientX;
+ o.lastMouseY = e.clientY;
+
+ if (o.hmode) {
+ if (o.minX != null) o.minMouseX = e.clientX - x + o.minX;
+ if (o.maxX != null) o.maxMouseX = o.minMouseX + o.maxX - o.minX;
+ } else {
+ if (o.minX != null) o.maxMouseX = -o.minX + e.clientX + x;
+ if (o.maxX != null) o.minMouseX = -o.maxX + e.clientX + x;
+ }
+
+ if (o.vmode) {
+ if (o.minY != null) o.minMouseY = e.clientY - y + o.minY;
+ if (o.maxY != null) o.maxMouseY = o.minMouseY + o.maxY - o.minY;
+ } else {
+ if (o.minY != null) o.maxMouseY = -o.minY + e.clientY + y;
+ if (o.maxY != null) o.minMouseY = -o.maxY + e.clientY + y;
+ }
+
+ document.onmousemove = DOMDrag.drag;
+ document.onmouseup = DOMDrag.end;
+
+ return false;
+ },
+
+ drag : function(e) {
+ e = DOMDrag.fixE(e);
+ var o = DOMDrag.obj;
+
+ var ey = e.clientY;
+ var ex = e.clientX;
+ var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom);
+ var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right );
+ var nx, ny;
+
+ if (o.minX != null) ex = o.hmode ? Math.max(ex, o.minMouseX) : Math.min(ex, o.maxMouseX);
+ if (o.maxX != null) ex = o.hmode ? Math.min(ex, o.maxMouseX) : Math.max(ex, o.minMouseX);
+ if (o.minY != null) ey = o.vmode ? Math.max(ey, o.minMouseY) : Math.min(ey, o.maxMouseY);
+ if (o.maxY != null) ey = o.vmode ? Math.min(ey, o.maxMouseY) : Math.max(ey, o.minMouseY);
+
+ nx = x + ((ex - o.lastMouseX) * (o.hmode ? 1 : -1));
+ ny = y + ((ey - o.lastMouseY) * (o.vmode ? 1 : -1));
+
+ if (o.xMapper) nx = o.xMapper(y)
+ else if (o.yMapper) ny = o.yMapper(x)
+
+ DOMDrag.obj.root.style[o.hmode ? "left" : "right"] = nx + "px";
+ DOMDrag.obj.root.style[o.vmode ? "top" : "bottom"] = ny + "px";
+ DOMDrag.obj.lastMouseX = ex;
+ DOMDrag.obj.lastMouseY = ey;
+
+ DOMDrag.obj.root.onDrag(nx, ny);
+ return false;
+ },
+
+ end : function() {
+ document.onmousemove = null;
+ document.onmouseup = null;
+ DOMDrag.obj.root.onDragEnd( parseInt(DOMDrag.obj.root.style[DOMDrag.obj.hmode ? "left" : "right"]),
+ parseInt(DOMDrag.obj.root.style[DOMDrag.obj.vmode ? "top" : "bottom"]));
+ DOMDrag.obj = null;
+ },
+
+ fixE : function(e) {
+ if (typeof e == 'undefined') e = window.event;
+ if (typeof e.layerX == 'undefined') e.layerX = e.offsetX;
+ if (typeof e.layerY == 'undefined') e.layerY = e.offsetY;
+ return e;
+ }
+};
\ No newline at end of file
--- /dev/null
+/**************************************************\r
+ * imgedit.js\r
+ * 2003-10-17\r
+ * www.sonnd.com / www.supergarv.de\r
+ *\r
+ * COPYRIGHT (C) BY sonnd / Garvin Hicking\r
+ * Published as GPL. Copyright notice has to stay in effect.\r
+ **************************************************/\r
+\r
+// Gets a position of an element on a certain axis\r
+function imgedit_position(element, axis) {\r
+ if (axis == 'x') {\r
+ return (element.x) ? element.x : imgedit_subposition(element, 'Left');\r
+ } else {\r
+ return (element.y) ? element.y : imgedit_subposition(element, 'Top');\r
+ }\r
+}\r
+\r
+// Calculate possible referenced subpositions to really get the absolute position.\r
+function imgedit_subposition(element, axis) {\r
+ currentPos = 0;\r
+ while (element != null) {\r
+ currentPos += element['offset' + axis];\r
+ element = element.offsetParent;\r
+ }\r
+\r
+ return currentPos;\r
+}\r
+\r
+// Places the cropping area to a certain X/Y coordinate. Then clips the overlay picture correspondingly\r
+function imgedit_placeArea(new_x, new_y) {\r
+ o_area.style.left = new_x + 'px';\r
+ o_area.style.top = new_y + 'px';\r
+ o_overlay.style.clip = "rect(" + (new_y+area_border) + " " + (new_x+inner_area_x) + " " + (new_y+inner_area_y) + " " + (new_x+area_border) + ")";\r
+}\r
+\r
+// Set correct restraints of the cropping area inside which it can move\r
+function imgedit_setMax(new_width, new_height) {\r
+ o_area.maxX = imgedit_getMax('x', new_width, area_orientation);\r
+ o_area.maxY = imgedit_getMax('y', new_height, area_orientation);\r
+}\r
+\r
+// Toggle the current area orientation to the opposite one\r
+function imgedit_areaOrientation() {\r
+ if (area_orientation == 'h') {\r
+ imgedit_toggleAreaOrientation('v');\r
+ } else {\r
+ imgedit_toggleAreaOrientation('h');\r
+ }\r
+\r
+ return false;\r
+}\r
+\r
+// Toggle the current area orientation\r
+function imgedit_toggleAreaOrientation(new_orientation) {\r
+ if (new_orientation == area_orientation) {\r
+ return;\r
+ }\r
+\r
+ // Display the corresponding cropping area and hide the other one.\r
+ if (new_orientation == 'h') {\r
+ area_orientation = 'h';\r
+ o_area = o_harea;\r
+ area_x = harea_x;\r
+ area_y = harea_y;\r
+ inner_area_x = inner_harea_x;\r
+ inner_area_y = inner_harea_y;\r
+\r
+ o_varea.style.visibility = 'hidden';\r
+ o_area.style.left = o_varea.style.left;\r
+ o_area.style.top = o_varea.style.top;\r
+ o_area.style.visibility = 'visible';\r
+ } else {\r
+ area_orientation = 'v';\r
+ o_area = o_varea;\r
+ area_x = varea_x;\r
+ area_y = varea_y;\r
+ inner_area_x = inner_varea_x;\r
+ inner_area_y = inner_varea_y;\r
+\r
+ o_harea.style.visibility = 'hidden';\r
+ o_area.style.left = o_harea.style.left;\r
+ o_area.style.top = o_harea.style.top;\r
+ o_area.style.visibility = 'visible';\r
+ }\r
+\r
+ // Set the new clipping inside the cropping area\r
+ imgedit_setMax(o_backdrop.width, o_backdrop.height);\r
+ o_overlay.style.clip = "rect(" + (parseInt(o_area.style.top)+area_border) + " " + (parseInt(o_area.style.left)+inner_area_x) + " " + (parseInt(o_area.style.top)+inner_area_y) + " " + (parseInt(o_area.style.left)+area_border) + ")";\r
+}\r
+\r
+// Zoom the image. Takes a given stepping (can be negative)\r
+function imgedit_zoom(step) {\r
+ pos = parseInt(o_zoombutton.style.top);\r
+ if (pos+step > slider_top && pos+step < slider_bottom) {\r
+ imgedit_zoomTo(position(o_zoombutton, 'y') + step);\r
+ o_zoombutton.style.top = pos + step + 'px';\r
+ }\r
+\r
+ return false;\r
+}\r
+\r
+// Automatically resize the window to fit cropping area\r
+function imgedit_autoSize(flip) {\r
+\r
+ // First find the largest side\r
+ if (real_x > real_y) {\r
+ // The image is a horizontal one. Resize height to fit.\r
+ fitmode = 'height';\r
+ } else {\r
+ // The image is a vertical one. Resize width to fit.\r
+ fitmode = 'width';\r
+ }\r
+\r
+ // Check if the size should be flipped. If it is 'true' the image will completely fit inside the cropping area.\r
+ // If it is 'false', it will only fit one side inside the cropping area\r
+ if (flip == 'true') {\r
+ if (fitmode == 'width') {\r
+ fitmode = 'height';\r
+ } else {\r
+ fitmode = 'width';\r
+ }\r
+ }\r
+\r
+ // Get new width/height of the image\r
+ if (fitmode == 'width') {\r
+ new_width = inner_area_x - area_border;\r
+ ratio = new_width / real_x;\r
+ new_height = real_y * ratio;\r
+ } else {\r
+ new_height = inner_area_y - area_border;\r
+ ratio = new_height / real_y;\r
+ new_width = real_x * ratio;\r
+ }\r
+\r
+ // Place cropping area to (0|0), because the image has been resized?\r
+ imgedit_scaleIMG(new_width, new_height);\r
+ imgedit_placeArea(-area_border, -area_border);\r
+\r
+ // Place the slider to corresponding ratio.\r
+ o_zoombutton.style.top = slider_bottom - parseInt(ratio/2 * (slider_middle/3)) + 'px';\r
+\r
+ // Adjust some settings inside the HTML form.\r
+ document.getElementById('scaletext').style.visibility = 'visible';\r
+ document.getElementById('autoguess_clicked').value = 'true';\r
+ new_ratio = ratio;\r
+\r
+ return false;\r
+}\r
+\r
+// Get the maximum width/height for a certain axis the cropping area is allowed to move to\r
+function imgedit_getMax(axis, pixels, area_orientation) {\r
+\r
+ // Which is the size we should get?\r
+ if (area_orientation == 'h') {\r
+ maxarea_x = harea_x;\r
+ maxarea_y = harea_y\r
+ } else if (area_orientation == 'v') {\r
+ maxarea_x = varea_x;\r
+ maxarea_y = varea_y\r
+ } else {\r
+ maxarea_x = area_x;\r
+ maxarea_y = area_y\r
+ }\r
+\r
+ if (axis == 'x') {\r
+ value = pixels - maxarea_x + area_border;\r
+ } else {\r
+ value = pixels - maxarea_y + area_border;\r
+ }\r
+\r
+ if (value < -area_border) {\r
+ value = -area_border;\r
+ }\r
+\r
+ return value;\r
+}\r
+\r
+// Scales the background image to a certain size\r
+function imgedit_scaleIMG(new_width, new_height) {\r
+ o_backdrop.width = new_width;\r
+ o_backdrop.height = new_height;\r
+\r
+ o_overlay.width = new_width;\r
+ o_overlay.height = new_height;\r
+\r
+ imgedit_setMax(new_width, new_height);\r
+\r
+ return true;\r
+}\r
+\r
+// Zooms the image to a certain stepping\r
+function imgedit_zoomTo(y) {\r
+ current = slider_bottom - y;\r
+\r
+ temp_height = current - slider_middle;\r
+ temp_ratio = temp_height / (slider_middle*3);\r
+\r
+ if (current > slider_middle) {\r
+ // make smaller than 100%\r
+ new_ratio = 1 + (temp_ratio+temp_ratio);\r
+ } else {\r
+ // make larger than 100%\r
+ new_ratio = 1 + (temp_ratio+temp_ratio);\r
+ }\r
+\r
+ new_width = parseInt(real_x * new_ratio);\r
+ new_height = parseInt(real_y * new_ratio);\r
+\r
+ imgedit_scaleIMG(new_width, new_height);\r
+\r
+ return true;\r
+}\r
+\r
+// OnSubmit catch. Parses current JS values into the form\r
+function imgedit_getCoordinates() {\r
+ document.getElementById('zoombox_x').value = parseInt(o_area.style.left);\r
+ document.getElementById('zoombox_y').value = parseInt(o_area.style.top);\r
+ document.getElementById('zoombox_factor').value = new_ratio;\r
+ document.getElementById('area_orientation').value = area_orientation;\r
+\r
+ return true;\r
+}\r
+\r
+// Initializes everything\r
+function imgedit_init(zoombox_width, init_area_border, pad_left, pad_top, init_area_orientation) {\r
+ // Store objects\r
+ o_backdrop = document.getElementById("backdrop");\r
+ o_overlay = document.getElementById("overlay");\r
+ o_harea = document.getElementById("harea");\r
+ o_varea = document.getElementById("varea");\r
+ o_zoombutton = document.getElementById("zoombutton");\r
+\r
+ // Object sizes\r
+ full_x = parseInt(o_backdrop.width);\r
+ full_y = parseInt(o_backdrop.height);\r
+\r
+ real_x = document.getElementById('real_img_width').value;\r
+ real_y = document.getElementById('real_img_height').value;\r
+\r
+ area_border = init_area_border;\r
+\r
+ harea_x = parseInt(o_harea.width);\r
+ harea_y = parseInt(o_harea.height);\r
+\r
+ varea_x = parseInt(o_varea.width);\r
+ varea_y = parseInt(o_varea.height);\r
+\r
+ inner_harea_x = harea_x - area_border;\r
+ inner_harea_y = harea_y - area_border;\r
+\r
+ inner_varea_x = varea_x - area_border;\r
+ inner_varea_y = varea_y - area_border;\r
+\r
+ new_ratio = document.getElementById('zoombox_factor').value;\r
+\r
+ slider_width = 10;\r
+ slider_top = 0;\r
+ slider_bottom = 95;\r
+ slider_space = slider_bottom - slider_top;\r
+ slider_middle = slider_space / 2;\r
+ zoombox_left = -(o_zoombutton.width/2) + (slider_width/2);\r
+\r
+ // Make objects dragabble\r
+ DOMDrag.init(o_harea, null, -area_border, imgedit_getMax('x', full_x, 'h'), -area_border, imgedit_getMax('y', full_y, 'h'));\r
+ DOMDrag.init(o_varea, null, -area_border, imgedit_getMax('x', full_x, 'v'), -area_border, imgedit_getMax('y', full_y, 'v'));\r
+ DOMDrag.init(o_zoombutton, null, zoombox_left, zoombox_left, slider_top, slider_bottom);\r
+\r
+ o_harea.onDrag = function (x, y) {\r
+ o_overlay.style.clip = "rect(" + (y+area_border) + " " + (x+inner_harea_x) + " " + (y+inner_harea_y) + " " + (x+area_border) + ")";\r
+ }\r
+\r
+ o_varea.onDrag = function (x, y) {\r
+ o_overlay.style.clip = "rect(" + (y+area_border) + " " + (x+inner_varea_x) + " " + (y+inner_varea_y) + " " + (x+area_border) + ")";\r
+ }\r
+\r
+ o_zoombutton.onDrag = function (x, y) {\r
+ imgedit_zoomTo(y);\r
+ }\r
+\r
+ o_zoombutton.style.left = zoombox_left + 'px';\r
+\r
+ zf = document.getElementById('zoombox_factor').value;\r
+ if (zf != 1) {\r
+ o_zoombutton.style.top = slider_bottom - parseInt(zf/2 * (slider_middle/3)) + 'px';\r
+ } else {\r
+ o_zoombutton.style.top = slider_top + slider_middle + 'px';\r
+ }\r
+\r
+ o_zoombutton.style.visibility = 'visible';\r
+\r
+ o_harea.style.cursor = 'move';\r
+ o_harea.style.left = pad_left + 'px';\r
+ o_harea.style.top = pad_top + 'px';\r
+\r
+ o_varea.style.cursor = 'move';\r
+ o_varea.style.left = pad_left + 'px';\r
+ o_varea.style.top = pad_top + 'px';\r
+\r
+ area_orientation = '';\r
+ imgedit_toggleAreaOrientation(init_area_orientation);\r
+}
\ No newline at end of file