[fusion-commits] r1959 - sandbox/jxlib-3.0/lib
svn_fusion at osgeo.org
svn_fusion at osgeo.org
Mon Nov 2 11:13:00 EST 2009
Author: pagameba
Date: 2009-11-02 11:12:58 -0500 (Mon, 02 Nov 2009)
New Revision: 1959
Modified:
sandbox/jxlib-3.0/lib/jxlib.uncompressed.js
Log:
updating jxlib to 3.0 dev version.
Modified: sandbox/jxlib-3.0/lib/jxlib.uncompressed.js
===================================================================
--- sandbox/jxlib-3.0/lib/jxlib.uncompressed.js 2009-11-02 16:08:07 UTC (rev 1958)
+++ sandbox/jxlib-3.0/lib/jxlib.uncompressed.js 2009-11-02 16:12:58 UTC (rev 1959)
@@ -1,12 +1,12 @@
/******************************************************************************
- * MooTools 1.2.1
+ * MooTools 1.2.2
* Copyright (c) 2006-2007 [Valerio Proietti](http://mad4milk.net/).
* MooTools is distributed under an MIT-style license.
******************************************************************************
* reset.css - Copyright (c) 2006, Yahoo! Inc. All rights reserved.
* Code licensed under the BSD License: http://developer.yahoo.net/yui/license.txt
******************************************************************************
- * Jx UI Library, 2.0.2
+ * Jx UI Library, 2.0.1
* Copyright (c) 2006-2008, DM Solutions Group Inc. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -28,26 +28,30 @@
* DEALINGS IN THE SOFTWARE.
*****************************************************************************/
/*
-Script: Core.js
- MooTools - My Object Oriented JavaScript Tools.
+---
-License:
- MIT-style license.
+script: Core.js
-Copyright:
- Copyright (c) 2006-2008 [Valerio Proietti](http://mad4milk.net/).
+description: The core of MooTools, contains all the base functions and the Native and Hash implementations. Required by all the other scripts.
-Code & Documentation:
- [The MooTools production team](http://mootools.net/developers/).
+license: MIT-style license.
-Inspiration:
- - Class implementation inspired by [Base.js](http://dean.edwards.name/weblog/2006/03/base/) Copyright (c) 2006 Dean Edwards, [GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)
- - Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c) 2005-2007 Sam Stephenson, [MIT License](http://opensource.org/licenses/mit-license.php)
+copyright: Copyright (c) 2006-2008 [Valerio Proietti](http://mad4milk.net/).
+
+authors: The MooTools production team (http://mootools.net/developers/)
+
+inspiration:
+- Class implementation inspired by [Base.js](http://dean.edwards.name/weblog/2006/03/base/) Copyright (c) 2006 Dean Edwards, [GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)
+- Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c) 2005-2007 Sam Stephenson, [MIT License](http://opensource.org/licenses/mit-license.php)
+
+provides: [Mootools, Native, Hash.base, Array.each, $util]
+
+...
*/
var MooTools = {
- 'version': '1.2.2',
- 'build': 'f0491d62fbb7e906789aa3733d6a67d43e5af7c9'
+ 'version': '1.2.4',
+ 'build': '0d9113241a90b9cd5643b926795852a2026710d4'
};
var Native = function(options){
@@ -82,7 +86,8 @@
object.alias = function(a1, a2, a3){
if (typeof a1 == 'string'){
- if ((a1 = this.prototype[a1])) return add(this, a2, a1, a3);
+ var pa1 = this.prototype[a1];
+ if ((a1 = pa1)) return add(this, a2, a1, a3);
}
for (var a in a1) this.alias(a, a1[a], a2);
return this;
@@ -128,7 +133,7 @@
'String': ["charAt", "charCodeAt", "concat", "indexOf", "lastIndexOf", "match", "replace", "search", "slice", "split", "substr", "substring", "toLowerCase", "toUpperCase", "valueOf"]
};
for (var g in generics){
- for (var i = generics[g].length; i--;) Native.genericize(window[g], generics[g][i], true);
+ for (var i = generics[g].length; i--;) Native.genericize(natives[g], generics[g][i], true);
}
})();
@@ -228,7 +233,7 @@
};
function $lambda(value){
- return (typeof value == 'function') ? value : function(){
+ return ($type(value) == 'function') ? value : function(){
return value;
};
};
@@ -314,11 +319,21 @@
return unlinked;
};
/*
-Script: Browser.js
- The Browser Core. Contains Browser initialization, Window and Document, and the Browser Hash.
+---
-License:
- MIT-style license.
+script: Browser.js
+
+description: The Browser Core. Contains Browser initialization, Window and Document, and the Browser Hash.
+
+license: MIT-style license.
+
+requires:
+- /Native
+- /$util
+
+provides: [Browser, Window, Document, $exec]
+
+...
*/
var Browser = $merge({
@@ -338,7 +353,7 @@
},
trident: function(){
- return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? 5 : 4);
+ return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4);
},
webkit: function(){
@@ -346,7 +361,7 @@
},
gecko: function(){
- return (document.getBoxObjectFor == undefined) ? false : ((document.getElementsByClassName) ? 19 : 18);
+ return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19 : 18);
}
}
@@ -377,6 +392,8 @@
return new XMLHttpRequest();
}, function(){
return new ActiveXObject('MSXML2.XMLHTTP');
+ }, function(){
+ return new ActiveXObject('Microsoft.XMLHTTP');
});
};
@@ -453,7 +470,7 @@
if (Browser.Engine.trident && Browser.Engine.version <= 4) $try(function(){
doc.execCommand("BackgroundImageCache", false, true);
});
- if (Browser.Engine.trident) doc.window.attachEvent('onunload', function() {
+ if (Browser.Engine.trident) doc.window.attachEvent('onunload', function(){
doc.window.detachEvent('onunload', arguments.callee);
doc.head = doc.html = doc.window = null;
});
@@ -470,11 +487,21 @@
new Document(document);
/*
-Script: Array.js
- Contains Array Prototypes like each, contains, and erase.
+---
-License:
- MIT-style license.
+script: Array.js
+
+description: Contains Array Prototypes like each, contains, and erase.
+
+license: MIT-style license.
+
+requires:
+- /$util
+- /Array.each
+
+provides: [Array]
+
+...
*/
Array.implement({
@@ -494,7 +521,7 @@
return results;
},
- clean: function() {
+ clean: function(){
return this.filter($defined);
},
@@ -610,11 +637,21 @@
});
/*
-Script: Function.js
- Contains Function Prototypes like create, bind, pass, and delay.
+---
-License:
- MIT-style license.
+script: Function.js
+
+description: Contains Function Prototypes like create, bind, pass, and delay.
+
+license: MIT-style license.
+
+requires:
+- /Native
+- /$util
+
+provides: [Function]
+
+...
*/
Function.implement({
@@ -671,11 +708,21 @@
});
/*
-Script: Number.js
- Contains Number Prototypes like limit, round, times, and ceil.
+---
-License:
- MIT-style license.
+script: Number.js
+
+description: Contains Number Prototypes like limit, round, times, and ceil.
+
+license: MIT-style license.
+
+requires:
+- /Native
+- /$util
+
+provides: [Number]
+
+...
*/
Number.implement({
@@ -715,11 +762,20 @@
Number.implement(methods);
})(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']);
/*
-Script: String.js
- Contains String Prototypes like camelCase, capitalize, test, and toInt.
+---
-License:
- MIT-style license.
+script: String.js
+
+description: Contains String Prototypes like camelCase, capitalize, test, and toInt.
+
+license: MIT-style license.
+
+requires:
+- /Native
+
+provides: [String]
+
+...
*/
String.implement({
@@ -800,11 +856,20 @@
});
/*
-Script: Hash.js
- Contains Hash Prototypes. Provides a means for overcoming the JavaScript practical impossibility of extending native Objects.
+---
-License:
- MIT-style license.
+script: Hash.js
+
+description: Contains Hash Prototypes. Provides a means for overcoming the JavaScript practical impossibility of extending native Objects.
+
+license: MIT-style license.
+
+requires:
+- /Hash.base
+
+provides: [Hash]
+
+...
*/
Hash.implement({
@@ -823,14 +888,14 @@
},
extend: function(properties){
- Hash.each(properties, function(value, key){
+ Hash.each(properties || {}, function(value, key){
Hash.set(this, key, value);
}, this);
return this;
},
combine: function(properties){
- Hash.each(properties, function(value, key){
+ Hash.each(properties || {}, function(value, key){
Hash.include(this, key, value);
}, this);
return this;
@@ -934,12 +999,27 @@
Hash.alias({keyOf: 'indexOf', hasValue: 'contains'});
/*
-Script: Event.js
- Contains the Event Native, to make the event object completely crossbrowser.
+---
-License:
- MIT-style license.
+script: Event.js
+
+description: Contains the Event Class, to make the event object cross-browser.
+
+license: MIT-style license.
+
+requires:
+- /Window
+- /Document
+- /Hash
+- /Array
+- /Function
+- /String
+
+provides: [Event]
+
+...
*/
+
var Event = new Native({
name: 'Event',
@@ -1047,11 +1127,26 @@
});
/*
-Script: Class.js
- Contains the Class Function for easily creating, extending, and implementing reusable Classes.
+---
-License:
- MIT-style license.
+script: Class.js
+
+description: Contains the Class Function for easily creating, extending, and implementing reusable Classes.
+
+license: MIT-style license.
+
+requires:
+- /$util
+- /Native
+- /Array
+- /String
+- /Function
+- /Number
+- /Hash
+
+provides: [Class]
+
+...
*/
function Class(params){
@@ -1199,11 +1294,20 @@
};
/*
-Script: Class.Extras.js
- Contains Utility Classes that can be implemented into your own Classes to ease the execution of many common tasks.
+---
-License:
- MIT-style license.
+script: Class.Extras.js
+
+description: Contains Utility Classes that can be implemented into your own Classes to ease the execution of many common tasks.
+
+license: MIT-style license.
+
+requires:
+- /Class
+
+provides: [Chain, Events, Options]
+
+...
*/
var Chain = new Class({
@@ -1262,12 +1366,13 @@
},
removeEvents: function(events){
+ var type;
if ($type(events) == 'object'){
- for (var type in events) this.removeEvent(type, events[type]);
+ for (type in events) this.removeEvent(type, events[type]);
return this;
}
if (events) events = Events.removeOn(events);
- for (var type in this.$events){
+ for (type in this.$events){
if (events && events != type) continue;
var fns = this.$events[type];
for (var i = fns.length; i--; i) this.removeEvent(type, fns[i]);
@@ -1278,7 +1383,7 @@
});
Events.removeOn = function(string){
- return string.replace(/^on([A-Z])/, function(full, first) {
+ return string.replace(/^on([A-Z])/, function(full, first){
return first.toLowerCase();
});
};
@@ -1298,12 +1403,26 @@
});
/*
-Script: Element.js
- One of the most important items in MooTools. Contains the dollar function, the dollars function, and an handful of cross-browser,
- time-saver methods to let you easily work with HTML Elements.
+---
-License:
- MIT-style license.
+script: Element.js
+
+description: One of the most important items in MooTools. Contains the dollar function, the dollars function, and an handful of cross-browser, time-saver methods to let you easily work with HTML Elements.
+
+license: MIT-style license.
+
+requires:
+- /Window
+- /Document
+- /Array
+- /String
+- /Function
+- /Number
+- /Hash
+
+provides: [Element, Elements, $, $$, Iframe]
+
+...
*/
var Element = new Native({
@@ -1316,7 +1435,7 @@
var konstructor = Element.Constructors.get(tag);
if (konstructor) return konstructor(props);
if (typeof tag == 'string') return document.newElement(tag, props);
- return $(tag).set(props);
+ return document.id(tag).set(props);
},
afterImplement: function(key, value){
@@ -1348,23 +1467,26 @@
initialize: function(){
var params = Array.link(arguments, {properties: Object.type, iframe: $defined});
var props = params.properties || {};
- var iframe = $(params.iframe) || false;
+ var iframe = document.id(params.iframe);
var onload = props.onload || $empty;
delete props.onload;
- props.id = props.name = $pick(props.id, props.name, iframe.id, iframe.name, 'IFrame_' + $time());
+ props.id = props.name = $pick(props.id, props.name, iframe ? (iframe.id || iframe.name) : 'IFrame_' + $time());
iframe = new Element(iframe || 'iframe', props);
var onFrameLoad = function(){
var host = $try(function(){
return iframe.contentWindow.location.host;
});
- if (host && host == window.location.host){
+ if (!host || host == window.location.host){
var win = new Window(iframe.contentWindow);
new Document(iframe.contentWindow.document);
$extend(win.Element.prototype, Element.Prototype);
}
onload.call(iframe.contentWindow, iframe.contentWindow.document);
};
- (window.frames[props.id]) ? onFrameLoad() : iframe.addListener('load', onFrameLoad);
+ var contentWindow = $try(function(){
+ return iframe.contentWindow;
+ });
+ ((contentWindow && contentWindow.document.body) || window.frames[props.id]) ? onFrameLoad() : iframe.addListener('load', onFrameLoad);
return iframe;
}
@@ -1378,12 +1500,12 @@
if (options.ddup || options.cash){
var uniques = {}, returned = [];
for (var i = 0, l = elements.length; i < l; i++){
- var el = $.element(elements[i], !options.cash);
+ var el = document.id(elements[i], !options.cash);
if (options.ddup){
if (uniques[el.uid]) continue;
uniques[el.uid] = true;
}
- returned.push(el);
+ if (el) returned.push(el);
}
elements = returned;
}
@@ -1414,7 +1536,7 @@
});
tag = '<' + tag + '>';
}
- return $.element(this.createElement(tag)).set(props);
+ return document.id(this.createElement(tag)).set(props);
},
newTextNode: function(text){
@@ -1427,18 +1549,53 @@
getWindow: function(){
return this.window;
- }
+ },
+
+ id: (function(){
+
+ var types = {
+ string: function(id, nocash, doc){
+ id = doc.getElementById(id);
+ return (id) ? types.element(id, nocash) : null;
+ },
+
+ element: function(el, nocash){
+ $uid(el);
+ if (!nocash && !el.$family && !(/^object|embed$/i).test(el.tagName)){
+ var proto = Element.Prototype;
+ for (var p in proto) el[p] = proto[p];
+ };
+ return el;
+ },
+
+ object: function(obj, nocash, doc){
+ if (obj.toElement) return types.element(obj.toElement(doc), nocash);
+ return null;
+ }
+
+ };
+
+ types.textnode = types.whitespace = types.window = types.document = $arguments(0);
+
+ return function(el, nocash, doc){
+ if (el && el.$family && el.uid) return el;
+ var type = $type(el);
+ return (types[type]) ? types[type](el, nocash, doc || document) : null;
+ };
+
+ })()
+
});
+if (window.$ == null) Window.implement({
+ $: function(el, nc){
+ return document.id(el, nc, this.document);
+ }
+});
+
Window.implement({
- $: function(el, nocash){
- if (el && el.$family && el.uid) return el;
- var type = $type(el);
- return ($[type]) ? $[type](el, nocash, this.document) : null;
- },
-
$$: function(selector){
if (arguments.length == 1 && typeof selector == 'string') return this.document.getElements(selector);
var elements = [];
@@ -1463,31 +1620,10 @@
});
-$.string = function(id, nocash, doc){
- id = doc.getElementById(id);
- return (id) ? $.element(id, nocash) : null;
-};
-
-$.element = function(el, nocash){
- $uid(el);
- if (!nocash && !el.$family && !(/^object|embed$/i).test(el.tagName)){
- var proto = Element.Prototype;
- for (var p in proto) el[p] = proto[p];
- };
- return el;
-};
-
-$.object = function(obj, nocash, doc){
- if (obj.toElement) return $.element(obj.toElement(doc), nocash);
- return null;
-};
-
-$.textnode = $.whitespace = $.window = $.document = $arguments(0);
-
Native.implement([Element, Document], {
getElement: function(selector, nocash){
- return $(this.getElements(selector, true)[0] || null, nocash);
+ return document.id(this.getElements(selector, true)[0] || null, nocash);
},
getElements: function(tags, nocash){
@@ -1538,7 +1674,7 @@
Hash.each(collected, clean);
if (Browser.Engine.trident) $A(document.getElementsByTagName('object')).each(clean);
if (window.CollectGarbage) CollectGarbage();
- collected = {}; storage = {};
+ collected = storage = null;
};
var walk = function(element, walk, start, match, all, nocash){
@@ -1546,7 +1682,7 @@
var elements = [];
while (el){
if (el.nodeType == 1 && (!match || Element.match(el, match))){
- if (!all) return $(el, nocash);
+ if (!all) return document.id(el, nocash);
elements.push(el);
}
el = el[walk];
@@ -1558,10 +1694,11 @@
'html': 'innerHTML',
'class': 'className',
'for': 'htmlFor',
+ 'defaultValue': 'defaultValue',
'text': (Browser.Engine.trident || (Browser.Engine.webkit && Browser.Engine.version < 420)) ? 'innerText' : 'textContent'
};
var bools = ['compact', 'nowrap', 'ismap', 'declare', 'noshade', 'checked', 'disabled', 'readonly', 'multiple', 'selected', 'noresize', 'defer'];
-var camels = ['value', 'accessKey', 'cellPadding', 'cellSpacing', 'colSpan', 'frameBorder', 'maxLength', 'readOnly', 'rowSpan', 'tabIndex', 'useMap'];
+var camels = ['value', 'type', 'defaultValue', 'accessKey', 'cellPadding', 'cellSpacing', 'colSpan', 'frameBorder', 'maxLength', 'readOnly', 'rowSpan', 'tabIndex', 'useMap'];
bools = bools.associate(bools);
@@ -1598,12 +1735,12 @@
where = where.capitalize();
Element.implement('inject' + where, function(el){
- inserter(this, $(el, true));
+ inserter(this, document.id(el, true));
return this;
});
Element.implement('grab' + where, function(el){
- inserter($(el, true), this);
+ inserter(document.id(el, true), this);
return this;
});
@@ -1689,7 +1826,7 @@
adopt: function(){
Array.flatten(arguments).each(function(element){
- element = $(element, true);
+ element = document.id(element, true);
if (element) this.appendChild(element);
}, this);
return this;
@@ -1700,23 +1837,23 @@
},
grab: function(el, where){
- inserters[where || 'bottom']($(el, true), this);
+ inserters[where || 'bottom'](document.id(el, true), this);
return this;
},
inject: function(el, where){
- inserters[where || 'bottom'](this, $(el, true));
+ inserters[where || 'bottom'](this, document.id(el, true));
return this;
},
replaces: function(el){
- el = $(el, true);
+ el = document.id(el, true);
el.parentNode.replaceChild(this, el);
return this;
},
wraps: function(el, where){
- el = $(el, true);
+ el = document.id(el, true);
return this.replaces(el).grab(el, where);
},
@@ -1752,7 +1889,7 @@
return walk(this, 'parentNode', null, match, true, nocash);
},
- getSiblings: function(match, nocash) {
+ getSiblings: function(match, nocash){
return this.getParent().getChildren(match, nocash).erase(this);
},
@@ -1774,7 +1911,7 @@
for (var parent = el.parentNode; parent != this; parent = parent.parentNode){
if (!parent) return null;
}
- return $.element(el, nocash);
+ return document.id(el, nocash);
},
getSelected: function(){
@@ -1792,7 +1929,7 @@
toQueryString: function(){
var queryString = [];
this.getElements('input, select, textarea', true).each(function(el){
- if (!el.name || el.disabled) return;
+ if (!el.name || el.disabled || el.type == 'submit' || el.type == 'reset' || el.type == 'file') return;
var value = (el.tagName.toLowerCase() == 'select') ? Element.getSelected(el).map(function(opt){
return opt.value;
}) : ((el.type == 'radio' || el.type == 'checkbox') && !el.checked) ? null : el.value;
@@ -1827,7 +1964,7 @@
}
clean(clone, this);
- return $(clone);
+ return document.id(clone);
},
destroy: function(){
@@ -1849,7 +1986,7 @@
},
hasChild: function(el){
- el = $(el, true);
+ el = document.id(el, true);
if (!el) return false;
if (Browser.Engine.webkit && Browser.Engine.version < 420) return $A(this.getElementsByTagName(el.tagName)).contains(el);
return (this.contains) ? (this != el && this.contains(el)) : !!(this.compareDocumentPosition(el) & 16);
@@ -1975,11 +2112,21 @@
}
};
/*
-Script: Element.Event.js
- Contains Element methods for dealing with events, and custom Events.
+---
-License:
- MIT-style license.
+script: Element.Event.js
+
+description: Contains Element methods for dealing with events. This file also includes mouseenter and mouseleave custom Element Events.
+
+license: MIT-style license.
+
+requires:
+- /Element
+- /Event
+
+provides: [Element.Event]
+
+...
*/
Element.Properties.events = {set: function(events){
@@ -2042,14 +2189,15 @@
},
removeEvents: function(events){
+ var type;
if ($type(events) == 'object'){
- for (var type in events) this.removeEvent(type, events[type]);
+ for (type in events) this.removeEvent(type, events[type]);
return this;
}
var attached = this.retrieve('events');
if (!attached) return this;
if (!events){
- for (var type in attached) this.removeEvents(type);
+ for (type in attached) this.removeEvents(type);
this.eliminate('events');
} else if (attached[events]){
while (attached[events].keys[0]) this.removeEvent(events, attached[events].keys[0]);
@@ -2068,7 +2216,7 @@
},
cloneEvents: function(from, type){
- from = $(from);
+ from = document.id(from);
var fevents = from.retrieve('events');
if (!fevents) return this;
if (!type){
@@ -2122,11 +2270,20 @@
})();
/*
-Script: Element.Style.js
- Contains methods for interacting with the styles of Elements in a fashionable way.
+---
-License:
- MIT-style license.
+script: Element.Style.js
+
+description: Contains methods for interacting with the styles of Elements in a fashionable way.
+
+license: MIT-style license.
+
+requires:
+- /Element
+
+provides: [Element.Style]
+
+...
*/
Element.Properties.styles = {set: function(styles){
@@ -2226,7 +2383,7 @@
getStyles: function(){
var result = {};
- Array.each(arguments, function(key){
+ Array.flatten(arguments).each(function(key){
result[key] = this.getStyle(key);
}, this);
return result;
@@ -2262,15 +2419,24 @@
Short.borderColor[bdc] = Short[bd][bdc] = All[bdc] = 'rgb(@, @, @)';
});
/*
-Script: Element.Dimensions.js
- Contains methods to work with size, scroll, or positioning of Elements and the window object.
+---
-License:
- MIT-style license.
+script: Element.Dimensions.js
-Credits:
- - Element positioning based on the [qooxdoo](http://qooxdoo.org/) code and smart browser fixes, [LGPL License](http://www.gnu.org/licenses/lgpl.html).
- - Viewport dimensions based on [YUI](http://developer.yahoo.com/yui/) code, [BSD License](http://developer.yahoo.com/yui/license.html).
+description: Contains methods to work with size, scroll, or positioning of Elements and the window object.
+
+license: MIT-style license.
+
+credits:
+- Element positioning based on the [qooxdoo](http://qooxdoo.org/) code and smart browser fixes, [LGPL License](http://www.gnu.org/licenses/lgpl.html).
+- Viewport dimensions based on [YUI](http://developer.yahoo.com/yui/) code, [BSD License](http://developer.yahoo.com/yui/license.html).
+
+requires:
+- /Element
+
+provides: [Element.Dimensions]
+
+...
*/
(function(){
@@ -2322,13 +2488,18 @@
return null;
},
- getOffsets: function(){
- if (Browser.Engine.trident){
- var bound = this.getBoundingClientRect(), html = this.getDocument().documentElement;
- var isFixed = styleString(this, 'position') == 'fixed';
+ getOffsets: function(){
+ if (this.getBoundingClientRect){
+ var bound = this.getBoundingClientRect(),
+ html = document.id(this.getDocument().documentElement),
+ htmlScroll = html.getScroll(),
+ elemScrolls = this.getScrolls(),
+ elemScroll = this.getScroll(),
+ isFixed = (styleString(this, 'position') == 'fixed');
+
return {
- x: bound.left + ((isFixed) ? 0 : html.scrollLeft) - html.clientLeft,
- y: bound.top + ((isFixed) ? 0 : html.scrollTop) - html.clientTop
+ x: bound.left.toInt() + elemScrolls.x - elemScroll.x + ((isFixed) ? 0 : htmlScroll.x) - html.clientLeft,
+ y: bound.top.toInt() + elemScrolls.y - elemScroll.y + ((isFixed) ? 0 : htmlScroll.y) - html.clientTop
};
}
@@ -2365,35 +2536,49 @@
getPosition: function(relative){
if (isBody(this)) return {x: 0, y: 0};
- var offset = this.getOffsets(), scroll = this.getScrolls();
- var position = {x: offset.x - scroll.x, y: offset.y - scroll.y};
- var relativePosition = (relative && (relative = $(relative))) ? relative.getPosition() : {x: 0, y: 0};
+ var offset = this.getOffsets(),
+ scroll = this.getScrolls();
+ var position = {
+ x: offset.x - scroll.x,
+ y: offset.y - scroll.y
+ };
+ var relativePosition = (relative && (relative = document.id(relative))) ? relative.getPosition() : {x: 0, y: 0};
return {x: position.x - relativePosition.x, y: position.y - relativePosition.y};
},
getCoordinates: function(element){
if (isBody(this)) return this.getWindow().getCoordinates();
- var position = this.getPosition(element), size = this.getSize();
- var obj = {left: position.x, top: position.y, width: size.x, height: size.y};
+ var position = this.getPosition(element),
+ size = this.getSize();
+ var obj = {
+ left: position.x,
+ top: position.y,
+ width: size.x,
+ height: size.y
+ };
obj.right = obj.left + obj.width;
obj.bottom = obj.top + obj.height;
return obj;
},
computePosition: function(obj){
- return {left: obj.x - styleNumber(this, 'margin-left'), top: obj.y - styleNumber(this, 'margin-top')};
+ return {
+ left: obj.x - styleNumber(this, 'margin-left'),
+ top: obj.y - styleNumber(this, 'margin-top')
+ };
},
- position: function(obj){
+ setPosition: function(obj){
return this.setStyles(this.computePosition(obj));
}
});
+
Native.implement([Document, Window], {
getSize: function(){
- if (Browser.Engine.presto || Browser.Engine.webkit) {
+ if (Browser.Engine.presto || Browser.Engine.webkit){
var win = this.getWindow();
return {x: win.innerWidth, y: win.innerHeight};
}
@@ -2454,6 +2639,7 @@
})();
//aliases
+Element.alias('setPosition', 'position'); //compatability
Native.implement([Window, Document, Element], {
@@ -2491,11 +2677,20 @@
});
/*
-Script: Selectors.js
- Adds advanced CSS Querying capabilities for targeting elements. Also includes pseudoselectors support.
+---
-License:
- MIT-style license.
+script: Selectors.js
+
+description: Adds advanced CSS-style querying capabilities for targeting HTML Elements. Includes pseudo selectors.
+
+license: MIT-style license.
+
+requires:
+- /Element
+
+provides: [Selectors]
+
+...
*/
Native.implement([Document, Element], {
@@ -2738,7 +2933,7 @@
},
byClass: function(self, klass){
- return (self.className && self.className.contains(klass, ' '));
+ return (self.className && self.className.contains && self.className.contains(klass, ' '));
},
byPseudo: function(self, parser, argument, local){
@@ -2848,17 +3043,30 @@
return Selectors.Pseudo['nth-child'].call(this, '2n', local);
},
- selected: function() {
+ selected: function(){
return this.selected;
+ },
+
+ enabled: function(){
+ return (this.disabled === false);
}
});
/*
-Script: Domready.js
- Contains the domready custom event.
+---
-License:
- MIT-style license.
+script: DomReady.js
+
+description: Contains the custom event domready.
+
+license: MIT-style license.
+
+requires:
+- /Element.Event
+
+provides: [DomReady]
+
+...
*/
Element.Events.domready = {
@@ -2877,13 +3085,15 @@
window.fireEvent('domready');
document.fireEvent('domready');
};
+
+ window.addEvent('load', domready);
if (Browser.Engine.trident){
var temp = document.createElement('div');
(function(){
($try(function(){
- temp.doScroll('left');
- return $(temp).inject(document.body).set('html', 'temp').dispose();
+ temp.doScroll(); // Technique by Diego Perini
+ return document.id(temp).inject(document.body).set('html', 'temp').dispose();
})) ? domready() : arguments.callee.delay(50);
})();
} else if (Browser.Engine.webkit && Browser.Engine.version < 525){
@@ -2891,24 +3101,38 @@
(['loaded', 'complete'].contains(document.readyState)) ? domready() : arguments.callee.delay(50);
})();
} else {
- window.addEvent('load', domready);
document.addEvent('DOMContentLoaded', domready);
}
})();
/*
-Script: JSON.js
- JSON encoder and decoder.
+---
-License:
- MIT-style license.
+script: JSON.js
-See Also:
- <http://www.json.org/>
+description: JSON encoder and decoder.
+
+license: MIT-style license.
+
+See Also: <http://www.json.org/>
+
+requires:
+- /Array
+- /String
+- /Number
+- /Function
+- /Hash
+
+provides: [JSON]
+
+...
*/
-var JSON = new Hash({
-
+var JSON = new Hash(this.JSON && {
+ stringify: JSON.stringify,
+ parse: JSON.parse
+}).extend({
+
$specialChars: {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\'},
$replaceChars: function(chr){
@@ -2920,7 +3144,7 @@
case 'string':
return '"' + obj.replace(/[\x00-\x1f\\"]/g, JSON.$replaceChars) + '"';
case 'array':
- return '[' + String(obj.map(JSON.encode).filter($defined)) + ']';
+ return '[' + String(obj.map(JSON.encode).clean()) + ']';
case 'object': case 'hash':
var string = [];
Hash.each(obj, function(value, key){
@@ -2950,14 +3174,23 @@
});
/*
-Script: Cookie.js
- Class for creating, loading, and saving browser Cookies.
+---
-License:
- MIT-style license.
+script: Cookie.js
-Credits:
- Based on the functions by Peter-Paul Koch (http://quirksmode.org).
+description: Class for creating, reading, and deleting browser Cookies.
+
+license: MIT-style license.
+
+credits:
+- Based on the functions by Peter-Paul Koch (http://quirksmode.org).
+
+requires:
+- /Options
+
+provides: [Cookie]
+
+...
*/
var Cookie = new Class({
@@ -3015,14 +3248,24 @@
return new Cookie(key, options).dispose();
};
/*
-Script: Swiff.js
- Wrapper for embedding SWF movies. Supports (and fixes) External Interface Communication.
+---
-License:
- MIT-style license.
+script: Swiff.js
-Credits:
- Flash detection & Internet Explorer + Flash Player 9 fix inspired by SWFObject.
+description: Wrapper for embedding SWF movies. Supports External Interface Communication.
+
+license: MIT-style license.
+
+credits:
+- Flash detection & Internet Explorer + Flash Player 9 fix inspired by SWFObject.
+
+requires:
+- /Options
+- /$util
+
+provides: [Swiff]
+
+...
*/
var Swiff = new Class({
@@ -3055,7 +3298,7 @@
this.setOptions(options);
options = this.options;
var id = this.id = options.id || this.instance;
- var container = $(options.container);
+ var container = document.id(options.container);
Swiff.CallBacks[this.instance] = {};
@@ -3092,13 +3335,13 @@
},
replaces: function(element){
- element = $(element, true);
+ element = document.id(element, true);
element.parentNode.replaceChild(this.toElement(), element);
return this;
},
inject: function(element){
- $(element, true).appendChild(this.toElement());
+ document.id(element, true).appendChild(this.toElement());
return this;
},
@@ -3115,11 +3358,22 @@
return eval(rs);
};
/*
-Script: Fx.js
- Contains the basic animation logic to be extended by all other Fx Classes.
+---
-License:
- MIT-style license.
+script: Fx.js
+
+description: Contains the basic animation logic to be extended by all other Fx Classes.
+
+license: MIT-style license.
+
+requires:
+- /Chain
+- /Events
+- /Options
+
+provides: [Fx]
+
+...
*/
var Fx = new Class({
@@ -3246,11 +3500,21 @@
Fx.Durations = {'short': 250, 'normal': 500, 'long': 1000};
/*
-Script: Fx.CSS.js
- Contains the CSS animation logic. Used by Fx.Tween, Fx.Morph, Fx.Elements.
+---
-License:
- MIT-style license.
+script: Fx.CSS.js
+
+description: Contains the CSS animation logic. Used by Fx.Tween, Fx.Morph, Fx.Elements.
+
+license: MIT-style license.
+
+requires:
+- /Fx
+- /Element.Style
+
+provides: [Fx.CSS]
+
+...
*/
Fx.CSS = new Class({
@@ -3378,11 +3642,20 @@
});
/*
-Script: Fx.Tween.js
- Formerly Fx.Style, effect to transition any CSS property for an element.
+---
-License:
- MIT-style license.
+script: Fx.Tween.js
+
+description: Formerly Fx.Style, effect to transition any CSS property for an element.
+
+license: MIT-style license.
+
+requires:
+- /Fx.CSS
+
+provides: [Fx.Tween, Element.fade, Element.highlight]
+
+...
*/
Fx.Tween = new Class({
@@ -3390,7 +3663,7 @@
Extends: Fx.CSS,
initialize: function(element, options){
- this.element = this.subject = $(element);
+ this.element = this.subject = document.id(element);
this.parent(options);
},
@@ -3473,11 +3746,20 @@
});
/*
-Script: Fx.Morph.js
- Formerly Fx.Styles, effect to transition any number of CSS properties for an element using an object of rules, or CSS based selector rules.
+---
-License:
- MIT-style license.
+script: Fx.Morph.js
+
+description: Formerly Fx.Styles, effect to transition any number of CSS properties for an element using an object of rules, or CSS based selector rules.
+
+license: MIT-style license.
+
+requires:
+- /Fx.CSS
+
+provides: [Fx.Morph]
+
+...
*/
Fx.Morph = new Class({
@@ -3485,7 +3767,7 @@
Extends: Fx.CSS,
initialize: function(element, options){
- this.element = this.subject = $(element);
+ this.element = this.subject = document.id(element);
this.parent(options);
},
@@ -3542,14 +3824,23 @@
});
/*
-Script: Fx.Transitions.js
- Contains a set of advanced transitions to be used with any of the Fx Classes.
+---
-License:
- MIT-style license.
+script: Fx.Transitions.js
-Credits:
- Easing Equations by Robert Penner, <http://www.robertpenner.com/easing/>, modified and optimized to be used with MooTools.
+description: Contains a set of advanced transitions to be used with any of the Fx Classes.
+
+license: MIT-style license.
+
+credits:
+- Easing Equations by Robert Penner, <http://www.robertpenner.com/easing/>, modified and optimized to be used with MooTools.
+
+requires:
+- /Fx
+
+provides: [Fx.Transitions]
+
+...
*/
Fx.implement({
@@ -3638,11 +3929,24 @@
});
});
/*
-Script: Request.js
- Powerful all purpose Request Class. Uses XMLHTTPRequest.
+---
-License:
- MIT-style license.
+script: Request.js
+
+description: Powerful all purpose Request Class. Uses XMLHTTPRequest.
+
+license: MIT-style license.
+
+requires:
+- /Element
+- /Chain
+- /Events
+- /Options
+- /Browser
+
+provides: [Request]
+
+...
*/
var Request = new Class({
@@ -3689,6 +3993,7 @@
$try(function(){
this.status = this.xhr.status;
}.bind(this));
+ this.xhr.onreadystatechange = $empty;
if (this.options.isSuccess.call(this, this.status)){
this.response = {text: this.xhr.responseText, xml: this.xhr.responseXML};
this.success(this.response.text, this.response.xml);
@@ -3696,7 +4001,6 @@
this.response = {text: null, xml: null};
this.failure();
}
- this.xhr.onreadystatechange = $empty;
},
isSuccess: function(){
@@ -3753,10 +4057,10 @@
var old = this.options;
options = $extend({data: old.data, url: old.url, method: old.method}, options);
- var data = options.data, url = options.url, method = options.method;
+ var data = options.data, url = String(options.url), method = options.method.toLowerCase();
switch ($type(data)){
- case 'element': data = $(data).toQueryString(); break;
+ case 'element': data = document.id(data).toQueryString(); break;
case 'object': case 'hash': data = Hash.toQueryString(data);
}
@@ -3765,7 +4069,7 @@
data = (data) ? format + '&' + data : format;
}
- if (this.options.emulation && ['put', 'delete'].contains(method)){
+ if (this.options.emulation && !['get', 'post'].contains(method)){
var _method = '_method=' + method;
data = (data) ? _method + '&' + data : _method;
method = 'post';
@@ -3776,18 +4080,19 @@
this.headers.set('Content-type', 'application/x-www-form-urlencoded' + encoding);
}
- if(this.options.noCache) {
- var noCache = "noCache=" + new Date().getTime();
+ if (this.options.noCache){
+ var noCache = 'noCache=' + new Date().getTime();
data = (data) ? noCache + '&' + data : noCache;
}
+ var trimPosition = url.lastIndexOf('/');
+ if (trimPosition > -1 && (trimPosition = url.indexOf('#')) > -1) url = url.substr(0, trimPosition);
if (data && method == 'get'){
url = url + (url.contains('?') ? '&' : '?') + data;
data = null;
}
-
this.xhr.open(method.toUpperCase(), url, this.options.async);
this.xhr.onreadystatechange = this.onStateChange.bind(this);
@@ -3824,18 +4129,59 @@
['get', 'post', 'put', 'delete', 'GET', 'POST', 'PUT', 'DELETE'].each(function(method){
methods[method] = function(){
var params = Array.link(arguments, {url: String.type, data: $defined});
- return this.send($extend(params, {method: method.toLowerCase()}));
+ return this.send($extend(params, {method: method}));
};
});
Request.implement(methods);
-})();/*
-Script: Request.HTML.js
- Extends the basic Request Class with additional methods for interacting with HTML responses.
+})();
-License:
- MIT-style license.
+Element.Properties.send = {
+
+ set: function(options){
+ var send = this.retrieve('send');
+ if (send) send.cancel();
+ return this.eliminate('send').store('send:options', $extend({
+ data: this, link: 'cancel', method: this.get('method') || 'post', url: this.get('action')
+ }, options));
+ },
+
+ get: function(options){
+ if (options || !this.retrieve('send')){
+ if (options || !this.retrieve('send:options')) this.set('send', options);
+ this.store('send', new Request(this.retrieve('send:options')));
+ }
+ return this.retrieve('send');
+ }
+
+};
+
+Element.implement({
+
+ send: function(url){
+ var sender = this.get('send');
+ sender.send({data: this, url: url || sender.options.url});
+ return this;
+ }
+
+});
+/*
+---
+
+script: Request.HTML.js
+
+description: Extends the basic Request Class with additional methods for interacting with HTML responses.
+
+license: MIT-style license.
+
+requires:
+- /Request
+- /Element
+
+provides: [Request.HTML]
+
+...
*/
Request.HTML = new Class({
@@ -3865,7 +4211,7 @@
doc = new DOMParser().parseFromString(root, 'text/xml');
}
root = doc.getElementsByTagName('root')[0];
- if (!root) return;
+ if (!root) return null;
for (var i = 0, k = root.childNodes.length; i < k; i++){
var child = Element.clone(root.childNodes[i], true, true);
if (child) container.grab(child);
@@ -3887,8 +4233,8 @@
response.elements = temp.getElements('*');
if (options.filter) response.tree = response.elements.filter(options.filter);
- if (options.update) $(options.update).empty().set('html', response.html);
- else if (options.append) $(options.append).adopt(temp.getChildren());
+ if (options.update) document.id(options.update).empty().set('html', response.html);
+ else if (options.append) document.id(options.append).adopt(temp.getChildren());
if (options.evalScripts) $exec(response.javascript);
this.onSuccess(response.tree, response.elements, response.html, response.javascript);
@@ -3896,26 +4242,6 @@
});
-Element.Properties.send = {
-
- set: function(options){
- var send = this.retrieve('send');
- if (send) send.cancel();
- return this.eliminate('send').store('send:options', $extend({
- data: this, link: 'cancel', method: this.get('method') || 'post', url: this.get('action')
- }, options));
- },
-
- get: function(options){
- if (options || !this.retrieve('send')){
- if (options || !this.retrieve('send:options')) this.set('send', options);
- this.store('send', new Request(this.retrieve('send:options')));
- }
- return this.retrieve('send');
- }
-
-};
-
Element.Properties.load = {
set: function(options){
@@ -3936,12 +4262,6 @@
Element.implement({
- send: function(url){
- var sender = this.get('send');
- sender.send({data: this, url: url || sender.options.url});
- return this;
- },
-
load: function(){
this.get('load').send(Array.link(arguments, {data: Object.type, url: String.type}));
return this;
@@ -3949,11 +4269,20 @@
});
/*
-Script: Request.JSON.js
- Extends the basic Request Class with additional methods for sending and receiving JSON data.
+---
-License:
- MIT-style license.
+script: Request.JSON.js
+
+description: Extends the basic Request Class with additional methods for sending and receiving JSON data.
+
+license: MIT-style license.
+
+requires:
+- /Request JSON
+
+provides: [Request.HTML]
+
+...
*/
Request.JSON = new Class({
@@ -3975,21 +4304,465 @@
}
});
+/*
+---
+
+script: More.js
+
+description: MooTools More
+
+license: MIT-style license
+
+authors:
+- Guillermo Rauch
+- Thomas Aylott
+- Scott Kyle
+
+requires:
+- core:1.2.4/MooTools
+
+provides: [MooTools.More]
+
+...
+*/
+
MooTools.More = {
- 'version': '1.2.2.1'
+ 'version': '1.2.4.1'
};/*
-Script: MooTools.Lang.js
- Provides methods for localization.
+---
- License:
- MIT-style license.
+script: Log.js
- Authors:
- Aaron Newton
+description: Provides basic logging functionality for plugins to implement.
+
+license: MIT-style license
+
+authors:
+- Guillermo Rauch
+- Thomas Aylott
+- Scott Kyle
+
+requires:
+- core:1.2.4/Class
+- /MooTools.More
+
+provides: [Log]
+
+...
*/
(function(){
+var global = this;
+
+var log = function(){
+ if (global.console && console.log){
+ try {
+ console.log.apply(console, arguments);
+ } catch(e) {
+ console.log(Array.slice(arguments));
+ }
+ } else {
+ Log.logged.push(arguments);
+ }
+ return this;
+};
+
+var disabled = function(){
+ this.logged.push(arguments);
+ return this;
+};
+
+this.Log = new Class({
+
+ logged: [],
+
+ log: disabled,
+
+ resetLog: function(){
+ this.logged.empty();
+ return this;
+ },
+
+ enableLog: function(){
+ this.log = log;
+ this.logged.each(function(args){
+ this.log.apply(this, args);
+ }, this);
+ return this.resetLog();
+ },
+
+ disableLog: function(){
+ this.log = disabled;
+ return this;
+ }
+
+});
+
+Log.extend(new Log).enableLog();
+
+// legacy
+Log.logger = function(){
+ return this.log.apply(this, arguments);
+};
+
+})();/*
+---
+
+script: Depender.js
+
+description: A stand alone dependency loader for the MooTools library.
+
+license: MIT-style license
+
+authors:
+- Aaron Newton
+
+requires:
+- core:1.2.4/Element.Events
+- core:1.2.4/Request.JSON
+- /MooTools.More
+- /Log
+
+provides: Depender
+
+...
+*/
+
+var Depender = {
+
+ options: {
+ /*
+ onRequire: $empty(options),
+ onRequirementLoaded: $empty([scripts, options]),
+ onScriptLoaded: $empty({
+ script: script,
+ totalLoaded: percentOfTotalLoaded,
+ loaded: scriptsState
+ }),
+ serial: false,
+ target: null,
+ noCache: false,
+ log: false,*/
+ loadedSources: [],
+ loadedScripts: ['Core', 'Browser', 'Array', 'String', 'Function', 'Number', 'Hash', 'Element', 'Event', 'Element.Event', 'Class', 'DomReady', 'Class.Extras', 'Request', 'JSON', 'Request.JSON', 'More', 'Depender', 'Log'],
+ useScriptInjection: true
+ },
+
+ loaded: [],
+
+ sources: {},
+
+ libs: {},
+
+ include: function(libs){
+ this.log('include: ', libs);
+ this.mapLoaded = false;
+ var loader = function(data){
+ this.libs = $merge(this.libs, data);
+ $each(this.libs, function(data, lib){
+ if (data.scripts) this.loadSource(lib, data.scripts);
+ }, this);
+ }.bind(this);
+ if ($type(libs) == 'string'){
+ this.log('fetching libs ', libs);
+ this.request(libs, loader);
+ } else {
+ loader(libs);
+ }
+ return this;
+ },
+
+ required: [],
+
+ require: function(options){
+ var loaded = function(){
+ var scripts = this.calculateDependencies(options.scripts);
+ if (options.sources){
+ options.sources.each(function(source){
+ scripts.combine(this.libs[source].files);
+ }, this);
+ }
+ if (options.serial) scripts.combine(this.getLoadedScripts());
+ options.scripts = scripts;
+ this.required.push(options);
+ this.fireEvent('require', options);
+ this.loadScripts(options.scripts);
+ };
+ if (this.mapLoaded){
+ loaded.call(this);
+ } else {
+ this.addEvent('mapLoaded', function(){
+ loaded.call(this);
+ this.removeEvent('mapLoaded', arguments.callee);
+ });
+ }
+ return this;
+ },
+
+ cleanDoubleSlash: function(str){
+ if (!str) return str;
+ var prefix = '';
+ if (str.test(/^http:\/\//)){
+ prefix = 'http://';
+ str = str.substring(7, str.length);
+ }
+ str = str.replace(/\/\//g, '/');
+ return prefix + str;
+ },
+
+ request: function(url, callback){
+ new Request.JSON({
+ url: url,
+ secure: false,
+ onSuccess: callback
+ }).send();
+ },
+
+ loadSource: function(lib, source){
+ if (this.libs[lib].files){
+ this.dataLoaded();
+ return;
+ }
+ this.log('loading source: ', source);
+ this.request(this.cleanDoubleSlash(source + '/scripts.json'), function(result){
+ this.log('loaded source: ', source);
+ this.libs[lib].files = result;
+ this.dataLoaded();
+ }.bind(this));
+ },
+
+ dataLoaded: function(){
+ var loaded = true;
+ $each(this.libs, function(v, k){
+ if (!this.libs[k].files) loaded = false;
+ }, this);
+ if (loaded){
+ this.mapTree();
+ this.mapLoaded = true;
+ this.calculateLoaded();
+ this.lastLoaded = this.getLoadedScripts().getLength();
+ this.fireEvent('mapLoaded');
+ }
+ },
+
+ calculateLoaded: function(){
+ var set = function(script){
+ this.scriptsState[script] = true;
+ }.bind(this);
+ if (this.options.loadedScripts) this.options.loadedScripts.each(set);
+ if (this.options.loadedSources){
+ this.options.loadedSources.each(function(lib){
+ $each(this.libs[lib].files, function(dir){
+ $each(dir, function(data, file){
+ set(file);
+ }, this);
+ }, this);
+ }, this);
+ }
+ },
+
+ deps: {},
+
+ pathMap: {},
+
+ mapTree: function(){
+ $each(this.libs, function(data, source){
+ $each(data.files, function(scripts, folder){
+ $each(scripts, function(details, script){
+ var path = source + ':' + folder + ':' + script;
+ if (this.deps[path]) return;
+ this.deps[path] = details.deps;
+ this.pathMap[script] = path;
+ }, this);
+ }, this);
+ }, this);
+ },
+
+ getDepsForScript: function(script){
+ return this.deps[this.pathMap[script]] || [];
+ },
+
+ calculateDependencies: function(scripts){
+ var reqs = [];
+ $splat(scripts).each(function(script){
+ if (script == 'None' || !script) return;
+ var deps = this.getDepsForScript(script);
+ if (!deps){
+ if (window.console && console.warn) console.warn('dependencies not mapped: script: %o, map: %o, :deps: %o', script, this.pathMap, this.deps);
+ } else {
+ deps.each(function(scr){
+ if (scr == script || scr == 'None' || !scr) return;
+ if (!reqs.contains(scr)) reqs.combine(this.calculateDependencies(scr));
+ reqs.include(scr);
+ }, this);
+ }
+ reqs.include(script);
+ }, this);
+ return reqs;
+ },
+
+ getPath: function(script){
+ try {
+ var chunks = this.pathMap[script].split(':');
+ var lib = this.libs[chunks[0]];
+ var dir = (lib.path || lib.scripts) + '/';
+ chunks.shift();
+ return this.cleanDoubleSlash(dir + chunks.join('/') + '.js');
+ } catch(e){
+ return script;
+ }
+ },
+
+ loadScripts: function(scripts){
+ scripts = scripts.filter(function(s){
+ if (!this.scriptsState[s] && s != 'None'){
+ this.scriptsState[s] = false;
+ return true;
+ }
+ }, this);
+ if (scripts.length){
+ scripts.each(function(scr){
+ this.loadScript(scr);
+ }, this);
+ } else {
+ this.check();
+ }
+ },
+
+ toLoad: [],
+
+ loadScript: function(script){
+ if (this.scriptsState[script] && this.toLoad.length){
+ this.loadScript(this.toLoad.shift());
+ return;
+ } else if (this.loading){
+ this.toLoad.push(script);
+ return;
+ }
+ var finish = function(){
+ this.loading = false;
+ this.scriptLoaded(script);
+ if (this.toLoad.length) this.loadScript(this.toLoad.shift());
+ }.bind(this);
+ var error = function(){
+ this.log('could not load: ', scriptPath);
+ }.bind(this);
+ this.loading = true;
+ var scriptPath = this.getPath(script);
+ if (this.options.useScriptInjection){
+ this.log('injecting script: ', scriptPath);
+ var loaded = function(){
+ this.log('loaded script: ', scriptPath);
+ finish();
+ }.bind(this);
+ new Element('script', {
+ src: scriptPath + (this.options.noCache ? '?noCache=' + new Date().getTime() : ''),
+ events: {
+ load: loaded,
+ readystatechange: function(){
+ if (['loaded', 'complete'].contains(this.readyState)) loaded();
+ },
+ error: error
+ }
+ }).inject(this.options.target || document.head);
+ } else {
+ this.log('requesting script: ', scriptPath);
+ new Request({
+ url: scriptPath,
+ noCache: this.options.noCache,
+ onComplete: function(js){
+ this.log('loaded script: ', scriptPath);
+ $exec(js);
+ finish();
+ }.bind(this),
+ onFailure: error,
+ onException: error
+ }).send();
+ }
+ },
+
+ scriptsState: $H(),
+
+ getLoadedScripts: function(){
+ return this.scriptsState.filter(function(state){
+ return state;
+ });
+ },
+
+ scriptLoaded: function(script){
+ this.log('loaded script: ', script);
+ this.scriptsState[script] = true;
+ this.check();
+ var loaded = this.getLoadedScripts();
+ var loadedLength = loaded.getLength();
+ var toLoad = this.scriptsState.getLength();
+ this.fireEvent('scriptLoaded', {
+ script: script,
+ totalLoaded: (loadedLength / toLoad * 100).round(),
+ currentLoaded: ((loadedLength - this.lastLoaded) / (toLoad - this.lastLoaded) * 100).round(),
+ loaded: loaded
+ });
+ if (loadedLength == toLoad) this.lastLoaded = loadedLength;
+ },
+
+ lastLoaded: 0,
+
+ check: function(){
+ var incomplete = [];
+ this.required.each(function(required){
+ var loaded = [];
+ required.scripts.each(function(script){
+ if (this.scriptsState[script]) loaded.push(script);
+ }, this);
+ if (required.onStep){
+ required.onStep({
+ percent: loaded.length / required.scripts.length * 100,
+ scripts: loaded
+ });
+ };
+ if (required.scripts.length != loaded.length) return;
+ required.callback();
+ this.required.erase(required);
+ this.fireEvent('requirementLoaded', [loaded, required]);
+ }, this);
+ }
+
+};
+
+$extend(Depender, new Events);
+$extend(Depender, new Options);
+$extend(Depender, new Log);
+
+Depender._setOptions = Depender.setOptions;
+Depender.setOptions = function(){
+ Depender._setOptions.apply(Depender, arguments);
+ if (this.options.log) Depender.enableLog();
+ return this;
+};
+/*
+---
+
+script: MooTools.Lang.js
+
+description: Provides methods for localization.
+
+license: MIT-style license
+
+authors:
+- Aaron Newton
+
+requires:
+- core:1.2.4/Events
+- /MooTools.More
+
+provides: [MooTools.Lang]
+
+...
+*/
+
+(function(){
+
var data = {
language: 'en-US',
languages: {
@@ -4069,38 +4842,24 @@
});
})();/*
-Script: Log.js
- Provides basic logging functionality for plugins to implement.
+---
- License:
- MIT-style license.
+script: Class.Refactor.js
- Authors:
- Guillermo Rauch
-*/
+description: Extends a class onto itself with new property, preserving any items attached to the class's namespace.
-var Log = new Class({
-
- log: function(){
- Log.logger.call(this, arguments);
- }
-
-});
+license: MIT-style license
-Log.logged = [];
+authors:
+- Aaron Newton
-Log.logger = function(){
- if(window.console && console.log) console.log.apply(console, arguments);
- else Log.logged.push(arguments);
-};/*
-Script: Class.Refactor.js
- Extends a class onto itself with new property, preserving any items attached to the class's namespace.
+requires:
+- core:1.2.4/Class
+- /MooTools.More
- License:
- MIT-style license.
+provides: [Class.refactor]
- Authors:
- Aaron Newton
+...
*/
Class.refactor = function(original, refactors){
@@ -4119,14 +4878,24 @@
return original;
};/*
-Script: Class.Binds.js
- Automagically binds specified methods in a class to the instance of the class.
+---
- License:
- MIT-style license.
+script: Class.Binds.js
- Authors:
- Aaron Newton
+description: Automagically binds specified methods in a class to the instance of the class.
+
+license: MIT-style license
+
+authors:
+- Aaron Newton
+
+requires:
+- core:1.2.4/Class
+- /MooTools.More
+
+provides: [Class.Binds]
+
+...
*/
Class.Mutators.Binds = function(binds){
@@ -4141,40 +4910,63 @@
}, this);
return initialize.apply(this, arguments);
};
-};/*
-Script: Class.Occlude.js
- Prevents a class from being applied to a DOM element twice.
+};
+/*
+---
- License:
- MIT-style license.
+script: Class.Occlude.js
- Authors:
- Aaron Newton
+description: Prevents a class from being applied to a DOM element twice.
+
+license: MIT-style license.
+
+authors:
+- Aaron Newton
+
+requires:
+- core/1.2.4/Class
+- core:1.2.4/Element
+- /MooTools.More
+
+provides: [Class.Occlude]
+
+...
*/
Class.Occlude = new Class({
occlude: function(property, element){
- element = $(element || this.element);
+ element = document.id(element || this.element);
var instance = element.retrieve(property || this.property);
- if (instance && !$defined(this.occluded)){
- this.occluded = instance;
- } else {
- this.occluded = false;
- element.store(property || this.property, this);
- }
+ if (instance && !$defined(this.occluded))
+ return this.occluded = instance;
+
+ this.occluded = false;
+ element.store(property || this.property, this);
return this.occluded;
}
});/*
-Script: Chain.Wait.js
- Adds a method to inject pauses between chained events.
+---
- License:
- MIT-style license.
+script: Chain.Wait.js
- Authors:
- Aaron Newton
+description: value, Adds a method to inject pauses between chained events.
+
+license: MIT-style license.
+
+authors:
+- Aaron Newton
+
+requires:
+- core:1.2.4/Chain
+- core:1.2.4/Element
+- core:1.2.4/Fx
+- /MooTools.More
+
+provides: [Chain.Wait]
+
+...
*/
(function(){
@@ -4196,35 +4988,41 @@
});
}
- try {
- Element.implement({
- chains: function(effects){
- $splat($pick(effects, ['tween', 'morph', 'reveal'])).each(function(effect){
- effect = this.get(effect);
- if (!effect) return;
- effect.setOptions({
- link:'chain'
- });
- }, this);
- return this;
- },
- pauseFx: function(duration, effect){
- this.chains(effect).get($pick(effect, 'tween')).wait(duration);
- return this;
- }
- });
- } catch(e){}
+ Element.implement({
+ chains: function(effects){
+ $splat($pick(effects, ['tween', 'morph', 'reveal'])).each(function(effect){
+ effect = this.get(effect);
+ if (!effect) return;
+ effect.setOptions({
+ link:'chain'
+ });
+ }, this);
+ return this;
+ },
+ pauseFx: function(duration, effect){
+ this.chains(effect).get($pick(effect, 'tween')).wait(duration);
+ return this;
+ }
+ });
})();/*
-Script: Array.Extras.js
- Extends the Array native object to include useful methods to work with arrays.
+---
- License:
- MIT-style license.
+script: Array.Extras.js
- Authors:
- Christoph Pojer
+description: Extends the Array native object to include useful methods to work with arrays.
+license: MIT-style license
+
+authors:
+- Christoph Pojer
+
+requires:
+- core:1.2.4/Array
+
+provides: [Array.Extras]
+
+...
*/
Array.implement({
@@ -4255,48 +5053,122 @@
}
});/*
-Script: Date.js
- Extends the Date native object to include methods useful in managing dates.
+---
- License:
- MIT-style license.
+script: Date.English.US.js
- Authors:
- Aaron Newton
- Nicholas Barthelemy - https://svn.nbarthelemy.com/date-js/
- Harald Kirshner - mail [at] digitarald.de; http://digitarald.de
+description: Date messages for US English.
+license: MIT-style license
+
+authors:
+- Aaron Newton
+
+requires:
+- /Lang
+- /Date
+
+provides: [Date.English.US]
+
+...
*/
-(function(){
+MooTools.lang.set('en-US', 'Date', {
-new Native({name: 'Date', initialize: Date, protect: true});
+ months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
+ days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
+ //culture's date order: MM/DD/YYYY
+ dateOrder: ['month', 'date', 'year'],
+ shortDate: '%m/%d/%Y',
+ shortTime: '%I:%M%p',
+ AM: 'AM',
+ PM: 'PM',
-['now','parse','UTC'].each(function(method){
- Native.genericize(Date, method, true);
-});
+ /* Date.Extras */
+ ordinal: function(dayOfMonth){
+ //1st, 2nd, 3rd, etc.
+ return (dayOfMonth > 3 && dayOfMonth < 21) ? 'th' : ['th', 'st', 'nd', 'rd', 'th'][Math.min(dayOfMonth % 10, 4)];
+ },
-Date.Methods = {};
+ lessThanMinuteAgo: 'less than a minute ago',
+ minuteAgo: 'about a minute ago',
+ minutesAgo: '{delta} minutes ago',
+ hourAgo: 'about an hour ago',
+ hoursAgo: 'about {delta} hours ago',
+ dayAgo: '1 day ago',
+ daysAgo: '{delta} days ago',
+ weekAgo: '1 week ago',
+ weeksAgo: '{delta} weeks ago',
+ monthAgo: '1 month ago',
+ monthsAgo: '{delta} months ago',
+ yearAgo: '1 year ago',
+ yearsAgo: '{delta} years ago',
+ lessThanMinuteUntil: 'less than a minute from now',
+ minuteUntil: 'about a minute from now',
+ minutesUntil: '{delta} minutes from now',
+ hourUntil: 'about an hour from now',
+ hoursUntil: 'about {delta} hours from now',
+ dayUntil: '1 day from now',
+ daysUntil: '{delta} days from now',
+ weekUntil: '1 week from now',
+ weeksUntil: '{delta} weeks from now',
+ monthUntil: '1 month from now',
+ monthsUntil: '{delta} months from now',
+ yearUntil: '1 year from now',
+ yearsUntil: '{delta} years from now'
-['Date', 'Day', 'FullYear', 'Hours', 'Milliseconds', 'Minutes', 'Month', 'Seconds', 'Time', 'TimezoneOffset',
- 'Week', 'Timezone', 'GMTOffset', 'DayOfYear', 'LastMonth', 'UTCDate', 'UTCDay', 'UTCFullYear',
- 'AMPM', 'UTCHours', 'UTCMilliseconds', 'UTCMinutes', 'UTCMonth', 'UTCSeconds'].each(function(method){
- Date.Methods[method.toLowerCase()] = method;
});
+/*
+---
-$each({
+script: Date.js
+
+description: Extends the Date native object to include methods useful in managing dates.
+
+license: MIT-style license
+
+authors:
+- Aaron Newton
+- Nicholas Barthelemy - https://svn.nbarthelemy.com/date-js/
+- Harald Kirshner - mail [at] digitarald.de; http://digitarald.de
+- Scott Kyle - scott [at] appden.com; http://appden.com
+
+requires:
+- core:1.2.4/Array
+- core:1.2.4/String
+- core:1.2.4/Number
+- core:1.2.4/Lang
+- core:1.2.4/Date.English.US
+- /MooTools.More
+
+provides: [Date]
+
+...
+*/
+
+(function(){
+
+var Date = this.Date;
+
+if (!Date.now) Date.now = $time;
+
+Date.Methods = {
ms: 'Milliseconds',
year: 'FullYear',
min: 'Minutes',
mo: 'Month',
sec: 'Seconds',
hr: 'Hours'
-}, function(value, key){
- Date.Methods[key] = value;
+};
+
+['Date', 'Day', 'FullYear', 'Hours', 'Milliseconds', 'Minutes', 'Month', 'Seconds', 'Time', 'TimezoneOffset',
+ 'Week', 'Timezone', 'GMTOffset', 'DayOfYear', 'LastMonth', 'LastDayOfMonth', 'UTCDate', 'UTCDay', 'UTCFullYear',
+ 'AMPM', 'Ordinal', 'UTCHours', 'UTCMilliseconds', 'UTCMinutes', 'UTCMonth', 'UTCSeconds'].each(function(method){
+ Date.Methods[method.toLowerCase()] = method;
});
-var zeroize = function(what, length){
- return '0'.repeat(length - what.toString().length) + what;
+var pad = function(what, length){
+ return new Array(length - String(what).length + 1).join('0') + what;
};
Date.implement({
@@ -4314,10 +5186,10 @@
return this;
},
- get: function(key){
- key = key.toLowerCase();
+ get: function(prop){
+ prop = prop.toLowerCase();
var m = Date.Methods;
- if (m[key]) return this['get' + m[key]]();
+ if (m[prop]) return this['get' + m[prop]]();
return null;
},
@@ -4326,91 +5198,61 @@
},
increment: function(interval, times){
- return this.multiply(interval, times);
+ interval = interval || 'day';
+ times = $pick(times, 1);
+
+ switch (interval){
+ case 'year':
+ return this.increment('month', times * 12);
+ case 'month':
+ var d = this.get('date');
+ this.set('date', 1).set('mo', this.get('mo') + times);
+ return this.set('date', d.min(this.get('lastdayofmonth')));
+ case 'week':
+ return this.increment('day', times * 7);
+ case 'day':
+ return this.set('date', this.get('date') + times);
+ }
+
+ if (!Date.units[interval]) throw new Error(interval + ' is not a supported interval');
+
+ return this.set('time', this.get('time') + times * Date.units[interval]());
},
decrement: function(interval, times){
- return this.multiply(interval, times, false);
+ return this.increment(interval, -1 * $pick(times, 1));
},
- multiply: function(interval, times, increment){
- interval = interval || 'day';
- times = $pick(times, 1);
- increment = $pick(increment, true);
- var multiplier = increment ? 1 : -1;
- var month = this.format('%m').toInt() - 1;
- var year = this.format('%Y').toInt();
- var time = this.get('time');
- var offset = 0;
- switch (interval) {
- case 'year':
- times.times(function(val) {
- if (Date.isLeapYear(year+val) && month > 1 && multiplier > 0) val++;
- if (Date.isLeapYear(year+val) && month <= 1 && multiplier < 0) val--;
- offset += Date.units.year(year+val);
- });
- break;
- case 'month':
- times.times(function(val){
- if (multiplier < 0) val++;
- var mo = month+(val * multiplier);
- var year = year;
- if (mo < 0) {
- year--;
- mo = 12+mo;
- }
- if (mo > 11 || mo < 0) {
- year += (mo / 12).toInt() * multiplier;
- mo = mo % 12;
- }
- offset += Date.units.month(mo, year);
- });
- break;
- case 'day':
- return this.set('date', this.get('date')+(multiplier*times));
- default:
- offset = Date.units[interval]() * times;
- break;
- }
- this.set('time', time + (offset * multiplier));
- return this;
- },
-
isLeapYear: function(){
return Date.isLeapYear(this.get('year'));
},
clearTime: function(){
- ['hr', 'min', 'sec', 'ms'].each(function(t){
- this.set(t, 0);
- }, this);
- return this;
+ return this.set({hr: 0, min: 0, sec: 0, ms: 0});
},
- diff: function(d, resolution){
- resolution = resolution || 'day';
- if ($type(d) == 'string') d = Date.parse(d);
- switch (resolution){
- case 'year':
- return d.format('%Y').toInt() - this.format('%Y').toInt();
- break;
- case 'month':
- var months = (d.format('%Y').toInt() - this.format('%Y').toInt())*12;
- return months + d.format('%m').toInt() - this.format('%m').toInt();
- break;
- default:
- var diff = d.get('time') - this.get('time');
- if (diff < 0 && Date.units[resolution]() > (-1*(diff))) return 0;
- else if (diff >= 0 && diff < Date.units[resolution]()) return 0;
- return ((d.get('time') - this.get('time')) / Date.units[resolution]()).round();
- }
- return null;
+ diff: function(date, resolution){
+ if ($type(date) == 'string') date = Date.parse(date);
+
+ return ((date - this) / Date.units[resolution || 'day'](3, 3)).toInt(); // non-leap year, 30-day month
},
+ getLastDayOfMonth: function(){
+ return Date.daysInMonth(this.get('mo'), this.get('year'));
+ },
+
+ getDayOfYear: function(){
+ return (Date.UTC(this.get('year'), this.get('mo'), this.get('date') + 1)
+ - Date.UTC(this.get('year'), 0, 1)) / Date.units.day();
+ },
+
getWeek: function(){
- var day = (new Date(this.get('year'), 0, 1)).get('date');
- return Math.round((this.get('dayofyear') + (day > 3 ? day - 4 : day + 3)) / 7);
+ return (this.get('dayofyear') / 7).ceil();
},
+
+ getOrdinal: function(day){
+ return Date.getMsg('ordinal', day || this.get('date'));
+ },
getTimezone: function(){
return this.toString()
@@ -4420,11 +5262,21 @@
getGMTOffset: function(){
var off = this.get('timezoneOffset');
- return ((off > 0) ? '-' : ' + ')
- + zeroize(Math.floor(Math.abs(off) / 60), 2)
- + zeroize(off % 60, 2);
+ return ((off > 0) ? '-' : '+') + pad((off.abs() / 60).floor(), 2) + pad(off % 60, 2);
},
+ setAMPM: function(ampm){
+ ampm = ampm.toUpperCase();
+ var hr = this.get('hr');
+ if (hr > 11 && ampm == 'AM') return this.decrement('hour', 12);
+ else if (hr < 12 && ampm == 'PM') return this.increment('hour', 12);
+ return this;
+ },
+
+ getAMPM: function(){
+ return (this.get('hr') < 12) ? 'AM' : 'PM';
+ },
+
parse: function(str){
this.set('time', Date.parse(str));
return this;
@@ -4437,34 +5289,26 @@
format: function(f){
if (!this.isValid()) return 'invalid date';
f = f || '%x %X';
- //replace short-hand with actual format
- f = ({
- db: '%Y-%m-%d %H:%M:%S',
- compact: '%Y%m%dT%H%M%S',
- iso8601: '%Y-%m-%dT%H:%M:%S%T',
- rfc822: '%a, %d %b %Y %H:%M:%S %Z',
- 'short': '%d %b %H:%M',
- 'long': '%B %d, %Y %H:%M'
- })[f.toLowerCase()] || f;
+ f = formats[f.toLowerCase()] || f; // replace short-hand with actual format
var d = this;
- return f.replace(/\%([aAbBcdHIjmMpSUWwxXyYTZ\%])/g,
- function($1, $2){
- switch ($2){
+ return f.replace(/%([a-z%])/gi,
+ function($0, $1){
+ switch ($1){
case 'a': return Date.getMsg('days')[d.get('day')].substr(0, 3);
case 'A': return Date.getMsg('days')[d.get('day')];
case 'b': return Date.getMsg('months')[d.get('month')].substr(0, 3);
case 'B': return Date.getMsg('months')[d.get('month')];
case 'c': return d.toString();
- case 'd': return zeroize(d.get('date'), 2);
- case 'H': return zeroize(d.get('hr'), 2);
+ case 'd': return pad(d.get('date'), 2);
+ case 'H': return pad(d.get('hr'), 2);
case 'I': return ((d.get('hr') % 12) || 12);
- case 'j': return zeroize(d.get('dayofyear'), 3);
- case 'm': return zeroize((d.get('mo') + 1), 2);
- case 'M': return zeroize(d.get('min'), 2);
- case 'p': return Date.getMsg(d.get('hr') < 12 ? 'AM' : 'PM');
- case 'S': return zeroize(d.get('seconds'), 2);
- case 'U': return zeroize(d.get('week'), 2);
- case 'W': throw new Error('%W is not supported yet');
+ case 'j': return pad(d.get('dayofyear'), 3);
+ case 'm': return pad((d.get('mo') + 1), 2);
+ case 'M': return pad(d.get('min'), 2);
+ case 'o': return d.get('ordinal');
+ case 'p': return Date.getMsg(d.get('ampm'));
+ case 'S': return pad(d.get('seconds'), 2);
+ case 'U': return pad(d.get('week'), 2);
case 'w': return d.get('day');
case 'x': return d.format(Date.getMsg('shortDate'));
case 'X': return d.format(Date.getMsg('shortTime'));
@@ -4472,37 +5316,60 @@
case 'Y': return d.get('year');
case 'T': return d.get('GMTOffset');
case 'Z': return d.get('Timezone');
- case '%': return '%';
}
- return $2;
+ return $1;
}
);
},
- setAMPM: function(ampm){
- ampm = ampm.toUpperCase();
- if (this.format('%H').toInt() > 11 && ampm == 'AM')
- return this.decrement('hour', 12);
- else if (this.format('%H').toInt() < 12 && ampm == 'PM')
- return this.increment('hour', 12);
- return this;
+ toISOString: function(){
+ return this.format('iso8601');
}
});
+Date.alias('toISOString', 'toJSON');
Date.alias('diff', 'compare');
Date.alias('format', 'strftime');
+var formats = {
+ db: '%Y-%m-%d %H:%M:%S',
+ compact: '%Y%m%dT%H%M%S',
+ iso8601: '%Y-%m-%dT%H:%M:%S%T',
+ rfc822: '%a, %d %b %Y %H:%M:%S %Z',
+ 'short': '%d %b %H:%M',
+ 'long': '%B %d, %Y %H:%M'
+};
+
+var parsePatterns = [];
var nativeParse = Date.parse;
-var daysInMonth = function(monthIndex, year){
- if (Date.isLeapYear(year.toInt()) && monthIndex === 1) return 29;
- return [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][monthIndex];
+var parseWord = function(type, word, num){
+ var ret = -1;
+ var translated = Date.getMsg(type + 's');
+
+ switch ($type(word)){
+ case 'object':
+ ret = translated[word.get(type)];
+ break;
+ case 'number':
+ ret = translated[month - 1];
+ if (!ret) throw new Error('Invalid ' + type + ' index: ' + index);
+ break;
+ case 'string':
+ var match = translated.filter(function(name){
+ return this.test(name);
+ }, new RegExp('^' + word, 'i'));
+ if (!match.length) throw new Error('Invalid ' + type + ' string');
+ if (match.length > 1) throw new Error('Ambiguous ' + type);
+ ret = match[0];
+ }
+
+ return (num) ? translated.indexOf(ret) : ret;
};
+Date.extend({
-$extend(Date, {
-
getMsg: function(key, args) {
return MooTools.lang.get('Date', key, args);
},
@@ -4514,87 +5381,58 @@
hour: $lambda(3600000),
day: $lambda(86400000),
week: $lambda(608400000),
- month: function(monthIndex, year){
- var d = new Date();
- return daysInMonth($pick(monthIndex,d.format('%m').toInt()), $pick(year,d.format('%Y').toInt())) * 86400000;
+ month: function(month, year){
+ var d = new Date;
+ return Date.daysInMonth($pick(month, d.get('mo')), $pick(year, d.get('year'))) * 86400000;
},
year: function(year){
- year = year || new Date().format('%Y').toInt();
- return Date.isLeapYear(year.toInt()) ? 31622400000 : 31536000000;
+ year = year || new Date().get('year');
+ return Date.isLeapYear(year) ? 31622400000 : 31536000000;
}
},
- isLeapYear: function(yr){
- return new Date(yr , 1, 29).getDate() == 29;
+ daysInMonth: function(month, year){
+ return [31, Date.isLeapYear(year) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
},
- fixY2K: function(d){
- if (!isNaN(d)){
- var newDate = new Date(d);
- if (newDate.get('year') < 2000 && d.toString().indexOf(newDate.get('year')) < 0) newDate.increment('year', 100);
- return newDate;
- } else {
- return d;
- }
+ isLeapYear: function(year){
+ return ((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0);
},
parse: function(from){
var t = $type(from);
if (t == 'number') return new Date(from);
if (t != 'string') return from;
+ from = from.clean();
if (!from.length) return null;
+
var parsed;
- Date.parsePatterns.each(function(pattern, i){
- if (parsed) return;
- var r = pattern.re.exec(from);
- if (r) parsed = pattern.handler(r);
+ parsePatterns.some(function(pattern){
+ var bits = pattern.re.exec(from);
+ return (bits) ? (parsed = pattern.handler(bits)) : false;
});
+
return parsed || new Date(nativeParse(from));
},
parseDay: function(day, num){
- var ret = -1;
- switch ($type(day)){
- case 'number':
- ret = Date.getMsg('days')[day - 1] || false;
- if (!ret) throw new Error('Invalid day index value must be between 1 and 7');
- break;
- case 'string':
- var match = Date.getMsg('days').filter(function(name){
- return this.test(name);
- }, new RegExp('^' + day, 'i'));
- if (!match.length) throw new Error('Invalid day string');
- if (match.length > 1) throw new Error('Ambiguous day');
- ret = match[0];
- }
- return (num) ? Date.getMsg('days').indexOf(ret) : ret;
+ return parseWord('day', day, num);
},
parseMonth: function(month, num){
- var ret = -1;
- switch ($type(month)){
- case 'object':
- ret = Date.getMsg('months')[month.get('mo')];
- break;
- case 'number':
- ret = Date.getMsg('months')[month - 1] || false;
- if (!ret) throw new Error('Invalid month index value must be between 1 and 12:' + index);
- break;
- case 'string':
- var match = Date.getMsg('months').filter(function(name){
- return this.test(name);
- }, new RegExp('^' + month, 'i'));
- if (!match.length) throw new Error('Invalid month string');
- if (match.length > 1) throw new Error('Ambiguous month');
- ret = match[0];
- }
- return (num) ? Date.getMsg('months').indexOf(ret) : ret;
+ return parseWord('month', month, num);
},
parseUTC: function(value){
var localDate = new Date(value);
- var utcSeconds = Date.UTC(localDate.get('year'), localDate.get('mo'),
- localDate.get('date'), localDate.get('hr'), localDate.get('min'), localDate.get('sec'));
+ var utcSeconds = Date.UTC(
+ localDate.get('year'),
+ localDate.get('mo'),
+ localDate.get('date'),
+ localDate.get('hr'),
+ localDate.get('min'),
+ localDate.get('sec')
+ );
return new Date(utcSeconds);
},
@@ -4602,279 +5440,294 @@
return Date.getMsg('dateOrder').indexOf(unit) + 1;
},
- parsePatterns: [
- {
- //"1999-12-31"
- re: /^(\d{4})[\.\-\/](\d{1,2})[\.\-\/](\d{1,2})$/,
- handler: function(bits){
- return new Date(bits[1], bits[2] - 1, bits[3]);
- }
- },
- {
- //"1999-12-31 23:59:59"
- re: /^(\d{4})[\.\-\/](\d{1,2})[\.\-\/](\d{1,2})\s(\d{1,2}):(\d{1,2})(?:\:(\d{1,2}))?(\w{2})?$/,
- handler: function(bits){
- var d = new Date(bits[1], bits[2] - 1, bits[3]);
- d.set('hr', bits[4]);
- d.set('min', bits[5]);
- d.set('sec', bits[6] || 0);
- if (bits[7]) d.set('ampm', bits[7]);
- return d;
- }
- },
- {
- //"12.31.08", "12-31-08", "12/31/08", "12.31.2008", "12-31-2008", "12/31/2008"
- re: /^(\d{1,2})[\.\-\/](\d{1,2})[\.\-\/](\d{2,4})$/,
- handler: function(bits){
- var d = new Date(bits[Date.orderIndex('year')],
- bits[Date.orderIndex('month')] - 1,
- bits[Date.orderIndex('date')]);
- return Date.fixY2K(d);
- }
- },
- //"12.31.08", "12-31-08", "12/31/08", "12.31.2008", "12-31-2008", "12/31/2008"
- //above plus "10:45pm" ex: 12.31.08 10:45pm
- {
- re: /^(\d{1,2})[\.\-\/](\d{1,2})[\.\-\/](\d{2,4})\s(\d{1,2})[:\.](\d{1,2})(?:[\:\.](\d{1,2}))?(\w{2})?$/,
- handler: function(bits){
- var d = new Date(bits[Date.orderIndex('year')],
- bits[Date.orderIndex('month')] - 1,
- bits[Date.orderIndex('date')]);
- d.set('hr', bits[4]);
- d.set('min', bits[5]);
- d.set('sec', bits[6] || 0);
- if (bits[7]) d.set('ampm', bits[7]);
- return Date.fixY2K(d);
- }
- }
- ]
+ defineFormat: function(name, format){
+ formats[name] = format;
+ },
+ defineFormats: function(formats){
+ for (var name in formats) Date.defineFormat(name, formats[name]);
+ },
+
+ parsePatterns: parsePatterns, // this is deprecated
+
+ defineParser: function(pattern){
+ parsePatterns.push((pattern.re && pattern.handler) ? pattern : build(pattern));
+ },
+
+ defineParsers: function(){
+ Array.flatten(arguments).each(Date.defineParser);
+ },
+
+ define2DigitYearStart: function(year){
+ startYear = year % 100;
+ startCentury = year - startYear;
+ }
+
});
-})();/*
-Script: Date.Extras.js
- Extends the Date native object to include extra methods (on top of those in Date.js).
+var startCentury = 1900;
+var startYear = 70;
- License:
- MIT-style license.
+var regexOf = function(type){
+ return new RegExp('(?:' + Date.getMsg(type).map(function(name){
+ return name.substr(0, 3);
+ }).join('|') + ')[a-z]*');
+};
- Authors:
- Aaron Newton
+var replacers = function(key){
+ switch(key){
+ case 'x': // iso8601 covers yyyy-mm-dd, so just check if month is first
+ return ((Date.orderIndex('month') == 1) ? '%m[.-/]%d' : '%d[.-/]%m') + '([.-/]%y)?';
+ case 'X':
+ return '%H([.:]%M)?([.:]%S([.:]%s)?)? ?%p? ?%T?';
+ }
+ return null;
+};
-*/
+var keys = {
+ d: /[0-2]?[0-9]|3[01]/,
+ H: /[01]?[0-9]|2[0-3]/,
+ I: /0?[1-9]|1[0-2]/,
+ M: /[0-5]?\d/,
+ s: /\d+/,
+ o: /[a-z]*/,
+ p: /[ap]\.?m\.?/,
+ y: /\d{2}|\d{4}/,
+ Y: /\d{4}/,
+ T: /Z|[+-]\d{2}(?::?\d{2})?/
+};
-['LastDayOfMonth', 'Ordinal'].each(function(method){
- Date.Methods[method.toLowerCase()] = method;
-});
+keys.m = keys.I;
+keys.S = keys.M;
+var currentLanguage;
-Date.implement({
+var recompile = function(language){
+ currentLanguage = language;
+
+ keys.a = keys.A = regexOf('days');
+ keys.b = keys.B = regexOf('months');
+
+ parsePatterns.each(function(pattern, i){
+ if (pattern.format) parsePatterns[i] = build(pattern.format);
+ });
+};
- timeDiffInWords: function(relative_to){
- return Date.distanceOfTimeInWords(this, relative_to || new Date);
- },
+var build = function(format){
+ if (!currentLanguage) return {format: format};
+
+ var parsed = [];
+ var re = (format.source || format) // allow format to be regex
+ .replace(/%([a-z])/gi,
+ function($0, $1){
+ return replacers($1) || $0;
+ }
+ ).replace(/\((?!\?)/g, '(?:') // make all groups non-capturing
+ .replace(/ (?!\?|\*)/g, ',? ') // be forgiving with spaces and commas
+ .replace(/%([a-z%])/gi,
+ function($0, $1){
+ var p = keys[$1];
+ if (!p) return $1;
+ parsed.push($1);
+ return '(' + p.source + ')';
+ }
+ ).replace(/\[a-z\]/gi, '[a-z\\u00c0-\\uffff]'); // handle unicode words
- getOrdinal: function(dayOfMonth){
- return Date.getMsg('ordinal', dayOfMonth || this.get('date'));
- },
+ return {
+ format: format,
+ re: new RegExp('^' + re + '$', 'i'),
+ handler: function(bits){
+ bits = bits.slice(1).associate(parsed);
+ var date = new Date().clearTime();
+ if ('d' in bits) handle.call(date, 'd', 1);
+ if ('m' in bits) handle.call(date, 'm', 1);
+ for (var key in bits) handle.call(date, key, bits[key]);
+ return date;
+ }
+ };
+};
- getDayOfYear: function(){
- return ((Date.UTC(this.getFullYear(), this.getMonth(), this.getDate() + 1, 0, 0, 0)
- - Date.UTC(this.getFullYear(), 0, 1, 0, 0, 0) ) / Date.units.day());
- },
+var handle = function(key, value){
+ if (!value) return this;
- getLastDayOfMonth: function(){
- var ret = this.clone();
- ret.setMonth(ret.getMonth() + 1, 0);
- return ret.getDate();
+ switch(key){
+ case 'a': case 'A': return this.set('day', Date.parseDay(value, true));
+ case 'b': case 'B': return this.set('mo', Date.parseMonth(value, true));
+ case 'd': return this.set('date', value);
+ case 'H': case 'I': return this.set('hr', value);
+ case 'm': return this.set('mo', value - 1);
+ case 'M': return this.set('min', value);
+ case 'p': return this.set('ampm', value.replace(/\./g, ''));
+ case 'S': return this.set('sec', value);
+ case 's': return this.set('ms', ('0.' + value) * 1000);
+ case 'w': return this.set('day', value);
+ case 'Y': return this.set('year', value);
+ case 'y':
+ value = +value;
+ if (value < 100) value += startCentury + (value < startYear ? 100 : 0);
+ return this.set('year', value);
+ case 'T':
+ if (value == 'Z') value = '+00';
+ var offset = value.match(/([+-])(\d{2}):?(\d{2})?/);
+ offset = (offset[1] + '1') * (offset[2] * 60 + (+offset[3] || 0)) + this.getTimezoneOffset();
+ return this.set('time', this - offset * 60000);
}
-});
+ return this;
+};
-Date.alias('timeDiffInWords', 'timeAgoInWords');
+Date.defineParsers(
+ '%Y([-./]%m([-./]%d((T| )%X)?)?)?', // "1999-12-31", "1999-12-31 11:59pm", "1999-12-31 23:59:59", ISO8601
+ '%Y%m%d(T%H(%M%S?)?)?', // "19991231", "19991231T1159", compact
+ '%x( %X)?', // "12/31", "12.31.99", "12-31-1999", "12/31/2008 11:59 PM"
+ '%d%o( %b( %Y)?)?( %X)?', // "31st", "31st December", "31 Dec 1999", "31 Dec 1999 11:59pm"
+ '%b( %d%o)?( %Y)?( %X)?', // Same as above with month and day switched
+ '%Y %b( %d%o( %X)?)?' // Same as above with year coming first
+);
-$extend(Date, {
+MooTools.lang.addEvent('langChange', function(language){
+ if (MooTools.lang.get('Date')) recompile(language);
+}).fireEvent('langChange', MooTools.lang.getCurrentLanguage());
- distanceOfTimeInWords: function(fromTime, toTime){
- return this.getTimePhrase(((toTime.getTime() - fromTime.getTime()) / 1000).toInt(), fromTime, toTime);
+})();/*
+---
+
+script: Date.Extras.js
+
+description: Extends the Date native object to include extra methods (on top of those in Date.js).
+
+license: MIT-style license
+
+authors:
+- Aaron Newton
+- Scott Kyle
+
+requires:
+- /Date
+
+provides: [Date.Extras]
+
+...
+*/
+
+Date.implement({
+
+ timeDiffInWords: function(relative_to){
+ return Date.distanceOfTimeInWords(this, relative_to || new Date);
},
- getTimePhrase: function(delta, fromTime, toTime){
- var getPhrase = function(){
- var suffix;
- if (delta >= 0){
- suffix = 'Ago';
+ timeDiff: function(to, joiner){
+ if (to == null) to = new Date;
+ var delta = ((to - this) / 1000).toInt();
+ if (!delta) return '0s';
+
+ var durations = {s: 60, m: 60, h: 24, d: 365, y: 0};
+ var duration, vals = [];
+
+ for (var step in durations){
+ if (!delta) break;
+ if ((duration = durations[step])){
+ vals.unshift((delta % duration) + step);
+ delta = (delta / duration).toInt();
} else {
- delta = delta * -1;
- suffix = 'Until';
+ vals.unshift(delta + step);
}
- if (delta < 60){
- return Date.getMsg('lessThanMinute' + suffix, delta);
- } else if (delta < 120){
- return Date.getMsg('minute' + suffix, delta);
- } else if (delta < (45 * 60)){
- delta = (delta / 60).round();
- return Date.getMsg('minutes' + suffix, delta);
- } else if (delta < (90 * 60)){
- return Date.getMsg('hour' + suffix, delta);
- } else if (delta < (24 * 60 * 60)){
- delta = (delta / 3600).round();
- return Date.getMsg('hours' + suffix, delta);
- } else if (delta < (48 * 60 * 60)){
- return Date.getMsg('day' + suffix, delta);
- } else {
- delta = (delta / 86400).round();
- return Date.getMsg('days' + suffix, delta);
- }
- };
- return getPhrase().substitute({delta: delta});
+ }
+
+ return vals.join(joiner || ':');
}
});
+Date.alias('timeDiffInWords', 'timeAgoInWords');
-Date.parsePatterns.extend([
+Date.extend({
- {
- // yyyy-mm-ddTHH:MM:SS-0500 (ISO8601) i.e.2007-04-17T23:15:22Z
- // inspired by: http://delete.me.uk/2005/03/iso8601.html
- re: /^(\d{4})(?:-?(\d{2})(?:-?(\d{2})(?:[T ](\d{2})(?::?(\d{2})(?::?(\d{2})(?:\.(\d+))?)?)?(?:Z|(?:([-+])(\d{2})(?::?(\d{2}))?)?)?)?)?)?$/,
- handler: function(bits){
- var offset = 0;
- var d = new Date(bits[1], 0, 1);
- if (bits[3]) d.set('date', bits[3]);
- if (bits[2]) d.set('mo', bits[2] - 1);
- if (bits[4]) d.set('hr', bits[4]);
- if (bits[5]) d.set('min', bits[5]);
- if (bits[6]) d.set('sec', bits[6]);
- if (bits[7]) d.set('ms', ('0.' + bits[7]).toInt() * 1000);
- if (bits[9]){
- offset = (bits[9].toInt() * 60) + bits[10].toInt();
- offset *= ((bits[8] == '-') ? 1 : -1);
- }
- //offset -= d.getTimezoneOffset();
- d.setTime((d * 1) + (offset * 60 * 1000).toInt());
- return d;
- }
+ distanceOfTimeInWords: function(from, to){
+ return Date.getTimePhrase(((to - from) / 1000).toInt());
},
- {
- //"today"
- re: /^tod/i,
- handler: function(){
- return new Date();
+ getTimePhrase: function(delta){
+ var suffix = (delta < 0) ? 'Until' : 'Ago';
+ if (delta < 0) delta *= -1;
+
+ var units = {
+ minute: 60,
+ hour: 60,
+ day: 24,
+ week: 7,
+ month: 52 / 12,
+ year: 12,
+ eon: Infinity
+ };
+
+ var msg = 'lessThanMinute';
+
+ for (var unit in units){
+ var interval = units[unit];
+ if (delta < 1.5 * interval){
+ if (delta > 0.75 * interval) msg = unit;
+ break;
+ }
+ delta /= interval;
+ msg = unit + 's';
}
- },
+
+ return Date.getMsg(msg + suffix).substitute({delta: delta.round()});
+ }
- {
- //"tomorow"
- re: /^tom/i,
- handler: function(){
- return new Date().increment();
- }
- },
+});
- {
- //"yesterday"
- re: /^yes/i,
- handler: function(){
- return new Date().decrement();
- }
- },
- {
- //4th, 23rd
- re: /^(\d{1,2})(st|nd|rd|th)?$/i,
- handler: function(bits){
- var d = new Date();
- d.set('date', bits[1].toInt());
- return d;
- }
- },
+Date.defineParsers(
{
- //4th Jan, 23rd May
- re: /^(\d{1,2})(?:st|nd|rd|th)? (\w+)$/i,
+ // "today", "tomorrow", "yesterday"
+ re: /^(?:tod|tom|yes)/i,
handler: function(bits){
- var d = new Date();
- d.set('mo', Date.parseMonth(bits[2], true), bits[1].toInt());
- return d;
+ var d = new Date().clearTime();
+ switch(bits[0]){
+ case 'tom': return d.increment();
+ case 'yes': return d.decrement();
+ default: return d;
+ }
}
},
{
- //4th Jan 2000, 23rd May 2004
- re: /^(\d{1,2})(?:st|nd|rd|th)? (\w+),? (\d{4})$/i,
+ // "next Wednesday", "last Thursday"
+ re: /^(next|last) ([a-z]+)$/i,
handler: function(bits){
- var d = new Date();
- d.set('mo', Date.parseMonth(bits[2], true), bits[1].toInt());
- d.setYear(bits[3]);
- return d;
- }
- },
-
- {
- //Jan 4th
- re: /^(\w+) (\d{1,2})(?:st|nd|rd|th)?,? (\d{4})$/i,
- handler: function(bits){
- var d = new Date();
- d.set('mo', Date.parseMonth(bits[1], true), bits[2].toInt());
- d.setYear(bits[3]);
- return d;
- }
- },
-
- {
- //Jan 4th 2003
- re: /^next (\w+)$/i,
- handler: function(bits){
- var d = new Date();
+ var d = new Date().clearTime();
var day = d.getDay();
- var newDay = Date.parseDay(bits[1], true);
+ var newDay = Date.parseDay(bits[2], true);
var addDays = newDay - day;
- if (newDay <= day){
- addDays += 7;
- }
- d.set('date', d.getDate() + addDays);
- return d;
+ if (newDay <= day) addDays += 7;
+ if (bits[1] == 'last') addDays -= 7;
+ return d.set('date', d.getDate() + addDays);
}
- },
+ }
- {
- //4 May 08:12
- re: /^\d+\s[a-zA-z]..\s\d.\:\d.$/,
- handler: function(bits){
- var d = new Date();
- bits = bits[0].split(' ');
- d.set('date', bits[0]);
- var m;
- Date.getMsg('months').each(function(mo, i){
- if (new RegExp('^' + bits[1]).test(mo)) m = i;
- });
- d.set('mo', m);
- d.set('hr', bits[2].split(':')[0]);
- d.set('min', bits[2].split(':')[1]);
- d.set('ms', 0);
- return d;
- }
- },
+);
+/*
+---
- {
- re: /^last (\w+)$/i,
- handler: function(bits){
- return Date.parse('next ' + bits[0]).decrement('day', 7);
- }
- }
+script: Hash.Extras.js
-]);/*
-Script: Hash.Extras.js
- Extends the Hash native object to include getFromPath which allows a path notation to child elements.
+description: Extends the Hash native object to include getFromPath which allows a path notation to child elements.
- License:
- MIT-style license.
+license: MIT-style license
- Authors:
- Aaron Newton
+authors:
+- Aaron Newton
+
+requires:
+- core:1.2.4/Hash.base
+- /MooTools.More
+
+provides: [Hash.Extras]
+
+...
*/
Hash.implement({
@@ -4906,21 +5759,31 @@
}
});/*
-Script: String.Extras.js
- Extends the String native object to include methods useful in managing various kinds of strings (query strings, urls, html, etc).
+---
- License:
- MIT-style license.
+script: String.Extras.js
- Authors:
- Aaron Newton
- Guillermo Rauch
+description: Extends the String native object to include methods useful in managing various kinds of strings (query strings, urls, html, etc).
+license: MIT-style license
+
+authors:
+- Aaron Newton
+- Guillermo Rauch
+
+requires:
+- core:1.2.4/String
+- core:1.2.4/$util
+- core:1.2.4/Array
+
+provides: [String.Extras]
+
+...
*/
(function(){
-var special = ['Ãâ¬','à','Ã�','á','Ãâ','â','ÃÆ','ã','Ãâ','ä','Ãâ¦','ÃÂ¥','Ãâ','ÃÆ','Ãâ','Ãâ¦','Ãâ ','Ãâ¡','ÃÅ','Ã�','Ãâ¡','ç', 'ÃŽ','Ã�','Ã�','Ãâ', 'ÃË','è','Ãâ°','é','ÃÅ ','ê','Ãâ¹','ë','ÃÅ¡','Ãâº','ÃË','Ãâ¢', 'Þ','ß','ÃÅ','ì','Ã�','ÃÂ','ÃŽ','î','Ã�','ï', 'ù','ú','ý','þ','Ã
�','Ã
â', 'Ãâ','ñ','Ã
â¡','Ã
Ë','Ã
Æ','Ã
â','Ãâ','ò','Ãâ','ó','Ãâ','ô','Ãâ¢','õ','Ãâ','ö','ÃË','ø','Ã
â','Ã
Ë','Ã
â¢','Ã
â','Ã
â¢','Ã
 ','Ã
¡','Ã
ž','Ã
Ÿ','Ã
Å¡','Ã
âº', 'Ã
¤','Ã
Â¥','Ã
¤','Ã
Â¥','Ã
¢','Ã
£','Ãâ¢','ù','ÃÅ¡','ú','Ãâº','û','ÃÅ','ü','Ã
®','Ã
¯', 'Ã
¸','ÿ','ý','Ã�','Ã
½','Ã
¾','Ã
¹','Ã
º','Ã
»','Ã
¼', 'Þ','þ','Ã�','ð','ß','Ã
â','Ã
â','Ãâ ','æ','õ'];
+var special = ['Ã','à ','Ã','á','Ã','â','Ã','ã','Ã','ä','Ã
','Ã¥','Ä','Ä','Ä','Ä
','Ä','Ä','Ä','Ä','Ã','ç', 'Ä','Ä','Ä','Ä', 'Ã','è','Ã','é','Ã','ê','Ã','ë','Ä','Ä','Ä','Ä', 'Ä','Ä','Ã','ì','Ã','Ã','Ã','î','Ã','ï', 'Ĺ','ĺ','Ľ','ľ','Å','Å', 'Ã','ñ','Å','Å','Å','Å','Ã','ò','Ã','ó','Ã','ô','Ã','õ','Ã','ö','Ã','ø','Å','Å','Å','Å','Å','Å ','Å¡','Å','Å','Å','Å', 'Ť','Å¥','Ť','Å¥','Å¢','Å£','Ã','ù','Ã','ú','Ã','û','Ã','ü','Å®','ů', 'Ÿ','ÿ','ý','Ã','Ž','ž','Ź','ź','Å»','ż', 'Ã','þ','Ã','ð','Ã','Å','Å','Ã','æ','µ'];
var standard = ['A','a','A','a','A','a','A','a','Ae','ae','A','a','A','a','A','a','C','c','C','c','C','c','D','d','D','d', 'E','e','E','e','E','e','E','e','E','e','E','e','G','g','I','i','I','i','I','i','I','i','L','l','L','l','L','l', 'N','n','N','n','N','n', 'O','o','O','o','O','o','O','o','Oe','oe','O','o','o', 'R','r','R','r', 'S','s','S','s','S','s','T','t','T','t','T','t', 'U','u','U','u','U','u','Ue','ue','U','u','Y','y','Y','y','Z','z','Z','z','Z','z','TH','th','DH','dh','ss','OE','oe','AE','ae','u'];
@@ -4935,6 +5798,13 @@
"\uFFFD": "»"
};
+var getRegForTag = function(tag, contents) {
+ tag = tag || '';
+ var regstr = contents ? "<" + tag + "[^>]*>([\\s\\S]*?)<\/" + tag + ">" : "<\/?" + tag + "([^>]+)?>";
+ reg = new RegExp(regstr, "gi");
+ return reg;
+};
+
String.implement({
standardize: function(){
@@ -4951,17 +5821,20 @@
pad: function(length, str, dir){
if (this.length >= length) return this;
- str = str || ' ';
- var pad = str.repeat(length - this.length).substr(0, length - this.length);
+ var pad = (str == null ? ' ' : '' + str).repeat(length - this.length).substr(0, length - this.length);
if (!dir || dir == 'right') return this + pad;
if (dir == 'left') return pad + this;
return pad.substr(0, (pad.length / 2).floor()) + this + pad.substr(0, (pad.length / 2).ceil());
},
- stripTags: function(){
- return this.replace(/<\/?[^>]+>/gi, '');
+ getTags: function(tag, contents){
+ return this.match(getRegForTag(tag, contents)) || [];
},
+ stripTags: function(tag, contents){
+ return this.replace(getRegForTag(tag, contents), '');
+ },
+
tidy: function(){
var txt = this.toString();
$each(tidymap, function(value, key){
@@ -4973,15 +5846,27 @@
});
})();/*
-Script: String.QueryString.js
- ...
+---
- License:
- MIT-style license.
+script: String.QueryString.js
- Authors:
- Sebastian MarkbÃÂ¥ge, Aaron Newton, Lennart Pilon, Valerio Proietti
+description: Methods for dealing with URI query strings.
+
+license: MIT-style license
+
+authors:
+- Sebastian Markbåge, Aaron Newton, Lennart Pilon, Valerio Proietti
+
+requires:
+- core:1.2.4/Array
+- core:1.2.4/String
+- /MooTools.More
+
+provides: [String.QueryString]
+
+...
*/
+
String.implement({
parseQueryString: function(){
@@ -5014,38 +5899,46 @@
}
});/*
-Script: URI.js
- Provides methods useful in managing the window location and uris.
+---
- License:
- MIT-style license.
+script: URI.js
- Authors:
- Sebastian Markbåge, Aaron Newton
+description: Provides methods useful in managing the window location and uris.
+
+license: MIT-style license
+
+authors:
+- Sebastian Markbåge
+- Aaron Newton
+
+requires:
+- core:1.2.4/Selectors
+- /String.QueryString
+
+provides: URI
+
+...
*/
var URI = new Class({
Implements: Options,
- /*
options: {
- base: false
+ /*base: false*/
},
- */
regex: /^(?:(\w+):)?(?:\/\/(?:(?:([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)?(\.\.?$|(?:[^?#\/]*\/)*)([^?#]*)(?:\?([^#]*))?(?:#(.*))?/,
parts: ['scheme', 'user', 'password', 'host', 'port', 'directory', 'file', 'query', 'fragment'],
- schemes: { http: 80, https: 443, ftp: 21, rtsp: 554, mms: 1755, file: 0 },
+ schemes: {http: 80, https: 443, ftp: 21, rtsp: 554, mms: 1755, file: 0},
initialize: function(uri, options){
this.setOptions(options);
var base = this.options.base || URI.base;
- uri = uri || base;
- if (uri && uri.parsed)
- this.parsed = $unlink(uri.parsed);
- else
- this.set('value', uri.href || uri.toString(), base ? new URI(base) : false);
+ if(!uri) uri = base;
+
+ if (uri && uri.parsed) this.parsed = $unlink(uri.parsed);
+ else this.set('value', uri.href || uri.toString(), base ? new URI(base) : false);
},
parse: function(value, base){
@@ -5056,7 +5949,7 @@
},
merge: function(bits, base){
- if (!bits.scheme && !base.scheme) return false;
+ if ((!bits || !bits.scheme) && (!base || !base.scheme)) return false;
if (base){
this.parts.every(function(part){
if (bits[part]) return false;
@@ -5095,6 +5988,8 @@
if (scheme) scheme = scheme[1];
if (scheme && !$defined(this.schemes[scheme.toLowerCase()])) this.parsed = { scheme: scheme, value: value };
else this.parsed = this.parse(value, (base || this).parsed) || (scheme ? { scheme: scheme, value: value } : { value: value });
+ } else if (part == 'data') {
+ this.setData(value);
} else {
this.parsed[part] = value;
}
@@ -5106,7 +6001,7 @@
case 'value': return this.combine(this.parsed, base ? base.parsed : false);
case 'data' : return this.getData();
}
- return this.parsed[part] || undefined;
+ return this.parsed[part] || '';
},
go: function(){
@@ -5125,9 +6020,9 @@
},
setData: function(values, merge, part){
- if ($type(arguments[0]) == 'string'){
- values = this.getData();
- values[arguments[0]] = arguments[1];
+ if (typeof values == 'string'){
+ values = this.getData();
+ values[arguments[0]] = arguments[1];
} else if (merge) {
values = $merge(this.getData(), values);
}
@@ -5140,34 +6035,44 @@
});
-['toString', 'valueOf'].each(function(method){
- URI.prototype[method] = function(){
- return this.get('value');
- };
-});
+URI.prototype.toString = URI.prototype.valueOf = function(){
+ return this.get('value');
+};
-
URI.regs = {
endSlash: /\/$/,
scheme: /^(\w+):/,
directoryDot: /\.\/|\.$/
};
-URI.base = new URI($$('base[href]').getLast(), { base: document.location });
+URI.base = new URI(document.getElements('base[href]', true).getLast(), {base: document.location});
String.implement({
- toURI: function(options){ return new URI(this, options); }
+ toURI: function(options){
+ return new URI(this, options);
+ }
});/*
-Script: URI.Relative.js
- Extends the URI class to add methods for computing relative and absolute urls.
+---
- License:
- MIT-style license.
+script: URI.Relative.js
- Authors:
- Sebastian MarkbÃÂ¥ge
+description: Extends the URI class to add methods for computing relative and absolute urls.
+
+license: MIT-style license
+
+authors:
+- Sebastian Markbåge
+
+
+requires:
+- /Class.refactor
+- /URI
+
+provides: [URI.Relative]
+
+...
*/
URI = Class.refactor(URI, {
@@ -5203,16 +6108,26 @@
}
});/*
-Script: Element.Forms.js
- Extends the Element native object to include methods useful in managing inputs.
+---
- License:
- MIT-style license.
+script: Element.Forms.js
- Authors:
- Aaron Newton
+description: Extends the Element native object to include methods useful in managing inputs.
+license: MIT-style license
+
+authors:
+- Aaron Newton
+
+requires:
+- core:1.2.4/Element
+- /MooTools.More
+
+provides: [Element.Forms]
+
+...
*/
+
Element.implement({
tidy: function(){
@@ -5224,8 +6139,8 @@
},
getSelectedText: function(){
- if (document.selection && document.selection.createRange) return document.selection.createRange().text;
- return this.getTextInRange(this.getSelectionStart(), this.getSelectionEnd());
+ if (this.setSelectionRange) return this.getTextInRange(this.getSelectionStart(), this.getSelectionEnd());
+ return document.selection.createRange().text;
},
getSelectedRange: function() {
@@ -5239,9 +6154,10 @@
pos.end = pos.start + range.text.length;
} else {
var value = this.get('value');
- var offset = value.length - value.match(/[\n\r]*$/)[0].length;
+ var offset = value.length;
dup.moveToElementText(this);
dup.setEndPoint('StartToEnd', range);
+ if(dup.text.length) offset -= value.match(/[\n\r]*$/)[0].length;
pos.end = offset - dup.text.length;
dup.setEndPoint('StartToStart', range);
pos.start = offset - dup.text.length;
@@ -5268,7 +6184,10 @@
},
selectRange: function(start, end){
- if (this.createTextRange){
+ if (this.setSelectionRange) {
+ this.focus();
+ this.setSelectionRange(start, end);
+ } else {
var value = this.get('value');
var diff = value.substr(start, end - start).replace(/\r/g, '').length;
start = value.substr(0, start).replace(/\r/g, '').length;
@@ -5277,9 +6196,6 @@
range.moveEnd('character', start + diff);
range.moveStart('character', start);
range.select();
- } else {
- this.focus();
- this.setSelectionRange(start, end);
}
return this;
},
@@ -5316,21 +6232,170 @@
}
});/*
-Script: Element.Measure.js
- Extends the Element native object to include methods useful in measuring dimensions.
+---
- Element.measure / .expose methods by Daniel Steigerwald
- License: MIT-style license.
- Copyright: Copyright (c) 2008 Daniel Steigerwald, daniel.steigerwald.cz
+script: Elements.From.js
- License:
- MIT-style license.
+description: Returns a collection of elements from a string of html.
- Authors:
- Aaron Newton
+license: MIT-style license
+authors:
+- Aaron Newton
+
+requires:
+- core:1.2.4/Element
+- /MooTools.More
+
+provides: [Elements.from]
+
+...
*/
+Elements.from = function(text, excludeScripts){
+ if ($pick(excludeScripts, true)) text = text.stripScripts();
+
+ var container, match = text.match(/^\s*<(t[dhr]|tbody|tfoot|thead)/i);
+
+ if (match){
+ container = new Element('table');
+ var tag = match[1].toLowerCase();
+ if (['td', 'th', 'tr'].contains(tag)){
+ container = new Element('tbody').inject(container);
+ if (tag != 'tr') container = new Element('tr').inject(container);
+ }
+ }
+
+ return (container || new Element('div')).set('html', text).getChildren();
+};/*
+---
+
+script: Element.Delegation.js
+
+description: Extends the Element native object to include the delegate method for more efficient event management.
+
+credits:
+- "Event checking based on the work of Daniel Steigerwald. License: MIT-style license. Copyright: Copyright (c) 2008 Daniel Steigerwald, daniel.steigerwald.cz"
+
+license: MIT-style license
+
+authors:
+- Aaron Newton
+- Daniel Steigerwald
+
+requires:
+- core:1.2.4/Element.Event
+- core:1.2.4/Selectors
+- /MooTools.More
+
+provides: [Element.Delegation]
+
+...
+*/
+(function(){
+
+ var match = /(.*?):relay\(([^)]+)\)$/,
+ combinators = /[+>~\s]/,
+ splitType = function(type){
+ var bits = type.match(match);
+ return !bits ? {event: type} : {
+ event: bits[1],
+ selector: bits[2]
+ };
+ },
+ check = function(e, selector){
+ var t = e.target;
+ if (combinators.test(selector = selector.trim())){
+ var els = this.getElements(selector);
+ for (var i = els.length; i--; ){
+ var el = els[i];
+ if (t == el || el.hasChild(t)) return el;
+ }
+ } else {
+ for ( ; t && t != this; t = t.parentNode){
+ if (Element.match(t, selector)) return document.id(t);
+ }
+ }
+ return null;
+ };
+
+ var oldAddEvent = Element.prototype.addEvent,
+ oldRemoveEvent = Element.prototype.removeEvent;
+
+ Element.implement({
+
+ addEvent: function(type, fn){
+ var splitted = splitType(type);
+ if (splitted.selector){
+ var monitors = this.retrieve('$moo:delegateMonitors', {});
+ if (!monitors[type]){
+ var monitor = function(e){
+ var el = check.call(this, e, splitted.selector);
+ if (el) this.fireEvent(type, [e, el], 0, el);
+ }.bind(this);
+ monitors[type] = monitor;
+ oldAddEvent.call(this, splitted.event, monitor);
+ }
+ }
+ return oldAddEvent.apply(this, arguments);
+ },
+
+ removeEvent: function(type, fn){
+ var splitted = splitType(type);
+ if (splitted.selector){
+ var events = this.retrieve('events');
+ if (!events || !events[type] || (fn && !events[type].keys.contains(fn))) return this;
+
+ if (fn) oldRemoveEvent.apply(this, [type, fn]);
+ else oldRemoveEvent.apply(this, type);
+
+ events = this.retrieve('events');
+ if (events && events[type] && events[type].length == 0){
+ var monitors = this.retrieve('$moo:delegateMonitors', {});
+ oldRemoveEvent.apply(this, [splitted.event, monitors[type]]);
+ delete monitors[type];
+ }
+ return this;
+ }
+
+ return oldRemoveEvent.apply(this, arguments);
+ },
+
+ fireEvent: function(type, args, delay, bind){
+ var events = this.retrieve('events');
+ if (!events || !events[type]) return this;
+ events[type].keys.each(function(fn){
+ fn.create({bind: bind || this, delay: delay, arguments: args})();
+ }, this);
+ return this;
+ }
+
+ });
+
+})();/*
+---
+
+script: Element.Measure.js
+
+description: Extends the Element native object to include methods useful in measuring dimensions.
+
+credits: "Element.measure / .expose methods by Daniel Steigerwald License: MIT-style license. Copyright: Copyright (c) 2008 Daniel Steigerwald, daniel.steigerwald.cz"
+
+license: MIT-style license
+
+authors:
+- Aaron Newton
+
+requires:
+- core:1.2.4/Element.Style
+- core:1.2.4/Element.Dimensions
+- /MooTools.More
+
+provides: [Element.Measure]
+
+...
+*/
+
Element.implement({
measure: function(fn){
@@ -5339,8 +6404,8 @@
};
if (vis(this)) return fn.apply(this);
var parent = this.getParent(),
- toMeasure = [],
- restorers = [];
+ restorers = [],
+ toMeasure = [];
while (!vis(parent) && parent != document.body) {
toMeasure.push(parent.expose());
parent = parent.getParent();
@@ -5356,12 +6421,15 @@
expose: function(){
if (this.getStyle('display') != 'none') return $empty;
- var before = this.getStyles('display', 'position', 'visibility');
- return this.setStyles({
+ var before = this.style.cssText;
+ this.setStyles({
display: 'block',
position: 'absolute',
visibility: 'hidden'
- }).setStyles.pass(before, this);
+ });
+ return function(){
+ this.style.cssText = before;
+ }.bind(this);
},
getDimensions: function(options){
@@ -5370,14 +6438,17 @@
var getSize = function(el, options){
return (options.computeSize)?el.getComputedSize(options):el.getSize();
};
- if (this.getStyle('display') == 'none'){
+ var parent = this.getParent('body');
+ if (parent && this.getStyle('display') == 'none'){
dim = this.measure(function(){
return getSize(this, options);
});
- } else {
+ } else if (parent){
try { //safari sometimes crashes here, so catch it
dim = getSize(this, options);
}catch(e){}
+ } else {
+ dim = {x: 0, y: 0};
}
return $chk(dim.x) ? $extend(dim, {width: dim.x, height: dim.y}) : $extend(dim, {x: dim.width, y: dim.height});
},
@@ -5416,8 +6487,7 @@
var subtracted = [];
$each(options.plains, function(plain, key){ //keys: width, height, plains: ['left', 'right'], ['top','bottom']
var capitalized = key.capitalize();
- size['total' + capitalized] = 0;
- size['computed' + capitalized] = 0;
+ size['total' + capitalized] = size['computed' + capitalized] = 0;
plain.each(function(edge){ //top, left, right, bottom
size['computed' + edge.capitalize()] = 0;
getStyles.each(function(style, i){ //padding, border, etc.
@@ -5450,14 +6520,26 @@
}
});/*
-Script: Element.Pin.js
- Extends the Element native object to include the pin method useful for fixed positioning for elements.
+---
- License:
- MIT-style license.
+script: Element.Pin.js
- Authors:
- Aaron Newton
+description: Extends the Element native object to include the pin method useful for fixed positioning for elements.
+
+license: MIT-style license
+
+authors:
+- Aaron Newton
+
+requires:
+- core:1.2.4/Element.Event
+- core:1.2.4/Element.Dimensions
+- core:1.2.4/Element.Style
+- /MooTools.More
+
+provides: [Element.Pin]
+
+...
*/
(function(){
@@ -5477,13 +6559,14 @@
pin: function(enable){
if (this.getStyle('display') == 'none') return null;
- var p;
+ var p,
+ scroll = window.getScroll();
if (enable !== false){
p = this.getPosition();
if (!this.retrieve('pinned')){
var pos = {
- top: p.y - window.getScroll().y,
- left: p.x - window.getScroll().x
+ top: p.y - scroll.y,
+ left: p.x - scroll.x
};
if (supportsPositionFixed){
this.setStyle('position', 'fixed').setStyles(pos);
@@ -5493,12 +6576,13 @@
position: 'absolute',
top: p.y,
left: p.x
- });
+ }).addClass('isPinned');
this.store('scrollFixer', (function(){
if (this.retrieve('pinned'))
+ var scroll = window.getScroll();
this.setStyles({
- top: pos.top.toInt() + window.getScroll().y,
- left: pos.left.toInt() + window.getScroll().x
+ top: pos.top.toInt() + scroll.y,
+ left: pos.left.toInt() + scroll.x
});
}).bind(this));
window.addEvent('scroll', this.retrieve('scrollFixer'));
@@ -5508,16 +6592,16 @@
} else {
var op;
if (!Browser.Engine.trident){
- if (this.getParent().getComputedStyle('position') != 'static') op = this.getParent();
- else op = this.getParent().getOffsetParent();
+ var parent = this.getParent();
+ op = (parent.getComputedStyle('position') != 'static' ? parent : parent.getOffsetParent());
}
p = this.getPosition(op);
this.store('pinned', false);
var reposition;
if (supportsPositionFixed && !this.retrieve('pinnedByJS')){
reposition = {
- top: p.y + window.getScroll().y,
- left: p.x + window.getScroll().x
+ top: p.y + scroll.y,
+ left: p.x + scroll.x
};
} else {
this.store('pinnedByJS', false);
@@ -5527,13 +6611,13 @@
left: p.x
};
}
- this.setStyles($merge(reposition, {position: 'absolute'}));
+ this.setStyles($merge(reposition, {position: 'absolute'})).removeClass('isPinned');
}
- return this.addClass('isPinned');
+ return this;
},
unpin: function(){
- return this.pin(false).removeClass('isPinned');
+ return this.pin(false);
},
togglepin: function(){
@@ -5543,14 +6627,24 @@
});
})();/*
-Script: Element.Position.js
- Extends the Element native object to include methods useful positioning elements relative to others.
+---
- License:
- MIT-style license.
+script: Element.Position.js
- Authors:
- Aaron Newton
+description: Extends the Element native object to include methods useful positioning elements relative to others.
+
+license: MIT-style license
+
+authors:
+- Aaron Newton
+
+requires:
+- core:1.2.4/Element.Dimensions
+- /Element.Measure
+
+provides: [Elements.Position]
+
+...
*/
(function(){
@@ -5564,6 +6658,8 @@
if (options && ($defined(options.x) || $defined(options.y))) return original ? original.apply(this, arguments) : this;
$each(options||{}, function(v, k){ if (!$defined(v)) delete options[k]; });
options = $merge({
+ // minimum: { x: 0, y: 0 },
+ // maximum: { x: 0, y: 0},
relativeTo: document.body,
position: {
x: 'center', //left, center, right
@@ -5574,22 +6670,23 @@
returnPos: false,
relFixedPosition: false,
ignoreMargins: false,
+ ignoreScroll: false,
allowNegative: false
}, options);
//compute the offset of the parent positioned element if this element is in one
- var parentOffset = {x: 0, y: 0};
- var parentPositioned = false;
+ var parentOffset = {x: 0, y: 0},
+ parentPositioned = false;
/* dollar around getOffsetParent should not be necessary, but as it does not return
* a mootools extended element in IE, an error occurs on the call to expose. See:
* http://mootools.lighthouseapp.com/projects/2706/tickets/333-element-getoffsetparent-inconsistency-between-ie-and-other-browsers */
var offsetParent = this.measure(function(){
- return $(this.getOffsetParent());
+ return document.id(this.getOffsetParent());
});
if (offsetParent && offsetParent != this.getDocument().body){
parentOffset = offsetParent.measure(function(){
return this.getPosition();
});
- parentPositioned = true;
+ parentPositioned = offsetParent != document.id(options.relativeTo);
options.offset.x = options.offset.x - parentOffset.x;
options.offset.y = options.offset.y - parentOffset.y;
}
@@ -5615,26 +6712,19 @@
}
this.setStyle('position', 'absolute');
- var rel = $(options.relativeTo) || document.body;
- var calc = rel == document.body ? window.getScroll() : rel.getPosition();
- var top = calc.y;
- var left = calc.x;
+ var rel = document.id(options.relativeTo) || document.body,
+ calc = rel == document.body ? window.getScroll() : rel.getPosition(),
+ top = calc.y, left = calc.x;
- if (Browser.Engine.trident){
- var scrolls = rel.getScrolls();
- top += scrolls.y;
- left += scrolls.x;
- }
+ var scrolls = rel.getScrolls();
+ top += scrolls.y;
+ left += scrolls.x;
var dim = this.getDimensions({computeSize: true, styles:['padding', 'border','margin']});
- if (options.ignoreMargins){
- options.offset.x = options.offset.x - dim['margin-left'];
- options.offset.y = options.offset.y - dim['margin-top'];
- }
- var pos = {};
- var prefY = options.offset.y;
- var prefX = options.offset.x;
- var winSize = window.getSize();
+ var pos = {},
+ prefY = options.offset.y,
+ prefX = options.offset.x,
+ winSize = window.getSize();
switch(options.position.x){
case 'left':
pos.x = left + prefX;
@@ -5657,7 +6747,6 @@
pos.y = top + ((rel == document.body ? winSize.y : rel.offsetHeight)/2) + prefY;
break;
}
-
if (options.edge){
var edgeOffset = {};
@@ -5669,7 +6758,7 @@
edgeOffset.x = -dim.x-dim.computedRight-dim.computedLeft;
break;
default: //center
- edgeOffset.x = -(dim.x/2);
+ edgeOffset.x = -(dim.totalWidth/2);
break;
}
switch(options.edge.y){
@@ -5680,22 +6769,47 @@
edgeOffset.y = -dim.y-dim.computedTop-dim.computedBottom;
break;
default: //center
- edgeOffset.y = -(dim.y/2);
+ edgeOffset.y = -(dim.totalHeight/2);
break;
}
- pos.x = pos.x + edgeOffset.x;
- pos.y = pos.y + edgeOffset.y;
+ pos.x += edgeOffset.x;
+ pos.y += edgeOffset.y;
}
pos = {
left: ((pos.x >= 0 || parentPositioned || options.allowNegative) ? pos.x : 0).toInt(),
top: ((pos.y >= 0 || parentPositioned || options.allowNegative) ? pos.y : 0).toInt()
};
+ var xy = {left: 'x', top: 'y'};
+ ['minimum', 'maximum'].each(function(minmax) {
+ ['left', 'top'].each(function(lr) {
+ var val = options[minmax] ? options[minmax][xy[lr]] : null;
+ if (val != null && pos[lr] < val) pos[lr] = val;
+ });
+ });
if (rel.getStyle('position') == 'fixed' || options.relFixedPosition){
var winScroll = window.getScroll();
- pos.top = pos.top.toInt() + winScroll.y;
- pos.left = pos.left.toInt() + winScroll.x;
+ pos.top+= winScroll.y;
+ pos.left+= winScroll.x;
}
-
+ if (options.ignoreScroll) {
+ var relScroll = rel.getScroll();
+ pos.top-= relScroll.y;
+ pos.left-= relScroll.x;
+ }
+ if (options.ignoreMargins) {
+ pos.left += (
+ options.edge.x == 'right' ? dim['margin-right'] :
+ options.edge.x == 'center' ? -dim['margin-left'] + ((dim['margin-right'] + dim['margin-left'])/2) :
+ - dim['margin-left']
+ );
+ pos.top += (
+ options.edge.y == 'bottom' ? dim['margin-bottom'] :
+ options.edge.y == 'center' ? -dim['margin-top'] + ((dim['margin-bottom'] + dim['margin-top'])/2) :
+ - dim['margin-top']
+ );
+ }
+ pos.left = Math.ceil(pos.left);
+ pos.top = Math.ceil(pos.top);
if (options.returnPos) return pos;
else this.setStyles(pos);
return this;
@@ -5704,15 +6818,24 @@
});
})();/*
-Script: Element.Shortcuts.js
- Extends the Element native object to include some shortcut methods.
+---
- License:
- MIT-style license.
+script: Element.Shortcuts.js
- Authors:
- Aaron Newton
+description: Extends the Element native object to include some shortcut methods.
+license: MIT-style license
+
+authors:
+- Aaron Newton
+
+requires:
+- core:1.2.4/Element.Style
+- /MooTools.More
+
+provides: [Element.Shortcuts]
+
+...
*/
Element.implement({
@@ -5721,6 +6844,12 @@
return this.getStyle('display') != 'none';
},
+ isVisible: function(){
+ var w = this.offsetWidth,
+ h = this.offsetHeight;
+ return (w == 0 && h == 0) ? false : (w > 0 && h > 0) ? true : this.isDisplayed();
+ },
+
toggle: function(){
return this[this.isDisplayed() ? 'hide' : 'show']();
},
@@ -5728,10 +6857,10 @@
hide: function(){
var d;
try {
- //IE fails here if the element is not in the dom
- if ('none' != this.getStyle('display')) d = this.getStyle('display');
+ // IE fails here if the element is not in the dom
+ if ((d = this.getStyle('display')) == 'none') d = null;
} catch(e){}
-
+
return this.store('originalDisplay', d || 'block').setStyle('display', 'none');
},
@@ -5745,15 +6874,1105 @@
});
/*
-Script: FormValidator.js
- A css-class based form validation system.
+---
- License:
- MIT-style license.
+script: IframeShim.js
- Authors:
- Aaron Newton
+description: Defines IframeShim, a class for obscuring select lists and flash objects in IE.
+
+license: MIT-style license
+
+authors:
+- Aaron Newton
+
+requires:
+- core:1.2.4/Element.Event
+- core:1.2.4/Element.Style
+- core:1.2.4/Options Events
+- /Element.Position
+- /Class.Occlude
+
+provides: [IframeShim]
+
+...
*/
+
+var IframeShim = new Class({
+
+ Implements: [Options, Events, Class.Occlude],
+
+ options: {
+ className: 'iframeShim',
+ src: 'javascript:false;document.write("");',
+ display: false,
+ zIndex: null,
+ margin: 0,
+ offset: {x: 0, y: 0},
+ browsers: (Browser.Engine.trident4 || (Browser.Engine.gecko && !Browser.Engine.gecko19 && Browser.Platform.mac))
+ },
+
+ property: 'IframeShim',
+
+ initialize: function(element, options){
+ this.element = document.id(element);
+ if (this.occlude()) return this.occluded;
+ this.setOptions(options);
+ this.makeShim();
+ return this;
+ },
+
+ makeShim: function(){
+ if(this.options.browsers){
+ var zIndex = this.element.getStyle('zIndex').toInt();
+
+ if (!zIndex){
+ zIndex = 1;
+ var pos = this.element.getStyle('position');
+ if (pos == 'static' || !pos) this.element.setStyle('position', 'relative');
+ this.element.setStyle('zIndex', zIndex);
+ }
+ zIndex = ($chk(this.options.zIndex) && zIndex > this.options.zIndex) ? this.options.zIndex : zIndex - 1;
+ if (zIndex < 0) zIndex = 1;
+ this.shim = new Element('iframe', {
+ src: this.options.src,
+ scrolling: 'no',
+ frameborder: 0,
+ styles: {
+ zIndex: zIndex,
+ position: 'absolute',
+ border: 'none',
+ filter: 'progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)'
+ },
+ 'class': this.options.className
+ }).store('IframeShim', this);
+ var inject = (function(){
+ this.shim.inject(this.element, 'after');
+ this[this.options.display ? 'show' : 'hide']();
+ this.fireEvent('inject');
+ }).bind(this);
+ if (IframeShim.ready) window.addEvent('load', inject);
+ else inject();
+ } else {
+ this.position = this.hide = this.show = this.dispose = $lambda(this);
+ }
+ },
+
+ position: function(){
+ if (!IframeShim.ready || !this.shim) return this;
+ var size = this.element.measure(function(){
+ return this.getSize();
+ });
+ if (this.options.margin != undefined){
+ size.x = size.x - (this.options.margin * 2);
+ size.y = size.y - (this.options.margin * 2);
+ this.options.offset.x += this.options.margin;
+ this.options.offset.y += this.options.margin;
+ }
+ this.shim.set({width: size.x, height: size.y}).position({
+ relativeTo: this.element,
+ offset: this.options.offset
+ });
+ return this;
+ },
+
+ hide: function(){
+ if (this.shim) this.shim.setStyle('display', 'none');
+ return this;
+ },
+
+ show: function(){
+ if (this.shim) this.shim.setStyle('display', 'block');
+ return this.position();
+ },
+
+ dispose: function(){
+ if (this.shim) this.shim.dispose();
+ return this;
+ },
+
+ destroy: function(){
+ if (this.shim) this.shim.destroy();
+ return this;
+ }
+
+});
+
+window.addEvent('load', function(){
+ IframeShim.ready = true;
+});/*
+---
+
+script: Mask.js
+
+description: Creates a mask element to cover another.
+
+license: MIT-style license
+
+authors:
+- Aaron Newton
+
+requires:
+- core:1.2.4/Options
+- core:1.2.4/Events
+- core:1.2.4/Element.Event
+- /Class.Binds
+- /Element.Position
+- /IframeShim
+
+provides: [Mask]
+
+...
+*/
+
+var Mask = new Class({
+
+ Implements: [Options, Events],
+
+ Binds: ['resize'],
+
+ options: {
+ // onShow: $empty,
+ // onHide: $empty,
+ // onDestroy: $empty,
+ // onClick: $empty,
+ //inject: {
+ // where: 'after',
+ // target: null,
+ //},
+ // hideOnClick: false,
+ // id: null,
+ // destroyOnHide: false,
+ style: {},
+ 'class': 'mask',
+ maskMargins: false,
+ useIframeShim: true
+ },
+
+ initialize: function(target, options){
+ this.target = document.id(target) || document.body;
+ this.target.store('mask', this);
+ this.setOptions(options);
+ this.render();
+ this.inject();
+ },
+
+ render: function() {
+ this.element = new Element('div', {
+ 'class': this.options['class'],
+ id: this.options.id || 'mask-' + $time(),
+ styles: $merge(this.options.style, {
+ display: 'none'
+ }),
+ events: {
+ click: function(){
+ this.fireEvent('click');
+ if (this.options.hideOnClick) this.hide();
+ }.bind(this)
+ }
+ });
+ this.hidden = true;
+ },
+
+ toElement: function(){
+ return this.element;
+ },
+
+ inject: function(target, where){
+ where = where || this.options.inject ? this.options.inject.where : '' || this.target == document.body ? 'inside' : 'after';
+ target = target || this.options.inject ? this.options.inject.target : '' || this.target;
+ this.element.inject(target, where);
+ if (this.options.useIframeShim) {
+ this.shim = new IframeShim(this.element);
+ this.addEvents({
+ show: this.shim.show.bind(this.shim),
+ hide: this.shim.hide.bind(this.shim),
+ destroy: this.shim.destroy.bind(this.shim)
+ });
+ }
+ },
+
+ position: function(){
+ this.resize(this.options.width, this.options.height);
+ this.element.position({
+ relativeTo: this.target,
+ position: 'topLeft',
+ ignoreMargins: !this.options.maskMargins,
+ ignoreScroll: this.target == document.body
+ });
+ return this;
+ },
+
+ resize: function(x, y){
+ var opt = {
+ styles: ['padding', 'border']
+ };
+ if (this.options.maskMargins) opt.styles.push('margin');
+ var dim = this.target.getComputedSize(opt);
+ if (this.target == document.body) {
+ var win = window.getSize();
+ if (dim.totalHeight < win.y) dim.totalHeight = win.y;
+ if (dim.totalWidth < win.x) dim.totalWidth = win.x;
+ }
+ this.element.setStyles({
+ width: $pick(x, dim.totalWidth, dim.x),
+ height: $pick(y, dim.totalHeight, dim.y)
+ });
+ return this;
+ },
+
+ show: function(){
+ if (!this.hidden) return this;
+ this.target.addEvent('resize', this.resize);
+ if (this.target != document.body) document.id(document.body).addEvent('resize', this.resize);
+ this.position();
+ this.showMask.apply(this, arguments);
+ return this;
+ },
+
+ showMask: function(){
+ this.element.setStyle('display', 'block');
+ this.hidden = false;
+ this.fireEvent('show');
+ },
+
+ hide: function(){
+ if (this.hidden) return this;
+ this.target.removeEvent('resize', this.resize);
+ this.hideMask.apply(this, arguments);
+ if (this.options.destroyOnHide) return this.destroy();
+ return this;
+ },
+
+ hideMask: function(){
+ this.element.setStyle('display', 'none');
+ this.hidden = true;
+ this.fireEvent('hide');
+ },
+
+ toggle: function(){
+ this[this.hidden ? 'show' : 'hide']();
+ },
+
+ destroy: function(){
+ this.hide();
+ this.element.destroy();
+ this.fireEvent('destroy');
+ this.target.eliminate('mask');
+ }
+
+});
+
+Element.Properties.mask = {
+
+ set: function(options){
+ var mask = this.retrieve('mask');
+ return this.eliminate('mask').store('mask:options', options);
+ },
+
+ get: function(options){
+ if (options || !this.retrieve('mask')){
+ if (this.retrieve('mask')) this.retrieve('mask').destroy();
+ if (options || !this.retrieve('mask:options')) this.set('mask', options);
+ this.store('mask', new Mask(this, this.retrieve('mask:options')));
+ }
+ return this.retrieve('mask');
+ }
+
+};
+
+Element.implement({
+
+ mask: function(options){
+ this.get('mask', options).show();
+ return this;
+ },
+
+ unmask: function(){
+ this.get('mask').hide();
+ return this;
+ }
+
+});/*
+---
+
+script: Spinner.js
+
+description: Adds a semi-transparent overlay over a dom element with a spinnin ajax icon.
+
+license: MIT-style license
+
+authors:
+- Aaron Newton
+
+requires:
+- core:1.2.4/Fx.Tween
+- /Class.refactor
+- /Mask
+
+provides: [Spinner]
+
+...
+*/
+
+var Spinner = new Class({
+
+ Extends: Mask,
+
+ options: {
+ /*message: false,*/
+ 'class':'spinner',
+ containerPosition: {},
+ content: {
+ 'class':'spinner-content'
+ },
+ messageContainer: {
+ 'class':'spinner-msg'
+ },
+ img: {
+ 'class':'spinner-img'
+ },
+ fxOptions: {
+ link: 'chain'
+ }
+ },
+
+ initialize: function(){
+ this.parent.apply(this, arguments);
+ this.target.store('spinner', this);
+
+ //add this to events for when noFx is true; parent methods handle hide/show
+ var deactivate = function(){ this.active = false; }.bind(this);
+ this.addEvents({
+ hide: deactivate,
+ show: deactivate
+ });
+ },
+
+ render: function(){
+ this.parent();
+ this.element.set('id', this.options.id || 'spinner-'+$time());
+ this.content = document.id(this.options.content) || new Element('div', this.options.content);
+ this.content.inject(this.element);
+ if (this.options.message) {
+ this.msg = document.id(this.options.message) || new Element('p', this.options.messageContainer).appendText(this.options.message);
+ this.msg.inject(this.content);
+ }
+ if (this.options.img) {
+ this.img = document.id(this.options.img) || new Element('div', this.options.img);
+ this.img.inject(this.content);
+ }
+ this.element.set('tween', this.options.fxOptions);
+ },
+
+ show: function(noFx){
+ if (this.active) return this.chain(this.show.bind(this));
+ if (!this.hidden) {
+ this.callChain.delay(20, this);
+ return this;
+ }
+ this.active = true;
+ return this.parent(noFx);
+ },
+
+ showMask: function(noFx){
+ var pos = function(){
+ this.content.position($merge({
+ relativeTo: this.element
+ }, this.options.containerPosition));
+ }.bind(this);
+ if (noFx) {
+ this.parent();
+ pos();
+ } else {
+ this.element.setStyles({
+ display: 'block',
+ opacity: 0
+ }).tween('opacity', this.options.style.opacity || 0.9);
+ pos();
+ this.hidden = false;
+ this.fireEvent('show');
+ this.callChain();
+ }
+ },
+
+ hide: function(noFx){
+ if (this.active) return this.chain(this.hide.bind(this));
+ if (this.hidden) {
+ this.callChain.delay(20, this);
+ return this;
+ }
+ this.active = true;
+ return this.parent();
+ },
+
+ hideMask: function(noFx){
+ if (noFx) return this.parent();
+ this.element.tween('opacity', 0).get('tween').chain(function(){
+ this.element.setStyle('display', 'none');
+ this.hidden = true;
+ this.fireEvent('hide');
+ this.callChain();
+ }.bind(this));
+ },
+
+ destroy: function(){
+ this.content.destroy();
+ this.parent();
+ this.target.eliminate('spinner');
+ }
+
+});
+
+Spinner.implement(new Chain);
+
+if (window.Request) {
+ Request = Class.refactor(Request, {
+ options: {
+ useSpinner: false,
+ spinnerOptions: {},
+ spinnerTarget: false
+ },
+ initialize: function(options){
+ this._send = this.send;
+ this.send = function(options){
+ if (this.spinner) this.spinner.chain(this._send.bind(this, options)).show();
+ else this._send(options);
+ return this;
+ };
+ this.previous(options);
+ var update = document.id(this.options.spinnerTarget) || document.id(this.options.update);
+ if (this.options.useSpinner && update) {
+ this.spinner = update.get('spinner', this.options.spinnerOptions);
+ ['onComplete', 'onException', 'onCancel'].each(function(event){
+ this.addEvent(event, this.spinner.hide.bind(this.spinner));
+ }, this);
+ }
+ }
+ });
+}
+
+Element.Properties.spinner = {
+
+ set: function(options){
+ var spinner = this.retrieve('spinner');
+ return this.eliminate('spinner').store('spinner:options', options);
+ },
+
+ get: function(options){
+ if (options || !this.retrieve('spinner')){
+ if (this.retrieve('spinner')) this.retrieve('spinner').destroy();
+ if (options || !this.retrieve('spinner:options')) this.set('spinner', options);
+ new Spinner(this, this.retrieve('spinner:options'));
+ }
+ return this.retrieve('spinner');
+ }
+
+};
+
+Element.implement({
+
+ spin: function(options){
+ this.get('spinner', options).show();
+ return this;
+ },
+
+ unspin: function(){
+ var opt = Array.link(arguments, {options: Object.type, callback: Function.type});
+ this.get('spinner', opt.options).hide(opt.callback);
+ return this;
+ }
+
+});/*
+---
+
+script: Form.Request.js
+
+description: Handles the basic functionality of submitting a form and updating a dom element with the result.
+
+license: MIT-style license
+
+authors:
+- Aaron Newton
+
+requires:
+- core:1.2.4/Element.Event
+- core:1.2.4/Request.HTML
+- /Class.Binds
+- /Class.Occlude
+- /Spinner
+- /String.QueryString
+
+provides: [Form.Request]
+
+...
+*/
+
+if (!window.Form) window.Form = {};
+
+(function(){
+
+ Form.Request = new Class({
+
+ Binds: ['onSubmit', 'onFormValidate'],
+
+ Implements: [Options, Events, Class.Occlude],
+
+ options: {
+ //onFailure: $empty,
+ //onSuccess: #empty, //aliased to onComplete,
+ //onSend: $empty
+ requestOptions: {
+ evalScripts: true,
+ useSpinner: true,
+ emulation: false,
+ link: 'ignore'
+ },
+ extraData: {},
+ resetForm: true
+ },
+
+ property: 'form.request',
+
+ initialize: function(form, update, options) {
+ this.element = document.id(form);
+ if (this.occlude()) return this.occluded;
+ this.update = document.id(update);
+ this.setOptions(options);
+ this.makeRequest();
+ if (this.options.resetForm) {
+ this.request.addEvent('success', function(){
+ $try(function(){ this.element.reset(); }.bind(this));
+ if (window.OverText) OverText.update();
+ }.bind(this));
+ }
+ this.attach();
+ },
+
+ toElement: function() {
+ return this.element;
+ },
+
+ makeRequest: function(){
+ this.request = new Request.HTML($merge({
+ url: this.element.get('action'),
+ update: this.update,
+ emulation: false,
+ spinnerTarget: this.element,
+ method: this.element.get('method') || 'post'
+ }, this.options.requestOptions)).addEvents({
+ success: function(text, xml){
+ ['success', 'complete'].each(function(evt){
+ this.fireEvent(evt, [this.update, text, xml]);
+ }, this);
+ }.bind(this),
+ failure: function(xhr){
+ this.fireEvent('failure', xhr);
+ }.bind(this),
+ exception: function(){
+ this.fireEvent('failure', xhr);
+ }.bind(this)
+ });
+ },
+
+ attach: function(attach){
+ attach = $pick(attach, true);
+ method = attach ? 'addEvent' : 'removeEvent';
+
+ var fv = this.element.retrieve('validator');
+ if (fv) fv[method]('onFormValidate', this.onFormValidate);
+ if (!fv || !attach) this.element[method]('submit', this.onSubmit);
+ },
+
+ detach: function(){
+ this.attach(false);
+ },
+
+ //public method
+ enable: function(){
+ this.attach();
+ },
+
+ //public method
+ disable: function(){
+ this.detach();
+ },
+
+ onFormValidate: function(valid, form, e) {
+ if (valid || !fv.options.stopOnFailure) {
+ if (e && e.stop) e.stop();
+ this.send();
+ }
+ },
+
+ onSubmit: function(e){
+ if (this.element.retrieve('validator')) {
+ //form validator was created after Form.Request
+ this.detach();
+ this.addFormEvent();
+ return;
+ }
+ e.stop();
+ this.send();
+ },
+
+ send: function(){
+ var str = this.element.toQueryString().trim();
+ var data = $H(this.options.extraData).toQueryString();
+ if (str) str += "&" + data;
+ else str = data;
+ this.fireEvent('send', [this.element, str]);
+ this.request.send({data: str});
+ return this;
+ }
+
+ });
+
+ Element.Properties.formRequest = {
+
+ set: function(){
+ var opt = Array.link(arguments, {options: Object.type, update: Element.type, updateId: String.type});
+ var update = opt.update || opt.updateId;
+ var updater = this.retrieve('form.request');
+ if (update) {
+ if (updater) updater.update = document.id(update);
+ this.store('form.request:update', update);
+ }
+ if (opt.options) {
+ if (updater) updater.setOptions(opt.options);
+ this.store('form.request:options', opt.options);
+ }
+ return this;
+ },
+
+ get: function(){
+ var opt = Array.link(arguments, {options: Object.type, update: Element.type, updateId: String.type});
+ var update = opt.update || opt.updateId;
+ if (opt.options || update || !this.retrieve('form.request')){
+ if (opt.options || !this.retrieve('form.request:options')) this.set('form.request', opt.options);
+ if (update) this.set('form.request', update);
+ this.store('form.request', new Form.Request(this, this.retrieve('form.request:update'), this.retrieve('form.request:options')));
+ }
+ return this.retrieve('form.request');
+ }
+
+ };
+
+ Element.implement({
+
+ formUpdate: function(update, options){
+ this.get('form.request', update, options).send();
+ return this;
+ }
+
+ });
+
+})();/*
+---
+
+script: Fx.Reveal.js
+
+description: Defines Fx.Reveal, a class that shows and hides elements with a transition.
+
+license: MIT-style license
+
+authors:
+- Aaron Newton
+
+requires:
+- core:1.2.4/Fx.Morph
+- /Element.Shortcuts
+- /Element.Measure
+
+provides: [Fx.Reveal]
+
+...
+*/
+
+Fx.Reveal = new Class({
+
+ Extends: Fx.Morph,
+
+ options: {/*
+ onShow: $empty(thisElement),
+ onHide: $empty(thisElement),
+ onComplete: $empty(thisElement),
+ heightOverride: null,
+ widthOverride: null, */
+ link: 'cancel',
+ styles: ['padding', 'border', 'margin'],
+ transitionOpacity: !Browser.Engine.trident4,
+ mode: 'vertical',
+ display: 'block',
+ hideInputs: Browser.Engine.trident ? 'select, input, textarea, object, embed' : false
+ },
+
+ dissolve: function(){
+ try {
+ if (!this.hiding && !this.showing){
+ if (this.element.getStyle('display') != 'none'){
+ this.hiding = true;
+ this.showing = false;
+ this.hidden = true;
+ var startStyles = this.element.getComputedSize({
+ styles: this.options.styles,
+ mode: this.options.mode
+ });
+ var setToAuto = (this.element.style.height === ''||this.element.style.height == 'auto');
+ this.element.setStyle('display', 'block');
+ if (this.options.transitionOpacity) startStyles.opacity = 1;
+ var zero = {};
+ $each(startStyles, function(style, name){
+ zero[name] = [style, 0];
+ }, this);
+ var overflowBefore = this.element.getStyle('overflow');
+ this.element.setStyle('overflow', 'hidden');
+ var hideThese = this.options.hideInputs ? this.element.getElements(this.options.hideInputs) : null;
+ this.$chain.unshift(function(){
+ if (this.hidden){
+ this.hiding = false;
+ $each(startStyles, function(style, name){
+ startStyles[name] = style;
+ }, this);
+ this.element.setStyles($merge({display: 'none', overflow: overflowBefore}, startStyles));
+ if (setToAuto){
+ if (['vertical', 'both'].contains(this.options.mode)) this.element.style.height = '';
+ if (['width', 'both'].contains(this.options.mode)) this.element.style.width = '';
+ }
+ if (hideThese) hideThese.setStyle('visibility', 'visible');
+ }
+ this.fireEvent('hide', this.element);
+ this.callChain();
+ }.bind(this));
+ if (hideThese) hideThese.setStyle('visibility', 'hidden');
+ this.start(zero);
+ } else {
+ this.callChain.delay(10, this);
+ this.fireEvent('complete', this.element);
+ this.fireEvent('hide', this.element);
+ }
+ } else if (this.options.link == 'chain'){
+ this.chain(this.dissolve.bind(this));
+ } else if (this.options.link == 'cancel' && !this.hiding){
+ this.cancel();
+ this.dissolve();
+ }
+ } catch(e){
+ this.hiding = false;
+ this.element.setStyle('display', 'none');
+ this.callChain.delay(10, this);
+ this.fireEvent('complete', this.element);
+ this.fireEvent('hide', this.element);
+ }
+ return this;
+ },
+
+ reveal: function(){
+ try {
+ if (!this.showing && !this.hiding){
+ if (this.element.getStyle('display') == 'none' ||
+ this.element.getStyle('visiblity') == 'hidden' ||
+ this.element.getStyle('opacity') == 0){
+ this.showing = true;
+ this.hiding = this.hidden = false;
+ var setToAuto, startStyles;
+ //toggle display, but hide it
+ this.element.measure(function(){
+ setToAuto = (this.element.style.height === '' || this.element.style.height == 'auto');
+ //create the styles for the opened/visible state
+ startStyles = this.element.getComputedSize({
+ styles: this.options.styles,
+ mode: this.options.mode
+ });
+ }.bind(this));
+ $each(startStyles, function(style, name){
+ startStyles[name] = style;
+ });
+ //if we're overridding height/width
+ if ($chk(this.options.heightOverride)) startStyles.height = this.options.heightOverride.toInt();
+ if ($chk(this.options.widthOverride)) startStyles.width = this.options.widthOverride.toInt();
+ if (this.options.transitionOpacity) {
+ this.element.setStyle('opacity', 0);
+ startStyles.opacity = 1;
+ }
+ //create the zero state for the beginning of the transition
+ var zero = {
+ height: 0,
+ display: this.options.display
+ };
+ $each(startStyles, function(style, name){ zero[name] = 0; });
+ var overflowBefore = this.element.getStyle('overflow');
+ //set to zero
+ this.element.setStyles($merge(zero, {overflow: 'hidden'}));
+ //hide inputs
+ var hideThese = this.options.hideInputs ? this.element.getElements(this.options.hideInputs) : null;
+ if (hideThese) hideThese.setStyle('visibility', 'hidden');
+ //start the effect
+ this.start(startStyles);
+ this.$chain.unshift(function(){
+ this.element.setStyle('overflow', overflowBefore);
+ if (!this.options.heightOverride && setToAuto){
+ if (['vertical', 'both'].contains(this.options.mode)) this.element.style.height = '';
+ if (['width', 'both'].contains(this.options.mode)) this.element.style.width = '';
+ }
+ if (!this.hidden) this.showing = false;
+ if (hideThese) hideThese.setStyle('visibility', 'visible');
+ this.callChain();
+ this.fireEvent('show', this.element);
+ }.bind(this));
+ } else {
+ this.callChain();
+ this.fireEvent('complete', this.element);
+ this.fireEvent('show', this.element);
+ }
+ } else if (this.options.link == 'chain'){
+ this.chain(this.reveal.bind(this));
+ } else if (this.options.link == 'cancel' && !this.showing){
+ this.cancel();
+ this.reveal();
+ }
+ } catch(e){
+ this.element.setStyles({
+ display: this.options.display,
+ visiblity: 'visible',
+ opacity: 1
+ });
+ this.showing = false;
+ this.callChain.delay(10, this);
+ this.fireEvent('complete', this.element);
+ this.fireEvent('show', this.element);
+ }
+ return this;
+ },
+
+ toggle: function(){
+ if (this.element.getStyle('display') == 'none' ||
+ this.element.getStyle('visiblity') == 'hidden' ||
+ this.element.getStyle('opacity') == 0){
+ this.reveal();
+ } else {
+ this.dissolve();
+ }
+ return this;
+ },
+
+ cancel: function(){
+ this.parent.apply(this, arguments);
+ this.hidding = false;
+ this.showing = false;
+ }
+
+});
+
+Element.Properties.reveal = {
+
+ set: function(options){
+ var reveal = this.retrieve('reveal');
+ if (reveal) reveal.cancel();
+ return this.eliminate('reveal').store('reveal:options', options);
+ },
+
+ get: function(options){
+ if (options || !this.retrieve('reveal')){
+ if (options || !this.retrieve('reveal:options')) this.set('reveal', options);
+ this.store('reveal', new Fx.Reveal(this, this.retrieve('reveal:options')));
+ }
+ return this.retrieve('reveal');
+ }
+
+};
+
+Element.Properties.dissolve = Element.Properties.reveal;
+
+Element.implement({
+
+ reveal: function(options){
+ this.get('reveal', options).reveal();
+ return this;
+ },
+
+ dissolve: function(options){
+ this.get('reveal', options).dissolve();
+ return this;
+ },
+
+ nix: function(){
+ var params = Array.link(arguments, {destroy: Boolean.type, options: Object.type});
+ this.get('reveal', params.options).dissolve().chain(function(){
+ this[params.destroy ? 'destroy' : 'dispose']();
+ }.bind(this));
+ return this;
+ },
+
+ wink: function(){
+ var params = Array.link(arguments, {duration: Number.type, options: Object.type});
+ var reveal = this.get('reveal', params.options);
+ reveal.reveal().chain(function(){
+ (function(){
+ reveal.dissolve();
+ }).delay(params.duration || 2000);
+ });
+ }
+
+
+});/*
+---
+
+script: Form.Request.Append.js
+
+description: Handles the basic functionality of submitting a form and updating a dom element with the result. The result is appended to the DOM element instead of replacing its contents.
+
+license: MIT-style license
+
+authors:
+- Aaron Newton
+
+requires:
+- /Form.Request
+- /Fx.Reveal
+- /Elements.from
+
+provides: [Form.Request.Append]
+
+...
+*/
+
+Form.Request.Append = new Class({
+
+ Extends: Form.Request,
+
+ options: {
+ //onBeforeEffect: $empty,
+ useReveal: true,
+ revealOptions: {},
+ inject: 'bottom'
+ },
+
+ makeRequest: function(){
+ this.request = new Request.HTML($merge({
+ url: this.element.get('action'),
+ method: this.element.get('method') || 'post',
+ spinnerTarget: this.element
+ }, this.options.requestOptions, {
+ evalScripts: false
+ })
+ ).addEvents({
+ success: function(tree, elements, html, javascript){
+ var container;
+ var kids = Elements.from(html);
+ if (kids.length == 1) {
+ container = kids[0];
+ } else {
+ container = new Element('div', {
+ styles: {
+ display: 'none'
+ }
+ }).adopt(kids);
+ }
+ container.inject(this.update, this.options.inject);
+ if (this.options.requestOptions.evalScripts) $exec(javascript);
+ this.fireEvent('beforeEffect', container);
+ var finish = function(){
+ this.fireEvent('success', [container, this.update, tree, elements, html, javascript]);
+ }.bind(this);
+ if (this.options.useReveal) {
+ container.get('reveal', this.options.revealOptions).chain(finish);
+ container.reveal();
+ } else {
+ finish();
+ }
+ }.bind(this),
+ failure: function(xhr){
+ this.fireEvent('failure', xhr);
+ }.bind(this)
+ });
+ }
+
+});/*
+---
+
+script: Form.Validator.English.js
+
+description: Date messages for English.
+
+license: MIT-style license
+
+authors:
+- Aaron Newton
+
+requires:
+- /Lang
+- /Form.Validator
+
+provides: [Form.Validator.English]
+
+...
+*/
+
+MooTools.lang.set('en-US', 'Form.Validator', {
+
+ required:'This field is required.',
+ minLength:'Please enter at least {minLength} characters (you entered {length} characters).',
+ maxLength:'Please enter no more than {maxLength} characters (you entered {length} characters).',
+ integer:'Please enter an integer in this field. Numbers with decimals (e.g. 1.25) are not permitted.',
+ numeric:'Please enter only numeric values in this field (i.e. "1" or "1.1" or "-1" or "-1.1").',
+ digits:'Please use numbers and punctuation only in this field (for example, a phone number with dashes or dots is permitted).',
+ alpha:'Please use letters only (a-z) with in this field. No spaces or other characters are allowed.',
+ alphanum:'Please use only letters (a-z) or numbers (0-9) only in this field. No spaces or other characters are allowed.',
+ dateSuchAs:'Please enter a valid date such as {date}',
+ dateInFormatMDY:'Please enter a valid date such as MM/DD/YYYY (i.e. "12/31/1999")',
+ email:'Please enter a valid email address. For example "fred at domain.com".',
+ url:'Please enter a valid URL such as http://www.google.com.',
+ currencyDollar:'Please enter a valid $ amount. For example $100.00 .',
+ oneRequired:'Please enter something for at least one of these inputs.',
+ errorPrefix: 'Error: ',
+ warningPrefix: 'Warning: ',
+
+ //Form.Validator.Extras
+
+ noSpace: 'There can be no spaces in this input.',
+ reqChkByNode: 'No items are selected.',
+ requiredChk: 'This field is required.',
+ reqChkByName: 'Please select a {label}.',
+ match: 'This field needs to match the {matchName} field',
+ startDate: 'the start date',
+ endDate: 'the end date',
+ currendDate: 'the current date',
+ afterDate: 'The date should be the same or after {label}.',
+ beforeDate: 'The date should be the same or before {label}.',
+ startMonth: 'Please select a start month',
+ sameMonth: 'These two dates must be in the same month - you must change one or the other.',
+ creditcard: 'The credit card number entered is invalid. Please check the number and try again. {length} digits entered.'
+
+});/*
+---
+
+script: Form.Validator.js
+
+description: A css-class based form validation system.
+
+license: MIT-style license
+
+authors:
+- Aaron Newton
+
+requires:
+- core:1.2.4/Options
+- core:1.2.4/Events
+- core:1.2.4/Selectors
+- core:1.2.4/Element.Event
+- core:1.2.4/Element.Style
+- core:1.2.4/JSON
+- /Lang- /Class.Binds
+- /Date Element.Forms
+- /Form.Validator.English
+- /Element.Shortcuts
+
+provides: [Form.Validator, InputValidator, FormValidator.BaseValidators]
+
+...
+*/
+if (!window.Form) window.Form = {};
+
var InputValidator = new Class({
Implements: [Options],
@@ -5769,18 +7988,18 @@
},
test: function(field, props){
- if ($(field)) return this.options.test($(field), props||this.getProps(field));
+ if (document.id(field)) return this.options.test(document.id(field), props||this.getProps(field));
else return false;
},
getError: function(field, props){
var err = this.options.errorMsg;
- if ($type(err) == 'function') err = err($(field), props||this.getProps(field));
+ if ($type(err) == 'function') err = err(document.id(field), props||this.getProps(field));
return err;
},
getProps: function(field){
- if (!$(field)) return {};
+ if (!document.id(field)) return {};
return field.get('validatorProps');
}
@@ -5825,7 +8044,7 @@
};
-var FormValidator = new Class({
+Form.Validator = new Class({
Implements:[Options, Events],
@@ -5838,6 +8057,7 @@
onElementFail: $empty(field, validatorsFailed) */
fieldSelectors: 'input, select, textarea',
ignoreHidden: true,
+ ignoreDisabled: true,
useTitles: false,
evaluateOnSubmit: true,
evaluateFieldsOnBlur: true,
@@ -5845,21 +8065,21 @@
serial: true,
stopOnFailure: true,
warningPrefix: function(){
- return FormValidator.getMsg('warningPrefix') || 'Warning: ';
+ return Form.Validator.getMsg('warningPrefix') || 'Warning: ';
},
errorPrefix: function(){
- return FormValidator.getMsg('errorPrefix') || 'Error: ';
+ return Form.Validator.getMsg('errorPrefix') || 'Error: ';
}
},
initialize: function(form, options){
this.setOptions(options);
- this.element = $(form);
+ this.element = document.id(form);
this.element.store('validator', this);
this.warningPrefix = $lambda(this.options.warningPrefix)();
this.errorPrefix = $lambda(this.options.errorPrefix)();
if (this.options.evaluateOnSubmit) this.element.addEvent('submit', this.onSubmit);
- if (this.options.evaluateFieldsOnBlur) this.watchFields(this.getFields());
+ if (this.options.evaluateFieldsOnBlur || this.options.evaluateFieldsOnChange) this.watchFields(this.getFields());
},
toElement: function(){
@@ -5872,12 +8092,18 @@
watchFields: function(fields){
fields.each(function(el){
- el.addEvent('blur', this.validateField.pass([el, false], this));
+ if (this.options.evaluateFieldsOnBlur)
+ el.addEvent('blur', this.validationMonitor.pass([el, false], this));
if (this.options.evaluateFieldsOnChange)
- el.addEvent('change', this.validateField.pass([el, true], this));
+ el.addEvent('change', this.validationMonitor.pass([el, true], this));
}, this);
},
+ validationMonitor: function(){
+ $clear(this.timer);
+ this.timer = this.validateField.delay(50, this, arguments);
+ },
+
onSubmit: function(event){
if (!this.validate(event) && event) event.preventDefault();
else this.reset();
@@ -5899,7 +8125,7 @@
validateField: function(field, force){
if (this.paused) return true;
- field = $(field);
+ field = document.id(field);
var passed = !field.hasClass('validation-failed');
var failed, warned;
if (this.options.serial && !force){
@@ -5942,28 +8168,20 @@
},
test: function(className, field, warn){
+ field = document.id(field);
+ if((this.options.ignoreHidden && !field.isVisible()) || (this.options.ignoreDisabled && field.get('disabled'))) return true;
var validator = this.getValidator(className);
- field = $(field);
if (field.hasClass('ignoreValidation')) return true;
warn = $pick(warn, false);
if (field.hasClass('warnOnly')) warn = true;
var isValid = validator ? validator.test(field) : true;
- if (validator && this.isVisible(field)) this.fireEvent('elementValidate', [isValid, field, className, warn]);
+ if (validator && field.isVisible()) this.fireEvent('elementValidate', [isValid, field, className, warn]);
if (warn) return true;
return isValid;
},
- isVisible : function(field){
- if (!this.options.ignoreHidden) return true;
- while(field != document.body){
- if ($(field).getStyle('display') == 'none') return false;
- field = field.getParent();
- }
- return true;
- },
-
resetField: function(field){
- field = $(field);
+ field = document.id(field);
if (field){
field.className.split(' ').each(function(className){
if (className.test('^warn-')) className = className.replace(/^warn-/, '');
@@ -5986,7 +8204,7 @@
},
ignoreField: function(field, warn){
- field = $(field);
+ field = document.id(field);
if (field){
this.enforceField(field);
if (warn) field.addClass('warnOnly');
@@ -5996,24 +8214,24 @@
},
enforceField: function(field){
- field = $(field);
+ field = document.id(field);
if (field) field.removeClass('warnOnly').removeClass('ignoreValidation');
return this;
}
});
-FormValidator.getMsg = function(key){
- return MooTools.lang.get('FormValidator', key);
+Form.Validator.getMsg = function(key){
+ return MooTools.lang.get('Form.Validator', key);
};
-FormValidator.adders = {
+Form.Validator.adders = {
validators:{},
add : function(className, options){
this.validators[className] = new InputValidator(className, options);
- //if this is a class (this method is used by instances of FormValidator and the FormValidator namespace)
+ //if this is a class (this method is used by instances of Form.Validator and the Form.Validator namespace)
//extend these validators into it
//this allows validators to be global and/or per instance
if (!this.initialize){
@@ -6035,11 +8253,11 @@
};
-$extend(FormValidator, FormValidator.adders);
+$extend(Form.Validator, Form.Validator.adders);
-FormValidator.implement(FormValidator.adders);
+Form.Validator.implement(Form.Validator.adders);
-FormValidator.add('IsEmpty', {
+Form.Validator.add('IsEmpty', {
errorMsg: false,
test: function(element){
@@ -6051,21 +8269,21 @@
});
-FormValidator.addAllThese([
+Form.Validator.addAllThese([
['required', {
errorMsg: function(){
- return FormValidator.getMsg('required');
+ return Form.Validator.getMsg('required');
},
test: function(element){
- return !FormValidator.getValidator('IsEmpty').test(element);
+ return !Form.Validator.getValidator('IsEmpty').test(element);
}
}],
['minLength', {
errorMsg: function(element, props){
if ($type(props.minLength))
- return FormValidator.getMsg('minLength').substitute({minLength:props.minLength,length:element.get('value').length });
+ return Form.Validator.getMsg('minLength').substitute({minLength:props.minLength,length:element.get('value').length });
else return '';
},
test: function(element, props){
@@ -6078,7 +8296,7 @@
errorMsg: function(element, props){
//props is {maxLength:10}
if ($type(props.maxLength))
- return FormValidator.getMsg('maxLength').substitute({maxLength:props.maxLength,length:element.get('value').length });
+ return Form.Validator.getMsg('maxLength').substitute({maxLength:props.maxLength,length:element.get('value').length });
else return '';
},
test: function(element, props){
@@ -6088,38 +8306,38 @@
}],
['validate-integer', {
- errorMsg: FormValidator.getMsg.pass('integer'),
+ errorMsg: Form.Validator.getMsg.pass('integer'),
test: function(element){
- return FormValidator.getValidator('IsEmpty').test(element) || (/^-?[1-9]\d*$/).test(element.get('value'));
+ return Form.Validator.getValidator('IsEmpty').test(element) || (/^(-?[1-9]\d*|0)$/).test(element.get('value'));
}
}],
['validate-numeric', {
- errorMsg: FormValidator.getMsg.pass('numeric'),
+ errorMsg: Form.Validator.getMsg.pass('numeric'),
test: function(element){
- return FormValidator.getValidator('IsEmpty').test(element) ||
+ return Form.Validator.getValidator('IsEmpty').test(element) ||
(/^-?(?:0$0(?=\d*\.)|[1-9]|0)\d*(\.\d+)?$/).test(element.get('value'));
}
}],
['validate-digits', {
- errorMsg: FormValidator.getMsg.pass('digits'),
+ errorMsg: Form.Validator.getMsg.pass('digits'),
test: function(element){
- return FormValidator.getValidator('IsEmpty').test(element) || (/^[\d() .:\-\+#]+$/.test(element.get('value')));
+ return Form.Validator.getValidator('IsEmpty').test(element) || (/^[\d() .:\-\+#]+$/.test(element.get('value')));
}
}],
['validate-alpha', {
- errorMsg: FormValidator.getMsg.pass('alpha'),
+ errorMsg: Form.Validator.getMsg.pass('alpha'),
test: function(element){
- return FormValidator.getValidator('IsEmpty').test(element) || (/^[a-zA-Z]+$/).test(element.get('value'));
+ return Form.Validator.getValidator('IsEmpty').test(element) || (/^[a-zA-Z]+$/).test(element.get('value'));
}
}],
['validate-alphanum', {
- errorMsg: FormValidator.getMsg.pass('alphanum'),
+ errorMsg: Form.Validator.getMsg.pass('alphanum'),
test: function(element){
- return FormValidator.getValidator('IsEmpty').test(element) || !(/\W/).test(element.get('value'));
+ return Form.Validator.getValidator('IsEmpty').test(element) || !(/\W/).test(element.get('value'));
}
}],
@@ -6127,13 +8345,13 @@
errorMsg: function(element, props){
if (Date.parse){
var format = props.dateFormat || '%x';
- return FormValidator.getMsg('dateSuchAs').substitute({date: new Date().format(format)});
+ return Form.Validator.getMsg('dateSuchAs').substitute({date: new Date().format(format)});
} else {
- return FormValidator.getMsg('dateInFormatMDY');
+ return Form.Validator.getMsg('dateInFormatMDY');
}
},
test: function(element, props){
- if (FormValidator.getValidator('IsEmpty').test(element)) return true;
+ if (Form.Validator.getValidator('IsEmpty').test(element)) return true;
var d;
if (Date.parse){
var format = props.dateFormat || '%x';
@@ -6153,34 +8371,34 @@
}],
['validate-email', {
- errorMsg: FormValidator.getMsg.pass('email'),
+ errorMsg: Form.Validator.getMsg.pass('email'),
test: function(element){
- return FormValidator.getValidator('IsEmpty').test(element) || (/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i).test(element.get('value'));
+ return Form.Validator.getValidator('IsEmpty').test(element) || (/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i).test(element.get('value'));
}
}],
['validate-url', {
- errorMsg: FormValidator.getMsg.pass('url'),
+ errorMsg: Form.Validator.getMsg.pass('url'),
test: function(element){
- return FormValidator.getValidator('IsEmpty').test(element) || (/^(https?|ftp|rmtp|mms):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(:(\d+))?\/?/i).test(element.get('value'));
+ return Form.Validator.getValidator('IsEmpty').test(element) || (/^(https?|ftp|rmtp|mms):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(:(\d+))?\/?/i).test(element.get('value'));
}
}],
['validate-currency-dollar', {
- errorMsg: FormValidator.getMsg.pass('currencyDollar'),
+ errorMsg: Form.Validator.getMsg.pass('currencyDollar'),
test: function(element){
// [$]1[##][,###]+[.##]
// [$]1###+[.##]
// [$]0.##
// [$].##
- return FormValidator.getValidator('IsEmpty').test(element) || (/^\$?\-?([1-9]{1}[0-9]{0,2}(\,[0-9]{3})*(\.[0-9]{0,2})?|[1-9]{1}\d*(\.[0-9]{0,2})?|0(\.[0-9]{0,2})?|(\.[0-9]{1,2})?)$/).test(element.get('value'));
+ return Form.Validator.getValidator('IsEmpty').test(element) || (/^\$?\-?([1-9]{1}[0-9]{0,2}(\,[0-9]{3})*(\.[0-9]{0,2})?|[1-9]{1}\d*(\.[0-9]{0,2})?|0(\.[0-9]{0,2})?|(\.[0-9]{1,2})?)$/).test(element.get('value'));
}
}],
['validate-one-required', {
- errorMsg: FormValidator.getMsg.pass('oneRequired'),
+ errorMsg: Form.Validator.getMsg.pass('oneRequired'),
test: function(element, props){
- var p = $(props['validate-one-required']) || element.parentNode;
+ var p = document.id(props['validate-one-required']) || element.getParent();
return p.getElements('input').some(function(el){
if (['checkbox', 'radio'].contains(el.get('type'))) return el.get('checked');
return el.get('value');
@@ -6201,7 +8419,7 @@
get: function(options){
if (options || !this.retrieve('validator')){
if (options || !this.retrieve('validator:options')) this.set('validator', options);
- this.store('validator', new FormValidator(this, this.retrieve('validator:options')));
+ this.store('validator', new Form.Validator(this, this.retrieve('validator:options')));
}
return this.retrieve('validator');
}
@@ -6215,24 +8433,36 @@
return this.get('validator', options).validate();
}
-});/*
-Script: FormValidator.Inline.js
- Extends FormValidator to add inline messages.
+});
+//legacy
+var FormValidator = Form.Validator;/*
+---
- License:
- MIT-style license.
+script: Form.Validator.Inline.js
- Authors:
- Aaron Newton
+description: Extends Form.Validator to add inline messages.
+
+license: MIT-style license
+
+authors:
+- Aaron Newton
+
+requires:
+- /Form.Validator
+
+provides: [Form.Validator.Inline]
+
+...
*/
-FormValidator.Inline = new Class({
+Form.Validator.Inline = new Class({
- Extends: FormValidator,
+ Extends: Form.Validator,
options: {
scrollToErrorsOnSubmit: true,
scrollFxOptions: {
+ transition: 'quad:out',
offset: {
y: -20
}
@@ -6256,11 +8486,11 @@
makeAdvice: function(className, field, error, warn){
var errorMsg = (warn)?this.warningPrefix:this.errorPrefix;
- errorMsg += (this.options.useTitles) ? field.title || error:error;
+ errorMsg += (this.options.useTitles) ? field.title || error:error;
var cssClass = (warn) ? 'warning-advice' : 'validation-advice';
var advice = this.getAdvice(className, field);
if(advice) {
- advice = advice.clone(true).set('html', errorMsg).replaces(advice);
+ advice = advice.clone(true, true).set('html', errorMsg).replaces(advice);
} else {
advice = new Element('div', {
html: errorMsg,
@@ -6303,7 +8533,7 @@
},
resetField: function(field){
- field = $(field);
+ field = document.id(field);
if (!field) return this;
this.parent(field);
field.className.split(' ').each(function(className){
@@ -6338,34 +8568,25 @@
//Check for error position prop
var props = field.get('validatorProps');
//Build advice
- if (!props.msgPos || !$(props.msgPos)){
+ if (!props.msgPos || !document.id(props.msgPos)){
if(field.type.toLowerCase() == 'radio') field.getParent().adopt(advice);
- else advice.inject($(field), 'after');
+ else advice.inject(document.id(field), 'after');
} else {
- $(props.msgPos).grab(advice);
+ document.id(props.msgPos).grab(advice);
}
},
- validate: function(field, force){
+ validateField: function(field, force){
var result = this.parent(field, force);
if (this.options.scrollToErrorsOnSubmit && !result){
- var failed = $(this).getElement('.validation-failed');
- var par = $(this).getParent();
- var isScrolled = function(p){
- return p.getScrollSize().y != p.getSize().y;
- };
- var scrolls;
- while (par != document.body && !isScrolled(par)){
+ var failed = document.id(this).getElement('.validation-failed');
+ var par = document.id(this).getParent();
+ while (par != document.body && par.getScrollSize().y == par.getSize().y){
par = par.getParent();
}
var fx = par.retrieve('fvScroller');
if (!fx && window.Fx && Fx.Scroll){
- fx = new Fx.Scroll(par, {
- transition: 'quad:out',
- offset: {
- y: -20
- }
- });
+ fx = new Fx.Scroll(par, this.options.scrollFxOptions);
par.store('fvScroller', fx);
}
if (failed){
@@ -6373,27 +8594,37 @@
else par.scrollTo(par.getScroll().x, failed.getPosition(par).y - 20);
}
}
+ return result;
}
});
/*
-Script: FormValidator.Extras.js
- Additional validators for the FormValidator class.
+---
- License:
- MIT-style license.
+script: Form.Validator.Extras.js
- Authors:
- Aaron Newton
+description: Additional validators for the Form.Validator class.
+
+license: MIT-style license
+
+authors:
+- Aaron Newton
+
+requires:
+- /Form.Validator
+
+provides: [Form.Validator.Extras]
+
+...
*/
-FormValidator.addAllThese([
+Form.Validator.addAllThese([
['validate-enforce-oncheck', {
test: function(element, props){
if (element.checked){
var fv = element.getParent('form').retrieve('validator');
if (!fv) return true;
- (props.toEnforce || $(props.enforceChildrenOf).getElements('input, select, textarea')).map(function(item){
+ (props.toEnforce || document.id(props.enforceChildrenOf).getElements('input, select, textarea')).map(function(item){
fv.enforceField(item);
});
}
@@ -6406,7 +8637,7 @@
if (element.checked){
var fv = element.getParent('form').retrieve('validator');
if (!fv) return true;
- (props.toIgnore || $(props.ignoreChildrenOf).getElements('input, select, textarea')).each(function(item){
+ (props.toIgnore || document.id(props.ignoreChildrenOf).getElements('input, select, textarea')).each(function(item){
fv.ignoreField(item);
fv.resetField(item);
});
@@ -6417,7 +8648,7 @@
['validate-nospace', {
errorMsg: function(){
- return FormValidator.getMsg('noSpace');
+ return Form.Validator.getMsg('noSpace');
},
test: function(element, props){
return !element.get('value').test(/\s/);
@@ -6428,7 +8659,7 @@
test: function(element, props){
var fv = element.getParent('form').retrieve('validator');
if (!fv) return true;
- var eleArr = props.toToggle || $(props.toToggleChildrenOf).getElements('input, select, textarea');
+ var eleArr = props.toToggle || document.id(props.toToggleChildrenOf).getElements('input, select, textarea');
if (!element.checked){
eleArr.each(function(item){
fv.ignoreField(item);
@@ -6445,10 +8676,10 @@
['validate-reqchk-bynode', {
errorMsg: function(){
- return FormValidator.getMsg('reqChkByNode');
+ return Form.Validator.getMsg('reqChkByNode');
},
test: function(element, props){
- return ($(props.nodeId).getElements(props.selector || 'input[type=checkbox], input[type=radio]')).some(function(item){
+ return (document.id(props.nodeId).getElements(props.selector || 'input[type=checkbox], input[type=radio]')).some(function(item){
return item.checked;
});
}
@@ -6456,7 +8687,7 @@
['validate-required-check', {
errorMsg: function(element, props){
- return props.useTitle ? element.get('title') : FormValidator.getMsg('requiredChk');
+ return props.useTitle ? element.get('title') : Form.Validator.getMsg('requiredChk');
},
test: function(element, props){
return !!element.checked;
@@ -6465,7 +8696,7 @@
['validate-reqchk-byname', {
errorMsg: function(element, props){
- return FormValidator.getMsg('reqChkByName').substitute({label: props.label || element.get('type')});
+ return Form.Validator.getMsg('reqChkByName').substitute({label: props.label || element.get('type')});
},
test: function(element, props){
var grpName = props.groupName || element.get('name');
@@ -6480,23 +8711,23 @@
['validate-match', {
errorMsg: function(element, props){
- return FormValidator.getMsg('match').substitute({matchName: props.matchName || $(props.matchInput).get('name')});
+ return Form.Validator.getMsg('match').substitute({matchName: props.matchName || document.id(props.matchInput).get('name')});
},
test: function(element, props){
var eleVal = element.get('value');
- var matchVal = $(props.matchInput) && $(props.matchInput).get('value');
+ var matchVal = document.id(props.matchInput) && document.id(props.matchInput).get('value');
return eleVal && matchVal ? eleVal == matchVal : true;
}
}],
['validate-after-date', {
errorMsg: function(element, props){
- return FormValidator.getMsg('afterDate').substitute({
- label: props.afterLabel || (props.afterElement ? FormValidator.getMsg('startDate') : FormValidator.getMsg('currentDate'))
+ return Form.Validator.getMsg('afterDate').substitute({
+ label: props.afterLabel || (props.afterElement ? Form.Validator.getMsg('startDate') : Form.Validator.getMsg('currentDate'))
});
},
test: function(element, props){
- var start = $(props.afterElement) ? Date.parse($(props.afterElement).get('value')) : new Date();
+ var start = document.id(props.afterElement) ? Date.parse(document.id(props.afterElement).get('value')) : new Date();
var end = Date.parse(element.get('value'));
return end && start ? end >= start : true;
}
@@ -6504,20 +8735,20 @@
['validate-before-date', {
errorMsg: function(element, props){
- return FormValidator.getMsg('beforeDate').substitute({
- label: props.beforeLabel || (props.beforeElement ? FormValidator.getMsg('endDate') : FormValidator.getMsg('currentDate'))
+ return Form.Validator.getMsg('beforeDate').substitute({
+ label: props.beforeLabel || (props.beforeElement ? Form.Validator.getMsg('endDate') : Form.Validator.getMsg('currentDate'))
});
},
test: function(element, props){
var start = Date.parse(element.get('value'));
- var end = $(props.beforeElement) ? Date.parse($(props.beforeElement).get('value')) : new Date();
+ var end = document.id(props.beforeElement) ? Date.parse(document.id(props.beforeElement).get('value')) : new Date();
return end && start ? end >= start : true;
}
}],
['validate-custom-required', {
errorMsg: function(){
- return FormValidator.getMsg('required');
+ return Form.Validator.getMsg('required');
},
test: function(element, props){
return element.get('value') != props.emptyValue;
@@ -6526,39 +8757,106 @@
['validate-same-month', {
errorMsg: function(element, props){
- var startMo = $(props.sameMonthAs) && $(props.sameMonthAs).get('value');
+ var startMo = document.id(props.sameMonthAs) && document.id(props.sameMonthAs).get('value');
var eleVal = element.get('value');
- if (eleVal != '') return FormValidator.getMsg(startMo ? 'sameMonth' : 'startMonth');
+ if (eleVal != '') return Form.Validator.getMsg(startMo ? 'sameMonth' : 'startMonth');
},
test: function(element, props){
var d1 = Date.parse(element.get('value'));
- var d2 = Date.parse($(props.sameMonthAs) && $(props.sameMonthAs).get('value'));
+ var d2 = Date.parse(document.id(props.sameMonthAs) && document.id(props.sameMonthAs).get('value'));
return d1 && d2 ? d1.format('%B') == d2.format('%B') : true;
}
+ }],
+
+
+ ['validate-cc-num', {
+ errorMsg: function(element){
+ var ccNum = element.get('value').ccNum.replace(/[^0-9]/g, '');
+ return Form.Validator.getMsg('creditcard').substitute({length: ccNum.length});
+ },
+ test: function(element){
+ // required is a different test
+ if (Form.Validator.getValidator('IsEmpty').test(element)) { return true; }
+
+ // Clean number value
+ var ccNum = element.get('value');
+ ccNum = ccNum.replace(/[^0-9]/g, '');
+
+ var valid_type = false;
+
+ if (ccNum.test(/^4[0-9]{12}([0-9]{3})?$/)) valid_type = 'Visa';
+ else if (ccNum.test(/^5[1-5]([0-9]{14})$/)) valid_type = 'Master Card';
+ else if (ccNum.test(/^3[47][0-9]{13}$/)) valid_type = 'American Express';
+ else if (ccNum.test(/^6011[0-9]{12}$/)) valid_type = 'Discover';
+
+ if (valid_type) {
+ var sum = 0;
+ var cur = 0;
+
+ for(var i=ccNum.length-1; i>=0; --i) {
+ cur = ccNum.charAt(i).toInt();
+ if (cur == 0) { continue; }
+
+ if ((ccNum.length-i) % 2 == 0) { cur += cur; }
+ if (cur > 9) { cur = cur.toString().charAt(0).toInt() + cur.toString().charAt(1).toInt(); }
+
+ sum += cur;
+ }
+ if ((sum % 10) == 0) { return true; }
+ }
+
+ var chunks = '';
+ while (ccNum != '') {
+ chunks += ' ' + ccNum.substr(0,4);
+ ccNum = ccNum.substr(4);
+ }
+
+ element.getParent('form').retrieve('validator').ignoreField(element);
+ element.set('value', chunks.clean());
+ element.getParent('form').retrieve('validator').enforceField(element);
+ return false;
+ }
}]
+
]);/*
-Script: OverText.js
- Shows text over an input that disappears when the user clicks into it. The text remains hidden if the user adds a value.
+---
- License:
- MIT-style license.
+script: OverText.js
- Authors:
- Aaron Newton
+description: Shows text over an input that disappears when the user clicks into it. The text remains hidden if the user adds a value.
+
+license: MIT-style license
+
+authors:
+- Aaron Newton
+
+requires:
+- core:1.2.4/Options
+- core:1.2.4/Events
+- core:1.2.4/Element.Event
+- /Class.Binds
+- /Class.Occlude
+- /Element.Position
+- /Element.Shortcuts
+
+provides: [OverText]
+
+...
*/
var OverText = new Class({
Implements: [Options, Events, Class.Occlude],
- Binds: ['reposition', 'assert', 'focus'],
+ Binds: ['reposition', 'assert', 'focus', 'hide'],
options: {/*
textOverride: null,
onFocus: $empty()
onTextHide: $empty(textEl, inputEl),
onTextShow: $empty(textEl, inputEl), */
+ element: 'label',
positionOptions: {
position: 'upperLeft',
edge: 'upperLeft',
@@ -6568,13 +8866,14 @@
}
},
poll: false,
- pollInterval: 250
+ pollInterval: 250,
+ wrap: false
},
property: 'OverText',
initialize: function(element, options){
- this.element = $(element);
+ this.element = document.id(element);
if (this.occlude()) return this.occluded;
this.setOptions(options);
this.attach(this.element);
@@ -6590,27 +8889,50 @@
attach: function(){
var val = this.options.textOverride || this.element.get('alt') || this.element.get('title');
if (!val) return;
- this.text = new Element('div', {
- 'class': 'overTxtDiv',
+ this.text = new Element(this.options.element, {
+ 'class': 'overTxtLabel',
styles: {
lineHeight: 'normal',
- position: 'absolute'
+ position: 'absolute',
+ cursor: 'text'
},
html: val,
events: {
- click: this.hide.pass(true, this)
+ click: this.hide.pass(this.options.element == 'label', this)
}
}).inject(this.element, 'after');
+ if (this.options.element == 'label') {
+ if (!this.element.get('id')) this.element.set('id', 'input_' + new Date().getTime());
+ this.text.set('for', this.element.get('id'));
+ }
+
+ if (this.options.wrap) {
+ this.textHolder = new Element('div', {
+ styles: {
+ lineHeight: 'normal',
+ position: 'relative'
+ },
+ 'class':'overTxtWrapper'
+ }).adopt(this.text).inject(this.element, 'before');
+ }
+
this.element.addEvents({
focus: this.focus,
blur: this.assert,
change: this.assert
}).store('OverTextDiv', this.text);
window.addEvent('resize', this.reposition.bind(this));
- this.assert();
+ this.assert(true);
this.reposition();
},
+ wrap: function(){
+ if (this.options.element == 'label') {
+ if (!this.element.get('id')) this.element.set('id', 'input_' + new Date().getTime());
+ this.text.set('for', this.element.get('id'));
+ }
+ },
+
startPolling: function(){
this.pollingPaused = false;
return this.poll();
@@ -6622,7 +8944,7 @@
//resumeon blur
if (this.poller && !stop) return this;
var test = function(){
- if (!this.pollingPaused) this.assert();
+ if (!this.pollingPaused) this.assert(true);
}.bind(this);
if (stop) $clear(this.poller);
else this.poller = test.periodical(this.options.pollInterval, this);
@@ -6635,24 +8957,25 @@
},
focus: function(){
- if (!this.text.isDisplayed() || this.element.get('disabled')) return;
+ if (this.text && (!this.text.isDisplayed() || this.element.get('disabled'))) return;
this.hide();
},
- hide: function(){
- if (this.text.isDisplayed() && !this.element.get('disabled')){
+ hide: function(suppressFocus, force){
+ if (this.text && (this.text.isDisplayed() && (!this.element.get('disabled') || force))){
this.text.hide();
this.fireEvent('textHide', [this.text, this.element]);
this.pollingPaused = true;
try {
- this.element.fireEvent('focus').focus();
+ if (!suppressFocus) this.element.fireEvent('focus');
+ this.element.focus();
} catch(e){} //IE barfs if you call focus on hidden elements
}
return this;
},
show: function(){
- if (!this.text.isDisplayed()){
+ if (this.text && !this.text.isDisplayed()){
this.text.show();
this.reposition();
this.fireEvent('textShow', [this.text, this.element]);
@@ -6661,8 +8984,8 @@
return this;
},
- assert: function(){
- this[this.test() ? 'show' : 'hide']();
+ assert: function(suppressFocus){
+ this[this.test() ? 'show' : 'hide'](suppressFocus);
},
test: function(){
@@ -6671,11 +8994,9 @@
},
reposition: function(){
- try {
- this.assert();
- if (!this.element.getParent() || !this.element.offsetHeight) return this.hide();
- if (this.test()) this.text.position($merge(this.options.positionOptions, {relativeTo: this.element}));
- } catch(e){ }
+ this.assert(true);
+ if (!this.element.isVisible()) return this.stopPolling().hide();
+ if (this.text && this.test()) this.text.position($merge(this.options.positionOptions, {relativeTo: this.element}));
return this;
}
@@ -6683,28 +9004,62 @@
OverText.instances = [];
-OverText.update = function(){
+$extend(OverText, {
- return OverText.instances.map(function(ot){
- if (ot.element && ot.text) return ot.reposition();
- return null; //the input or the text was destroyed
- });
+ each: function(fn) {
+ return OverText.instances.map(function(ot, i){
+ if (ot.element && ot.text) return fn.apply(OverText, [ot, i]);
+ return null; //the input or the text was destroyed
+ });
+ },
+
+ update: function(){
-};
+ return OverText.each(function(ot){
+ return ot.reposition();
+ });
+ },
+
+ hideAll: function(){
+
+ return OverText.each(function(ot){
+ return ot.hide(true, true);
+ });
+
+ },
+
+ showAll: function(){
+ return OverText.each(function(ot) {
+ return ot.show();
+ });
+ }
+
+});
+
if (window.Fx && Fx.Reveal) {
Fx.Reveal.implement({
- hideInputs: Browser.Engine.trident ? 'select, input, textarea, object, embed, .overTxtDiv' : false
+ hideInputs: Browser.Engine.trident ? 'select, input, textarea, object, embed, .overTxtLabel' : false
});
}/*
-Script: Fx.Elements.js
- Effect to change any number of CSS properties of any number of Elements.
+---
- License:
- MIT-style license.
+script: Fx.Elements.js
- Authors:
- Valerio Proietti
+description: Effect to change any number of CSS properties of any number of Elements.
+
+license: MIT-style license
+
+authors:
+- Valerio Proietti
+
+requires:
+- core:1.2.4/Fx.CSS
+- /MooTools.More
+
+provides: [Fx.Elements]
+
+...
*/
Fx.Elements = new Class({
@@ -6748,14 +9103,24 @@
}
});/*
-Script: Fx.Accordion.js
- An Fx.Elements extension which allows you to easily create accordion type controls.
+---
- License:
- MIT-style license.
+script: Fx.Accordion.js
- Authors:
- Valerio Proietti
+description: An Fx.Elements extension which allows you to easily create accordion type controls.
+
+license: MIT-style license
+
+authors:
+- Valerio Proietti
+
+requires:
+- core:1.2.4/Element.Event
+- /Fx.Elements
+
+provides: [Fx.Accordion]
+
+...
*/
var Accordion = Fx.Accordion = new Class({
@@ -6764,26 +9129,28 @@
options: {/*
onActive: $empty(toggler, section),
- onBackground: $empty(toggler, section),*/
+ onBackground: $empty(toggler, section),
+ fixedHeight: false,
+ fixedWidth: false,
+ */
display: 0,
show: false,
height: true,
width: false,
opacity: true,
- fixedHeight: false,
- fixedWidth: false,
- wait: false,
alwaysHide: false,
trigger: 'click',
- initialDisplayFx: true
+ initialDisplayFx: true,
+ returnHeightToAuto: true
},
initialize: function(){
var params = Array.link(arguments, {'container': Element.type, 'options': Object.type, 'togglers': $defined, 'elements': $defined});
this.parent(params.elements, params.options);
this.togglers = $$(params.togglers);
- this.container = $(params.container);
+ this.container = document.id(params.container);
this.previous = -1;
+ this.internalChain = new Chain();
if (this.options.alwaysHide) this.options.wait = true;
if ($chk(this.options.show)){
this.options.display = false;
@@ -6806,16 +9173,19 @@
}
}, this);
if ($chk(this.options.display)) this.display(this.options.display, this.options.initialDisplayFx);
+ this.addEvent('complete', this.internalChain.callChain.bind(this.internalChain));
},
addSection: function(toggler, element){
- toggler = $(toggler);
- element = $(element);
+ toggler = document.id(toggler);
+ element = document.id(element);
var test = this.togglers.contains(toggler);
this.togglers.include(toggler);
this.elements.include(element);
var idx = this.togglers.indexOf(toggler);
- toggler.addEvent(this.options.trigger, this.display.bind(this, idx));
+ var displayer = this.display.bind(this, idx);
+ toggler.store('accordion:display', displayer);
+ toggler.addEvent(this.options.trigger, displayer);
if (this.options.height) element.setStyles({'padding-top': 0, 'border-top': 'none', 'padding-bottom': 0, 'border-bottom': 'none'});
if (this.options.width) element.setStyles({'padding-left': 0, 'border-left': 'none', 'padding-right': 0, 'border-right': 'none'});
element.fullOpacity = 1;
@@ -6828,31 +9198,63 @@
return this;
},
+ detach: function(){
+ this.togglers.each(function(toggler) {
+ toggler.removeEvent(this.options.trigger, toggler.retrieve('accordion:display'));
+ }, this);
+ },
+
display: function(index, useFx){
+ if (!this.check(index, useFx)) return this;
useFx = $pick(useFx, true);
+ if (this.options.returnHeightToAuto) {
+ var prev = this.elements[this.previous];
+ if (prev) {
+ for (var fx in this.effects) {
+ prev.setStyle(fx, prev[this.effects[fx]]);
+ }
+ }
+ }
index = ($type(index) == 'element') ? this.elements.indexOf(index) : index;
if ((this.timer && this.options.wait) || (index === this.previous && !this.options.alwaysHide)) return this;
this.previous = index;
var obj = {};
this.elements.each(function(el, i){
obj[i] = {};
- var hide = (i != index) || (this.options.alwaysHide && (el.offsetHeight > 0));
+ var hide = (i != index) ||
+ (this.options.alwaysHide && ((el.offsetHeight > 0 && this.options.height) ||
+ el.offsetWidth > 0 && this.options.width));
this.fireEvent(hide ? 'background' : 'active', [this.togglers[i], el]);
for (var fx in this.effects) obj[i][fx] = hide ? 0 : el[this.effects[fx]];
}, this);
+ this.internalChain.chain(function(){
+ if (this.options.returnHeightToAuto) {
+ var el = this.elements[index];
+ el.setStyle('height', 'auto');
+ };
+ }.bind(this));
return useFx ? this.start(obj) : this.set(obj);
}
});/*
-Script: Fx.Move.js
- Defines Fx.Move, a class that works with Element.Position.js to transition an element from one location to another.
+---
- License:
- MIT-style license.
+script: Fx.Move.js
- Authors:
- Aaron Newton
+description: Defines Fx.Move, a class that works with Element.Position.js to transition an element from one location to another.
+license: MIT-style license
+
+authors:
+- Aaron Newton
+
+requires:
+- core:1.2.4/Fx.Morph
+- /Element.Position
+
+provides: [Fx.Move]
+
+...
*/
Fx.Move = new Class({
@@ -6899,246 +9301,26 @@
});
/*
-Script: Fx.Reveal.js
- Defines Fx.Reveal, a class that shows and hides elements with a transition.
+---
- License:
- MIT-style license.
+script: Fx.Scroll.js
- Authors:
- Aaron Newton
+description: Effect to smoothly scroll any element, including the window.
-*/
+license: MIT-style license
-Fx.Reveal = new Class({
+authors:
+- Valerio Proietti
- Extends: Fx.Morph,
+requires:
+- core:1.2.4/Fx
+- core:1.2.4/Element.Event
+- core:1.2.4/Element.Dimensions
+- /MooTools.More
- options: {/*
- onShow: $empty(thisElemeng),
- onHide: $empty(thisElemeng),
- onComplete: $empty(thisElemeng),
- heightOverride: null,
- widthOverride: null, */
- styles: ['padding', 'border', 'margin'],
- transitionOpacity: !Browser.Engine.trident4,
- mode: 'vertical',
- display: 'block',
- hideInputs: Browser.Engine.trident ? 'select, input, textarea, object, embed' : false
- },
+provides: [Fx.Scroll]
- dissolve: function(){
- try {
- if (!this.hiding && !this.showing){
- if (this.element.getStyle('display') != 'none'){
- this.hiding = true;
- this.showing = false;
- this.hidden = true;
- var startStyles = this.element.getComputedSize({
- styles: this.options.styles,
- mode: this.options.mode
- });
- var setToAuto = (this.element.style.height === ''||this.element.style.height == 'auto');
- this.element.setStyle('display', 'block');
- if (this.options.transitionOpacity) startStyles.opacity = 1;
- var zero = {};
- $each(startStyles, function(style, name){
- zero[name] = [style, 0];
- }, this);
- var overflowBefore = this.element.getStyle('overflow');
- this.element.setStyle('overflow', 'hidden');
- var hideThese = this.options.hideInputs ? this.element.getElements(this.options.hideInputs) : null;
- this.$chain.unshift(function(){
- if (this.hidden){
- this.hiding = false;
- $each(startStyles, function(style, name){
- startStyles[name] = style;
- }, this);
- this.element.setStyles($merge({display: 'none', overflow: overflowBefore}, startStyles));
- if (setToAuto){
- if (['vertical', 'both'].contains(this.options.mode)) this.element.style.height = '';
- if (['width', 'both'].contains(this.options.mode)) this.element.style.width = '';
- }
- if (hideThese) hideThese.setStyle('visibility', 'visible');
- }
- this.fireEvent('hide', this.element);
- this.callChain();
- }.bind(this));
- if (hideThese) hideThese.setStyle('visibility', 'hidden');
- this.start(zero);
- } else {
- this.callChain.delay(10, this);
- this.fireEvent('complete', this.element);
- this.fireEvent('hide', this.element);
- }
- } else if (this.options.link == 'chain'){
- this.chain(this.dissolve.bind(this));
- } else if (this.options.link == 'cancel' && !this.hiding){
- this.cancel();
- this.dissolve();
- }
- } catch(e){
- this.hiding = false;
- this.element.setStyle('display', 'none');
- this.callChain.delay(10, this);
- this.fireEvent('complete', this.element);
- this.fireEvent('hide', this.element);
- }
- return this;
- },
-
- reveal: function(){
- try {
- if (!this.showing && !this.hiding){
- if (this.element.getStyle('display') == 'none' ||
- this.element.getStyle('visiblity') == 'hidden' ||
- this.element.getStyle('opacity') == 0){
- this.showing = true;
- this.hiding = false;
- this.hidden = false;
- var setToAuto, startStyles;
- //toggle display, but hide it
- this.element.measure(function(){
- setToAuto = (this.element.style.height === '' || this.element.style.height == 'auto');
- //create the styles for the opened/visible state
- startStyles = this.element.getComputedSize({
- styles: this.options.styles,
- mode: this.options.mode
- });
- }.bind(this));
- $each(startStyles, function(style, name){
- startStyles[name] = style;
- });
- //if we're overridding height/width
- if ($chk(this.options.heightOverride)) startStyles.height = this.options.heightOverride.toInt();
- if ($chk(this.options.widthOverride)) startStyles.width = this.options.widthOverride.toInt();
- if (this.options.transitionOpacity) {
- this.element.setStyle('opacity', 0);
- startStyles.opacity = 1;
- }
- //create the zero state for the beginning of the transition
- var zero = {
- height: 0,
- display: this.options.display
- };
- $each(startStyles, function(style, name){ zero[name] = 0; });
- var overflowBefore = this.element.getStyle('overflow');
- //set to zero
- this.element.setStyles($merge(zero, {overflow: 'hidden'}));
- //hide inputs
- var hideThese = this.options.hideInputs ? this.element.getElements(this.options.hideInputs) : null;
- if (hideThese) hideThese.setStyle('visibility', 'hidden');
- //start the effect
- this.start(startStyles);
- this.$chain.unshift(function(){
- this.element.setStyle('overflow', overflowBefore);
- if (!this.options.heightOverride && setToAuto){
- if (['vertical', 'both'].contains(this.options.mode)) this.element.style.height = '';
- if (['width', 'both'].contains(this.options.mode)) this.element.style.width = '';
- }
- if (!this.hidden) this.showing = false;
- if (hideThese) hideThese.setStyle('visibility', 'visible');
- this.callChain();
- this.fireEvent('show', this.element);
- }.bind(this));
- } else {
- this.callChain();
- this.fireEvent('complete', this.element);
- this.fireEvent('show', this.element);
- }
- } else if (this.options.link == 'chain'){
- this.chain(this.reveal.bind(this));
- } else if (this.options.link == 'cancel' && !this.showing){
- this.cancel();
- this.reveal();
- }
- } catch(e){
- this.element.setStyles({
- display: this.options.display,
- visiblity: 'visible',
- opacity: 1
- });
- this.showing = false;
- this.callChain.delay(10, this);
- this.fireEvent('complete', this.element);
- this.fireEvent('show', this.element);
- }
- return this;
- },
-
- toggle: function(){
- if (this.element.getStyle('display') == 'none' ||
- this.element.getStyle('visiblity') == 'hidden' ||
- this.element.getStyle('opacity') == 0){
- this.reveal();
- } else {
- this.dissolve();
- }
- return this;
- }
-
-});
-
-Element.Properties.reveal = {
-
- set: function(options){
- var reveal = this.retrieve('reveal');
- if (reveal) reveal.cancel();
- return this.eliminate('reveal').store('reveal:options', $extend({link: 'cancel'}, options));
- },
-
- get: function(options){
- if (options || !this.retrieve('reveal')){
- if (options || !this.retrieve('reveal:options')) this.set('reveal', options);
- this.store('reveal', new Fx.Reveal(this, this.retrieve('reveal:options')));
- }
- return this.retrieve('reveal');
- }
-
-};
-
-Element.Properties.dissolve = Element.Properties.reveal;
-
-Element.implement({
-
- reveal: function(options){
- this.get('reveal', options).reveal();
- return this;
- },
-
- dissolve: function(options){
- this.get('reveal', options).dissolve();
- return this;
- },
-
- nix: function(){
- var params = Array.link(arguments, {destroy: Boolean.type, options: Object.type});
- this.get('reveal', params.options).dissolve().chain(function(){
- this[params.destroy ? 'destroy' : 'dispose']();
- }.bind(this));
- return this;
- },
-
- wink: function(){
- var params = Array.link(arguments, {duration: Number.type, options: Object.type});
- var reveal = this.get('reveal', params.options);
- reveal.reveal().chain(function(){
- (function(){
- reveal.dissolve();
- }).delay(params.duration || 2000);
- });
- }
-
-
-});/*
-Script: Fx.Scroll.js
- Effect to smoothly scroll any element, including the window.
-
- License:
- MIT-style license.
-
- Authors:
- Valerio Proietti
+...
*/
Fx.Scroll = new Class({
@@ -7151,11 +9333,11 @@
},
initialize: function(element, options){
- this.element = this.subject = $(element);
+ this.element = this.subject = document.id(element);
this.parent(options);
var cancel = this.cancel.bind(this, false);
- if ($type(this.element) != 'element') this.element = $(this.element.getDocument().body);
+ if ($type(this.element) != 'element') this.element = document.id(this.element.getDocument().body);
var stopper = this.element;
@@ -7171,6 +9353,7 @@
set: function(){
var now = Array.flatten(arguments);
+ if (Browser.Engine.gecko) now = [Math.round(now[0]), Math.round(now[1])];
this.element.scrollTo(now[0], now[1]);
},
@@ -7182,11 +9365,12 @@
start: function(x, y){
if (!this.check(x, y)) return this;
- var offsetSize = this.element.getSize(), scrollSize = this.element.getScrollSize();
- var scroll = this.element.getScroll(), values = {x: x, y: y};
+ var scrollSize = this.element.getScrollSize(),
+ scroll = this.element.getScroll(),
+ values = {x: x, y: y};
for (var z in values){
- var max = scrollSize[z] - offsetSize[z];
- if ($chk(values[z])) values[z] = ($type(values[z]) == 'number') ? values[z].limit(0, max) : max;
+ var max = scrollSize[z];
+ if ($chk(values[z])) values[z] = ($type(values[z]) == 'number') ? values[z] : max;
else values[z] = scroll[z];
values[z] += this.options.offset[z];
}
@@ -7210,20 +9394,78 @@
},
toElement: function(el){
- var position = $(el).getPosition(this.element);
+ var position = document.id(el).getPosition(this.element);
return this.start(position.x, position.y);
+ },
+
+ scrollIntoView: function(el, axes, offset){
+ axes = axes ? $splat(axes) : ['x','y'];
+ var to = {};
+ el = document.id(el);
+ var pos = el.getPosition(this.element);
+ var size = el.getSize();
+ var scroll = this.element.getScroll();
+ var containerSize = this.element.getSize();
+ var edge = {
+ x: pos.x + size.x,
+ y: pos.y + size.y
+ };
+ ['x','y'].each(function(axis) {
+ if (axes.contains(axis)) {
+ if (edge[axis] > scroll[axis] + containerSize[axis]) to[axis] = edge[axis] - containerSize[axis];
+ if (pos[axis] < scroll[axis]) to[axis] = pos[axis];
+ }
+ if (to[axis] == null) to[axis] = scroll[axis];
+ if (offset && offset[axis]) to[axis] = to[axis] + offset[axis];
+ }, this);
+ if (to.x != scroll.x || to.y != scroll.y) this.start(to.x, to.y);
+ return this;
+ },
+
+ scrollToCenter: function(el, axes, offset){
+ axes = axes ? $splat(axes) : ['x', 'y'];
+ el = $(el);
+ var to = {},
+ pos = el.getPosition(this.element),
+ size = el.getSize(),
+ scroll = this.element.getScroll(),
+ containerSize = this.element.getSize(),
+ edge = {
+ x: pos.x + size.x,
+ y: pos.y + size.y
+ };
+
+ ['x','y'].each(function(axis){
+ if(axes.contains(axis)){
+ to[axis] = pos[axis] - (containerSize[axis] - size[axis])/2;
+ }
+ if(to[axis] == null) to[axis] = scroll[axis];
+ if(offset && offset[axis]) to[axis] = to[axis] + offset[axis];
+ }, this);
+ if (to.x != scroll.x || to.y != scroll.y) this.start(to.x, to.y);
+ return this;
}
});
/*
-Script: Fx.Slide.js
- Effect to slide an element in and out of view.
+---
- License:
- MIT-style license.
+script: Fx.Slide.js
- Authors:
- Valerio Proietti
+description: Effect to slide an element in and out of view.
+
+license: MIT-style license
+
+authors:
+- Valerio Proietti
+
+requires:
+- core:1.2.4/Fx Element.Style
+- /MooTools.More
+
+provides: [Fx.Slide]
+
+...
*/
Fx.Slide = new Class({
@@ -7239,11 +9481,11 @@
this.open = (this.wrapper['offset' + this.layout.capitalize()] != 0);
if (this.open && Browser.Engine.webkit419) this.element.dispose().inject(this.wrapper);
}, true);
- this.element = this.subject = $(element);
+ this.element = this.subject = document.id(element);
this.parent(options);
var wrapper = this.element.retrieve('wrapper');
this.wrapper = wrapper || new Element('div', {
- styles: $extend(this.element.getStyles('margin', 'position'), {overflow: 'hidden'})
+ styles: this.element.getStyles('margin', 'position', 'overflow')
}).wraps(this.element);
this.element.store('wrapper', this.wrapper).setStyle('margin', 0);
this.now = [];
@@ -7356,14 +9598,24 @@
});
/*
-Script: Fx.SmoothScroll.js
- Class for creating a smooth scrolling effect to all internal links on the page.
+---
- License:
- MIT-style license.
+script: Fx.SmoothScroll.js
- Authors:
- Valerio Proietti
+description: Class for creating a smooth scrolling effect to all internal links on the page.
+
+license: MIT-style license
+
+authors:
+- Valerio Proietti
+
+requires:
+- core:1.2.4/Selectors
+- /Fx.Scroll
+
+provides: [Fx.SmoothScroll]
+
+...
*/
var SmoothScroll = Fx.SmoothScroll = new Class({
@@ -7375,7 +9627,7 @@
this.doc = context.getDocument();
var win = context.getWindow();
this.parent(this.doc, options);
- this.links = this.options.links ? $$(this.options.links) : $$(this.doc.links);
+ this.links = $$(this.options.links || this.doc.links);
var location = win.location.href.match(/^[^#]*/)[0] + '#';
this.links.each(function(link){
if (link.href.indexOf(location) != 0) {return;}
@@ -7392,7 +9644,7 @@
useLink: function(link, anchor){
var el;
link.addEvent('click', function(event){
- if (el !== false && !el) el = $(anchor) || this.doc.getElement('a[name=' + anchor + ']');
+ if (el !== false && !el) el = document.id(anchor) || this.doc.getElement('a[name=' + anchor + ']');
if (el) {
event.preventDefault();
this.anchor = anchor;
@@ -7401,17 +9653,26 @@
}
}.bind(this));
}
-
});/*
-Script: Fx.Sort.js
- Defines Fx.Sort, a class that reorders lists with a transition.
+---
- License:
- MIT-style license.
+script: Fx.Sort.js
- Authors:
- Aaron Newton
+description: Defines Fx.Sort, a class that reorders lists with a transition.
+license: MIT-style license
+
+authors:
+- Aaron Newton
+
+requires:
+- core:1.2.4/Element.Dimensions
+- /Fx.Elements
+- /Element.Measure
+
+provides: [Fx.Sort]
+
+...
*/
Fx.Sort = new Class({
@@ -7438,10 +9699,11 @@
sort: function(newOrder){
if ($type(newOrder) != 'array') return false;
- var top = 0;
- var left = 0;
- var zero = {};
- var vert = this.options.mode == 'vertical';
+ var top = 0,
+ left = 0,
+ next = {},
+ zero = {},
+ vert = this.options.mode == 'vertical';
var current = this.elements.map(function(el, index){
var size = el.getComputedSize({styles: ['border', 'padding', 'margin']});
var val;
@@ -7475,10 +9737,7 @@
if (newOrder.length > this.elements.length)
newOrder.splice(this.elements.length-1, newOrder.length - this.elements.length);
}
- top = 0;
- left = 0;
- var margin = 0;
- var next = {};
+ var margin = top = left = 0;
newOrder.each(function(item, index){
var newPos = {};
if (vert){
@@ -7549,20 +9808,32 @@
var newOrder = $A(this.currentOrder);
newOrder[this.currentOrder.indexOf(one)] = two;
newOrder[this.currentOrder.indexOf(two)] = one;
- this.sort(newOrder);
+ return this.sort(newOrder);
}
});/*
-Script: Drag.js
- The base Drag Class. Can be used to drag and resize Elements using mouse events.
+---
- License:
- MIT-style license.
+script: Drag.js
- Authors:
- Valerio Proietti
- Tom Occhinno
- Jan Kassens
+description: The base Drag Class. Can be used to drag and resize Elements using mouse events.
+
+license: MIT-style license
+
+authors:
+- Valerio Proietti
+- Tom Occhinno
+- Jan Kassens
+
+requires:
+- core:1.2.4/Events
+- core:1.2.4/Options
+- core:1.2.4/Element.Event
+- core:1.2.4/Element.Style
+- /MooTools.More
+
+provides: [Drag]
+
*/
var Drag = new Class({
@@ -7589,11 +9860,11 @@
initialize: function(){
var params = Array.link(arguments, {'options': Object.type, 'element': $defined});
- this.element = $(params.element);
+ this.element = document.id(params.element);
this.document = this.element.getDocument();
this.setOptions(params.options || {});
var htype = $type(this.options.handle);
- this.handles = ((htype == 'array' || htype == 'collection') ? $$(this.options.handle) : $(this.options.handle)) || this.element;
+ this.handles = ((htype == 'array' || htype == 'collection') ? $$(this.options.handle) : document.id(this.options.handle)) || this.element;
this.mouse = {'now': {}, 'pos': {}};
this.value = {'start': {}, 'now': {}};
@@ -7621,6 +9892,7 @@
},
start: function(event){
+ if (event.rightClick) return;
if (this.options.preventDefault) event.preventDefault();
this.mouse.start = event.page;
this.fireEvent('beforeStart', this.element);
@@ -7670,9 +9942,12 @@
this.value.now[z] = this.limit[z][0];
}
}
- if (this.options.grid[z]) this.value.now[z] -= ((this.value.now[z] - this.limit[z][0]) % this.options.grid[z]);
- if (this.options.style) this.element.setStyle(this.options.modifiers[z], this.value.now[z] + this.options.unit);
- else this.element[this.options.modifiers[z]] = this.value.now[z];
+ if (this.options.grid[z]) this.value.now[z] -= ((this.value.now[z] - (this.limit[z][0]||0)) % this.options.grid[z]);
+ if (this.options.style) {
+ this.element.setStyle(this.options.modifiers[z], this.value.now[z] + this.options.unit);
+ } else {
+ this.element[this.options.modifiers[z]] = this.value.now[z];
+ }
}
this.fireEvent('drag', [this.element, event]);
},
@@ -7707,17 +9982,30 @@
});
/*
-Script: Drag.Move.js
- A Drag extension that provides support for the constraining of draggables to containers and droppables.
+---
- License:
- MIT-style license.
+script: Drag.Move.js
- Authors:
- Valerio Proietti
- Tom Occhinno
- Jan Kassens*/
+description: A Drag extension that provides support for the constraining of draggables to containers and droppables.
+license: MIT-style license
+
+authors:
+- Valerio Proietti
+- Tom Occhinno
+- Jan Kassens
+- Aaron Newton
+- Scott Kyle
+
+requires:
+- core:1.2.4/Element.Dimensions
+- /Drag
+
+provides: [Drag.Move]
+
+...
+*/
+
Drag.Move = new Class({
Extends: Drag,
@@ -7735,56 +10023,101 @@
initialize: function(element, options){
this.parent(element, options);
+ element = this.element;
+
this.droppables = $$(this.options.droppables);
- this.container = $(this.options.container);
- if (this.container && $type(this.container) != 'element') this.container = $(this.container.getDocument().body);
+ this.container = document.id(this.options.container);
+
+ if (this.container && $type(this.container) != 'element')
+ this.container = document.id(this.container.getDocument().body);
+
+ var styles = element.getStyles('left', 'right', 'position');
+ if (styles.left == 'auto' || styles.top == 'auto')
+ element.setPosition(element.getPosition(element.getOffsetParent()));
+
+ if (styles.position == 'static')
+ element.setStyle('position', 'absolute');
- var position = this.element.getStyle('position');
- if (position=='static') position = 'absolute';
- if ([this.element.getStyle('left'), this.element.getStyle('top')].contains('auto')) this.element.position(this.element.getPosition(this.element.offsetParent));
- this.element.setStyle('position', position);
-
this.addEvent('start', this.checkDroppables, true);
this.overed = null;
},
start: function(event){
- if (this.container){
- var ccoo = this.container.getCoordinates(this.element.getOffsetParent()), cbs = {}, ems = {};
+ if (this.container) this.options.limit = this.calculateLimit();
+
+ if (this.options.precalculate){
+ this.positions = this.droppables.map(function(el){
+ return el.getCoordinates();
+ });
+ }
+
+ this.parent(event);
+ },
+
+ calculateLimit: function(){
+ var offsetParent = this.element.getOffsetParent(),
+ containerCoordinates = this.container.getCoordinates(offsetParent),
+ containerBorder = {},
+ elementMargin = {},
+ elementBorder = {},
+ containerMargin = {},
+ offsetParentPadding = {};
- ['top', 'right', 'bottom', 'left'].each(function(pad){
- cbs[pad] = this.container.getStyle('border-' + pad).toInt();
- ems[pad] = this.element.getStyle('margin-' + pad).toInt();
- }, this);
+ ['top', 'right', 'bottom', 'left'].each(function(pad){
+ containerBorder[pad] = this.container.getStyle('border-' + pad).toInt();
+ elementBorder[pad] = this.element.getStyle('border-' + pad).toInt();
+ elementMargin[pad] = this.element.getStyle('margin-' + pad).toInt();
+ containerMargin[pad] = this.container.getStyle('margin-' + pad).toInt();
+ offsetParentPadding[pad] = offsetParent.getStyle('padding-' + pad).toInt();
+ }, this);
- var width = this.element.offsetWidth + ems.left + ems.right;
- var height = this.element.offsetHeight + ems.top + ems.bottom;
+ var width = this.element.offsetWidth + elementMargin.left + elementMargin.right,
+ height = this.element.offsetHeight + elementMargin.top + elementMargin.bottom,
+ left = 0,
+ top = 0,
+ right = containerCoordinates.right - containerBorder.right - width,
+ bottom = containerCoordinates.bottom - containerBorder.bottom - height;
- if (this.options.includeMargins) {
- $each(ems, function(value, key) {
- ems[key] = 0;
- });
+ if (this.options.includeMargins){
+ left += elementMargin.left;
+ top += elementMargin.top;
+ } else {
+ right += elementMargin.right;
+ bottom += elementMargin.bottom;
+ }
+
+ if (this.element.getStyle('position') == 'relative'){
+ var coords = this.element.getCoordinates(offsetParent);
+ coords.left -= this.element.getStyle('left').toInt();
+ coords.top -= this.element.getStyle('top').toInt();
+
+ left += containerBorder.left - coords.left;
+ top += containerBorder.top - coords.top;
+ right += elementMargin.left - coords.left;
+ bottom += elementMargin.top - coords.top;
+
+ if (this.container != offsetParent){
+ left += containerMargin.left + offsetParentPadding.left;
+ top += (Browser.Engine.trident4 ? 0 : containerMargin.top) + offsetParentPadding.top;
}
- if (this.container == this.element.getOffsetParent()) {
- this.options.limit = {
- x: [0 - ems.left, ccoo.right - cbs.left - cbs.right - width + ems.right],
- y: [0 - ems.top, ccoo.bottom - cbs.top - cbs.bottom - height + ems.bottom]
- };
+ } else {
+ left -= elementMargin.left;
+ top -= elementMargin.top;
+
+ if (this.container == offsetParent){
+ right -= containerBorder.left;
+ bottom -= containerBorder.top;
} else {
- this.options.limit = {
- x: [ccoo.left + cbs.left - ems.left, ccoo.right - cbs.right - width + ems.right],
- y: [ccoo.top + cbs.top - ems.top, ccoo.bottom - cbs.bottom - height + ems.bottom]
- };
+ left += containerCoordinates.left + containerBorder.left;
+ top += containerCoordinates.top + containerBorder.top;
}
-
}
- if (this.options.precalculate){
- this.positions = this.droppables.map(function(el) {
- return el.getCoordinates();
- });
- }
- this.parent(event);
+
+ return {
+ x: [left, right],
+ y: [top, bottom]
+ };
},
checkAgainst: function(el, i){
@@ -7826,14 +10159,27 @@
});
/*
-Script: Slider.js
- Class for creating horizontal and vertical slider controls.
+---
- License:
- MIT-style license.
+script: Slider.js
- Authors:
- Valerio Proietti
+description: Class for creating horizontal and vertical slider controls.
+
+license: MIT-style license
+
+authors:
+- Valerio Proietti
+
+requires:
+- core:1.2.4/Element.Dimensions
+- /Class.Binds
+- /Drag
+- /Element.Dimensions
+- /Element.Measure
+
+provides: [Slider]
+
+...
*/
var Slider = new Class({
@@ -7850,6 +10196,7 @@
if (this.options.snap) position = this.toPosition(this.step);
this.knob.setStyle(this.property, position);
},
+ initialStep: 0,
snap: false,
offset: 0,
range: false,
@@ -7860,8 +10207,8 @@
initialize: function(element, knob, options){
this.setOptions(options);
- this.element = $(element);
- this.knob = $(knob);
+ this.element = document.id(element);
+ this.knob = document.id(knob);
this.previousChange = this.previousEnd = this.step = -1;
var offset, limit = {}, modifiers = {'x': false, 'y': false};
switch (this.options.mode){
@@ -7875,8 +10222,12 @@
this.property = 'left';
offset = 'offsetWidth';
}
- this.half = this.knob[offset] / 2;
- this.full = this.element[offset] - this.knob[offset] + (this.options.offset * 2);
+
+ this.full = this.element.measure(function(){
+ this.half = this.knob[offset] / 2;
+ return this.element[offset] - this.knob[offset] + (this.options.offset * 2);
+ }.bind(this));
+
this.min = $chk(this.options.range[0]) ? this.options.range[0] : 0;
this.max = $chk(this.options.range[1]) ? this.options.range[1] : this.options.steps;
this.range = this.max - this.min;
@@ -7884,25 +10235,22 @@
this.stepSize = Math.abs(this.range) / this.steps;
this.stepWidth = this.stepSize * this.full / Math.abs(this.range) ;
- this.knob.setStyle('position', 'relative').setStyle(this.property, - this.options.offset);
+ this.knob.setStyle('position', 'relative').setStyle(this.property, this.options.initialStep ? this.toPosition(this.options.initialStep) : - this.options.offset);
modifiers[this.axis] = this.property;
limit[this.axis] = [- this.options.offset, this.full - this.options.offset];
- this.bound = {
- clickedElement: this.clickedElement.bind(this),
- scrolledElement: this.scrolledElement.bindWithEvent(this),
- draggedKnob: this.draggedKnob.bind(this)
- };
-
var dragOptions = {
snap: 0,
limit: limit,
modifiers: modifiers,
- onDrag: this.bound.draggedKnob,
- onStart: this.bound.draggedKnob,
+ onDrag: this.draggedKnob,
+ onStart: this.draggedKnob,
onBeforeStart: (function(){
this.isDragging = true;
}).bind(this),
+ onCancel: function() {
+ this.isDragging = false;
+ }.bind(this),
onComplete: function(){
this.isDragging = false;
this.draggedKnob();
@@ -7919,15 +10267,15 @@
},
attach: function(){
- this.element.addEvent('mousedown', this.bound.clickedElement);
- if (this.options.wheel) this.element.addEvent('mousewheel', this.bound.scrolledElement);
+ this.element.addEvent('mousedown', this.clickedElement);
+ if (this.options.wheel) this.element.addEvent('mousewheel', this.scrolledElement);
this.drag.attach();
return this;
},
detach: function(){
- this.element.removeEvent('mousedown', this.bound.clickedElement);
- this.element.removeEvent('mousewheel', this.bound.scrolledElement);
+ this.element.removeEvent('mousedown', this.clickedElement);
+ this.element.removeEvent('mousewheel', this.scrolledElement);
this.drag.detach();
return this;
},
@@ -7994,14 +10342,23 @@
}
});/*
-Script: Sortables.js
- Class for creating a drag and drop sorting interface for lists of items.
+---
- License:
- MIT-style license.
+script: Sortables.js
- Authors:
- Tom Occhino
+description: Class for creating a drag and drop sorting interface for lists of items.
+
+license: MIT-style license
+
+authors:
+- Tom Occhino
+
+requires:
+- /Drag.Move
+
+provides: [Slider]
+
+...
*/
var Sortables = new Class({
@@ -8026,7 +10383,7 @@
this.lists = [];
this.idle = true;
- this.addLists($$($(lists) || lists));
+ this.addLists($$(document.id(lists) || lists));
if (!this.options.clone) this.options.revert = false;
if (this.options.revert) this.effect = new Fx.Morph(null, $merge({duration: 250, link: 'cancel'}, this.options.revert));
},
@@ -8085,7 +10442,7 @@
position: 'absolute',
visibility: 'hidden',
'width': element.getStyle('width')
- }).inject(this.list).position(element.getPosition(element.getOffsetParent()));
+ }).inject(this.list).setPosition(element.getPosition(element.getOffsetParent()));
},
getDroppables: function(){
@@ -8173,15 +10530,26 @@
});
/*
-Script: Request.JSONP.js
- Defines Request.JSONP, a class for cross domain javascript via script injection.
+---
- License:
- MIT-style license.
+script: Request.JSONP.js
- Authors:
- Aaron Newton
- Guillermo Rauch
+description: Defines Request.JSONP, a class for cross domain javascript via script injection.
+
+license: MIT-style license
+
+authors:
+- Aaron Newton
+- Guillermo Rauch
+
+requires:
+- core:1.2.4/Element
+- core:1.2.4/Request
+- /Log
+
+provides: [Request.JSONP]
+
+...
*/
Request.JSONP = new Class({
@@ -8193,7 +10561,9 @@
onRequest: $empty(scriptElement),
onComplete: $empty(data),
onSuccess: $empty(data),
- onCancel: $empty(),*/
+ onCancel: $empty(),
+ log: false,
+ */
url: '',
data: {},
retries: 0,
@@ -8205,6 +10575,7 @@
initialize: function(options){
this.setOptions(options);
+ if (this.options.log) this.enableLog();
this.running = false;
this.requests = 0;
this.triesRemaining = [];
@@ -8222,7 +10593,9 @@
send: function(options){
if (!$chk(arguments[1]) && !this.check(options)) return this;
- var type = $type(options), old = this.options, index = $chk(arguments[1]) ? arguments[1] : this.requests++;
+ var type = $type(options),
+ old = this.options,
+ index = $chk(arguments[1]) ? arguments[1] : this.requests++;
if (type == 'string' || type == 'element') options = {data: options};
options = $extend({data: old.data, url: old.url}, options);
@@ -8241,13 +10614,11 @@
this.triesRemaining[index] = remaining - 1;
if (script){
script.destroy();
- this.request(options, index);
- this.fireEvent('retry', this.triesRemaining[index]);
+ this.send(options, index).fireEvent('retry', this.triesRemaining[index]);
}
} else if(script && this.options.timeout){
script.destroy();
- this.cancel();
- this.fireEvent('failure');
+ this.cancel().fireEvent('failure');
}
}).delay(this.options.timeout, this);
}).delay(Browser.Engine.trident ? 50 : 0, this);
@@ -8262,11 +10633,12 @@
},
getScript: function(options){
- var index = Request.JSONP.counter, data;
+ var index = Request.JSONP.counter,
+ data;
Request.JSONP.counter++;
switch ($type(options.data)){
- case 'element': data = $(options.data).toQueryString(); break;
+ case 'element': data = document.id(options.data).toQueryString(); break;
case 'object': case 'hash': data = Hash.toQueryString(options.data);
}
@@ -8293,14 +10665,25 @@
Request.JSONP.counter = 0;
Request.JSONP.request_map = {};/*
-Script: Request.Queue.js
- Controls several instances of Request and its variants to run only one request at a time.
+---
- License:
- MIT-style license.
+script: Request.Queue.js
- Authors:
- Aaron Newton
+description: Controls several instances of Request and its variants to run only one request at a time.
+
+license: MIT-style license
+
+authors:
+- Aaron Newton
+
+requires:
+- core:1.2.4/Element
+- core:1.2.4/Request
+- /Log
+
+provides: [Request.Queue]
+
+...
*/
Request.Queue = new Class({
@@ -8315,7 +10698,9 @@
onComplete: $empty(argsPassedToOnComplete),
onCancel: $empty(argsPassedToOnCancel),
onException: $empty(argsPassedToOnException),
- onFailure: $empty(argsPassedToOnFailure),*/
+ onFailure: $empty(argsPassedToOnFailure),
+ onEnd: $empty,
+ */
stopOnFailure: true,
autoAdvance: true,
concurrent: 1,
@@ -8323,11 +10708,16 @@
},
initialize: function(options){
+ if(options){
+ var requests = options.requests;
+ delete options.requests;
+ }
this.setOptions(options);
this.requests = new Hash;
- this.addRequests(this.options.requests);
this.queue = [];
this.reqBinders = {};
+
+ if(requests) this.addRequests(requests);
},
addRequest: function(name, request){
@@ -8337,7 +10727,9 @@
},
addRequests: function(obj){
- $each(obj, this.addRequest, this);
+ $each(obj, function(req, name){
+ this.addRequest(name, req);
+ }, this);
return this;
},
@@ -8376,11 +10768,13 @@
},
getRunning: function(){
- return this.requests.filter(function(r){ return r.running; });
+ return this.requests.filter(function(r){
+ return r.running;
+ });
},
isRunning: function(){
- return !!this.getRunning().getKeys().length;
+ return !!(this.getRunning().getKeys().length);
},
send: function(name, options){
@@ -8450,6 +10844,7 @@
onComplete: function(){
this.fireEvent('complete', arguments);
+ if (!this.queue.length) this.fireEvent('end');
},
onCancel: function(){
@@ -8476,15 +10871,24 @@
});
/*
-Script: Request.Periodical.js
- Requests the same url at a time interval that increases when no data is returned from the requested server
+---
- License:
- MIT-style license.
+script: Request.Periodical.js
- Authors:
- Christoph Pojer
+description: Requests the same URL to pull data from a server but increases the intervals if no data is returned to reduce the load
+license: MIT-style license
+
+authors:
+- Christoph Pojer
+
+requires:
+- core:1.2.4/Request
+- /MooTools.More
+
+provides: [Request.Periodical]
+
+...
*/
Request.implement({
@@ -8496,36 +10900,43 @@
},
startTimer: function(data){
- var fn = (function(){
+ var fn = function(){
if (!this.running) this.send({data: data});
- });
+ };
this.timer = fn.delay(this.options.initialDelay, this);
this.lastDelay = this.options.initialDelay;
- this.completeCheck = function(j){
+ this.completeCheck = function(response){
$clear(this.timer);
- if (j) this.lastDelay = this.options.delay;
- else this.lastDelay = (this.lastDelay+this.options.delay).min(this.options.limit);
+ this.lastDelay = (response) ? this.options.delay : (this.lastDelay + this.options.delay).min(this.options.limit);
this.timer = fn.delay(this.lastDelay, this);
};
- this.addEvent('complete', this.completeCheck);
- return this;
+ return this.addEvent('complete', this.completeCheck);
},
stopTimer: function(){
$clear(this.timer);
- this.removeEvent('complete', this.completeCheck);
- return this;
+ return this.removeEvent('complete', this.completeCheck);
}
});/*
-Script: Assets.js
- Provides methods to dynamically load JavaScript, CSS, and Image files into the document.
+---
- License:
- MIT-style license.
+script: Assets.js
- Authors:
- Valerio Proietti
+description: Provides methods to dynamically load JavaScript, CSS, and Image files into the document.
+
+license: MIT-style license
+
+authors:
+- Valerio Proietti
+
+requires:
+- core:1.2.4/Element.Event
+- /MooTools.More
+
+provides: [Assets]
+
+...
*/
var Asset = {
@@ -8539,8 +10950,12 @@
var script = new Element('script', {src: source, type: 'text/javascript'});
- var load = properties.onload.bind(script), check = properties.check, doc = properties.document;
- delete properties.onload; delete properties.check; delete properties.document;
+ var load = properties.onload.bind(script),
+ check = properties.check,
+ doc = properties.document;
+ delete properties.onload;
+ delete properties.check;
+ delete properties.document;
script.addEvents({
load: load,
@@ -8560,7 +10975,10 @@
css: function(source, properties){
return new Element('link', $merge({
- rel: 'stylesheet', media: 'screen', type: 'text/css', href: source
+ rel: 'stylesheet',
+ media: 'screen',
+ type: 'text/css',
+ href: source
}, properties)).inject(document.head);
},
@@ -8571,7 +10989,7 @@
onerror: $empty
}, properties);
var image = new Image();
- var element = $(image) || new Element('img');
+ var element = document.id(image) || new Element('img');
['load', 'abort', 'error'].each(function(name){
var type = 'on' + name;
var event = properties[type];
@@ -8595,31 +11013,52 @@
images: function(sources, options){
options = $merge({
onComplete: $empty,
- onProgress: $empty
+ onProgress: $empty,
+ onError: $empty,
+ properties: {}
}, options);
sources = $splat(sources);
var images = [];
var counter = 0;
return new Elements(sources.map(function(source){
- return Asset.image(source, {
+ return Asset.image(source, $extend(options.properties, {
onload: function(){
options.onProgress.call(this, counter, sources.indexOf(source));
counter++;
if (counter == sources.length) options.onComplete();
+ },
+ onerror: function(){
+ options.onError.call(this, counter, sources.indexOf(source));
+ counter++;
+ if (counter == sources.length) options.onComplete();
}
- });
+ }));
}));
}
};/*
-Script: Color.js
- Class for creating and manipulating colors in JavaScript. Supports HSB -> RGB Conversions and vice versa.
+---
- License:
- MIT-style license.
+script: Color.js
- Authors:
- Valerio Proietti
+description: Class for creating and manipulating colors in JavaScript. Supports HSB -> RGB Conversions and vice versa.
+
+license: MIT-style license
+
+authors:
+- Valerio Proietti
+
+requires:
+- core:1.2.4/Array
+- core:1.2.4/String
+- core:1.2.4/Number
+- core:1.2.4/Hash
+- core:1.2.4/Function
+- core:1.2.4/$util
+
+provides: [Color]
+
+...
*/
var Color = new Native({
@@ -8697,15 +11136,16 @@
Array.implement({
rgbToHsb: function(){
- var red = this[0], green = this[1], blue = this[2];
- var hue, saturation, brightness;
- var max = Math.max(red, green, blue), min = Math.min(red, green, blue);
+ var red = this[0],
+ green = this[1],
+ blue = this[2],
+ hue = 0;
+ var max = Math.max(red, green, blue),
+ min = Math.min(red, green, blue);
var delta = max - min;
- brightness = max / 255;
- saturation = (max != 0) ? delta / max : 0;
- if (saturation == 0){
- hue = 0;
- } else {
+ var brightness = max / 255,
+ saturation = (max != 0) ? delta / max : 0;
+ if(saturation != 0) {
var rr = (max - red) / delta;
var gr = (max - green) / delta;
var br = (max - blue) / delta;
@@ -8756,14 +11196,24 @@
});
/*
-Script: Group.js
- Class for monitoring collections of events
+---
- License:
- MIT-style license.
+script: Group.js
- Authors:
- Valerio Proietti
+description: Class for monitoring collections of events
+
+license: MIT-style license
+
+authors:
+- Valerio Proietti
+
+requires:
+- core:1.2.4/Events
+- /MooTools.More
+
+provides: [Group]
+
+...
*/
var Group = new Class({
@@ -8799,15 +11249,26 @@
});
/*
-Script: Hash.Cookie.js
- Class for creating, reading, and deleting Cookies in JSON format.
+---
- License:
- MIT-style license.
+script: Hash.Cookie.js
- Authors:
- Valerio Proietti
- Aaron Newton
+description: Class for creating, reading, and deleting Cookies in JSON format.
+
+license: MIT-style license
+
+authors:
+- Valerio Proietti
+- Aaron Newton
+
+requires:
+- core:1.2.4/Cookie
+- core:1.2.4/JSON
+- /MooTools.More
+
+provides: [Hash.Cookie]
+
+...
*/
Hash.Cookie = new Class({
@@ -8845,127 +11306,862 @@
return value;
});
});/*
-Script: IframeShim.js
- Defines IframeShim, a class for obscuring select lists and flash objects in IE.
+---
- License:
- MIT-style license.
+script: HtmlTable.js
- Authors:
- Aaron Newton
+description: Builds table elements with methods to add rows.
+
+license: MIT-style license
+
+authors:
+- Aaron Newton
+
+requires:
+- core:1.2.4/Options
+- core:1.2.4/Events
+- /Class.Occlude
+
+provides: [HtmlTable]
+
+...
*/
-var IframeShim = new Class({
+var HtmlTable = new Class({
Implements: [Options, Events, Class.Occlude],
options: {
- className: 'iframeShim',
- display: false,
- zIndex: null,
- margin: 0,
- offset: {x: 0, y: 0},
- browsers: true || (Browser.Engine.trident4 || (Browser.Engine.gecko && !Browser.Engine.gecko19 && Browser.Platform.mac))
+ properties: {
+ cellpadding: 0,
+ cellspacing: 0,
+ border: 0
+ },
+ rows: [],
+ headers: [],
+ footers: []
},
- property: 'IframeShim',
+ property: 'HtmlTable',
- initialize: function(element, options){
- this.element = $(element);
+ initialize: function(){
+ var params = Array.link(arguments, {options: Object.type, table: Element.type});
+ this.setOptions(params.options);
+ this.element = params.table || new Element('table', this.options.properties);
if (this.occlude()) return this.occluded;
- this.setOptions(options);
- this.makeShim();
+ this.build();
+ },
+
+ build: function(){
+ this.element.store('HtmlTable', this);
+
+ this.body = document.id(this.element.tBodies[0]) || new Element('tbody').inject(this.element);
+ $$(this.body.rows);
+
+ if (this.options.headers.length) this.setHeaders(this.options.headers);
+ else this.thead = document.id(this.element.tHead);
+ if (this.thead) this.head = document.id(this.thead.rows[0]);
+
+ if (this.options.footers.length) this.setFooters(this.options.footers);
+ this.tfoot = document.id(this.element.tFoot);
+ if (this.tfoot) this.foot = document.id(this.thead.rows[0]);
+
+ this.options.rows.each(this.push.bind(this));
+
+ ['adopt', 'inject', 'wraps', 'grab', 'replaces', 'dispose'].each(function(method){
+ this[method] = this.element[method].bind(this.element);
+ }, this);
+ },
+
+ toElement: function(){
+ return this.element;
+ },
+
+ empty: function(){
+ this.body.empty();
return this;
},
- makeShim: function(){
- if(this.options.browsers){
- var zIndex = this.element.getStyle('zIndex').toInt();
+ setHeaders: function(headers){
+ this.thead = (document.id(this.element.tHead) || new Element('thead').inject(this.element, 'top')).empty();
+ this.push(headers, this.thead, 'th');
+ this.head = document.id(this.thead.rows[0]);
+ return this;
+ },
- if (!zIndex){
- var pos = this.element.getStyle('position');
- if (pos == 'static' || !pos) this.element.setStyle('position', 'relative');
- this.element.setStyle('zIndex', zIndex || 1);
- }
- zIndex = ($chk(this.options.zIndex) && zIndex > this.options.zIndex) ? this.options.zIndex : zIndex - 1;
- if (zIndex < 0) zIndex = 1;
- this.shim = new Element('iframe', {
- src: (window.location.protocol == 'https') ? '://0' : 'javascript:void(0)',
- scrolling: 'no',
- frameborder: 0,
- styles: {
- zIndex: zIndex,
- position: 'absolute',
- border: 'none',
- filter: 'progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)'
- },
- 'class': this.options.className
- }).store('IframeShim', this);
- var inject = (function(){
- this.shim.inject(this.element, 'after');
- this[this.options.display ? 'show' : 'hide']();
- this.fireEvent('inject');
- }).bind(this);
- if (Browser.Engine.trident && !IframeShim.ready) window.addEvent('load', inject);
- else inject();
- } else {
- this.position = this.hide = this.show = this.dispose = $lambda(this);
+ setFooters: function(footers){
+ this.tfoot = (document.id(this.element.tFoot) || new Element('tfoot').inject(this.element, 'top')).empty();
+ this.push(footers, this.tfoot);
+ this.foot = document.id(this.thead.rows[0]);
+ return this;
+ },
+
+ push: function(row, target, tag){
+ var tds = row.map(function(data){
+ var td = new Element(tag || 'td', data.properties),
+ type = data.content || data || '',
+ element = document.id(type);
+
+ if(element) td.adopt(element);
+ else td.set('html', type);
+
+ return td;
+ });
+
+ return {
+ tr: new Element('tr').inject(target || this.body).adopt(tds),
+ tds: tds
+ };
+ }
+
+});/*
+---
+
+script: HtmlTable.Zebra.js
+
+description: Builds a stripy table with methods to add rows.
+
+license: MIT-style license
+
+authors:
+- Harald Kirschner
+- Aaron Newton
+
+requires:
+- /HtmlTable
+- /Class.refactor
+
+provides: [HtmlTable.Zebra]
+
+...
+*/
+
+HtmlTable = Class.refactor(HtmlTable, {
+
+ options: {
+ classZebra: 'table-tr-odd',
+ zebra: true
+ },
+
+ initialize: function(){
+ this.previous.apply(this, arguments);
+ if (this.occluded) return this.occluded;
+ if (this.options.zebra) this.updateZebras();
+ },
+
+ updateZebras: function(){
+ Array.each(this.body.rows, this.zebra, this);
+ },
+
+ zebra: function(row, i){
+ return row[((i % 2) ? 'remove' : 'add')+'Class'](this.options.classZebra);
+ },
+
+ push: function(){
+ var pushed = this.previous.apply(this, arguments);
+ if (this.options.zebra) this.updateZebras();
+ return pushed;
+ }
+
+});/*
+---
+
+script: HtmlTable.Sort.js
+
+description: Builds a stripy, sortable table with methods to add rows.
+
+license: MIT-style license
+
+authors:
+- Harald Kirschner
+- Aaron Newton
+
+requires:
+- core:1.2.4/Hash
+- /HtmlTable
+- /Class.refactor
+- /Element.Delegation
+- /Date
+
+provides: [HtmlTable.Sort]
+
+...
+*/
+
+HtmlTable = Class.refactor(HtmlTable, {
+
+ options: {/*
+ onSort: $empty, */
+ sortIndex: 0,
+ sortReverse: false,
+ parsers: [],
+ defaultParser: 'string',
+ classSortable: 'table-sortable',
+ classHeadSort: 'table-th-sort',
+ classHeadSortRev: 'table-th-sort-rev',
+ classNoSort: 'table-th-nosort',
+ classGroupHead: 'table-tr-group-head',
+ classGroup: 'table-tr-group',
+ classCellSort: 'table-td-sort',
+ classSortSpan: 'table-th-sort-span',
+ sortable: false
+ },
+
+ initialize: function () {
+ this.previous.apply(this, arguments);
+ if (this.occluded) return this.occluded;
+ this.sorted = {index: null, dir: 1};
+ this.bound = {
+ headClick: this.headClick.bind(this)
+ };
+ this.sortSpans = new Elements();
+ if (this.options.sortable) {
+ this.enableSort();
+ if (this.options.sortIndex != null) this.sort(this.options.sortIndex, this.options.sortReverse);
}
},
- position: function(){
- if (!IframeShim.ready) return this;
- var size = this.element.measure(function(){ return this.getSize(); });
- if ($type(this.options.margin)){
- size.x = size.x - (this.options.margin * 2);
- size.y = size.y - (this.options.margin * 2);
- this.options.offset.x += this.options.margin;
- this.options.offset.y += this.options.margin;
+ attachSorts: function(attach){
+ this.element[$pick(attach, true) ? 'addEvent' : 'removeEvent']('click:relay(th)', this.bound.headClick);
+ },
+
+ setHeaders: function(){
+ this.previous.apply(this, arguments);
+ if (this.sortEnabled) this.detectParsers();
+ },
+
+ detectParsers: function(force){
+ if (!this.head) return;
+ var parsers = this.options.parsers,
+ rows = this.body.rows;
+
+ // auto-detect
+ this.parsers = $$(this.head.cells).map(function(cell, index) {
+ if (!force && (cell.hasClass(this.options.classNoSort) || cell.retrieve('htmltable-sort'))) return cell.retrieve('htmltable-sort');
+ var sortSpan = new Element('span', {'html': ' ', 'class': this.options.classSortSpan}).inject(cell, 'top');
+ this.sortSpans.push(sortSpan);
+
+ var parser = parsers[index],
+ cancel;
+ switch ($type(parser)) {
+ case 'function': parser = {convert: parser}; cancel = true; break;
+ case 'string': parser = parser; cancel = true; break;
+ }
+ if (!cancel) {
+ HtmlTable.Parsers.some(function(current) {
+ var match = current.match;
+ if (!match) return false;
+ if (Browser.Engine.trident) return false;
+ for (var i = 0, j = rows.length; i < j; i++) {
+ var text = rows[i].cells[index].get('html').clean();
+ if (text && match.test(text)) {
+ parser = current;
+ return true;
+ }
+ }
+ });
+ }
+
+ if (!parser) parser = this.options.defaultParser;
+ cell.store('htmltable-parser', parser);
+ return parser;
+ }, this);
+ },
+
+ headClick: function(event, el) {
+ if (!this.head) return;
+ var index = Array.indexOf(this.head.cells, el);
+ this.sort(index);
+ return false;
+ },
+
+ sort: function(index, reverse, pre) {
+ if (!this.head) return;
+ pre = !!(pre);
+ var classCellSort = this.options.classCellSort;
+ var classGroup = this.options.classGroup,
+ classGroupHead = this.options.classGroupHead;
+
+ if (!pre) {
+ if (index != null) {
+ if (this.sorted.index == index) {
+ this.sorted.reverse = !(this.sorted.reverse);
+ } else {
+ if (this.sorted.index != null) {
+ this.sorted.reverse = false;
+ this.head.cells[this.sorted.index].removeClass(this.options.classHeadSort).removeClass(this.options.classHeadSortRev);
+ } else {
+ this.sorted.reverse = true;
+ }
+ this.sorted.index = index;
+ }
+ } else {
+ index = this.sorted.index;
+ }
+
+ if (reverse != null) this.sorted.reverse = reverse;
+
+ var head = document.id(this.head.cells[index]);
+ if (head) {
+ head.addClass(this.options.classHeadSort);
+ if (this.sorted.reverse) head.addClass(this.options.classHeadSortRev);
+ else head.removeClass(this.options.classHeadSortRev);
+ }
+
+ this.body.getElements('td').removeClass(this.options.classCellSort);
}
- if (this.shim) {
- this.shim.set({width: size.x, height: size.y}).position({
- relativeTo: this.element,
- offset: this.options.offset
- });
+
+ var parser = this.parsers[index];
+ if ($type(parser) == 'string') parser = HtmlTable.Parsers.get(parser);
+ if (!parser) return;
+
+ if (!Browser.Engine.trident) {
+ var rel = this.body.getParent();
+ this.body.dispose();
}
- return this;
+
+ var data = Array.map(this.body.rows, function(row, i) {
+ var value = parser.convert.call(document.id(row.cells[index]));
+
+ if (parser.number || $type(value) == 'number') {
+ value = String(value).replace(/[^\d]/, '');
+ value = '00000000000000000000000000000000'.substr(0, 32 - value.length).concat(value);
+ }
+
+ return {
+ position: i,
+ value: value,
+ toString: function() {
+ return value;
+ }
+ };
+ }, this);
+
+ data.reverse(true);
+ data.sort();
+
+ if (!this.sorted.reverse) data.reverse(true);
+
+ var i = data.length, body = this.body;
+ var j, position, entry, group;
+
+ while (i) {
+ var item = data[--i];
+ position = item.position;
+ var row = body.rows[position];
+ if (row.disabled) continue;
+
+ if (!pre) {
+ if (group === item.value) {
+ row.removeClass(classGroupHead).addClass(classGroup);
+ } else {
+ group = item.value;
+ row.removeClass(classGroup).addClass(classGroupHead);
+ }
+ if (this.zebra) this.zebra(row, i);
+
+ row.cells[index].addClass(classCellSort);
+ }
+
+ body.appendChild(row);
+ for (j = 0; j < i; j++) {
+ if (data[j].position > position) data[j].position--;
+ }
+ };
+ data = null;
+ if (rel) rel.grab(body);
+
+ return this.fireEvent('sort', [body, index]);
},
- hide: function(){
- if (this.shim) this.shim.setStyle('display', 'none');
+ reSort: function(){
+ if (this.sortEnabled) this.sort.call(this, this.sorted.index, this.sorted.reverse);
return this;
},
- show: function(){
- if (this.shim) this.shim.setStyle('display', 'block');
- return this.position();
- },
-
- dispose: function(){
- if (this.shim) this.shim.dispose();
+ enableSort: function(){
+ this.element.addClass(this.options.classSortable);
+ this.attachSorts(true);
+ this.detectParsers();
+ this.sortEnabled = true;
return this;
},
- destroy: function(){
- if (this.shim) this.shim.destroy();
+ disableSort: function(){
+ this.element.remove(this.options.classSortable);
+ this.attachSorts(false);
+ this.sortSpans.each(function(span) { span.destroy(); });
+ this.sortSpans.empty();
+ this.sortEnabled = false;
return this;
}
});
-window.addEvent('load', function(){
- IframeShim.ready = true;
+HtmlTable.Parsers = new Hash({
+
+ 'date': {
+ match: /^\d{4}[^\d]|[^\d]\d{4}$/,
+ convert: function() {
+ return Date.parse(this.get('text'));
+ },
+ type: 'date'
+ },
+ 'input-checked': {
+ match: / type="(radio|checkbox)" /,
+ convert: function() {
+ return this.getElement('input').checked;
+ }
+ },
+ 'input-value': {
+ match: /<input/,
+ convert: function() {
+ return this.getElement('input').value;
+ }
+ },
+ 'number': {
+ match: /^\d+[^\d.,]*$/,
+ convert: function() {
+ return this.get('text').toInt();
+ },
+ number: true
+ },
+ 'numberLax': {
+ match: /^[^\d]+\d+$/,
+ convert: function() {
+ return this.get('text').replace(/[^0-9]/, '').toInt();
+ },
+ number: true
+ },
+ 'float': {
+ match: /^[\d]+\.[\d]+/,
+ convert: function() {
+ return this.get('text').replace(/[^\d.]/, '').toFloat();
+ },
+ number: true
+ },
+ 'floatLax': {
+ match: /^[^\d]+[\d]+\.[\d]+$/,
+ convert: function() {
+ return this.get('text').replace(/[^\d.]/, '');
+ },
+ number: true
+ },
+ 'string': {
+ match: null,
+ convert: function() {
+ return this.get('text');
+ }
+ },
+ 'title': {
+ match: null,
+ convert: function() {
+ return this.title;
+ }
+ }
+
});/*
-Script: Scroller.js
- Class which scrolls the contents of any Element (including the window) when the mouse reaches the Element's boundaries.
+---
- License:
- MIT-style license.
+script: Keyboard.js
- Authors:
- Valerio Proietti
+description: KeyboardEvents used to intercept events on a class for keyboard and format modifiers in a specific order so as to make alt+shift+c the same as shift+alt+c.
+
+license: MIT-style license
+
+authors:
+- Perrin Westrich
+- Aaron Newton
+- Scott Kyle
+
+requires:
+- core:1.2.4/Events
+- core:1.2.4/Options
+- core:1.2.4/Element.Event
+- /Log
+
+provides: [Keyboard]
+
+...
*/
+(function(){
+
+ var parsed = {};
+ var modifiers = ['shift', 'control', 'alt', 'meta'];
+ var regex = /^(?:shift|control|ctrl|alt|meta)$/;
+
+ var parse = function(type, eventType){
+ type = type.toLowerCase().replace(/^(keyup|keydown):/, function($0, $1){
+ eventType = $1;
+ return '';
+ });
+
+ if (!parsed[type]){
+ var key = '', mods = {};
+ type.split('+').each(function(part){
+ if (regex.test(part)) mods[part] = true;
+ else key = part;
+ });
+
+ mods.control = mods.control || mods.ctrl; // allow both control and ctrl
+ var match = '';
+ modifiers.each(function(mod){
+ if (mods[mod]) match += mod + '+';
+ });
+
+ parsed[type] = match + key;
+ }
+
+ return eventType + ':' + parsed[type];
+ };
+
+ this.Keyboard = new Class({
+
+ Extends: Events,
+
+ Implements: [Options, Log],
+
+ options: {
+ /*
+ onActivate: $empty,
+ onDeactivate: $empty,
+ */
+ defaultEventType: 'keydown',
+ active: false,
+ events: {}
+ },
+
+ initialize: function(options){
+ this.setOptions(options);
+ //if this is the root manager, nothing manages it
+ if (Keyboard.manager) Keyboard.manager.manage(this);
+ this.setup();
+ },
+
+ setup: function(){
+ this.addEvents(this.options.events);
+ if (this.options.active) this.activate();
+ },
+
+ handle: function(event, type){
+ //Keyboard.stop(event) prevents key propagation
+ if (!this.active || event.preventKeyboardPropagation) return;
+
+ var bubbles = !!this.manager;
+ if (bubbles && this.activeKB){
+ this.activeKB.handle(event, type);
+ if (event.preventKeyboardPropagation) return;
+ }
+ this.fireEvent(type, event);
+
+ if (!bubbles && this.activeKB) this.activeKB.handle(event, type);
+ },
+
+ addEvent: function(type, fn, internal) {
+ return this.parent(parse(type, this.options.defaultEventType), fn, internal);
+ },
+
+ removeEvent: function(type, fn) {
+ return this.parent(parse(type, this.options.defaultEventType), fn);
+ },
+
+ activate: function(){
+ this.active = true;
+ return this.enable();
+ },
+
+ deactivate: function(){
+ this.active = false;
+ return this.fireEvent('deactivate');
+ },
+
+ toggleActive: function(){
+ return this[this.active ? 'deactivate' : 'activate']();
+ },
+
+ enable: function(instance){
+ if (instance) {
+ //if we're stealing focus, store the last keyboard to have it so the relenquish command works
+ if (instance != this.activeKB) this.previous = this.activeKB;
+ //if we're enabling a child, assign it so that events are now passed to it
+ this.activeKB = instance.fireEvent('activate');
+ } else if (this.manager) {
+ //else we're enabling ourselves, we must ask our parent to do it for us
+ this.manager.enable(this);
+ }
+ return this;
+ },
+
+ relenquish: function(){
+ if (this.previous) this.enable(this.previous);
+ },
+
+ //management logic
+ manage: function(instance) {
+ if (instance.manager) instance.manager.drop(instance);
+ this.instances.push(instance);
+ instance.manager = this;
+ if (!this.activeKB) this.enable(instance);
+ else this._disable(instance);
+ },
+
+ _disable: function(instance) {
+ if (this.activeKB == instance) this.activeKB = null;
+ },
+
+ drop: function(instance) {
+ this._disable(instance);
+ this.instances.erase(instance);
+ },
+
+ instances: [],
+
+ trace: function(){
+ this.enableLog();
+ var item = this;
+ this.log('the following items have focus: ');
+ while (item) {
+ this.log(document.id(item.widget) || item.widget || item, 'active: ' + this.active);
+ item = item.activeKB;
+ }
+ }
+
+ });
+
+ Keyboard.stop = function(event) {
+ event.preventKeyboardPropagation = true;
+ };
+
+ Keyboard.manager = new this.Keyboard({
+ active: true
+ });
+
+ Keyboard.trace = function(){
+ Keyboard.manager.trace();
+ };
+
+ var handler = function(event){
+ var mods = '';
+ modifiers.each(function(mod){
+ if (event[mod]) mods += mod + '+';
+ });
+ Keyboard.manager.handle(event, event.type + ':' + mods + event.key);
+ };
+
+ document.addEvents({
+ 'keyup': handler,
+ 'keydown': handler
+ });
+
+ Event.Keys.extend({
+ 'pageup': 33,
+ 'pagedown': 34,
+ 'end': 35,
+ 'home': 36,
+ 'capslock': 20,
+ 'numlock': 144,
+ 'scrolllock': 145
+ });
+
+})();
+/*
+---
+
+script: HtmlTable.Select.js
+
+description: Builds a stripy, sortable table with methods to add rows. Rows can be selected with the mouse or keyboard navigation.
+
+license: MIT-style license
+
+authors:
+- Harald Kirschner
+- Aaron Newton
+
+requires:
+- /Keyboard
+- /HtmlTable
+- /Class.refactor
+- /Element.Delegation
+
+provides: [HtmlTable.Select]
+
+...
+*/
+
+HtmlTable = Class.refactor(HtmlTable, {
+
+ options: {
+ /*onRowSelect: $empty,
+ onRowUnselect: $empty,*/
+ useKeyboard: true,
+ classRowSelected: 'table-tr-selected',
+ classRowHovered: 'table-tr-hovered',
+ classSelectable: 'table-selectable',
+ allowMultiSelect: true,
+ selectable: false
+ },
+
+ initialize: function(){
+ this.previous.apply(this, arguments);
+ if (this.occluded) return this.occluded;
+ this.selectedRows = new Elements();
+ this.bound = {
+ mouseleave: this.mouseleave.bind(this),
+ focusRow: this.focusRow.bind(this)
+ };
+ if (this.options.selectable) this.enableSelect();
+ },
+
+ enableSelect: function(){
+ this.selectEnabled = true;
+ this.attachSelects();
+ this.element.addClass(this.options.classSelectable);
+ },
+
+ disableSelect: function(){
+ this.selectEnabled = false;
+ this.attach(false);
+ this.element.removeClass(this.options.classSelectable);
+ },
+
+ attachSelects: function(attach){
+ attach = $pick(attach, true);
+ var method = attach ? 'addEvents' : 'removeEvents';
+ this.element[method]({
+ mouseleave: this.bound.mouseleave
+ });
+ this.body[method]({
+ 'click:relay(tr)': this.bound.focusRow
+ });
+ if (this.options.useKeyboard || this.keyboard){
+ if (!this.keyboard) this.keyboard = new Keyboard({
+ events: {
+ down: function(e) {
+ e.preventDefault();
+ this.shiftFocus(1);
+ }.bind(this),
+ up: function(e) {
+ e.preventDefault();
+ this.shiftFocus(-1);
+ }.bind(this),
+ enter: function(e) {
+ e.preventDefault();
+ if (this.hover) this.focusRow(this.hover);
+ }.bind(this)
+ },
+ active: true
+ });
+ this.keyboard[attach ? 'activate' : 'deactivate']();
+ }
+ this.updateSelects();
+ },
+
+ mouseleave: function(){
+ if (this.hover) this.leaveRow(this.hover);
+ },
+
+ focus: function(){
+ if (this.keyboard) this.keyboard.activate();
+ },
+
+ blur: function(){
+ if (this.keyboard) this.keyboard.deactivate();
+ },
+
+ push: function(){
+ var ret = this.previous.apply(this, arguments);
+ this.updateSelects();
+ return ret;
+ },
+
+ updateSelects: function(){
+ Array.each(this.body.rows, function(row){
+ var binders = row.retrieve('binders');
+ if ((binders && this.selectEnabled) || (!binders && !this.selectEnabled)) return;
+ if (!binders){
+ binders = {
+ mouseenter: this.enterRow.bind(this, [row]),
+ mouseleave: this.leaveRow.bind(this, [row])
+ };
+ row.store('binders', binders).addEvents(binders);
+ } else {
+ row.removeEvents(binders);
+ }
+ }, this);
+ },
+
+ enterRow: function(row){
+ if (this.hover) this.hover = this.leaveRow(this.hover);
+ this.hover = row.addClass(this.options.classRowHovered);
+ },
+
+ shiftFocus: function(offset){
+ if (!this.hover) return this.enterRow(this.body.rows[0]);
+ var to = Array.indexOf(this.body.rows, this.hover) + offset;
+ if (to < 0) to = 0;
+ if (to >= this.body.rows.length) to = this.body.rows.length - 1;
+ if (this.hover == this.body.rows[to]) return this;
+ this.enterRow(this.body.rows[to]);
+ },
+
+ leaveRow: function(row){
+ row.removeClass(this.options.classRowHovered);
+ },
+
+ focusRow: function(){
+ var row = arguments[1] || arguments[0]; //delegation passes the event first
+ var unfocus = function(row){
+ this.selectedRows.erase(row);
+ row.removeClass(this.options.classRowSelected);
+ this.fireEvent('rowUnfocus', [row, this.selectedRows]);
+ }.bind(this);
+ if (!this.options.allowMultiSelect) this.selectedRows.each(unfocus);
+ if (!this.selectedRows.contains(row)) {
+ this.selectedRows.push(row);
+ row.addClass(this.options.classRowSelected);
+ this.fireEvent('rowFocus', [row, this.selectedRows]);
+ } else {
+ unfocus(row);
+ }
+ return false;
+ },
+
+ selectAll: function(status){
+ status = $pick(status, true);
+ if (!this.options.allowMultiSelect && status) return;
+ if (!status) this.selectedRows.removeClass(this.options.classRowSelected).empty();
+ else this.selectedRows.combine(this.body.rows).addClass(this.options.classRowSelected);
+ return this;
+ },
+
+ selectNone: function(){
+ return this.selectAll(false);
+ }
+
+});/*
+---
+
+script: Scroller.js
+
+description: Class which scrolls the contents of any Element (including the window) when the mouse reaches the Element's boundaries.
+
+license: MIT-style license
+
+authors:
+- Valerio Proietti
+
+requires:
+- core:1.2.4/Events
+- core:1.2.4/Options
+- core:1.2.4/Element.Event
+- core:1.2.4/Element.Dimensions
+
+provides: [Scroller]
+
+...
+*/
+
var Scroller = new Class({
Implements: [Events, Options],
@@ -8981,8 +12177,8 @@
initialize: function(element, options){
this.setOptions(options);
- this.element = $(element);
- this.listener = ($type(this.element) != 'element') ? $(this.element.getDocument().body) : this.element;
+ this.element = document.id(element);
+ this.listener = ($type(this.element) != 'element') ? document.id(this.element.getDocument().body) : this.element;
this.timer = null;
this.bound = {
attach: this.attach.bind(this),
@@ -8993,16 +12189,17 @@
start: function(){
this.listener.addEvents({
- mouseenter: this.bound.attach,
- mouseleave: this.bound.detach
+ mouseover: this.bound.attach,
+ mouseout: this.bound.detach
});
},
stop: function(){
this.listener.removeEvents({
- mouseenter: this.bound.attach,
- mouseleave: this.bound.detach
+ mouseover: this.bound.attach,
+ mouseout: this.bound.detach
});
+ this.detach();
this.timer = $clear(this.timer);
},
@@ -9036,54 +12233,78 @@
}
});/*
-Script: Tips.js
- Class for creating nice tips that follow the mouse cursor when hovering an element.
+---
- License:
- MIT-style license.
+script: Tips.js
- Authors:
- Valerio Proietti
- Christoph Pojer
+description: Class for creating nice tips that follow the mouse cursor when hovering an element.
+
+license: MIT-style license
+
+authors:
+- Valerio Proietti
+- Christoph Pojer
+
+requires:
+- core:1.2.4/Options
+- core:1.2.4/Events
+- core:1.2.4/Element.Event
+- core:1.2.4/Element.Style
+- core:1.2.4/Element.Dimensions
+- /MooTools.More
+
+provides: [Tips]
+
+...
*/
-var Tips = new Class({
+(function(){
+var read = function(option, element){
+ return (option) ? ($type(option) == 'function' ? option(element) : element.get(option)) : '';
+};
+
+this.Tips = new Class({
+
Implements: [Events, Options],
options: {
- onShow: function(tip){
- tip.setStyle('visibility', 'visible');
+ /*
+ onAttach: $empty(element),
+ onDetach: $empty(element),
+ */
+ onShow: function(){
+ this.tip.setStyle('display', 'block');
},
- onHide: function(tip){
- tip.setStyle('visibility', 'hidden');
+ onHide: function(){
+ this.tip.setStyle('display', 'none');
},
title: 'title',
- text: function(el){
- return el.get('rel') || el.get('href');
+ text: function(element){
+ return element.get('rel') || element.get('href');
},
showDelay: 100,
hideDelay: 100,
- className: null,
+ className: 'tip-wrap',
offset: {x: 16, y: 16},
fixed: false
},
initialize: function(){
var params = Array.link(arguments, {options: Object.type, elements: $defined});
- if (params.options && params.options.offsets) params.options.offset = params.options.offsets;
this.setOptions(params.options);
- this.container = new Element('div', {'class': 'tip'});
- this.tip = this.getTip();
+ document.id(this);
if (params.elements) this.attach(params.elements);
},
- getTip: function(){
- return new Element('div', {
+ toElement: function(){
+ if (this.tip) return this.tip;
+
+ this.container = new Element('div', {'class': 'tip'});
+ return this.tip = new Element('div', {
'class': this.options.className,
styles: {
- visibility: 'hidden',
display: 'none',
position: 'absolute',
top: 0,
@@ -9097,20 +12318,22 @@
},
attach: function(elements){
- var read = function(option, element){
- if (option == null) return '';
- return $type(option) == 'function' ? option(element) : element.get(option);
- };
$$(elements).each(function(element){
- var title = read(this.options.title, element);
+ var title = read(this.options.title, element),
+ text = read(this.options.text, element);
+
element.erase('title').store('tip:native', title).retrieve('tip:title', title);
- element.retrieve('tip:text', read(this.options.text, element));
+ element.retrieve('tip:text', text);
+ this.fireEvent('attach', [element]);
var events = ['enter', 'leave'];
if (!this.options.fixed) events.push('move');
events.each(function(value){
- element.addEvent('mouse' + value, element.retrieve('tip:' + value, this['element' + value.capitalize()].bindWithEvent(this, element)));
+ var event = element.retrieve('tip:' + value);
+ if (!event) event = this['element' + value.capitalize()].bindWithEvent(this, element);
+
+ element.store('tip:' + value, event).addEvent('mouse' + value, event);
}, this);
}, this);
@@ -9120,12 +12343,12 @@
detach: function(elements){
$$(elements).each(function(element){
['enter', 'leave', 'move'].each(function(value){
- element.removeEvent('mouse' + value, element.retrieve('tip:' + value) || $empty);
+ element.removeEvent('mouse' + value, element.retrieve('tip:' + value)).eliminate('tip:' + value);
});
- element.eliminate('tip:enter').eliminate('tip:leave').eliminate('tip:move');
+ this.fireEvent('detach', [element]);
- if ($type(this.options.title) == 'string' && this.options.title == 'title'){
+ if (this.options.title == 'title'){ // This is necessary to check if we can revert the title
var original = element.retrieve('tip:native');
if (original) element.set('title', original);
}
@@ -9135,29 +12358,32 @@
},
elementEnter: function(event, element){
- $A(this.container.childNodes).each(Element.dispose);
+ this.container.empty();
['title', 'text'].each(function(value){
var content = element.retrieve('tip:' + value);
- if (!content) return;
-
- this[value + 'Element'] = new Element('div', {'class': 'tip-' + value}).inject(this.container);
- this.fill(this[value + 'Element'], content);
+ if (content) this.fill(new Element('div', {'class': 'tip-' + value}).inject(this.container), content);
}, this);
- this.timer = $clear(this.timer);
+ $clear(this.timer);
this.timer = this.show.delay(this.options.showDelay, this, element);
- this.tip.setStyle('display', 'block');
- this.position((!this.options.fixed) ? event : {page: element.getPosition()});
+ this.position((this.options.fixed) ? {page: element.getPosition()} : event);
},
elementLeave: function(event, element){
$clear(this.timer);
- this.tip.setStyle('display', 'none');
this.timer = this.hide.delay(this.options.hideDelay, this, element);
+ this.fireForParent(event, element);
},
- elementMove: function(event){
+ fireForParent: function(event, element) {
+ parentNode = element.getParent();
+ if (parentNode == document.body) return;
+ if (parentNode.retrieve('tip:enter')) parentNode.fireEvent('mouseenter', event);
+ else return this.fireForParent(parentNode, event);
+ },
+
+ elementMove: function(event, element){
this.position(event);
},
@@ -9180,105 +12406,1443 @@
else element.adopt(contents);
},
- show: function(el){
- this.fireEvent('show', [this.tip, el]);
+ show: function(element){
+ this.fireEvent('show', [element]);
},
- hide: function(el){
- this.fireEvent('hide', [this.tip, el]);
+ hide: function(element){
+ this.fireEvent('hide', [element]);
}
+});
+
+})();/*
+---
+
+script: Date.Catalan.US.js
+
+description: Date messages for Catalan.
+
+license: MIT-style license
+
+authors:
+- Alfons Sanchez
+
+requires:
+- /Lang
+- /Date
+
+provides: [Date.Catalan]
+
+...
+*/
+
+MooTools.lang.set('ca-CA', 'Date', {
+
+ months: ['Gener', 'Febrer', 'Març', 'Abril', 'Maig', 'Juny', 'Juli', 'Agost', 'Setembre', 'Octubre', 'Novembre', 'Desembre'],
+ days: ['Diumenge', 'Dilluns', 'Dimarts', 'Dimecres', 'Dijous', 'Divendres', 'Dissabte'],
+ //culture's date order: MM/DD/YYYY
+ dateOrder: ['date', 'month', 'year'],
+
+ shortDate: '%d/%m/%Y',
+ shortTime: '%H:%M',
+
+ AM: 'AM',
+ PM: 'PM',
+
+ /* Date.Extras */
+ ordinal: '',
+
+ lessThanMinuteAgo: 'fa menys d`un minut',
+ minuteAgo: 'fa un minut',
+ minutesAgo: 'fa {delta} minuts',
+ hourAgo: 'fa un hora',
+ hoursAgo: 'fa unes {delta} hores',
+ dayAgo: 'fa un dia',
+ daysAgo: 'fa {delta} dies',
+ lessThanMinuteUntil: 'menys d`un minut des d`ara',
+ minuteUntil: 'un minut des d`ara',
+ minutesUntil: '{delta} minuts des d`ara',
+ hourUntil: 'un hora des d`ara',
+ hoursUntil: 'unes {delta} hores des d`ara',
+ dayUntil: '1 dia des d`ara',
+ daysUntil: '{delta} dies des d`ara'
+
});/*
-Script: Date.English.US.js
- Date messages for US English.
+---
- License:
- MIT-style license.
+script: Date.Danish.js
- Authors:
- Aaron Newton
+description: Date messages for Danish.
+license: MIT-style license
+
+authors:
+- Martin Overgaard
+- Henrik Hansen
+
+requires:
+- /Lang
+- /Date
+
+provides: [Date.Danish]
+
+...
*/
+
+MooTools.lang.set('da-DK', 'Date', {
-MooTools.lang.set('en-US', 'Date', {
+ months: ['Januar', 'Februa', 'Marts', 'April', 'Maj', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'December'],
+ days: ['Søndag', 'Mandag', 'Tirsdag', 'Onsdag', 'Torsdag', 'Fredag', 'Lørdag'],
+ //culture's date order: DD/MM/YYYY
+ dateOrder: ['date', 'month', 'year'],
- months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
- days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
- //culture's date order: MM/DD/YYYY
+ AM: 'AM',
+ PM: 'PM',
+
+ shortDate: '%d-%m-%Y',
+ shortTime: '%H:%M',
+
+ /* Date.Extras */
+ ordinal: function(dayOfMonth){
+ //1st, 2nd, 3rd, etc.
+ return (dayOfMonth > 3 && dayOfMonth < 21) ? 'th' : ['th', 'st', 'nd', 'rd', 'th'][Math.min(dayOfMonth % 10, 4)];
+ },
+
+ lessThanMinuteAgo: 'mindre end et minut siden',
+ minuteAgo: 'omkring et minut siden',
+ minutesAgo: '{delta} minutter siden',
+ hourAgo: 'omkring en time siden',
+ hoursAgo: 'omkring {delta} timer siden',
+ dayAgo: '1 dag siden',
+ daysAgo: '{delta} dage siden',
+ weekAgo: '1 uge siden',
+ weeksAgo: '{delta} uger siden',
+ monthAgo: '1 måned siden',
+ monthsAgo: '{delta} måneder siden',
+ yearthAgo: '1 år siden',
+ yearsAgo: '{delta} år siden',
+ lessThanMinuteUntil: 'mindre end et minut fra nu',
+ minuteUntil: 'omkring et minut fra nu',
+ minutesUntil: '{delta} minutter fra nu',
+ hourUntil: 'omkring en time fra nu',
+ hoursUntil: 'omkring {delta} timer fra nu',
+ dayUntil: '1 dag fra nu',
+ daysUntil: '{delta} dage fra nu',
+ weekUntil: '1 uge fra nu',
+ weeksUntil: '{delta} uger fra nu',
+ monthUntil: '1 måned fra nu',
+ monthsUntil: '{delta} måneder fra nu',
+ yearUntil: '1 år fra nu',
+ yearsUntil: '{delta} år fra nu'
+
+});
+/*
+---
+
+script: Date.Dutch.js
+
+description: Date messages in Dutch.
+
+license: MIT-style license
+
+authors:
+- Lennart Pilon
+
+requires:
+- /Lang
+- /Date
+
+provides: [Date.Dutch]
+
+...
+*/
+
+MooTools.lang.set('nl-NL', 'Date', {
+
+ months: ['Januari', 'Februari', 'Maart', 'April', 'Mei', 'Juni', 'Juli', 'Augustus', 'September', 'Oktober', 'November', 'December'],
+ days: ['Zondag', 'Maandag', 'Dinsdag', 'Woensdag', 'Donderdag', 'Vrijdag', 'Zaterdag'],
+ //culture's date order: DD/MM/YYYY
+ dateOrder: ['date', 'month', 'year'],
+
+ AM: 'AM',
+ PM: 'PM',
+
+ shortDate: '%d/%m/%Y',
+ shortTime: '%H:%M',
+
+ /* Date.Extras */
+ ordinal: 'e',
+
+ lessThanMinuteAgo: 'minder dan een minuut geleden',
+ minuteAgo: 'ongeveer een minuut geleden',
+ minutesAgo: 'minuten geleden',
+ hourAgo: 'ongeveer een uur geleden',
+ hoursAgo: 'ongeveer {delta} uur geleden',
+ dayAgo: '{delta} dag geleden',
+ daysAgo: 'dagen geleden',
+ weekAgo: 'een week geleden',
+ weeksAgo: '{delta} weken geleden',
+ monthAgo: 'een maand geleden',
+ monthsAgo: '{delta} maanden geleden',
+ yearAgo: 'een jaar geleden',
+ yearsAgo: '{delta} jaar geleden',
+ lessThanMinuteUntil: 'minder dan een minuut vanaf nu',
+ minuteUntil: 'ongeveer een minuut vanaf nu',
+ minutesUntil: '{delta} minuten vanaf nu',
+ hourUntil: 'ongeveer een uur vanaf nu',
+ hoursUntil: 'ongeveer {delta} uur vanaf nu',
+ dayUntil: '1 dag vanaf nu',
+ daysUntil: '{delta} dagen vanaf nu',
+ weekAgo: 'een week geleden',
+ weeksAgo: '{delta} weken geleden',
+ monthAgo: 'een maand geleden',
+ monthsAgo: '{delta} maanden geleden',
+ yearthAgo: 'een jaar geleden',
+ yearsAgo: '{delta} jaar geleden',
+
+ weekUntil: 'over een week',
+ weeksUntil: 'over {delta} weken',
+ monthUntil: 'over een maand',
+ monthsUntil: 'over {delta} maanden',
+ yearUntil: 'over een jaar',
+ yearsUntil: 'over {delta} jaar'
+
+});/*
+---
+
+script: Date.English.GB.js
+
+description: Date messages for British English.
+
+license: MIT-style license
+
+authors:
+- Aaron Newton
+
+requires:
+- /Lang
+- /Date
+
+provides: [Date.English.GB]
+
+...
+*/
+
+MooTools.lang.set('en-GB', 'Date', {
+
+ dateOrder: ['date', 'month', 'year'],
+
+ shortDate: '%d/%m/%Y',
+ shortTime: '%H:%M'
+
+}).set('cascade', ['en-US']);/*
+---
+
+script: Date.Estonian.js
+
+description: Date messages for Estonian.
+
+license: MIT-style license
+
+authors:
+- Kevin Valdek
+
+requires:
+- /Lang
+- /Date
+
+provides: [Date.Estonian]
+
+...
+*/
+
+MooTools.lang.set('et-EE', 'Date', {
+
+ months: ['jaanuar', 'veebruar', 'märts', 'aprill', 'mai', 'juuni', 'juuli', 'august', 'september', 'oktoober', 'november', 'detsember'],
+ days: ['pühapäev', 'esmaspäev', 'teisipäev', 'kolmapäev', 'neljapäev', 'reede', 'laupäev'],
+ //culture's date order: MM.DD.YYYY
dateOrder: ['month', 'date', 'year'],
- shortDate: '%m/%d/%Y',
- shortTime: '%I:%M%p',
+
AM: 'AM',
PM: 'PM',
+ shortDate: '%m.%d.%Y',
+ shortTime: '%H:%M',
+
/* Date.Extras */
+ ordinal: '',
+
+ lessThanMinuteAgo: 'vähem kui minut aega tagasi',
+ minuteAgo: 'umbes minut aega tagasi',
+ minutesAgo: '{delta} minutit tagasi',
+ hourAgo: 'umbes tund aega tagasi',
+ hoursAgo: 'umbes {delta} tundi tagasi',
+ dayAgo: '1 päev tagasi',
+ daysAgo: '{delta} päeva tagasi',
+ weekAgo: '1 nädal tagasi',
+ weeksAgo: '{delta} nädalat tagasi',
+ monthAgo: '1 kuu tagasi',
+ monthsAgo: '{delta} kuud tagasi',
+ yearAgo: '1 aasta tagasi',
+ yearsAgo: '{delta} aastat tagasi',
+ lessThanMinuteUntil: 'vähem kui minuti aja pärast',
+ minuteUntil: 'umbes minuti aja pärast',
+ minutesUntil: '{delta} minuti pärast',
+ hourUntil: 'umbes tunni aja pärast',
+ hoursUntil: 'umbes {delta} tunni pärast',
+ dayUntil: '1 päeva pärast',
+ daysUntil: '{delta} päeva pärast',
+ weekUntil: '1 nädala pärast',
+ weeksUntil: '{delta} nädala pärast',
+ monthUntil: '1 kuu pärast',
+ monthsUntil: '{delta} kuu pärast',
+ yearUntil: '1 aasta pärast',
+ yearsUntil: '{delta} aasta pärast'
+
+});/*
+---
+
+script: Date.French.js
+
+description: Date messages in French.
+
+license: MIT-style license
+
+authors:
+- Nicolas Sorosac
+- Antoine Abt
+
+requires:
+- /Lang
+- /Date
+
+provides: [Date.French]
+
+...
+*/
+
+MooTools.lang.set('fr-FR', 'Date', {
+
+ months: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'],
+ days: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'],
+ dateOrder: ['date', 'month', 'year'],
+
+ AM: 'AM',
+ PM: 'PM',
+
+ shortDate: '%d/%m/%Y',
+ shortTime: '%H:%M',
+
+ /* Date.Extras */
+ getOrdinal: function(dayOfMonth){
+ return (dayOfMonth > 1) ? '' : 'er';
+ },
+
+ lessThanMinuteAgo: 'il y a moins d\'une minute',
+ minuteAgo: 'il y a une minute',
+ minutesAgo: 'il y a {delta} minutes',
+ hourAgo: 'il y a une heure',
+ hoursAgo: 'il y a {delta} heures',
+ dayAgo: 'il y a un jour',
+ daysAgo: 'il y a {delta} jours',
+ weekAgo: 'il y a une semaine',
+ weeksAgo: 'il y a {delta} semaines',
+ monthAgo: 'il y a 1 mois',
+ monthsAgo: 'il y a {delta} mois',
+ yearthAgo: 'il y a 1 an',
+ yearsAgo: 'il y a {delta} ans',
+ lessThanMinuteUntil: 'dans moins d\'une minute',
+ minuteUntil: 'dans une minute',
+ minutesUntil: 'dans {delta} minutes',
+ hourUntil: 'dans une heure',
+ hoursUntil: 'dans {delta} heures',
+ dayUntil: 'dans un jour',
+ daysUntil: 'dans {delta} jours',
+ weekUntil: 'dans 1 semaine',
+ weeksUntil: 'dans {delta} semaines',
+ monthUntil: 'dans 1 mois',
+ monthsUntil: 'dans {delta} mois',
+ yearUntil: 'dans 1 an',
+ yearsUntil: 'dans {delta} ans'
+
+});
+/*
+---
+
+script: Date.Italian.js
+
+description: Date messages for Italian.
+
+license: MIT-style license.
+
+authors:
+- Andrea Novero
+- Valerio Proietti
+
+requires:
+- /Lang
+- /Date
+
+provides: [Date.Italian]
+
+...
+*/
+
+MooTools.lang.set('it-IT', 'Date', {
+
+ months: ['Gennaio', 'Febbraio', 'Marzo', 'Aprile', 'Maggio', 'Giugno', 'Luglio', 'Agosto', 'Settembre', 'Ottobre', 'Novembre', 'Dicembre'],
+ days: ['Domenica', 'Lunedì', 'Martedì', 'Mercoledì', 'Giovedì', 'Venerdì', 'Sabato'],
+ //culture's date order: DD/MM/YYYY
+ dateOrder: ['date', 'month', 'year'],
+
+ AM: 'AM',
+ PM: 'PM',
+
+ shortDate: '%d/%m/%Y',
+ shortTime: '%H.%M',
+
+ /* Date.Extras */
+ ordinal: 'º',
+
+ lessThanMinuteAgo: 'meno di un minuto fa',
+ minuteAgo: 'circa un minuto fa',
+ minutesAgo: 'circa {delta} minuti fa',
+ hourAgo: 'circa un\'ora fa',
+ hoursAgo: 'circa {delta} ore fa',
+ dayAgo: 'circa 1 giorno fa',
+ daysAgo: 'circa {delta} giorni fa',
+ lessThanMinuteUntil: 'tra meno di un minuto',
+ minuteUntil: 'tra circa un minuto',
+ minutesUntil: 'tra circa {delta} minuti',
+ hourUntil: 'tra circa un\'ora',
+ hoursUntil: 'tra circa {delta} ore',
+ dayUntil: 'tra circa un giorno',
+ daysUntil: 'tra circa {delta} giorni'
+
+});/*
+---
+
+script: Date.Norwegian.js
+
+description: Date messages in Norwegian.
+
+license: MIT-style license
+
+authors:
+- Espen 'Rexxars' Hovlandsdal
+
+requires:
+- /Lang
+- /Date
+
+provides: [Date.Norwegian]
+
+...
+*/
+
+MooTools.lang.set('no-NO', 'Date', {
+
+ dateOrder: ['date', 'month', 'year'],
+
+ shortDate: '%d.%m.%Y',
+ shortTime: '%H:%M',
+
+ lessThanMinuteAgo: 'kortere enn et minutt siden',
+ minuteAgo: 'omtrent et minutt siden',
+ minutesAgo: '{delta} minutter siden',
+ hourAgo: 'omtrent en time siden',
+ hoursAgo: 'omtrent {delta} timer siden',
+ dayAgo: '{delta} dag siden',
+ daysAgo: '{delta} dager siden'
+
+});/*
+---
+
+script: Date.Polish.js
+
+description: Date messages for Polish.
+
+license: MIT-style license
+
+authors:
+- Oskar Krawczyk
+
+requires:
+- /Lang
+- /Date
+
+provides: [Date.Polish]
+
+...
+*/
+
+MooTools.lang.set('pl-PL', 'Date', {
+ months: ['StyczeÅ', 'Luty', 'Marzec', 'KwiecieÅ', 'Maj', 'Czerwiec', 'Lipiec', 'SierpieÅ', 'WrzesieÅ', 'Październik', 'Listopad', 'GrudzieÅ'],
+ days: ['Niedziela', 'PoniedziaÅek', 'Wtorek', 'Åroda', 'Czwartek', 'PiÄ
tek', 'Sobota'],
+ dateOrder: ['year', 'month', 'date'],
+ AM: 'nad ranem',
+ PM: 'po poÅudniu',
+
+ shortDate: '%Y-%m-%d',
+ shortTime: '%H:%M',
+
+ /* Date.Extras */
ordinal: function(dayOfMonth){
- //1st, 2nd, 3rd, etc.
- return (dayOfMonth > 3 && dayOfMonth < 21) ? 'th' : ['th', 'st', 'nd', 'rd', 'th'][Math.min(dayOfMonth % 10, 4)];
+ return (dayOfMonth > 3 && dayOfMonth < 21) ? 'ty' : ['ty', 'szy', 'gi', 'ci', 'ty'][Math.min(dayOfMonth % 10, 4)];
},
- lessThanMinuteAgo: 'less than a minute ago',
- minuteAgo: 'about a minute ago',
- minutesAgo: '{delta} minutes ago',
- hourAgo: 'about an hour ago',
- hoursAgo: 'about {delta} hours ago',
- dayAgo: '1 day ago',
- daysAgo: '{delta} days ago',
- lessThanMinuteUntil: 'less than a minute from now',
- minuteUntil: 'about a minute from now',
- minutesUntil: '{delta} minutes from now',
- hourUntil: 'about an hour from now',
- hoursUntil: 'about {delta} hours from now',
- dayUntil: '1 day from now',
- daysUntil: '{delta} days from now'
+ lessThanMinuteAgo: 'mniej niż minute temu',
+ minuteAgo: 'okoÅo minutÄ temu',
+ minutesAgo: '{delta} minut temu',
+ hourAgo: 'okoÅo godzinÄ temu',
+ hoursAgo: 'okoÅo {delta} godzin temu',
+ dayAgo: 'Wczoraj',
+ daysAgo: '{delta} dni temu',
+ lessThanMinuteUntil: 'za niecaÅÄ
minutÄ',
+ minuteUntil: 'za okoÅo minutÄ',
+ minutesUntil: 'za {delta} minut',
+ hourUntil: 'za okoÅo godzinÄ',
+ hoursUntil: 'za okoÅo {delta} godzin',
+ dayUntil: 'za 1 dzieÅ',
+ daysUntil: 'za {delta} dni'
+});/*
+---
+script: Date.Portuguese.BR.js
+
+description: Date messages in Portuguese-BR (Brazil).
+
+license: MIT-style license
+
+authors:
+- Fabio Miranda Costa
+
+requires:
+- /Lang
+- /Date
+
+provides: [Date.Portuguese.BR]
+
+...
+*/
+
+MooTools.lang.set('pt-BR', 'Date', {
+
+ months: ['Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho', 'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro'],
+ days: ['Domingo', 'Segunda-feira', 'Terça-feira', 'Quarta-feira', 'Quinta-feira', 'Sexta-feira', 'Sábado'],
+ //culture's date order: DD/MM/YYYY
+ dateOrder: ['date', 'month', 'year'],
+ shortDate: '%d/%m/%Y',
+ shortTime: '%H:%M',
+
+ /* Date.Extras */
+ ordinal: function(dayOfMonth){
+ //1º, 2º, 3º, etc.
+ return 'º';
+ },
+
+ lessThanMinuteAgo: 'há menos de um minuto',
+ minuteAgo: 'há cerca de um minuto',
+ minutesAgo: 'há {delta} minutos',
+ hourAgo: 'há cerca de uma hora',
+ hoursAgo: 'há cerca de {delta} horas',
+ dayAgo: 'há um dia',
+ daysAgo: 'há {delta} dias',
+ weekAgo: 'há uma semana',
+ weeksAgo: 'há {delta} semanas',
+ monthAgo: 'há um mês',
+ monthsAgo: 'há {delta} meses',
+ yearAgo: 'há um ano',
+ yearsAgo: 'há {delta} anos',
+ lessThanMinuteUntil: 'em menos de um minuto',
+ minuteUntil: 'em um minuto',
+ minutesUntil: 'em {delta} minutos',
+ hourUntil: 'em uma hora',
+ hoursUntil: 'em {delta} horas',
+ dayUntil: 'em um dia',
+ daysUntil: 'em {delta} dias',
+ weekUntil: 'em uma semana',
+ weeksUntil: 'em {delta} semanas',
+ monthUntil: 'em um mês',
+ monthsUntil: 'em {delta} meses',
+ yearUntil: 'em um ano',
+ yearsUntil: 'em {delta} anos'
+
});/*
-Script: FormValidator.English.js
- Date messages for English.
+---
- License:
- MIT-style license.
+script: Date.Spanish.US.js
- Authors:
- Aaron Newton
+description: Date messages for Spanish.
+license: MIT-style license
+
+authors:
+- Ãlfons Sanchez
+
+requires:
+- /Lang
+- /Date
+
+provides: [Date.Spanish]
+
+...
*/
-MooTools.lang.set('en-US', 'FormValidator', {
+MooTools.lang.set('es-ES', 'Date', {
- required:'This field is required.',
- minLength:'Please enter at least {minLength} characters (you entered {length} characters).',
- maxLength:'Please enter no more than {maxLength} characters (you entered {length} characters).',
- integer:'Please enter an integer in this field. Numbers with decimals (e.g. 1.25) are not permitted.',
- numeric:'Please enter only numeric values in this field (i.e. "1" or "1.1" or "-1" or "-1.1").',
- digits:'Please use numbers and punctuation only in this field (for example, a phone number with dashes or dots is permitted).',
- alpha:'Please use letters only (a-z) with in this field. No spaces or other characters are allowed.',
- alphanum:'Please use only letters (a-z) or numbers (0-9) only in this field. No spaces or other characters are allowed.',
- dateSuchAs:'Please enter a valid date such as {date}',
- dateInFormatMDY:'Please enter a valid date such as MM/DD/YYYY (i.e. "12/31/1999")',
- email:'Please enter a valid email address. For example "fred at domain.com".',
- url:'Please enter a valid URL such as http://www.google.com.',
- currencyDollar:'Please enter a valid $ amount. For example $100.00 .',
- oneRequired:'Please enter something for at least one of these inputs.',
+ months: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'],
+ days: ['Domingo', 'Lunes', 'Martes', 'Miercoles', 'Jueves', 'Viernes', 'Sabado'],
+ //culture's date order: MM/DD/YYYY
+ dateOrder: ['date', 'month', 'year'],
+ AM: 'AM',
+ PM: 'PM',
+
+ shortDate: '%d/%m/%Y',
+ shortTime: '%H:%M',
+
+ /* Date.Extras */
+ ordinal: '',
+
+ lessThanMinuteAgo: 'hace menos de un minuto',
+ minuteAgo: 'hace un minuto',
+ minutesAgo: 'hace {delta} minutos',
+ hourAgo: 'hace una hora',
+ hoursAgo: 'hace unas {delta} horas',
+ dayAgo: 'hace un dia',
+ daysAgo: 'hace {delta} dias',
+ weekAgo: 'hace una semana',
+ weeksAgo: 'hace unas {delta} semanas',
+ monthAgo: 'hace un mes',
+ monthsAgo: 'hace {delta} meses',
+ yearAgo: 'hace un año',
+ yearsAgo: 'hace {delta} años',
+ lessThanMinuteUntil: 'menos de un minuto desde ahora',
+ minuteUntil: 'un minuto desde ahora',
+ minutesUntil: '{delta} minutos desde ahora',
+ hourUntil: 'una hora desde ahora',
+ hoursUntil: 'unas {delta} horas desde ahora',
+ dayUntil: 'un dia desde ahora',
+ daysUntil: '{delta} dias desde ahora',
+ weekUntil: 'una semana desde ahora',
+ weeksUntil: 'unas {delta} semanas desde ahora',
+ monthUntil: 'un mes desde ahora',
+ monthsUntil: '{delta} meses desde ahora',
+ yearUntil: 'un año desde ahora',
+ yearsUntil: '{delta} años desde ahora'
+
+});/*
+---
+
+script: Date.Swedish.js
+
+description: Date messages for Swedish (SE).
+
+license: MIT-style license
+
+authors:
+- Martin Lundgren
+
+requires:
+- /Lang
+- /Date
+
+provides: [Date.Swedish]
+
+...
+*/
+
+MooTools.lang.set('sv-SE', 'Date', {
+
+ months: ['januari', 'februari', 'mars', 'april', 'maj', 'juni', 'juli', 'augusti', 'september', 'oktober', 'november', 'december'],
+ days: ['söndag', 'måndag', 'tisdag', 'onsdag', 'torsdag', 'fredag', 'lördag'],
+ // culture's date order: YYYY-MM-DD
+ dateOrder: ['year', 'month', 'date'],
+ AM: '',
+ PM: '',
+
+ shortDate: '%Y-%m-%d',
+ shortTime: '%H:%M',
+
+ /* Date.Extras */
+ ordinal: function(dayOfMonth){
+ // Not used in Swedish
+ return '';
+ },
+
+ lessThanMinuteAgo: 'mindre än en minut sedan',
+ minuteAgo: 'ungefär en minut sedan',
+ minutesAgo: '{delta} minuter sedan',
+ hourAgo: 'ungefär en timme sedan',
+ hoursAgo: 'ungefär {delta} timmar sedan',
+ dayAgo: '1 dag sedan',
+ daysAgo: '{delta} dagar sedan',
+ lessThanMinuteUntil: 'mindre än en minut sedan',
+ minuteUntil: 'ungefär en minut sedan',
+ minutesUntil: '{delta} minuter sedan',
+ hourUntil: 'ungefär en timme sedan',
+ hoursUntil: 'ungefär {delta} timmar sedan',
+ dayUntil: '1 dag sedan',
+ daysUntil: '{delta} dagar sedan'
+
+});/*
+---
+
+script: Form.Validator.Arabic.js
+
+description: Form.Validator messages in Arabic.
+
+license: MIT-style license
+
+authors:
+- Chafik Barbar
+
+requires:
+- /Lang
+- /Form.Validator
+
+provides: [Form.Validator.Arabic]
+
+...
+*/
+
+MooTools.lang.set('ar', 'Form.Validator', {
+ required:'Ùذا اÙØÙÙ Ù
Ø·ÙÙب.',
+ minLength:'رجاء٠إدخا٠{minLength} Ø£Øر٠عÙ٠اÙØ£ÙÙ (تÙ
إدخا٠{length} Ø£ØرÙ).',
+ maxLength:'اÙرجاء عدÙ
إدخا٠أÙثر Ù
Ù {maxLength} Ø£Øر٠(تÙ
إدخا٠{length} Ø£ØرÙ).',
+ integer:'اÙرجاء إدخا٠عدد صØÙØ ÙÙ Ùذا اÙØÙÙ. أ٠رÙÙ
Ø°Ù Ùسر عشر٠أ٠Ù
ئÙÙ (Ù
ثا٠1.25 ) غÙر Ù
سÙ
ÙØ.',
+ numeric:'اÙرجاء إدخا٠ÙÙÙ
رÙÙ
ÙØ© ÙÙ Ùذا اÙØÙÙ (Ù
ثا٠"1" أ٠"1.1" أ٠"-1" أ٠"-1.1").',
+ digits:'اÙرجاء أستخداÙ
ÙÙÙ
رÙÙ
ÙØ© ÙعÙاÙ
ات ترÙÙÙ
ÙØ© ÙÙØ· ÙÙ Ùذا اÙØÙÙ (Ù
ثاÙ, رÙÙ
Ùات٠Ù
ع ÙÙطة Ø£Ù Ø´Øطة)',
+ alpha:'اÙرجاء أستخداÙ
Ø£Øر٠ÙÙØ· (ا-Ù) ÙÙ Ùذا اÙØÙÙ. Ø£Ù Ùراغات أ٠عÙاÙ
ات غÙر Ù
سÙ
ÙØØ©.',
+ alphanum:'اÙرجاء أستخداÙ
Ø£Øر٠ÙÙØ· (ا-Ù) أ٠أرÙاÙ
(0-9) ÙÙØ· ÙÙ Ùذا اÙØÙÙ. Ø£Ù Ùراغات أ٠عÙاÙ
ات غÙر Ù
سÙ
ÙØØ©.',
+ dateSuchAs:'اÙرجاء إدخا٠تارÙØ® صØÙØ ÙاÙتاÙÙ {date}',
+ dateInFormatMDY:'اÙرجاء إدخا٠تارÙØ® صØÙØ (Ù
ثاÙ, 31-12-1999)',
+ email:'اÙرجاء إدخا٠برÙد Ø¥ÙÙترÙÙ٠صØÙØ.',
+ url:'اÙرجاء إدخا٠عÙÙا٠إÙÙترÙÙ٠صØÙØ Ù
Ø«Ù http://www.google.com',
+ currencyDollar:'اÙرجاء إدخا٠ÙÙÙ
Ø© $ صØÙØØ©. Ù
ثاÙ, 100.00$',
+ oneRequired:'اÙرجاء إدخا٠ÙÙÙ
Ø© Ù٠أØد Ùذ٠اÙØÙÙ٠عÙ٠اÙØ£ÙÙ.',
+ errorPrefix: 'خطأ: ',
+ warningPrefix: 'تØØ°Ùر: '
+}).set('ar', 'Date', {
+ dateOrder: ['date', 'month', 'year', '/']
+});/*
+---
+
+script: Form.Validator.Catalan.js
+
+description: Date messages for Catalan.
+
+license: MIT-style license
+
+authors:
+- Miquel Hudin
+- Alfons Sanchez
+
+requires:
+- /Lang
+- /Form.Validator
+
+provides: [Form.Validator.Catalan]
+
+...
+*/
+
+MooTools.lang.set('ca-CA', 'Form.Validator', {
+
+ required:'Aquest camp es obligatori.',
+ minLength:'Per favor introdueix al menys {minLength} caracters (has introduit {length} caracters).',
+ maxLength:'Per favor introdueix no mes de {maxLength} caracters (has introduit {length} caracters).',
+ integer:'Per favor introdueix un nombre enter en aquest camp. Nombres amb decimals (p.e. 1,25) no estan permesos.',
+ numeric:'Per favor introdueix sols valors numerics en aquest camp (p.e. "1" o "1,1" o "-1" o "-1,1").',
+ digits:'Per favor usa sols numeros i puntuacio en aquest camp (per exemple, un nombre de telefon amb guions i punts no esta permes).',
+ alpha:'Per favor utilitza lletres nomes (a-z) en aquest camp. No s´admiteixen espais ni altres caracters.',
+ alphanum:'Per favor, utilitza nomes lletres (a-z) o numeros (0-9) en aquest camp. No s´admiteixen espais ni altres caracters.',
+ dateSuchAs:'Per favor introdueix una data valida com {date}',
+ dateInFormatMDY:'Per favor introdueix una data valida com DD/MM/YYYY (p.e. "31/12/1999")',
+ email:'Per favor, introdueix una adreça de correu electronic valida. Per exemple, "fred at domain.com".',
+ url:'Per favor introdueix una URL valida com http://www.google.com.',
+ currencyDollar:'Per favor introdueix una quantitat valida de â¬. Per exemple â¬100,00 .',
+ oneRequired:'Per favor introdueix alguna cosa per al menys una d´aquestes entrades.',
errorPrefix: 'Error: ',
- warningPrefix: 'Warning: ',
+ warningPrefix: 'Avis: ',
- //FormValidator.Extras
+ //Form.Validator.Extras
- noSpace: 'There can be no spaces in this input.',
- reqChkByNode: 'No items are selected.',
- requiredChk: 'This field is required.',
- reqChkByName: 'Please select a {label}.',
- match: 'This field needs to match the {matchName} field',
- startDate: 'the start date',
- endDate: 'the end date',
- currendDate: 'the current date',
- afterDate: 'The date should be the same or after {label}.',
- beforeDate: 'The date should be the same or before {label}.',
- startMonth: 'Please select a start month',
- sameMonth: 'These two dates must be in the same month - you must change one or the other.'
+ noSpace: 'No poden haver espais en aquesta entrada.',
+ reqChkByNode: 'No hi han elements seleccionats.',
+ requiredChk: 'Aquest camp es obligatori.',
+ reqChkByName: 'Per favor selecciona una {label}.',
+ match: 'Aquest camp necessita coincidir amb el camp {matchName}',
+ startDate: 'la data de inici',
+ endDate: 'la data de fi',
+ currendDate: 'la data actual',
+ afterDate: 'La data deu ser igual o posterior a {label}.',
+ beforeDate: 'La data deu ser igual o anterior a {label}.',
+ startMonth: 'Per favor selecciona un mes d´orige',
+ sameMonth: 'Aquestes dos dates deuen estar dins del mateix mes - deus canviar una o altra.'
-});// $Id: common.js 478 2009-07-10 14:09:58Z pagameba $
+});/*
+---
+
+script: Form.Validator.Chinese.js
+
+description: Form.Validator messages in chinese (both simplified and traditional).
+
+license: MIT-style license
+
+authors:
+- éæ¡å - guidy <at> ixuer [dot] net
+
+requires:
+- /Lang
+- /Form.Validator
+
+provides: [Form.Validator.Chinese]
+
+...
+*/
+
+/*
+In Chinese:
+------------
+éè¦æåºçæ¯ï¼
+ç®ä½ä¸æéç¨äºä¸å½å¤§éï¼
+ç¹ä½ä¸æéç¨äºé¦æ¸¯ãæ¾³é¨åå°æ¹¾çã
+ç®ä½ä¸æåç¹ä½ä¸æå¨åä½åè¯æ³ä¸æå¾å¤çä¸åä¹å¤ã
+
+æå¯ä»¥ç¡®ä¿ç®ä½ä¸æè¯è¨å
çåç¡®æ§ï¼
+ä½å¯¹äºç¹ä½ä¸æï¼æå¯ä»¥ä¿è¯ç¨æ·å¯ä»¥åç¡®çç解ï¼ä½æ æ³ä¿è¯è¯å¥ç¬¦åä»ä»¬çé
è¯»ä¹ æ¯ã
+å¦ææ¨ä¸è½ç¡®è®¤çè¯ï¼å¯ä»¥åªä½¿ç¨ç®ä½ä¸æè¯è¨å
ï¼å 为å®æ¯æéç¨çã
+
+In English:
+------------
+It should be noted that:
+Simplified Chinese apply to mainland Chinese,
+Traditional Chinese apply to Hong Kong, Macao and Taiwan Province.
+There are a lot of different from Simplified Chinese and Traditional Chinese , Contains font and syntax .
+
+I can assure Simplified Chinese language pack accuracy .
+For Traditional Chinese, I can only guarantee that users can understand, but not necessarily in line with their reading habits.
+If you are unsure, you can only use the simplified Chinese language pack, as it is the most common.
+
+*/
+
+// Simplified Chinese
+MooTools.lang.set('zhs-CN', 'Form.Validator', {
+ required:'è¿æ¯å¿
填项ã',
+ minLength:'请è³å°è¾å
¥ {minLength} 个å符 (å·²è¾å
¥ {length} 个)ã',
+ maxLength:'æå¤åªè½è¾å
¥ {maxLength} 个å符 (å·²è¾å
¥ {length} 个)ã',
+ integer:'请è¾å
¥ä¸ä¸ªæ´æ°ï¼ä¸è½å
å«å°æ°ç¹ãä¾å¦ï¼"1", "200"ã',
+ numeric:'请è¾å
¥ä¸ä¸ªæ°åï¼ä¾å¦ï¼"1", "1.1", "-1", "-1.1"ã',
+ digits:'è¿éåªè½æ¥åæ°ååæ ç¹çè¾å
¥ï¼æ ç¹å¯ä»¥æ¯ï¼"(", ")", ".", ":", "-", "+", "#"åç©ºæ ¼ã',
+ alpha:'请è¾å
¥ A-Z ç 26 个åæ¯ï¼ä¸è½å
å«ç©ºæ ¼æä»»ä½å
¶ä»å符ã',
+ alphanum:'请è¾å
¥ A-Z ç 26 个åæ¯æ 0-9 ç 10 个æ°åï¼ä¸è½å
å«ç©ºæ ¼æä»»ä½å
¶ä»å符ã',
+ dateSuchAs:'请è¾å
¥åæ³çæ¥ææ ¼å¼ï¼å¦ï¼{date}ã',
+ dateInFormatMDY:'请è¾å
¥åæ³çæ¥ææ ¼å¼ï¼ä¾å¦ï¼MM/DD/YYYY ("12/31/1999")ã',
+ email:'请è¾å
¥åæ³ççµåä¿¡ç®±å°åï¼ä¾å¦ï¼"fred at domain.com"ã',
+ url:'请è¾å
¥åæ³ç Url å°åï¼ä¾å¦ï¼http://www.google.comã',
+ currencyDollar:'请è¾å
¥åæ³çè´§å¸ç¬¦å·ï¼ä¾å¦ï¼ï¿¥',
+ oneRequired:'请è³å°éæ©ä¸é¡¹ã',
+ errorPrefix: 'é误ï¼',
+ warningPrefix: 'è¦åï¼'
+});
+
+// Traditional Chinese
+MooTools.lang.set('zht-CN', 'Form.Validator', {
+ required:'éæ¯å¿
å¡«é
ã',
+ minLength:'è«è³å°éµå
¥ {minLength} åå符(å·²éµå
¥ {length} å)ã',
+ maxLength:'æå¤åªè½éµå
¥ {maxLength} åå符(å·²éµå
¥ {length} å)ã',
+ integer:'è«éµå
¥ä¸åæ´æ¸ï¼ä¸è½å
å«å°æ¸é»ãä¾å¦ï¼"1", "200"ã',
+ numeric:'è«éµå
¥ä¸åæ¸åï¼ä¾å¦ï¼"1", "1.1", "-1", "-1.1"ã',
+ digits:'é裡åªè½æ¥åæ¸ååæ¨é»çéµå
¥ï¼æ¨é»å¯ä»¥æ¯ï¼"(", ")", ".", ":", "-", "+", "#"åç©ºæ ¼ã',
+ alpha:'è«éµå
¥ A-Z ç 26 ååæ¯ï¼ä¸è½å
å«ç©ºæ ¼æä»»ä½å
¶ä»å符ã',
+ alphanum:'è«éµå
¥ A-Z ç 26 ååæ¯æ 0-9 ç 10 åæ¸åï¼ä¸è½å
å«ç©ºæ ¼æä»»ä½å
¶ä»å符ã',
+ dateSuchAs:'è«éµå
¥åæ³çæ¥ææ ¼å¼ï¼å¦ï¼{date}ã',
+ dateInFormatMDY:'è«éµå
¥åæ³çæ¥ææ ¼å¼ï¼ä¾å¦ï¼MM/DD/YYYY ("12/31/1999")ã',
+ email:'è«éµå
¥åæ³çé»åä¿¡ç®±å°åï¼ä¾å¦ï¼"fred at domain.com"ã',
+ url:'è«éµå
¥åæ³ç Url å°åï¼ä¾å¦ï¼http://www.google.comã',
+ currencyYuan:'è«éµå
¥åæ³ç貨幣符èï¼ä¾å¦ï¼ï¿¥',
+ oneRequired:'è«è³å°é¸æä¸é
ã',
+ errorPrefix: 'é¯èª¤ï¼',
+ warningPrefix: 'è¦åï¼'
+});
+
+Form.Validator.add('validate-currency-yuan', {
+ errorMsg: function(){
+ return Form.Validator.getMsg('currencyYuan');
+ },
+ test: function(element) {
+ // [ï¿¥]1[##][,###]+[.##]
+ // [ï¿¥]1###+[.##]
+ // [ï¿¥]0.##
+ // [ï¿¥].##
+ return Form.Validator.getValidator('IsEmpty').test(element) || (/^ï¿¥?\-?([1-9]{1}[0-9]{0,2}(\,[0-9]{3})*(\.[0-9]{0,2})?|[1-9]{1}\d*(\.[0-9]{0,2})?|0(\.[0-9]{0,2})?|(\.[0-9]{1,2})?)$/).test(element.get('value'));
+ }
+});
+/*
+---
+
+script: Form.Validator.Dutch.js
+
+description: Form.Validator messages in Dutch.
+
+license: MIT-style license
+
+authors:
+- Lennart Pilon
+
+requires:
+- /Lang
+- /Form.Validator
+
+provides: [Form.Validator.Dutch]
+
+...
+*/
+
+MooTools.lang.set('nl-NL', 'Form.Validator', {
+ required:'Dit veld is verplicht.',
+ minLength:'Vul minimaal {minLength} karakters in (je hebt {length} karakters ingevoerd).',
+ maxLength:'Vul niet meer dan {maxLength} karakters in (je hebt {length} karakters ingevoerd).',
+ integer:'Vul een getal in. Getallen met decimalen (bijvoorbeeld 1,25) zijn niet toegestaan.',
+ numeric:'Vul alleen numerieke waarden in (bijvoorbeeld. "1" of "1.1" of "-1" of "-1.1").',
+ digits:'Vul alleen nummers en leestekens in (bijvoorbeeld een telefoonnummer met een streepje).',
+ alpha:'Vul alleen letters in (a-z). Spaties en andere karakters zijn niet toegestaan.',
+ alphanum:'Vul alleen letters in (a-z) of nummers (0-9). Spaties en andere karakters zijn niet toegestaan.',
+ dateSuchAs:'Vul een geldige datum in, zoals {date}',
+ dateInFormatMDY:'Vul een geldige datum, in het formaat MM/DD/YYYY (bijvoorbeeld "12/31/1999")',
+ email:'Vul een geldig e-mailadres in. Bijvoorbeeld "fred at domein.nl".',
+ url:'Vul een geldige URL in, zoals http://www.google.nl.',
+ currencyDollar:'Vul een geldig $ bedrag in. Bijvoorbeeld $100.00 .',
+ oneRequired:'Vul iets in bij minimaal een van de invoervelden.',
+ warningPrefix: 'Waarschuwing: ',
+ errorPrefix: 'Fout: '
+});/*
+---
+
+script: Form.Validator.Estonian.js
+
+description: Date messages for Estonian.
+
+license: MIT-style license
+
+authors:
+- Kevin Valdek
+
+requires:
+- /Lang
+- /Form.Validator
+
+provides: [Form.Validator.Estonian]
+
+...
+*/
+
+MooTools.lang.set('et-EE', 'Form.Validator', {
+
+ required:'Väli peab olema täidetud.',
+ minLength:'Palun sisestage vähemalt {minLength} tähte (te sisestasite {length} tähte).',
+ maxLength:'Palun ärge sisestage rohkem kui {maxLength} tähte (te sisestasite {length} tähte).',
+ integer:'Palun sisestage väljale täisarv. Kümnendarvud (näiteks 1.25) ei ole lubatud.',
+ numeric:'Palun sisestage ainult numbreid väljale (näiteks "1", "1.1", "-1" või "-1.1").',
+ digits:'Palun kasutage ainult numbreid ja kirjavahemärke (telefoninumbri sisestamisel on lubatud kasutada kriipse ja punkte).',
+ alpha:'Palun kasutage ainult tähti (a-z). Tühikud ja teised sümbolid on keelatud.',
+ alphanum:'Palun kasutage ainult tähti (a-z) või numbreid (0-9). Tühikud ja teised sümbolid on keelatud.',
+ dateSuchAs:'Palun sisestage kehtiv kuupäev kujul {date}',
+ dateInFormatMDY:'Palun sisestage kehtiv kuupäev kujul MM.DD.YYYY (näiteks: "12.31.1999").',
+ email:'Palun sisestage kehtiv e-maili aadress (näiteks: "fred at domain.com").',
+ url:'Palun sisestage kehtiv URL (näiteks: http://www.google.com).',
+ currencyDollar:'Palun sisestage kehtiv $ summa (näiteks: $100.00).',
+ oneRequired:'Palun sisestage midagi vähemalt ühele antud väljadest.',
+ errorPrefix: 'Viga: ',
+ warningPrefix: 'Hoiatus: ',
+
+ //Form.Validator.Extras
+
+ noSpace: 'Väli ei tohi sisaldada tühikuid.',
+ reqChkByNode: 'Ãkski väljadest pole valitud.',
+ requiredChk: 'Välja täitmine on vajalik.',
+ reqChkByName: 'Palun valige üks {label}.',
+ match: 'Väli peab sobima {matchName} väljaga',
+ startDate: 'algkuupäev',
+ endDate: 'lõppkuupäev',
+ currendDate: 'praegune kuupäev',
+ afterDate: 'Kuupäev peab olema võrdne või pärast {label}.',
+ beforeDate: 'Kuupäev peab olema võrdne või enne {label}.',
+ startMonth: 'Palun valige algkuupäev.',
+ sameMonth: 'Antud kaks kuupäeva peavad olema samas kuus - peate muutma ühte kuupäeva.'
+
+});/*
+---
+
+script: Form.Validator.French.js
+
+description: Form.Validator messages in French.
+
+license: MIT-style license
+
+authors:
+- Miquel Hudin
+- Nicolas Sorosac <nicolas <dot> sorosac <at> gmail <dot> com>
+
+requires:
+- /Lang
+- /Form.Validator
+
+provides: [Form.Validator.French]
+
+...
+*/
+
+MooTools.lang.set('fr-FR', 'Form.Validator', {
+ required:'Ce champ est obligatoire.',
+ minLength:'Veuillez saisir un minimum de {minLength} caractère(s) (vous avez saisi {length} caractère(s)).',
+ maxLength:'Veuillez saisir un maximum de {maxLength} caractère(s) (vous avez saisi {length} caractère(s)).',
+ integer:'Veuillez saisir un nombre entier dans ce champ. Les nombres décimaux (ex : "1,25") ne sont pas autorisés.',
+ numeric:'Veuillez saisir uniquement des chiffres dans ce champ (ex : "1" ou "1,1" ou "-1" ou "-1,1").',
+ digits:'Veuillez saisir uniquement des chiffres et des signes de ponctuation dans ce champ (ex : un numéro de téléphone avec des traits d\'union est autorisé).',
+ alpha:'Veuillez saisir uniquement des lettres (a-z) dans ce champ. Les espaces ou autres caractères ne sont pas autorisés.',
+ alphanum:'Veuillez saisir uniquement des lettres (a-z) ou des chiffres (0-9) dans ce champ. Les espaces ou autres caractères ne sont pas autorisés.',
+ dateSuchAs:'Veuillez saisir une date correcte comme {date}',
+ dateInFormatMDY:'Veuillez saisir une date correcte, au format JJ/MM/AAAA (ex : "31/11/1999").',
+ email:'Veuillez saisir une adresse de courrier électronique. Par example "fred at domaine.com".',
+ url:'Veuillez saisir une URL, comme http://www.google.com.',
+ currencyDollar:'Veuillez saisir une quantité correcte. Par example 100,00€.',
+ oneRequired:'Veuillez sélectionner au moins une de ces options.',
+ errorPrefix: 'Erreur : ',
+ warningPrefix: 'Attention : ',
+
+ //Form.Validator.Extras
+
+ noSpace: 'Ce champ n\'accepte pas les espaces.',
+ reqChkByNode: 'Aucun élément n\'est sélectionné.',
+ requiredChk: 'Ce champ est obligatoire.',
+ reqChkByName: 'Veuillez sélectionner un(e) {label}.',
+ match: 'Ce champ doit correspondre avec le champ {matchName}.',
+ startDate: 'date de début',
+ endDate: 'date de fin',
+ currendDate: 'date actuelle',
+ afterDate: 'La date doit être identique ou postérieure à {label}.',
+ beforeDate: 'La date doit être identique ou antérieure à {label}.',
+ startMonth: 'Veuillez sélectionner un mois de début.',
+ sameMonth: 'Ces deux dates doivent être dans le même mois - vous devez en modifier une.'
+
+});/*
+---
+
+script: Form.Validator.Italian.js
+
+description: Form.Validator messages in Italian.
+
+license: MIT-style license
+
+authors:
+- Leonardo Laureti
+- Andrea Novero
+
+requires:
+- /Lang
+- /Form.Validator
+
+provides: [Form.Validator.Italian]
+
+...
+*/
+
+MooTools.lang.set('it-IT', 'Form.Validator', {
+
+ required:'Il campo è obbligatorio.',
+ minLength:'Inserire almeno {minLength} caratteri (ne sono stati inseriti {length}).',
+ maxLength:'Inserire al massimo {maxLength} caratteri (ne sono stati inseriti {length}).',
+ integer:'Inserire un numero intero. Non sono consentiti decimali (es.: 1.25).',
+ numeric:'Inserire solo valori numerici (es.: "1" oppure "1.1" oppure "-1" oppure "-1.1").',
+ digits:'Inserire solo numeri e caratteri di punteggiatura. Per esempio è consentito un numero telefonico con trattini o punti.',
+ alpha:'Inserire solo lettere (a-z). Non sono consentiti spazi o altri caratteri.',
+ alphanum:'Inserire solo lettere (a-z) o numeri (0-9). Non sono consentiti spazi o altri caratteri.',
+ dateSuchAs:'Inserire una data valida del tipo {date}',
+ dateInFormatMDY:'Inserire una data valida nel formato MM/GG/AAAA (es.: "12/31/1999")',
+ email:'Inserire un indirizzo email valido. Per esempio "nome at dominio.com".',
+ url:'Inserire un indirizzo valido. Per esempio "http://www.dominio.com".',
+ currencyDollar:'Inserire un importo valido. Per esempio "$100.00".',
+ oneRequired:'Completare almeno uno dei campi richiesti.',
+ errorPrefix: 'Errore: ',
+ warningPrefix: 'Attenzione: ',
+
+ //Form.Validator.Extras
+
+ noSpace: 'Non sono consentiti spazi.',
+ reqChkByNode: 'Nessuna voce selezionata.',
+ requiredChk: 'Il campo è obbligatorio.',
+ reqChkByName: 'Selezionare un(a) {label}.',
+ match: 'Il valore deve corrispondere al campo {matchName}',
+ startDate: 'data d\'inizio',
+ endDate: 'data di fine',
+ currendDate: 'data attuale',
+ afterDate: 'La data deve corrispondere o essere successiva al {label}.',
+ beforeDate: 'La data deve corrispondere o essere precedente al {label}.',
+ startMonth: 'Selezionare un mese d\'inizio',
+ sameMonth: 'Le due date devono essere dello stesso mese - occorre modificarne una.'
+
+});/*
+---
+
+script: Form.Validator.Norwegian.js
+
+description: Form.Validator messages in Norwegian.
+
+license: MIT-style license
+
+authors:
+- Espen 'Rexxars' Hovlandsdal
+
+requires:
+- /Lang
+- /Form.Validator
+
+provides: [Form.Validator.Norwegian]
+
+...
+*/
+
+MooTools.lang.set('no-NO', 'Form.Validator', {
+ required:'Dette feltet er pÃÂ¥krevd.',
+ minLength:'Vennligst skriv inn minst {minLength} tegn (du skrev {length} tegn).',
+ maxLength:'Vennligst skriv inn maksimalt {maxLength} tegn (du skrev {length} tegn).',
+ integer:'Vennligst skriv inn et tall i dette feltet. Tall med desimaler (for eksempel 1,25) er ikke tillat.',
+ numeric:'Vennligst skriv inn kun numeriske verdier i dette feltet (for eksempel "1", "1.1", "-1" eller "-1.1").',
+ digits:'Vennligst bruk kun nummer og skilletegn i dette feltet.',
+ alpha:'Vennligst bruk kun bokstaver (a-z) i dette feltet. Ingen mellomrom eller andre tegn er tillat.',
+ alphanum:'Vennligst bruk kun bokstaver (a-z) eller nummer (0-9) i dette feltet. Ingen mellomrom eller andre tegn er tillat.',
+ dateSuchAs:'Vennligst skriv inn en gyldig dato, som {date}',
+ dateInFormatMDY:'Vennligst skriv inn en gyldig dato, i formatet MM/DD/YYYY (for eksempel "12/31/1999")',
+ email:'Vennligst skriv inn en gyldig epost-adresse. For eksempel "espen at domene.no".',
+ url:'Vennligst skriv inn en gyldig URL, for eksempel http://www.google.no.',
+ currencyDollar:'Vennligst fyll ut et gyldig $ beløp. For eksempel $100.00 .',
+ oneRequired:'Vennligst fyll ut noe i minst ett av disse feltene.',
+ errorPrefix: 'Feil: ',
+ warningPrefix: 'Advarsel: '
+});/*
+---
+
+script: Form.Validator.Polish.js
+
+description: Date messages for Polish.
+
+license: MIT-style license
+
+authors:
+- Oskar Krawczyk
+
+requires:
+- /Lang
+- /Form.Validator
+
+provides: [Form.Validator.Polish]
+
+...
+*/
+
+MooTools.lang.set('pl-PL', 'Form.Validator', {
+
+ required:'To pole jest wymagane.',
+ minLength:'Wymagane jest przynajmniej {minLenght} znaków (wpisanych zostaÅo tylko {length}).',
+ maxLength:'Dozwolone jest nie wiÄcej niż {maxLenght} znaków (wpisanych zostaÅo {length})',
+ integer:'To pole wymaga liczb caÅych. Liczby dziesiÄtne (np. 1.25) sÄ
niedozwolone.',
+ numeric:'Prosimy używaÄ tylko numerycznych wartoÅci w tym polu (np. "1", "1.1", "-1" lub "-1.1").',
+ digits:'Prosimy używaÄ liczb oraz zankow punktuacyjnych w typ polu (dla przykÅadu, przy numerze telefonu myÅlniki i kropki sÄ
dozwolone).',
+ alpha:'Prosimy używaÄ tylko liter (a-z) w tym polu. Spacje oraz inne znaki sÄ
niedozwolone.',
+ alphanum:'Prosimy używaÄ tylko liter (a-z) lub liczb (0-9) w tym polu. Spacje oraz inne znaki sÄ
niedozwolone.',
+ dateSuchAs:'Prosimy podaÄ prawidÅowÄ
datÄ w formacie: {date}',
+ dateInFormatMDY:'Prosimy podaÄ poprawnÄ
date w formacie DD.MM.RRRR (i.e. "12.01.2009")',
+ email:'Prosimy podaÄ prawidÅowy adres e-mail, np. "jan at domena.pl".',
+ url:'Prosimy podaÄ prawidÅowy adres URL, np. http://www.google.pl.',
+ currencyDollar:'Prosimy podaÄ prawidÅowÄ
sumÄ w PLN. Dla przykÅadu: 100.00 PLN.',
+ oneRequired:'Prosimy wypeÅniÄ chociaż jedno z pól.',
+ errorPrefix: 'BÅÄ
d: ',
+ warningPrefix: 'Uwaga: ',
+
+ //Form.Validator.Extras
+
+ noSpace: 'W tym polu nie mogÄ
znajdowaÄ siÄ spacje.',
+ reqChkByNode: 'Brak zaznaczonych elementów.',
+ requiredChk: 'To pole jest wymagane.',
+ reqChkByName: 'Prosimy wybraÄ z {label}.',
+ match: 'To pole musi byÄ takie samo jak {matchName}',
+ startDate: 'data poczÄ
tkowa',
+ endDate: 'data koÅcowa',
+ currendDate: 'aktualna data',
+ afterDate: 'Podana data poinna byÄ taka sama lub po {label}.',
+ beforeDate: 'Podana data poinna byÄ taka sama lub przed {label}.',
+ startMonth: 'Prosimy wybraÄ poczÄ
tkowy miesiÄ
c.',
+ sameMonth: 'Te dwie daty muszÄ
byÄ w zakresie tego samego miesiÄ
ca - wymagana jest zmiana któregoŠz pól.'
+
+});/*
+---
+
+script: Form.Validator.Portuguese.js
+
+description: Form.Validator messages in Portuguese.
+
+license: MIT-style license
+
+authors:
+- Miquel Hudin
+
+requires:
+- /Lang
+- /Form.Validator
+
+provides: [Form.Validator.Portuguese]
+
+...
+*/
+
+MooTools.lang.set('pt-PT', 'Form.Validator', {
+ required:'Este campo é necessário.',
+ minLength:'Digite pelo menos{minLength} caracteres (comprimento {length} caracteres).',
+ maxLength:'Não insira mais de {maxLength} caracteres (comprimento {length} caracteres).',
+ integer:'Digite um número inteiro neste domÃnio. Com números decimais (por exemplo, 1,25), não são permitidas.',
+ numeric:'Digite apenas valores numéricos neste domÃnio (p.ex., "1" ou "1.1" ou "-1" ou "-1,1").',
+ digits:'Por favor, use números e pontuação apenas neste campo (p.ex., um número de telefone com traços ou pontos é permitida).',
+ alpha:'Por favor use somente letras (a-z), com nesta área. Não utilize espaços nem outros caracteres são permitidos.',
+ alphanum:'Use somente letras (a-z) ou números (0-9) neste campo. Não utilize espaços nem outros caracteres são permitidos.',
+ dateSuchAs:'Digite uma data válida, como {date}',
+ dateInFormatMDY:'Digite uma data válida, como DD/MM/YYYY (p.ex. "31/12/1999")',
+ email:'Digite um endereço de email válido. Por exemplo "fred at domain.com".',
+ url:'Digite uma URL válida, como http://www.google.com.',
+ currencyDollar:'Digite um valor válido $. Por exemplo $ 100,00. ',
+ oneRequired:'Digite algo para pelo menos um desses insumos.',
+ errorPrefix: 'Erro: ',
+ warningPrefix: 'Aviso: '
+
+}).set('pt-PT', 'Date', {
+ dateOrder: ['date', 'month', 'year', '/']
+});/*
+---
+
+script: Form.Validator.Portuguese.BR.js
+
+description: Form.Validator messages in Portuguese-BR.
+
+license: MIT-style license
+
+authors:
+- Fábio Miranda Costa
+
+requires:
+- /Lang
+- /Form.Validator.Portuguese
+
+provides: [Form.Validator.Portuguese.BR]
+
+...
+*/
+
+MooTools.lang.set('pt-BR', 'Form.Validator', {
+
+ required: 'Este campo é obrigatório.',
+ minLength: 'Digite pelo menos {minLength} caracteres (tamanho atual: {length}).',
+ maxLength: 'Não digite mais de {maxLength} caracteres (tamanho atual: {length}).',
+ integer: 'Por favor digite apenas um número inteiro neste campo. Não são permitidos números decimais (por exemplo, 1,25).',
+ numeric: 'Por favor digite apenas valores numéricos neste campo (por exemplo, "1" ou "1.1" ou "-1" ou "-1,1").',
+ digits: 'Por favor use apenas números e pontuação neste campo (por exemplo, um número de telefone com traços ou pontos é permitido).',
+ alpha: 'Por favor use somente letras (a-z). Espaço e outros caracteres não são permitidos.',
+ alphanum: 'Use somente letras (a-z) ou números (0-9) neste campo. Espaço e outros caracteres não são permitidos.',
+ dateSuchAs: 'Digite uma data válida, como {date}',
+ dateInFormatMDY: 'Digite uma data válida, como DD/MM/YYYY (por exemplo, "31/12/1999")',
+ email: 'Digite um endereço de email válido. Por exemplo "nome at dominio.com".',
+ url: 'Digite uma URL válida. Exemplo: http://www.google.com.',
+ currencyDollar: 'Digite um valor em dinheiro válido. Exemplo: R$100,00 .',
+ oneRequired: 'Digite algo para pelo menos um desses campos.',
+ errorPrefix: 'Erro: ',
+ warningPrefix: 'Aviso: ',
+
+ //Form.Validator.Extras
+
+ noSpace: 'Não é possÃvel digitar espaços neste campo.',
+ reqChkByNode: 'Não foi selecionado nenhum item.',
+ requiredChk: 'Este campo é obrigatório.',
+ reqChkByName: 'Por favor digite um {label}.',
+ match: 'Este campo deve ser igual ao campo {matchName}.',
+ startDate: 'a data inicial',
+ endDate: 'a data final',
+ currendDate: 'a data atual',
+ afterDate: 'A data deve ser igual ou posterior a {label}.',
+ beforeDate: 'A data deve ser igual ou anterior a {label}.',
+ startMonth: 'Por favor selecione uma data inicial.',
+ sameMonth: 'Estas duas datas devem ter o mesmo mês - você deve modificar uma das duas.',
+ creditcard: 'O número do cartão de crédito informado é inválido. Por favor verifique o valor e tente novamente. {length} números informados.'
+
+});/*
+---
+
+script: Form.Validator.Russian.js
+
+description: Form.Validator messages in Russian (utf-8 and cp1251).
+
+license: MIT-style license
+
+authors:
+- Chernodarov Egor
+
+requires:
+- /Lang
+- /Form.Validator
+
+provides: [Form.Validator.Russian]
+
+...
+*/
+
+MooTools.lang.set('ru-RU-unicode', 'Form.Validator', {
+ required:'ÐÑо поле обÑзаÑелÑно к заполнениÑ.',
+ minLength:'ÐожалÑйÑÑа, введиÑе Ñ
оÑÑ Ð±Ñ {minLength} Ñимволов (ÐÑ Ð²Ð²ÐµÐ»Ð¸ {length}).',
+ maxLength:'ÐожалÑйÑÑа, введиÑе не болÑÑе {maxLength} Ñимволов (ÐÑ Ð²Ð²ÐµÐ»Ð¸ {length}).',
+ integer:'ÐожалÑйÑÑа, введиÑе в ÑÑо поле ÑиÑло. ÐÑобнÑе ÑиÑла (напÑÐ¸Ð¼ÐµÑ 1.25) ÑÑÑ Ð½Ðµ ÑазÑеÑенÑ.',
+ numeric:'ÐожалÑйÑÑа, введиÑе в ÑÑо поле ÑиÑло (напÑÐ¸Ð¼ÐµÑ "1" или "1.1", или "-1", или "-1.1").',
+ digits:'Ð ÑÑом поле ÐÑ Ð¼Ð¾Ð¶ÐµÑе иÑполÑзоваÑÑ ÑолÑко ÑиÑÑÑ Ð¸ знаки пÑнкÑÑаÑии (напÑимеÑ, ÑелеÑоннÑй Ð½Ð¾Ð¼ÐµÑ Ñо знаками деÑиÑа или Ñ ÑоÑками).',
+ alpha:'Ð ÑÑом поле можно иÑполÑзоваÑÑ ÑолÑко лаÑинÑкие бÑÐºÐ²Ñ (a-z). ÐÑÐ¾Ð±ÐµÐ»Ñ Ð¸ дÑÑгие ÑÐ¸Ð¼Ð²Ð¾Ð»Ñ Ð·Ð°Ð¿ÑеÑенÑ.',
+ alphanum:'Ð ÑÑом поле можно иÑполÑзоваÑÑ ÑолÑко лаÑинÑкие бÑÐºÐ²Ñ (a-z) и ÑиÑÑÑ (0-9). ÐÑÐ¾Ð±ÐµÐ»Ñ Ð¸ дÑÑгие ÑÐ¸Ð¼Ð²Ð¾Ð»Ñ Ð·Ð°Ð¿ÑеÑенÑ.',
+ dateSuchAs:'ÐожалÑйÑÑа, введиÑе коÑÑекÑнÑÑ Ð´Ð°ÑÑ {date}',
+ dateInFormatMDY:'ÐожалÑйÑÑа, введиÑе даÑÑ Ð² ÑоÑмаÑе ÐÐ/ÐÐ/ÐÐÐÐ (напÑÐ¸Ð¼ÐµÑ "12/31/1999")',
+ email:'ÐожалÑйÑÑа, введиÑе коÑÑекÑнÑй емейл-адÑеÑ. ÐÐ»Ñ Ð¿ÑимеÑа "fred at domain.com".',
+ url:'ÐожалÑйÑÑа, введиÑе пÑавилÑнÑÑ ÑÑÑÐ»ÐºÑ Ð²Ð¸Ð´Ð° http://www.google.com.',
+ currencyDollar:'ÐожалÑйÑÑа, введиÑе ÑÑÐ¼Ð¼Ñ Ð² доллаÑаÑ
. ÐапÑимеÑ: $100.00 .',
+ oneRequired:'ÐожалÑйÑÑа, вÑбеÑиÑе Ñ
оÑÑ ÑÑо-нибÑÐ´Ñ Ð² одном из ÑÑиÑ
полей.',
+ errorPrefix: 'ÐÑибка: ',
+ warningPrefix: 'Ðнимание: '
+});
+
+//translation in windows-1251 codepage
+MooTools.lang.set('ru-RU', 'Form.Validator', {
+ required:'Ãòî ïîëå îáÿçà òåëüÃî ê çà ïîëÃÃ¥Ãèþ.',
+ minLength:'Ãîæà ëóéñòà , ââåäèòå õîòÿ áû {minLength} ñèìâîëîâ (Ãû ââåëè {length}).',
+ maxLength:'Ãîæà ëóéñòà , ââåäèòå ÃÃ¥ áîëüøå {maxLength} ñèìâîëîâ (Ãû ââåëè {length}).',
+ integer:'Ãîæà ëóéñòà , ââåäèòå â ýòî ïîëå ÷èñëî. ÃðîáÃûå ÷èñëà (Ãà ïðèìåð 1.25) òóò ÃÃ¥ ðà çðåøåÃû.',
+ numeric:'Ãîæà ëóéñòà , ââåäèòå â ýòî ïîëå ÷èñëî (Ãà ïðèìåð "1" èëè "1.1", èëè "-1", èëè "-1.1").',
+ digits:'à ýòîì ïîëå Ãû ìîæåòå èñïîëüçîâà òü òîëüêî öèôðû è çÃà êè ïóÃêòóà öèè (Ãà ïðèìåð, òåëåôîÃÃûé Ãîìåð ñî çÃà êà ìè äåôèñà èëè ñ òî÷êà ìè).',
+ alpha:'à ýòîì ïîëå ìîæÃî èñïîëüçîâà òü òîëüêî ëà òèÃñêèå áóêâû (a-z). Ãðîáåëû è äðóãèå ñèìâîëû çà ïðåùåÃû.',
+ alphanum:'à ýòîì ïîëå ìîæÃî èñïîëüçîâà òü òîëüêî ëà òèÃñêèå áóêâû (a-z) è öèôðû (0-9). Ãðîáåëû è äðóãèå ñèìâîëû çà ïðåùåÃû.',
+ dateSuchAs:'Ãîæà ëóéñòà , ââåäèòå êîððåêòÃóþ äà òó {date}',
+ dateInFormatMDY:'Ãîæà ëóéñòà , ââåäèòå äà òó â ôîðìà òå ÃÃ/ÃÃ/ÃÃÃà (Ãà ïðèìåð "12/31/1999")',
+ email:'Ãîæà ëóéñòà , ââåäèòå êîððåêòÃûé åìåéë-à äðåñ. Ãëÿ ïðèìåðà "fred at domain.com".',
+ url:'Ãîæà ëóéñòà , ââåäèòå ïðà âèëüÃóþ ññûëêó âèäà http://www.google.com.',
+ currencyDollar:'Ãîæà ëóéñòà , ââåäèòå ñóììó â äîëëà ðà õ. Ãà ïðèìåð: $100.00 .',
+ oneRequired:'Ãîæà ëóéñòà , âûáåðèòå õîòü ÷òî-Ãèáóäü â îäÃîì èç ýòèõ ïîëåé.',
+ errorPrefix: 'Ãøèáêà : ',
+ warningPrefix: 'ÃÃèìà Ãèå: '
+});/*
+---
+
+script: Form.Validator.Spanish.js
+
+description: Date messages for Spanish.
+
+license: MIT-style license
+
+authors:
+- Ãlfons Sanchez
+
+requires:
+- /Lang
+- /Form.Validator
+
+provides: [Form.Validator.Spanish]
+
+...
+*/
+
+MooTools.lang.set('es-ES', 'Form.Validator', {
+
+ required:'Este campo es obligatorio.',
+ minLength:'Por favor introduce al menos {minLength} caracteres (has introducido {length} caracteres).',
+ maxLength:'Por favor introduce no mas de {maxLength} caracteres (has introducido {length} caracteres).',
+ integer:'Por favor introduce un numero entero en este campo. Numeros con decimales (p.e. 1,25) no se permiten.',
+ numeric:'Por favor introduce solo valores numericos en este campo (p.e. "1" o "1,1" o "-1" o "-1,1").',
+ digits:'Por favor usa solo numeros y puntuacion en este campo (por ejemplo, un numero de telefono con guines y puntos no esta permitido).',
+ alpha:'Por favor usa letras solo (a-z) en este campo. No se admiten espacios ni otros caracteres.',
+ alphanum:'Por favor, usa solo letras (a-z) o numeros (0-9) en este campo. No se admiten espacios ni otros caracteres.',
+ dateSuchAs:'Por favor introduce una fecha valida como {date}',
+ dateInFormatMDY:'Por favor introduce una fecha valida como DD/MM/YYYY (p.e. "31/12/1999")',
+ email:'Por favor, introduce una direccione de email valida. Por ejemplo, "fred at domain.com".',
+ url:'Por favor introduce una URL valida como http://www.google.com.',
+ currencyDollar:'Por favor introduce una cantidad valida de â¬. Por ejemplo â¬100,00 .',
+ oneRequired:'Por favor introduce algo para por lo menos una de estas entradas.',
+ errorPrefix: 'Error: ',
+ warningPrefix: 'Aviso: ',
+
+ //Form.Validator.Extras
+
+ noSpace: 'No pueden haber espacios en esta entrada.',
+ reqChkByNode: 'No hay elementos seleccionados.',
+ requiredChk: 'Este campo es obligatorio.',
+ reqChkByName: 'Por favor selecciona una {label}.',
+ match: 'Este campo necesita coincidir con el campo {matchName}',
+ startDate: 'la fecha de inicio',
+ endDate: 'la fecha de fin',
+ currendDate: 'la fecha actual',
+ afterDate: 'La fecha debe ser igual o posterior a {label}.',
+ beforeDate: 'La fecha debe ser igual o anterior a {label}.',
+ startMonth: 'Por favor selecciona un mes de origen',
+ sameMonth: 'Estas dos fechas deben estar en el mismo mes - debes cambiar una u otra.'
+
+});/*
+---
+
+script: Form.Validator.Swedish.js
+
+description: Date messages for Swedish.
+
+license: MIT-style license
+
+authors:
+- Martin Lundgren
+
+requires:
+- /Lang
+- /Form.Validator
+
+provides: [Form.Validator.Swedish]
+
+...
+*/
+
+MooTools.lang.set('sv-SE', 'Form.Validator', {
+
+ required:'Fältet är obligatoriskt.',
+ minLength:'Ange minst {minLength} tecken (du angav {length} tecken).',
+ maxLength:'Ange högst {maxLength} tecken (du angav {length} tecken). ',
+ integer:'Ange ett heltal i fältet. Tal med decimaler (t.ex. 1,25) är inte tillåtna.',
+ numeric:'Ange endast numeriska värden i detta fält (t.ex. "1" eller "1.1" eller "-1" eller "-1,1").',
+ digits:'Använd endast siffror och skiljetecken i detta fält (till exempel ett telefonnummer med bindestreck tillåtet).',
+ alpha:'Använd endast bokstäver (a-ö) i detta fält. Inga mellanslag eller andra tecken är tillåtna.',
+ alphanum:'Använd endast bokstäver (a-ö) och siffror (0-9) i detta fält. Inga mellanslag eller andra tecken är tillåtna.',
+ dateSuchAs:'Ange ett giltigt datum som t.ex. {date}',
+ dateInFormatMDY:'Ange ett giltigt datum som t.ex. YYYY-MM-DD (i.e. "1999-12-31")',
+ email:'Ange en giltig e-postadress. Till exempel "erik at domain.com".',
+ url:'Ange en giltig webbadress som http://www.google.com.',
+ currencyDollar:'Ange en giltig belopp. Exempelvis 100,00.',
+ oneRequired:'Vänligen ange minst ett av dessa alternativ.',
+ errorPrefix: 'Fel: ',
+ warningPrefix: 'Varning: ',
+
+ //Form.Validator.Extras
+
+ noSpace: 'Det får inte finnas några mellanslag i detta fält.',
+ reqChkByNode: 'Inga objekt är valda.',
+ requiredChk: 'Detta är ett obligatoriskt fält.',
+ reqChkByName: 'Välj en {label}.',
+ match: 'Detta fält måste matcha {matchName}',
+ startDate: 'startdatumet',
+ endDate: 'slutdatum',
+ currendDate: 'dagens datum',
+ afterDate: 'Datumet bör vara samma eller senare än {label}.',
+ beforeDate: 'Datumet bör vara samma eller tidigare än {label}.',
+ startMonth: 'Välj en start månad',
+ sameMonth: 'Dessa två datum måste vara i samma månad - du måste ändra det ena eller det andra.'
+
+});// $Id: common.js 555 2009-10-23 05:23:59Z jonlb at comcast.net $
/**
* Class: Jx
* Jx is a global singleton object that contains the entire Jx library
@@ -9319,22 +13883,43 @@
}
})();
*/
+
Class.Mutators.Family = function(self,name) {
if ($defined(name)){
- self.$family = {'name': name};
- $[name] = $.object;
+ self.jxFamily = name;
return self;
}
- else {
- this.implement('$family',{'name':self});
+ else {
+ this.implement({'jxFamily':self});
}
};
+function $unlink(object){
+ if (object && object.jxFamily){
+ return object
+ }
+ var unlinked;
+ switch ($type(object)){
+ case 'object':
+ unlinked = {};
+ for (var p in object) unlinked[p] = $unlink(object[p]);
+ break;
+ case 'hash':
+ unlinked = new Hash(object);
+ break;
+ case 'array':
+ unlinked = [];
+ for (var i = 0, l = object.length; i < l; i++) unlinked[i] = $unlink(object[i]);
+ break;
+ default: return object;
+ }
+ return unlinked;
+};
/* Setup global namespace
* If jxcore is loaded by jx.js, then the namespace and baseURL are
* already established
*/
-if (typeof Jx == 'undefined') {
+if (typeof Jx === 'undefined') {
var Jx = {};
(function() {
var aScripts = document.getElementsByTagName('SCRIPT');
@@ -9369,18 +13954,24 @@
}
}
- /**
- * Determine if we're running in Adobe AIR. If so, determine which sandbox we're in
- */
- var src = aScripts[0].src;
- if (src.contains('app:')){
- Jx.isAir = true;
- } else {
- Jx.isAir = false;
- }
+
})();
}
+(function(){
+ /**
+ * Determine if we're running in Adobe AIR. Run this regardless of whether the above runs
+ * or not.
+ */
+ var aScripts = document.getElementsByTagName('SCRIPT');
+ var src = aScripts[0].src;
+ if (src.contains('app:')){
+ Jx.isAir = true;
+ } else {
+ Jx.isAir = false;
+ }
+})();
+
/**
* Method: applyPNGFilter
*
@@ -9407,6 +13998,10 @@
}
};
+/**
+ * NOTE: We should consider moving the image loading code into a separate
+ * class. Perhaps as Jx.Preloader which could extend Jx.Object
+ */
Jx.imgQueue = []; //The queue of images to be loaded
Jx.imgLoaded = {}; //a hash table of images that have been loaded and cached
Jx.imagesLoading = 0; //counter for number of concurrent image loads
@@ -9470,11 +14065,15 @@
* Returns:
* an HTML iframe element that can be inserted into the DOM.
*/
+/**
+ * NOTE: This could be replaced by Mootools-more's IFrameShim class.
+ */
Jx.createIframeShim = function() {
return new Element('iframe', {
'class':'jxIframeShim',
'scrolling':'no',
- 'frameborder':0
+ 'frameborder':0,
+ 'src': Jx.baseURL+'/empty.html'
});
};
/**
@@ -9491,7 +14090,7 @@
Jx.getNumber = function(n, def) {
var result = n===null||isNaN(parseInt(n,10))?(def||0):parseInt(n,10);
return result;
-};
+}
/**
* Method: getPageDimensions
@@ -9503,8 +14102,14 @@
*/
Jx.getPageDimensions = function() {
return {width: window.getWidth(), height: window.getHeight()};
-};
+}
+Jx.type = function(obj){
+ if (typeof obj == 'undefined'){
+ return false;
+ }
+ return obj.jxFamily ? obj.jxFamily : $type(obj);
+}
/**
* Class: Element
*
@@ -9517,7 +14122,15 @@
* there may be better MooTools methods to use to accomplish these things.
* Ultimately, it would be nice to eliminate most or all of these and find the
* MooTools equivalent or convince MooTools to add them.
+ *
+ * NOTE: Many of these methods can be replaced with mootools-more's
+ * Element.Measure
*/
+
+
+;(function($){ // Wrapper for document.id
+
+
Element.implement({
/**
* Method: getBoxSizing
@@ -9564,13 +14177,12 @@
* are the size of the content area of the measured element.
*/
getContentBoxSize : function() {
- var w = this.offsetWidth;
- var h = this.offsetHeight;
- var padding = this.getPaddingSize();
- var border = this.getBorderSize();
- w = w - padding.left - padding.right - border.left - border.right;
- h = h - padding.bottom - padding.top - border.bottom - border.top;
- return {width: w, height: h};
+ var w = this.offsetWidth;
+ var h = this.offsetHeight;
+ var s = this.getSizes(['padding','border']);
+ w = w - s.padding.left - s.padding.right - s.border.left - s.border.right;
+ h = h - s.padding.bottom - s.padding.top - s.border.bottom - s.border.top;
+ return {width: w, height: h};
},
/**
* Method: getBorderBoxSize
@@ -9585,9 +14197,9 @@
* are the size of the border area of the measured element.
*/
getBorderBoxSize: function() {
- var w = this.offsetWidth;
- var h = this.offsetHeight;
- return {width: w, height: h};
+ var w = this.offsetWidth;
+ var h = this.offsetHeight;
+ return {width: w, height: h};
},
/**
@@ -9603,13 +14215,44 @@
* are the size of the margin area of the measured element.
*/
getMarginBoxSize: function() {
- var margins = this.getMarginSize();
- var w = this.offsetWidth + margins.left + margins.right;
- var h = this.offsetHeight + margins.top + margins.bottom;
+ var s = this.getSizes(['margin']);
+ var w = this.offsetWidth + s.margin.left + s.margin.right;
+ var h = this.offsetHeight + s.margin.top + s.margin.bottom;
return {width: w, height: h};
},
-
/**
+ * Method: getSizes
+ * measure the size of various styles on various edges and return
+ * the values.
+ *
+ * Parameters:
+ * styles - array, the styles to compute. By default, this is ['padding',
+ * 'border','margin']. If you don't need all the styles, just request
+ * the ones you need to minimize compute time required.
+ * edges - array, the edges to compute styles for. By default, this is
+ * ['top','right','bottom','left']. If you don't need all the edges,
+ * then request the ones you need to minimize compute time.
+ *
+ * Returns:
+ * {Object} an object with one member for each requested style. Each
+ * style member is an object containing members for each requested edge.
+ * Values are the computed style for each edge in pixels.
+ */
+ getSizes: function(which, edges) {
+ which = which || ['padding','border','margin'];
+ edges = edges || ['left','top','right','bottom'];
+ var result={};
+ which.each(function(style) {
+ result[style]={};
+ edges.each(function(edge) {
+ var e = (style == 'border') ? edge + '-width' : edge;
+ var n = this.getStyle(style+'-'+e);
+ result[style][edge] = n===null||isNaN(parseInt(n,10))?0:parseInt(n,10);
+ }, this);
+ }, this);
+ return result;
+ },
+ /**
* Method: setContentBoxSize
* set either or both of the width and height of an element to
* the provided size. This function ensures that the content
@@ -9624,27 +14267,28 @@
*/
setContentBoxSize : function(size) {
if (this.getBoxSizing() == 'border-box') {
- var padding = this.getPaddingSize();
- var border = this.getBorderSize();
- if (typeof size.width != 'undefined') {
- var width = (size.width + padding.left + padding.right + border.left + border.right);
+ var m = this.measure(function() {
+ return this.getSizes(['padding','border']);
+ });
+ if ($defined(size.width)) {
+ var width = size.width + m.padding.left + m.padding.right + m.border.left + m.border.right;
if (width < 0) {
width = 0;
}
this.style.width = width + 'px';
}
- if (typeof size.height != 'undefined') {
- var height = (size.height + padding.top + padding.bottom + border.top + border.bottom);
+ if ($defined(size.height)) {
+ var height = size.height + m.padding.top + m.padding.bottom + m.border.top + m.border.bottom;
if (height < 0) {
height = 0;
}
this.style.height = height + 'px';
}
} else {
- if (typeof size.width != 'undefined') {
+ if ($defined(size.width) && size.width >= 0) {
this.style.width = size.width + 'px';
}
- if (typeof size.height != 'undefined') {
+ if ($defined(size.height) && size.height >= 0) {
this.style.height = size.height + 'px';
}
}
@@ -9664,86 +14308,33 @@
*/
setBorderBoxSize : function(size) {
if (this.getBoxSizing() == 'content-box') {
- var padding = this.getPaddingSize();
- var border = this.getBorderSize();
- var margin = this.getMarginSize();
- if (typeof size.width != 'undefined') {
- var width = (size.width - padding.left - padding.right - border.left - border.right - margin.left - margin.right);
+ var m = this.measure(function() {
+ return this.getSizes();
+ });
+
+ if ($defined(size.width)) {
+ var width = size.width - m.padding.left - m.padding.right - m.border.left - m.border.right - m.margin.left - m.margin.right;
if (width < 0) {
width = 0;
}
this.style.width = width + 'px';
}
- if (typeof size.height != 'undefined') {
- var height = (size.height - padding.top - padding.bottom - border.top - border.bottom - margin.top - margin.bottom);
+ if ($defined(size.height)) {
+ var height = size.height - m.padding.top - m.padding.bottom - m.border.top - m.border.bottom - m.margin.top - m.margin.bottom;
if (height < 0) {
height = 0;
}
this.style.height = height + 'px';
}
} else {
- if (typeof size.width != 'undefined' && size.width >= 0) {
+ if ($defined(size.width) && size.width >= 0) {
this.style.width = size.width + 'px';
}
- if (typeof size.height != 'undefined' && size.height >= 0) {
+ if ($defined(size.height) && size.height >= 0) {
this.style.height = size.height + 'px';
}
}
},
- /**
- * Method: getPaddingSize
- * returns the padding for each edge of an element
- *
- * Parameters:
- * elem - {Object} The element to get the padding for.
- *
- * Returns:
- * {Object} an object with properties left, top, right and bottom
- * that contain the associated padding values.
- */
- getPaddingSize : function () {
- var l = Jx.getNumber(this.getStyle('padding-left'));
- var t = Jx.getNumber(this.getStyle('padding-top'));
- var r = Jx.getNumber(this.getStyle('padding-right'));
- var b = Jx.getNumber(this.getStyle('padding-bottom'));
- return {left:l, top:t, right: r, bottom: b};
- },
- /**
- * Method: getBorderSize
- * returns the border size for each edge of an element
- *
- * Parameters:
- * elem - {Object} The element to get the borders for.
- *
- * Returns:
- * {Object} an object with properties left, top, right and bottom
- * that contain the associated border values.
- */
- getBorderSize : function() {
- var l = Jx.getNumber(this.getStyle('border-left-width'));
- var t = Jx.getNumber(this.getStyle('border-top-width'));
- var r = Jx.getNumber(this.getStyle('border-right-width'));
- var b = Jx.getNumber(this.getStyle('border-bottom-width'));
- return {left:l, top:t, right: r, bottom: b};
- },
- /**
- * Method: getMarginSize
- * returns the margin size for each edge of an element
- *
- * Parameters:
- * elem - {Object} The element to get the margins for.
- *
- * Returns:
- *: {Object} an object with properties left, top, right and bottom
- * that contain the associated margin values.
- */
- getMarginSize : function() {
- var l = Jx.getNumber(this.getStyle('margin-left'));
- var t = Jx.getNumber(this.getStyle('margin-top'));
- var r = Jx.getNumber(this.getStyle('margin-right'));
- var b = Jx.getNumber(this.getStyle('margin-bottom'));
- return {left:l, top:t, right: r, bottom: b};
- },
/**
* Method: descendantOf
@@ -9756,9 +14347,9 @@
* {Boolean} true if the element is a descendent, false otherwise.
*/
descendantOf: function(node) {
- var parent = $(this.parentNode);
+ var parent = document.id(this.parentNode);
while (parent != node && parent && parent.parentNode && parent.parentNode != parent) {
- parent = $(parent.parentNode);
+ parent = document.id(parent.parentNode);
}
return parent == node;
},
@@ -9779,17 +14370,322 @@
var o = this;
var tagName = o.tagName;
while (o.tagName != type && o && o.parentNode && o.parentNode != o) {
- o = $(o.parentNode);
+ o = document.id(o.parentNode);
}
return o.tagName == type ? o : false;
}
} );
+Array.implement({
+
+ /**
+ * Method: swap
+ * swaps 2 elements of an array
+ *
+ * Parameters:
+ * a - the first position to swap
+ * b - the second position to swap
+ */
+ 'swap': function(a,b){
+ var temp;
+ temp = this[a];
+ this[a] = this[b];
+ this[b] = temp;
+ }
+
+});
+
+})(document.id || $); // End Wrapper for document.id
+
/**
- * Class: Jx.ContentLoader
+ * Class: Jx.Styles
+ * Dynamic stylesheet class. Used for creating and manipulating dynamic
+ * stylesheets.
+ *
+ * TBD: should we handle the case of putting the same selector in a stylesheet
+ * twice? Right now the code that stores the index of each rule on the
+ * stylesheet is not really safe for that when combined with delete or get
+ *
+ * This is a singleton and should be called directly, like so:
+ *
+ * (code)
+ * // create a rule that turns all para text red and 15px.
+ * var rule = Jx.Styles.insertCssRule("p", "color: red;", "myStyle");
+ * rule.style.fontSize = "15px";
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ * Additional code by Paul Spencer
*
- * ContentLoader is a mix-in class that provides a consistent
- * mechanism for other Jx controls to load content in one of
+ * This file is licensed under an MIT style license
+ *
+ * Inspired by dojox.html.styles, VisitSpy by nwhite,
+ * http://www.hunlock.com/blogs/Totally_Pwn_CSS_with_Javascript
+ *
+ */
+Jx.Styles = new(new Class({
+ /**
+ * dynamicStyleMap - <Hash> used to keep a reference to dynamically created
+ * style sheets for quick access
+ */
+ dynamicStyleMap: new Hash(),
+ /**
+ * Method: getCssRule
+ * retrieve a reference to a CSS rule in a specific style sheet based on its
+ * selector. If the rule does not exist, create it.
+ *
+ * Parameters:
+ * selector - <String> the CSS selector for the rule
+ * styleSheetName - <String> the name of the sheet to get the rule from
+ *
+ * Returns:
+ * <CSSRule> - the requested rule
+ */
+ getCssRule: function(selector, styleSheetName) {
+ var ss = this.getDynamicStyleSheet(styleSheetName);
+ var rule = null;
+ if (ss.indicies) {
+ var i = ss.indicies.indexOf(selector);
+ if (i == -1) {
+ rule = this.insertCssRule(selector, '', styleSheetName);
+ } else {
+ if (Browser.Engine.name === 'trident') {
+ rule = ss.sheet.rules[i];
+ } else {
+ rule = ss.sheet.cssRules[i];
+ }
+ }
+ }
+ return rule;
+ },
+ /**
+ * Method: insertCssRule
+ * insert a new dynamic rule into the given stylesheet. If no name is
+ * given for the stylesheet then the default stylesheet is used.
+ *
+ * Parameters:
+ * selector - <String> the CSS selector for the rule
+ * declaration - <String> CSS-formatted rules to include. May be empty, in
+ * which case you may want to use the returned rule object to manipulate
+ * styles
+ * styleSheetName - <String> the name of the sheet to place the rules in,
+ * or empty to put them in a default sheet.
+ *
+ * Returns:
+ * <CSSRule> - a CSS Rule object with properties that are browser
+ * dependent. In general, you can use rule.styles to set any CSS properties
+ * in the same way that you would set them on a DOM object.
+ */
+ insertCssRule: function (selector, declaration, styleSheetName) {
+ var ss = this.getDynamicStyleSheet(styleSheetName);
+ var rule;
+ var text = selector + " {" + declaration + "}";
+ if (Browser.Engine.name === 'trident') {
+ ss.styleSheet.cssText += text;
+ rule = ss.sheet.rules[ss.insertRule.length];
+ } else {
+ ss.sheet.insertRule(text, ss.indicies.length);
+ rule = ss.sheet.cssRules[ss.indicies.length];
+ }
+ ss.indicies.push(selector);
+ return rule;
+ },
+ /**
+ * Method: removeCssRule
+ * removes a CSS rule from the named stylesheet.
+ *
+ * Parameters:
+ * selector - <String> the CSS selector for the rule
+ * styleSheetName - <String> the name of the sheet to remove the rule from,
+ * or empty to remove them from the default sheet.
+ *
+ * Returns:
+ * <Boolean> true if the rule was removed, false if it was not.
+ */
+ removeCssRule: function (selector, styleSheetName) {
+ var ss = this.getDynamicStyleSheet(styleSheetName);
+ var i = ss.indicies.indexOf(selector);
+ ss.indicies.splice(i, 1);
+ if (Browser.Engine.name === 'trident') {
+ ss.removeRule(i);
+ return true;
+ } else {
+ ss.sheet.deleteRule(i);
+ return true;
+ }
+ return false;
+ },
+ /**
+ * Method: getDynamicStyleSheet
+ * return a reference to a styleSheet based on its title. If the sheet
+ * does not already exist, it is created.
+ *
+ * Parameter:
+ * name - <String> the title of the stylesheet to create or obtain
+ *
+ * Returns:
+ * <StyleSheet> a StyleSheet object with browser dependent capabilities.
+ */
+ getDynamicStyleSheet: function (name) {
+ name = (name) ? name : 'default';
+ if (!this.dynamicStyleMap.has(name)) {
+ var sheet = new Element('style').set('type', 'text/css').set('title', name).inject(document.head);
+ sheet.indicies = [];
+ this.dynamicStyleMap.set(name, sheet);
+ }
+ return this.dynamicStyleMap.get(name);
+ },
+ /* Method: enableStyleSheet
+ * enable a style sheet
+ *
+ * Parameters:
+ * name - <String> the title of the stylesheet to enable
+ */
+ enableStyleSheet: function (name) {
+ this.getDynamicStyleSheet(name).disabled = false;
+ },
+ /* Method: disableStyleSheet
+ * enable a style sheet
+ *
+ * Parameters:
+ * name - <String> the title of the stylesheet to disable
+ */
+ disableStyleSheet: function (name) {
+ this.getDynamicStyleSheet(name).disabled = true;
+ }
+}))();// $Id: $
+/**
+ * Class: Jx.Object
+ * Base class for all other object in the JxLib framework. This class
+ * implements both mootools mixins Events and Options so the rest of the
+ * classes don't need to.
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Object = new Class({
+
+ Implements: [Options, Events],
+
+ Family: "Jx.Object",
+
+ plugins: new Hash(),
+
+ pluginNamespace: 'Other',
+
+ parameters: ['options'],
+
+ initialize: function(){
+ //normalize arguments
+ var numArgs = arguments.length;
+ var options = {};
+
+ if (numArgs > 0) {
+ if (numArgs === 1
+ && (Jx.type(arguments[0])==='object' || Jx.type(arguments[0])==='Hash')
+ && this.parameters.length === 1
+ && this.parameters[0] === 'options') {
+ options = arguments[0];
+ } else {
+ var numParams = this.parameters.length;
+ var index;
+ if (numParams <= numArgs) {
+ index = numParams;
+ } else {
+ index = numArgs;
+ }
+ options = {};
+ for (var i = 0; i < index; i++) {
+ if (this.parameters[i] === 'options') {
+ options = $merge(options, arguments[i]);
+ } else {
+ options[this.parameters[i]] = arguments[i];
+ }
+ }
+ }
+ }
+
+ this.setOptions(options);
+ this.fireEvent('preInit');
+ this.init();
+ this.fireEvent('postInit');
+ this.fireEvent('prePluginInit');
+ this.initPlugins();
+ this.fireEvent('postPluginInit');
+ this.fireEvent('initializeDone');
+ },
+
+ initPlugins: function () {
+ //pluginNamespace must be defined in order to pass plugins to the object
+ if ($defined(this.pluginNamespace)) {
+ if ($defined(this.options.plugins)
+ && Jx.type(this.options.plugins) === 'array') {
+ this.options.plugins.each(function (plugin) {
+ if (plugin instanceof Jx.Plugin) {
+ plugin.attach(this);
+ this.plugins.set(plugin.name, plugin);
+ } else if (Jx.type(plugin) === 'object') {
+ //All plugin-enabled objects should define a pluginNamespace member variable
+ //that is used for locating the plugins. The default namespace is 'Other' for
+ //now until we come up with a better idea
+ var p = new Jx.Plugin[this.pluginNamespace][plugin.name](plugin.options);
+ p.attach(this);
+ this.plugins.set(p.name, p);
+ }
+ }, this);
+ }
+ }
+ },
+
+ destroy: function () {
+ this.fireEvent('preDestroy');
+ this.cleanup();
+ this.fireEvent('postDestroy');
+ },
+
+ cleanup: function () {
+ //detach plugins
+ if (this.plugins.getLength > 0) {
+ this.plugins.each(function (plugin) {
+ plugin.detach();
+ plugin.destroy();
+ }, this);
+ }
+ },
+
+ init: $empty,
+
+ registerPlugin: function (plugin) {
+ if (!this.plugins.has(plugin.name)) {
+ this.plugins.set(plugin.name, plugin);
+ }
+ },
+
+ deregisterPlugin: function (plugin) {
+ if (this.plugins.has(plugin.name)) {
+ this.plugins.erase(plugin.name);
+ }
+ }
+
+});
+ // $Id: $
+/**
+ * Class: Jx.Widget
+ * Base class for all widgets (visual classes) in the JxLib Framework. This
+ * class extends <Jx.Object> and adds the Chrome, ContentLoader, Addable, and
+ * AutoPosition mixins from the original framework.
+ *
+ * ContentLoader:
+ *
+ * ContentLoader functionality provides a consistent
+ * mechanism for descendants of Jx.Widget to load content in one of
* four different ways:
*
* o using an existing element, by id
@@ -9800,26 +14696,81 @@
*
* o using a URL to get the content remotely
*
- * Use the Implements syntax in your Class to add Jx.ContentLoader
- * to your class.
+ * Chrome:
+ *
+ * Chrome is the extraneous visual element that provides the look and feel to some elements
+ * i.e. dialogs. Chrome is added inside the element specified but may
+ * bleed outside the element to provide drop shadows etc. This is done by
+ * absolutely positioning the chrome objects in the container based on
+ * calculations using the margins, borders, and padding of the jxChrome
+ * class and the element it is added to.
*
- * Option: content
- * content may be an HTML element reference, the id of an HTML element
- * already in the DOM, or an HTML string that becomes the inner HTML of
- * the element.
+ * Chrome can consist of either pure CSS border and background colors, or
+ * a background-image on the jxChrome class. Using a background-image on
+ * the jxChrome class creates four images inside the chrome container that
+ * are positioned in the top-left, top-right, bottom-left and bottom-right
+ * corners of the chrome container and are sized to fill 50% of the width
+ * and height. The images are positioned and clipped such that the
+ * appropriate corners of the chrome image are displayed in those locations.
*
- * Option: contentURL
- * the URL to load content from
+ *
*/
-Jx.ContentLoader = new Class ({
- /**
+Jx.Widget = new Class({
+
+ Extends: Jx.Object,
+
+ options: {
+ /**
+ * Option: content
+ * content may be an HTML element reference, the id of an HTML element
+ * already in the DOM, or an HTML string that becomes the inner HTML of
+ * the element.
+ */
+ content: null,
+ /**
+ * Option: contentURL
+ * the URL to load content from
+ */
+ contentURL: null
+ },
+
+ /**
+ * Property: domObj
+ * The HTMLElement that represents this widget.
+ */
+ domObj: null,
+
+ /**
* Property: contentIsLoaded
*
* tracks the load state of the content, specifically useful
* in the case of remote content.
*/
contentIsLoaded: false,
+
/**
+ * Property: chrome
+ * the DOM element that contains the chrome
+ */
+ chrome: null,
+
+
+ /**
+ * APIMethod: init
+ * sets up the base widget code and runs the render function.
+ */
+ init: function(){
+ if (!this.options.deferRender) {
+ this.fireEvent('preRender');
+ this.render();
+ this.fireEvent('postRender');
+ } else {
+ this.fireEvent('deferRender');
+ }
+ },
+
+
+ /**
* Method: loadContent
*
* triggers loading of content based on options set for the current
@@ -9842,13 +14793,13 @@
* useful when using the contentURL method of loading content.
*/
loadContent: function(element) {
- element = $(element);
+ element = document.id(element);
if (this.options.content) {
var c;
if (this.options.content.domObj) {
- c = $(this.options.content.domObj);
+ c = document.id(this.options.content.domObj);
} else {
- c = $(this.options.content);
+ c = document.id(this.options.content);
}
if (c) {
if (this.options.content.addTo) {
@@ -9915,47 +14866,8 @@
}
}
}, this);
- }
-});
-
-
-/**
- * It seems AIR never returns an XHR that "fails" by not finding the
- * appropriate file when run in the application sandbox and retrieving a local
- * file. This affects Jx.ContentLoader in that a "failed" event is never fired.
- *
- * To fix this, I've added a timeout that waits about 10 seconds or so in the code above
- * for the XHR to return, if it hasn't returned at the end of the timeout, we cancel the
- * XHR and fire the failure event.
- *
- * This code only gets added if we're in AIR.
- */
-if (Jx.isAir){
- Jx.ContentLoader.implement({
- /**
- * Method: checkRequest()
- * Is fired after a delay to check the request to make sure it's not
- * failing in AIR.
- */
- checkRequest: function(){
- if (this.req.xhr.readyState === 1) {
- //we still haven't gotten the file. Cancel and fire the
- //failure
- $clear(this.reqTimeout);
- this.req.cancel();
- this.contentIsLoaded = true;
- this.fireEvent('contentLoadFailed', this);
- }
- }
- });
-}
-
-/**
- * Class: Jx.AutoPosition
- * Mix-in class that provides a method for positioning
- * elements relative to other elements.
- */
-Jx.AutoPosition = new Class({
+ },
+
/**
* Method: position
* positions an element relative to another element
@@ -10012,8 +14924,8 @@
* being positioned as top, right, bottom and left properties.
*/
position: function(element, relative, options) {
- element = $(element);
- relative = $(relative);
+ element = document.id(element);
+ relative = document.id(relative);
var hor = $splat(options.horizontal || ['center center']);
var ver = $splat(options.vertical || ['center center']);
var offsets = $merge({top:0,right:0,bottom:0,left:0}, options.offsets || {});
@@ -10021,12 +14933,12 @@
var coords = relative.getCoordinates(); //top, left, width, height
var page;
var scroll;
- if (!$(element.parentNode) || element.parentNode == document.body) {
+ if (!document.id(element.parentNode) || element.parentNode == document.body) {
page = Jx.getPageDimensions();
- scroll = $(document.body).getScroll();
+ scroll = document.id(document.body).getScroll();
} else {
- page = $(element.parentNode).getContentBoxSize(); //width, height
- scroll = $(element.parentNode).getScroll();
+ page = document.id(element.parentNode).getContentBoxSize(); //width, height
+ scroll = document.id(element.parentNode).getScroll();
}
if (relative == document.body) {
// adjust coords for the scroll offsets to make the object
@@ -10183,33 +15095,7 @@
jxl.options.left = left;
jxl.options.top = top;
}
- }
-});
-
-/**
- * Class: Jx.Chrome
- * A mix-in class that provides chrome helper functions. Chrome is the
- * extraneous visual element that provides the look and feel to some elements
- * i.e. dialogs. Chrome is added inside the element specified but may
- * bleed outside the element to provide drop shadows etc. This is done by
- * absolutely positioning the chrome objects in the container based on
- * calculations using the margins, borders, and padding of the jxChrome
- * class and the element it is added to.
- *
- * Chrome can consist of either pure CSS border and background colors, or
- * a background-image on the jxChrome class. Using a background-image on
- * the jxChrome class creates four images inside the chrome container that
- * are positioned in the top-left, top-right, bottom-left and bottom-right
- * corners of the chrome container and are sized to fill 50% of the width
- * and height. The images are positioned and clipped such that the
- * appropriate corners of the chrome image are displayed in those locations.
- */
-Jx.Chrome = new Class({
- /**
- * Property: chrome
- * the DOM element that contains the chrome
- */
- chrome: null,
+ },
/**
* Method: makeChrome
@@ -10233,7 +15119,9 @@
* through padding on the chrome object. Other code can then
* make use of these offset values to fix positioning.
*/
- this.chromeOffsets = c.getPaddingSize();
+ this.chromeOffsets = c.measure(function() {
+ return this.getSizes(['padding']).padding;
+ });
c.setStyle('padding', 0);
/* get the chrome image from the background image of the element */
@@ -10275,6 +15163,7 @@
c.dispose();
this.chrome = c;
},
+
/**
* Method: showChrome
* show the chrome on an element. This creates the chrome if necessary.
@@ -10288,7 +15177,7 @@
* element - {HTMLElement} the element to show the chrome on.
*/
showChrome: function(element) {
- element = $(element);
+ element = document.id(element);
if (!this.chrome) {
this.makeChrome(element);
}
@@ -10297,6 +15186,7 @@
element.adopt(this.chrome);
}
},
+
/**
* Method: hideChrome
* removes the chrome from the DOM. If you do this, you can't
@@ -10307,20 +15197,13 @@
this.chrome.dispose();
}
},
+
resizeChrome: function(o) {
- if (this.chrome && Browser.Engine.trident) {
- this.chrome.setContentBoxSize($(o).getBorderBoxSize());
+ if (this.chrome && Browser.Engine.trident4) {
+ this.chrome.setContentBoxSize(document.id(o).getBorderBoxSize());
}
- }
-});
-
-/**
- * Class: Jx.Addable
- * A mix-in class that provides a helper function that allows an object
- * to be added to an existing element on the page.
- */
-Jx.Addable = new Class({
- addable: null,
+ },
+
/**
* Method: addTo
* adds the object to the DOM relative to another element. If you use
@@ -10339,22 +15222,2228 @@
* the object itself, which is useful for chaining calls together
*/
addTo: function(reference, where) {
- $(this.addable || this.domObj).inject(reference,where);
- this.fireEvent('addTo',this);
+ var el = document.id(this.addable) || document.id(this.domObj);
+ if (el) {
+ ref = document.id(reference);
+ el.inject(ref,where);
+ this.fireEvent('addTo',this);
+ }
return this;
},
toElement: function() {
- return this.addable || this.domObj;
+ return this.domObj;
+ },
+
+ /**
+ * APIMethod: processTemplate
+ * This function pulls the needed elements from a provided template
+ *
+ * Parameters:
+ * template - the template to use in grabbing elements
+ * classes - an array of class names to use in grabbing elements
+ * container - the container to add the template into
+ *
+ * Returns:
+ * a hash object containing the requested Elements keyed by the class names
+ */
+ processTemplate: function(template,classes,container){
+
+ var h = new Hash();
+ var element;
+ if ($defined(container)){
+ element = container.set('html',template);
+ } else {
+ element = new Element('div',{html:template,styles:{border:'20px solid red'}});
+ }
+ classes.each(function(klass){
+ var el = element.getElement('.'+klass);
+ if ($defined(el)){
+ h.set(klass,el);
+ }
+ });
+
+ return h;
+
+ },
+
+ /**
+ * Method: generateId
+ * Used to generate a unique ID for Jx Widgets.
+ */
+ generateId: function(prefix){
+ prefix = (prefix) ? prefix : 'jx-';
+ var uid = $uid(this);
+ delete this.uid;
+ return prefix + uid;
+ },
+
+ remove: function(){
+ var el = document.id(this.addable) || document.id(this.domObj);
+ if (el) {
+ el.dispose();
+ }
+ },
+
+ cleanup: function(){
+ if ($defined(this.domObj)) {
+ this.domObj.destroy();
+ }
+ if ($defined(this.addable)) {
+ this.addable.destroy();
+ }
+ if ($defined(this.domA)) {
+ this.domA.destroy();
+ }
+ this.parent();
+ },
+
+ render: $empty
+});
+
+
+/**
+ * It seems AIR never returns an XHR that "fails" by not finding the
+ * appropriate file when run in the application sandbox and retrieving a local
+ * file. This affects Jx.ContentLoader in that a "failed" event is never fired.
+ *
+ * To fix this, I've added a timeout that waits about 10 seconds or so in the code above
+ * for the XHR to return, if it hasn't returned at the end of the timeout, we cancel the
+ * XHR and fire the failure event.
+ *
+ * This code only gets added if we're in AIR.
+ */
+if (Jx.isAir){
+ Jx.Widget.implement({
+ /**
+ * Method: checkRequest
+ * Is fired after a delay to check the request to make sure it's not
+ * failing in AIR.
+ */
+ checkRequest: function(){
+ if (this.req.xhr.readyState === 1) {
+ //we still haven't gotten the file. Cancel and fire the
+ //failure
+ $clear(this.reqTimeout);
+ this.req.cancel();
+ this.contentIsLoaded = true;
+ this.fireEvent('contentLoadFailed', this);
+ }
+ }
+ });
+}
+
+Jx.Selection = new Class({
+
+ Extends: Jx.Object,
+ Family: 'Jx.Selection',
+
+ options: {
+ /**
+ * Option: eventToFire
+ * Allows the developer to change the event that is fired in case one
+ * object is using multiple selectionManager instances.
+ */
+ eventToFire: {
+ select: 'select',
+ unselect: 'unselect'
+ },
+ /**
+ * APIProperty: selectClass
+ * the CSS class name to add to the wrapper element when it is selected
+ */
+ selectClass: 'jxSelected',
+ /**
+ * Option: selectMode
+ * {string} default single. May be single or multiple. In single mode
+ * only one item may be selected. Selecting a new item will implicitly
+ * unselect the currently selected item.
+ */
+ selectMode: 'single',
+ /**
+ * Option: selectToggle
+ * {Boolean} Default true. Selection of a selected item will unselect
+ * it.
+ */
+ selectToggle: true,
+ /**
+ * Option: minimumSelection
+ * {Integer} Default 0. The minimum number of items that must be
+ * selected. If set to a number higher than 0, items added to a list
+ * are automatically selected until this minimum is met. The user may
+ * not unselect items if unselecting them will drop the total number of
+ * items selected below the minimum.
+ */
+ minimumSelection: 0
+ },
+
+ selection: null,
+
+ init: function () {
+ this.selection = [];
+ },
+
+ defaultSelect: function(item) {
+ if (this.selection.length < this.options.minimumSelection) {
+ this.select(item);
+ }
+ },
+
+ select: function (item) {
+ if (this.options.selectMode === 'multiple') {
+ if (this.selection.contains(item)) {
+ this.unselect(item);
+ } else {
+ document.id(item).addClass(this.options.selectClass);
+ this.selection.push(item);
+ this.fireEvent(this.options.eventToFire.select, item, this);
+ }
+ } else if (this.options.selectMode == 'single') {
+ if (!this.selection.contains(item)) {
+ document.id(item).addClass(this.options.selectClass);
+ this.selection.push(item);
+ if (this.selection.length > 1) {
+ this.unselect(this.selection[0]);
+ }
+ } else {
+ this.unselect(item);
+ }
+ this.fireEvent(this.options.eventToFire.select, item, this);
+ }
+ },
+
+ unselect: function (item) {
+ if (this.selection.contains(item) &&
+ this.selection.length > this.options.minimumSelection) {
+ document.id(item).removeClass(this.options.selectClass);
+ this.selection.erase(item);
+ this.fireEvent(this.options.eventToFire.unselect, item, this);
+ }
+ },
+
+ selected: function () {
+ return this.selection;
+ },
+
+ isSelected: function(item) {
+ return this.selection.contains(item);
}
-});// $Id: button.js 424 2009-05-12 12:51:44Z pagameba $
+
+});// $Id: $
/**
+ * Class: Jx.List
+ *
+ * Manage a list of DOM elements and provide an API and events for managing
+ * those items within a container. Works with Jx.Selection to manage
+ * selection of items in the list. You have two options for managing
+ * selections. The first, and default, option is to specify select: true
+ * in the constructor options and any of the <Jx.Selection> options as well.
+ * This will create a default Jx.Selection object to manage selections. The
+ * second option is to pass a Jx.Selection object as the third constructor
+ * argument. This allows sharing selection between multiple lists.
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * Events:
+ * add - fired when an item is added
+ * remove - fired when an item is removed
+ * mouseenter - fired when the user mouses over an element
+ * mouseleave - fired when the user mouses out of an element
+ * select - fired when an item is selected
+ * unselect - fired when an item is selected
+ *
+ * License:
+ * Copyright (c) 2008, DM Solutions Group Inc.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.List = new Class({
+ Family: 'Jx.List',
+ Extends: Jx.Object,
+ parameters: ['container', 'options', 'selection'],
+ /* does this object own the selection object (and should clean it up) */
+ ownsSelection: false,
+ /**
+ * APIProperty: itemContainer
+ * the element that will contain items as they are added
+ */
+ container: null,
+ /**
+ * APIProperty: selection
+ * <Jx.Selection> a selection object if selection is enabled
+ */
+ selection: null,
+ options: {
+ /**
+ * APIProperty: items
+ * an array of items to add to the list right away
+ */
+ items: null,
+ /**
+ * Option: hover
+ * {Boolean} default true. If set to true, the wrapper element will
+ * obtain the defined hoverClass if set and mouseenter/mouseleave
+ * events will be emitted when the user hovers over and out of elements
+ */
+ hover: false,
+ /**
+ * APIProperty: hoverClass
+ * the CSS class name to add to the wrapper element when the mouse is
+ * over an item
+ */
+ hoverClass: 'jxHover',
+
+ /**
+ * Option: press
+ * {Boolean} default true. If set to true, the wrapper element will
+ * obtain the defined pressClass if set and mousedown/mouseup
+ * events will be emitted when the user clicks on elements
+ */
+ press: false,
+ /**
+ * APIProperty: pressedClass
+ * the CSS class name to add to the wrapper element when the mouse is
+ * down on an item
+ */
+ pressClass: 'jxPressed',
+
+ /**
+ * Option: select
+ * {Boolean} default true. If set to true, the wrapper element will
+ * obtain the defined selectClass if set and select/unselect events
+ * will be emitted when items are selected and unselected. For other
+ * selection objects, see <Jx.Selection>
+ */
+ select: false
+ },
+
+ init: function() {
+ this.container = document.id(this.options.container);
+ this.container.store('jxList', this);
+
+ var target = this;
+ this.bound = {
+ mousedown: function() {
+ this.addClass(target.options.pressClass);
+ target.fireEvent('mousedown', this, target);
+ },
+ mouseup: function() {
+ this.removeClass(target.options.pressClass);
+ target.fireEvent('mouseup', this, target);
+ },
+ mouseenter: function() {
+ this.addClass(target.options.hoverClass);
+ target.fireEvent('mouseenter', this, target);
+ },
+ mouseleave: function() {
+ this.removeClass(target.options.hoverClass);
+ target.fireEvent('mouseleave', this, target);
+ }
+ };
+ if (this.options.selection) {
+ this.selection = this.options.selection;
+ } else if (this.options.select) {
+ this.selection = new Jx.Selection(this.options);
+ this.ownsSelection = true;
+ }
+
+ if (this.selection) {
+ this.bound.click = function () {
+ target.selection.select(this, target);
+ };
+ this.selection.addEvents({
+ select: function(item) {
+ target.fireEvent('select', item);
+ },
+ unselect: function(item) {
+ target.fireEvent('select', item);
+ }
+ })
+ }
+ if ($defined(this.options.items)) {
+ this.add(this.options.items);
+ }
+ },
+
+ cleanup: function() {
+ this.container.getChildren().each(function(item){
+ this.remove(item);
+ }, this);
+ this.bound = null;
+ if (this.ownsSelection && this.selection) {
+ this.selection.destroy();
+ }
+ this.container.eliminate('jxList');
+ },
+
+ /**
+ * APIMethod: add
+ * add an item to the list of items at the specified position
+ *
+ * Parameters:
+ * item - {mixed} the object to add, a DOM element or an
+ * object that provides a getElement method. An array of items may also
+ * be provided. All items are inserted sequentially at the indicated
+ * position.
+ * position - {mixed} optional, the position to add the element, either
+ * an integer position in the list or another item to place this item after
+ */
+ add: function(item, position) {
+ if ($type(item) == 'array') {
+ item.each(function(what){ this.add(what, position); }.bind(this) );
+ return;
+ }
+ /* the element being wrapped */
+ var el = document.id(item);
+ if (el) {
+ if (this.options.press && this.options.pressClass) {
+ el.addEvents({
+ mousedown: this.bound.mousedown,
+ mouseup: this.bound.mouseup
+ });
+ }
+ if (this.options.hover && this.options.hoverClass) {
+ el.addEvents({
+ mouseenter: this.bound.mouseenter,
+ mouseleave: this.bound.mouseleave
+ });
+ }
+ if (this.selection) {
+ el.addEvents({
+ click: this.bound.click
+ });
+ }
+ if ($defined(position)) {
+ if ($type(position) == 'integer') {
+ if (position < this.container.childNodes.length) {
+ el.inject(this.container.childNodes[position],' before');
+ } else {
+ el.inject(this.container, 'bottom');
+ }
+ } else if (this.container.hasChild(position)) {
+ el.inject(position,'after');
+ }
+ this.fireEvent('add', item, this);
+ } else {
+ el.inject(this.container, 'bottom');
+ this.fireEvent('add', item, this);
+ }
+ if (this.selection) {
+ this.selection.defaultSelect(el);
+ }
+ }
+ },
+ /**
+ * Method: remove
+ * remove an item from the list of items
+ *
+ * Parameters:
+ * item - {mixed} the item to remove or the index of the item to remove. An
+ * array of items may also be provided.
+ *
+ * Returns:
+ * {mixed} the item that was removed or null if the item is not a member
+ * of this list.
+ */
+ remove: function(item) {
+ if (this.container.hasChild(item)) {
+ this.unselect(item, true);
+ document.id(item).dispose();
+ document.id(item).removeEvents(this.bound);
+ this.fireEvent('remove', item, this);
+ return item;
+ }
+ return null;
+ },
+ /**
+ * Method: replace
+ * replace one item with another
+ *
+ * Parameters:
+ * item - {mixed} the item to replace or the index of the item to replace
+ * withItem - {mixed} the object, DOM element, Jx.Object or an object
+ * implementing getElement to add
+ *
+ * Returns:
+ * {mixed} the item that was removed
+ */
+ replace: function(item, withItem) {
+ if (this.container.hasChild(item)) {
+ this.add(withItem, item);
+ this.remove(item);
+ }
+ },
+ /**
+ * APIMethod: indexOf
+ * find the index of an item in the list
+ *
+ * Parameters:
+ * item - {mixed} the object, DOM element, Jx.Object or an object
+ * implementing getElement to find the index of
+ *
+ * Returns:
+ * {integer} the position of the item or -1 if not found
+ */
+ indexOf: function(item) {
+ return $A(this.container.childNodes).indexOf(item);
+ },
+ /**
+ * APIMethod: count
+ * returns the number of items in the list
+ */
+ count: function() {
+ return this.container.childNodes.length;
+ },
+ /**
+ * APIMethod: items
+ * returns an array of the items in the list
+ */
+ items: function() {
+ return $A(this.container.childNodes);
+ },
+ /**
+ * APIMethod: each
+ * applies the supplied function to each item
+ *
+ * Parameters:
+ * func - {function} the function to apply, it will receive the item and
+ * index of the item as parameters
+ */
+ each: function(f) {
+ $A(this.container.childNodes).each(f);
+ },
+ /**
+ * APIMethod: select
+ * select an item
+ *
+ * Parameters:
+ * item - {mixed} the object to select, a DOM element, a Jx.Object, or an
+ * object that provides a getElement method. An array of items may also be
+ * provided.
+ */
+ select: function(item) {
+ if (this.selection) {
+ this.selection.select(item);
+ }
+ },
+ /**
+ * APIMethod: unselect
+ * unselect an item or items
+ *
+ * Parameters:
+ * item - {mixed} the object to select, a DOM element, a Jx.Object, or an
+ * object that provides a getElement method. An array of elements may also
+ * be provided.
+ * force - {Boolean} force deselection even if this violates the minimum
+ * selection constraint (used internally when removing items)
+ */
+ unselect: function(item, force) {
+ if (this.selection) {
+ this.selection.unselect(item);
+ }
+ },
+ /**
+ * APIMethod: selected
+ * returns the selected item or items
+ *
+ * Returns:
+ * {mixed} the selected item or an array of selected items
+ */
+ selected: function() {
+ return this.selection ? this.selection.selected : [];
+ },
+ /**
+ * APIMethod: empty
+ * clears all of the items from the list
+ */
+ empty: function(){
+ this.container.getChildren().each(function(item){
+ this.remove(item);
+ }, this);
+ }
+
+});// $Id: $
+/**
+ * Class: Jx.Compare
+ *
+ * Extends: <Jx.Object>
+ *
+ * Class that holds functions for doing comparison operations.
+ * This class requires the clientside Date() extensions (deps/date.js).
+ *
+ * notes:
+ * Each function that does a comparison returns
+ *
+ * 0 - if equal.
+ * 1 - if the first value is greater that the second.
+ * -1 - if the first value is less than the second.
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+
+Jx.Compare = new Class({
+ Extends: Jx.Object,
+
+ options: { separator: '.' },
+
+ /**
+ * APIMethod: alphanumeric
+ * Compare alphanumeric variables. This is case sensitive
+ *
+ * Parameters:
+ * a - a value
+ * b - another value
+ */
+ alphanumeric: function (a, b) {
+ return (a === b) ? 0 :(a < b) ? -1 : 1;
+ },
+
+ /**
+ * APIMethod: numeric
+ * Compares numbers
+ *
+ * Parameters:
+ * a - a number
+ * b - another number
+ */
+ numeric: function (a, b) {
+ return this.alphanumeric(this.convert(a), this.convert(b));
+ },
+
+ /**
+ * Method: _convert
+ * Normalizes numbers relative to the separator.
+ *
+ * Parameters:
+ * val - the number to normalize
+ *
+ * Returns:
+ * the normalized value
+ */
+ convert: function (val) {
+ if (Jx.type(val) === 'string') {
+ val = parseFloat(val.replace(/^[^\d\.]*([\d., ]+).*/g, "$1").replace(new RegExp("[^\\\d" + this.options.separator + "]", "g"), '').replace(/,/, '.')) || 0;
+ }
+ return val || 0;
+ },
+
+ /**
+ * APIMethod: ignorecase
+ * Compares to alphanumeric strings without regard to case.
+ *
+ * Parameters:
+ * a - a value
+ * b - another value
+ */
+ ignorecase: function (a, b) {
+ return this.alphanumeric(("" + a).toLowerCase(), ("" + b).toLowerCase());
+ },
+
+ /**
+ * APIMethod: currency
+ * Compares to currency values.
+ *
+ * Parameters:
+ * a - a currency value without the $
+ * b - another currency value without the $
+ */
+ currency: function (a, b) {
+ return this.numeric(a, b);
+ },
+
+ /**
+ * APIMethod: date
+ * Compares 2 date values (either a string or an object)
+ *
+ * Parameters:
+ * a - a date value
+ * b - another date value
+ */
+ date: function (a, b) {
+ var x = new Date().parse(a);
+ var y = new Date().parse(b);
+ return (x < y) ? -1 : (x > y) ? 1 : 0;
+ },
+ /**
+ * APIMethod: boolean
+ * Compares 2 bolean values
+ *
+ * Parameters:
+ * a - a boolean value
+ * b - another boolean value
+ */
+ 'boolean': function (a, b) {
+ return (a === true && b === false) ? -1 : (a === b) ? 0 : 1;
+ }
+
+});// $Id: $
+/**
+ * Class: Jx.Sort Base class for all of the sorting algorithm classes.
+ *
+ * Extends: <Jx.Object>
+ *
+ * Events:
+ * onStart() - called when the sort starts
+ * onEnd() - called when the sort stops
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Sort = new Class({
+
+ Family : 'Jx.Sort',
+
+ Extends : Jx.Object,
+
+ options : {
+ /**
+ * Option: timeIt
+ * whether to time the sort
+ */
+ timeIt : false,
+ /**
+ * Event: onStart
+ */
+ onStart : $empty,
+ /**
+ * Event: onEnd
+ */
+ onEnd : $empty
+ },
+
+ /**
+ * Property: timer
+ * holds the timer instance
+ */
+ timer : null,
+ /**
+ * Property: data
+ * The data to sort
+ */
+ data : null,
+ /**
+ * Property: Comparator
+ * The comparator to use in sorting
+ */
+ comparator : $empty,
+ /**
+ * Property: col
+ * The column to sort by
+ */
+ col : null,
+
+ parameters: ['data','fn','col','options'],
+
+ /**
+ * APIMethod: init
+ */
+ init : function () {
+ this.parent();
+ if (this.options.timeIt) {
+ this.addEvent('start', this.startTimer.bind(this));
+ this.addEvent('stop', this.stopTimer.bind(this));
+ }
+ this.data = this.options.data;
+ this.comparator = this.options.fn;
+ this.col = this.options.col;
+ },
+
+ /**
+ * APIMethod: sort
+ * Actually does the sorting. Must be overridden by subclasses.
+ */
+ sort : $empty,
+
+ /**
+ * Method: startTimer
+ * Saves the starting time of the sort
+ */
+ startTimer : function () {
+ this.timer = new Date();
+ },
+
+ /**
+ * Method: stopTimer
+ * Determines the time the sort took.
+ */
+ stopTimer : function () {
+ this.end = new Date();
+ this.dif = this.timer.diff(this.end, 'ms');
+ },
+
+ /**
+ * APIMethod: setData
+ * sets the data to sort
+ *
+ * Parameters:
+ * data - the data to sort
+ */
+ setData : function (data) {
+ if ($defined(data)) {
+ this.data = data;
+ }
+ },
+
+ /**
+ * APIMethod: setColumn
+ * Sets the column to sort by
+ *
+ * Parameters:
+ * col - the column to sort by
+ */
+ setColumn : function (col) {
+ if ($defined(col)) {
+ this.col = col;
+ }
+ },
+
+ /**
+ * APIMethod: setComparator
+ * Sets the comparator to use in sorting
+ *
+ * Parameters:
+ * fn - the function to use as the comparator
+ */
+ setComparator : function (fn) {
+ this.comparator = fn;
+ }
+});
+// $Id: $
+/**
+ * Class: Jx.Store
+ *
+ * Extends: <Jx.Object>
+ *
+ * This class is the base store. It keeps track of data. It
+ * allows adding, deleting, iterating, sorting etc...
+ *
+ * Events: onLoadFinished(store) - fired when the store finishes loading the
+ * data onLoadError(store,data) - fired when there is an error loading the data
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+
+Jx.Store = new Class({
+
+ Extends : Jx.Object,
+
+ Family : "Jx.Store",
+
+ options : {
+ /**
+ * Option: id
+ * the identifier for this store
+ */
+ id : null,
+ /**
+ * Option: columns
+ * an array listing the columns of the store in order of their
+ * appearance in the data object formatted as an object
+ * {name: 'column name', type: 'column type'}
+ * where type can be one of alphanumeric, numeric, date, boolean,
+ * or currency.
+ */
+ columns : [],
+ /**
+ * Option: defaultSort
+ * The default sorting type, currently set to merge but can be any of the
+ * sorters available
+ */
+ defaultSort : 'merge',
+ /**
+ * Option: separator
+ * The separator to pass to the comparator
+ * constructor (<Jx.Compare>) - defaults to '.'
+ */
+ separator : '.',
+ /**
+ * Option: sortCols
+ * An array of columns to sort by arranged in the order you want
+ * them sorted.
+ */
+ sortCols : [],
+ /**
+ * Event: onLoadFinished(store)
+ * event for a completed, successful data load
+ */
+ onLoadFinished : $empty,
+ /**
+ * Event: onLoadError(store,data)
+ * event for an unsuccessful load
+ */
+ onLoadError : $empty,
+ /**
+ * Event: onColumnChanged
+ * event fired for changes to a column
+ */
+ onColumnChanged : $empty,
+ /**
+ * Option: paginate
+ * Set to true to enable pagination
+ */
+ paginate: false,
+ /**
+ * Option: pageSize
+ * Sets the size of each page. Only used if paginate is true.
+ */
+ pageSize: 0
+
+ },
+
+ /**
+ * Property: sorters
+ * an object listing the different sorters available
+ */
+ sorters : {
+ quick : "Quicksort",
+ merge : "Mergesort",
+ heap : "Heapsort",
+ 'native' : "Nativesort"
+ },
+ /**
+ * Property: data
+ * Holds the data for this store
+ */
+ data : null,
+ /**
+ * Property: index
+ * Holds the current position of the store relative to the data and the pageIndex.
+ * Zero-based index.
+ */
+ index : 0,
+ /**
+ * Property: pageIndex
+ * Holds the current page index
+ */
+ pageIndex: 0,
+ /**
+ * Property: dirty
+ * Tells us if the store is dirty
+ */
+ dirty : false,
+ /**
+ * Property: id
+ * The id of this store.
+ */
+ id : null,
+ /**
+ * Property: loaded
+ * Tells whether the store has been loaded or not
+ */
+ loaded: false,
+
+ /**
+ * APIMethod: load
+ * Loads data into the store.
+ *
+ * Parameters:
+ * data - the data to load
+ */
+ load : function (data) {
+ if ($defined(data)) {
+ this.loaded = false;
+ this.processData(data);
+
+ } else {
+ this.loaded = false;
+ }
+ },
+
+ /**
+ * APIMethod: hasNext
+ * Determines if there are more records past the current
+ * one.
+ *
+ * Returns: true | false (Null if there's a problem)
+ */
+ hasNext : function () {
+ if ($defined(this.data)) {
+ if (this.index < this.data[this.pageIndex].length - 1) {
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return null;
+ }
+ },
+
+ /**
+ * APIMethod: hasPrevious
+ * Determines if there are records before the current
+ * one.
+ *
+ * Returns: true | false
+ */
+ hasPrevious : function () {
+ if ($defined(this.data)) {
+ if (this.index > 0) {
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return null;
+ }
+ },
+
+ /**
+ * APIMethod: valid
+ * Tells us if the current index has any data (i.e. that the
+ * index is valid).
+ *
+ * Returns: true | false
+ */
+ valid : function () {
+ return ($defined(this.data[this.pageIndex][this.index]));
+ },
+
+ /**
+ * APIMethod: next
+ * Moves the store to the next record
+ *
+ * Returns: nothing | null if error
+ */
+ next : function () {
+ if ($defined(this.data)) {
+ this.index++;
+ if (this.index === this.data[this.pageIndex].length) {
+ this.index = this.data[this.pageIndex].length - 1;
+ }
+ this.fireEvent('storeMove', this);
+ } else {
+ return null;
+ }
+ },
+
+ /**
+ * APIMethod: previous
+ * moves the store to the previous record
+ *
+ * Returns: nothing | null if error
+ *
+ */
+ previous : function () {
+ if ($defined(this.data)) {
+ this.index--;
+ if (this.index < 0) {
+ this.index = 0;
+ }
+ this.fireEvent('storeMove', this);
+ } else {
+ return null;
+ }
+ },
+
+ /**
+ * APIMethod: first
+ * Moves the store to the first record
+ *
+ * Returns: nothing | null if error
+ *
+ */
+ first : function () {
+ if ($defined(this.data)) {
+ this.index = 0;
+ this.fireEvent('storeMove', this);
+ } else {
+ return null;
+ }
+ },
+
+ /**
+ * APIMethod: last
+ * Moves to the last record in the store
+ *
+ * Returns: nothing | null if error
+ */
+ last : function () {
+ if ($defined(this.data)) {
+ this.index = this.data[this.pageIndex].length - 1;
+ this.fireEvent('storeMove', this);
+ } else {
+ return null;
+ }
+ },
+
+ /**
+ * APIMethod: count
+ * Returns the number of records in the store
+ *
+ * Returns: an integer indicating the number of records in the store or null
+ * if there's an error
+ */
+ count : function () {
+ if ($defined(this.data)) {
+ return this.data[this.pageIndex].length;
+ } else {
+ return null;
+ }
+ },
+
+ /**
+ * APIMethod: getPosition
+ * Tells us where we are in the store
+ *
+ * Returns: an integer indicating the position in the store or null if
+ * there's an error
+ */
+ getPosition : function () {
+ if ($defined(this.data[this.pageIndex])) {
+ return this.index;
+ } else {
+ return null;
+ }
+ },
+
+ /**
+ * APIMethod: moveTo
+ * Moves the index to a specific record in the store
+ *
+ * Parameters:
+ * index - the record to move to
+ *
+ * Returns: true - if successful false - if not successful null - on error
+ */
+ moveTo : function (index) {
+ if ($defined(this.data) && index >= 0 && index < this.data[this.pageIndex].length) {
+ this.index = index;
+ this.fireEvent('storeMove', this);
+ return true;
+ } else if (!$defined(this.data)) {
+ return null;
+ } else {
+ return false;
+ }
+ },
+
+ /**
+ * APIMethod: get
+ * Retrieves the data for a specific column of the current
+ * record
+ *
+ * Parameters:
+ * col - the column to get (either an integer or a string)
+ *
+ * Returns: the data in the column or null if the column doesn't exist
+ */
+ get : function (col) {
+ if ($defined(this.data)) {
+ col = this.resolveCol(col);
+ var h = this.data[this.pageIndex][this.index];
+ if (h.has(col.name)) {
+ return h.get(col.name);
+ } else {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ },
+
+ /**
+ * APIMethod: set
+ * Puts a value into a specific column of the current record and
+ * sets the dirty flag.
+ *
+ * Parameters:
+ * column - the column to put the value in value - the data to put
+ * into the column
+ *
+ * returns: nothing | null if an error
+ */
+ set : function (column, value) {
+ if ($defined(this.data)) {
+ // set the column to the value and set the dirty flag
+
+ if (Jx.type(column) === 'number' || Jx.type(column) === 'string') {
+ column = this.resolveCol(column);
+ }
+
+ var oldValue = this.data[this.pageIndex][this.index].get(column.name);
+ this.data[this.pageIndex][this.index].set(column.name, value);
+ this.data[this.pageIndex][this.index].set('dirty', true);
+ this.fireEvent('columnChanged', [ this.index, column, oldValue, value ]);
+ } else {
+ return null;
+ }
+ },
+
+ /**
+ * APIMethod: refresh
+ * Sets new data into the store
+ *
+ * Parameters:
+ * data - the data to set
+ * reset - flag as to whether to reset the index to 0
+ *
+ * Returns: nothing or null if no data is passed
+ */
+ refresh : function (data, reset) {
+ if ($defined(data)) {
+ this.processData(data);
+ if (reset) {
+ this.index = 0;
+ }
+ } else {
+ return null;
+ }
+ },
+
+ /**
+ * APIMethod: isDirty
+ * Tells us if the store is dirty and needs to be saved
+ *
+ * Returns: true | false | null on error
+ */
+ isDirty : function () {
+ if ($defined(this.data)) {
+ var dirty = false;
+ this.data.each(function (row) {
+ if (this.isRowDirty(row)) {
+ dirty = true;
+ return;
+ }
+ }, this);
+ return dirty;
+ } else {
+ return null;
+ }
+ },
+
+ /**
+ * APIMethod: newRow
+ * Adds a new row to the store. It can either be empty or made
+ * from an array of data
+ *
+ * Parameters:
+ * data - data to use in the new row (optional)
+ */
+ newRow : function (data) {
+ // check if array is not defined
+ if (!$defined(this.data)) {
+ // if not, then create a new array
+ this.data = [];
+ this.data[this.pageIndex] = [];
+ }
+
+ var d;
+
+ if (!$defined(data)) {
+ d = new Hash();
+ } else {
+ var t = Jx.type(data);
+ switch (t) {
+ case 'object':
+ d = new Hash(data);
+ break;
+ case 'hash':
+ d = data;
+ break;
+ }
+ }
+ d.set('dirty', true);
+ this.data[this.pageIndex][this.data[this.pageIndex].length] = d;
+ this.index = this.data[this.pageIndex].length - 1;
+ this.fireEvent('newrow', this);
+ },
+
+ /**
+ * APIMethod: sort
+ * Runs the sorting and grouping
+ *
+ * Parameters:
+ * cols - Optional. An array of columns to sort/group by
+ * sort - the sort type (quick,heap,merge,native),defaults to options.defaultSort
+ * dir - the direction to sort. Set to "desc" for descending,
+ * anything else implies ascending (even null).
+ */
+ sort : function (cols, sort, dir) {
+
+ if (this.count()) {
+
+ this.fireEvent('sortStart', this);
+
+ var c;
+ if ($defined(cols) && Jx.type(cols) === 'array') {
+ c = this.options.sortCols = cols;
+ } else if ($defined(cols) && Jx.type(cols) === 'string') {
+ this.options.sortCols = [];
+ this.options.sortCols.push(cols);
+ c = this.options.sortCols;
+ } else if ($defined(this.options.sortCols)) {
+ c = this.options.sortCols;
+ } else {
+ return null;
+ }
+
+ this.sortType = sort;
+ // first sort on the first array item
+ this.data[this.pageIndex] = this.doSort(c[0], sort, this.data[this.pageIndex], true);
+
+ if (c.length > 1) {
+ this.data[this.pageIndex] = this.subSort(this.data[this.pageIndex], 0, 1);
+ }
+
+ if ($defined(dir) && dir === 'desc') {
+ this.data[this.pageIndex].reverse();
+ }
+
+ this.fireEvent('sortFinished', this);
+ }
+ },
+
+ /**
+ * Method: subSort
+ * Does the actual group sorting.
+ *
+ * Parameters:
+ * data - what to sort
+ * groupByCol - the column that determines the groups
+ * sortCol - the column to sort by
+ *
+ * returns: the result of the grouping/sorting
+ */
+ subSort : function (data, groupByCol, sortByCol) {
+
+ if (sortByCol >= this.options.sortCols.length) {
+ return data;
+ }
+ /**
+ * loop through the data array and create another array with just the
+ * items for each group. Sort that sub-array and then concat it
+ * to the result.
+ */
+ var result = [];
+ var sub = [];
+
+ var groupCol = this.options.sortCols[groupByCol];
+ var sortCol = this.options.sortCols[sortByCol];
+
+ var group = data[0].get(groupCol);
+ this.sorter.setColumn(sortCol);
+ for (var i = 0; i < data.length; i++) {
+ if (group === (data[i]).get(groupCol)) {
+ sub.push(data[i]);
+ } else {
+ // sort
+
+ if (sub.length > 1) {
+ result = result.concat(this.subSort(this.doSort(sortCol, this.sortType, sub, true), groupByCol + 1, sortByCol + 1));
+ } else {
+ result = result.concat(sub);
+ }
+
+ // change group
+ group = (data[i]).get(groupCol);
+ // clear sub
+ sub.empty();
+ // add to sub
+ sub.push(data[i]);
+ }
+ }
+
+ if (sub.length > 1) {
+ this.sorter.setData(sub);
+ result = result.concat(this.subSort(this.doSort(sortCol, this.sortType, sub, true), groupByCol + 1, sortByCol + 1));
+ } else {
+ result = result.concat(sub);
+ }
+
+ //this.data = result;
+
+ return result;
+ },
+
+ /**
+ * Method: doSort
+ * Called to change the sorting of the data
+ *
+ * Parameters:
+ * col - the column to sort by
+ * sort - the kind of sort to use (see list above)
+ * data - the data to sort (leave blank or pass null to sort data
+ * existing in the store)
+ * ret - flag that tells the function whether to pass
+ * back the sorted data or store it in the store
+ * options - any options needed to pass to the sorter upon creation
+ *
+ * returns: nothing or the data depending on the value of ret parameter.
+ */
+ doSort : function (col, sort, data, ret, options) {
+ options = {} || options;
+
+ sort = (sort) ? this.sorters[sort] : this.sorters[this.options.defaultSort];
+ data = data ? data : this.data;
+ ret = ret ? true : false;
+
+ if (!$defined(this.comparator)) {
+ this.comparator = new Jx.Compare({
+ separator : this.options.separator
+ });
+ }
+
+ this.col = col = this.resolveCol(col);
+
+ var fn = this.comparator[col.type].bind(this.comparator);
+ if (!$defined(this.sorter)) {
+ this.sorter = new Jx.Sort[sort](data, fn, col.name, options);
+ } else {
+ this.sorter.setComparator(fn);
+ this.sorter.setColumn(col.name);
+ this.sorter.setData(data);
+ }
+ var d = this.sorter.sort();
+
+ if (ret) {
+ return d;
+ } else {
+ this.data = d;
+ }
+ },
+
+ /**
+ * Method: isRowDirty
+ * Helps determine if a row is dirty
+ *
+ * Parameters:
+ * row - the row to check
+ *
+ * Returns: true | false
+ */
+ isRowDirty : function (row) {
+ if (row.has('dirty')) {
+ return row.get('dirty');
+ } else {
+ return false;
+ }
+ },
+
+ /**
+ * Method: resolveCol
+ * Determines which array index this column refers to
+ *
+ * Parameters:
+ * col - a number referencing a column in the store
+ *
+ * Returns: the name of the column
+ */
+ resolveCol : function (col) {
+ var t = Jx.type(col);
+ if (t === 'number') {
+ col = this.options.columns[col];
+ } else if (t === 'string') {
+ this.options.columns.each(function (column) {
+ if (column.name === col) {
+ col = column;
+ }
+ }, this);
+ }
+ return col;
+ },
+
+ /**
+ * Method: processData
+ * Processes the data passed into the function into the store.
+ *
+ * Parameters:
+ * data - the data to put into the store
+ */
+ processData : function (data) {
+ this.loaded = false;
+ this.fireEvent('preload', [ this, data ]);
+
+ if (!$defined(this.data)) {
+ this.data = [];
+ this.data[this.pageIndex] = [];
+ }
+
+ if ($defined(data)) {
+ this.data[this.pageIndex].empty();
+ var type = Jx.type(data);
+ // is this an array?
+ if (type === 'array') {
+ if (this.options.paginate) {
+ var i = 1;
+ var p = 0;
+ data.each(function (item) {
+ this.data[p].include(new Hash(item));
+ i++;
+ if (i === this.options.pageSize) {
+ i = 1;
+ p++;
+ this.data[p] = [];
+ }
+ }, this);
+ } else {
+ data.each(function (item, index) {
+ this.data[this.pageIndex].include(new Hash(item));
+ }, this);
+ }
+
+ this.loaded = true;
+ this.fireEvent('loadFinished', this);
+ } else {
+ this.fireEvent('loadError', [this, data]);
+ }
+
+ } else {
+ this.loaded = false;
+ this.fireEvent('loadError', [ this, data ]);
+ }
+ },
+
+ /**
+ * APIMethod: getColumns
+ * Allows retrieving the columns array
+ */
+ getColumns: function () {
+ return this.options.columns;
+ },
+
+ /**
+ * APIMethod: findByColumn
+ * Used to find a specific record by the value in a specific column. This
+ * is particularly useful for finding records by a unique id column. The search
+ * will stop on the first instance of the value
+ *
+ * Parameters:
+ * column - the name of the column to search by
+ * value - the value to look for
+ * inPage - flag telling method whether to search only in the current page.
+ * Defaults to true.
+ */
+ findByColumn: function (column, value, inPage) {
+
+ inPage = $defined(inPage) ? inPage : true;
+
+ if (!$defined(this.comparator)) {
+ this.comparator = new Jx.Compare({
+ separator : this.options.separator
+ });
+ }
+
+ column = this.resolveCol(column);
+
+ var fn = this.comparator[column.type].bind(this.comparator);
+
+
+ var i = 0;
+ var index = null;
+ if (inPage) {
+ this.data[this.pageIndex].each(function (record) {
+ if (fn(record.get(column.name), value) === 0) {
+ index = i;
+ }
+ i++;
+ }, this);
+ } else {
+ this.data.each(function (page) {
+ page.each(function (record) {
+ if (fn(record.get(column.name), value) === 0) {
+ index = i;
+ }
+ i++;
+ }, this);
+ }, this);
+ }
+ return index;
+ },
+
+ /**
+ * APIMethod: getRowObject
+ * Allows the user to get all of the data for the current row as an object.
+ *
+ */
+ getRowObject: function () {
+ return this.data[this.pageIndex][this.index].getClean();
+ }
+});
+// $Id: $
+/**
+ * Class: Jx.Store.Remote
+ *
+ * Extends: <Jx.Store>
+ *
+ * This class adds the ability to load/save data remotely.
+ *
+ * Events:
+ * onSaveSuccess() - event fired when all saving happens successfully
+ * onSaveError() - event fired when the server returns an error during saving
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Store.Remote = new Class({
+
+ Extends : Jx.Store,
+
+ options : {
+ /**
+ * Option: dataUrl
+ * The URL to get data from
+ */
+ dataUrl : '',
+ /**
+ * Option: autoSave
+ * Whether to automatically save data changes
+ */
+ autoSave : false,
+ /**
+ * Option: saveUrl
+ * The URL to send data to to be saved
+ */
+ saveUrl : ''
+ },
+
+ saveCount : 0,
+ continueSaving : true,
+ /**
+ * APIMethod: init
+ * Creates the Remote Store.
+ */
+ init : function () {
+ this.parent();
+ this.addEvent('newrow', this.onNewRow.bind(this));
+ this.addEvent('columnChanged', this.saveRow.bind(this));
+ },
+
+ /**
+ * APIMethod: load
+ * Used to load data either locally or remote
+ *
+ * Parameters:
+ * params - an object of params to pass to load. These will be sent in the request.
+ */
+ load : function (params) {
+ this.params = $defined(params) ? params : {};
+ this.remoteLoad(params);
+ },
+
+ /**
+ * APIMethod: refresh
+ * Override of base function <Jx.Store#refresh>. Allow refreshing data from the server
+ *
+ * Parameters:
+ * params - an object of params to pass to load. These will be sent in the request.
+ * reset - whether to reset the counter after the refresh
+ */
+ refresh : function (params, reset) {
+ //Call the load function to get the data
+ //from the server and reset the counter if requested
+ if ($defined(this.options.dataUrl)) {
+ this.params = $defined(params) ? params : this.params;
+ this.load(this.params);
+ } else {
+ return null;
+ }
+ if (reset) {
+ this.index = 0;
+ }
+
+ },
+
+ /**
+ * APIMethod: save
+ * Determines if a row is dirty and needs to be saved to the server.
+ */
+ save : function () {
+ if ($defined(this.data)) {
+ //count how many rows to save
+ this.data.each(function (row, index) {
+ if (this.isRowDirty(row)) {
+ this.saveCount++;
+ }
+ }, this);
+ //save all dirty rows
+ this.data.each(function (row, index) {
+ if (this.isRowDirty(row) && this.continueSaving) {
+ row.erase('dirty');
+ this.remoteSave(row);
+ }
+ }, this);
+ } else {
+ return null;
+ }
+ },
+
+ saveRow: function (index, column, oldValue, newValue) {
+ if (this.options.autoSave) {
+ this.remoteSave(this.data[index]);
+ }
+ },
+
+ /**
+ * Method: onNewRow
+ * Called when a new row is added (event listener). If autoSave is set, this will
+ * fire off the save method.
+ */
+ onNewRow : function () {
+ if (this.options.autoSave) {
+ this.save();
+ }
+ },
+
+ /**
+ * Method: remoteSave
+ * Actually does the work of sending the row to the server for saving.
+ *
+ * Parameters:
+ * data - the row to save
+ */
+ remoteSave : function (data) {
+ //save the data passed in.
+ if (Jx.type(data) === 'hash' && this.continueSaving) {
+ // save it
+ var d = data.getClean();
+ var req = new Request.JSON({
+ data : d,
+ url : this.options.saveUrl,
+ onSuccess : this.processReturn.bind(this),
+ onFailure : this.handleSaveError.bind(this),
+ method : 'post'
+ });
+ req.send();
+ } else {
+ //don't save it
+ return false;
+ }
+ },
+
+ /**
+ * Method: remoteLoad
+ * Calls the server to get data
+ */
+ remoteLoad : function (params) {
+ params = $defined(params) ? params : {};
+ var req = new Request.JSON({
+ url : this.options.dataUrl,
+ data: params,
+ onSuccess : this.processGetReturn.bind(this),
+ onFailure : this.handleLoadError.bind(this),
+ method : 'get'
+ });
+ req.send();
+ },
+
+ /**
+ * Method: processReturn
+ * processes the return from the save request
+ *
+ * Parameters:
+ * data - decoded JSON object
+ * text - the JSON object as a string
+ */
+ processReturn : function (data, text) {
+ if ($defined(data) && $defined(data.success) && data.success === true) {
+ this.processSaveReturn(data.data);
+ } else {
+ this.handleSaveError(data, text);
+ }
+ },
+ /**
+ * Method: processGetReturn
+ * Processes returned data from the get request
+ *
+ * Parameters:
+ * data - decoded JSON object
+ * text - the JSON object as a string
+ */
+ processGetReturn : function (data, text) {
+ if ($defined(data) && $defined(data.success) && data.success === true) {
+ this.processGetData(data.data);
+ } else {
+ this.handleLoadError(data, text);
+ }
+ },
+ /**
+ * Method: processSaveReturn
+ * Private function. Decreases save counter and fires saveSuccess event when all rows are saved
+ *
+ * Parameters:
+ * data - json data returned from server
+ */
+ processSaveReturn : function (data) {
+ this.saveCount--;
+ if (this.saveCount === 0) {
+ this.fireEvent('saveSuccess', this);
+ }
+ },
+
+ /**
+ * Method: handleSaveError
+ * Private function. Handles the case where the server returns an error (no JSON object, usually a 500 or 404 type error)
+ * Fires saveError event in this case and sets continue saving to false.
+ *
+ * Parameters:
+ * data - the data returned from the server
+ * text - the text version of the data
+ */
+ handleSaveError : function (data, text) {
+ this.continueSaving = false;
+ this.fireEvent('saveError', [ this, data, text ]);
+ },
+
+ /**
+ * Method: handleLoadError
+ * Private function. Handles problems with loading data by firing the loadError event.
+ *
+ * Parameters:
+ * data - the data returned from the server
+ * text - the text version of the data
+ */
+ handleLoadError : function (data, text) {
+ this.fireEvent('loadError', [ this, data ]);
+ },
+
+ /**
+ * Method: processGetData
+ * Private function. Used to process data retrieved from the server
+ *
+ * Parameters:
+ * data - the data returned from the server
+ * text - the text version of the data
+ */
+ processGetData : function (data) {
+ if ($defined(data.columns)) {
+ this.options.columns = data.columns;
+ }
+ this.processData(data.data);
+ }
+
+});
+// $Id: $
+/**
+ * class: Jx.Sort.Mergesort
+ *
+ * Extends: <Jx.Sort>
+ *
+ * Implementation of a mergesort algorithm designed to
+ * work on <Jx.Store> data.
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Sort.Mergesort = new Class({
+
+ Extends : Jx.Sort,
+
+ name : 'mergesort',
+
+ /**
+ * APIMethod: sort
+ * Actually runs the sort on the data
+ *
+ * returns: the sorted data
+ */
+ sort : function () {
+ this.fireEvent('start');
+ var d = this.mergeSort(this.data);
+ this.fireEvent('stop');
+ return d;
+
+ },
+
+ /**
+ * Method: mergeSort
+ * Does the physical sorting. Called
+ * recursively.
+ *
+ * Parameters:
+ * arr - the array to sort
+ *
+ * returns: the sorted array
+ */
+ mergeSort : function (arr) {
+ if (arr.length <= 1) {
+ return arr;
+ }
+
+ var middle = (arr.length) / 2;
+ var left = arr.slice(0, middle);
+ var right = arr.slice(middle);
+ left = this.mergeSort(left);
+ right = this.mergeSort(right);
+ var result = this.merge(left, right);
+ return result;
+ },
+
+ /**
+ * Method: merge
+ * Does the work of merging to arrays in order.
+ *
+ * parameters:
+ * left - the left hand array
+ * right - the right hand array
+ *
+ * returns: the merged array
+ */
+ merge : function (left, right) {
+ var result = [];
+
+ while (left.length > 0 && right.length > 0) {
+ if (this.comparator((left[0]).get(this.col), (right[0])
+ .get(this.col)) <= 0) {
+ result.push(left[0]);
+ left = left.slice(1);
+ } else {
+ result.push(right[0]);
+ right = right.slice(1);
+ }
+ }
+ while (left.length > 0) {
+ result.push(left[0]);
+ left = left.slice(1);
+ }
+ while (right.length > 0) {
+ result.push(right[0]);
+ right = right.slice(1);
+ }
+ return result;
+ }
+
+});
+// $Id: $
+/**
+ * Class: Jx.Sort.Heapsort
+ *
+ * Extends: <Jx.Sort>
+ *
+ * Implementation of a heapsort algorithm designed to
+ * work on <Jx.Store> data.
+ *
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Sort.Heapsort = new Class({
+
+ Extends : Jx.Sort,
+
+ name : 'heapsort',
+
+ /**
+ * APIMethod: sort
+ * Actually runs the sort on the data
+ *
+ * Returns: the sorted data
+ */
+ sort : function () {
+ this.fireEvent('start');
+
+ var count = this.data.length;
+
+ if (count === 1) {
+ return this.data;
+ }
+
+ if (count > 2) {
+ this.heapify(count);
+
+ var end = count - 1;
+ while (end > 1) {
+ this.data.swap(end, 0);
+ end = end - 1;
+ this.siftDown(0, end);
+ }
+ } else {
+ // check then order the two we have
+ if ((this.comparator((this.data[0]).get(this.col), (this.data[1])
+ .get(this.col)) > 0)) {
+ this.data.swap(0, 1);
+ }
+ }
+
+ this.fireEvent('stop');
+ return this.data;
+ },
+
+ /**
+ * Method: heapify
+ * Puts the data in Max-heap order
+ *
+ * Parameters: count - the number of records we're sorting
+ */
+ heapify : function (count) {
+ var start = Math.round((count - 2) / 2);
+
+ while (start >= 0) {
+ this.siftDown(start, count - 1);
+ start = start - 1;
+ }
+ },
+
+ /**
+ * Method: siftDown
+ *
+ * Parameters: start - the beginning of the sort range end - the end of the
+ * sort range
+ */
+ siftDown : function (start, end) {
+ var root = start;
+
+ while (root * 2 <= end) {
+ var child = root * 2;
+ if ((child + 1 < end) && (this.comparator((this.data[child]).get(this.col),
+ (this.data[child + 1]).get(this.col)) < 0)) {
+ child = child + 1;
+ }
+ if ((this.comparator((this.data[root]).get(this.col),
+ (this.data[child]).get(this.col)) < 0)) {
+ this.data.swap(root, child);
+ root = child;
+ } else {
+ return;
+ }
+ }
+ }
+
+});
+// $Id: $
+/**
+ * Class: Jx.Sort.Quicksort
+ *
+ * Extends: <Jx.Sort>
+ *
+ * Implementation of a quicksort algorithm designed to
+ * work on <Jx.Store> data.
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Sort.Quicksort = new Class({
+
+ Extends : Jx.Sort,
+
+ name : 'quicksort',
+
+ /**
+ * APIMethod: sort
+ * Actually runs the sort on the data
+ *
+ * returns: the sorted data
+ */
+ sort : function (left, right) {
+ this.fireEvent('start');
+
+ if (!$defined(left)) {
+ left = 0;
+ }
+ if (!$defined(right)) {
+ right = this.data.length - 1;
+ }
+
+ this.quicksort(left, right);
+
+ this.fireEvent('stop');
+
+ return this.data;
+
+ },
+
+ /**
+ * Method: quicksort
+ * Initiates the sorting. Is
+ * called recursively
+ *
+ * Parameters:
+ * left - the left hand, or lower, bound of the sort
+ * right - the right hand, or upper, bound of the sort
+ */
+ quicksort : function (left, right) {
+ if (left >= right) {
+ return;
+ }
+
+ var index = this.partition(left, right);
+ this.quicksort(left, index - 1);
+ this.quicksort(index + 1, right);
+ },
+
+ /**
+ * Method: partition
+ *
+ * Parameters:
+ * left - the left hand, or lower, bound of the sort
+ * right - the right hand, or upper, bound of the sort
+ */
+ partition : function (left, right) {
+ this.findMedianOfMedians(left, right);
+ var pivotIndex = left;
+ var pivotValue = (this.data[pivotIndex]).get(this.col);
+ var index = left;
+ var i;
+
+ this.data.swap(pivotIndex, right);
+ for (i = left; i < right; i++) {
+ if (this.comparator((this.data[i]).get(this.col),
+ pivotValue) < 0) {
+ this.data.swap(i, index);
+ index = index + 1;
+ }
+ }
+ this.data.swap(right, index);
+
+ return index;
+
+ },
+
+ /**
+ * Method: findMedianOfMedians
+ *
+ * Parameters: l
+ * eft - the left hand, or lower, bound of the sort
+ * right - the right hand, or upper, bound of the sort
+ */
+ findMedianOfMedians : function (left, right) {
+ if (left === right) {
+ return this.data[left];
+ }
+
+ var i;
+ var shift = 1;
+ while (shift <= (right - left)) {
+ for (i = left; i <= right; i += shift * 5) {
+ var endIndex = (i + shift * 5 - 1 < right) ? i + shift * 5 - 1 : right;
+ var medianIndex = this.findMedianIndex(i, endIndex,
+ shift);
+
+ this.data.swap(i, medianIndex);
+ }
+ shift *= 5;
+ }
+
+ return this.data[left];
+ },
+
+ /**
+ * Method: findMedianIndex
+ *
+ * Parameters:
+ * left - the left hand, or lower, bound of the sort
+ * right - the right hand, or upper, bound of the sort
+ */
+ findMedianIndex : function (left, right, shift) {
+ var groups = Math.round((right - left) / shift + 1);
+ var k = Math.round(left + groups / 2 * shift);
+ if (k > this.data.length - 1) {
+ k = this.data.length - 1;
+ }
+ for (var i = left; i < k; i += shift) {
+ var minIndex = i;
+ var v = this.data[minIndex];
+ var minValue = v.get(this.col);
+
+ for (var j = i; j <= right; j += shift) {
+ if (this.comparator((this.data[j]).get(this.col),
+ minValue) < 0) {
+ minIndex = j;
+ minValue = (this.data[minIndex]).get(this.col);
+ }
+ }
+ this.data.swap(i, minIndex);
+ }
+
+ return k;
+ }
+});
+// $Id: $
+/**
+ * Class: Jx.Sort.Nativesort
+ *
+ * Extends: <Jx.Sort>
+ *
+ * Implementation of a native sort algorithm designed to work on <Jx.Store> data.
+ *
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Sort.Nativesort = new Class({
+
+ Extends : Jx.Sort,
+
+ name : 'nativesort',
+
+ /**
+ * Method: sort
+ * Actually runs the sort on the data
+ *
+ * Returns:
+ * the sorted data
+ */
+ sort : function () {
+ this.fireEvent('start');
+
+ var compare = function (a, b) {
+ return this.comparator((this.data[a]).get(this.col), (this.data[b])
+ .get(this.col));
+ };
+
+ this.data.sort(compare);
+ this.fireEvent('stop');
+ return this.data;
+ }
+
+});
+// $Id: button.js 524 2009-09-18 05:40:16Z jonlb at comcast.net $
+/**
* Class: Jx.Button
*
- * Extends: Object
+ * Extends: <Jx.Widget>
*
- * Implements: Options, Events, <Jx.Addable>
- *
* Jx.Button creates a clickable element that can be added to a web page.
* When the button is clicked, it fires a 'click' event.
*
@@ -10444,7 +17533,7 @@
*/
Jx.Button = new Class({
Family: 'Jx.Button',
- Implements: [Options,Events,Jx.Addable],
+ Extends: Jx.Widget,
/**
* the HTML element that is inserted into the DOM for this button. You
@@ -10459,12 +17548,6 @@
* container.
*/
id: '',
- /* Option: type
- * optional. A string value that indicates what type of button this
- * is. The default value is Button. The type is used to form the CSS
- * class names used for various HTML elements within the button.
- */
- type: 'Button',
/* Option: image
* optional. A string value that is the url to load the image to
* display in this button. The default styles size this image to 16 x
@@ -10487,21 +17570,6 @@
* default true, whether the button is a toggle button or not.
*/
toggle: false,
- /* Option: toggleClass
- * defaults to Toggle, this is class is added to buttons with the
- * option toggle: true
- */
- toggleClass: 'Toggle',
- /* Option: halign
- * horizontal alignment of the button label, 'center' by default.
- * Other values are 'left' and 'right'.
- */
- halign: 'center',
- /* Option: valign
- * {String} vertical alignment of the button label, 'middle' by
- * default. Other values are 'top' and 'bottom'.
- */
- valign: 'middle',
/* Option: active
* optional, default false. Controls the initial state of toggle
* buttons.
@@ -10511,123 +17579,115 @@
* whether the button is enabled or not.
*/
enabled: true,
- /* Option: container
- * the tag name of the HTML element that should be created to contain
- * the button, by default this is 'div'.
+ /* Option: template
+ * the HTML structure of the button. As a minimum, there must be a
+ * containing element with a class of jxButtonContainer and an internal
+ * element with a class of jxButton. jxButtonIcon and jxButtonLabel are
+ * used if present to put the image and label into the button.
*/
- container: 'div'
+ template: '<span class="jxButtonContainer"><a class="jxButton"><span class="jxButtonContent"><img class="jxButtonIcon" src="'+Jx.aPixel.src+'"><span class="jxButtonLabel"></span></span></a></span>'
},
+ type: 'Button',
+ classes: ['jxButtonContainer', 'jxButton','jxButtonIcon','jxButtonLabel'],
+ elements: null,
/**
- * Constructor: Jx.Button
+ * APIMethod: render
* create a new button.
- *
- * Parameters:
- * options - {Object} an object containing optional properties for this
- * button as below.
*/
- initialize : function( options ) {
- this.setOptions(options);
+ render: function() {
+ this.parent();
+ this.elements = this.processTemplate(this.options.template, this.classes);
- // the main container for the button
- var d = new Element(this.options.container, {'class': 'jx'+this.options.type+'Container'});
- if (this.options.toggle && this.options.toggleClass) {
- d.addClass('jx'+this.options.type+this.options.toggleClass);
+ this.domObj = this.elements.get('jx'+this.type+'Container');
+ this.domA = this.elements.get('jx'+this.type);
+ this.domImg = this.elements.get('jx'+this.type+'Icon');
+ this.domLabel = this.elements.get('jx'+this.type + 'Label');
+
+ /* is the button toggle-able? */
+ if (this.options.toggle) {
+ this.domObj.addClass('jx'+this.type+'Toggle');
}
+
// the clickable part of the button
- var hasFocus;
- var mouseDown;
- var a = new Element('a', {
- 'class': 'jx'+this.options.type,
- href: 'javascript:void(0)',
- title: this.options.tooltip,
- alt: this.options.tooltip,
- events: {
+ if (this.domA) {
+ var hasFocus;
+ var mouseDown;
+ this.domA.set({
+ href: 'javascript:void(0)',
+ title: this.options.tooltip,
+ alt: this.options.tooltip
+ });
+ this.domA.addEvents({
click: this.clicked.bindWithEvent(this),
drag: (function(e) {e.stop();}).bindWithEvent(this),
mousedown: (function(e) {
- this.domA.addClass('jx'+this.options.type+'Pressed');
+ this.domA.addClass('jx'+this.type+'Pressed');
hasFocus = true;
mouseDown = true;
this.focus();
}).bindWithEvent(this),
mouseup: (function(e) {
- this.domA.removeClass('jx'+this.options.type+'Pressed');
+ this.domA.removeClass('jx'+this.type+'Pressed');
mouseDown = false;
}).bindWithEvent(this),
mouseleave: (function(e) {
- this.domA.removeClass('jx'+this.options.type+'Pressed');
+ this.domA.removeClass('jx'+this.type+'Pressed');
}).bindWithEvent(this),
mouseenter: (function(e) {
if (hasFocus && mouseDown) {
- this.domA.addClass('jx'+this.options.type+'Pressed');
+ this.domA.addClass('jx'+this.type+'Pressed');
}
}).bindWithEvent(this),
keydown: (function(e) {
if (e.key == 'enter') {
- this.domA.addClass('jx'+this.options.type+'Pressed');
+ this.domA.addClass('jx'+this.type+'Pressed');
}
}).bindWithEvent(this),
keyup: (function(e) {
if (e.key == 'enter') {
- this.domA.removeClass('jx'+this.options.type+'Pressed');
+ this.domA.removeClass('jx'+this.type+'Pressed');
}
}).bindWithEvent(this),
blur: function() { hasFocus = false; }
+ });
+
+ if (typeof Drag != 'undefined') {
+ new Drag(this.domA, {
+ onStart: function() {this.stop();}
+ });
}
- });
- d.adopt(a);
-
- if (typeof Drag != 'undefined') {
- new Drag(a, {
- onStart: function() {this.stop();}
- });
}
-
- var s = new Element('span', {'class': 'jx'+this.options.type+'Content'});
- a.adopt(s);
-
- if (this.options.image || !this.options.label) {
- var i = new Element('img', {
- 'class':'jx'+this.options.type+'Icon',
- 'src': Jx.aPixel.src,
- title: this.options.tooltip,
- alt: this.options.tooltip
- });
- //if image is not a_pixel, set the background image of the image
- //otherwise let the default css take over.
- if (this.options.image && this.options.image.indexOf('a_pixel.png') == -1) {
- i.setStyle('backgroundImage',"url("+this.options.image+")");
+
+ if (this.domImg) {
+ if (this.options.image || !this.options.label) {
+ this.domImg.set({
+ title: this.options.tooltip,
+ alt: this.options.tooltip
+ });
+ if (this.options.image && this.options.image.indexOf('a_pixel.png') == -1) {
+ this.domImg.setStyle('backgroundImage',"url("+this.options.image+")");
+ }
+ if (this.options.imageClass) {
+ this.domImg.addClass(this.options.imageClass);
+ }
+ } else {
+ //remove the image if we don't need it
+ this.domImg.setStyle('display','none');
}
- s.appendChild(i);
- if (this.options.imageClass) {
- i.addClass(this.options.imageClass);
- }
- this.domImg = i;
}
- var l = new Element('span', {
- html: this.options.label
- });
- if (this.options.label) {
- l.addClass('jx'+this.options.type+'Label');
+ if (this.domLabel) {
+ if (this.options.label) {
+ this.domLabel.set('html',this.options.label);
+ } else {
+ this.domLabel.removeClass('jx'+this.type+'Label');
+ }
}
- s.appendChild(l);
if (this.options.id) {
- d.id = this.options.id;
+ this.domObj.set('id', this.options.id);
}
- if (this.options.halign == 'left') {
- d.addClass('jx'+this.options.type+'ContentLeft');
- }
-
- if (this.options.valign == 'top') {
- d.addClass('jx'+this.options.type+'ContentTop');
- }
- this.domA = a;
- this.domLabel = l;
- this.domObj = d;
-
//update the enabled state
this.setEnabled(this.options.enabled);
@@ -10706,10 +17766,10 @@
}
this.options.active = active;
if (this.options.active) {
- this.domA.addClass('jx'+this.options.type+'Active');
+ this.domA.addClass('jx'+this.type+'Active');
this.fireEvent('down', this);
} else {
- this.domA.removeClass('jx'+this.options.type+'Active');
+ this.domA.removeClass('jx'+this.type+'Active');
this.fireEvent('up', this);
}
},
@@ -10722,42 +17782,30 @@
*/
setImage: function(path) {
this.options.image = path;
- if (path) {
- if (!this.domImg) {
- var i = new Element('img', {
- 'class':'jx'+this.options.type+'Icon',
- 'src': Jx.aPixel.src,
- alt: '',
- title: ''
- });
- if (this.options.imageClass) {
- i.addClass(this.options.imageClass);
- }
- this.domA.firstChild.grab(i, 'top');
- this.domImg = i;
- }
- this.domImg.setStyle('backgroundImage',"url("+this.options.image+")");
- } else if (this.domImg){
- this.domImg.dispose();
- this.domImg = null;
+ if (this.domImg) {
+ this.domImg.setStyle('backgroundImage',
+ "url("+this.options.image+")");
+ this.domImg.setStyle('display', path ? null : 'none');
}
},
/**
* Method: setLabel
*
- * sets the text of the button. Only works if a label was supplied
- * when the button was constructed
+ * sets the text of the button.
*
* Parameters:
*
* label - {String} the new label for the button
*/
setLabel: function(label) {
- this.domLabel.set('html', label);
- if (!label && this.domLabel.hasClass('jxButtonLabel')) {
- this.domLabel.removeClass('jxButtonLabel');
- } else if (label && !this.domLabel.hasClass('jxButtonLabel')) {
- this.domLabel.addClass('jxButtonLabel');
+ this.options.label = label;
+ if (this.domLabel) {
+ this.domLabel.set('html', label);
+ if (!label && this.domLabel.hasClass('jxButtonLabel')) {
+ this.domLabel.removeClass('jxButtonLabel');
+ } else if (label && !this.domLabel.hasClass('jxButtonLabel')) {
+ this.domLabel.addClass('jxButtonLabel');
+ }
}
},
/**
@@ -10766,7 +17814,7 @@
* returns the text of the button.
*/
getLabel: function() {
- return this.domLabel ? this.domLabel.innerHTML : '';
+ return this.options.label;
},
/**
* Method: setTooltip
@@ -10798,7 +17846,7 @@
this.domA.blur();
}
});
-// $Id: flyout.js 424 2009-05-12 12:51:44Z pagameba $
+// $Id: flyout.js 524 2009-09-18 05:40:16Z jonlb at comcast.net $
/**
* Class: Jx.Button.Flyout
*
@@ -10832,6 +17880,9 @@
* flyout buttons inside the content area of another flyout button. In this
* case, opening the inner flyout will not close the outer flyout but it will
* close any other flyouts that are siblings.
+ *
+ * The options argument takes a combination of options that apply to <Jx.Button>,
+ * <Jx.ContentLoader>, and <Jx.AutoPosition>.
*
* Example:
* (code)
@@ -10859,7 +17910,6 @@
Jx.Button.Flyout = new Class({
Family: 'Jx.Button.Flyout',
Extends: Jx.Button,
- Implements: [Jx.ContentLoader, Jx.AutoPosition, Jx.Chrome],
/**
* Property: content
@@ -10867,22 +17917,15 @@
*/
content: null,
/**
- * Constructor: initialize
- * construct a new instance of a flyout button. The single options
- * argument takes a combination of options that apply to <Jx.Button>,
- * <Jx.ContentLoader>, and <Jx.AutoPosition>.
- *
- * Parameters:
- * options - an options object used to initialize the button, see
- * <Jx.Button.Options>, <Jx.ContentLoader.Options>, and
- * <Jx.AutoPosition.Options> for details.
+ * APIMethod: render
+ * construct a new instance of a flyout button.
*/
- initialize: function(options) {
+ render: function() {
if (!Jx.Button.Flyout.Stack) {
Jx.Button.Flyout.Stack = [];
}
- this.parent(options);
- this.domA.addClass('jx'+this.options.type+'Flyout');
+ this.parent();
+ this.domA.addClass('jx'+this.type+'Flyout');
this.contentContainer = new Element('div',{
'class':'jxFlyout'
@@ -10916,14 +17959,14 @@
/* find out what we are contained by if we don't already know */
if (!this.owner) {
this.owner = document.body;
- var node = $(this.domObj.parentNode);
+ var node = document.id(this.domObj.parentNode);
while (node != document.body && this.owner == document.body) {
var flyout = node.retrieve('jxFlyout');
if (flyout) {
this.owner = flyout;
break;
} else {
- node = $(node.parentNode);
+ node = document.id(node.parentNode);
}
}
}
@@ -10955,9 +17998,9 @@
Jx.Button.Flyout.Stack.push(this);
this.options.active = true;
- this.domA.addClass('jx'+this.options.type+'Active');
+ this.domA.addClass('jx'+this.type+'Active');
this.contentContainer.setStyle('visibility','hidden');
- $(document.body).adopt(this.contentContainer);
+ document.id(document.body).adopt(this.contentContainer);
this.content.getChildren().each(function(child) {
if (child.resize) {
child.resize();
@@ -10974,7 +18017,7 @@
/* we have to size the container for IE to render the chrome correctly
* there is some horrible peekaboo bug in IE 6
*/
- this.contentContainer.setContentBoxSize($(this.content).getMarginBoxSize());
+ this.contentContainer.setContentBoxSize(document.id(this.content).getMarginBoxSize());
this.contentContainer.setStyle('visibility','');
@@ -11000,7 +18043,7 @@
/* hide flyout if the user clicks outside of the flyout */
clickHandler: function(e) {
e = new Event(e);
- var elm = $(e.target);
+ var elm = document.id(e.target);
var flyout = Jx.Button.Flyout.Stack[Jx.Button.Flyout.Stack.length - 1];
if (!elm.descendantOf(flyout.content) &&
!elm.descendantOf(flyout.domObj)) {
@@ -11014,14 +18057,12 @@
Jx.Button.Flyout.Stack[Jx.Button.Flyout.Stack.length - 1].hide();
}
}
-});// $Id: layout.js 424 2009-05-12 12:51:44Z pagameba $
+});// $Id: layout.js 524 2009-09-18 05:40:16Z jonlb at comcast.net $
/**
* Class: Jx.Layout
*
- * Extends: Object
+ * Extends: <Jx.Object>
*
- * Implements: Options, Events
- *
* Jx.Layout is used to provide more flexible layout options for applications
*
* Jx.Layout wraps an existing DOM element (typically a div) and provides
@@ -11047,7 +18088,7 @@
Jx.Layout = new Class({
Family: 'Jx.Layout',
- Implements: [Options,Events],
+ Extends: Jx.Object,
options: {
/* Option: propagate
@@ -11126,17 +18167,20 @@
*/
maxHeight: -1
},
+
/**
- * Constructor: Jx.Layout
- * Create a new instance of Jx.Layout.
- *
* Parameters:
* domObj - {HTMLElement} element or id to apply the layout to
* options - <Jx.Layout.Options>
*/
- initialize: function(domObj, options) {
- this.setOptions(options);
- this.domObj = $(domObj);
+ parameters: ['domObj','options'],
+
+ /**
+ * APIMethod: init
+ * Create a new instance of Jx.Layout.
+ */
+ init: function() {
+ this.domObj = document.id(this.options.domObj);
this.domObj.resize = this.resize.bind(this);
this.domObj.setStyle('position', this.options.position);
this.domObj.store('jxLayout', this);
@@ -11190,7 +18234,7 @@
needsResize = true;
}
}
- if (!$(this.domObj.parentNode)) {
+ if (!document.id(this.domObj.parentNode)) {
return;
}
@@ -11198,7 +18242,7 @@
if (this.domObj.parentNode.tagName == 'BODY') {
parentSize = Jx.getPageDimensions();
} else {
- parentSize = $(this.domObj.parentNode).getContentBoxSize();
+ parentSize = document.id(this.domObj.parentNode).getContentBoxSize();
}
if (this.lastParentSize && !needsResize) {
@@ -11411,11 +18455,13 @@
/* apply the new sizes */
var sizeOpts = {width: w};
if (this.options.position == 'absolute') {
- var padding = $(this.domObj.parentNode).getPaddingSize();
+ var m = document.id(this.domObj.parentNode).measure(function(){
+ return this.getSizes(['padding'],['left','top']).padding;
+ });
this.domObj.setStyles({
position: this.options.position,
- left: l+padding.left,
- top: t+padding.top
+ left: l+m.left,
+ top: t+m.top
});
sizeOpts.height = h;
} else {
@@ -11437,14 +18483,12 @@
this.fireEvent('sizeChange',this);
}
-});// $Id: tab.js 424 2009-05-12 12:51:44Z pagameba $
+});// $Id: tab.js 524 2009-09-18 05:40:16Z jonlb at comcast.net $
/**
* Class: Jx.Button.Tab
*
* Extends: <Jx.Button>
*
- * Implements: <Jx.ContentLoader>
- *
* A single tab in a tab set. A tab has a label (displayed in the tab) and a
* content area that is displayed when the tab is active. A tab has to be
* added to both a <Jx.TabSet> (for the content) and <Jx.Toolbar> (for the
@@ -11485,48 +18529,44 @@
Jx.Button.Tab = new Class({
Family: 'Jx.Button.Tab',
Extends: Jx.Button,
- Implements: [Jx.ContentLoader],
/**
* Property: content
* {HTMLElement} The content area that is displayed when the tab is active.
*/
content: null,
+
+ options: {
+ template: '<div class="jxTabContainer"><a class="jxTab"><span class="jxTabContent"><img class="jxTabIcon"><span class="jxTabLabel"></span></span></a><a class="jxTabClose"><img src="'+Jx.aPixel.src+'"></a></div>'
+ },
+ type: 'Tab',
+ classes: ['jxTabContainer','jxTab','jxTabIcon','jxTabLabel','jxTabClose'],
+
/**
- * Constructor: Jx.Button.Tab
+ * APIMethod: render
* Create a new instance of Jx.Button.Tab. Any layout options passed are used
* to create a <Jx.Layout> for the tab content area.
- *
- * Parameters:
- * options - {Object} an object containing options that are used
- * to control the appearance of the tab. See <Jx.Button>,
- * <Jx.ContentLoader::loadContent> and <Jx.Layout::Jx.Layout> for
- * valid options.
*/
- initialize : function( options) {
- this.parent($merge(options, {type:'Tab', toggle:true}));
+ render : function( ) {
+ this.options = $merge(this.options, {toggle:true});
+ this.parent();
this.content = new Element('div', {'class':'tabContent'});
- new Jx.Layout(this.content, options);
+ new Jx.Layout(this.content, this.options);
this.loadContent(this.content);
var that = this;
this.addEvent('down', function(){that.content.addClass('tabContentActive');});
this.addEvent('up', function(){that.content.removeClass('tabContentActive');});
- if (this.options.close) {
- this.domObj.addClass('jxTabClose');
- var a = new Element('a', {
- 'class': 'jxTabClose',
- events: {
- 'click': (function(){
- this.fireEvent('close');
- }).bind(this)
- }
- });
- a.adopt(new Element('img', {
- src: Jx.aPixel.src,
- alt: '',
- title: ''
- }));
- this.domObj.adopt(a);
+ //remove the close button if necessary
+ var closer = this.elements.get('jx'+this.type+'Close');
+ if (closer) {
+ if (this.options.close) {
+ this.domObj.addClass('jx'+this.type+'Close');
+ closer.addEvent('click', (function(){
+ this.fireEvent('close');
+ }).bind(this));
+ } else {
+ closer.dispose();
+ }
}
},
/**
@@ -11539,20 +18579,18 @@
this.setActive(true);
}
}
-});// $Id: colorpalette.js 473 2009-07-08 16:46:21Z pagameba $
+});// $Id: colorpalette.js 524 2009-09-18 05:40:16Z jonlb at comcast.net $
/**
* Class: Jx.ColorPalette
*
- * Extends: Object
+ * Extends: <Jx.Widget>
*
- * Implements: Options, Events, <Jx.Addable>
- *
* A Jx.ColorPalette presents a user interface for selecting colors.
* Currently, the user can either enter a HEX colour value or select from a
* palette of web-safe colours. The user can also enter an opacity value.
*
* A Jx.ColorPalette can be embedded anywhere in a web page using its addTo
- * method. However, a <Jx.Button> subclass is provided (<Jx.Button.Color>)
+ * method. However, a <Jx.Button> suJx.Tooltipbclass is provided (<Jx.Button.Color>)
* that embeds a colour panel inside a button for easy use in toolbars.
*
* Colour changes are propogated via a change event. To be notified
@@ -11573,7 +18611,7 @@
*/
Jx.ColorPalette = new Class({
Family: 'Jx.ColorPalette',
- Implements: [Options, Events, Jx.Addable],
+ Extends: Jx.Widget,
/**
* Property: {HTMLElement} domObj
* the HTML element representing the color panel
@@ -11603,15 +18641,10 @@
alphaLabel: 'alpha (%)'
},
/**
- * Constructor: Jx.ColorPalette
+ * APIMethod: render
* initialize a new instance of Jx.ColorPalette
- *
- * Parameters:
- * options - <Jx.ColorPalette.Options>
*/
- initialize: function(options) {
- this.setOptions(options);
-
+ render: function() {
this.domObj = new Element('div', {
id: this.options.id,
'class':'jxColorPalette'
@@ -11725,8 +18758,7 @@
a.store('swatchColor', bgColor);
td.adopt(a);
} else {
- td.addClass('emptyCell');
- var span = new Element('span');
+ var span = new Element('span', {'class':'emptyCell'});
td.adopt(span);
}
tr.adopt(td);
@@ -11846,7 +18878,7 @@
}
});
-// $Id: color.js 424 2009-05-12 12:51:44Z pagameba $
+// $Id: color.js 524 2009-09-18 05:40:16Z jonlb at comcast.net $
/**
* Class: Jx.Button.Color
*
@@ -11903,14 +18935,10 @@
},
/**
- * Constructor: Jx.Button.Color
- * initialize a new color button.
- *
- * Parameters:
- * options - <Jx.Button.Color.Options> initialize instance options.
- *
+ * APIMethod: render
+ * creates a new color button.
*/
- initialize: function(options) {
+ render: function() {
if (!Jx.Button.Color.ColorPalette) {
Jx.Button.Color.ColorPalette = new Jx.ColorPalette(this.options);
}
@@ -11923,10 +18951,10 @@
this.hideFn = this.hide.bind(this);
/* we need to have an image to replace, but if a label is
requested, there wouldn't normally be an image. */
- options.image = Jx.aPixel.src;
+ this.options.image = Jx.aPixel.src;
/* now we can safely initialize */
- this.parent(options);
+ this.parent();
// now replace the image with our swatch
d.replaces(this.domImg);
@@ -12034,14 +19062,12 @@
this.selectedSwatch.setStyles(styles);
}
});
-// $Id: menu.js 424 2009-05-12 12:51:44Z pagameba $
+// $Id: menu.js 524 2009-09-18 05:40:16Z jonlb at comcast.net $
/**
* Class: Jx.Menu
*
- * Extends: Object
+ * Extends: <Jx.Widget>
*
- * Implements: Options, Events, <Jx.AutoPosition>, <Jx.Chrome>, <Jx.Addable>
- *
* A main menu as opposed to a sub menu that lives inside the menu.
*
* TODO: Jx.Menu
@@ -12059,21 +19085,8 @@
*/
Jx.Menu = new Class({
Family: 'Jx.Menu',
+ Extends: Jx.Widget,
/**
- * Implements:
- * * Options
- * * Events
- * * <Jx.AutoPosition>
- * * <Jx.Chrome>
- * * <Jx.Addable>
- */
- Implements: [Options, Events, Jx.AutoPosition, Jx.Chrome, Jx.Addable],
- /**
- * Property: domObj
- * {HTMLElement} The HTML element containing the menu.
- */
- domObj : null,
- /**
* Property: button
* {<Jx.Button>} The button that represents this menu in a toolbar and
* opens the menu.
@@ -12086,47 +19099,58 @@
*/
subDomObj : null,
/**
- * Property: items
- * {Array} the items in this menu
+ * Property: list
+ * {<Jx.List>} the list of items in the menu
*/
- items : null,
+ list: null,
+
+ parameters: ['buttonOptions', 'options'],
+
+ options: {
+ template: "<div class='jxMenuContainer'><ul class='jxMenu'></ul></div>",
+ buttonTemplate: '<span class="jxButtonContainer"><a class="jxButton jxButtonMenu"><span class="jxButtonContent"><img class="jxButtonIcon" src="'+Jx.aPixel.src+'"><span class="jxButtonLabel"></span></span></a></span>',
+ position: {
+ horizontal: ['left left'],
+ vertical: ['bottom top', 'top bottom']
+ }
+ },
+
+ classes: ['jxMenuContainer','jxMenu'],
+
/**
- * Constructor: Jx.Menu
+ * APIMethod: render
* Create a new instance of Jx.Menu.
- *
- * Parameters:
- * options - see <Jx.Button.Options>. If no options are provided then
- * no button is created.
*/
- initialize : function(options) {
- this.setOptions(options);
+ render : function() {
+ this.parent();
if (!Jx.Menu.Menus) {
Jx.Menu.Menus = [];
}
- /* stores menu items and sub menus */
- this.items = [];
+
+ this.elements = this.processTemplate(this.options.template, this.classes);
+
+ this.contentContainer = this.elements.get('jxMenuContainer');
+ this.contentContainer.addEvent('onContextmenu', function(e){e.stop();});
- this.contentContainer = new Element('div',{
- 'class':'jxMenuContainer',
- events: {
- contextmenu: function(e){e.stop();}
- }
- });
+ this.subDomObj = this.elements.get('jxMenu');
- /* the DOM element that holds the actual menu */
- this.subDomObj = new Element('ul',{
- 'class':'jxMenu'
+ this.list = new Jx.List(this.subDomObj, {
+ onAdd: function(item) {
+ item.setOwner(this);
+ }.bind(this),
+ onRemove: function(item) {
+ item.setOwner(null);
+ }.bind(this)
});
-
- this.contentContainer.adopt(this.subDomObj);
-
+
/* if options are passed, make a button inside an LI so the
menu can be embedded inside a toolbar */
- if (options) {
- this.button = new Jx.Button($merge(options,{
+ if (this.options.buttonOptions) {
+ this.button = new Jx.Button($merge(this.options.buttonOptions,{
+ template: this.options.buttonTemplate,
onClick:this.show.bind(this)
}));
- this.button.domA.addClass('jxButtonMenu');
+
this.button.domA.addEvent('mouseover', this.onMouseOver.bindWithEvent(this));
this.domObj = this.button.domObj;
@@ -12148,15 +19172,34 @@
* item - {<Jx.MenuItem>} the menu item to add. Multiple menu items
* can be added by passing multiple arguments to this function.
*/
- add : function() {
- $A(arguments).flatten().each(function(item){
- this.items.push(item);
- item.setOwner(this);
- this.subDomObj.adopt(item.domObj);
- }, this);
+ add: function(item, position) {
+ this.list.add(item, position);
return this;
},
/**
+ * Method: remove
+ * Remove a menu item from the menu
+ *
+ * Parameters:
+ * item - {<Jx.MenuItem>} the menu item to remove
+ */
+ remove: function(item) {
+ this.list.remove(item);
+ return this;
+ },
+ /**
+ * Method: replace
+ * Replace a menu item with another menu item
+ *
+ * Parameters:
+ * what - {<Jx.MenuItem>} the menu item to replace
+ * withWhat - {<Jx.MenuItem>} the menu item to replace it with
+ */
+ replace: function(item, withItem) {
+ this.list.replace(item, withItem);
+ return this;
+ },
+ /**
* Method: deactivate
* Deactivate the menu by hiding it.
*/
@@ -12188,7 +19231,7 @@
* a sub menu of this menu, false otherwise
*/
eventInMenu: function(e) {
- var target = $(e.target);
+ var target = document.id(e.target);
if (!target) {
return false;
}
@@ -12211,6 +19254,16 @@
}
return false;
}
+
+ /*
+ this.list.items().some(
+ function(item) {
+ var menuItem = item.retrieve('jxMenuItem');
+ return menuItem instanceof Jx.Menu.SubMenu &&
+ menuItem.eventInMenu(e);
+ }
+ );
+ */
},
/**
@@ -12236,38 +19289,40 @@
if (this.button && this.button.domA) {
this.button.domA.removeClass('jx'+this.button.options.type+'Active');
}
- this.items.each(function(item){item.hide(e);});
+ this.list.each(function(item){item.retrieve('jxMenuItem').hide(e);});
document.removeEvent('mousedown', this.hideWatcher);
document.removeEvent('keydown', this.keypressWatcher);
- this.contentContainer.setStyle('display','none');
+ this.contentContainer.dispose();
this.fireEvent('hide', this);
},
/**
* Method: show
* Show the menu
- *
- * Parameters:
- * e - {Event} the mouse event
*/
- show : function(o) {
- var e = o.event;
- if (Jx.Menu.Menus[0]) {
- if (Jx.Menu.Menus[0] != this) {
- Jx.Menu.Menus[0].button.blur();
- Jx.Menu.Menus[0].hide(e);
- } else {
- this.hide();
+ show : function() {
+ if (this.button) {
+ if (Jx.Menu.Menus[0]) {
+ if (Jx.Menu.Menus[0] != this) {
+ Jx.Menu.Menus[0].button.blur();
+ Jx.Menu.Menus[0].hide();
+ } else {
+ this.hide();
+ return;
+ }
+ }
+ Jx.Menu.Menus[0] = this;
+ this.button.focus();
+ if (this.list.count() == 0) {
return;
- }
- }
- if (this.items.length === 0) {
- return;
+ }
}
- Jx.Menu.Menus[0] = this;
- this.button.focus();
- this.contentContainer.setStyle('visibility','hidden');
- this.contentContainer.setStyle('display','block');
- $(document.body).adopt(this.contentContainer);
+ this.contentContainer.setStyle('display','none');
+ document.id(document.body).adopt(this.contentContainer);
+ this.contentContainer.setStyles({
+ visibility: 'hidden',
+ display: 'block'
+ });
+
/* we have to size the container for IE to render the chrome correctly
* but just in the menu/sub menu case - there is some horrible peekaboo
* bug in IE related to ULs that we just couldn't figure out
@@ -12275,22 +19330,16 @@
this.contentContainer.setContentBoxSize(this.subDomObj.getMarginBoxSize());
this.showChrome(this.contentContainer);
- this.position(this.contentContainer, this.button.domObj, {
- horizontal: ['left left'],
- vertical: ['bottom top', 'top bottom'],
+ this.position(this.contentContainer, this.domObj, $merge({
offsets: this.chromeOffsets
- });
+ }, this.options.position));
- this.contentContainer.setStyle('visibility','');
+ this.contentContainer.setStyle('visibility','visible');
if (this.button && this.button.domA) {
this.button.domA.addClass('jx'+this.button.options.type+'Active');
}
- if (e) {
- //why were we doing this? it is affecting the closing of
- //other elements like flyouts (issue 13)
- //e.stop();
- }
+
/* fix bug in IE that closes the menu as it opens because of bubbling */
document.addEvent('mousedown', this.hideWatcher);
document.addEvent('keydown', this.keypressWatcher);
@@ -12322,14 +19371,12 @@
}
});
-// $Id: set.js 424 2009-05-12 12:51:44Z pagameba $
+// $Id: set.js 524 2009-09-18 05:40:16Z jonlb at comcast.net $
/**
* Class: Jx.ButtonSet
*
- * Extends: Object
+ * Extends: <Jx.Object>
*
- * Implements: Options, Events
- *
* A ButtonSet manages a set of <Jx.Button> instances by ensuring that only one
* of the buttons is active. All the buttons need to have been created with
* the toggle option set to true for this to work.
@@ -12357,22 +19404,17 @@
*/
Jx.ButtonSet = new Class({
Family: 'Jx.ButtonSet',
- Implements: [Options,Events],
+ Extends: Jx.Object,
/**
* Property: buttons
* {Array} array of buttons that are managed by this button set
*/
buttons: null,
/**
- * Constructor: Jx.ButtonSet
- * Create a new instance of <Jx.ButtonSet>
- *
- * Parameters:
- * options - an options object, only event handlers are supported
- * as options at this time.
+ * APIMethod: init
+ * initializes the button set.
*/
- initialize : function(options) {
- this.setOptions(options);
+ init : function() {
this.buttons = [];
this.buttonChangedHandler = this.buttonChanged.bind(this);
},
@@ -12455,17 +19497,17 @@
-// $Id: multi.js 424 2009-05-12 12:51:44Z pagameba $
+// $Id: multi.js 524 2009-09-18 05:40:16Z jonlb at comcast.net $
/**
* Class: Jx.Button.Multi
*
* Extends: <Jx.Button>
*
- * Implements:
+ * Implements:
*
* Multi buttons are used to contain multiple buttons in a drop down list
* where only one button is actually visible and clickable in the interface.
- *
+ *
* When the user clicks the active button, it performs its normal action.
* The user may also click a drop-down arrow to the right of the button and
* access the full list of buttons. Clicking a button in the list causes
@@ -12476,7 +19518,7 @@
*
* This is not really a button, but rather a container for buttons. The
* button structure is a div containing two buttons, a normal button and
- * a flyout button. The flyout contains a toolbar into which all the
+ * a flyout button. The flyout contains a toolbar into which all the
* added buttons are placed. The main button content is cloned from the
* last button clicked (or first button added).
*
@@ -12507,9 +19549,9 @@
* multiButton.add(b1, b2, b3);
* (end)
*
- * License:
+ * License:
* Copyright (c) 2008, DM Solutions Group Inc.
- *
+ *
* This file is licensed under an MIT style license
*/
Jx.Button.Multi = new Class({
@@ -12526,123 +19568,112 @@
* {Array} the buttons added to this multi button
*/
buttons: null,
+
+ options: {
+ template: '<span class="jxButtonContainer"><a class="jxButton jxButtonMulti"><span class="jxButtonContent"><img src="'+Jx.aPixel.src+'" class="jxButtonIcon"><span class="jxButtonLabel"></span></span></a><a class="jxButtonDisclose" href="javascript:void(0)"><img src="'+Jx.aPixel.src+'"></a></span>'
+ },
+ classes: ['jxButtonContainer','jxButton','jxButtonIcon','jxButtonLabel','jxButtonDisclose'],
+
/**
- * Constructor: Jx.Button.Multi
+ * APIMethod: render
* construct a new instance of Jx.Button.Multi.
*/
- initialize: function(opts) {
- this.parent(opts);
-
+ render: function() {
+ this.parent();
this.buttons = [];
- this.domA.addClass('jxButtonMulti');
-
this.menu = new Jx.Menu();
this.menu.button = this;
this.buttonSet = new Jx.ButtonSet();
-
+
this.clickHandler = this.clicked.bind(this);
-
- var a = new Element('a', {
- 'class': 'jxButtonDisclose',
- 'href': 'javascript:void(0)'
- });
- var button = this;
- var hasFocus;
-
- a.addEvents({
- 'click': (function(e) {
- if (this.items.length === 0) {
- return;
- }
- if (!button.options.enabled) {
- return;
- }
- this.contentContainer.setStyle('visibility','hidden');
- this.contentContainer.setStyle('display','block');
- $(document.body).adopt(this.contentContainer);
- /* we have to size the container for IE to render the chrome correctly
- * but just in the menu/sub menu case - there is some horrible peekaboo
- * bug in IE related to ULs that we just couldn't figure out
- */
- this.contentContainer.setContentBoxSize(this.subDomObj.getMarginBoxSize());
- this.showChrome(this.contentContainer);
+ var a = this.elements.get('jxButtonDisclose');
+ if (a) {
+ var button = this;
+ var hasFocus;
- this.position(this.contentContainer, this.button.domObj, {
- horizontal: ['right right'],
- vertical: ['bottom top', 'top bottom'],
- offsets: this.chromeOffsets
- });
+ a.addEvents({
+ 'click': (function(e) {
+ if (this.items.length === 0) {
+ return;
+ }
+ if (!button.options.enabled) {
+ return;
+ }
+ this.contentContainer.setStyle('visibility','hidden');
+ this.contentContainer.setStyle('display','block');
+ document.id(document.body).adopt(this.contentContainer);
+ /* we have to size the container for IE to render the chrome correctly
+ * but just in the menu/sub menu case - there is some horrible peekaboo
+ * bug in IE related to ULs that we just couldn't figure out
+ */
+ this.contentContainer.setContentBoxSize(this.subDomObj.getMarginBoxSize());
- this.contentContainer.setStyle('visibility','');
+ this.showChrome(this.contentContainer);
- document.addEvent('mousedown', this.hideWatcher);
- document.addEvent('keyup', this.keypressWatcher);
-
- /* why were we doing this? It was affecting issue 13
- if (e.$extended) {
- e.stop();
- } else if (e.event && e.event.$extended) {
- e.event.stop();
- }
- */
- this.fireEvent('show', this);
- }).bindWithEvent(this.menu),
- 'mouseenter':(function(){
- $(this.domObj.firstChild).addClass('jxButtonHover');
- if (hasFocus) {
- a.addClass('jx'+this.options.type+'Pressed');
- }
- }).bind(this),
- 'mouseleave':(function(){
- $(this.domObj.firstChild).removeClass('jxButtonHover');
- a.removeClass('jx'+this.options.type+'Pressed');
- }).bind(this),
- mousedown: (function(e) {
- a.addClass('jx'+this.options.type+'Pressed');
- hasFocus = true;
- this.focus();
- }).bindWithEvent(this),
- mouseup: (function(e) {
- a.removeClass('jx'+this.options.type+'Pressed');
- }).bindWithEvent(this),
- keydown: (function(e) {
- if (e.key == 'enter') {
- a.addClass('jx'+this.options.type+'Pressed');
- }
- }).bindWithEvent(this),
- keyup: (function(e) {
- if (e.key == 'enter') {
- a.removeClass('jx'+this.options.type+'Pressed');
- }
- }).bindWithEvent(this),
- blur: function() { hasFocus = false; }
-
- });
- if (typeof Drag != 'undefined') {
- new Drag(a, {
- onStart: function() {this.stop();}
+ this.position(this.contentContainer, this.button.domObj, {
+ horizontal: ['right right'],
+ vertical: ['bottom top', 'top bottom'],
+ offsets: this.chromeOffsets
+ });
+
+ this.contentContainer.setStyle('visibility','');
+
+ document.addEvent('mousedown', this.hideWatcher);
+ document.addEvent('keyup', this.keypressWatcher);
+
+ this.fireEvent('show', this);
+ }).bindWithEvent(this.menu),
+ 'mouseenter':(function(){
+ document.id(this.domObj.firstChild).addClass('jxButtonHover');
+ if (hasFocus) {
+ a.addClass('jx'+this.type+'Pressed');
+ }
+ }).bind(this),
+ 'mouseleave':(function(){
+ document.id(this.domObj.firstChild).removeClass('jxButtonHover');
+ a.removeClass('jx'+this.type+'Pressed');
+ }).bind(this),
+ mousedown: (function(e) {
+ a.addClass('jx'+this.type+'Pressed');
+ hasFocus = true;
+ this.focus();
+ }).bindWithEvent(this),
+ mouseup: (function(e) {
+ a.removeClass('jx'+this.type+'Pressed');
+ }).bindWithEvent(this),
+ keydown: (function(e) {
+ if (e.key == 'enter') {
+ a.addClass('jx'+this.type+'Pressed');
+ }
+ }).bindWithEvent(this),
+ keyup: (function(e) {
+ if (e.key == 'enter') {
+ a.removeClass('jx'+this.type+'Pressed');
+ }
+ }).bindWithEvent(this),
+ blur: function() { hasFocus = false; }
+
});
+ if (typeof Drag != 'undefined') {
+ new Drag(a, {
+ onStart: function() {this.stop();}
+ });
+ }
+ this.discloser = a;
}
-
+
this.menu.addEvents({
'show': (function() {
- this.domA.addClass('jxButtonActive');
+ this.domA.addClass('jx'+this.type+'Active');
}).bind(this),
'hide': (function() {
if (this.options.active) {
- this.domA.addClass('jxButtonActive');
+ this.domA.addClass('jx'+this.type+'Active');
}
}).bind(this)
});
- a.adopt(new Element('img', {
- src: Jx.aPixel.src,
- alt: '',
- title: ''
- }));
- this.domObj.adopt(a);
- this.discloser = a;
if (this.options.items) {
this.add(this.options.items);
}
@@ -12650,11 +19681,11 @@
/**
* Method: add
* adds one or more buttons to the Multi button. The first button
- * added becomes the active button initialize. This function
+ * added becomes the active button initialize. This function
* takes a variable number of arguments, each of which is expected
* to be an instance of <Jx.Button>.
*
- * Parameters:
+ * Parameters:
* button - {<Jx.Button>} a <Jx.Button> instance, may be repeated in the parameter list
*/
add: function() {
@@ -12664,13 +19695,15 @@
}
this.buttons.push(theButton);
var f = this.setButton.bind(this, theButton);
- var opts = $merge(
- theButton.options,
- { toggle: true, onClick: f}
- );
- if (!opts.label) {
- opts.label = ' ';
- }
+ var opts = {
+ image: theButton.options.image,
+ imageClass: theButton.options.imageClass,
+ label: theButton.options.label || ' ',
+ enabled: theButton.options.enabled,
+ tooltip: theButton.options.tooltip,
+ toggle: true,
+ onClick: f
+ };
if (!opts.image || opts.image.indexOf('a_pixel') != -1) {
delete opts.image;
}
@@ -12722,7 +19755,7 @@
* Method: setActiveButton
* update the menu item to be the requested button.
*
- * Parameters:
+ * Parameters:
* button - {<Jx.Button>} a <Jx.Button> instance that was added to this multi button.
*/
setActiveButton: function(button) {
@@ -12736,8 +19769,8 @@
this.domA.addEvent('click', this.clickHandler);
if (this.options.toggle) {
this.options.active = false;
- this.setActive(true);
- }
+ this.setActive(true);
+ }
}
this.activeButton = button;
},
@@ -12746,14 +19779,14 @@
* update the active button in the menu item, trigger the button's action
* and hide the flyout that contains the buttons.
*
- * Parameters:
+ * Parameters:
* button - {<Jx.Button>} The button to set as the active button
*/
setButton: function(button) {
this.setActiveButton(button);
button.clicked();
}
-});// $Id: menu.item.js 424 2009-05-12 12:51:44Z pagameba $
+});// $Id: menu.item.js 524 2009-09-18 05:40:16Z jonlb at comcast.net $
/**
* Class: Jx.Menu.Item
*
@@ -12787,29 +19820,31 @@
*/
owner: null,
options: {
- enabled: true,
- image: null,
+ //image: null,
label: ' ',
- toggleClass: 'Toggle'
+ /* Option: template
+ * the HTML structure of the button. As a minimum, there must be a
+ * containing element with a class of jxMenuItemContainer and an
+ * internal element with a class of jxMenuItem. jxMenuItemIcon and
+ * jxMenuItemLabel are used if present to put the image and label into
+ * the button.
+ */
+ template: '<li class="jxMenuItemContainer"><a class="jxMenuItem"><span class="jxMenuItemContent"><img class="jxMenuItemIcon" src="'+Jx.aPixel.src+'"><span class="jxMenuItemLabel"></span></span></a></li>'
},
+ classes: ['jxMenuItemContainer', 'jxMenuItem','jxMenuItemIcon','jxMenuItemLabel'],
+ type: 'MenuItem',
/**
- * Constructor: Jx.Menu.Item
+ * APIMethod: render
* Create a new instance of Jx.Menu.Item
- *
- * Parameters:
- * options - See <Jx.Button.Options>
*/
- initialize: function(options) {
- this.parent($merge({
- image: Jx.aPixel.src
- },
- options, {
- container:'li',
- type:'MenuItem',
- toggleClass: (options.image ? null : this.options.toggleClass)
- }
- ));
- this.domObj.addEvent('mouseover', this.onMouseOver.bindWithEvent(this));
+ render: function() {
+ this.options = $merge({image: Jx.aPixel.src}, this.options);
+ this.parent();
+ if (this.options.image) {
+ this.domObj.removeClass('jx'+this.type+'Toggle');
+ }
+ this.domObj.addEvent('mouseover', this.onMouseOver.bind(this));
+ this.domObj.store('jxMenuItem', this);
},
/**
* Method: setOwner
@@ -12854,19 +19889,15 @@
/**
* Method: onmouseover
* handle the mouse moving over the menu item
- *
- * Parameters:
- * e - {Event} the mousemove event
*/
- onMouseOver: function(e) {
+ onMouseOver: function() {
if (this.owner && this.owner.setVisibleItem) {
this.owner.setVisibleItem(this);
}
- this.show(e);
}
});
-// $Id: combo.js 424 2009-05-12 12:51:44Z pagameba $
+// $Id: combo.js 524 2009-09-18 05:40:16Z jonlb at comcast.net $
/**
* Class: Jx.Button.Combo
*
@@ -12900,8 +19931,7 @@
*/
Jx.Button.Combo = new Class({
Family: 'Jx.Button.Combo',
- Extends: Jx.Button.Multi,
- domObj : null,
+ Extends: Jx.Button,
ul : null,
/**
* Property: currentSelection
@@ -12910,79 +19940,34 @@
currentSelection : null,
options: {
- /* Option: editable
- * boolean, default false. Can the value be edited by the user?
- */
- editable: false,
/* Option: label
* string, default ''. The label to display next to the combo.
*/
- label: ''
- },
-
+ label: '',
+ /* Option: template
+ */
+ template: '<span class="jxButtonContainer"><a class="jxButton jxButtonCombo"><span class="jxButtonContent"><img class="jxButtonIcon" src="'+Jx.aPixel.src+'"><span class="jxButtonLabel"></span></span></a></span>'
+ },
+
/**
- * Constructor: Jx.Combo
+ * APIMethod: render
* create a new instance of Jx.Combo
- *
- * Parameters:
- * options - <Jx.button.Combo.Options>
*/
- initialize: function(options) {
- this.parent(); //we don't want to pass options to parent
- this.setOptions(options);
- this.domA.removeClass('jxButtonMulti');
- if (this.options.editable) {
- // remove the button's normal A tag and replace it with a span
- // so the input ends up not being inside an A tag - this was
- // causing all kinds of problems for selecting text inside it
- // due to some user-select: none classes that were introduced
- // to make buttons not selectable in the first place.
- //
- // Ultimately I think we want to fix this so that the discloser
- // in Jx.Button.Multi is a separate beast and we can use it here
- // without inheriting from multi buttons
- var s = new Element('span', {'class':'jxButton'});
- s.adopt(this.domA.firstChild);
- this.domA = s.replaces(this.domA);
- this.domA.addClass('jxButtonComboDefault');
- this.domA.addClass('jxButtonEditCombo');
- this.domInput = new Element('input',{
- type:'text',
- events:{
- change: this.valueChanged.bindWithEvent(this),
- keydown: this.onKeyPress.bindWithEvent(this),
- focus: (function() {
- if (this.domA.hasClass('jxButtonComboDefault')) {
- this.domInput.value = '';
- this.domA.removeClass('jxButtonComboDefault');
- }
- }).bind(this)
- },
- value: this.options.label
- });
- this.domLabel.empty();
- this.domLabel.addClass('jxComboInput');
- this.domLabel.adopt(this.domInput);
- } else {
- this.discloser.dispose();
- this.domA.addClass('jxButtonCombo');
- this.addEvent('click', (function(e){
- this.discloser.fireEvent('click', e);
- }).bindWithEvent(this));
- }
+ render: function() {
+ this.parent();
+
+ this.menu = new Jx.Menu();
+ this.menu.button = this;
+ this.buttonSet = new Jx.ButtonSet();
+
this.buttonSet = new Jx.ButtonSet({
onChange: (function(set) {
var button = set.activeButton;
- this.domA.removeClass('jxButtonComboDefault');
- if (this.options.editable) {
- this.domInput.value = button.options.label;
- } else {
- var l = button.options.label;
- if (l == ' ') {
- l = '';
- }
- this.setLabel(l);
+ var l = button.options.label;
+ if (l == ' ') {
+ l = '';
}
+ this.setLabel(l);
var img = button.options.image;
if (img.indexOf('a_pixel') != -1) {
img = '';
@@ -13001,29 +19986,50 @@
if (this.options.items) {
this.add(this.options.items);
}
- this.setEnabled(this.options.enabled);
- },
-
- /**
- * Method: setEnabled
- * enable or disable the combo button.
- *
- * Parameters:
- * enabled - {Boolean} the new enabled state of the button
- */
- setEnabled: function(enabled) {
- this.options.enabled = enabled;
- if (this.options.enabled) {
- this.domObj.removeClass('jxDisabled');
- if (this.domInput) {
- this.domInput.disabled = false;
+ var button = this;
+ this.addEvent('click', (function(e) {
+ if (this.items.length === 0) {
+ return;
}
- } else {
- this.domObj.addClass('jxDisabled');
- if (this.domInput) {
- this.domInput.disabled = true;
+ if (!button.options.enabled) {
+ return;
}
- }
+ this.contentContainer.setStyle('visibility','hidden');
+ this.contentContainer.setStyle('display','block');
+ $(document.body).adopt(this.contentContainer);
+ /* we have to size the container for IE to render the chrome correctly
+ * but just in the menu/sub menu case - there is some horrible peekaboo
+ * bug in IE related to ULs that we just couldn't figure out
+ */
+ this.contentContainer.setContentBoxSize(this.subDomObj.getMarginBoxSize());
+
+ this.showChrome(this.contentContainer);
+
+ this.position(this.contentContainer, this.button.domObj, {
+ horizontal: ['right right'],
+ vertical: ['bottom top', 'top bottom'],
+ offsets: this.chromeOffsets
+ });
+
+ this.contentContainer.setStyle('visibility','');
+
+ document.addEvent('mousedown', this.hideWatcher);
+ document.addEvent('keyup', this.keypressWatcher);
+
+ this.fireEvent('show', this);
+ }).bindWithEvent(this.menu));
+
+ this.menu.addEvents({
+ 'show': (function() {
+ this.domA.addClass('jxButtonActive');
+ }).bind(this),
+ 'hide': (function() {
+ if (this.options.active) {
+ this.domA.addClass('jxButtonActive');
+ }
+ }).bind(this)
+ });
+
},
/**
@@ -13035,6 +20041,18 @@
},
/**
+ * Method: getValue
+ * returns the currently selected value
+ */
+ getValue: function() {
+ return this.options.label;
+ },
+
+ setValue: function() {
+
+ },
+
+ /**
* Method: onKeyPress
* Handle the user pressing a key by looking for an ENTER key to set the
* value.
@@ -13076,48 +20094,649 @@
*/
remove: function(idx) {
//TODO: implement remove?
+ }
+});// $Id: toolbar.js 524 2009-09-18 05:40:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Toolbar
+ *
+ * Extends: <Jx.Widget>
+ *
+ * A toolbar is a container object that contains other objects such as
+ * buttons. The toolbar organizes the objects it contains automatically,
+ * wrapping them as necessary. Multiple toolbars may be placed within
+ * the same containing object.
+ *
+ * Jx.Toolbar includes CSS classes for styling the appearance of a
+ * toolbar to be similar to traditional desktop application toolbars.
+ *
+ * There is one special object, Jx.ToolbarSeparator, that provides
+ * a visual separation between objects in a toolbar.
+ *
+ * While a toolbar is generally a *dumb* container, it serves a special
+ * purpose for menus by providing some infrastructure so that menus can behave
+ * properly.
+ *
+ * In general, almost anything can be placed in a Toolbar, and mixed with
+ * anything else.
+ *
+ * Example:
+ * The following example shows how to create a Jx.Toolbar instance and place
+ * two objects in it.
+ *
+ * (code)
+ * //myToolbarContainer is the id of a <div> in the HTML page.
+ * function myFunction() {}
+ * var myToolbar = new Jx.Toolbar('myToolbarContainer');
+ *
+ * var myButton = new Jx.Button(buttonOptions);
+ *
+ * var myElement = document.createElement('select');
+ *
+ * myToolbar.add(myButton, new Jx.ToolbarSeparator(), myElement);
+ * (end)
+ *
+ * Events:
+ * add - fired when one or more buttons are added to a toolbar
+ * remove - fired when on eor more buttons are removed from a toolbar
+ *
+ * Implements:
+ * Options
+ *
+ * License:
+ * Copyright (c) 2008, DM Solutions Group Inc.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Toolbar = new Class({
+ Family: 'Jx.Toolbar',
+ Extends: Jx.Widget,
+ /**
+ * Property: items
+ * {Array} an array of the things in the toolbar.
+ */
+ items : null,
+ /**
+ * Property: domObj
+ * {HTMLElement} the HTML element that the toolbar lives in
+ */
+ domObj : null,
+ /**
+ * Property: isActive
+ * When a toolbar contains <Jx.Menu> instances, they want to know
+ * if any menu in the toolbar is active and this is how they
+ * find out.
+ */
+ isActive : false,
+ options: {
+ type: 'Toolbar',
+ /* Option: position
+ * the position of this toolbar in the container. The position
+ * affects some items in the toolbar, such as menus and flyouts, which
+ * need to open in a manner sensitive to the position. May be one of
+ * 'top', 'right', 'bottom' or 'left'. Default is 'top'.
+ */
+ position: 'top',
+ /* Option: parent
+ * a DOM element to add this toolbar to
+ */
+ parent: null,
+ /* Option: autoSize
+ * if true, the toolbar will attempt to set its size based on the
+ * things it contains. Default is false.
+ */
+ autoSize: false,
+ /* Option: scroll
+ * if true, the toolbar may scroll if the contents are wider than
+ * the size of the toolbar
+ */
+ scroll: true
},
+ /**
+ * APIMethod: render
+ * Create a new instance of Jx.Toolbar.
+ */
+ render : function() {
+ this.parent();
+ this.items = [];
+
+ this.domObj = new Element('ul', {
+ id: this.options.id,
+ 'class':'jx'+this.options.type
+ });
+
+ if (this.options.parent) {
+ this.addTo(this.options.parent);
+ }
+ this.deactivateWatcher = this.deactivate.bindWithEvent(this);
+ if (this.options.items) {
+ this.add(this.options.items);
+ }
+ },
/**
- * Method: setValue
- * set the value of the Combo
+ * Method: addTo
+ * add this toolbar to a DOM element automatically creating a toolbar
+ * container if necessary
*
* Parameters:
- * value - {Object} the new value. May be a string, a text node, or
- * another DOM element.
+ * parent - the DOM element or toolbar container to add this toolbar to.
*/
- setValue: function(value) {
- if (this.options.editable) {
- this.domInput.value = value;
+ addTo: function(parent) {
+ var tbc = document.id(parent).retrieve('jxBarContainer');
+ if (!tbc) {
+ tbc = new Jx.Toolbar.Container({
+ parent: parent,
+ position: this.options.position,
+ autoSize: this.options.autoSize,
+ scroll: this.options.scroll
+ });
+ }
+ tbc.add(this);
+ return this;
+ },
+
+ /**
+ * Method: add
+ * Add an item to the toolbar. If the item being added is a Jx component
+ * with a domObj property, the domObj is added. If the item being added
+ * is an LI element, then it is given a CSS class of *jxToolItem*.
+ * Otherwise, the thing is wrapped in a <Jx.ToolbarItem>.
+ *
+ * Parameters:
+ * thing - {Object} the thing to add. More than one thing can be added
+ * by passing multiple arguments.
+ */
+ add: function( ) {
+ $A(arguments).flatten().each(function(thing) {
+ if (thing.domObj) {
+ thing = thing.domObj;
+ }
+ if (thing.tagName == 'LI') {
+ if (!thing.hasClass('jxToolItem')) {
+ thing.addClass('jxToolItem');
+ }
+ this.domObj.appendChild(thing);
+ } else {
+ var item = new Jx.Toolbar.Item(thing);
+ this.domObj.appendChild(item.domObj);
+ }
+ }, this);
+
+ if (arguments.length > 0) {
+ this.fireEvent('add', this);
+ }
+ return this;
+ },
+ /**
+ * Method: remove
+ * remove an item from a toolbar. If the item is not in this toolbar
+ * nothing happens
+ *
+ * Parameters:
+ * item - {Object} the object to remove
+ *
+ * Returns:
+ * {Object} the item that was removed, or null if the item was not
+ * removed.
+ */
+ remove: function(item) {
+ if (item.domObj) {
+ item = item.domObj;
+ }
+ var li = item.findElement('LI');
+ if (li && li.parentNode == this.domObj) {
+ item.dispose();
+ li.dispose();
+ this.fireEvent('remove', this);
} else {
- this.setLabel(value);
+ return null;
}
},
+ /**
+ * Method: deactivate
+ * Deactivate the Toolbar (when it is acting as a menu bar).
+ */
+ deactivate: function() {
+ this.items.each(function(o){o.hide();});
+ this.setActive(false);
+ },
+ /**
+ * Method: isActive
+ * Indicate if the toolbar is currently active (as a menu bar)
+ *
+ * Returns:
+ * {Boolean}
+ */
+ isActive: function() {
+ return this.isActive;
+ },
+ /**
+ * Method: setActive
+ * Set the active state of the toolbar (for menus)
+ *
+ * Parameters:
+ * b - {Boolean} the new state
+ */
+ setActive: function(b) {
+ this.isActive = b;
+ if (this.isActive) {
+ document.addEvent('click', this.deactivateWatcher);
+ } else {
+ document.removeEvent('click', this.deactivateWatcher);
+ }
+ },
+ /**
+ * Method: setVisibleItem
+ * For menus, they want to know which menu is currently open.
+ *
+ * Parameters:
+ * obj - {<Jx.Menu>} the menu that just opened.
+ */
+ setVisibleItem: function(obj) {
+ if (this.visibleItem && this.visibleItem.hide && this.visibleItem != obj) {
+ this.visibleItem.hide();
+ }
+ this.visibleItem = obj;
+ if (this.isActive()) {
+ this.visibleItem.show();
+ }
+ },
+ showItem: function(item) {
+ this.fireEvent('show', item);
+ }
+});
+// $Id: container.js 524 2009-09-18 05:40:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Toolbar.Container
+ *
+ * Extends: <Jx.Widget>
+ *
+ * A toolbar container contains toolbars. A single toolbar container fills
+ * the available space horizontally. Toolbars placed in a toolbar container
+ * do not wrap when they exceed the available space.
+ *
+ * Events:
+ * add - fired when one or more toolbars are added to a container
+ * remove - fired when one or more toolbars are removed from a container
+ *
+ * Implements:
+ * Options
+ * Events
+ * {<Jx.Addable>}
+ *
+ * License:
+ * Copyright (c) 2008, DM Solutions Group Inc.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Toolbar.Container = new Class({
+ Family: 'Jx.Toolbar.Container',
+ Extends: Jx.Widget,
+ /**
+ * Property: domObj
+ * {HTMLElement} the HTML element that the container lives in
+ */
+ domObj : null,
+ options: {
+ /* Option: parent
+ * a DOM element to add this to
+ */
+ parent: null,
+ /* Option: position
+ * the position of the toolbar container in its parent, one of 'top',
+ * 'right', 'bottom', or 'left'. Default is 'top'
+ */
+ position: 'top',
+ /* Option: autoSize
+ * automatically size the toolbar container to fill its container.
+ * Default is false
+ */
+ autoSize: false,
+ /* Option: scroll
+ * Control whether the user can scroll of the content of the
+ * container if the content exceeds the size of the container.
+ * Default is true.
+ */
+ scroll: true
+ },
+ /**
+ * APIMethod: render
+ * Create a new instance of Jx.Toolbar.Container
+ */
+ render : function() {
+ this.parent();
+
+ var d = document.id(this.options.parent);
+ this.domObj = d || new Element('div');
+ this.domObj.addClass('jxBarContainer');
+
+ if (this.options.scroll) {
+ this.scroller = new Element('div', {'class':'jxBarScroller'});
+ this.domObj.adopt(this.scroller);
+ }
+
+ /* this allows toolbars to add themselves to this bar container
+ * once it already exists without requiring an explicit reference
+ * to the toolbar container
+ */
+ this.domObj.store('jxBarContainer', this);
+
+ if (['top','right','bottom','left'].contains(this.options.position)) {
+ this.domObj.addClass('jxBar' +
+ this.options.position.capitalize());
+ } else {
+ this.domObj.addClass('jxBarTop');
+ this.options.position = 'top';
+ }
+
+ if (this.options.scroll && ['top','bottom'].contains(this.options.position)) {
+ // make sure we update our size when we get added to the DOM
+ this.addEvent('addTo', this.update.bind(this));
+
+ //making Fx.Tween optional
+ if (typeof Fx != 'undefined' && typeof Fx.Tween != 'undefined'){
+ this.scrollFx = scrollFx = new Fx.Tween(this.scroller, {
+ link: 'chain'
+ });
+ }
+
+ this.scrollLeft = new Jx.Button({
+ image: Jx.aPixel.src
+ }).addTo(this.domObj);
+ this.scrollLeft.domObj.addClass('jxBarScrollLeft');
+ this.scrollLeft.addEvents({
+ click: (function(){
+ var from = this.scroller.getStyle('left').toInt();
+ if (isNaN(from)) { from = 0; }
+ var to = Math.min(from+100, 0);
+ if (to >= 0) {
+ this.scrollLeft.domObj.setStyle('visibility', 'hidden');
+ }
+ this.scrollRight.domObj.setStyle('visibility', '');
+ if ($defined(this.scrollFx)){
+ this.scrollFx.start('left', from, to);
+ } else {
+ this.scroller.setStyle('left',to);
+ }
+ }).bind(this)
+ });
+
+ this.scrollRight = new Jx.Button({
+ image: Jx.aPixel.src
+ }).addTo(this.domObj);
+ this.scrollRight.domObj.addClass('jxBarScrollRight');
+ this.scrollRight.addEvents({
+ click: (function(){
+ var from = this.scroller.getStyle('left').toInt();
+ if (isNaN(from)) { from = 0; }
+ var to = Math.max(from - 100, this.scrollWidth);
+ if (to == this.scrollWidth) {
+ this.scrollRight.domObj.setStyle('visibility', 'hidden');
+ }
+ this.scrollLeft.domObj.setStyle('visibility', '');
+ if ($defined(this.scrollFx)){
+ this.scrollFx.start('left', from, to);
+ } else {
+ this.scroller.setStyle('left',to);
+ }
+ }).bind(this)
+ });
+
+ } else {
+ this.options.scroll = false;
+ }
+
+ if (this.options.toolbars) {
+ this.add(this.options.toolbars);
+ }
+ },
+ update: function() {
+ if (this.options.autoSize) {
+ /* delay the size update a very small amount so it happens
+ * after the current thread of execution finishes. If the
+ * current thread is part of a window load event handler,
+ * rendering hasn't quite finished yet and the sizes are
+ * all wrong
+ */
+ (function(){
+ var x = 0;
+ this.scroller.getChildren().each(function(child){
+ x+= child.getSize().x;
+ });
+ this.domObj.setStyles({width:x});
+ this.measure();
+ }).delay(1,this);
+ } else {
+ this.measure();
+ }
+ },
+
+ measure: function() {
+ if (!this.options.scroll) { return; }
+
+ if ((!this.scrollLeftSize || !this.scrollLeftSize.x) && this.domObj.parentNode) {
+ this.scrollLeftSize = this.scrollLeft.domObj.getSize();
+ this.scrollRightSize = this.scrollRight.domObj.getSize();
+ }
+ /* decide if we need to show the scroller buttons and
+ * do some calculations that will make it faster
+ */
+ this.scrollWidth = this.domObj.getSize().x;
+ this.scroller.getChildren().each(function(child){
+ this.scrollWidth -= child.getSize().x;
+ }, this);
+ if (this.scrollWidth < 0) {
+ /* we need to show scrollers on at least one side */
+ var l = this.scroller.getStyle('left').toInt();
+ if (l < 0) {
+ this.scrollLeft.domObj.setStyle('visibility','');
+ } else {
+ this.scrollLeft.domObj.setStyle('visibility','hidden');
+ }
+ if (l <= this.scrollWidth) {
+ this.scrollRight.domObj.setStyle('visibility', 'hidden');
+ if (l < this.scrollWidth) {
+ if ($defined(this.scrollFx)){
+ this.scrollFx.start('left', l, this.scrollWidth);
+ } else {
+ this.scroller.setStyle('left',this.scrollWidth);
+ }
+ }
+ } else {
+ this.scrollRight.domObj.setStyle('visibility', '');
+ }
+
+ } else {
+ /* don't need any scrollers but we might need to scroll
+ * the toolbar into view
+ */
+ this.scrollLeft.domObj.setStyle('visibility','hidden');
+ this.scrollRight.domObj.setStyle('visibility','hidden');
+ var from = this.scroller.getStyle('left').toInt();
+ if (!isNaN(from) && from !== 0) {
+ if ($defined(this.scrollFx)) {
+ this.scrollFx.start('left', 0);
+ } else {
+ this.scroller.setStyle('left',0);
+ }
+ }
+ }
+ },
+
/**
- * Method: getValue
- * Return the current value
+ * Method: add
+ * Add a toolbar to the container.
*
+ * Parameters:
+ * toolbar - {Object} the toolbar to add. More than one toolbar
+ * can be added by passing multiple arguments.
+ */
+ add: function( ) {
+ $A(arguments).flatten().each(function(thing) {
+ if (this.options.scroll) {
+ /* we potentially need to show or hide scroller buttons
+ * when the toolbar contents change
+ */
+ thing.addEvent('add', this.update.bind(this));
+ thing.addEvent('remove', this.update.bind(this));
+ thing.addEvent('show', this.scrollIntoView.bind(this));
+ }
+ if (this.scroller) {
+ this.scroller.adopt(thing.domObj);
+ } else {
+ this.domObj.adopt(thing.domObj);
+ }
+ this.domObj.addClass('jx'+thing.options.type+this.options.position.capitalize());
+ }, this);
+ if (this.options.scroll) {
+ this.update();
+ }
+ if (arguments.length > 0) {
+ this.fireEvent('add', this);
+ }
+ return this;
+ },
+ /**
+ * Method: remove
+ * remove an item from a toolbar. If the item is not in this toolbar
+ * nothing happens
+ *
+ * Parameters:
+ * item - {Object} the object to remove
+ *
* Returns:
- * {Object} returns the currently selected item
+ * {Object} the item that was removed, or null if the item was not
+ * removed.
*/
- getValue: function() {
- value = '';
- if (this.options.editable) {
- value = this.domInput.value;
+ remove: function(item) {
+
+ },
+ /**
+ * Method: scrollIntoView
+ * scrolls an item in one of the toolbars into the currently visible
+ * area of the container if it is not already fully visible
+ *
+ * Parameters:
+ * item - the item to scroll.
+ */
+ scrollIntoView: function(item) {
+ var width = this.domObj.getSize().x;
+ var coords = item.domObj.getCoordinates(this.scroller);
+
+ //left may be set to auto or even a zero length string.
+ //In the previous version, in air, this would evaluate to
+ //NaN which would cause the right hand scroller to show when
+ //the component was first created.
+
+ //So, get the left value first
+ var l = this.scroller.getStyle('left');
+ //then check to see if it's auto or a zero length string
+ if (l === 'auto' || l.length <= 0) {
+ //If so, set to 0.
+ l = 0;
} else {
- value = this.getLabel();
+ //otherwise, convert to int
+ l = l.toInt();
}
- return value;
+ var slSize = this.scrollLeftSize ? this.scrollLeftSize.x : 0;
+ var srSize = this.scrollRightSize ? this.scrollRightSize.x : 0;
+
+ var left = l;
+ if (l < -coords.left + slSize) {
+ /* the left edge of the item is not visible */
+ left = -coords.left + slSize;
+ if (left >= 0) {
+ left = 0;
+ }
+ } else if (width - coords.right - srSize< l) {
+ /* the right edge of the item is not visible */
+ left = width - coords.right - srSize;
+ if (left < this.scrollWidth) {
+ left = this.scrollWidth;
+ }
+ }
+
+ if (left < 0) {
+ this.scrollLeft.domObj.setStyle('visibility','');
+ } else {
+ this.scrollLeft.domObj.setStyle('visibility','hidden');
+ }
+ if (left <= this.scrollWidth) {
+ this.scrollRight.domObj.setStyle('visibility', 'hidden');
+ } else {
+ this.scrollRight.domObj.setStyle('visibility', '');
+ }
+ if (left != l) {
+ if ($defined(this.scrollFx)) {
+ this.scrollFx.start('left', left);
+ } else {
+ this.scroller.setStyle('left',left);
+ }
+ }
}
-});// $Id: panel.js 429 2009-05-12 16:10:47Z pagameba $
+});
+// $Id: toolbar.item.js 524 2009-09-18 05:40:16Z jonlb at comcast.net $
/**
+ * Class: Jx.Toolbar.Item
+ *
+ * Extends: Object
+ *
+ * Implements: Options
+ *
+ * A helper class to provide a container for something to go into
+ * a <Jx.Toolbar>.
+ *
+ * License:
+ * Copyright (c) 2008, DM Solutions Group Inc.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Toolbar.Item = new Class( {
+ Family: 'Jx.Toolbar.Item',
+ Extends: Jx.Object,
+ options: {
+ /* Option: active
+ * is this item active or not? Default is true.
+ */
+ active: true
+ },
+ /**
+ * Property: domObj
+ * {HTMLElement} an element to contain the thing to be placed in the
+ * toolbar.
+ */
+ domObj: null,
+
+ parameters: ['jxThing'],
+
+ /**
+ * APIMethod: init
+ * Create a new instance of Jx.Toolbar.Item.
+ */
+ init : function() {
+ this.al = [];
+ this.domObj = new Element('li', {'class':'jxToolItem'});
+ if (this.options.jxThing) {
+ if (this.options.jxThing.domObj) {
+ this.domObj.appendChild(this.options.jxThing.domObj);
+ if (this.options.jxThing instanceof Jx.Button.Tab) {
+ this.domObj.addClass('jxTabItem');
+ }
+ } else {
+ this.domObj.appendChild(this.options.jxThing);
+ if (this.options.jxThing.hasClass('jxTab')) {
+ this.domObj.addClass('jxTabItem');
+ }
+ }
+ }
+ }
+});// $Id: panel.js 524 2009-09-18 05:40:16Z jonlb at comcast.net $
+/**
* Class: Jx.Panel
*
- * Extends: Object
+ * Extends: <Jx.Widget>
*
- * Implements: Options, Events, <Jx.ContentLoader>
- *
* A panel is a fundamental container object that has a content
* area and optional toolbars around the content area. It also
* has a title bar area that contains an optional label and
@@ -13140,7 +20759,7 @@
*/
Jx.Panel = new Class({
Family: 'Jx.Panel',
- Implements: [Options, Events, Jx.ContentLoader, Jx.Addable],
+ Extends: Jx.Widget,
toolbarContainers: {
top: null,
@@ -13150,7 +20769,7 @@
},
options: {
- position: 'absolute',
+ position: null,
type: 'Panel',
/* Option: id
* String, an id to assign to the panel's container
@@ -13224,18 +20843,14 @@
},
/**
- * Constructor: Jx.Panel
+ * APIMethod: render
* Initialize a new Jx.Panel instance
- *
- * Options: <Jx.Panel.Options>, <Jx.ContentLoader.Options>
*/
- initialize : function(options){
- this.setOptions(options);
- this.toolbars = options ? options.toolbars || [] : [];
+ render : function(){
+ this.parent();
+ this.toolbars = this.options ? this.options.toolbars || [] : [];
- if ($defined(this.options.height) && !$defined(options.position)) {
- this.options.position = 'relative';
- }
+ this.options.position = ($defined(this.options.height) && !$defined(this.options.position)) ? 'relative' : 'absolute';
/* set up the title object */
this.title = new Element('div', {
@@ -13369,7 +20984,7 @@
});
this.domObj.adopt(this.contentContainer);
- if ($type(this.options.toolbars) == 'array') {
+ if (Jx.type(this.options.toolbars) == 'array') {
this.options.toolbars.each(function(tb){
var position = tb.options.position;
var tbc = this.toolbarContainers[position];
@@ -13441,16 +21056,16 @@
this.toolbarContainers[position].style.height = '';
}
}, this);
- if ($type(this.options.toolbars) == 'array') {
+ if (Jx.type(this.options.toolbars) == 'array') {
this.options.toolbars.each(function(tb){
position = tb.options.position;
tbc = this.toolbarContainers[position];
// IE 6 doesn't seem to want to measure the width of
// things correctly
if (Browser.Engine.trident4) {
- var oldParent = $(tbc.parentNode);
+ var oldParent = document.id(tbc.parentNode);
tbc.style.visibility = 'hidden';
- $(document.body).adopt(tbc);
+ document.id(document.body).adopt(tbc);
}
var size = tbc.getBorderBoxSize();
// put it back into its real parent now we are done
@@ -13622,8 +21237,10 @@
if (!this.domObj.hasClass('jx'+this.options.type+'Min')) {
this.domObj.addClass('jx'+this.options.type+'Min');
this.contentContainer.setStyle('display','none');
- var margin = this.domObj.getMarginSize();
- var height = margin.top + margin.bottom;
+ var m = this.domObj.measure(function(){
+ return this.getSizes(['margin'],['top','bottom']).margin;
+ });
+ var height = m.top + m.bottom;
if (this.title.parentNode == this.domObj) {
height += this.title.getMarginBoxSize().height;
}
@@ -13649,14 +21266,12 @@
this.fireEvent('close', this);
}
-});// $Id: dialog.js 477 2009-07-09 17:41:50Z pagameba $
+});// $Id: dialog.js 524 2009-09-18 05:40:16Z jonlb at comcast.net $
/**
* Class: Jx.Dialog
*
* Extends: <Jx.Panel>
*
- * Implements: <Jx.AutoPosition>, <Jx.Chrome>
- *
* A Jx.Dialog implements a floating dialog. Dialogs represent a useful way
* to present users with certain information or application controls.
* Jx.Dialog is designed to provide the same types of features as traditional
@@ -13696,7 +21311,7 @@
Jx.Dialog = new Class({
Family: 'Jx.Dialog',
Extends: Jx.Panel,
- Implements: [Jx.AutoPosition, Jx.Chrome],
+ //Implements: [Jx.AutoPosition, Jx.Chrome],
/**
* Property: {HTMLElement} blanket
@@ -13751,7 +21366,7 @@
* the dialog is to be contained by. The default value is for the dialog
* to be contained by the body element.
*/
- parent: null,
+ //parent: null,
/* Option: resize
* (optional) {Boolean} determines whether the dialog is
* resizeable by the user or not. Default is false.
@@ -13773,26 +21388,23 @@
close: true
},
/**
- * Constructor: Jx.Dialog
- * Construct a new instance of Jx.Dialog
- *
- * Parameters:
- * options - {Object} an object containing options for the dialog.
- *
- * Options: <Jx.Dialog.Options>, <Jx.Panel.Options>, <Jx.ContentLoader.Options>
+ * APIMethod: render
+ * renders Jx.Dialog
*/
- initialize: function(options) {
+ render: function() {
this.isOpening = false;
this.firstShow = true;
- /* initialize the panel overriding the type and position */
- this.parent($merge(
+ this.options = $merge(
{parent:document.body}, // these are defaults that can be overridden
- options,
+ this.options,
{type:'Dialog', position: 'absolute'} // these override anything passed to the options
- ));
+ );
- this.options.parent = $(this.options.parent);
+ /* initialize the panel overriding the type and position */
+ this.parent();
+ this.openOnLoaded = this.open.bind(this);
+ this.options.parent = document.id(this.options.parent);
if (this.options.modal) {
this.blanket = new Element('div',{
@@ -13803,7 +21415,7 @@
}
});
this.blanket.resize = (function() {
- var ss = $(document.body).getScrollSize();
+ var ss = document.id(document.body).getScrollSize();
this.setStyles({
width: ss.x,
height: ss.y
@@ -13964,9 +21576,11 @@
}
if (this.options.closed) {
- var margin = this.domObj.getMarginSize();
+ var m = this.domObj.measure(function(){
+ return this.getSizes(['margin'],['top','bottom']).margin;
+ });
var size = this.title.getMarginBoxSize();
- this.domObj.resize({height: margin.top + size.height + margin.bottom});
+ this.domObj.resize({height: m.top + size.height + m.bottom});
this.fireEvent('collapse');
} else {
this.domObj.resize(this.options);
@@ -14002,9 +21616,11 @@
}
if (this.options.closed) {
- var margin = this.domObj.getMarginSize();
+ var m = this.domObj.measure(function(){
+ return this.getSizes(['margin'],['top','bottom']).margin;
+ });
var size = this.title.getMarginBoxSize();
- this.domObj.resize({height: margin.top + size.height + margin.bottom});
+ this.domObj.resize({height: m.top + size.height + m.bottom});
} else {
this.domObj.resize(this.options);
}
@@ -14053,9 +21669,13 @@
openURL: function(url) {
if (url) {
this.options.contentURL = url;
+ this.options.content = null; //force Url loading
this.loadContent(this.content);
+ this.addEvent('contentLoaded', this.openOnLoaded);
}
- this.open();
+ else {
+ this.open();
+ }
},
/**
@@ -14070,11 +21690,12 @@
this.isOpening = true;
}
if (this.contentIsLoaded) {
+ this.removeEvent('contentLoaded', this.openOnLoaded);
this.show();
this.fireEvent('open', this);
this.isOpening = false;
} else {
- this.addEvent('contentLoaded', this.open.bind(this));
+ this.addEvent('contentLoaded', this.openOnLoaded);
}
},
/**
@@ -14086,6 +21707,10 @@
this.isOpening = false;
this.hide();
this.fireEvent('close');
+ },
+
+ cleanup: function() {
+ this.blanket.destroy();
}
});
@@ -14097,22 +21722,20 @@
Jx.Dialog.BaseZIndex = Math.max(Jx.Dialog.Stack[0].domObj.getStyle('zIndex').toInt(), 1);
}
Jx.Dialog.Stack.each(function(d, i) {
- var z = Jx.Dialog.BaseZIndex+(i*2);
+ var z = Jx.Dialog.BaseZIndex+i;
if (d.blanket) {
- d.blanket.setStyle('zIndex',z-1);
+ d.blanket.setStyle('zIndex',z);
}
d.domObj.setStyle('zIndex',z);
});
};
-// $Id: splitter.js 424 2009-05-12 12:51:44Z pagameba $
+// $Id: splitter.js 524 2009-09-18 05:40:16Z jonlb at comcast.net $
/**
* Class: Jx.Splitter
*
- * Extends: Object
+ * Extends: <Jx.Object>
*
- * Implements: Options
- *
* a Jx.Splitter creates two or more containers within a parent container
* and provides user control over the size of the containers. The split
* can be made horizontally or vertically.
@@ -14133,7 +21756,7 @@
Jx.Splitter = new Class({
Family: 'Jx.Splitter',
- Implements: [Options],
+ Extends: Jx.Object,
/**
* Property: domObj
* {HTMLElement} the element being split
@@ -14217,18 +21840,15 @@
*/
onFinish: null
},
+
+ parameters: ['domObj','options'],
+
/**
- * Constructor: Jx.Splitter
+ * APIMethod: init
* Create a new instance of Jx.Splitter
- *
- * Parameters:
- * domObj - {HTMLElement} the element or id of the element to split
- * options - <Jx.Splitter.Options>
*/
- initialize: function(domObj, options) {
- this.setOptions(options);
-
- this.domObj = $(domObj);
+ init: function() {
+ this.domObj = document.id(this.options.domObj);
this.domObj.addClass('jxSplitContainer');
var jxLayout = this.domObj.retrieve('jxLayout');
if (jxLayout) {
@@ -14249,10 +21869,10 @@
for (var i=0; i<nSplits; i++) {
var el;
if (this.options.elements && this.options.elements[i]) {
- if (options.elements[i].domObj) {
- el = options.elements[i].domObj;
+ if (this.options.elements[i].domObj) {
+ el = this.options.elements[i].domObj;
} else {
- el = $(this.options.elements[i]);
+ el = document.id(this.options.elements[i]);
}
if (!el) {
el = this.prepareElement();
@@ -14380,9 +22000,17 @@
new Drag(bar, {
//limit: limit,
modifiers: modifiers,
- onSnap : function(obj) {
+ onSnap : (function(obj) {
obj.addClass('jxSplitBarDrag');
- },
+ this.fireEvent('snap',[obj]);
+ }).bind(this),
+ onCancel: (function(obj){
+ mask.destroy();
+ this.fireEvent('cancel',[obj]);
+ }).bind(this),
+ onDrag: (function(obj, event){
+ this.fireEvent('drag',[obj,event]);
+ }).bind(this),
onComplete : (function(obj) {
mask.destroy();
obj.removeClass('jxSplitBarDrag');
@@ -14390,17 +22018,15 @@
return;
}
fn.apply(this,[obj]);
+ this.fireEvent('complete',[obj]);
+ this.fireEvent('finish',[obj]);
}).bind(this),
- onStart: (function(obj) {
+ onBeforeStart: (function(obj) {
+ this.fireEvent('beforeStart',[obj]);
mask = new Element('div',{'class':'jxSplitterMask'}).inject(obj, 'after');
- if (this.options.onStart) {
- this.options.onStart();
- }
}).bind(this),
- onFinish: (function() {
- if (this.options.onFinish) {
- this.options.onFinish();
- }
+ onStart: (function(obj, event) {
+ this.fireEvent('start',[obj, event]);
}).bind(this)
});
}, this);
@@ -14422,7 +22048,10 @@
var leftJxl = leftSide.retrieve('jxLayout');
var rightJxl = rightSide.retrieve('jxLayout');
- var paddingLeft = this.domObj.getPaddingSize().left;
+ var paddingLeft = this.domObj.measure(function(){
+ var m = this.getSizes(['padding'], ['left']);
+ return m.padding.left;
+ });
/* process right side first */
var rsLeft, rsWidth, rsRight;
@@ -14524,8 +22153,12 @@
var topJxl = topSide.retrieve('jxLayout');
var bottomJxl = bottomSide.retrieve('jxLayout');
- var paddingTop = this.domObj.getPaddingSize().top;
+ var paddingTop = this.domObj.measure(function(){
+ var m = this.getSizes(['padding'], ['top']);
+ return m.padding.top;
+ });
+
/* measure the bar and parent container for later use */
var size = obj.retrieve('size');
if (!size) {
@@ -14675,7 +22308,10 @@
/* account for rounding errors */
var remainder = availableSpace % nVariable;
- var leftPadding = this.domObj.getPaddingSize().left;
+ var leftPadding = this.domObj.measure(function(){
+ var m = this.getSizes(['padding'], ['left']);
+ return m.padding.left;
+ });
var currentPosition = 0;
@@ -14779,7 +22415,10 @@
/* account for rounding errors */
var remainder = availableSpace % nVariable;
- var paddingTop = this.domObj.getPaddingSize().top;
+ var paddingTop = this.domObj.measure(function(){
+ var m = this.getSizes(['padding'], ['top']);
+ return m.padding.top;
+ });
var currentPosition = 0;
@@ -14834,14 +22473,12 @@
}
}
}
-});// $Id: panelset.js 424 2009-05-12 12:51:44Z pagameba $
+});// $Id: panelset.js 524 2009-09-18 05:40:16Z jonlb at comcast.net $
/**
* Class: Jx.PanelSet
*
- * Extends: Object
+ * Extends: <Jx.Widget>
*
- * Implements: Options, Events, <Jx.Addable>
- *
* A panel set manages a set of panels within a DOM element. The PanelSet fills
* its container by resizing the panels in the set to fill the width and then
* distributing the height of the container across all the panels. Panels
@@ -14870,7 +22507,7 @@
*/
Jx.PanelSet = new Class({
Family: 'Jx.PanelSet',
- Implements: [Options, Events, Jx.Addable],
+ Extends: Jx.Widget,
options: {
/* Option: parent
@@ -14903,21 +22540,14 @@
*/
firstLayout: true,
/**
- * Constructor: Jx.PanelSet
+ * APIMethod: render
* Create a new instance of Jx.PanelSet.
- *
- * Parameters:
- * options - <Jx.PanelSet.Options>
- *
- * TODO: Jx.PanelSet.initialize
- * Remove the panels parameter in favour of an add method.
*/
- initialize: function(options) {
- if (options && options.panels) {
- this.panels = options.panels;
- options.panels = null;
+ render: function() {
+ if (this.options.panels) {
+ this.panels = this.options.panels;
+ this.options.panels = null;
}
- this.setOptions(options);
this.domObj = new Element('div');
new Jx.Layout(this.domObj);
@@ -14946,7 +22576,7 @@
var panel = this.panels[i];
panel.title.setStyle('visibility', 'hidden');
- $(document.body).adopt(panel.title);
+ document.id(document.body).adopt(panel.title);
var size = panel.title.getBorderBoxSize();
bar.adopt(panel.title);
panel.title.setStyle('visibility','');
@@ -14958,7 +22588,7 @@
}).bind(this)
});
this.addEvent('addTo', function() {
- $(this.domObj.parentNode).setStyle('overflow', 'hidden');
+ document.id(this.domObj.parentNode).setStyle('overflow', 'hidden');
this.domObj.resize();
});
if (this.options.parent) {
@@ -15072,451 +22702,4052 @@
}
panel.domObj.resize({top: top, height:panelSize, bottom: null});
}
-});// $Id: grid.js 424 2009-05-12 12:51:44Z pagameba $
+});// $Id:$
/**
+ * Class: Jx.Dialog.Message
+ *
+ * Extends: <Jx.Dialog>
+ *
+ * Jx.Dialog.Confirm is an extension of Jx.Dialog that allows the developer
+ * to display a message to the user. It only presents an OK button.
+ *
+ * License:
+ * Copyright (c) 2009, Jonathan Bomgardner
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Dialog.Message = new Class({
+
+ Extends: Jx.Dialog,
+
+ options: {
+ /**
+ * Option: message
+ * The message to display to the user
+ */
+ message: '',
+ /**
+ * Jx.Dialog option defaults
+ */
+ width: 300,
+ height: 150,
+ close: true,
+ resize: true,
+ collapse: false
+ },
+ /**
+ * APIMethod: render
+ * constructs the dialog.
+ */
+ render: function () {
+ //create content to be added
+ this.buttons = new Jx.Toolbar({position: 'bottom'});
+ this.buttons.add(
+ new Jx.Button({
+ label: 'Ok',
+ onClick: this.onClick.bind(this, 'Ok')
+ })
+ );
+ this.options.toolbars = [this.buttons];
+ if (Jx.type(this.options.message) === 'string') {
+ this.question = new Element('div', {
+ 'class': 'jxMessage',
+ html: this.options.message
+ });
+ } else {
+ this.question = this.options.question;
+ $(this.question).addClass('jxMessage');
+ }
+ this.options.content = this.question;
+ this.parent();
+ },
+ /**
+ * Method: onClick
+ * Called when the OK button is clicked. Closes the dialog.
+ */
+ onClick: function (value) {
+ this.close();
+ }
+
+
+});
+// $Id:$
+/**
+ * Class: Jx.Dialog.Confirm
+ *
+ * Extends: <Jx.Dialog>
+ *
+ * Jx.Dialog.Confirm is an extension of Jx.Dialog that allows the developer
+ * to prompt their user with e yes/no question.
+ *
+ * License:
+ * Copyright (c) 2009, Jonathan Bomgardner
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Dialog.Confirm = new Class({
+
+ Extends: Jx.Dialog,
+
+ options: {
+ /**
+ * Option: question
+ * The question to ask the user
+ */
+ question: '',
+ /**
+ * Option: affirmitiveLabel
+ * The text to use for the affirmitive button. Defaults to 'Yes'.
+ */
+ affirmitiveLabel: 'Yes',
+ /**
+ * Option: negativeLabel
+ * The text to use for the negative button. Defaults to 'No'.
+ */
+ negativeLabel: 'No',
+
+ /**
+ * Jx.Dialog option defaults
+ */
+ width: 300,
+ height: 150,
+ close: false,
+ resize: true,
+ collapse: false
+ },
+ /**
+ * APIMethod: render
+ * creates the dialog
+ */
+ render: function () {
+ //create content to be added
+ this.buttons = new Jx.Toolbar({position: 'bottom'});
+ this.buttons.add(
+ new Jx.Button({
+ label: this.options.affirmitiveLabel,
+ onClick: this.onClick.bind(this, this.options.affirmitiveLabel)
+ }),
+ new Jx.Button({
+ label: this.options.negativeLabel,
+ onClick: this.onClick.bind(this, this.options.negativeLabel)
+ })
+ );
+ this.options.toolbars = [this.buttons];
+ if (Jx.type(this.options.question) === 'string') {
+ this.question = new Element('div', {
+ 'class': 'jxConfirmQuestion',
+ html: this.options.question
+ });
+ } else {
+ this.question = this.options.question;
+ $(this.question).addClass('jxConfirmQuestion');
+ }
+ this.options.content = this.question;
+ this.parent();
+ },
+ /**
+ * Method: onClick
+ * called when any button is clicked. It hides the dialog and fires
+ * the close event passing it the value of the button that was pressed.
+ */
+ onClick: function (value) {
+ this.isOpening = false;
+ this.hide();
+ this.fireEvent('close', [this, value]);
+ }
+
+
+});// $Id: $
+/**
+ * Class: Jx.Panel.DataView
+ *
+ * Extends: <Jx.Panel>
+ *
+ * This panel extension takes a standard Jx.Store (or subclass) and displays
+ * each record as an item using a provided template. It sorts the store as requested
+ * before doing so. The class only creates the HTML and has no default CSS display. All
+ * styling must be done by the developer using the control.
+ *
+ *
+ * Events:
+ * renderDone - fires when the panel completes creating all of the items.
+ *
+ * License:
+ * Copyright (c) 2009, Jonathan Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Panel.DataView = new Class({
+
+ Extends: Jx.Panel,
+
+ options: {
+ /**
+ * Option: data
+ * The store containing the data
+ */
+ data: null,
+ /**
+ * Option: sortColumns
+ * An array of columns to sort the store by.
+ */
+ sortColumns: null,
+ /**
+ * Option: itemTemplate
+ * The template to use in rendering records
+ */
+ itemTemplate: null,
+ /**
+ * Option: emptyTemplate
+ * the template that is displayed when there are no records in the
+ * store.
+ */
+ emptyTemplate: null,
+ /**
+ * Option: containerClass
+ * The class added to the container. It can be used to target the items
+ * in the panel.
+ */
+ containerClass: null,
+ /**
+ * Option: itemClass
+ * The class to add to each item. Used for styling purposes
+ */
+ itemClass: null,
+ /**
+ * Option: itemOptions
+ * Options to pass to the list object
+ */
+ listOptions: {
+ select: true,
+ hover: true
+ }
+ },
+
+ /**
+ * Property: bound
+ * hold bound functions
+ */
+ bound: {},
+
+ init: function () {
+ this.domA = new Element('div');
+ this.list = this.createList(this.domA, this.options.listOptions);
+ this.parent();
+ },
+ /**
+ * APIMethod: render
+ * Renders the dataview. If the store already has data loaded it will be rendered
+ * at the end of the method.
+ */
+ render: function () {
+ if (!$defined(this.options.data)) {
+ //we can't do anything without data
+ return;
+ }
+
+ this.options.content = this.domA;
+
+ //pass to parent
+ this.parent();
+
+ this.domA.addClass(this.options.containerClass);
+
+ //parse templates so we know what values are needed in each
+ this.itemCols = this.parseTemplate(this.options.itemTemplate);
+
+ this.bound.update = this.update.bind(this);
+ //listen for data updates
+ this.options.data.addEvent('loadFinished', this.bound.update);
+ this.options.data.addEvent('sortFinished', this.bound.update);
+ this.options.data.addEvent('loadError', this.bound.update);
+
+ if (this.options.data.loaded) {
+ this.update();
+ }
+
+ },
+
+ /**
+ * Method: draw
+ * begins the process of creating the items
+ */
+ draw: function () {
+ var n = this.options.data.count();
+ if ($defined(n) && n > 0) {
+ for (var i = 0; i < n; i++) {
+ this.options.data.moveTo(i);
+
+ var item = this.createItem();
+ this.list.add(item);
+ }
+ } else {
+ var empty = new Element('div', {html: this.options.emptyTemplate});
+ this.list.add(item);
+ }
+ this.fireEvent('renderDone', this);
+ },
+ /**
+ * Method: createItem
+ * Actually does the work of getting the data from the store
+ * and creating a single item based on the provided template
+ */
+ createItem: function () {
+ //create the item
+ var itemObj = {};
+ this.itemCols.each(function (col) {
+ itemObj[col] = this.options.data.get(col);
+ }, this);
+ var itemTemp = this.options.itemTemplate.substitute(itemObj);
+ var item = new Element('div', {
+ 'class': this.options.itemClass,
+ html: itemTemp
+ });
+ return item;
+ },
+ /**
+ * APIMethod: update
+ * This method begins the process of creating the items. It is called when
+ * the store is loaded or can be called to manually recreate the view.
+ */
+ update: function () {
+ if (!this.updating) {
+ this.updating = true;
+ this.list.empty();
+ this.options.data.sort(this.options.sortColumns);
+ this.draw();
+ this.updating = false;
+ }
+ },
+ /**
+ * Method: parseTemplate
+ * parses the provided template to determine which store columns are
+ * required to complete it.
+ *
+ * Parameters:
+ * template - the template to parse
+ */
+ parseTemplate: function (template) {
+ //we parse the template based on the columns in the data store looking
+ //for the pattern {column-name}. If it's in there we add it to the
+ //array of ones to look for
+ var columns = this.options.data.getColumns();
+ var arr = [];
+ columns.each(function (col) {
+ var s = '{' + col.name + '}';
+ if (template.contains(s)) {
+ arr.push(col.name);
+ }
+ }, this);
+ return arr;
+ },
+ /**
+ * Method: enterItem
+ * Fires mouseenter event
+ *
+ * Parameters:
+ * item - the item that is the target of the event
+ * list - the list this item is in.
+ */
+ enterItem: function(item, list){
+ this.fireEvent('mouseenter', item, list);
+ },
+ /**
+ * Method: leaveItem
+ * Fires mouseleave event
+ *
+ * Parameters:
+ * item - the item that is the target of the event
+ * list - the list this item is in.
+ */
+ leaveItem: function(item, list){
+ this.fireEvent('mouseleave', item, list);
+ },
+ /**
+ * Method: selectItem
+ * Fires select event
+ *
+ * Parameters:
+ * item - the item that is the target of the event
+ * list - the list this item is in.
+ */
+ selectItem: function(item, list){
+ this.fireEvent('select', item, list);
+ },
+ /**
+ * Method: unselectItem
+ * Fires unselect event
+ *
+ * Parameters:
+ * item - the item that is the target of the event
+ * list - the list this item is in.
+ */
+ unselectItem: function(item, list){
+ this.fireEvent('unselect', item, list);
+ },
+ /**
+ * Method: addItem
+ * Fires add event
+ *
+ * Parameters:
+ * item - the item that is the target of the event
+ * list - the list this item is in.
+ */
+ addItem: function(item, list) {
+ this.fireEvent('add', item, list);
+ },
+ /**
+ * Method: removeItem
+ * Fires remove event
+ *
+ * Parameters:
+ * item - the item that is the target of the event
+ * list - the list this item is in.
+ */
+ removeItem: function(item, list) {
+ this.fireEvent('remove', item, list);
+ },
+ /**
+ * Method: createList
+ * Creates the list object
+ *
+ * Parameters:
+ * container - the container to use in the list
+ * options - the options for the list
+ */
+ createList: function(container, options){
+ return new Jx.List(container, $extend({
+ onMouseenter: this.enterItem.bind(this),
+ onMouseleave: this.leaveItem.bind(this),
+ onSelect: this.selectItem.bind(this),
+ onAdd: this.addItem.bind(this),
+ onRemove: this.removeItem.bind(this),
+ onUnselect: this.unselectItem.bind(this)
+ }, options));
+ }
+});
+// $Id: $
+/**
+ * Class: Jx.Panel.DataView.Group
+ *
+ * Extends: <Jx.Panel.DataView>
+ *
+ * This extension of Jx.Panel.DataView that provides for grouping the items
+ * by a particular column.
+ *
+ * License:
+ * Copyright (c) 2009, Jonathan Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Panel.DataView.Group = new Class({
+
+ Extends: Jx.Panel.DataView,
+
+ options: {
+ /**
+ * Option: groupTemplate
+ * The template used to render the group heading
+ */
+ groupTemplate: null,
+ /**
+ * Option: groupContainerClass
+ * The class added to the group container. All of the items and header
+ * for a single grouping is contained by a div that has this class added.
+ */
+ groupContainerClass: null,
+ /**
+ * Option: groupHeaderClass
+ * The class added to the heading. Used for styling.
+ */
+ groupHeaderClass: null,
+ /**
+ * Option: listOption
+ * Options to pass to the main list
+ */
+ listOptions: {
+ select: false,
+ hover: false
+ },
+ /**
+ * Option: itemOption
+ * Options to pass to the item lists
+ */
+ itemOptions: {
+ select: true,
+ hover: true,
+ hoverClass: 'jxItemHover',
+ selectClass: 'jxItemSelect'
+ }
+ },
+
+ init: function() {
+ this.groupCols = this.parseTemplate(this.options.groupTemplate);
+ this.itemManager = new Jx.Selection({
+ eventToFire: {
+ select: 'itemselect',
+ unselect: 'itemunselect'
+ },
+ selectClass: 'jxItemSelected'
+ });
+ this.groupManager = new Jx.Selection({
+ eventToFire: {
+ select: 'groupselect',
+ unselect: 'groupunselect'
+ },
+ selectClass: 'jxGroupSelected'
+ });
+ this.parent();
+
+ },
+ /**
+ * APIMethod: render
+ * sets up the list container and calls the parent class' render function.
+ */
+ render: function () {
+ this.list = this.createList(this.domA, this.listOptions, this.groupManager);
+ this.parent();
+
+ },
+ /**
+ * Method: draw
+ * actually does the work of creating the view
+ */
+ draw: function () {
+ var d = this.options.data;
+ var n = d.count();
+
+ if ($defined(n) && n > 0) {
+ var currentGroup = '';
+ var itemList = null;
+
+ for (var i = 0; i < n; i++) {
+ d.moveTo(i);
+ var group = d.get(this.options.sortColumns[0]);
+
+ if (group !== currentGroup) {
+ //we have a new grouping
+
+ //group container
+ var container = new Element('div', {
+ 'class': this.options.groupContainerClass
+ });
+ var l = this.createList(container,{
+ select: false,
+ hover: false
+ });
+ this.list.add(l.container);
+
+ //group header
+ currentGroup = group;
+ var obj = {};
+ this.groupCols.each(function (col) {
+ obj[col] = d.get(col);
+ }, this);
+ var temp = this.options.groupTemplate.substitute(obj);
+ var g = new Element('div', {
+ 'class': this.options.groupHeaderClass,
+ 'html': temp,
+ id: 'group-' + group.replace(" ","-","g")
+ });
+ l.add(g);
+
+ //items container
+ var currentItemContainer = new Element('div', {
+ 'class': this.options.containerClass
+ });
+ itemList = this.createList(currentItemContainer, this.options.itemOptions, this.itemManager);
+ l.add(itemList.container);
+ }
+
+ var item = this.createItem();
+ itemList.add(item);
+ }
+ } else {
+ var empty = new Element('div', {html: this.options.emptyTemplate});
+ this.list.add(empty);
+ }
+ this.fireEvent('renderDone', this);
+ },
+
+ /**
+ * Method: createList
+ * Creates the list object
+ *
+ * Parameters:
+ * container - the container to use in the list
+ * options - the options for the list
+ * manager - <Jx.Selection> which selection obj to connect to this list
+ */
+ createList: function(container, options, manager){
+ return new Jx.List(container, $extend({
+ onMouseenter: this.enterItem.bind(this),
+ onMouseleave: this.leaveItem.bind(this),
+ onAdd: this.addItem.bind(this),
+ onRemove: this.removeItem.bind(this)
+ }, options), manager);
+ }
+
+});
+// $Id: $
+/**
+ * Class: Jx.Tooltip
+ *
+ * Extends: <Jx.Widget>
+ *
+ * An implementation of tooltips. These are very simple tooltips that are
+ * designed to be instantiated in javascript and directly attached to the object
+ * that they are the tip for. We can only have one Tip per element so we use
+ * element storage to store the tip object and check for it's presence
+ * before creating a new tip. If one is there we remove it and create this new
+ * one.
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Tooltip = new Class({
+
+ Extends : Jx.Widget,
+
+ options : {
+ /**
+ * Option: offsets
+ * An object with x and y components for where to put the tip related to
+ * the mouse cursor.
+ */
+ offsets : {
+ x : 15,
+ y : 15
+ },
+ /**
+ * Option: showDelay
+ * The amount of time to delay before showing the tip. This ensures we
+ * don't show a tip if we're just passing over an element quickly.
+ */
+ showDelay : 100,
+ /**
+ * Option: cssClass
+ * a class to be added to the tip's container. This can be used to style
+ * the tip.
+ */
+ cssClass : null
+ },
+
+ /**
+ * Parameters:
+ * target - The DOM element that triggers the toltip when moused over.
+ * tip - The contents of the tip itself. This can be either a string or
+ * an Element.
+ * options - <Jx.Tooltip.Options> and <Jx.Widget.Options>
+ */
+ parameters: ['target','tip','options'],
+
+ /**
+ * APIMethod: render
+ * Creates the tooltip
+ *
+
+ */
+ render : function () {
+ this.parent();
+ this.target = document.id(this.options.target);
+
+ var t = this.target.retrieve('Tip');
+ if (t) {
+ this.target.eliminate('Tip');
+ }
+
+ //set up the tip options
+ this.domObj = new Element('div', {
+ styles : {
+ 'position' : 'absolute',
+ 'top' : 0,
+ 'left' : 0,
+ 'visibility' : 'hidden'
+ }
+ }).inject(document.body);
+
+ if (Jx.type(this.options.tip) === 'string') {
+ this.domObj.set('html', this.options.tip);
+ } else {
+ this.domObj.grab(this.options.tip);
+ }
+
+ this.domObj.addClass('jxTooltip');
+ if ($defined(this.options.cssClass)) {
+ this.domObj.addClass(this.options.cssClass);
+ }
+
+ this.options.target.store('Tip', this);
+
+ //add events
+ this.options.target.addEvent('mouseenter', this.enter.bindWithEvent(this));
+ this.options.target.addEvent('mouseleave', this.leave.bindWithEvent(this));
+ this.options.target.addEvent('mousemove', this.move.bindWithEvent(this));
+
+ },
+
+ /**
+ * Method: enter
+ * Method run when the cursor passes over an element with a tip
+ *
+ * Parameters:
+ * event - the event object
+ * element - the element the cursor passed over
+ */
+ enter : function (event, element) {
+ this.timer = $clear(this.timer);
+ this.timer = (function () {
+ this.domObj.setStyle('visibility', 'visible');
+ this.position(event);
+ }).delay(this.options.delay, this);
+ },
+ /**
+ * Method: leave
+ * Executed when the mouse moves out of an element with a tip
+ *
+ * Parameters:
+ * event - the event object
+ * element - the element the cursor passed over
+ */
+ leave : function (event, element) {
+ this.timer = $clear(this.timer);
+ this.timer = (function () {
+ this.domObj.setStyle('visibility', 'hidden');
+ }).delay(this.options.delay, this);
+ },
+ /**
+ * Method: move
+ * Called when the mouse moves over an element with a tip.
+ *
+ * Parameters:
+ * event - the event object
+ */
+ move : function (event) {
+ this.position(event);
+ },
+ /**
+ * Method: position
+ * Called to position the tooltip.
+ *
+ * Parameters:
+ * event - the event object
+ */
+ position : function (event) {
+ var size = window.getSize(), scroll = window.getScroll();
+ var tipSize = this.domObj.getMarginBoxSize();
+ var tip = {
+ x : this.domObj.offsetWidth,
+ y : this.domObj.offsetHeight
+ };
+ var tipPlacement = {
+ x: event.page.x + this.options.offsets.x,
+ y: event.page.y + this.options.offsets.y
+ };
+
+ if (event.page.y + this.options.offsets.y + tip.y + tipSize.height - scroll.y > size.y) {
+ tipPlacement.y = event.page.y - this.options.offsets.y - tipSize.height - scroll.y;
+ }
+
+ if (event.page.x + this.options.offsets.x + tip.x + tipSize.width - scroll.x > size.x) {
+ tipPlacement.x = event.page.x - this.options.offsets.x - tipSize.width - scroll.x;
+ }
+
+ this.domObj.setStyle('top', tipPlacement.y);
+ this.domObj.setStyle('left', tipPlacement.x);
+ },
+ /**
+ * Method: detach
+ * Called to manually remove a tooltip.
+ */
+ detach : function () {
+ this.target.eliminate('Tip');
+ this.destroy();
+ }
+});
+// $Id: $
+/**
+ * Class: Jx.Field
+ *
+ * Extends: <Jx.Widget>
+ *
+ * This class is the base class for all form fields.
+ * The class will also allow for displaying error messages generated by
+ * form validation.
+ *
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Field = new Class({
+
+ Extends : Jx.Widget,
+ pluginNamespace: 'Field',
+
+ options : {
+ /**
+ * Option: id
+ * The ID of the field.
+ */
+ id : null,
+ /**
+ * Option: name
+ * The name of the field (used when submitting to the server). Will also be used for the
+ * name attribute of the field.
+ */
+ name : null,
+ /**
+ * Option: label
+ * The text that goes next to the field.
+ */
+ label : null,
+ /**
+ * Option: labelSeparator
+ * A character to use as the separator between the label and the input.
+ * Make it an empty string for no separator.
+ */
+ labelSeparator : ":",
+ /**
+ * Option: value
+ * A default value to populate the field with.
+ */
+ value : null,
+ /**
+ * Option: tag
+ * a string to use as the HTML of the tag element (default is a
+ * <span> element).
+ */
+ tag : null,
+ /**
+ * Option: tip
+ * A string that will eventually serve as a tooltip for an input field.
+ * Currently only implemented as OverText for text fields.
+ */
+ tip : null,
+ /**
+ * Option: template
+ * A string holding the template for the field.
+ */
+ template : null,
+ /**
+ * Option: containerClass
+ * a CSS class that will be added to the containing element.
+ */
+ containerClass : null,
+ /**
+ * Option: labelClass
+ * a CSS to add to the label
+ */
+ labelClass : null,
+ /**
+ * Option: fieldClass
+ * a CSS class to add to the input field
+ */
+ fieldClass : null,
+ /**
+ * Option: tagClass
+ * a CSS class to add to the tag field
+ */
+ tagClass : null,
+ /**
+ * Option: required
+ * Whether the field is required. Setting this to true will trigger
+ * the addition of a "required" validator class and the form
+ * will not submit until it is filled in and validates.
+ */
+ required : false,
+ /**
+ * Option: requiredText
+ * Text to be displayed if a field is required. It is added as an
+ * <em> element inside the <label>.
+ */
+ requiredText : '*',
+ /**
+ * Option: readonly
+ * {True|False} defaults to false. Whether this field is readonly.
+ */
+ readonly : false,
+ /**
+ * Option: disabled
+ * {True|False} defaults to false. Whether this field is disabled.
+ */
+ disabled : false
+
+ },
+
+ /**
+ * Property: overtextOptions
+ * The default options Jx uses for mootools-more's OverText
+ * plugin
+ */
+ overtextOptions : {
+ element : 'label'
+ },
+
+ /**
+ * Property: field
+ * An element representing the input field itself.
+ */
+ field : null,
+ /**
+ * Property: label
+ * A reference to the label element for this field
+ */
+ label : null,
+ /**
+ * Property: tag
+ * A reference to the "tag" field of this input if available
+ */
+ tag : null,
+ /**
+ * Property: id
+ * The name of this field.
+ */
+ id : null,
+ /**
+ * Property: overText
+ * The overText instance for this field.
+ */
+ overText : null,
+ /**
+ * Property: type
+ * Indicates that this is a field type
+ */
+ type : 'field',
+ /**
+ * Property: classes
+ * The classes to search for in the template. Not
+ * required, but we look for them.
+ */
+ classes : [ 'jxInputLabel', 'jxInputTag' ],
+
+ /**
+ * APIMethod: render
+ */
+ render : function () {
+ this.parent();
+
+ this.id = ($defined(this.options.id)) ? this.options.id : this
+ .generateId();
+ this.name = this.options.name;
+
+ // first the container
+ this.domObj = new Element('span', {
+ 'class' : 'jxInputContainer'
+ });
+
+ if ($defined(this.type)) {
+ this.domObj.addClass('jxInputContainer'+this.type);
+ }
+
+ if ($defined(this.options.containerClass)) {
+ this.domObj.addClass(this.options.containerClass);
+ }
+ if ($defined(this.options.required) && this.options.required) {
+ this.domObj.addClass('jxFieldRequired');
+ if ($defined(this.options.validatorClasses)) {
+ this.options.validatorClasses = 'required ' + this.options.validatorClasses;
+ } else {
+ this.options.validatorClasses = 'required';
+ }
+ }
+
+ var field = 'jxInput' + this.type;
+ this.classes.push(field);
+
+ var name = $defined(this.options.name) ? this.options.name : '';
+ var template = this.options.template.substitute({name:name});
+ var els = this.processTemplate(template, this.classes, this.domObj);
+
+ // LABEL
+ if (els.has('jxInputLabel')) {
+ this.label = els.get('jxInputLabel');
+ if ($defined(this.options.labelClass)) {
+ this.label.addClass(this.options.labelClass);
+ }
+ if ($defined(this.options.label)) {
+ this.label.set('html', this.options.label
+ + this.options.labelSeparator);
+ }
+
+ this.label.set('for', this.id);
+
+ if (this.options.required) {
+ var em = new Element('em', {
+ 'html' : this.options.requiredText,
+ 'class' : 'required'
+ });
+ em.inject(this.label);
+ }
+ }
+
+ // FIELD
+ if (els.has(field)) {
+ this.field = els.get(field);
+ if ($defined(this.options.fieldClass)) {
+ this.field.addClass(this.options.fieldClass);
+ }
+
+ if ($defined(this.options.value)) {
+ this.field.set('value', this.options.value);
+ }
+
+ this.field.set('id', this.id);
+
+ if ($defined(this.options.readonly)
+ && this.options.readonly) {
+ this.field.set("readonly", "readonly");
+ this.field.addClass('jxFieldReadonly');
+ }
+
+ if ($defined(this.options.disabled)
+ && this.options.disabled) {
+ this.field.set("disabled", "disabled");
+ this.field.addClass('jxFieldDisabled');
+ }
+
+ // add validator classes
+ if ($defined(this.options.validatorClasses)) {
+ this.field.addClass(this.options.validatorClasses);
+ }
+
+ this.field.store('field', this);
+ }
+
+ // TAG
+ if (els.has('jxInputTag')) {
+ this.tag = els.get('jxInputTag');
+ if ($defined(this.options.tagClass)) {
+ this.tag.addClass(this.options.tagClass);
+ }
+ if ($defined(this.options.tag)) {
+ this.tag.set('html', this.options.tag);
+ }
+ }
+
+ if ($defined(this.options.form)
+ && this.options.form instanceof Jx.Form) {
+ this.form = this.options.form;
+ this.form.addField(this);
+ }
+
+ },
+ /**
+ * APIMethod: setValue Sets the value property of the field
+ *
+ * Parameters:
+ * v - The value to set the field to.
+ */
+ setValue : function (v) {
+ this.field.set('value', v);
+ },
+
+ /**
+ * APIMethod: getValue
+ * Returns the current value of the field.
+ */
+ getValue : function () {
+ return this.field.get("value");
+ },
+
+ /**
+ * APIMethod: reset
+ * Sets the field back to the value passed in the
+ * original options
+ */
+ reset : function () {
+ this.field.set('value', this.options.value);
+ this.fireEvent('reset', this);
+ },
+ /**
+ * APIMethod: disable
+ * Disabled the field
+ */
+ disable : function () {
+ this.field.set("disabled", "disabled");
+ this.field.addClass('jxFieldDisabled');
+ },
+ /**
+ * APIMethod: enable
+ * Enables the field
+ */
+ enable : function () {
+ this.field.erase("disabled");
+ this.field.removeClass('jxFieldDisabled');
+ }
+
+});
+// $Id: $
+/**
+ * Class: Jx.Field.Text
+ *
+ * Extends: <Jx.Field>
+ *
+ * This class represents a text input field.
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Field.Text = new Class({
+
+ Extends: Jx.Field,
+
+ options: {
+ /**
+ * Option: overText
+ * an object holding options for mootools-more's OverText class. Leave it null to
+ * not enable it, make it an object to enable.
+ */
+ overText: null,
+ /**
+ * Option: template
+ * The template used to render this field
+ */
+ template: '<label class="jxInputLabel"></label><input class="jxInputText" type="text" name="{name}"/><span class="jxInputTag"></span>'
+ },
+ /**
+ * Property: type
+ * The type of this field
+ */
+ type: 'Text',
+
+ /**
+ * APIMethod: render
+ * Creates a text input field.
+ */
+ render: function () {
+ this.parent();
+
+ //create the overText instance if needed
+ if ($defined(this.options.overText)) {
+ var opts = $extend({}, this.options.overText);
+ this.field.set('alt', this.options.tip);
+ this.overText = new OverText(this.field, opts);
+ this.overText.show();
+ }
+
+ }
+
+});// $Id: $
+/**
+ * Class: Jx.Field.Hidden
+ *
+ * Extends: <Jx.Field>
+ *
+ * This class represents a hidden input field.
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Field.Hidden = new Class({
+
+ Extends: Jx.Field,
+
+ options: {
+ /**
+ * Option: template
+ * The template used to render this field
+ */
+ template: '<input class="jxInputHidden" type="hidden" name="{name}"/>'
+ },
+ /**
+ * Property: type
+ * The type of this field
+ */
+ type: 'Hidden'
+
+});
+
+
+
+
+// $Id: $
+/**
+ * Class: Jx.Form
+ *
+ * Extends: <Jx.Widget>
+ *
+ * A class that represents an HTML form. You add fields using either Jx.Form.add()
+ * or by using the field's .addTo() method. You can get all form values or set them
+ * using this class. It also handles validation of fields.
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Form = new Class({
+
+ Extends: Jx.Widget,
+
+ options: {
+ /**
+ * Option: method
+ * the method used to submit the form
+ */
+ method: 'post',
+ /**
+ * Option: action
+ * where to submit it to
+ */
+ action: '',
+ /**
+ * Option: fileUpload
+ * whether this form handles file uploads or not.
+ */
+ fileUpload: false,
+ /**
+ * Option: id
+ * the id of this form
+ */
+ id: null,
+ /**
+ * Option: formClass
+ */
+ formClass: null,
+ /**
+ * Option: name
+ * the name property for the form
+ */
+ name: ''
+ },
+
+ /**
+ * Property: fields
+ * An array of all of the single fields (not contained in a fieldset) for this form
+ */
+ fields : new Hash(),
+ /**
+ * Property: pluginNamespace
+ * required variable for plugins
+ */
+ pluginNamespace: 'Form',
+
+ /**
+ * APIMethod: render
+ * Constructs the form but does not add it to anything to be shown. The caller
+ * should use form.addTo() to add the form to the DOM.
+ */
+ render : function () {
+ //create the form first
+ this.domObj = new Element('form', {
+ 'method' : this.options.method,
+ 'action' : this.options.action,
+ 'class' : 'jxForm',
+ 'name' : this.options.name
+ });
+
+ if (this.options.fileUpload) {
+ this.domObj.set('enctype', 'multipart/form-data');
+ }
+ if ($defined(this.options.id)) {
+ this.domObj.set('id', this.options.id);
+ }
+ if ($defined(this.options.formClass)) {
+ this.domObj.addClass(this.options.formClass);
+ }
+ },
+
+ /**
+ * APIMethod: addField
+ * Adds a <Jx.Field> subclass to this form's fields hash
+ *
+ * Parameters:
+ * field - <Jx.Field> to add
+ */
+ addField : function (field) {
+ this.fields.set(field.id, field);
+ },
+
+
+ /**
+ * Method: isValid
+ * Determines if the form passes validation
+ *
+ * Parameters:
+ * evt - the Mootools event object
+ */
+ isValid : function (evt) {
+ return true;
+ },
+
+ /**
+ * APIMethod: getValues
+ * Gets the values of all the fields in the form as a Hash object. This
+ * uses the mootools function Element.toQueryString to get the values and
+ * will either return the values as a querystring or as an object (using
+ * mootools-more's String.parseQueryString method).
+ *
+ * Parameters:
+ * asQueryString - {boolean} indicates whether to return the value as a
+ * query string or an object.
+ */
+ getValues : function (asQueryString) {
+ var queryString = this.domObj.toQueryString();
+ if ($defined(asQueryString)) {
+ return queryString;
+ } else {
+ return queryString.parseQueryString();
+ }
+ },
+ /**
+ * APIMethod: setValues
+ * Used to set values on the form
+ *
+ * Parameters:
+ * values - A Hash of values to set keyed by field name.
+ */
+ setValues : function (values) {
+ //TODO: This may have to change with change to getValues().
+ if (Jx.type(values) === 'object') {
+ values = new Hash(values);
+ }
+ this.fields.each(function (item) {
+ item.setValue(values.get(item.name));
+ }, this);
+ },
+
+ /**
+ * APIMethod: add
+ *
+ * Parameters:
+ * Pass as many parameters as you like. However, they should all be
+ * <Jx.Field> objects.
+ */
+ add : function () {
+ var field;
+ for (var x = 0; x < arguments.length; x++) {
+ field = arguments[x];
+ //add form to the field and field to the form if not already there
+ if (field instanceof Jx.Field && !$defined(field.form)) {
+ field.form = this;
+ this.addField(field);
+ }
+ this.domObj.grab(field);
+ }
+ return this;
+ },
+
+ /**
+ * APIMethod: reset
+ *
+ */
+ reset : function () {
+ this.fields.each(function (field, name) {
+ field.reset();
+ }, this);
+ this.fireEvent('reset',this);
+ },
+
+ getFieldsByName: function (name) {
+ var fields = [];
+ this.fields.each(function(val, id){
+ if (val.name === name) {
+ fields.push(val);
+ }
+ },this);
+ return fields;
+ }
+});
+/**
+ * Class: Jx.Field.File
+ *
+ * Extends: <Jx.Field>
+ *
+ * This class is designed to work with an iFrame and APC upload progress.
+ * APC is a php specific technology but any server side implementation that
+ * works in the same manner should work. You can then wire this class to the
+ * progress bar class to show progress.
+ *
+ * The other option is to not use progress tracking and just use the base
+ * upload which works through a hidden iFrame. In order to use this with Jx.Form
+ * you'll need to add it normally but keep a reference to it. When you call
+ * Jx.Form.getValues() it will not return any file information. You can then
+ * call the Jx.Field.File.upload() method for each file input directly and
+ * then submit the rest of the form via ajax.
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Field.File = new Class({
+
+ Extends: Jx.Field,
+
+ options: {
+ /**
+ * Option: template
+ * The template used to render the field
+ */
+ template: '<label class="jxInputLabel"></label><div class="jxFileInputs"><input class="jxInputFile" type="file" name="{name}" /></div><span class="jxInputTag"></span>',
+ /**
+ * Option: autoUpload
+ * Whether to upload the file immediatelly upon selection
+ */
+ autoUpload: false,
+ /**
+ * Option: Progress
+ * Whether to use the APC, or similar, progress method.
+ */
+ progress: false,
+ /**
+ * Option: progressIDUrl
+ * The url to call in order to get the ID, or key, to use
+ * with the APC upload process
+ */
+ progressIDUrl: '',
+ /**
+ * Option: progressName
+ * The name to give the field that holds the generated progress ID retrieved
+ * from the server. Defaults to 'APC_UPLOAD_PROGRESS' which is the default
+ * for APC.
+ */
+ progressName: 'APC_UPLOAD_PROGRESS',
+ /**
+ * Option: progressId
+ * The id to give the form element that holds the generated progress ID
+ * retrieved from the server. Defaults to 'progress_key'.
+ */
+ progressId: 'progress_key',
+ /**
+ * Option: handlerUrl
+ * The url to send the file to.
+ */
+ handlerUrl: '',
+ /**
+ * Option: progressUrl
+ * The url used to retrieve the upload prgress of the file.
+ */
+ progressUrl: '',
+ /**
+ * Option: debug
+ * Defaults to false. If set to true it will prevent the hidden form
+ * and IFrame from being destroyed at the end of the upload so it can be
+ * inspected during development
+ */
+ debug: false,
+ /**
+ * Events
+ */
+ onUploadBegin: $empty,
+ onUploadComplete: $empty,
+ onUploadProgress: $empty,
+ onUploadError: $empty,
+ onFileSelected: $empty
+
+ },
+ /**
+ * Property: type
+ * The Field type used in rendering
+ */
+ type: 'File',
+ /**
+ * APIMethod: render
+ * renders the file input
+ */
+ render: function () {
+ this.parent();
+
+ //add a unique ID if no id is defined
+ if (!$defined(this.options.id)) {
+ this.field.set('id', this.generateId());
+ }
+
+ //now, create the fake inputs
+ this.fake = new Element('div', {
+ 'class' : 'jxFileFake'
+ });
+ this.text = new Jx.Field.Text({
+ template : '<input class="jxInputText" type="text" />'
+ });
+ this.browseButton = new Jx.Button({
+ label : 'Browse...'
+ });
+
+
+ this.fake.adopt(this.text, this.browseButton);
+ this.field.grab(this.fake, 'after');
+
+ this.field.addEvents({
+ change : this.copyValue.bind(this),
+ mouseout : this.copyValue.bind(this),
+ mouseenter : this.mouseEnter.bind(this),
+ mouseleave : this.mouseLeave.bind(this)
+ });
+
+ },
+ /**
+ * Method: copyValue
+ * Called when the value in the actual file input changes and when
+ * the mouse moves out of it to copy the value into the "fake" text box.
+ */
+ copyValue: function () {
+ if (this.field.value !== '' && (this.text.field.value !== this.field.value)) {
+ this.text.field.value = this.field.value;
+ this.fireEvent('fileSelected', this);
+ }
+ },
+ /**
+ * Method: mouseEnter
+ * Called when the mouse enters the actual file input to make the
+ * fake button highlight.
+ */
+ mouseEnter: function () {
+ this.browseButton.domA.addClass('jxButtonPressed');
+ },
+ /**
+ * Method: mouseLeave
+ * called when the mouse leaves the actual file input to turn off
+ * the highlight of the fake button.
+ */
+ mouseLeave: function () {
+ this.browseButton.domA.removeClass('jxButtonPressed');
+ },
+ /**
+ * APIMethod: upload
+ * Call this to upload the file to the server
+ */
+ upload: function () {
+ this.fireEvent('uploadBegin', this);
+ //create the iframe
+ this.iframe = new IFrame(null, {
+ styles: {
+ display: 'none'
+ },
+
+ name : this.generateId()
+ });
+ this.iframe.inject(document.body);
+
+ //load in the form
+ this.form = new Jx.Form({
+ action : this.options.handlerUrl,
+ name : 'jxUploadForm',
+ fileUpload: true
+ });
+
+ //iframeBody.grab(this.form);
+ $(this.form).set('target', this.iframe.get('name')).setStyles({
+ visibility: 'hidden',
+ display: 'none'
+ }).inject(document.body);
+
+
+ //move the form input into it (cloneNode)
+ $(this.form).grab(this.field.cloneNode(true));
+ //if polling the server we need an APC_UPLOAD_PROGRESS id.
+ //get it from the server.
+ if (this.options.progress) {
+ var req = new Request.JSON({
+ url: this.options.progressIDUrl,
+ method: 'get',
+ onSuccess: this.submitUpload.bind(this)
+ });
+ req.send();
+ } else {
+ this.submitUpload();
+ }
+ },
+ /**
+ * Method: submitUpload
+ * Called either after upload() or as a result of a successful call
+ * to get a progress ID.
+ *
+ * Parameters:
+ * data - Optional. The data returned from the call for a progress ID.
+ */
+ submitUpload: function (data) {
+ //check for ID in data
+ if ($defined(data) && data.success && $defined(data.id)) {
+ this.progressID = data.id;
+ //if have id, create hidden progress field
+ var id = new Jx.Field.Hidden({
+ name : this.options.progressName,
+ id : this.options.progressId,
+ value : this.progressID
+ });
+ id.addTo(this.form, 'top');
+ }
+ this.iframe.addEvent('load', this.processIFrameUpload.bind(this));
+
+
+ //submit the form
+ $(this.form).submit();
+ //begin polling if needed
+ if (this.options.progress && $defined(this.progressID)) {
+ this.pollUpload();
+ }
+ },
+ /**
+ * Method: pollUpload
+ * polls the server for upload progress information
+ */
+ pollUpload: function () {
+ var d = { id : this.progressID };
+ var r = new Request.JSON({
+ data: d,
+ url : this.options.progressUrl,
+ method : 'get',
+ onSuccess : this.processProgress.bind(this),
+ onFailure : this.uploadFailure.bind(this)
+ });
+ r.send();
+ },
+
+ /**
+ * Method: processProgress
+ * process the data returned from the request
+ *
+ * Parameters:
+ * data - The data from the request as an object.
+ */
+ processProgress: function (data) {
+ if ($defined(data)) {
+ this.fireEvent('uploadProgress', [data, this]);
+ if (data.current < data.total) {
+ this.polling = true;
+ this.pollUpload();
+ } else {
+ this.polling = false;
+ if (this.done) {
+ this.uploadCleanUp();
+ this.fireEvent('uploadComplete', [this.doneData, this]);
+ }
+ }
+ }
+ },
+ /**
+ * Method: uploadFailure
+ * called if there is a problem getting progress on the upload
+ */
+ uploadFailure: function (xhr) {
+ this.fireEvent('uploadProgressError', this);
+ },
+ /**
+ * Method: processIFrameUpload
+ * Called if we are not using progress and the IFrame finished loading the
+ * server response.
+ */
+ processIFrameUpload: function () {
+ //the body text should be a JSON structure
+ //get the body
+ var iframeBody = this.iframe.contentDocument.defaultView.document.body.innerHTML;
+
+ var data = JSON.decode(iframeBody);
+ if ($defined(data.success) && data.success) {
+ this.done = true;
+ this.doneData = data;
+ if (!this.polling) {
+ this.uploadCleanUp();
+ this.fireEvent('uploadComplete', [data, this]);
+ }
+ } else {
+ this.fireEvent('uploadError', [data , this]);
+ }
+ },
+ /**
+ * Method: uploadCleanUp
+ * Cleans up the hidden form and IFrame after a completed upload. Set
+ * this.options.debug to true to keep this from happening
+ */
+ uploadCleanUp: function () {
+ if (!this.options.debug) {
+ this.form.destroy();
+ this.iframe.destroy();
+ }
+ },
+ /**
+ * APIMethod: getFileName
+ * Allows caller to get the filename of the file we're uploading
+ */
+ getFileName: function () {
+ var fn = this.field.get('value');
+ return fn.slice(fn.lastIndexOf('/') + 1);
+ },
+ /**
+ * Method: getExt
+ * Returns the 3-letter extension of this file.
+ */
+ getExt: function () {
+ var fn = this.getFileName();
+ return fn.slice(fn.length - 3);
+ }
+});
+/**
+ * Class: Jx.Progressbar
+ *
+ *
+ * Example:
+ * The following just uses the defaults.
+ * (code)
+ * var progressBar = new Jx.Progressbar();
+ * progressBar.addEvent('update',function(){alert('updated!');});
+ * progressBar.addEvent('complete',function(){
+ * alert('completed!');
+ * this.destroy();
+ * });
+ *
+ * progressbar.addTo('container');
+ *
+ * var total = 90;
+ * for (i=0; i < total; i++) {
+ * progressbar.update(total, i);
+ * }
+ * (end)
+ *
+ * Events:
+ * onUpdate - Fired when the bar is updated
+ * onComplete - fires when the progress bar completes it's fill
+ *
+ */
+Jx.Progressbar = new Class({
+
+ Extends: Jx.Widget,
+
+ options: {
+ onUpdate: $empty,
+ onComplete: $empty,
+ /**
+ * Option: messageText
+ * The text of a message displayed above the bar. Set to NULL to prevent any text from appearing
+ */
+ messageText: 'Loading...',
+ /**
+ * Option: progressText
+ * The text displayed inside the bar. This defaults to "{progress} of {total}"
+ * where {progress} and {total} are substituted for passed in values.
+ */
+ progressText: '{progress} of {total}',
+ /**
+ * Option: bar
+ * an object that gives options for the bar itself. Specifically,
+ * the width and height of the bar. You can set either to 'auto' to
+ * have the bar calculate its own width.
+ */
+ bar: {
+ width: 'auto',
+ height: 20
+ },
+ /**
+ * Option: parent
+ * The element to put this progressbar into
+ */
+ parent: null,
+ /**
+ * Option: template
+ * The template used to create the progressbar
+ */
+ template: '<div class="jxProgressBar-message"></div><div class="jxProgressBar"><div class="jxProgressBar-outline"></div><div class="jxProgressBar-fill"></div><div class="jxProgressBar-text"></div></div>'
+ },
+ /**
+ * Property: classes
+ * The classes used in the template
+ */
+ classes: [
+ 'jxProgressBar-message',
+ 'jxProgressBar',
+ 'jxProgressBar-outline',
+ 'jxProgressBar-fill',
+ 'jxProgressBar-text'],
+ /**
+ * Property: bar
+ * the bar that is filled
+ */
+ bar: null,
+ /**
+ * Property: text
+ * the element that contains the text that's shown on the bar (if any).
+ */
+ text: null,
+
+ /**
+ * APIMethod: render
+ * Creates a new progressbar.
+ */
+ render: function () {
+
+ this.domObj = new Element('div', {
+ 'class': 'jxProgressBar-container'
+ });
+
+ var els = this.processTemplate(this.options.template,this.classes,this.domObj);
+
+ if ($defined(this.options.parent)) {
+ this.domObj.inject($(this.options.parent));
+ }
+
+ //determine width of progressbar
+ if (this.options.bar.width === 'auto') {
+ //get width of container
+ this.options.bar.width = this.domObj.getStyle('width').toInt();
+ }
+
+ //determine height
+ if (this.options.bar.height === 'auto') {
+ this.options.bar.height = this.domObj.getStyle('height').toInt() - 4;
+ }
+
+ //Message
+ if (els.has('jxProgressBar-message')) {
+ this.message = els.get('jxProgressBar-message');
+ if ($defined(this.options.messageText)) {
+ this.message.set('html', this.options.messsageText);
+ } else {
+ this.message.destroy();
+ }
+ }
+
+ //bar container itself
+ if (els.has('jxProgressBar')) {
+ this.container = els.get('jxProgressBar');
+ this.container.setStyles({
+ 'position': 'relative',
+ 'width': this.options.bar.width,
+ 'height' : this.options.bar.height + 4
+ });
+ }
+
+ //Outline
+ if (els.has('jxProgressBar-outline')) {
+ this.outline = els.get('jxProgressBar-outline');
+ this.outline.setStyles({
+ 'width': this.options.bar.width,
+ 'height' : this.options.bar.height
+ });
+ }
+
+ //Fill
+ if (els.has('jxProgressBar-fill')) {
+ this.fill = els.get('jxProgressBar-fill');
+ this.fill.setStyles({
+ 'width': 0,
+ 'height' : this.options.bar.height
+ });
+ }
+
+ //TODO: check for {progress} and {total} in progressText
+ var obj = {};
+ if (this.options.progressText.contains('{progress}')) {
+ obj.progress = 0;
+ }
+ if (this.options.progressText.contains('{total}')) {
+ obj.total = 0;
+ }
+
+ //Progress text
+ if (els.has('jxProgressBar-text')) {
+ this.text = els.get('jxProgressBar-text');
+ this.text.set('html', this.options.progressText.substitute(obj));
+ }
+
+ },
+ /**
+ * APIMethod: update
+ * called to update the progress bar with new percentage.
+ *
+ * Parameters:
+ * total - the total # to progress up to
+ * progress - the current position in the progress (must be less than or
+ * equal to the total)
+ */
+ update: function (total, progress) {
+ var newWidth = (progress * this.options.bar.width) / total;
+
+ //update bar width
+ //TODO: animate this
+
+ this.fill.get('tween', {property: 'width', onComplete: (function () {
+ var obj = {};
+ if (this.options.progressText.contains('{progress}')) {
+ obj.progress = progress;
+ }
+ if (this.options.progressText.contains('{total}')) {
+ obj.total = total;
+ }
+ var t = this.options.progressText.substitute(obj);
+ this.text.set('text', t);
+
+ if (total === progress) {
+ this.complete = true;
+ this.fireEvent('complete');
+ } else {
+ this.fireEvent('update');
+ }
+ }).bind(this)}).start(newWidth);
+
+ }
+
+});
+/**
+ * Class: Jx.Panel.FileUpload
+ *
+ * Extends: <Jx.Panel>
+ *
+ * This class extends Jx.Panel to provide a consistent interface for uploading
+ * files in an application.
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Panel.FileUpload = new Class({
+
+ Extends: Jx.Panel,
+
+ options: {
+ /**
+ * Option: file
+ * An object containing the options for Jx.Field.File
+ */
+ file: {
+ autoUpload: false,
+ progress: false,
+ progressIDUrl: '',
+ handlerUrl: '',
+ progressUrl: ''
+ },
+ /**
+ * Option: onFileUploadComplete
+ * An event handler that is called when a file has been uploaded
+ */
+ onFileComplete: $empty,
+ /**
+ * Option: onComplete
+ * An event handler that is called when all files have been uploaded
+ */
+ onComplete: $empty,
+ /**
+ * Option: prompt
+ * The prompt to display at the top of the panel - before the
+ * file input
+ */
+ prompt: null,
+ /**
+ * Option: buttonText
+ * The text to place on the upload button
+ */
+ buttonText: 'Upload Files',
+ /**
+ * Option: removeOnComplete
+ * Determines whether a file is removed from the queue after uploading
+ */
+ removeOnComplete: false
+ },
+ /**
+ * Property: domObjA
+ * An HTML Element used to hold the interface while it is being
+ * constructed.
+ */
+ domObjA: null,
+ /**
+ * Property: fileQueue
+ * An array holding Jx.Field.File elements that are to be uploaded
+ */
+ fileQueue: [],
+ /**
+ * APIMethod: render
+ * Sets up the upload panel.
+ */
+ render: function () {
+ //first create panel content
+ this.domObjA = new Element('div', {'class' : 'jxFileUploadPanel'});
+
+
+ if ($defined(this.options.prompt)) {
+ var desc;
+ if (Jx.type(this.options.prompt === 'string')) {
+ desc = new Element('p', {
+ html: this.options.prompt
+ });
+ } else {
+ desc = this.options.prompt;
+ }
+ desc.inject(this.domObjA);
+ }
+
+ //add the file field
+ this.fileOpt = this.options.file;
+ this.fileOpt.template = '<div class="jxFileInputs"><input class="jxInputFile" type="file" name={name} /></div>';
+
+ this.currentFile = new Jx.Field.File(this.fileOpt);
+ this.currentFile.addEvent('fileSelected', this.moveToQueue.bind(this));
+ this.currentFile.addTo(this.domObjA);
+
+ //now the 'queue' listing with delete button
+
+ this.queueDiv = new Element('div', {
+ 'class': 'jxUploadQueue'
+ });
+ this.queueDiv.inject(this.domObjA);
+ this.uploadBtn = new Jx.Button({
+ label : this.options.buttonText,
+ onClick: this.upload.bind(this)
+ });
+ var tlb = new Jx.Toolbar({position: 'bottom'}).add(this.uploadBtn);
+ this.uploadBtn.setEnabled(false);
+ this.options.toolbars = [tlb];
+ //then pass it on to the Panel constructor
+ this.options.content = this.domObjA;
+ this.parent(this.options);
+ },
+ /**
+ * Method: moveToQueue
+ * Called by Jx.Field.File's fileSelected event. Moves the selected file into the
+ * upload queue.
+ */
+ moveToQueue: function (file) {
+ var cf = this.currentFile;
+ var name = cf.getFileName();
+
+ this.fileQueue.push(this.currentFile);
+
+ this.currentFile = new Jx.Field.File(this.fileOpt);
+ this.currentFile.addEvent('fileSelected', this.moveToQueue.bind(this));
+ $(this.currentFile).replaces($(cf));
+
+ //add to queue div
+
+ cf.queuedDiv = new Element('div', {id : name});
+ var s = new Element('span', {
+ html : name,
+ 'class' : 'jxUploadFileName'
+ });
+ var del = new Element('span', {
+ 'class' : 'jxUploadFileDelete',
+ title : 'Remove from queue'
+ });
+
+ del.addEvent('click', this.removeFromQueue.bind(this, cf));
+ cf.queuedDiv.adopt(s, del);
+ cf.queuedDiv.inject(this.queueDiv);
+ if (!this.uploadBtn.isEnabled()) {
+ this.uploadBtn.setEnabled(true);
+ }
+
+ },
+ /**
+ * Method: upload
+ * Called when the user clicks the upload button. Runs the upload process.
+ */
+ upload: function () {
+ var file = this.fileQueue.shift();
+ file.addEvent('uploadComplete', this.fileUploadComplete.bind(this));
+ file.addEvent('uploadError', this.fileUploadError.bind(this));
+
+ if (this.options.file.progress) {
+ file.addEvent('uploadProgress', this.fileUploadProgress.bind(this));
+ //progressbar
+ //setup options
+ var options = {
+ containerClass: 'progress-container',
+ messageText: null,
+ messageClass: 'progress-message',
+ progressText: 'uploading ' + file.getFileName(),
+ progressClass: 'progress-bar',
+ bar: {
+ width: file.queuedDiv.getStyle('width').toInt(),
+ height: file.queuedDiv.getFirst().getStyle('height').toInt()
+ }
+ };
+ var pb = new Jx.Progressbar(options);
+ file.pb = pb;
+ $(pb).replaces(file.queuedDiv);
+ } else {
+ file.queuedDiv.getLast().removeClass('jxUploadFileDelete').addClass('jxUploadFileProgress');
+ }
+ file.upload();
+ },
+ /**
+ * Method: fileUploadComplete
+ * Called when a single file is uploaded completely (called by
+ * Jx.Field.File's uploadComplete event).
+ *
+ * Parameters:
+ * data - the data returned from the event
+ * file - the file we're tracking
+ */
+ fileUploadComplete: function (data, file) {
+ if ($defined(data.success) && data.success ){
+ this.removeUploadedFile(file);
+ } else {
+ this.fileUploadError(data, file);
+ }
+ },
+ /**
+ * Method: fileUploadError
+ * Called when there is an error uploading a file.
+ *
+ * Parameters:
+ * data - the data passed back from the server, if any.
+ * file - the file we're tracking
+ */
+ fileUploadError: function (data, file) {
+ var icon = file.queuedDiv.getLast();
+ icon.erase('title');
+ if (icon.hasClass('jxUploadFileProgress')) {
+ icon.removeClass('jxUploadFileProgress').addClass('jxUploadFileError');
+ } else {
+ //queued div is hidden, show it
+ file.queuedDiv.replaces(file.pb);
+ icon.removeClass('jxUploadFileDelete').addClass('jxUploadFileError');
+ }
+ if ($defined(data.error.message)) {
+ var tt = new Jx.Tooltip(icon, data.error.message, {
+ cssClass : 'jxUploadFileErrorTip'
+ });
+ }
+ },
+ /**
+ * Method: removeUploadedFile
+ * Removes the passed file from the upload queue upon it's completion.
+ *
+ * Parameters:
+ * file - the file we're tracking
+ */
+ removeUploadedFile: function (file) {
+
+ if (this.options.removeOnComplete) {
+ if ($defined(file.pb)) {
+ file.pb.destroy();
+ }
+ file.queuedDiv.dispose();
+ var name = file.getFileName();
+ this.fileQueue.erase(name);
+ } else {
+ if ($defined(file.pb)) {
+ file.queuedDiv.replaces(file.pb);
+ file.pb.destroy();
+ }
+ var l = file.queuedDiv.getLast();
+ if (l.hasClass('jxUploadFileDelete')) {
+ l.removeClass('jxUploadFileDelete').addClass('jxUploadFileComplete');
+ } else if (l.hasClass('jxUploadFileProgress')) {
+ l.removeClass('jxUploadFileProgress').addClass('jxUploadFileComplete');
+ }
+ }
+
+ this.fireEvent('fileComplete', file);
+ if (this.fileQueue.length > 0) {
+ this.upload();
+ } else {
+ this.fireEvent('complete');
+ }
+ },
+ /**
+ * Method: fileUploadProgress
+ * Function to pass progress information to the progressbar instance
+ * in the file. Only used if we're tracking progress.
+ */
+ fileUploadProgress: function (data, file) {
+ file.pb.update(data.total, data.current);
+ },
+ /**
+ * Method: removeFromQueue
+ * Called when the delete icon is clicked for an individual file. It
+ * removes the file from the queue, disposes of it, and does NOT upload
+ * the file to the server.
+ *
+ * Pparameters:
+ * file - the file we're getting rid of.
+ */
+ removeFromQueue: function (file) {
+ var name = file.getFileName();
+ //TODO: Should prompt the user to be sure - use Jx.Dialog.Confirm?
+ $(name).destroy();
+ this.fileQueue = this.fileQueue.erase(file);
+ if (this.fileQueue.length === 0) {
+ this.uploadBtn.setEnabled(false);
+ }
+ }
+});
+// $Id: $
+/**
+ * Class: Jx.ListItem
+ *
+ * Extends: <Jx.Widget>
+ *
+ * Events:
+ *
+ * License:
+ * Copyright (c) 2009, DM Solutions Group.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.ListItem = new Class({
+
+ Extends: Jx.Widget,
+
+ options: {
+ template: '<li class="jxListItemContainer jxListItem"></li>'
+ },
+
+ classes: ['jxListItemContainer','jxListItem'],
+
+ /**
+ * APIMethod: render
+ */
+ render: function () {
+ this.parent();
+ this.elements = this.processTemplate(this.options.template, this.classes);
+ this.domObj = this.elements.get('jxListItemContainer');
+ this.domContent = this.elements.get('jxListItem')
+ this.loadContent(this.domContent);
+ }
+});// $Id: $
+/**
+ * Class: Jx.ListView
+ *
+ * Extends: <Jx.Widget>
+ *
+ * Events:
+ *
+ * License:
+ * Copyright (c) 2009, DM Solutions Group.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.ListView = new Class({
+
+ Extends: Jx.Widget,
+
+ options: {
+ template: '<ul class="jxListView"></ul>',
+ /**
+ * Option: listOptions
+ * control the behaviour of the list, see <Jx.List>
+ */
+ listOptions: {
+ hover: true,
+ press: true,
+ select: true
+ }
+ },
+
+ classes: ['jxListView'],
+
+ /**
+ * APIMethod: render
+ */
+ render: function () {
+ this.parent();
+ this.elements = this.processTemplate(this.options.template, this.classes);
+ this.domObj = this.elements.get('jxListView');
+
+ if (this.options.selection) {
+ this.selection = this.options.selection;
+ } else if (this.options.select) {
+ this.selection = new Jx.Selection(this.options);
+ this.ownsSelection = true;
+ }
+
+ this.list = new Jx.List(this.domObj, this.listOptions, this.selection);
+
+ },
+
+ cleanup: function() {
+ if (this.ownsSelection) {
+ this.selection.destroy();
+ }
+ this.list.destroy();
+ },
+
+ add: function(item, where) {
+ this.list.add(item, where);
+ return this;
+ },
+
+ remove: function(item) {
+ this.list.remove(item);
+ return this;
+ },
+
+ replace: function(item, withItem) {
+ this.list.replace(item, withItem);
+ return this;
+ }
+});// $Id: $
+/**
+ * Class: Jx.Column
+ *
+ * Extends: <Jx.Object>
+ *
+ * The class used for defining columns for grids.
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Column = new Class({
+
+ Extends: Jx.Object,
+
+ options: {
+ /**
+ * Option: header
+ * The text to be used as a header for this column
+ */
+ header: null,
+ /**
+ * Option: modelField
+ * The field of the model that this column is keyed to
+ */
+ modelField: null,
+ /**
+ * Option: width
+ * Determines the width of the column. Set to 'null' or 'auto'
+ * to allow the column to autocalculate it's width based on its
+ * contents
+ */
+ width: null,
+ /**
+ * Option: isEditable
+ * allows/disallows editing of the column contents
+ */
+ isEditable: false,
+ /**
+ * Option: isSortable
+ * allows/disallows sorting based on this column
+ */
+ isSortable: false,
+ /**
+ * Option: isResizable
+ * allows/disallows resizing this column dynamically
+ */
+ isResizable: false,
+ /**
+ * Option: isHidden
+ * determines if this column can be shown or not
+ */
+ isHidden: false,
+ /**
+ * Option: formatter
+ * an instance of <Jx.Formatter> or one of its subclasses which
+ * will be used to format the data in this column. It can also be
+ * an object containing the name (This should be the part after
+ * Jx.Formatter in the class name. For instance, to get a currency
+ * formatter, specify 'Currency' as the name.) and options for the
+ * needed formatter (see individual formatters for options).
+ * (code)
+ * {
+ * name: 'formatter name',
+ * options: {}
+ * }
+ * (end)
+ */
+ formatter: null,
+ /**
+ * Option: name
+ * The name given to this column
+ */
+ name: '',
+ /**
+ * Option: dataType
+ * The type of the data in this column, used for sorting. Can be
+ * alphanumeric, numeric, currency, boolean, or date
+ */
+ dataType: 'alphanumeric',
+ /**
+ * Option: templates
+ * objects used to determine the type of tag and css class to
+ * assign to a header cell and a regular cell. The css class can
+ * also be a function that returns a string to assign as the css
+ * class. The function will be passed the text to be formatted.
+ */
+ templates: {
+ header: {
+ tag: 'span',
+ cssClass: null
+ },
+ cell: {
+ tag: 'span',
+ cssClass: null
+ }
+ }
+
+ },
+ /**
+ * Property: model
+ * holds a reference to the model (an instance of <Jx.Store> or subclass)
+ */
+ model: null,
+
+ parameters: ['options','grid'],
+
+ /**
+ * Constructor: Jx.Column
+ * initializes the column object
+ */
+ init : function () {
+ this.parent();
+ if ($defined(this.options.grid) && this.options.grid instanceof Jx.Grid) {
+ this.grid = this.options.grid;
+ }
+ this.name = this.options.name;
+ //we need to check the formatter
+ if ($defined(this.options.formatter)
+ && !(this.options.formatter instanceof Jx.Formatter)) {
+ var t = Jx.type(this.options.formatter);
+ if (t === 'object') {
+ this.options.formatter = new Jx.Formatter[this.options.formatter.name](
+ this.options.formatter.options);
+ }
+ }
+ },
+ /**
+ * APIMethod: getHeaderHTML
+ * Returns the header text wrapped in the tag specified in
+ * options.templates.hedaer.tag
+ */
+ getHeaderHTML : function () {
+ var text = this.options.header ? this.options.header
+ : this.options.modelField;
+ var ht = this.options.templates.header;
+ var el = new Element(ht.tag, {
+ 'class' : 'jxGridCellContent',
+ 'html' : text
+ });
+ if ($defined(ht.cssClass)) {
+ if (Jx.type(ht.cssClass) === 'function') {
+ el.addClass(ht.cssClass.run(text));
+ } else {
+ el.addClass(ht.cssClass);
+ }
+ }
+ this.header = el;
+ return el;
+ },
+
+ setWidth: function(newWidth) {
+ if (this.rule && parseInt(newWidth) >= 0) {
+ this.width = parseInt(newWidth);
+ this.rule.style.width = parseInt(newWidth) + "px";
+ }
+ },
+ /**
+ * APIMethod: getWidth
+ * returns the width of the column.
+ *
+ * Parameters:
+ * recalculate - {boolean} determines if the width should be recalculated
+ * if the column is set to autocalculate. Has no effect if the width is
+ * preset
+ * rowHeader - flag to tell us if this calculation is for the row header
+ */
+ getWidth : function (recalculate, rowHeader) {
+ rowHeader = $defined(rowHeader) ? rowHeader : false;
+ var maxWidth;
+ //check for null width or for "auto" setting and measure all contents in this column
+ //in the entire model as well as the header (really only way to do it).
+ if (!$defined(this.width) || recalculate) {
+ if (this.options.width !== null
+ && this.options.width !== 'auto') {
+ maxWidth = this.width = Jx.getNumber(this.options.width);
+ } else {
+ //calculate the width
+ var model = this.grid.getModel();
+ var oldPos = model.getPosition();
+ maxWidth = 0;
+ model.first();
+ while (model.valid()) {
+ //check size by placing text into a TD and measuring it.
+ //TODO: this should add .jxGridRowHead/.jxGridColHead if
+ // this is a header to get the correct measurement.
+ var text = model.get(this.options.modelField);
+ var klass = 'jxGridCell';
+ if (this.grid.row.useHeaders()
+ && this.options.modelField === this.grid.row
+ .getRowHeaderField()) {
+ klass = 'jxGridRowHead';
+ }
+ var s = this.measure(text, klass, rowHeader);
+ if (s.width > maxWidth) {
+ maxWidth = s.width;
+ }
+ if (model.hasNext()) {
+ model.next();
+ } else {
+ break;
+ }
+ }
+
+ //check the column header as well (unless this is the row header)
+ if (!(this.grid.row.useHeaders() && this.options.modelField === this.grid.row
+ .getRowHeaderField())) {
+ klass = 'jxGridColHead';
+ if (this.isEditable()) {
+ klass += ' jxColEditable';
+ }
+ if (this.isResizable()) {
+ klass += ' jxColResizable';
+ }
+ if (this.isSortable()) {
+ klass += ' jxColSortable';
+ }
+ s = this.measure(this.options.header, klass);
+ if (s.width > maxWidth) {
+ maxWidth = s.width;
+ }
+ }
+ if (!rowHeader) {
+ this.width = maxWidth;
+ }
+ model.moveTo(oldPos);
+ }
+ }
+ if (!rowHeader) {
+ return this.width;
+ } else {
+ return maxWidth;
+ }
+ },
+ /**
+ * Method: measure
+ * This method does the dirty work of actually measuring a cell
+ *
+ * Parameters:
+ * text - the text to measure
+ * klass - a string indicating and extra classes to add so that
+ * css classes can be taken into account.
+ */
+ measure : function (text, klass, rowHeader) {
+ if ($defined(this.options.formatter)
+ && text !== this.options.header) {
+ text = this.options.formatter.format(text);
+ }
+ var d = new Element('span', {
+ 'class' : klass
+ });
+ var el = new Element('span', {
+ 'html' : text,
+ 'class': 'jxGridCellContent'
+ }).inject(d);
+ d.setStyle('height', this.grid.row.getHeight());
+ d.setStyles({
+ 'visibility' : 'hidden',
+ 'width' : 'auto'
+ //'font-family' : 'Arial' removed because CSS may impose different font(s)
+ });
+ d.inject(document.body, 'bottom');
+ var s = d.measure(function () {
+ //if nogt rowHeader, get size of innner span
+ if (!rowHeader) {
+ return this.getFirst().getContentBoxSize();
+ } else {
+ return this.getMarginBoxSize();
+ }
+ });
+ d.destroy();
+ return s;
+ },
+ /**
+ * APIMethod: isEditable
+ * Returns whether this column can be edited
+ */
+ isEditable : function () {
+ return this.options.isEditable;
+ },
+ /**
+ * APIMethod: isSortable
+ * Returns whether this column can be sorted
+ */
+ isSortable : function () {
+ return this.options.isSortable;
+ },
+ /**
+ * APIMethod: isResizable
+ * Returns whether this column can be resized
+ */
+ isResizable : function () {
+ return this.options.isResizable;
+ },
+ /**
+ * APIMethod: isHidden
+ * Returns whether this column is hidden
+ */
+ isHidden : function () {
+ return this.options.isHidden;
+ },
+ /**
+ * APIMethod: getHTML
+ * returns the content of the current model row wrapped in the tag
+ * specified by options.templates.cell.tag and with the appropriate classes
+ * added
+ */
+ getHTML : function () {
+ var text = this.grid.getModel().get(this.options.modelField);
+ var ct = this.options.templates.cell;
+ if ($defined(this.options.formatter)) {
+ text = this.options.formatter.format(text);
+ }
+ var el = new Element(ct.tag, {
+ 'html' : text,
+ 'class' : 'jxGridCellContent',
+ styles: {
+ // width: this.getWidth()
+ }
+ });
+ if ($defined(ct.cssClass)) {
+ if (Jx.type(ct.cssClass) === 'function') {
+ el.addClass(ct.cssClass.run(text));
+ } else {
+ el.addClass(ct.cssClass);
+ }
+ }
+ return el;
+ }
+
+});// $Id: $
+/**
+ * Class: Jx.Columns
+ *
+ * Extends: <Jx.Object>
+ *
+ * This class is the container for all columns needed for a grid. It
+ * consolidates many functions that didn't make sense to put directly
+ * in the column class. Think of it as a model for columns.
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Columns = new Class({
+
+ Extends : Jx.Object,
+
+ options : {
+ /**
+ * Option: headerRowHeight
+ * the default height of the header row. Set to null or 'auto' to
+ * have this class attempt to figure out a suitable height.
+ */
+ headerRowHeight : 20,
+ /**
+ * Option: useHeaders
+ * Determines if the column headers should be displayed or not
+ */
+ useHeaders : false,
+ /**
+ * Option: columns
+ * an array holding all of the column instances or objects containing
+ * configuration info for the column
+ */
+ columns : []
+ },
+ /**
+ * Property: columns
+ * an array holding the actual instantiated column objects
+ */
+ columns : [],
+
+ parameters: ['options','grid'],
+
+ /**
+ * APIMethod: init
+ * Creates the class.
+ */
+ init : function () {
+ this.parent();
+
+ if ($defined(this.options.grid) && this.options.grid instanceof Jx.Grid) {
+ this.grid = this.options.grid;
+ }
+
+ this.options.columns.each(function (col) {
+ //check the column to see if it's a Jx.Grid.Column or an object
+ if (col instanceof Jx.Column) {
+ this.columns.push(col);
+ } else if (Jx.type(col) === "object") {
+ col.grid = this.grid;
+ this.columns.push(new Jx.Column(col));
+ }
+
+ }, this);
+ },
+ /**
+ * APIMethod: getHeaderHeight
+ * returns the height of the column header row
+ *
+ * Parameters:
+ * recalculate - determines if we should recalculate the height. Currently does nothing.
+ */
+ getHeaderHeight : function (recalculate) {
+ if (!$defined(this.height) || recalculate) {
+ if ($defined(this.options.headerRowHeight)
+ && this.options.headerRowHeight !== 'auto') {
+ this.height = this.options.headerRowHeight;
+ } else {
+ //figure out a height.
+ }
+ }
+ return this.height;
+ },
+ /**
+ * APIMethod: useHeaders
+ * returns whether the grid is/should display headers or not
+ */
+ useHeaders : function () {
+ return this.options.useHeaders;
+ },
+ /**
+ * APIMethod: getByName
+ * Used to get a column object by the name of the column
+ *
+ * Parameters:
+ * colName - the name of the column
+ */
+ getByName : function (colName) {
+ var ret;
+ this.columns.each(function (col) {
+ if (col.name === colName) {
+ ret = col;
+ }
+ }, this);
+ return ret;
+ },
+ /**
+ * APIMethod: getByField
+ * Used to get a column by the model field it represents
+ *
+ * Parameters:
+ * field - the field name to search by
+ */
+ getByField : function (field) {
+ var ret;
+ this.columns.each(function (col) {
+ if (col.options.modelField === field) {
+ ret = col;
+ }
+ }, this);
+ return ret;
+ },
+ /**
+ * APIMethod: getByGridIndex
+ * Used to get a column when all you know is the cell index in the grid
+ *
+ * Parameters:
+ * index - an integer denoting the placement of the column in the grid (zero-based)
+ */
+ getByGridIndex : function (index) {
+ var headers = this.grid.colTableBody.getFirst().getChildren();
+ var cell = headers[index];
+ var hClasses = cell.get('class').split(' ').filter(function (cls) {
+ return cls.test('jxColHead-');
+ });
+ var parts = hClasses[0].split('-');
+ return this.getByName(parts[1]);
+ },
+
+ /**
+ * APIMethod: getHeaders
+ * Returns a row with the headers in it.
+ *
+ * Parameters:
+ * row - the row to add the headers to.
+ */
+ getHeaders : function (row) {
+ var r = this.grid.row.useHeaders();
+ var hf = this.grid.row.getRowHeaderField();
+ this.columns.each(function (col, idx) {
+ if (r && hf === col.options.modelField) {
+ //do nothing
+ } else if (!col.isHidden()) {
+ var th = new Element('td', {
+ 'class' : 'jxGridColHead jxGridCol'+idx
+ });
+ th.adopt(col.getHeaderHTML());
+ // th.setStyle('width', col.getWidth());
+ th.addClass('jxColHead-' + col.options.modelField);
+ //add other styles for different attributes
+ if (col.isEditable()) {
+ th.addClass('jxColEditable');
+ }
+ if (col.isResizable()) {
+ th.addClass('jxColResizable');
+ }
+ if (col.isSortable()) {
+ th.addClass('jxColSortable');
+ }
+ // col.header = th;
+ row.appendChild(th);
+ }
+ }, this);
+ return row;
+ },
+ /**
+ * APIMethod: getColumnCells
+ * Appends the cells from each column for a specific row
+ *
+ * Parameters:
+ * row - the row (tr) to add the cells to.
+ */
+ getColumnCells : function (row) {
+ var r = this.grid.row;
+ var f = r.getRowHeaderField();
+ var h = r.useHeaders();
+ this.columns.each(function (col, idx) {
+ if (h && col.options.modelField !== f && !col.isHidden()) {
+ row.appendChild(this.getColumnCell(col, idx));
+ } else if (!h && !col.isHidden()) {
+ row.appendChild(this.getColumnCell(col, idx));
+ }
+ }, this);
+ return row;
+ },
+ /**
+ * APIMethod: getColumnCell
+ * Returns the cell (td) for a particular column.
+ *
+ * Paremeters:
+ * col - the column to get a cell for.
+ */
+ getColumnCell : function (col, idx) {
+
+ var td = new Element('td', {
+ 'class' : 'jxGridCell'
+ });
+ td.adopt(col.getHTML());
+ td.addClass('jxCol-' + col.options.modelField);
+ td.addClass('jxGridCol'+idx);
+ //add other styles for different attributes
+ if (col.isEditable()) {
+ td.addClass('jxColEditable');
+ }
+ if (col.isResizable()) {
+ td.addClass('jxColResizable');
+ }
+ if (col.isSortable()) {
+ td.addClass('jxColSortable');
+ }
+
+ return td;
+ },
+
+ createRules: function(styleSheet, scope) {
+ this.columns.each(function(col, idx) {
+ var selector = scope+' .jxGridCol'+idx+', '+scope + " .jxGridCol" + idx + " .jxGridCellContent";
+ col.rule = Jx.Styles.insertCssRule(selector, '', styleSheet);
+ col.rule.style.width = col.getWidth() + "px";
+ }, this);
+ },
+
+ /**
+ * APIMethod: getColumnCOunt
+ * returns the number of columns in this model (including hidden).
+ */
+ getColumnCount : function () {
+ return this.columns.length;
+ },
+ /**
+ * APIMethod: getIndexFromGrid
+ * Gets the index of a column from its place in the grid.
+ *
+ * Parameters:
+ * name - the name of the column to get an index for
+ */
+ getIndexFromGrid : function (name) {
+ var headers = this.grid.colTableBody.getFirst().getChildren();
+ var c;
+ var i = -1;
+ headers.each(function (h) {
+ i++;
+ var hClasses = h.get('class').split(' ').filter(function (cls) {
+ return cls.test('jxColHead-');
+ });
+ hClasses.each(function (cls) {
+ if (cls.test(name)) {
+ c = i;
+ }
+ });
+ }, this);
+ return c;
+ }
+
+});
+// $Id: $
+/**
+ * Class: Jx.Row
+ *
+ * Extends: <Jx.Object>
+ *
+ * A class defining a grid row.
+ *
+ * Inspired by code in the original Jx.Grid class
+ *
+ * License:
+ * Original Copyright (c) 2008, DM Solutions Group Inc.
+ * This version Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Row = new Class(
+{
+
+ Extends : Jx.Object,
+
+ options : {
+ /**
+ * Option: useHeaders
+ * defaults to false. If set to true, then a column of row header
+ * cells are displayed.
+ */
+ useHeaders : false,
+ /**
+ * Option: alternateRowColors
+ * defaults to false. If set to true, then alternating CSS classes
+ * are used for rows.
+ */
+ alternateRowColors : false,
+ /**
+ * Option: rowClasses
+ * object containing class names to apply to rows
+ */
+ rowClasses : {
+ odd : 'jxGridRowOdd',
+ even : 'jxGridRowEven',
+ all : 'jxGridRowAll'
+ },
+ /**
+ * Option: rowHeight
+ * The height of the row. Make it null or 'auto' to auto-calculate
+ */
+ rowHeight : 20,
+ /**
+ * Option: headerWidth
+ * The width of the row header. Make it null or 'auto' to auto-calculate
+ */
+ headerWidth : 20,
+ /**
+ * Option: headerField
+ * The field in the model to use as the header
+ */
+ headerField : 'id',
+ /**
+ * Option: templates
+ * objects used to determine the type of tag and css class to
+ * assign to a header cell. The css class can
+ * also be a function that returns a string to assign as the css
+ * class. The function will be passed the text to be formatted.
+ */
+ templates: {
+ header: {
+ tag: 'span',
+ cssClass: null
+ }
+ }
+
+ },
+ /**
+ * Property: grid
+ * A reference to the grid that this row model belongs to
+ */
+ grid : null,
+
+ parameters: ['options','grid'],
+
+ /**
+ * APIMethod: init
+ * Creates the row model object.
+ */
+ init : function () {
+ this.parent();
+
+ if ($defined(this.options.grid) && this.options.grid instanceof Jx.Grid) {
+ this.grid = this.options.grid;
+ }
+ },
+ /**
+ * APIMethod: getGridRowElement
+ * Used to create the TR for the main grid row
+ */
+ getGridRowElement : function () {
+
+ var tr = new Element('tr');
+ tr.setStyle('height', this.getHeight());
+ if (this.options.alternateRowColors) {
+ tr.className = (this.grid.getModel().getPosition() % 2) ? this.options.rowClasses.even
+ : this.options.rowClasses.odd;
+ } else {
+ tr.className = this.options.rowClasses.all;
+ }
+ return tr;
+ },
+ /**
+ * Method: getRowHeaderCell
+ * creates the TH for the row's header
+ */
+ getRowHeaderCell : function () {
+ //get and set text for element
+ var model = this.grid.getModel();
+ var th = new Element('td', {
+ 'class' : 'jxGridRowHead'
+ });
+
+ var text = model.get(this.options.headerField);
+ var ht = this.options.templates.header;
+ var el = new Element(ht.tag, {
+ 'class' : 'jxGridCellContent',
+ 'html' : text
+ }).inject(th);
+ if ($defined(ht.cssClass)) {
+ if (Jx.type(ht.cssClass) === 'function') {
+ el.addClass(ht.cssClass.run(text));
+ } else {
+ el.addClass(ht.cssClass);
+ }
+ }
+
+ return th;
+
+ },
+ /**
+ * APIMethod: getRowHeaderWidth
+ * determines the row header's width.
+ */
+ getRowHeaderWidth : function () {
+ //this can be drawn from the column for the
+ //header field
+ var col = this.grid.columns.getByField(this.options.headerField);
+ return col.getWidth(true, true);
+ },
+
+ /**
+ * APIMethod: getHeight
+ * determines and returns the height of a row
+ */
+ getHeight : function () {
+ //this should eventually compute a height, however, we would need
+ //a fixed width to do so reliably. For right now, we use a fixed height
+ //for all rows.
+ return this.options.rowHeight;
+ },
+ /**
+ * APIMethod: useHeaders
+ * determines and returns whether row headers should be used
+ */
+ useHeaders : function () {
+ return this.options.useHeaders;
+ },
+ /**
+ * APIMethod: getRowHeader
+ * creates and returns the header for the current row
+ */
+ getRowHeader : function () {
+ var rowHeight = this.getHeight();
+ var tr = new Element('tr', {
+ styles : {
+ height : rowHeight
+ }
+ });
+ var th = this.getRowHeaderCell();
+ if (this.grid.model.getPosition() === 0) {
+ var rowWidth = this.getRowHeaderWidth();
+ th.setStyle("width", rowWidth);
+ }
+ tr.appendChild(th);
+ return tr;
+ },
+ /**
+ * APIMethod: getRowHeaderField
+ * returns the name of the model field that is used for the header
+ */
+ getRowHeaderField : function () {
+ return this.options.headerField;
+ }
+});
+// $Id: $
+/**
+ * Class: Jx.Grid.Plugin
+ *
+ * Extend: <Jx.Object>
+ *
+ * Base class for all plugins. In order for a plugin to be used it must
+ * extend from this class.
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Plugin = new Class({
+
+ Extends: Jx.Object,
+
+ Family: "Jx.Plugin",
+
+ options: {},
+
+ /**
+ * APIMethod: attach
+ * Empty method that must be overridden by subclasses. It is
+ * called by the user of the plugin to setup the plugin for use.
+ */
+ attach: function(obj){
+ obj.registerPlugin(this);
+ },
+
+ /**
+ * APIMethod: detach
+ * Empty method that must be overridden by subclasses. It is
+ * called by the user of the plugin to remove the plugin.
+ */
+ detach: function(obj){
+ obj.deregisterPlugin(this);
+ }
+
+});// $Id: $
+/**
+ * Class: Jx.Plugin.Grid
+ * Grid plugin namespace
+ *
+ *
+ * License:
+ * This version Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Plugin.Grid = {};// $Id: grid.js 572 2009-10-29 05:53:36Z jonlb at comcast.net $
+/**
* Class: Jx.Grid
*
- * Extends: Object
+ * Extends: <Jx.Widget>
*
- * Implements: Options, Events, <Jx.Addable>
+ * A tabular control that has fixed, optional, scrolling headers on the rows and
+ * columns like a spreadsheet.
*
- * A tabular control that has fixed scrolling headers on the rows and columns
- * like a spreadsheet.
- *
* Jx.Grid is a tabular control with convenient controls for resizing columns,
- * sorting, and inline editing. It is created inside another element, typically a
- * div. If the div is resizable (for instance it fills the page or there is a
+ * sorting, and inline editing. It is created inside another element, typically
+ * a div. If the div is resizable (for instance it fills the page or there is a
* user control allowing it to be resized), you must call the resize() method
* of the grid to let it know that its container has been resized.
*
* When creating a new Jx.Grid, you can specify a number of options for the grid
- * that control its appearance and functionality.
+ * that control its appearance and functionality. You can also specify plugins
+ * to load for additional functionality. Currently Jx provides the following
+ * plugins
+ *
+ * Prelighter - prelights rows, columns, and cells
+ * Selector - selects rows, columns, and cells
+ * Sorter - sorts rows by specific column
*
* Jx.Grid renders data that comes from an external source. This external
- * source, called the model, must implement the following interface.
+ * source, called the model, must be a Jx.Store or extended from it (such as
+ * Jx.Store.Remote).
*
*
- * Example:
- * (code)
- * (end)
*
* License:
* Copyright (c) 2008, DM Solutions Group Inc.
+ * This version Copyright (c) 2009, Jon Bomgardner.
*
* This file is licensed under an MIT style license
*/
Jx.Grid = new Class({
- Family: 'Jx.Grid',
- Implements: [Options, Events, Jx.Addable],
- domObj : null,
- model : null,
- options: {
- /* Option: parent
- * the HTML element to create the grid inside. The grid will resize
- * to fill the domObj.
- */
- parent: null,
- /* Option: alternateRowColors
- * defaults to false. If set to true, then alternating CSS classes
- * are used for rows.
- */
- alternateRowColors: false,
- /* Option: rowHeaders
- * defaults to false. If set to true, then a column of row header
- * cells are displayed.
- */
- rowHeaders: false,
- /* Option: columnHeaders
- * defaults to false. If set to true, then a column of row header
- * cells are displayed.
- */
- columnHeaders: false,
- /* Option: rowSelection
- * defaults to false. If set to true, allow the user to select rows.
- */
- rowSelection: false,
- /* Option: columnSelection
- * defaults to false. If set to true, allow the user to select
- * columns.
- */
- columnSelection: false,
- /* Option: cellPrelight
- * defaults to false. If set to true, the cell under the mouse is
- * highlighted as the mouse moves.
- */
- cellPrelight: false,
- /* Option: rowPrelight
- * defaults to false. If set to true, the row under the mouse is
- * highlighted as the mouse moves.
- */
- rowPrelight: false,
- /* Option: columnPrelight
- * defaults to false. If set to true, the column under the mouse is
- * highlighted as the mouse moves.
- */
- columnPrelight: false,
- /* Option: rowHeaderPrelight
- * defaults to false. If set to true, the row header of the row under
- * the mouse is highlighted as the mouse moves.
+
+ Family : 'Jx.Grid',
+ Extends : Jx.Widget,
+
+ options : {
+ /**
+ * Option: parent
+ * the HTML element to create the grid inside. The grid will resize
+ * to fill the domObj.
*/
- rowHeaderPrelight: false,
- /* Option: columnHeaderPrelight
- * defaults to false. If set to true, the column header of the column
- * under the mouse is highlighted as the mouse moves.
+ parent : null,
+
+ /**
+ * Options: columns
+ * an object consisting of a columns array that defines the individuals
+ * columns as well as containing any options for Jx.Grid.Columns or
+ * a Jx.Grid.Columns object itself.
*/
- columnHeaderPrelight: false,
- /* Option: cellSelection
- * defaults to false. If set to true, allow the user to select
- * cells.
- */
- cellSelection: false
+ columns : {
+ columns : []
+ },
+
+ /**
+ * Option: row
+ * Either a Jx.Grid.Row object or a json object defining options for
+ * the class
+ */
+ row : null,
+
+ /**
+ * Option: plugins
+ * an array containing Jx.Grid.Plugin subclasses or an object
+ * that indicates the name of a predefined plugin and its options.
+ */
+ plugins : [],
+
+ /**
+ * Option: model
+ * An instance of Jx.Store or one of its descendants
+ */
+ model : null,
+
+ deferRender: true
+
},
/**
+ * Property: model
+ * holds a reference to the <Jx.Store> that is the model for this
+ * grid
+ */
+ model : null,
+ /**
+ * Property: columns
+ * holds a reference to the columns object
+ */
+ columns : null,
+ /**
+ * Property: row
+ * Holds a reference to the row object
+ */
+ row : null,
+ /**
+ * Property: currentCell
+ * holds an object indicating the current cell that the mouse is over
+ */
+ currentCell : null,
+
+ /**
+ * Property: styleSheet
+ * the name of the dynamic style sheet to use for manipulating styles
+ */
+ styleSheet: 'JxGridStyles',
+ /**
+ * Property: pluginNamespace
+ * the required variable for plugins
+ */
+ pluginNamespace: 'Grid',
+
+ /**
* Constructor: Jx.Grid
- * construct a new instance of Jx.Grid within the domObj
- *
- * Parameters:
- * options - <Jx.Grid.Options>
*/
- initialize : function( options ) {
- this.setOptions(options);
+ init : function () {
+ this.uniqueId = this.generateId('jxGrid_');
- this.domObj = new Element('div');
- new Jx.Layout(this.domObj, {
- onSizeChange: this.resize.bind(this)
+ if ($defined(this.options.model)
+ && this.options.model instanceof Jx.Store) {
+ this.model = this.options.model;
+ this.model.addEvent('columnChanged', this.modelChanged
+ .bind(this));
+ this.model.addEvent('sortFinished', this.render.bind(this));
+ }
+
+ if ($defined(this.options.columns)) {
+ if (this.options.columns instanceof Jx.Columns) {
+ this.columns = this.options.columns;
+ } else if (Jx.type(this.options.columns) === 'object') {
+ var opts = this.options.columns;
+ opts.grid = this;
+ this.columns = new Jx.Columns(opts);
+ }
+ }
+
+ //check for row
+ if ($defined(this.options.row)) {
+ if (this.options.row instanceof Jx.Row) {
+ this.row = this.options.row;
+ } else if (Jx.type(this.options.row) === "object") {
+ var opts = this.options.row;
+ opts.grid = this;
+ this.row = new Jx.Row(opts);
+ }
+ } else {
+ this.row = new Jx.Row({grid: this});
+ }
+
+ //initialize the grid
+ this.domObj = new Element('div', {'class':this.uniqueId});
+ var l = new Jx.Layout(this.domObj, {
+ onSizeChange : this.resize.bind(this)
});
-
+
if (this.options.parent) {
this.addTo(this.options.parent);
- }
-
- this.rowColObj = new Element('div', {'class':'jxGridContainer'});
-
- this.colObj = new Element('div', {'class':'jxGridContainer'});
- this.colTable = new Element('table', {'class':'jxGridTable'});
- this.colTableHead = new Element('thead');
- this.colTable.appendChild(this.colTableHead);
+ }
+
+ //top left corner
+ this.rowColObj = new Element('div', {
+ 'class' : 'jxGridContainer'
+ });
+
+ //holds the column headers
+ this.colObj = new Element('div', {
+ 'class' : 'jxGridContainer'
+ });
+ this.colTable = new Element('table', {
+ 'class' : 'jxGridTable jxGridHeader'
+ });
this.colTableBody = new Element('tbody');
this.colTable.appendChild(this.colTableBody);
this.colObj.appendChild(this.colTable);
-
- this.rowObj = new Element('div', {'class':'jxGridContainer'});
- this.rowTable = new Element('table', {'class':'jxGridTable'});
+
+ //hold the row headers
+ this.rowObj = new Element('div', {
+ 'class' : 'jxGridContainer jxGridHeader'
+ });
+ this.rowTable = new Element('table', {
+ 'class' : 'jxGridTable'
+ });
this.rowTableHead = new Element('thead');
this.rowTable.appendChild(this.rowTableHead);
this.rowObj.appendChild(this.rowTable);
-
- this.gridObj = new Element('div', {'class':'jxGridContainer',styles:{overflow:'scroll'}});
- this.gridTable = new Element('table', {'class':'jxGridTable'});
+
+ //The actual body of the grid
+ this.gridObj = new Element('div', {
+ 'class' : 'jxGridContainer',
+ styles : {
+ overflow : 'auto'
+ }
+ });
+ this.gridTable = new Element('table', {
+ 'class' : 'jxGridTable'
+ });
this.gridTableBody = new Element('tbody');
this.gridTable.appendChild(this.gridTableBody);
this.gridObj.appendChild(this.gridTable);
-
+
this.domObj.appendChild(this.rowColObj);
this.domObj.appendChild(this.rowObj);
this.domObj.appendChild(this.colObj);
this.domObj.appendChild(this.gridObj);
-
+
this.gridObj.addEvent('scroll', this.onScroll.bind(this));
- this.gridObj.addEvent('click', this.onClickGrid.bindWithEvent(this));
- this.rowObj.addEvent('click', this.onClickRowHeader.bindWithEvent(this));
- this.colObj.addEvent('click', this.onClickColumnHeader.bindWithEvent(this));
- this.gridObj.addEvent('mousemove', this.onMouseMoveGrid.bindWithEvent(this));
- this.rowObj.addEvent('mousemove', this.onMouseMoveRowHeader.bindWithEvent(this));
- this.colObj.addEvent('mousemove', this.onMouseMoveColumnHeader.bindWithEvent(this));
+ this.gridObj.addEvent('click', this.onGridClick
+ .bindWithEvent(this));
+ this.rowObj.addEvent('click', this.onGridClick
+ .bindWithEvent(this));
+ this.colObj.addEvent('click', this.onGridClick
+ .bindWithEvent(this));
+ this.gridObj.addEvent('mousemove', this.onMouseMove
+ .bindWithEvent(this));
+ this.rowObj.addEvent('mousemove', this.onMouseMove
+ .bindWithEvent(this));
+ this.colObj.addEvent('mousemove', this.onMouseMove
+ .bindWithEvent(this));
+
+ this.parent();
+
+ this.domObj.store('grid', this);
},
-
+
/**
* Method: onScroll
* handle the grid scrolling by updating the position of the headers
*/
- onScroll: function() {
+ onScroll : function () {
this.colObj.scrollLeft = this.gridObj.scrollLeft;
- this.rowObj.scrollTop = this.gridObj.scrollTop;
+ this.rowObj.scrollTop = this.gridObj.scrollTop;
},
+
+ /**
+ * Method: onMouseMove
+ * Handle the mouse moving over the grid. This determines
+ * what column and row it's over and fires the gridMove event
+ * with that information for plugins to respond to.
+ *
+ * Parameters:
+ * e - {Event} the browser event object
+ */
+ onMouseMove : function (e) {
+ var rc = this.getRowColumnFromEvent(e);
+ if (!$defined(this.currentCell)
+ || (this.currentCell.row !== rc.row || this.currentCell.column !== rc.column)) {
+ this.currentCell = rc;
+ this.fireEvent('gridMove', rc);
+ }
+
+ },
+ /**
+ * Method: onGridClick
+ * handle the user clicking on the grid. Fires gridClick
+ * event for plugins to respond to.
+ *
+ * Parameters:
+ * e - {Event} the browser event object
+ */
+ onGridClick : function (e) {
+ var rc = this.getRowColumnFromEvent(e);
+ this.fireEvent('gridClick', rc);
+ },
+
+ /**
+ * Method: getRowColumnFromEvent
+ * retrieve the row and column indexes from an event click.
+ * This function is used by the grid, row header and column
+ * header to safely get these numbers.
+ *
+ * If the event isn't valid (i.e. it wasn't on a TD or TH) then
+ * the returned values will be -1, -1
+ *
+ * Parameters:
+ * e - {Event} the browser event object
+ *
+ * @return Object an object with two properties, row and column,
+ * that contain the row and column that was clicked
+ */
+ getRowColumnFromEvent : function (e) {
+ var td = e.target;
+ if (td.tagName === 'SPAN') {
+ td = document.id(td).getParent();
+ }
+ if (td.tagName !== 'TD' && td.tagName !== 'TH') {
+ return {
+ row : -1,
+ column : -1
+ };
+ }
+
+ var colheader = false;
+ var rowheader = false;
+ //check if this is a header (row or column)
+ if (td.descendantOf(this.colTable)) {
+ colheader = true;
+ }
+ if (td.descendantOf(this.rowTable)) {
+ rowheader = true;
+ }
+
+ var tr = td.parentNode;
+ var col = td.cellIndex;
+ var row = tr.rowIndex;
+ /*
+ * if this is not a header cell, then increment the row and col. We do this
+ * based on whether the header is shown. This way the row/col remains consistent
+ * to the grid but also takes into account the headers. It also allows
+ * us to refrain from having to fire a separate event for headers.
+ *
+ * Plugins/event listeners should always take into account whether headers
+ * are displayed or not.
+ */
+ if (this.row.useHeaders() && !rowheader) {
+ col++;
+ }
+ if (this.columns.useHeaders() && !colheader) {
+ row++;
+ }
+
+ if (Browser.Engine.webkit) {
+ /* bug in safari (webkit) returns 0 for cellIndex - only choice seems
+ * to be to loop through the row
+ */
+ for (var i = 0; i < tr.childNodes.length; i++) {
+ if (tr.childNodes[i] === td) {
+ col = i;
+ break;
+ }
+ }
+ }
+ return {
+ row : row,
+ column : col
+ };
+ },
+
/**
- * Method: resize
+ * APIMethod: resize
* resize the grid to fit inside its container. This involves knowing something
* about the model it is displaying (the height of the column header and the
* width of the row header) so nothing happens if no model is set
*/
- resize: function() {
+ resize : function () {
if (!this.model) {
return;
}
-
- /* TODO: Jx.Grid.resize
- * if not showing column or row, should we handle the resize differently
- */
- var colHeight = this.options.columnHeaders ? this.model.getColumnHeaderHeight() : 1;
- var rowWidth = this.options.rowHeaders ? this.model.getRowHeaderWidth() : 1;
-
- var size = Element.getContentBoxSize(this.domObj);
-
+
+ var colHeight = this.columns.useHeaders() ? this.columns
+ .getHeaderHeight() : 1;
+ var rowWidth = this.row.useHeaders() ? this.row
+ .getRowHeaderWidth() : 1;
+
+ var size = this.domObj.getContentBoxSize();
+
+ //sum all of the column widths except the hidden columns and the header column
+ var w = size.width - rowWidth - 1;
+ var totalCols = 0;
+ this.columns.columns.each(function (col) {
+ if (col.options.modelField !== this.row.getRowHeaderField()
+ && !col.isHidden()) {
+ totalCols += col.getWidth();
+ }
+ }, this);
+
/* -1 because of the right/bottom borders */
this.rowColObj.setStyles({
- width: rowWidth-1,
- height: colHeight-1
+ width : rowWidth - 1,
+ height : colHeight - 1
});
this.rowObj.setStyles({
- top:colHeight,
- left:0,
- width:rowWidth-1,
- height:size.height-colHeight-1
+ top : colHeight,
+ left : 0,
+ width : rowWidth - 1,
+ height : size.height - colHeight - 1
});
this.colObj.setStyles({
- top: 0,
- left: rowWidth,
- width: size.width - rowWidth - 1,
- height: colHeight - 1
+ top : 0,
+ left : rowWidth,
+ width : size.width - rowWidth - 1,
+ height : colHeight - 1
});
this.gridObj.setStyles({
- top: colHeight,
- left: rowWidth,
- width: size.width - rowWidth - 1,
- height: size.height - colHeight - 1
+ top : colHeight,
+ left : rowWidth,
+ width : size.width - rowWidth - 1,
+ height : size.height - colHeight - 1
});
+
},
-
+
/**
- * Method: setModel
+ * APIMethod: setModel
* set the model for the grid to display. If a model is attached to the grid
- * it is removed and the new model is displayed.
+ * it is removed and the new model is displayed. However, It needs to have
+ * the same columns
*
* Parameters:
* model - {Object} the model to use for this grid
*/
- setModel: function(model) {
+ setModel : function (model) {
this.model = model;
if (this.model) {
- if (this.domObj.resize) {
- this.domObj.resize();
- }
- this.createGrid();
- this.resize();
+ this.render();
+ this.domObj.resize();
} else {
this.destroyGrid();
}
},
-
+
/**
- * Method: destroyGrid
+ * APIMethod: getModel
+ * gets the model set for this grid.
+ */
+ getModel : function () {
+ return this.model;
+ },
+
+ /**
+ * APIMethod: destroyGrid
* destroy the contents of the grid safely
*/
- destroyGrid: function() {
- var n = this.colTableHead.cloneNode(false);
- this.colTable.replaceChild(n, this.colTableHead);
- this.colTableHead = n;
-
- n = this.colTableBody.cloneNode(false);
+ destroyGrid : function () {
+
+ var n = this.colTableBody.cloneNode(false);
this.colTable.replaceChild(n, this.colTableBody);
this.colTableBody = n;
-
+
n = this.rowTableHead.cloneNode(false);
this.rowTable.replaceChild(n, this.rowTableHead);
this.rowTableHead = n;
-
+
n = this.gridTableBody.cloneNode(false);
this.gridTable.replaceChild(n, this.gridTableBody);
this.gridTableBody = n;
-
+
},
-
+
/**
- * Method: createGrid
- * create the grid for the current model
+ * APIMethod: render
+ * Create the grid for the current model
*/
- createGrid: function() {
+ render : function () {
this.destroyGrid();
+
+ this.fireEvent('beginCreateGrid', this);
+
if (this.model) {
var model = this.model;
- var nColumns = model.getColumnCount();
- var nRows = model.getRowCount();
+ var nColumns = this.columns.getColumnCount();
+ var nRows = model.count();
+ var th;
/* create header if necessary */
- if (this.options.columnHeaders) {
- var colHeight = model.getColumnHeaderHeight();
- var trHead = new Element('tr');
- this.colTableHead.appendChild(trHead);
- var trBody = new Element('tr');
+ if (this.columns.useHeaders()) {
+ this.colTableBody.setStyle('visibility', 'visible');
+ var colHeight = this.columns.getHeaderHeight();
+ var trBody = new Element('tr', {
+ styles : {
+ height : colHeight
+ }
+ });
this.colTableBody.appendChild(trBody);
-
- var th = new Element('th', {styles:{width:0,height:0}});
- trHead.appendChild(th);
- th = th.cloneNode(true);
- th.setStyle('height',colHeight);
- trBody.appendChild(th);
- for (var i=0; i<nColumns; i++) {
- var colWidth = model.getColumnWidth(i);
- th = new Element('th', {'class':'jxGridColHeadHide',styles:{width:colWidth}});
- var p = new Element('p', {styles:{height:0,width:colWidth}});
- th.appendChild(p);
- trHead.appendChild(th);
- th = new Element('th', {
- 'class':'jxGridColHead',
- html:model.getColumnHeaderHTML(i)
- });
- trBody.appendChild(th);
- }
+
+ this.columns.getHeaders(trBody);
+
/* one extra column at the end for filler */
- var th = new Element('th',{styles:{width:1000,height:0}});
- trHead.appendChild(th);
- th = th.cloneNode(true);
- th.setStyle('height',colHeight - 1);
- th.className = 'jxGridColHead';
- trBody.appendChild(th);
-
+ th = new Element('td', {
+ 'class':'jxGridColHead'
+ }).inject(trBody);
+ new Element('span',{
+ 'class': 'jxGridCellContent',
+ styles : {
+ width : 1000,
+ height : colHeight - 1
+ }
+ }).inject(th);
+
+ } else {
+ //hide the headers
+ this.colTableBody.setStyle('visibility', 'hidden');
}
- if (this.options.rowHeaders) {
- var rowWidth = model.getRowHeaderWidth();
- var tr = new Element('tr');
- var td = new Element('td', {styles:{width:0,height:0}});
- tr.appendChild(td);
- var th = new Element('th', {styles:{width:rowWidth,height:0}});
- tr.appendChild(th);
- this.rowTableHead.appendChild(tr);
- for (var i=0; i<nRows; i++) {
- var rowHeight = model.getRowHeight(i);
- var tr = new Element('tr');
- var td = new Element('td', {'class':'jxGridRowHeadHide', styles:{width:0,height:rowHeight}});
- var p = new Element('p', {styles:{width:0,height:rowHeight}});
- td.appendChild(p);
- tr.appendChild(td);
- var th = new Element('th', {'class':'jxGridRowHead', html:model.getRowHeaderHTML(i)});
- tr.appendChild(th);
+ if (this.row.useHeaders()) {
+ this.rowTableHead.setStyle('visibility', 'visible');
+
+ var tr;
+ //loop through all rows and add header
+ this.model.first();
+ while (this.model.valid()) {
+ tr = this.row.getRowHeader();
this.rowTableHead.appendChild(tr);
+ if (this.model.hasNext()) {
+ this.model.next();
+ } else {
+ break;
+ }
}
/* one extra row at the end for filler */
- var tr = new Element('tr');
- var td = new Element('td',{
- styles:{
- width:0,
- height:1000
+ tr = new Element('tr').inject(this.rowTableHead);
+ th = new Element('td', {
+ 'class' : 'jxGridRowHead',
+ styles : {
+ width : this.row.getRowHeaderWidth(),
+ height : 1000
}
- });
- tr.appendChild(td);
- var th = new Element('th',{
- 'class':'jxGridRowHead',
- styles:{
- width:rowWidth,
- height:1000
- }
- });
- tr.appendChild(th);
- this.rowTableHead.appendChild(tr);
+ }).inject(tr);
+ } else {
+ //hide row headers
+ this.rowTableHead.setStyle('visibility', 'hidden');
}
- var colHeight = model.getColumnHeaderHeight();
- var trBody = new Element('tr');
- this.gridTableBody.appendChild(trBody);
+ colHeight = this.columns.getHeaderHeight();
- var td = new Element('td', {styles:{width:0,height:0}});
- trBody.appendChild(td);
- for (var i=0; i<nColumns; i++) {
- var colWidth = model.getColumnWidth(i);
- td = new Element('td', {'class':'jxGridColHeadHide', styles:{width:colWidth}});
- var p = new Element('p', {styles:{width:colWidth,height:0}});
- td.appendChild(p);
- trBody.appendChild(td);
- }
-
- for (var j=0; j<nRows; j++) {
- var rowHeight = model.getRowHeight(j);
- var actualRowHeight = rowHeight;
- var tr = new Element('tr');
+ //This section actually adds the rows
+ this.model.first();
+ while (this.model.valid()) {
+ tr = this.row.getGridRowElement();
this.gridTableBody.appendChild(tr);
-
- var td = new Element('td', {
- 'class':'jxGridRowHeadHide',
- styles: {
- width: 0,
- height:rowHeight
- }
- });
- var p = new Element('p',{styles:{height:rowHeight}});
- td.appendChild(p);
- tr.appendChild(td);
- for (var i=0; i<nColumns; i++) {
- var colWidth = model.getColumnWidth(i);
- td = new Element('td', {'class':'jxGridCell'});
- td.innerHTML = model.getValueAt(j,i);
- tr.appendChild(td);
- var tdSize = td.getSize();
- if (tdSize.height > actualRowHeight) {
- actualRowHeight = tdSize.height;
- }
- }
- /* some notes about row sizing
- * In Safari, the height of a TR is always returned as 0
- * In Safari, the height of any given TD is the height it would
- * render at, not the actual height of the row
- * In IE, the height is returned 1px bigger than any other browser
- * Firefox just works
- *
- * So, for Safari, we have to measure every TD and take the highest one
- * and if its IE, we subtract 1 from the overall height, making all
- * browsers identical
- *
- * Using document.all is not a good hack for this
- */
- if (document.all) {
- actualRowHeight -= 1;
- }
- if (this.options.rowHeaders) {
- this.setRowHeaderHeight(j, actualRowHeight);
- }
- /* if we apply the class before adding content, it
- * causes a rendering error in IE (off by 1) that is 'fixed'
- * when another class is applied to the row, causing dynamic
- * shifting of the row heights
- */
- if (this.options.alternateRowColors) {
- tr.className = (j%2) ? 'jxGridRowOdd' : 'jxGridRowEven';
+
+ //Actually add the columns
+ this.columns.getColumnCells(tr);
+
+ if (this.model.hasNext()) {
+ this.model.next();
} else {
- tr.className = 'jxGridRowAll';
+ break;
}
+
}
+ Jx.Styles.enableStyleSheet(this.styleSheet);
+ this.columns.createRules(this.styleSheet, "."+this.uniqueId);
}
+ this.domObj.resize();
+ this.fireEvent('doneCreateGrid', this);
},
/**
- * Method: setRowHeaderHeight
- * set the height of a row. This is used internally to adjust the height of
- * the row header when cell contents wrap. A limitation of the table structure
- * is that overflow: hidden on a td will work horizontally but not vertically
+ * Method: modelChanged
+ * Event listener that is fired when the model changes in some way
+ */
+ modelChanged : function (row, col) {
+ //grab new TD
+ var column = this.columns.getIndexFromGrid(col.name);
+ var td = document.id(this.gridObj.childNodes[0].childNodes[0].childNodes[row].childNodes[column]);
+
+ var currentRow = this.model.getPosition();
+ this.model.moveTo(row);
+
+ var newTD = this.columns.getColumnCell(this.columns.getByName(col.name));
+ newTD.replaces(td);
+
+ this.model.moveTo(currentRow);
+ }
+
+});
+// $Id: $
+/**
+ * Class: Jx.Plugin.Selector
+ *
+ * Extends: <Jx.Plugin>
+ *
+ * Grid plugin to select rows, columns, and/or cells.
+ *
+ * Original selection code from Jx.Grid's original class
+ *
+ * License:
+ * Original Copyright (c) 2008, DM Solutions Group Inc.
+ * This version Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Plugin.Grid.Selector = new Class({
+
+ Extends : Jx.Plugin,
+
+ options : {
+ /**
+ * Option: cell
+ * determines if cells are selectable
+ */
+ cell : false,
+ /**
+ * Option: row
+ * determines if rows are selectable
+ */
+ row : false,
+ /**
+ * Option: column
+ * determines if columns are selectable
+ */
+ column : false
+ },
+ /**
+ * Property: bound
+ * storage for bound methods useful for working with events
+ */
+ bound: {},
+ /**
+ * APIMethod: init
+ * construct a new instance of the plugin. The plugin must be attached
+ * to a Jx.Grid instance to be useful though.
+ */
+ init: function() {
+ this.parent();
+ this.bound.select = this.select.bind(this);
+ },
+ /**
+ * APIMethod: attach
+ * Sets up the plugin and attaches the plugin to the grid events it
+ * will be monitoring
+ */
+ attach: function (grid) {
+ if (!$defined(grid) && !(grid instanceof Jx.Grid)) {
+ return;
+ }
+ this.grid = grid;
+ this.grid.addEvent('gridClick', this.bound.select);
+ },
+ /**
+ * APIMethod: detach
+ */
+ detach: function() {
+ if (this.grid) {
+ this.grid.removeEvent('gridClick', this.bound.select);
+ }
+ this.grid = null;
+ },
+ /**
+ * Method: select
+ * dispatches the grid click to the various selection methods
+ */
+ select : function (rc) {
+ if ($defined(rc) && rc.column !== -1 && rc.row !== -1) {
+ var row = rc.row;
+ if (this.grid.columns.useHeaders()) {
+ row--;
+ }
+ var column = rc.column;
+ if (this.grid.row.useHeaders()) {
+ column--;
+ }
+ if (this.options.cell) {
+ this.selectCell(row, column);
+ }
+ if (this.options.row) {
+ this.selectRow(row);
+ }
+ if (this.options.column) {
+ this.selectColumn(column);
+ }
+ }
+ },
+ /**
+ * Method: selectCell
+ * Select a cell and apply the jxGridCellSelected style to it.
+ * This deselects a previously selected cell.
*
+ * If the model supports cell selection, it should implement
+ * a cellSelected function to receive notification of the selection.
+ *
* Parameters:
- * row - {Integer} the row to set the height for
- * height - {Integer} the height to set the row (in pixels)
+ * row - {Integer} the row of the cell to select
+ * col - {Integer} the column of the cell to select
*/
- setRowHeaderHeight: function(row, height) {
- //this.rowTableHead.childNodes[row+1].childNodes[0].style.height = (height) + 'px';
- this.rowTableHead.childNodes[row+1].childNodes[0].childNodes[0].style.height = (height) + 'px';
+ selectCell : function (row, col) {
+ var td = (row >= 0 && col >= 0
+ && row < this.grid.gridTableBody.rows.length && col < this.grid.gridTableBody.rows[row].cells.length) ? this.grid.gridTableBody.rows[row].cells[col]
+ : null;
+ if (!td) {
+ return;
+ }
+
+ if (this.selectedCell) {
+ this.selectedCell.removeClass('jxGridCellSelected');
+ }
+ this.selectedCell = td;
+ this.selectedCell.addClass('jxGridCellSelected');
},
-
- /**
- * Method: gridChanged
- * called through the grid listener interface when data has changed in the
- * underlying model
+ /**
+ * Method: selectRow
+ * Select a row and apply the jxGridRowSelected style to it.
*
* Parameters:
- * model - {Object} the model that changed
- * row - {Integer} the row that changed
- * col - {Integer} the column that changed
- * value - {Mixed} the new value
+ * row - {Integer} the row to select
*/
- gridChanged: function(model, row, col, value) {
- if (this.model == model) {
- this.gridObj.childNodes[row].childNodes[col].innerHTML = value;
+ selectRow : function (row) {
+ var tr = (row >= 0 && row < this.grid.gridTableBody.rows.length) ? this.grid.gridTableBody.rows[row]
+ : null;
+ if (this.selectedRow !== tr) {
+ if (this.selectedRow) {
+ this.selectedRow.removeClass('jxGridRowSelected');
+ }
+ this.selectedRow = tr;
+ this.selectedRow.addClass('jxGridRowSelected');
+ this.selectRowHeader(row);
}
},
-
/**
+ * Method: selectRowHeader
+ * Apply the jxGridRowHea}derSelected style to the row header cell of a
+ * selected row.
+ *
+ * Parameters:
+ * row - {Integer} the row header to select
+ */
+ selectRowHeader : function (row) {
+ if (!this.grid.row.useHeaders()) {
+ return;
+ }
+ var cell = (row >= 0 && row < this.grid.rowTableHead.rows.length) ? this.grid.rowTableHead.rows[row].cells[0]
+ : null;
+ if (!cell) {
+ return;
+ }
+ if (this.selectedRowHead !== cell) {
+ if (this.selectedRowHead) {
+ this.selectedRowHead
+ .removeClass('jxGridRowHeaderSelected');
+ }
+ this.selectedRowHead = cell;
+ cell.addClass('jxGridRowHeaderSelected');
+ }
+ },
+ /**
+ * Method: selectColumn
+ * Select a column.
+ * This deselects a previously selected column.
+ *
+ * Parameters:
+ * col - {Integer} the column to select
+ */
+ selectColumn : function (col) {
+ if (col >= 0 && col < this.grid.gridTable.rows[0].cells.length) {
+ if (col !== this.selectedCol) {
+ if ($defined(this.selectedCol)) {
+ for (var i = 0; i < this.grid.gridTable.rows.length; i++) {
+ this.grid.gridTable.rows[i].cells[this.selectedCol]
+ .removeClass('jxGridColumnSelected');
+ }
+ }
+ this.selectedCol = col;
+ for (i = 0; i < this.grid.gridTable.rows.length; i++) {
+ this.grid.gridTable.rows[i].cells[col]
+ .addClass('jxGridColumnSelected');
+ }
+ this.selectColumnHeader(col);
+ }
+ }
+ },
+ /**
+ * method: selectColumnHeader
+ * Apply the jxGridColumnHeaderSelected style to the column header cell of a
+ * selected column.
+ *
+ * Parameters:
+ * col - {Integer} the column header to select
+ */
+ selectColumnHeader : function (col) {
+ if (this.grid.colTableBody.rows.length === 0
+ || !this.grid.row.useHeaders()) {
+ return;
+ }
+
+ var cell = (col >= 0 && col < this.grid.colTableBody.rows[0].cells.length) ? this.grid.colTableBody.rows[0].cells[col]
+ : null;
+ if (cell === null) {
+ return;
+ }
+
+ if (this.selectedColHead !== cell) {
+ if (this.selectedColHead) {
+ this.selectedColHead
+ .removeClass('jxGridColumnHeaderSelected');
+ }
+ this.selectedColHead = cell;
+ cell.addClass('jxGridColumnHeaderSelected');
+ }
+ }
+});
+// $Id: $
+/**
+ * Class: Jx.Plugin.Prelighter
+ *
+ * Extends: <Jx.Plugin>
+ *
+ * Grid plugin to prelight rows, columns, and cells
+ *
+ * Inspired by the original code in Jx.Grid
+ *
+ * License:
+ * Original Copyright (c) 2008, DM Solutions Group Inc.
+ * This version Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Plugin.Grid.Prelighter = new Class({
+
+ Extends : Jx.Plugin,
+
+ options : {
+ /**
+ * Option: cell
+ * defaults to false. If set to true, the cell under the mouse is
+ * highlighted as the mouse moves.
+ */
+ cell : false,
+ /**
+ * Option: row
+ * defaults to false. If set to true, the row under the mouse is
+ * highlighted as the mouse moves.
+ */
+ row : false,
+ /**
+ * Option: column
+ * defaults to false. If set to true, the column under the mouse is
+ * highlighted as the mouse moves.
+ */
+ column : false,
+ /**
+ * Option: rowHeader
+ * defaults to false. If set to true, the row header of the row under
+ * the mouse is highlighted as the mouse moves.
+ */
+ rowHeader : false,
+ /**
+ * Option: columnHeader
+ * defaults to false. If set to true, the column header of the column
+ * under the mouse is highlighted as the mouse moves.
+ */
+ columnHeader : false
+ },
+ /**
+ * Property: bound
+ * storage for bound methods useful for working with events
+ */
+ bound: {},
+ /**
+ * APIMethod: init
+ * construct a new instance of the plugin. The plugin must be attached
+ * to a Jx.Grid instance to be useful though.
+ */
+ init: function() {
+ this.parent();
+ this.bound.prelight = this.prelight.bind(this);
+ },
+ /**
+ * APIMethod: attach
+ * Sets up the plugin and connects it to the grid
+ */
+ attach: function (grid) {
+ if (!$defined(grid) && !(grid instanceof Jx.Grid)) {
+ return;
+ }
+ this.grid = grid;
+ this.grid.addEvent('gridMove', this.bound.prelight);
+ },
+ /**
+ * APIMethod: detach
+ */
+ detach: function() {
+ if (this.grid) {
+ this.grid.removeEvent('gridMove', this.bound.prelight);
+ }
+ this.grid = null;
+ },
+ /**
+ * Method: prelight
+ * dispatches the event to the various prelight methods.
+ */
+ prelight : function (rc) {
+ if ($defined(rc) && rc.column !== -1 && rc.row !== -1) {
+
+ var row = rc.row;
+ if (this.grid.columns.useHeaders()) {
+ row--;
+ }
+ var column = rc.column;
+ if (this.grid.row.useHeaders()) {
+ column--;
+ }
+
+ if (this.options.cell) {
+ this.prelightCell(row, column);
+ }
+ if (this.options.row) {
+ this.prelightRow(row);
+ }
+ if (this.options.column) {
+ this.prelightColumn(column);
+ }
+ if (this.options.rowHeader) {
+ this.prelightRowHeader(row);
+ }
+ if (this.options.columnHeader) {
+ this.prelightColumnHeader(column);
+ }
+ }
+ },
+ /**
* Method: prelightRowHeader
* apply the jxGridRowHeaderPrelight style to the header cell of a row.
* This removes the style from the previously pre-lit row header.
@@ -15524,19 +26755,21 @@
* Parameters:
* row - {Integer} the row to pre-light the header cell of
*/
- prelightRowHeader: function(row) {
- var cell = (row >= 0 && row < this.rowTableHead.rows.length-1) ? this.rowTableHead.rows[row+1].cells[1] : null;
- if (this.prelitRowHeader != cell) {
+ prelightRowHeader : function (row) {
+ var cell = (row >= 0 && row < this.grid.rowTableHead.rows.length) ? this.grid.rowTableHead.rows[row].cells[0]
+ : null;
+ if (this.prelitRowHeader !== cell) {
if (this.prelitRowHeader) {
- this.prelitRowHeader.removeClass('jxGridRowHeaderPrelight');
+ this.prelitRowHeader
+ .removeClass('jxGridRowHeaderPrelight');
}
this.prelitRowHeader = cell;
if (this.prelitRowHeader) {
- this.prelitRowHeader.addClass('jxGridRowHeaderPrelight');
+ this.prelitRowHeader
+ .addClass('jxGridRowHeaderPrelight');
}
}
},
-
/**
* Method: prelightColumnHeader
* apply the jxGridColumnHeaderPrelight style to the header cell of a column.
@@ -15545,22 +26778,25 @@
* Parameters:
* col - {Integer} the column to pre-light the header cell of
*/
- prelightColumnHeader: function(col) {
- if (this.colTableBody.rows.length == 0) {
+ prelightColumnHeader : function (col) {
+ if (this.grid.colTableBody.rows.length === 0) {
return;
}
- var cell = (col >= 0 && col < this.colTableBody.rows[0].cells.length-1) ? this.colTableBody.rows[0].cells[col+1] : null;
- if (this.prelitColumnHeader != cell) {
+
+ var cell = (col >= 0 && col < this.grid.colTableBody.rows[0].cells.length) ? this.grid.colTableBody.rows[0].cells[col]
+ : null;
+ if (this.prelitColumnHeader !== cell) {
if (this.prelitColumnHeader) {
- this.prelitColumnHeader.removeClass('jxGridColumnHeaderPrelight');
+ this.prelitColumnHeader
+ .removeClass('jxGridColumnHeaderPrelight');
}
this.prelitColumnHeader = cell;
if (this.prelitColumnHeader) {
- this.prelitColumnHeader.addClass('jxGridColumnHeaderPrelight');
+ this.prelitColumnHeader
+ .addClass('jxGridColumnHeaderPrelight');
}
}
},
-
/**
* Method: prelightRow
* apply the jxGridRowPrelight style to row.
@@ -15569,10 +26805,11 @@
* Parameters:
* row - {Integer} the row to pre-light
*/
- prelightRow: function(row) {
- var tr = (row >= 0 && row < this.gridTableBody.rows.length-1) ? this.gridTableBody.rows[row+1] : null;
-
- if (this.prelitRow != row) {
+ prelightRow : function (row) {
+ var tr = (row >= 0 && row < this.grid.gridTableBody.rows.length) ? this.grid.gridTableBody.rows[row]
+ : null;
+
+ if (this.prelitRow !== row) {
if (this.prelitRow) {
this.prelitRow.removeClass('jxGridRowPrelight');
}
@@ -15583,7 +26820,6 @@
}
}
},
-
/**
* Method: prelightColumn
* apply the jxGridColumnPrelight style to a column.
@@ -15591,29 +26827,23 @@
*
* Parameters:
* col - {Integer} the column to pre-light
- *
- * TODO: Jx.Grid.prelightColumn
- * Not Yet Implemented.
*/
- prelightColumn: function(col) {
- /* TODO: Jx.Grid.prelightColumn
- * implement column prelighting (possibly)
- */
- if (col >= 0 && col < this.gridTable.rows[0].cells.length) {
+ prelightColumn : function (col) {
+ if (col >= 0 && col < this.grid.gridTable.rows[0].cells.length) {
if ($chk(this.prelitColumn)) {
- for (var i=0; i<this.gridTable.rows.length; i++) {
- this.gridTable.rows[i].cells[this.prelitColumn + 1].removeClass('jxGridColumnPrelight');
+ for (var i = 0; i < this.grid.gridTable.rows.length; i++) {
+ this.grid.gridTable.rows[i].cells[this.prelitColumn]
+ .removeClass('jxGridColumnPrelight');
}
}
this.prelitColumn = col;
- for (var i=0; i<this.gridTable.rows.length; i++) {
- this.gridTable.rows[i].cells[col + 1].addClass('jxGridColumnPrelight');
- }
+ for (i = 0; i < this.grid.gridTable.rows.length; i++) {
+ this.grid.gridTable.rows[i].cells[col]
+ .addClass('jxGridColumnPrelight');
+ }
+ this.prelightColumnHeader(col);
}
-
- this.prelightColumnHeader(col);
},
-
/**
* Method: prelightCell
* apply the jxGridCellPrelight style to a cell.
@@ -15623,9 +26853,11 @@
* row - {Integer} the row of the cell to pre-light
* col - {Integer} the column of the cell to pre-light
*/
- prelightCell: function(row, col) {
- var td = (row >=0 && col >=0 && row < this.gridTableBody.rows.length - 1 && col < this.gridTableBody.rows[row+1].cells.length - 1) ? this.gridTableBody.rows[row+1].cells[col+1] : null;
- if (this.prelitCell != td) {
+ prelightCell : function (row, col) {
+ var td = (row >= 0 && col >= 0
+ && row < this.grid.gridTableBody.rows.length && col < this.grid.gridTableBody.rows[row].cells.length) ? this.grid.gridTableBody.rows[row].cells[col]
+ : null;
+ if (this.prelitCell !== td) {
if (this.prelitCell) {
this.prelitCell.removeClass('jxGridCellPrelight');
}
@@ -15633,483 +26865,850 @@
if (this.prelitCell) {
this.prelitCell.addClass('jxGridCellPrelight');
}
- }
- },
-
- /**
- * Method: selectCell
- * Select a cell and apply the jxGridCellSelected style to it.
- * This deselects a previously selected cell.
- *
- * If the model supports cell selection, it should implement
- * a cellSelected function to receive notification of the selection.
- *
- * Parameters:
- * row - {Integer} the row of the cell to select
- * col - {Integer} the column of the cell to select
+ }
+ }
+});
+// $Id: $
+/**
+ * Class: Jx.Plugin.Sorter
+ *
+ * Extends: <Jx.Plugin>
+ *
+ * Grid plugin to sort the grid by a single column.
+ *
+ * Original selection code from Jx.Grid's original class
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Plugin.Grid.Sorter = new Class({
+
+ Extends : Jx.Plugin,
+
+ options : {},
+ /**
+ * Property: current
+ * refernce to the currently sorted column
*/
- selectCell: function(row, col) {
- var td = (row >=0 && col >=0 && row < this.gridTableBody.rows.length - 1 && col < this.gridTableBody.rows[row+1].cells.length - 1) ? this.gridTableBody.rows[row+1].cells[col+1] : null;
- if (!td) {
- return;
- }
-
- if (this.selectedCell) {
- this.selectedCell.removeClass('jxGridCellSelected');
- }
- this.selectedCell = td;
- this.selectedCell.addClass('jxGridCellSelected');
+ current : null,
+ /**
+ * Property: direction
+ * tell us what direction the sort is in (either 'asc' or 'desc')
+ */
+ direction : null,
+ /**
+ * Property: currentGridIndex
+ * Holds the index of the column in the grid
+ */
+ currentGridIndex : null,
+ /**
+ * Property: bound
+ * storage for bound methods useful for working with events
+ */
+ bound: {},
+ /**
+ * APIMethod: init
+ * construct a new instance of the plugin. The plugin must be attached
+ * to a Jx.Grid instance to be useful though.
+ */
+ init: function() {
+ this.parent();
+ this.bound.sort = this.sort.bind(this);
+ this.bound.addHeaderClass = this.addHeaderClass.bind(this);
},
-
- /**
- * Method: selectRowHeader
- * Apply the jxGridRowHeaderSelected style to the row header cell of a
- * selected row.
- *
- * Parameters:
- * row - {Integer} the row header to select
- * selected - {Boolean} the new state of the row header
+ /**
+ * APIMethod: attach
+ * Sets up the plugin and attaches the plugin to the grid events it
+ * will be monitoring
*/
- selectRowHeader: function(row, selected) {
- var cell = (row >= 0 && row < this.rowTableHead.rows.length-1) ? this.rowTableHead.rows[row+1].cells[1] : null;
- if (!cell) {
+ attach: function (grid) {
+ if (!$defined(grid) && !(grid instanceof Jx.Grid)) {
return;
}
- if (selected) {
- cell.addClass('jxGridRowHeaderSelected');
- } else {
- cell.removeClass('jxGridRowHeaderSelected');
+
+ this.grid = grid;
+
+ this.grid.addEvent('gridClick', this.bound.sort);
+ this.boundAddHeader = this.addHeaderClass.bind(this);
+ },
+ /**
+ * APIMethod: detach
+ */
+ detach: function() {
+ if (this.grid) {
+ this.grid.removeEvent('gridClick', this.bound.sort);
}
+ this.grid = null;
},
-
- /**
- * Method: selectRow
- * Select a row and apply the jxGridRowSelected style to it.
- *
- * If the model supports row selection, it should implement
- * a rowSelected function to receive notification of the selection.
- *
+ /**
+ * Method: sort
+ * called when a grid header is clicked.
+ *
* Parameters:
- * row - {Integer} the row to select
- * selected - {Boolean} the new state of the row
+ * rc - an object holding the row and column indexes for the clicked header
*/
- selectRow: function(row, selected) {
- var tr = (row >= 0 && row < this.gridTableBody.rows.length - 1) ? this.gridTableBody.rows[row+1] : null;
- if (tr) {
- if (selected) {
- tr.addClass('jxGridRowSelected');
- } else {
- tr.removeClass('jxGridRowSelected');
+ sort : function (rc) {
+ if ($defined(rc) && rc.column !== -1 && rc.row !== -1) {
+ //check to find the header
+ if (rc.row === 0) {
+ if (this.grid.row.useHeaders()) {
+ rc.column--;
+ }
+ var column = this.grid.columns.getByGridIndex(rc.column);
+ if (column.isSortable()) {
+ if (column === this.current) {
+ //reverse sort order
+ this.direction = (this.direction === 'asc') ? 'desc' : 'asc';
+ } else {
+ this.current = column;
+ this.direction = 'asc';
+ this.currentGridIndex = rc.column;
+ }
+
+ //The grid should be listening for the sortFinished event and will re-render the grid
+ //we will listen for the grid's doneCreateGrid event to add the header
+ this.grid.addEvent('doneCreateGrid', this.bound.addHeaderClass);
+ //sort the store
+ var model = this.grid.getModel();
+ model.sort(this.current.name, null, this.direction);
+ }
+
}
- this.selectRowHeader(row, selected);
}
},
+ /**
+ * Method: addHeaderClass
+ * Event listener that adds the proper sorted column class to the
+ * column we sorted by so that the sort arrow shows
+ */
+ addHeaderClass : function () {
+ this.grid.removeEvent('doneCreateGrid', this.bound.addHeaderClass);
+
+ //get header TD
+ var th = this.grid.colTable.rows[0].cells[this.currentGridIndex];
+ th.addClass('jxGridColumnSorted' + this.direction.capitalize());
+ }
+});
+// $Id: $
+/**
+ * Class: Jx.Plugin.Resize
+ *
+ * Extends: <Jx.Plugin>
+ *
+ * Grid plugin to enable dynamic resizing of column width and row height
+ *
+ *
+ * License:
+ * Copyright (c) 2009, DM Solutions Group.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Plugin.Grid.Resize = new Class({
+
+ Extends : Jx.Plugin,
- /**
- * method: selectColumnHeader
- * Apply the jxGridColumnHeaderSelected style to the column header cell of a
- * selected column.
- *
- * Parameters:
- * col - {Integer} the column header to select
- * selected - {Boolean} the new state of the column header
+ options: {
+ /**
+ * Option: columns
+ * set to true to make column widths resizeable
+ */
+ columns: false,
+ /**
+ * Option: rows
+ * set to true to make row heights resizeable
+ */
+ rows: false,
+ /**
+ * Option: tooltip
+ * the tooltip to display for the draggable portion of the
+ * cell header
+ */
+ tooltip: 'Drag to resize, double click to auto-size.'
+ },
+ /**
+ * Property: els
+ * the DOM elements by which the rows/columns are resized.
*/
- selectColumnHeader: function(col, selected) {
- if (this.colTableBody.rows.length == 0) {
+ els: [],
+
+ /**
+ * Property: drags
+ * the Drag instances
+ */
+ drags: [],
+
+ /**
+ * Property: bound
+ * storage for bound methods useful for working with events
+ */
+ bound: {},
+ /**
+ * APIMethod: init
+ * construct a new instance of the plugin. The plugin must be attached
+ * to a Jx.Grid instance to be useful though.
+ */
+ init: function() {
+ this.parent();
+ this.bound.createResizeHandles = this.createResizeHandles.bind(this);
+ this.bound.removeResizeHandles = this.removeResizeHandles.bind(this);
+ },
+ /**
+ * APIMethod: attach
+ * Sets up the plugin and connects it to the grid
+ */
+ attach: function (grid) {
+ if (!$defined(grid) && !(grid instanceof Jx.Grid)) {
return;
}
- var cell = (col >= 0 && col < this.colTableBody.rows[0].cells.length-1) ? this.colTableBody.rows[0].cells[col+1] : null;
- if (cell == null) {
- return;
+ this.grid = grid;
+ this.grid.addEvent('doneCreateGrid', this.bound.createResizeHandles);
+ this.grid.addEvent('beginCreateGrid', this.bound.removeResizeHandles);
+ this.createResizeHandles();
+ },
+ /**
+ * APIMethod: detach
+ */
+ detach: function() {
+ if (this.grid) {
+ this.grid.removeEvent('doneCreateGrid', this.bound.createResizeHandles);
+ this.grid.removeEvent('beginCreateGrid', this.bound.removeResizeHandles);
}
+ this.grid = null;
+ },
+
+ removeResizeHandles: function() {
+ this.els.each(function(el) { el.dispose(); } );
+ this.els = [];
+ this.drags.each(function(drag){ drag.detach(); });
+ this.drags = [];
+ },
+
+ createResizeHandles: function() {
+ if (this.options.columns && this.grid.columns.useHeaders()) {
+ this.grid.columns.columns.each(function(col, idx) {
+ if (col.header) {
+ var el = new Element('div', {
+ 'class':'jxGridColumnResize',
+ title: this.options.tooltip,
+ events: {
+ dblclick: function() {
+ col.options.width = 'auto';
+ col.setWidth(col.getWidth(true));
+ }
+ }
+ }).inject(col.header);
+ this.els.push(el);
+ this.drags.push(new Drag(el, {
+ limit: {y:[0,0]},
+ onDrag: function(el) {
+ var w = el.getPosition(el.parentNode).x.toInt();
+ col.setWidth(w);
+ }
+ }));
+ }
+ }, this);
+ }
- if (selected) {
- cell.addClass('jxGridColumnHeaderSelected');
+ if (this.options.rows && this.grid.row.useHeaders()) {
+
+ }
+ }
+});
+/**
+ * Namespace: Jx.Plugin.DataView
+ * The namespace for all dataview plugins
+ */
+Jx.Plugin.DataView = {};
+/**
+ * Class: Jx.Slide
+ * Hides and shows an element without depending on a fixed width or height
+ *
+ * Copyright 2009 by Jonathan Bomgardner
+ * License: MIT-style
+ */
+Jx.Slide = new Class({
+
+ Implements: Jx.Object,
+
+ options: {
+ /**
+ * Option: target
+ * The element to slide
+ */
+ target: null,
+ /**
+ * Option: trigger
+ * The element that will have a click event added to start the slide
+ */
+ trigger: null,
+ /**
+ * Option: type
+ * The type of slide. Can be either "width" or "height". defaults to "height"
+ */
+ type: 'height',
+ /**
+ * Option: setOpenTo
+ * Allows the caller to determine what the open target is set to. Defaults to 'auto'.
+ */
+ setOpenTo: 'auto',
+ /**
+ * Option: onSlideOut
+ * function called when the target is revealed.
+ */
+ onSlideOut: $empty,
+ /**
+ * Option: onSlideIn
+ * function called when a panel is hidden.
+ */
+ onSlideIn: $empty
+ },
+ /**
+ * APIMethod: init
+ * sets up the slide
+ */
+ init: function () {
+
+ this.target = $(this.options.target);
+
+ this.target.set('tween', {onComplete: this.setDisplay.bind(this)});
+
+ if ($defined(this.options.trigger)) {
+ this.trigger = $(this.options.trigger);
+ this.trigger.addEvent('click', this.handleClick.bindWithEvent(this));
+ }
+
+ this.target.store('slider', this);
+
+ },
+ /**
+ * APIMethod: handleClick
+ * event handler for clicks on the trigger. Starts the slide process
+ */
+ handleClick: function () {
+ var sizes = this.target.getMarginBoxSize();
+ if (sizes.height === 0) {
+ this.slide('in');
} else {
- cell.removeClass('jxGridColumnHeaderSelected');
+ this.slide('out');
}
},
-
- /**
- * Method: selectColumn
- * Select a column.
- * This deselects a previously selected column.
- *
+ /**
+ * Method: setDisplay
+ * called at the end of the animation to set the target's width or
+ * height as well as other css values to the appropriate values
+ */
+ setDisplay: function () {
+ var h = this.target.getStyle(this.options.type).toInt();
+ if (h === 0) {
+ this.target.setStyle('display', 'none');
+ this.fireEvent('slideOut', this.target);
+ } else {
+ //this.target.setStyle('overflow', 'auto');
+ if (this.target.getStyle('position') !== 'absolute') {
+ this.target.setStyle(this.options.type, this.options.setOpenTo);
+ }
+ this.fireEvent('slideIn', this.target);
+ }
+ },
+ /**
+ * APIMethod: slide
+ * Actually determines how to slide and initiates the animation.
+ *
* Parameters:
- * col - {Integer} the column to select
- * selected - {Boolean} the new state of the column
+ * dir - the direction to slide (either "in" or "out")
*/
- selectColumn: function(col, selected) {
- /* todo: implement column selection */
- if (col >= 0 && col < this.gridTable.rows[0].cells.length) {
- if (selected) {
- for (var i=0; i<this.gridTable.rows.length; i++) {
- this.gridTable.rows[i].cells[col + 1].addClass('jxGridColumnSelected');
- }
+ slide: function (dir) {
+ var h;
+ if (dir === 'in') {
+ h = this.target.retrieve(this.options.type);
+ this.target.setStyles({
+ 'overflow': 'hidden',
+ 'display': 'block'
+ });
+ this.target.setStyle(this.options.type, 0);
+ this.target.tween(this.options.type, h);
+ } else {
+ if (this.options.type === 'height') {
+ h = this.target.getMarginBoxSize().height;
} else {
- for (var i=0; i<this.gridTable.rows.length; i++) {
- this.gridTable.rows[i].cells[col + 1].removeClass('jxGridColumnSelected');
- }
+ h = this.target.getMarginBoxSize().width;
}
- this.selectColumnHeader(col, selected);
- }
+ this.target.store(this.options.type, h);
+ this.target.setStyle('overflow', 'hidden');
+ this.target.setStyle(this.options.type, h);
+ this.target.tween(this.options.type, 0);
+ }
+ }
+});
+/**
+ * Class: Jx.Plugin.DataView.GroupFolder
+ *
+ * Extends: <Jx.Plugin>
+ *
+ * Plugin for DataView - allows folding/unfolding of the groups in the
+ * grouped dataview
+ *
+ * License:
+ * Copyright (c) 2009, Jonathan Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Plugin.DataView.GroupFolder = new Class({
+
+ Extends: Jx.Plugin,
+
+ options: {
+ /**
+ * Option: headerClass
+ * The base for styling the header. Gets '-open' or '-closed' added
+ * to it.
+ */
+ headerClass: null
},
-
/**
- * Method: onMouseMoveGrid
- * handle the mouse moving over the main grid. This pre-lights the cell,
- * and subsquently the row and column (and headers).
- *
- * Parameters:
- * e - {Event} the browser event object
+ * Property: headerState
+ * Hash that holds the open/closed state of each header
*/
- onMouseMoveGrid: function(e) {
- var rc = this.getRowColumnFromEvent(e);
- if (this.options.cellPrelight) {
- this.prelightCell(rc.row, rc.column);
+ headerState: new Hash(),
+ /**
+ * APIMethod: attach
+ * Attaches this plugin to a dataview
+ */
+ attach: function (dataView) {
+ if (!$defined(dataView) && !(dataview instanceof Jx.Panel.DataView)) {
+ return;
}
- if (this.options.rowPrelight) {
- this.prelightRow(rc.row);
- }
- if (this.options.rowHeaderPrelight) {
- this.prelightRowHeader(rc.row);
- }
- if (this.options.columnPrelight) {
- this.prelightColumn(rc.column);
- }
- if (this.options.columnHeaderPrelight) {
- this.prelightColumnHeader(rc.column);
- }
+
+ this.dv = dataView;
+ this.dv.addEvent('renderDone', this.setHeaders.bind(this));
},
+ /**
+ * Method: setHeaders
+ * Called after the dataview is rendered. Sets up the Jx.Slide instance
+ * for each header. It also sets the initial state of each header so that
+ * if the dataview is redrawn for some reason the open/closed state is
+ * preserved.
+ */
+ setHeaders: function () {
+ var headers = this.dv.domA.getElements('.' + this.dv.options.groupHeaderClass);
+
+ headers.each(function (header) {
+ var id = header.get('id');
+ var s = new Jx.Slide({
+ target: header.getNext(),
+ trigger: id,
+ onSlideOut: this.onSlideOut.bind(this, header),
+ onSlideIn: this.onSlideIn.bind(this, header)
+ });
+
+ if (this.headerState.has(id)) {
+ var state = this.headerState.get(id);
+ if (state === 'open') {
+ s.slide('in');
+ } else {
+ s.slide('out');
+ }
+ } else {
+ s.slide('in');
+ }
+ }, this);
+ },
/**
- * Method: onMouseMoveRowHeader
- * handle the mouse moving over the row header cells. This pre-lights
- * the row and subsequently the row header.
- *
+ * Method: onSlideIn
+ * Called when a group opens.
+ *
* Parameters:
- * e - {Event} the browser event object
+ * header - the header that was clicked.
*/
- onMouseMoveRowHeader: function(e) {
- if (this.options.rowPrelight) {
- var rc = this.getRowColumnFromEvent(e);
- this.prelightRow(rc.row);
+ onSlideIn: function (header) {
+ this.headerState.set(header.get('id'), 'open');
+ if (header.hasClass(this.options.headerClass + '-closed')) {
+ header.removeClass(this.options.headerClass + '-closed');
}
+ header.addClass(this.options.headerClass + '-open');
},
-
/**
- * Method: onMouseMoveColumnHeader
- * handle the mouse moving over the column header cells. This pre-lights
- * the column and subsequently the column header.
- *
+ * Method: onSlideOut
+ * Called when a group closes.
+ *
* Parameters:
- * e - {Event} the browser event object
+ * header - the header that was clicked.
*/
- onMouseMoveColumnHeader: function(e) {
- if (this.options.columnPrelight) {
- var rc = this.getRowColumnFromEvent(e);
- this.prelightColumn(rc.column);
+ onSlideOut: function (header) {
+ this.headerState.set(header.get('id'), 'closed');
+ if (header.hasClass(this.options.headerClass + '-open')) {
+ header.removeClass(this.options.headerClass + '-open');
}
+ header.addClass(this.options.headerClass + '-closed');
+ }
+});
+// $Id: $
+/**
+ * Class: Jx.Plugin.Field
+ * Field plugin namespace
+ *
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Plugin.Field = {};// $Id: $
+/**
+ * Class: Jx.Plugin.Field.Validator
+ *
+ * Extends: <Jx.Plugin>
+ *
+ * Field plugin for enforcing validation when a field is not used in a form.
+ *
+ *
+ * License:
+ * Copyright (c) 2009, Jonathan Bomgardner.
+ * Parts inspired by mootools-more's Form.Validator class
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Plugin.Field.Validator = new Class({
+
+ Extends : Jx.Plugin,
+ name: 'Field.Validator',
+
+ options: {
+ /**
+ * Option: validators
+ * An array that contains either a string that names the predefined
+ * validator to use with its needed options or an object that defines
+ * the options of an InputValidator (also with needed options) defined
+ * like so:
+ *
+ * (code)
+ * {
+ * validatorClass: 'name:with options', //gets applied to the field
+ * validator: { //used to create the InputValidator
+ * name: 'validatorName',
+ * options: {
+ * errorMsg: 'error message',
+ * test: function(field,props){}
+ * }
+ * }
+ * }
+ * (end)
+ */
+ validators: [],
+ /**
+ * Option: validateOnBlur
+ * Determines whether the plugin will validate the field on blur.
+ * Defaults to true.
+ */
+ validateOnBlur: true,
+ /**
+ * Option: validateOnChange
+ * Determines whether the plugin will validate the field on change.
+ * Defaults to true.
+ */
+ validateOnChange: true
},
-
/**
- * Method: onClickGrid
- * handle the user clicking on the grid. This triggers an
- * event to the model (if a cellSelected function is provided).
- *
- * The following is an example of a function in the model that selects
- * a row when the cellSelected function is called and deselects any rows
- * that are currently selected.
- *
- * (code)
- * cellSelected: function(grid, row,col) {
- * if (this.selectedRow != null) {
- * grid.selectRow(this.selectedRow, false);
- * }
- * this.selectedRow = row;
- * grid.selectRow(row, true);
- * }
- *
- * Parameters:
- * e - {Event} the browser event object
+ * Property: valid
+ * tells whether this field passed validation or not.
*/
- onClickGrid: function(e) {
- var rc = this.getRowColumnFromEvent(e);
-
- if (this.options.cellSelection && this.model.cellSelected) {
- this.model.cellSelected(this, rc.row, rc.column);
+ valid: null,
+ /**
+ * Property: errors
+ * array of errors found on this field
+ */
+ errors: [],
+ /**
+ * Property: bound
+ * storage for bound methods useful for working with events
+ */
+ bound: {},
+ /**
+ * APIMethod: init
+ * construct a new instance of the plugin. The plugin must be attached
+ * to a Jx.Grid instance to be useful though.
+ */
+ init: function () {
+ this.parent();
+ this.bound.validate = this.validate.bind(this);
+ this.bound.reset = this.reset.bind(this);
+ },
+ /**
+ * APIMethod: attach
+ * Sets up the plugin and connects it to the field
+ */
+ attach: function (field) {
+ if (!$defined(field) && !(field instanceof Jx.Field)) {
+ return;
}
- if (this.options.rowSelection && this.model.rowSelected) {
- this.model.rowSelected(this, rc.row);
+ this.field = field;
+ if (this.field.options.required && !this.options.validators.contains('required')) {
+ //would have used unshift() but reading tells me it may not work in IE.
+ this.options.validators.reverse().push('required');
+ this.options.validators.reverse();
}
- if (this.options.columnSelection && this.model.columnSelected) {
- this.model.columnSelected(this, rc.column);
+ //add validation classes
+ this.options.validators.each(function (v) {
+ var t = Jx.type(v);
+ if (t === 'string') {
+ this.field.field.addClass(v);
+ } else if (t === 'object') {
+ this.validators.add(v.validator.name, new InputValidator(v.validator.name, v.validator.options));
+ this.field.field.addClass(v.validatorClass);
+ }
+ }, this);
+ if (this.options.validateOnBlur) {
+ this.field.field.addEvent('blur', this.bound.validate);
}
-
+ if (this.options.validateOnChange) {
+ this.field.field.addEvent('change', this.bound.validate);
+ }
+ this.field.addEvent('reset', this.bound.reset);
},
-
/**
- * Method: onClickRowHeader
- * handle the user clicking on the row header. This triggers an
- * event to the model (if a rowSelected function is provided) which
- * can then select the row if desired.
- *
- * The following is an example of a function in the model that selects
- * a row when the rowSelected function is called and deselects any rows
- * that are currently selected. More complex code could be written to
- * allow the user to select multiple rows.
- *
- * (code)
- * rowSelected: function(grid, row) {
- * if (this.selectedRow != null) {
- * grid.selectRow(this.selectedRow, false);
- * }
- * this.selectedRow = row;
- * grid.selectRow(row, true);
- * }
- * (end)
- *
- * Parameters:
- * e - {Event} the browser event object
+ * APIMethod: detach
*/
- onClickRowHeader: function(e) {
- var rc = this.getRowColumnFromEvent(e);
-
- if (this.options.rowSelection && this.model.rowSelected) {
- this.model.rowSelected(this, rc.row);
+ detach: function () {
+ if (this.field) {
+ this.field.field.removeEvent('blur', this.bound.validate);
+ this.field.field.removeEvent('change', this.bound.validate);
}
+ this.field.removeEvent('reset', this.bound.reset);
+ this.field = null;
+ this.validators = null;
},
- /**
- * Method: onClickColumnHeader
- * handle the user clicking on the column header. This triggers column
- * selection and column (and header) styling changes and an
- * event to the model (if a columnSelected function is provided)
- *
- * The following is an example of a function in the model that selects
- * a column when the columnSelected function is called and deselects any
- * columns that are currently selected. More complex code could be written
- * to allow the user to select multiple columns.
- *
- * (code)
- * colSelected: function(grid, col) {
- * if (this.selectedColumn != null) {
- * grid.selectColumn(this.selectedColumn, false);
- * }
- * this.selectedColumn = col;
- * grid.selectColumn(col, true);
- * }
- * (end)
- *
- * Parameters:
- * e - {Event} the browser event object
- */
- onClickColumnHeader: function(e) {
- var rc = this.getRowColumnFromEvent(e);
-
- if (this.options.columnSelection && this.model.columnSelected) {
- this.model.columnSelected(this, rc.column);
+ validate: function () {
+ $clear(this.timer);
+ this.timer = this.validateField.delay(50, this);
+ },
+
+ validateField: function () {
+ //loop through the validators
+ this.valid = true;
+ this.errors = [];
+ this.options.validators.each(function (v) {
+ var val = (Jx.type(v) === 'string') ? Form.Validator.getValidator(v) : this.validators.get(v.validator.name);
+ if (val) {
+ if (!val.test(this.field.field)) {
+ this.valid = false;
+ this.errors.push(val.getError(this.field.field));
+ }
+ }
+ }, this);
+ if (!this.valid) {
+ this.field.domObj.removeClass('jxFieldValidated').addClass('jxFieldInvalid');
+ this.fireEvent('fieldValidationFailed', [this.field, this]);
+ } else {
+ this.field.domObj.removeClass('jxFieldInvalid').addClass('jxFieldValidated');
+ this.fireEvent('fieldValidationPassed', [this.field, this]);
}
+ return this.valid;
},
+ isValid: function () {
+ return this.validateField();
+ },
+
+ reset: function () {
+ this.valid = null;
+ this.errors = [];
+ this.field.field.removeClass('jxFieldInvalid').removeClass('jxFieldValidated');
+ },
/**
- * method: getRowColumnFromEvent
- * retrieve the row and column indexes from an event click.
- * This function is used by the grid, row header and column
- * header to safely get these numbers.
- *
- * If the event isn't valid (i.e. it wasn't on a TD or TH) then
- * the returned values will be -1, -1
- *
- * Parameters:
- * e - {Event} the browser event object
- *
- * @return Object an object with two properties, row and column,
- * that contain the row and column that was clicked
+ * APIMethod: getErrors
+ * USe this method to retrieve all of the errors noted for this field.
*/
- getRowColumnFromEvent: function(e) {
- var td = e.target;
- if (td.tagName != 'TD' && td.tagName != 'TH') {
- return {row:-1,column:-1};
- }
- var tr = td.parentNode;
- var col = td.cellIndex - 1; /* because of hidden spacer column */
- var row = tr.rowIndex - 1; /* because of hidden spacer row */
-
- if (col == -1) {
- /* bug in safari returns 0 for cellIndex - only choice seems
- * to be to loop through the row
- */
- for (var i=0; i<tr.childNodes.length; i++) {
- if (tr.childNodes[i] == td) {
- col = i - 1;
- break;
- }
- }
- }
- return {row:row,column:col};
+ getErrors: function () {
+ return this.errors;
}
-});/**
- * Class: Jx.Grid.Model
+
+
+});
+// $Id: $
+/**
+ * Class: Jx.Plugin.Form
+ * Form plugin namespace
+ *
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Plugin.Form = {};// $Id: $
+/**
+ * Class: Jx.Plugin.Form.Validator
+ *
+ * Extends: <Jx.Plugin>
+ *
+ * Form plugin for enforcing validation on the fields in a form.
*
- * Extends: Object
- *
- * Implements: Options, Events
- *
- * A Jx.Grid.Model is the source of data for a <Jx.Grid> instance. The
- * default implementation of the grid model works with two-dimensional
- * arrays of data and acts as a convenient base class for custom models
- * based on other sources of data.
- *
* License:
- * Copyright (c) 2008, DM Solutions Group Inc.
+ * Copyright (c) 2009, Jonathan Bomgardner.
+ * Parts inspired by mootools-more's Form.Validator class
*
* This file is licensed under an MIT style license
*/
-Jx.Grid.Model = new Class({
- Family: 'Jx.Grid.Model',
- Implements: [Events, Options],
+Jx.Plugin.Form.Validator = new Class({
+
+ Extends : Jx.Plugin,
+ name: 'Form.Validator',
+
options: {
- /* Option: colHeaderHeight
- * default 28, the height of the column header row
+ /**
+ * Option: fields
+ * This will be key/value pairs for each of the fields as shown here:
+ * {
+ * fieldID: {
+ * ... options for Field.Validator plugin ...
+ * },
+ * fieldID: {...
+ * }
+ * }
*/
- colHeaderHeight: 28,
- /* Option: colHeaderHeight
- * default 28, the height of the column header row
- */
- rowHeaderWidth: 28,
- /* Option: rowHeaderWidth
- * default 28, the width of the row header column.
- */
- colWidth: 50,
- /* Option: colWidth
- * default 50, the width of columns
- */
- rowHeight: 20,
- /* Option: rowHeight
- * default 20, the height of rows
- */
- rowHeaders: null,
- /* Option: columnHeaders
- * optional column headers, defaults to null
- */
- columnHeaders: null
+ fields: null,
+
+ fieldDefaults: {
+ validateOnBlur: true,
+ validateOnChange: true
+ },
+
+ validateOnSubmit: true,
+
+ suspendSubmit: false
},
- data: null,
/**
- * Constructor: Jx.Grid.Model
- * create a new grid model
- *
- * Parameters:
- * data - array of data to display in the grid
- * options - <Jx.Grid.Model.Options>
+ * Property: errorMessagess
+ * element holding
*/
- initialize: function(data, options) {
- this.data = data || [];
- this.setOptions(options);
+ errorMessage: null,
+ /**
+ * Property: bound
+ * storage for bound methods useful for working with events
+ */
+ bound: {},
+ /**
+ * APIMethod: init
+ * construct a new instance of the plugin. The plugin must be attached
+ * to a Jx.Grid instance to be useful though.
+ */
+ init: function() {
+ this.parent();
+ this.bound.validate = this.validate.bind(this);
+ this.bound.failed = this.fieldFailed.bind(this);
+ this.bound.passed = this.fieldPassed.bind(this);
},
- /**
- * Method: getColumnCount
- * This function returns the number of columns of data in the
- * model as an integer value.
- */
- getColumnCount: function() { return (this.data && this.data[0]) ? this.data[0].length : 0; },
- /* Method: getColumnHeaderHTML
- * This function returns an HTML string to be placed in the
- * column header for the given column index.
- */
- getColumnHeaderHTML: function(col) {
- return this.options.columnHeaders?this.options.columnHeaders[col]:col+1;
- },
- /* Method: getColumnHeaderHeight
- * This function returns an integer which is the height of the
- * column header row in pixels.
- */
- getColumnHeaderHeight: function() { return this.options.colHeaderHeight; },
- /* Method: getColumnWidth
- * This function returns an integer which is the width of the
- * given column in pixels.
- */
- getColumnWidth: function(col) { return this.options.colWidth; },
- /* Method: getRowHeaderHTML
- * This function returns an HTML string to be placed in the row
- * header for the given row index
- */
- getRowHeaderHTML: function(row) {
- return this.options.rowHeaders?this.options.rowHeaders[row]:row+1;
+ /**
+ * APIMethod: attach
+ * Sets up the plugin and connects it to the form
+ */
+ attach: function (form) {
+ if (!$defined(form) && !(form instanceof Jx.Form)) {
+ return;
+ }
+ this.form = form;
+ var plugin = this;
+ //override the isValid function in the form
+ this.form.isValid = function () {
+ return plugin.isValid();
+ };
+
+ if (this.options.validateOnSubmit && !this.options.suspendSubmit) {
+ document.id(this.form).addEvent('submit', this.bound.validate);
+ } else if (this.options.suspendSubmit) {
+ document.id(this.form).addEvent('submit', function (ev) {
+ ev.stop();
+ });
+ }
+
+ this.plugins = $H();
+
+ //setup the fields
+ $H(this.options.fields).each(function (val, key) {
+ var opts = $merge(this.options.fieldDefaults, val);
+ var field = document.id(key).retrieve('field');
+ var plugin = new Jx.Plugin.Field.Validator(opts);
+ this.plugins.set(key, plugin);
+ plugin.attach(field);
+ plugin.addEvent('fieldValidationFailed', this.bound.failed);
+ plugin.addEvent('fieldValidationPassed', this.bound.passed);
+
+ }, this);
+
},
- /* Method: getRowHeaderWidth
- * This function returns an integer which is the width of the row
- * header column in pixels.
- */
- getRowHeaderWidth: function() { return this.options.rowHeaderWidth; },
- /* Method: getRowHeight
- * This function returns an integer which is the height of the
- * given row in pixels.
- */
- getRowHeight: function(row) { return this.options.rowHeight; },
- /* Method: getRowCount
- * This function returns the number of rows of data in the model
- * as an integer value.
- */
- getRowCount: function() { return this.data.length },
- /* Method: getValueAt
- * This function returns an HTML string which is the text to place
- * in the cell at the given row and column.
- */
- getValueAt: function(row, col) { return (this.data && $chk(this.data[row])) ? this.data[row][col] : ''; },
- /* Method: setColumnWidth
- * This function is called with a column index and width in pixels
- * when a column is resized. This function is only required if the grid
- * allows resizeable columns.
+ /**
+ * APIMethod: detach
*/
- setColumnWidth: function() {},
- /* Method: isCellEditable
- * This function returns a boolean value to indicate if a given
- * cell is editable by the user.
- */
- isCellEditable: function() { return false },
- /* Method: setValueAt
- * This function is called with the row and column of a cell and a
- * new value for the cell. It is mandatory to provide this function if any of
- * the cells in the model are editable.
- */
- setValueAt: function(row, col, value) {},
- /* Method: rowSelected
- * This function is called by the grid to indicate that the user
- * has selected a row by clicking on the row header.
- */
- rowSelected: function(grid, row) {
- if (this.selectedRow != null) {
- grid.selectRow(this.selectedRow, false);
+ detach: function() {
+ if (this.form) {
+ document.id(this.form).removeEvent('submit');
}
- this.selectedRow = row;
- grid.selectRow(row, true);
- this.fireEvent('select-row', row);
+ this.form = null;
+ this.plugins.each(function(plugin){
+ plugin.detach();
+ plugin = null;
+ },this);
+ this.plugins = null;
},
- /* Method: columnSelected
- * This function is called by the grid to indicate that the user
- * has selected a column by clicking on the column header.
- */
- columnSelected: function(grid, col) {
- if (this.selectedCol != null) {
- grid.selectColumn(this.selectedCol, false);
+ /**
+ * APIMethod: isValid
+ * Call this to determine whether the form validates.
+ */
+ isValid: function () {
+ return this.validate();
+ },
+ /**
+ * Method: validate
+ * Method that actually does the work of validating the fields in the form.
+ */
+ validate: function () {
+ var valid = true;
+ this.errors = $H();
+ this.plugins.each(function(plugin){
+ if (!plugin.isValid()) {
+ valid = false;
+ this.errors.set(plugin.field.id,plugin.getErrors());
+ }
+ }, this);
+ if (valid) {
+ this.fireEvent('formValidationPassed', [this.form, this]);
+ } else {
+ this.fireEvent('formValidationFailed', [this.form, this]);
}
- this.selectedCol = col;
- grid.selectColumn(col, true);
- this.fireEvent('select-column', col);
+ return valid;
},
- /* Method: cellSelected
- * This function is called by the grid to indicate that the user
- * has selected a cell by clicking on the cell in the grid.
+ /**
+ * Method: fieldFailed
+ * Refires the fieldValidationFailed event from the field validators it contains
*/
- cellSelected: function(grid, row,col) {
- grid.selectCell(row, col);
- this.fireEvent('select-cell', [row, col]);
+ fieldFailed: function (field, validator) {
+ this.fireEvent('fieldValidationFailed', [field, validator]);
+ },
+ /**
+ * Method: fielPassed
+ * Refires the fieldValidationPassed event from the field validators it contains
+ */
+ fieldPassed: function (field, validator) {
+ this.fireEvent('fieldValidationPassed', [field, validator]);
+ },
+ /**
+ * APIMethod: getErrors
+ * Use this method to get all of the errors from all of the fields.
+ */
+ getErrors: function () {
+ if (!$defined(this.errors)) {
+ this.validate();
+ }
+ return this.errors;
+ }
- }
+
});
-// $Id: context.js 424 2009-05-12 12:51:44Z pagameba $
+// $Id: context.js 524 2009-09-18 05:40:16Z jonlb at comcast.net $
/**
* Class: Jx.Menu.Context
*
@@ -16136,21 +27735,19 @@
* <Jx.Menu>
*/
Extends: Jx.Menu,
+
+ parameters: ['id'],
+
/**
- * Constructor: Jx.ContextMenu
+ * APIMethod: render
* create a new context menu
- *
- * Parameters:
- * id - {HTMLElement} element or id to make this the context menu
- * for. The menu hooks the oncontextmenu event of the element
- * and shows itself at the mouse position where the right-click
- * happened.
*/
- initialize : function(id) {
+ render: function() {
+ this.id = document.id(this.options.id);
+ if (this.id) {
+ this.id.addEvent('contextmenu', this.show.bindWithEvent(this));
+ }
this.parent();
- if ($(id)) {
- $(id).addEvent('contextmenu', this.show.bindWithEvent(this));
- }
},
/**
* Method: show
@@ -16160,13 +27757,13 @@
* e - {Event} the mouse event
*/
show : function(e) {
- if (this.items.length ==0) {
+ if (this.list.count() ==0) {
return;
}
this.contentContainer.setStyle('visibility','hidden');
this.contentContainer.setStyle('display','block');
- $(document.body).adopt(this.contentContainer);
+ document.id(document.body).adopt(this.contentContainer);
/* we have to size the container for IE to render the chrome correctly
* but just in the menu/sub menu case - there is some horrible peekaboo
* bug in IE related to ULs that we just couldn't figure out
@@ -16187,11 +27784,11 @@
e.stop();
}
-});// $Id: menu.separator.js 424 2009-05-12 12:51:44Z pagameba $
+});// $Id: menu.separator.js 524 2009-09-18 05:40:16Z jonlb at comcast.net $
/**
* Class: Jx.Menu.Separator
*
- * Extends: Object
+ * Extends: <Jx.Object>
*
* A convenience class to create a visual separator in a menu.
*
@@ -16206,6 +27803,7 @@
*/
Jx.Menu.Separator = new Class({
Family: 'Jx.Menu.Separator',
+ Extends: Jx.Object,
/**
* Property: domObj
* {HTMLElement} the HTML element that the separator is contained
@@ -16217,14 +27815,18 @@
* {<Jx.Menu>, <Jx.Menu.SubMenu>} the menu that the separator is in.
*/
owner: null,
+ options: {
+ template: "<li class='jxMenuItem'><span class='jxMenuSeparator'></span></li>"
+ },
+ classes: ['jxMenuItem'],
/**
- * Constructor: Jx.Menu.Separator
+ * APIMethod: render
* Create a new instance of a menu separator
*/
- initialize: function() {
- this.domObj = new Element('li',{'class':'jxMenuItem'});
- var span = new Element('span', {'class':'jxMenuSeparator','html':' '});
- this.domObj.appendChild(span);
+ render: function() {
+ this.parent();
+ this.elements = this.processTemplate(this.options.template, this.classes);
+ this.domObj = this.elements.get('jxMenuItem');
},
/**
* Method: setOwner
@@ -16246,7 +27848,7 @@
* Show the menu item
*/
show: $empty
-});// $Id: submenu.js 424 2009-05-12 12:51:44Z pagameba $
+});// $Id: submenu.js 524 2009-09-18 05:40:16Z jonlb at comcast.net $
/**
* Class: Jx.Menu.SubMenu
*
@@ -16272,7 +27874,6 @@
Jx.Menu.SubMenu = new Class({
Family: 'Jx.Menu.SubMenu',
Extends: Jx.Menu.Item,
- Implements: [Jx.AutoPosition, Jx.Chrome],
/**
* Property: subDomObj
* {HTMLElement} the HTML container for the sub menu.
@@ -16290,30 +27891,32 @@
*/
visibleItem: null,
/**
- * Property: items
- * {Array} the menu items that are in this sub menu.
+ * Property: list
+ * {<Jx.List>} a list to manage menu items
*/
- items: null,
+ list: null,
+ options: {
+ template: '<li class="jxMenuItemContainer"><a class="jxMenuItem jxButtonSubMenu"><span class="jxMenuItemContent"><img class="jxMenuItemIcon" src="'+Jx.aPixel.src+'"><span class="jxMenuItemLabel"></span></span></a></li>',
+ position: {
+ horizontal: ['right left', 'left right'],
+ vertical: ['top top']
+ }
+ },
+
+ classes: ['jxMenuItemContainer', 'jxMenuItem','jxMenuItemIcon','jxMenuItemLabel'],
+
/**
- * Constructor: Jx.SubMenu
+ * APIMethod: render
* Create a new instance of Jx.SubMenu
- *
- * Parameters:
- * options - see <Jx.Button.Options>
*/
- initialize: function(options) {
+ render: function() {
+ this.parent();
this.open = false;
- this.items = [];
- this.parent(options);
- this.domA.addClass('jxButtonSubMenu');
- this.contentContainer = new Element('div', {
- 'class': 'jxMenuContainer'
+ this.menu = new Jx.Menu(null, {
+ position: this.options.position
});
- this.subDomObj = new Element('ul', {
- 'class':'jxSubMenu'
- });
- this.contentContainer.adopt(this.subDomObj);
+ this.menu.domObj = this.domObj;
},
/**
* Method: setOwner
@@ -16330,29 +27933,11 @@
* Show the sub menu
*/
show: function() {
- if (this.open || this.items.length == 0) {
+ if (this.open || this.menu.list.count() == 0) {
return;
}
-
- this.contentContainer.setStyle('visibility','hidden');
- this.contentContainer.setStyle('display','block');
- $(document.body).adopt(this.contentContainer);
- /* we have to size the container for IE to render the chrome correctly
- * but just in the menu/sub menu case - there is some horrible peekaboo
- * bug in IE related to ULs that we just couldn't figure out
- */
- this.contentContainer.setContentBoxSize(this.subDomObj.getMarginBoxSize());
- this.showChrome(this.contentContainer);
-
- this.position(this.contentContainer, this.domObj, {
- horizontal: ['right left', 'left right'],
- vertical: ['top top'],
- offsets: this.chromeOffsets
- });
-
+ this.menu.show();
this.open = true;
- this.contentContainer.setStyle('visibility','');
-
this.setActive(true);
},
@@ -16362,14 +27947,9 @@
this.visibleItem.eventInMenu(e)) {
return true;
}
- return $(e.target).descendantOf(this.domObj) ||
- $(e.target).descendantOf(this.subDomObj) ||
- this.items.some(
- function(item) {
- return item instanceof Jx.Menu.SubMenu &&
- item.eventInMenu(e);
- }
- );
+ return document.id(e.target).descendantOf(this.domObj) ||
+ this.menu.eventInMenu(e);
+ document.id(e.target).descendantOf(this.menusubDomObj);
},
/**
@@ -16381,8 +27961,7 @@
return;
}
this.open = false;
- this.items.each(function(item){item.hide();});
- this.contentContainer.setStyle('display','none');
+ this.menu.hide();
this.visibleItem = null;
},
/**
@@ -16393,52 +27972,32 @@
* item - {<Jx.MenuItem>} the menu item to add. Multiple menu items
* can be added by passing multiple arguments to this function.
*/
- add : function() { /* menu */
- var that = this;
- $A(arguments).each(function(item){
- that.items.push(item);
- item.setOwner(that);
- that.subDomObj.adopt(item.domObj);
- });
+ add: function(item, position) {
+ this.menu.add(item, position);
return this;
},
/**
- * Method: insertBefore
- * Insert a menu item before another menu item.
+ * Method: remove
+ * Remove a menu item from the menu
*
* Parameters:
- * newItem - {<Jx.MenuItem>} the menu item to insert
- * targetItem - {<Jx.MenuItem>} the menu item to insert before
+ * item - {<Jx.MenuItem>} the menu item to remove
*/
- insertBefore: function(newItem, targetItem) {
- var bInserted = false;
- for (var i=0; i<this.items.length; i++) {
- if (this.items[i] == targetItem) {
- this.items.splice(i, 0, newItem);
- this.subDomObj.insertBefore(newItem.domObj, targetItem.domObj);
- bInserted = true;
- break;
- }
- }
- if (!bInserted) {
- this.add(newItem);
- }
+ remove: function(item) {
+ this.menu.remove(item);
+ return this;
},
/**
- * Method: remove
- * Remove a single menu item from the menu.
+ * Method: replace
+ * Replace a menu item with another menu item
*
* Parameters:
- * item - {<Jx.MenuItem} the menu item to remove.
+ * what - {<Jx.MenuItem>} the menu item to replace
+ * withWhat - {<Jx.MenuItem>} the menu item to replace it with
*/
- remove: function(item) {
- for (var i=0; i<this.items.length; i++) {
- if (this.items[i] == item) {
- this.items.splice(i,1);
- this.subDomObj.removeChild(item.domObj);
- break;
- }
- }
+ replace: function(item, withItem) {
+ this.menu.replace(item, withItem);
+ return this;
},
/**
* Method: deactivate
@@ -16497,11 +28056,11 @@
this.visibleItem.show();
}
}
-});// $Id: snap.js 484 2009-07-17 20:06:27Z pagameba $
+});// $Id: snap.js 524 2009-09-18 05:40:16Z jonlb at comcast.net $
/**
* Class: Jx.Splitter.Snap
*
- * Extends: Object
+ * Extends: <Jx.Object>
*
* A helper class to create an element that can snap a split panel open or
* closed.
@@ -16517,6 +28076,7 @@
*/
Jx.Splitter.Snap = new Class({
Family: 'Jx.Splitter.Snap',
+ Extends: Jx.Object,
/**
* Property: snap
* {HTMLElement} the DOM element of the snap (the thing that gets
@@ -16540,22 +28100,26 @@
*/
layout: 'vertical',
/**
- * Constructor: Jx.Splitter.Snap
- * Create a new Jx.Splitter.Snap
- *
* Parameters:
* snap - {HTMLElement} the clickable thing that snaps the element
* open and closed
* element - {HTMLElement} the element that gets controlled by the snap
* splitter - {<Jx.Splitter>} the splitter that this all happens inside of.
*/
- initialize: function(snap, element, splitter, events) {
- this.snap = snap;
- this.element = element;
- var jxl = element.retrieve('jxLayout');
+ parameters: ['snap','element','splitter','events'],
+
+ /**
+ * APIMethod: init
+ * Create a new Jx.Splitter.Snap
+ */
+ init: function() {
+ this.snap = this.options.snap;
+ this.element = this.options.element;
+ this.splitter = this.options.splitter;
+ this.events = this.options.events;
+ var jxl = this.element.retrieve('jxLayout');
jxl.addEvent('sizeChange', this.sizeChange.bind(this));
- this.splitter = splitter;
- this.layout = splitter.options.layout;
+ this.layout = this.splitter.options.layout;
var jxo = jxl.options;
var size = this.element.getContentBoxSize();
if (this.layout == 'vertical') {
@@ -16565,8 +28129,8 @@
this.originalSize = size.width;
this.minimumSize = jxo.minWidth ? jxo.minWidth : 0;
}
- events.each(function(eventName) {
- snap.addEvent(eventName, this.toggleElement.bind(this));
+ this.events.each(function(eventName) {
+ this.snap.addEvent(eventName, this.toggleElement.bind(this));
}, this);
},
@@ -16575,19 +28139,20 @@
* Snap the element open or closed.
*/
toggleElement: function() {
+ var size = this.element.getContentBoxSize();
var newSize = {};
if (this.layout == 'vertical') {
- if (this.element.clientHeight <= this.minimumSize) {
+ if (size.height == this.minimumSize) {
newSize.height = this.originalSize;
} else {
- this.originalSize = this.element.clientHeight;
+ this.originalSize = size.height;
newSize.height = this.minimumSize;
}
} else {
- if (this.element.clientWidth <= this.minimumSize) {
+ if (size.width == this.minimumSize) {
newSize.width = this.originalSize;
} else {
- this.originalSize = this.element.clientWidth;
+ this.originalSize = size.width;
newSize.width = this.minimumSize;
}
}
@@ -16603,7 +28168,7 @@
sizeChange: function() {
var size = this.element.getContentBoxSize();
if (this.layout == 'vertical') {
- if (this.element.clientHeight == this.minimumSize) {
+ if (size.height == this.minimumSize) {
this.snap.addClass('jxSnapClosed');
this.snap.removeClass('jxSnapOpened');
} else {
@@ -16611,7 +28176,7 @@
this.snap.removeClass('jxSnapClosed');
}
} else {
- if (this.element.clientWidth == this.minimumSize) {
+ if (size.width == this.minimumSize) {
this.snap.addClass('jxSnapClosed');
this.snap.removeClass('jxSnapOpened');
} else {
@@ -16620,269 +28185,12 @@
}
}
}
-});// $Id: toolbar.js 451 2009-05-31 21:21:30Z pagameba $
+});// $Id: tabset.js 524 2009-09-18 05:40:16Z jonlb at comcast.net $
/**
- * Class: Jx.Toolbar
- *
- * Extends: Object
- *
- * Implements: Options, Events
- *
- * A toolbar is a container object that contains other objects such as
- * buttons. The toolbar organizes the objects it contains automatically,
- * wrapping them as necessary. Multiple toolbars may be placed within
- * the same containing object.
- *
- * Jx.Toolbar includes CSS classes for styling the appearance of a
- * toolbar to be similar to traditional desktop application toolbars.
- *
- * There is one special object, Jx.ToolbarSeparator, that provides
- * a visual separation between objects in a toolbar.
- *
- * While a toolbar is generally a *dumb* container, it serves a special
- * purpose for menus by providing some infrastructure so that menus can behave
- * properly.
- *
- * In general, almost anything can be placed in a Toolbar, and mixed with
- * anything else.
- *
- * Example:
- * The following example shows how to create a Jx.Toolbar instance and place
- * two objects in it.
- *
- * (code)
- * //myToolbarContainer is the id of a <div> in the HTML page.
- * function myFunction() {}
- * var myToolbar = new Jx.Toolbar('myToolbarContainer');
- *
- * var myButton = new Jx.Button(buttonOptions);
- *
- * var myElement = document.createElement('select');
- *
- * myToolbar.add(myButton, new Jx.ToolbarSeparator(), myElement);
- * (end)
- *
- * Events:
- * add - fired when one or more buttons are added to a toolbar
- * remove - fired when on eor more buttons are removed from a toolbar
- *
- * Implements:
- * Options
- *
- * License:
- * Copyright (c) 2008, DM Solutions Group Inc.
- *
- * This file is licensed under an MIT style license
- */
-Jx.Toolbar = new Class({
- Family: 'Jx.Toolbar',
- Implements: [Options,Events],
- /**
- * Property: items
- * {Array} an array of the things in the toolbar.
- */
- items : null,
- /**
- * Property: domObj
- * {HTMLElement} the HTML element that the toolbar lives in
- */
- domObj : null,
- /**
- * Property: isActive
- * When a toolbar contains <Jx.Menu> instances, they want to know
- * if any menu in the toolbar is active and this is how they
- * find out.
- */
- isActive : false,
- options: {
- type: 'Toolbar',
- /* Option: position
- * the position of this toolbar in the container. The position
- * affects some items in the toolbar, such as menus and flyouts, which
- * need to open in a manner sensitive to the position. May be one of
- * 'top', 'right', 'bottom' or 'left'. Default is 'top'.
- */
- position: 'top',
- /* Option: parent
- * a DOM element to add this toolbar to
- */
- parent: null,
- /* Option: autoSize
- * if true, the toolbar will attempt to set its size based on the
- * things it contains. Default is false.
- */
- autoSize: false,
- /* Option: scroll
- * if true, the toolbar may scroll if the contents are wider than
- * the size of the toolbar
- */
- scroll: true
- },
- /**
- * Constructor: Jx.Toolbar
- * Create a new instance of Jx.Toolbar.
- *
- * Parameters:
- * options - <Jx.Toolbar.Options>
- */
- initialize : function(options) {
- this.setOptions(options);
- this.items = [];
-
- this.domObj = new Element('ul', {
- id: this.options.id,
- 'class':'jx'+this.options.type
- });
-
- if (this.options.parent) {
- this.addTo(this.options.parent);
- }
- this.deactivateWatcher = this.deactivate.bindWithEvent(this);
- if (this.options.items) {
- this.add(this.options.items);
- }
- },
-
- /**
- * Method: addTo
- * add this toolbar to a DOM element automatically creating a toolbar
- * container if necessary
- *
- * Parameters:
- * parent - the DOM element or toolbar container to add this toolbar to.
- */
- addTo: function(parent) {
- var tbc = $(parent).retrieve('jxBarContainer');
- if (!tbc) {
- tbc = new Jx.Toolbar.Container({
- parent: parent,
- position: this.options.position,
- autoSize: this.options.autoSize,
- scroll: this.options.scroll
- });
- }
- tbc.add(this);
- return this;
- },
-
- /**
- * Method: add
- * Add an item to the toolbar. If the item being added is a Jx component
- * with a domObj property, the domObj is added. If the item being added
- * is an LI element, then it is given a CSS class of *jxToolItem*.
- * Otherwise, the thing is wrapped in a <Jx.ToolbarItem>.
- *
- * Parameters:
- * thing - {Object} the thing to add. More than one thing can be added
- * by passing multiple arguments.
- */
- add: function( ) {
- $A(arguments).flatten().each(function(thing) {
- if (thing.domObj) {
- thing = thing.domObj;
- }
- if (thing.tagName == 'LI') {
- if (!thing.hasClass('jxToolItem')) {
- thing.addClass('jxToolItem');
- }
- this.domObj.appendChild(thing);
- } else {
- var item = new Jx.Toolbar.Item(thing);
- this.domObj.appendChild(item.domObj);
- }
- }, this);
-
- if (arguments.length > 0) {
- this.fireEvent('add', this);
- }
- return this;
- },
- /**
- * Method: remove
- * remove an item from a toolbar. If the item is not in this toolbar
- * nothing happens
- *
- * Parameters:
- * item - {Object} the object to remove
- *
- * Returns:
- * {Object} the item that was removed, or null if the item was not
- * removed.
- */
- remove: function(item) {
- if (item.domObj) {
- item = item.domObj;
- }
- var li = item.findElement('LI');
- if (li && li.parentNode == this.domObj) {
- item.dispose();
- li.dispose();
- this.fireEvent('remove', this);
- } else {
- return null;
- }
- },
- /**
- * Method: deactivate
- * Deactivate the Toolbar (when it is acting as a menu bar).
- */
- deactivate: function() {
- this.items.each(function(o){o.hide();});
- this.setActive(false);
- },
- /**
- * Method: isActive
- * Indicate if the toolbar is currently active (as a menu bar)
- *
- * Returns:
- * {Boolean}
- */
- isActive: function() {
- return this.isActive;
- },
- /**
- * Method: setActive
- * Set the active state of the toolbar (for menus)
- *
- * Parameters:
- * b - {Boolean} the new state
- */
- setActive: function(b) {
- this.isActive = b;
- if (this.isActive) {
- document.addEvent('click', this.deactivateWatcher);
- } else {
- document.removeEvent('click', this.deactivateWatcher);
- }
- },
- /**
- * Method: setVisibleItem
- * For menus, they want to know which menu is currently open.
- *
- * Parameters:
- * obj - {<Jx.Menu>} the menu that just opened.
- */
- setVisibleItem: function(obj) {
- if (this.visibleItem && this.visibleItem.hide && this.visibleItem != obj) {
- this.visibleItem.hide();
- }
- this.visibleItem = obj;
- if (this.isActive()) {
- this.visibleItem.show();
- }
- },
- showItem: function(item) {
- this.fireEvent('show', item);
- }
-});
-// $Id: tabset.js 424 2009-05-12 12:51:44Z pagameba $
-/**
* Class: Jx.TabSet
*
- * Extends: Object
+ * Extends: <Jx.Object>
*
- * Implements: Options, Events
- *
* A TabSet manages a set of <Jx.Button.Tab> content areas by ensuring that only one
* of the content areas is visible (i.e. the active tab). TabSet does not
* manage the actual tabs. The instances of <Jx.Button.Tab> that are to be managed
@@ -16914,7 +28222,7 @@
*/
Jx.TabSet = new Class({
Family: 'Jx.TabSet',
- Implements: [Options,Events],
+ Extends: Jx.Object,
/**
* Property: tabs
* {Array} array of tabs that are managed by this tab set
@@ -16927,20 +28235,22 @@
*/
domObj : null,
/**
- * Constructor: Jx.TabSet
- * Create a new instance of <Jx.TabSet> within a specific element of
- * the DOM.
- *
* Parameters:
* domObj - {HTMLElement} an element or id of an element to put the
* content of the tabs into.
* options - an options object, only event handlers are supported
* as options at this time.
*/
- initialize: function(domObj, options) {
- this.setOptions(options);
+ parameters: ['domObj','options'],
+
+ /**
+ * APIMethod: init
+ * Create a new instance of <Jx.TabSet> within a specific element of
+ * the DOM.
+ */
+ init: function() {
this.tabs = [];
- this.domObj = $(domObj);
+ this.domObj = document.id(this.options.domObj);
if (!this.domObj.hasClass('jxTabSetContainer')) {
this.domObj.addClass('jxTabSetContainer');
}
@@ -17021,14 +28331,12 @@
-// $Id: tabbox.js 426 2009-05-12 15:29:00Z pagameba $
+// $Id: tabbox.js 524 2009-09-18 05:40:16Z jonlb at comcast.net $
/**
* Class: Jx.TabBox
*
- * Extends: Object
+ * Extends: <Jx.Widget>
*
- * Implements: Options, Events, <Jx.Addable>
- *
* A convenience class to handle the common case of a single toolbar
* directly attached to the content area of the tabs. It manages both a
* <Jx.Toolbar> and a <Jx.TabSet> so that you don't have to. If you are using
@@ -17052,7 +28360,7 @@
*/
Jx.TabBox = new Class({
Family: 'Jx.TabBox',
- Implements: [Options, Events, Jx.Addable],
+ Extends: Jx.Widget,
options: {
/* Option: parent
* a DOM element to add the tab box to
@@ -17091,14 +28399,11 @@
*/
tabSet: null,
/**
- * Constructor: Jx.TabBox
+ * APIMethod: render
* Create a new instance of a TabBox.
- *
- * Parameters:
- * options - <Jx.TabBox.Options>
*/
- initialize : function(options) {
- this.setOptions(options);
+ render : function() {
+ this.parent();
this.tabBar = new Jx.Toolbar({
type: 'TabBar',
position: this.options.position,
@@ -17177,400 +28482,11 @@
this.tabSet.remove(tab);
}
});
-// $Id: container.js 424 2009-05-12 12:51:44Z pagameba $
+// $Id: toolbar.separator.js 524 2009-09-18 05:40:16Z jonlb at comcast.net $
/**
- * Class: Jx.Toolbar.Container
- *
- * Extends: Object
- *
- * Implements: Options, Events, <Jx.Addable>
- *
- * A toolbar container contains toolbars. A single toolbar container fills
- * the available space horizontally. Toolbars placed in a toolbar container
- * do not wrap when they exceed the available space.
- *
- * Events:
- * add - fired when one or more toolbars are added to a container
- * remove - fired when one or more toolbars are removed from a container
- *
- * Implements:
- * Options
- * Events
- * {<Jx.Addable>}
- *
- * License:
- * Copyright (c) 2008, DM Solutions Group Inc.
- *
- * This file is licensed under an MIT style license
- */
-Jx.Toolbar.Container = new Class({
- Family: 'Jx.Toolbar.Container',
- Implements: [Options,Events, Jx.Addable],
- /**
- * Property: domObj
- * {HTMLElement} the HTML element that the container lives in
- */
- domObj : null,
- options: {
- /* Option: parent
- * a DOM element to add this to
- */
- parent: null,
- /* Option: position
- * the position of the toolbar container in its parent, one of 'top',
- * 'right', 'bottom', or 'left'. Default is 'top'
- */
- position: 'top',
- /* Option: autoSize
- * automatically size the toolbar container to fill its container.
- * Default is false
- */
- autoSize: false,
- /* Option: scroll
- * Control whether the user can scroll of the content of the
- * container if the content exceeds the size of the container.
- * Default is true.
- */
- scroll: true
- },
- /**
- * Constructor: Jx.Toolbar.Container
- * Create a new instance of Jx.Toolbar.Container
- *
- * Parameters:
- * options - <Jx.Toolbar.Options>
- */
- initialize : function(options) {
- this.setOptions(options);
-
- var d = $(this.options.parent);
- this.domObj = d || new Element('div');
- this.domObj.addClass('jxBarContainer');
-
- if (this.options.scroll) {
- this.scroller = new Element('div', {'class':'jxBarScroller'});
- this.domObj.adopt(this.scroller);
- }
-
- /* this allows toolbars to add themselves to this bar container
- * once it already exists without requiring an explicit reference
- * to the toolbar container
- */
- this.domObj.store('jxBarContainer', this);
-
- if (['top','right','bottom','left'].contains(this.options.position)) {
- this.domObj.addClass('jxBar' +
- this.options.position.capitalize());
- } else {
- this.domObj.addClass('jxBarTop');
- this.options.position = 'top';
- }
-
- if (this.options.scroll && ['top','bottom'].contains(this.options.position)) {
- // make sure we update our size when we get added to the DOM
- this.addEvent('addTo', this.update.bind(this));
-
- //making Fx.Tween optional
- if (typeof Fx != 'undefined' && typeof Fx.Tween != 'undefined'){
- this.scrollFx = scrollFx = new Fx.Tween(this.scroller, {
- link: 'chain'
- });
- }
-
- this.scrollLeft = new Jx.Button({
- image: Jx.aPixel.src
- }).addTo(this.domObj);
- this.scrollLeft.domObj.addClass('jxBarScrollLeft');
- this.scrollLeft.addEvents({
- click: (function(){
- var from = this.scroller.getStyle('left').toInt();
- if (isNaN(from)) { from = 0; }
- var to = Math.min(from+100, 0);
- if (to >= 0) {
- this.scrollLeft.domObj.setStyle('visibility', 'hidden');
- }
- this.scrollRight.domObj.setStyle('visibility', '');
- if ($defined(this.scrollFx)){
- this.scrollFx.start('left', from, to);
- } else {
- this.scroller.setStyle('left',to);
- }
- }).bind(this)
- });
-
- this.scrollRight = new Jx.Button({
- image: Jx.aPixel.src
- }).addTo(this.domObj);
- this.scrollRight.domObj.addClass('jxBarScrollRight');
- this.scrollRight.addEvents({
- click: (function(){
- var from = this.scroller.getStyle('left').toInt();
- if (isNaN(from)) { from = 0; }
- var to = Math.max(from - 100, this.scrollWidth);
- if (to == this.scrollWidth) {
- this.scrollRight.domObj.setStyle('visibility', 'hidden');
- }
- this.scrollLeft.domObj.setStyle('visibility', '');
- if ($defined(this.scrollFx)){
- this.scrollFx.start('left', from, to);
- } else {
- this.scroller.setStyle('left',to);
- }
- }).bind(this)
- });
-
- } else {
- this.options.scroll = false;
- }
-
- if (this.options.toolbars) {
- this.add(this.options.toolbars);
- }
- },
-
- update: function() {
- if (this.options.autoSize) {
- /* delay the size update a very small amount so it happens
- * after the current thread of execution finishes. If the
- * current thread is part of a window load event handler,
- * rendering hasn't quite finished yet and the sizes are
- * all wrong
- */
- (function(){
- var x = 0;
- this.scroller.getChildren().each(function(child){
- x+= child.getSize().x;
- });
- this.domObj.setStyles({width:x});
- this.measure();
- }).delay(1,this);
- } else {
- this.measure();
- }
- },
-
- measure: function() {
- if ((!this.scrollLeftSize || !this.scrollLeftSize.x) && this.domObj.parentNode) {
- this.scrollLeftSize = this.scrollLeft.domObj.getSize();
- this.scrollRightSize = this.scrollRight.domObj.getSize();
- }
- /* decide if we need to show the scroller buttons and
- * do some calculations that will make it faster
- */
- this.scrollWidth = this.domObj.getSize().x;
- this.scroller.getChildren().each(function(child){
- this.scrollWidth -= child.getSize().x;
- }, this);
- if (this.scrollWidth < 0) {
- /* we need to show scrollers on at least one side */
- var l = this.scroller.getStyle('left').toInt();
- if (l < 0) {
- this.scrollLeft.domObj.setStyle('visibility','');
- } else {
- this.scrollLeft.domObj.setStyle('visibility','hidden');
- }
- if (l <= this.scrollWidth) {
- this.scrollRight.domObj.setStyle('visibility', 'hidden');
- if (l < this.scrollWidth) {
- if ($defined(this.scrollFx)){
- this.scrollFx.start('left', l, this.scrollWidth);
- } else {
- this.scroller.setStyle('left',this.scrollWidth);
- }
- }
- } else {
- this.scrollRight.domObj.setStyle('visibility', '');
- }
-
- } else {
- /* don't need any scrollers but we might need to scroll
- * the toolbar into view
- */
- this.scrollLeft.domObj.setStyle('visibility','hidden');
- this.scrollRight.domObj.setStyle('visibility','hidden');
- var from = this.scroller.getStyle('left').toInt();
- if (!isNaN(from) && from !== 0) {
- if ($defined(this.scrollFx)) {
- this.scrollFx.start('left', 0);
- } else {
- this.scroller.setStyle('left',0);
- }
- }
- }
- },
-
- /**
- * Method: add
- * Add a toolbar to the container.
- *
- * Parameters:
- * toolbar - {Object} the toolbar to add. More than one toolbar
- * can be added by passing multiple arguments.
- */
- add: function( ) {
- $A(arguments).flatten().each(function(thing) {
- if (this.options.scroll) {
- /* we potentially need to show or hide scroller buttons
- * when the toolbar contents change
- */
- thing.addEvent('add', this.update.bind(this));
- thing.addEvent('remove', this.update.bind(this));
- thing.addEvent('show', this.scrollIntoView.bind(this));
- }
- if (this.scroller) {
- this.scroller.adopt(thing.domObj);
- } else {
- this.domObj.adopt(thing.domObj);
- }
- this.domObj.addClass('jx'+thing.options.type+this.options.position.capitalize());
- }, this);
- if (this.options.scroll) {
- this.update();
- }
- if (arguments.length > 0) {
- this.fireEvent('add', this);
- }
- return this;
- },
- /**
- * Method: remove
- * remove an item from a toolbar. If the item is not in this toolbar
- * nothing happens
- *
- * Parameters:
- * item - {Object} the object to remove
- *
- * Returns:
- * {Object} the item that was removed, or null if the item was not
- * removed.
- */
- remove: function(item) {
-
- },
- /**
- * Method: scrollIntoView
- * scrolls an item in one of the toolbars into the currently visible
- * area of the container if it is not already fully visible
- *
- * Parameters:
- * item - the item to scroll.
- */
- scrollIntoView: function(item) {
- var width = this.domObj.getSize().x;
- var coords = item.domObj.getCoordinates(this.scroller);
-
- //left may be set to auto or even a zero length string.
- //In the previous version, in air, this would evaluate to
- //NaN which would cause the right hand scroller to show when
- //the component was first created.
-
- //So, get the left value first
- var l = this.scroller.getStyle('left');
- //then check to see if it's auto or a zero length string
- if (l === 'auto' || l.length <= 0) {
- //If so, set to 0.
- l = 0;
- } else {
- //otherwise, convert to int
- l = l.toInt();
- }
- var slSize = this.scrollLeftSize ? this.scrollLeftSize.x : 0;
- var srSize = this.scrollRightSize ? this.scrollRightSize.x : 0;
-
- var left = l;
- if (l < -coords.left + slSize) {
- /* the left edge of the item is not visible */
- left = -coords.left + slSize;
- if (left >= 0) {
- left = 0;
- }
- } else if (width - coords.right - srSize< l) {
- /* the right edge of the item is not visible */
- left = width - coords.right - srSize;
- if (left < this.scrollWidth) {
- left = this.scrollWidth;
- }
- }
-
- if (left < 0) {
- this.scrollLeft.domObj.setStyle('visibility','');
- } else {
- this.scrollLeft.domObj.setStyle('visibility','hidden');
- }
- if (left <= this.scrollWidth) {
- this.scrollRight.domObj.setStyle('visibility', 'hidden');
- } else {
- this.scrollRight.domObj.setStyle('visibility', '');
- }
- if (left != l) {
- if ($defined(this.scrollFx)) {
- this.scrollFx.start('left', left);
- } else {
- this.scroller.setStyle('left',left);
- }
- }
- }
-});
-// $Id: toolbar.item.js 424 2009-05-12 12:51:44Z pagameba $
-/**
- * Class: Jx.Toolbar.Item
- *
- * Extends: Object
- *
- * Implements: Options
- *
- * A helper class to provide a container for something to go into
- * a <Jx.Toolbar>.
- *
- * License:
- * Copyright (c) 2008, DM Solutions Group Inc.
- *
- * This file is licensed under an MIT style license
- */
-Jx.Toolbar.Item = new Class( {
- Family: 'Jx.Toolbar.Item',
- Implements: [Options],
- options: {
- /* Option: active
- * is this item active or not? Default is true.
- */
- active: true
- },
- /**
- * Property: domObj
- * {HTMLElement} an element to contain the thing to be placed in the
- * toolbar.
- */
- domObj: null,
- /**
- * Constructor: Jx.Toolbar.Item
- * Create a new instance of Jx.Toolbar.Item.
- *
- * Parameters:
- * jxThing - {Object} the thing to be contained.
- */
- initialize : function( jxThing ) {
- this.al = [];
- this.domObj = new Element('li', {'class':'jxToolItem'});
- if (jxThing) {
- if (jxThing.domObj) {
- this.domObj.appendChild(jxThing.domObj);
- if (jxThing instanceof Jx.Button.Tab) {
- this.domObj.addClass('jxTabItem');
- }
- } else {
- this.domObj.appendChild(jxThing);
- if (jxThing.hasClass('jxTab')) {
- this.domObj.addClass('jxTabItem');
- }
- }
- }
- }
-});// $Id: toolbar.separator.js 424 2009-05-12 12:51:44Z pagameba $
-/**
* Class: Jx.Toolbar.Separator
*
- * Extends: Object
+ * Extends: <Jx.Object>
*
* A helper class that represents a visual separator in a <Jx.Toolbar>
*
@@ -17585,32 +28501,26 @@
*/
Jx.Toolbar.Separator = new Class({
Family: 'Jx.Toolbar.Separator',
+ Extends: Jx.Widget,
/**
- * Property: domObj
- * {HTMLElement} The DOM element that goes in the <Jx.Toolbar>
- */
- domObj: null,
- /**
- * Constructor: Jx.Toolbar.Separator
+ * APIMethod: render
* Create a new Jx.Toolbar.Separator
*/
- initialize: function() {
+ render: function() {
this.domObj = new Element('li', {'class':'jxToolItem'});
this.domSpan = new Element('span', {'class':'jxBarSeparator'});
this.domObj.appendChild(this.domSpan);
}
});
-// $Id: treeitem.js 424 2009-05-12 12:51:44Z pagameba $
+// $Id: treeitem.js 581 2009-10-29 20:59:48Z pagameba $
/**
- * Class: Jx.TreeItem
+ * Class: Jx.TreeItem
*
- * Extends: Object
+ * Extends: <Jx.Widget>
*
- * Implements: Options, Events
- *
* An item in a tree. An item is a leaf node that has no children.
*
- * Jx.TreeItem supports selection via the click event. The application
+ * Jx.TreeItem supports selection via the click event. The application
* is responsible for changing the style of the selected item in the tree
* and for tracking selection if that is important.
*
@@ -17625,14 +28535,15 @@
* Events - MooTools Class.Extras
* Options - MooTools Class.Extras
*
- * License:
+ * License:
* Copyright (c) 2008, DM Solutions Group Inc.
- *
+ *
* This file is licensed under an MIT style license
*/
Jx.TreeItem = new Class ({
Family: 'Jx.TreeItem',
- Implements: [Options,Events],
+ Extends: Jx.Widget,
+ selection: null,
/**
* Property: domObj
* {HTMLElement} a reference to the HTML element that is the TreeItem
@@ -17647,7 +28558,7 @@
options: {
/* Option: label
* {String} the label to display for the TreeItem
- */
+ */
label: '',
/* Option: data
* {Object} any arbitrary data to be associated with the TreeItem
@@ -17659,11 +28570,10 @@
*/
contextMenu: null,
/* Option: enabled
- * {Boolean} the initial state of the TreeItem. If the
+ * {Boolean} the initial state of the TreeItem. If the
* TreeItem is not enabled, it cannot be clicked.
*/
enabled: true,
- type: 'Item',
/* Option: image
* {String} URL to an image to use as the icon next to the
* label of this TreeItem
@@ -17673,108 +28583,96 @@
* {String} CSS class to apply to the image, useful for using CSS
* sprites
*/
- imageClass: ''
+ imageClass: '',
+ lastLeafClass: 'jxTreeLeafLast',
+ template: '<li class="jxTreeContainer jxTreeLeaf"><img class="jxTreeImage" src="'+Jx.aPixel.src+'" alt="" title=""><a class="jxTreeItem" href="javascript:void(0);"><img class="jxTreeIcon" src="'+Jx.aPixel.src+'" alt="" title=""><span class="jxTreeLabel"></span></a></li>'
},
+ classes: ['jxTreeContainer', 'jxTreeItem', 'jxTreeImage', 'jxTreeIcon','jxTreeLabel'],
+
/**
- * Constructor: Jx.TreeItem
+ * APIMethod: render
* Create a new instance of Jx.TreeItem with the associated options
- *
- * Parameters:
- * options - <Jx.TreeItem.Options>
*/
- initialize : function( options ) {
- this.setOptions(options);
+ render : function() {
+ this.parent();
+ this.elements = this.processTemplate(this.options.template, this.classes);
- this.domObj = new Element('li', {'class':'jxTree'+this.options.type});
- if (this.options.id) {
- this.domObj.id = this.options.id;
+ this.domObj = this.elements.get('jxTreeContainer');
+ this.domObj.store('jxTreeItem', this);
+ var domA = this.elements.get('jxTreeItem');
+ var domImg = this.elements.get('jxTreeIcon');
+ var domLabel = this.elements.get('jxTreeLabel');
+
+ if (this.domObj) {
+ if (this.options.id) {
+ this.domObj.id = this.options.id;
+ }
+ this.domObj.store('jxTreeItem', this);
+ if (!this.options.enabled) {
+ this.domObj.addClass('jxDisabled');
+ }
}
-
- this.domNode = new Element('img',{
- 'class': 'jxTreeImage',
- src: Jx.aPixel.src,
- alt: '',
- title: ''
- });
- this.domObj.appendChild(this.domNode);
-
- this.domLabel = (this.options.draw) ?
- this.options.draw.apply(this) :
- this.draw();
- this.domObj.appendChild(this.domLabel);
- this.domObj.store('jxTreeItem', this);
-
- if (!this.options.enabled) {
- this.domObj.addClass('jxDisabled');
- }
- },
- draw: function() {
- var domImg = new Element('img',{
- 'class':'jxTreeIcon',
- src: Jx.aPixel.src,
- alt: '',
- title: ''
- });
- if (this.options.image) {
+ if (this.options.image && domImg) {
domImg.setStyle('backgroundImage', 'url('+this.options.image+')');
+ if (this.options.imageClass) {
+ domImg.addClass(this.options.imageClass);
+ }
+
}
- if (this.options.imageClass) {
- domImg.addClass(this.options.imageClass);
+
+ if (this.options.label && domLabel) {
+ domLabel.set('html',this.options.label);
}
- // the clickable part of the button
- var hasFocus;
- var mouseDown;
-
- var domA = new Element('a',{
- href:'javascript:void(0)',
- html: this.options.label
- });
- domA.addEvents({
- click: this.selected.bind(this),
- dblclick: this.selected.bind(this),
- drag: function(e) {e.stop();},
- contextmenu: function(e) { e.stop(); },
- mousedown: (function(e) {
- domA.addClass('jxTreeItemPressed');
- hasFocus = true;
- mouseDown = true;
- domA.focus();
- if (e.rightClick && this.options.contextMenu) {
- this.options.contextMenu.show(e);
- }
- }).bind(this),
- mouseup: function(e) {
- domA.removeClass('jxTreeItemPressed');
- mouseDown = false;
- },
- mouseleave: function(e) {
- domA.removeClass('jxTreeItemPressed');
- },
- mouseenter: function(e) {
- if (hasFocus && mouseDown) {
- domA.addClass('jxTreeItemPressed');
- }
- },
- keydown: function(e) {
- if (e.key == 'enter') {
- domA.addClass('jxTreeItemPressed');
- }
- },
- keyup: function(e) {
- if (e.key == 'enter') {
+
+ if (domA) {
+ var hasFocus;
+ var mouseDown;
+ domA.addEvents({
+ click: this.click.bind(this),
+ dblclick: this.dblclick.bind(this),
+ drag: function(e) { e.stop(); },
+ contextmenu: function(e) { e.stop(); },
+ mousedown: (function(e) {
+ domA.addClass('jxTreeItemPressed');
+ hasFocus = true;
+ mouseDown = true;
+ domA.focus();
+ if (e.rightClick && this.options.contextMenu) {
+ this.options.contextMenu.show(e);
+ }
+ }).bind(this),
+ mouseup: function(e) {
domA.removeClass('jxTreeItemPressed');
- }
- },
- blur: function() { hasFocus = false; }
- });
- domA.appendChild(domImg);
- if (typeof Drag != 'undefined') {
- new Drag(domA, {
- onStart: function() {this.stop();}
+ mouseDown = false;
+ },
+ mouseleave: function(e) {
+ domA.removeClass('jxTreeItemPressed');
+ },
+ mouseenter: function(e) {
+ if (hasFocus && mouseDown) {
+ domA.addClass('jxTreeItemPressed');
+ }
+ },
+ keydown: function(e) {
+ if (e.key == 'enter') {
+ domA.addClass('jxTreeItemPressed');
+ }
+ },
+ keyup: function(e) {
+ if (e.key == 'enter') {
+ domA.removeClass('jxTreeItemPressed');
+ }
+ },
+ blur: function() { hasFocus = false; }
});
+ domA.appendChild(domImg);
+ if (typeof Drag != 'undefined') {
+ new Drag(domA, {
+ onStart: function() {this.stop();}
+ });
+ }
}
- return domA;
},
/**
* Method: finalize
@@ -17785,67 +28683,59 @@
* Method: finalizeItem
* Clean up the TreeItem and remove all DOM references
*/
- finalizeItem: function() {
+ finalizeItem: function() {
if (!this.domObj) {
return;
}
- //this.domA.removeEvents();
this.options = null;
this.domObj.dispose();
this.domObj = null;
this.owner = null;
},
/**
- * Method: clone
- * Create a clone of the TreeItem
- *
- * Returns:
- * {<Jx.TreeItem>} a copy of the TreeItem
- */
- clone : function() {
- return new Jx.TreeItem(this.options);
- },
- /**
* Method: update
* Update the CSS of the TreeItem's DOM element in case it has changed
* position
*
* Parameters:
- * shouldDescend - {Boolean} propagate changes to child nodes?
+ * isLast - {Boolean} is the item the last one or not?
*/
- update : function(shouldDescend) {
- var isLast = (arguments.length > 1) ? arguments[1] :
- (this.owner && this.owner.isLastNode(this));
+ update : function(isLast) {
if (isLast) {
- this.domObj.removeClass('jxTree'+this.options.type);
- this.domObj.addClass('jxTree'+this.options.type+'Last');
+ this.domObj.addClass(this.options.lastLeafClass);
} else {
- this.domObj.removeClass('jxTree'+this.options.type+'Last');
- this.domObj.addClass('jxTree'+this.options.type);
+ this.domObj.removeClass(this.options.lastLeafClass);
}
},
+ click: function() {
+ this.fireEvent('click', this);
+ this.select();
+ },
+ dblclick: function() {
+ this.fireEvent('dblclick', this);
+ this.select();
+ },
/**
- * Method: selected
- * Called when the DOM element for the TreeItem is clicked, the
- * node is selected.
- *
- * Parameters:
- * e - {Event} the DOM event
+ * Method: select
+ * Select a tree node.
*/
- selected : function(e) {
- this.fireEvent('click', this);
+ select: function() {
+ if (this.selection) {
+ console.log('select: '+this.options.label);
+ this.selection.select(document.id(this));
+ }
},
/**
* Method: getName
* Get the label associated with a TreeItem
*
- * Returns:
+ * Returns:
* {String} the name
*/
getName : function() { return this.options.label; },
/**
* Method: propertyChanged
- * A property of an object has changed, synchronize the state of the
+ * A property of an object has changed, synchronize the state of the
* TreeItem with the state of the object
*
* Parameters:
@@ -17858,9 +28748,12 @@
} else {
this.domObj.addClass('jxDisabled');
}
+ },
+ setSelection: function(selection){
+ this.selection = selection;
}
});
-// $Id: treefolder.js 424 2009-05-12 12:51:44Z pagameba $
+// $Id: treefolder.js 581 2009-10-29 20:59:48Z pagameba $
/**
* Class: Jx.TreeFolder
*
@@ -17885,411 +28778,1453 @@
Family: 'Jx.TreeFolder',
Extends: Jx.TreeItem,
/**
- * Property: subDomObj
- * {HTMLElement} an HTML container for the things inside the folder
+ * Property: tree
+ * {<Jx.Tree>} a Jx.Tree instance for managing the folder contents
*/
- subDomObj : null,
- /**
- * Property: nodes
- * {Array} an array of references to the javascript objects that are
- * children of this folder
- */
- nodes : null,
+ tree : null,
options: {
/* Option: open
* is the folder open? false by default.
*/
- open : false
+ open: false,
+ /* folders will share a selection with the tree they are in */
+ select: false,
+ template: '<li class="jxTreeContainer jxTreeBranch"><img class="jxTreeImage" src="'+Jx.aPixel.src+'" alt="" title=""><a class="jxTreeItem" href="javascript:void(0);"><img class="jxTreeIcon" src="'+Jx.aPixel.src+'" alt="" title=""><span class="jxTreeLabel"></span></a><ul class="jxTree"></ul></li>'
},
+ classes: ['jxTreeContainer','jxTreeImage','jxTreeItem','jxTreeIcon','jxTreeLabel','jxTree'],
/**
- * Constructor: Jx.TreeFolder
+ * APIMethod: render
* Create a new instance of Jx.TreeFolder
- *
- * Parameters:
- * options - <Jx.TreeFolder.Options> and <Jx.TreeItem.Options>
*/
- initialize : function( options ) {
- this.parent($merge(options,{type:'Branch'}));
-
- $(this.domNode).addEvent('click', this.clicked.bindWithEvent(this));
- this.addEvent('click', this.clicked.bindWithEvent(this));
+ render : function() {
+ this.parent();
+ this.domObj.store('jxTreeFolder', this);
+ this.addEvents({
+ click: this.toggle.bind(this),
+ dblclick: this.toggle.bind(this)
+ })
+
+ var node = this.elements.get('jxTreeImage');
+ if (node) {
+ document.id(node).addEvent('click', this.toggle.bind(this));
+ }
- this.nodes = [];
- this.subDomObj = new Element('ul', {'class':'jxTree'});
- this.domObj.appendChild(this.subDomObj);
+ this.tree = new Jx.Tree({
+ template: this.options.template,
+ onAdd: function(item) {
+ this.update();
+ this.fireEvent('add', item);
+ }.bind(this),
+ onRemove: function(item) {
+ this.update();
+ this.fireEvent('remove', item);
+ }.bind(this)
+ }, this.elements.get('jxTree'));
if (this.options.open) {
this.expand();
} else {
this.collapse();
}
+
+ this.addEvent('postDestroy',function() {
+ this.tree.destroy();
+ }.bind(this));
},
+
+ add: function(item, position) {
+ this.tree.add(item, position);
+ return this;
+ },
+ remove: function(item) {
+ this.tree.remove(item);
+ return this;
+ },
+ replace: function(item, withItem) {
+ this.tree.replace(item, withItem);
+ return this;
+ },
/**
- * Method: finalize
- * Clean up a TreeFolder.
+ * Method: update
+ * Update the CSS of the TreeFolder's DOM element in case it has changed
+ * position.
+ *
+ * Parameters:
+ * shouldDescend - {Boolean} propagate changes to child nodes?
+ * isLast - {Boolean} is this the last item in the list?
*/
- finalize: function() {
- this.finalizeFolder();
- this.finalizeItem();
- this.subDomObj.dispose();
- this.subDomObj = null;
+ update: function(shouldDescend,isLast) {
+ /* avoid update if not attached to tree yet */
+ if (!this.domObj.parentNode) return;
+
+ if (!$defined(isLast)) {
+ isLast = this.domObj.hasClass('jxTreeBranchLastOpen') ||
+ this.domObj.hasClass('jxTreeBranchLastClosed');
+ }
+
+ ['jxTreeBranchOpen','jxTreeBranchLastOpen','jxTreeBranchClosed',
+ 'jxTreeBranchLastClosed'].each(function(c){
+ this.removeClass(c);
+ }.bind(this.domObj));
+ var c = 'jxTreeBranch';
+ c += isLast ? 'Last' : '';
+ c += this.options.open ? 'Open' : 'Closed';
+ this.domObj.addClass(c);
+
+ this.tree.update(shouldDescend, isLast);
},
+ toggle: function() {
+ if (this.options.open) {
+ this.collapse();
+ } else {
+ this.expand();
+ }
+ },
/**
- * Method: finalizeFolder
- * Internal method to clean up folder-related stuff.
+ * Method: expand
+ * Expands the folder
*/
- finalizeFolder: function() {
- this.domObj.childNodes[0].removeEvents();
- for (var i=this.nodes.length-1; i>=0; i--) {
- this.nodes[i].finalize();
- this.nodes.pop();
+ expand : function() {
+ this.options.open = true;
+ document.id(this.tree).setStyle('display', 'block');
+ this.update(true);
+ this.fireEvent('disclosed', this);
+ },
+ /**
+ * Method: collapse
+ * Collapses the folder
+ */
+ collapse : function() {
+ this.options.open = false;
+ document.id(this.tree).setStyle('display', 'none');
+ this.update(true);
+ this.fireEvent('disclosed', this);
+ },
+ findChild : function(path) {
+ //path is empty - we are asking for this node
+ if (path.length == 0) {
+ return this;
+ } else {
+ return this.tree.findChild(path);
}
+ }
+});// $Id: tree.js 581 2009-10-29 20:59:48Z pagameba $
+/**
+ * Class: Jx.Tree
+ *
+ * Extends: Jx.TreeFolder
+ *
+ * Jx.Tree displays hierarchical data in a tree structure of folders and nodes.
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * Extends: <Jx.Widget>
+ *
+ * License:
+ * Copyright (c) 2008, DM Solutions Group Inc.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Tree = new Class({
+ Family: 'Jx.Tree',
+ Extends: Jx.Widget,
+ parameters: ['options','container', 'selection'],
+ selection: null,
+ ownsSelection: false,
+ isOpen: true,
+ list: null,
+ domObj: null,
+ options: {
+ /* APIProperty: select
+ * {Boolean} are items in the tree selectable? See <Jx.Selection>
+ * for other options relating to selections that can be set here.
+ */
+ select: true,
+ template: '<ul class="jxTreeRoot"></ul>'
+ },
+ classes: ['jxTreeRoot'],
+ /**
+ * APIMethod: render
+ * Create a new instance of Jx.Tree
+ */
+ render: function() {
+ this.parent();
+ if (this.options.selection) {
+ this.selection = this.options.selection;
+ } else if (this.options.select) {
+ this.selection = new Jx.Selection(this.options);
+ this.ownsSelection = true;
+ }
+
+ if (this.selection && this.ownsSelection) {
+ this.selection.addEvent('select', function(item) {
+ this.fireEvent('select', item.retrieve('jxTreeItem'));
+ }.bind(this));
+ }
+ if ($defined(this.options.container) &&
+ document.id(this.options.container)) {
+ this.domObj = this.options.container;
+ } else {
+ this.elements = this.processTemplate(this.options.template, this.classes);
+ this.domObj = this.elements.get('jxTreeRoot');
+ }
+ this.list = new Jx.List(this.domObj, {
+ hover: true,
+ press: true,
+ select: true,
+ onAdd: function(item) {this.update();}.bind(this),
+ onRemove: function(item) {this.update();}.bind(this)
+ }, this.selection);
+ if (this.options.parent) {
+ this.addTo(this.options.parent);
+ }
},
+ add: function(item, position) {
+ item.addEvents({
+ add: function(what) { this.fireEvent('add', what).bind(this); },
+ remove: function(what) { this.fireEvent('remove', what).bind(this); },
+ disclose: function(what) { this.fireEvent('disclose', what).bind(this); }
+ })
+ item.setSelection(this.selection);
+ this.list.add(item, position);
+ return this;
+ },
+ remove: function(item) {
+ item.removeEvents('add');
+ item.removeEvents('remove');
+ item.removeEvents('disclose');
+ this.list.remove(item);
+ item.setSelection(null);
+ return this;
+ },
+ replace: function(item, withItem) {
+ this.list.replace(item, withItem);
+ withItem.setSelection(this.selection);
+ item.setSelection(null);
+ return this;
+ },
+
/**
- * Method: clone
- * Create a clone of the TreeFolder
- *
- * Returns:
- * {<Jx.TreeFolder>} a copy of the TreeFolder
+ * Method: cleanup
+ * Clean up a Jx.Tree instance
*/
- clone : function() {
- var node = new Jx.TreeFolder(this.options);
- this.nodes.each(function(n){node.append(n.clone());});
- return node;
+ cleanup: function() {
+ if (this.ownsSelection) {
+ this.selection.destroy();
+ }
+ this.list.destroy();
+ this.domObj.dispose();
},
/**
- * Method: isLastNode
- * Indicates if a node is the last thing in the folder.
+ * Method: update
+ * Update the CSS of the Tree's DOM element in case it has changed
+ * position
*
* Parameters:
- * node - {Jx.TreeItem} the node to check
+ * shouldDescend - {Boolean} propagate changes to child nodes?
+ */
+ update: function(shouldDescend, isLast) {
+
+ if ($defined(isLast)) {
+ if (isLast) {
+ this.domObj.removeClass('jxTreeNest');
+ } else {
+ this.domObj.addClass('jxTreeNest');
+ }
+ }
+ var last = this.list.count() - 1;
+ this.list.each(function(item, idx){
+ var lastItem = idx == last;
+ if (item.retrieve('jxTreeFolder')) {
+ item.retrieve('jxTreeFolder').update(shouldDescend, lastItem);
+ }
+ if (item.retrieve('jxTreeItem')) {
+ item.retrieve('jxTreeItem').update(lastItem);
+ }
+ });
+ },
+
+ /**
+ * Method: findChild
+ * Get a reference to a child node by recursively searching the tree
+ *
+ * Parameters:
+ * path - {Array} an array of labels of nodes to search for
*
* Returns:
- *
- * {Boolean}
+ * {Object} the node or null if the path was not found
*/
- isLastNode : function(node) {
- if (this.nodes.length == 0) {
+ findChild : function(path) {
+ //path is empty - we are asking for this node
+ if (path.length == 0) {
return false;
- } else {
- return this.nodes[this.nodes.length-1] == node;
}
+ //path has more than one thing in it, find a folder and descend into it
+ var name = path.shift();
+ var result = false;
+ this.list.items().some(function(item) {
+ var treeItem = item.retrieve('jxTreeItem');
+ if (treeItem && treeItem.getName() == name) {
+ if (path.length > 0) {
+ var folder = item.retrieve('jxTreeFolder');
+ if (folder) {
+ result = folder.findChild(path)
+ }
+ } else {
+ result = treeItem;
+ }
+ }
+ return result;
+ });
+ return result;
+ }
+});
+
+
+/**
+ * Class: Jx.Slider
+ * This class wraps the mootools-more slider class to make it more Jx.Slider
+ *
+ * Copyright 2009 by Jonathan Bomgardner
+ * License: MIT-style
+ */
+Jx.Slider = new Class({
+
+ Extends: Jx.Widget,
+
+ options: {
+ /**
+ * Option: template
+ * The template used to render the slider
+ */
+ template: '<div class="jxSliderContainer"><div class="jxSliderKnob"></div></div>',
+ /**
+ * Option: max
+ * The maximum value the slider should have
+ */
+ max: 100,
+ /**
+ * Option: min
+ * The minimum value the slider should ever have
+ */
+ min: 0,
+ /**
+ * Option: step
+ * The distance between adjacent steps. For example, the default (1)
+ * with min of 0 and max of 100, provides 100 steps between the min
+ * and max values
+ */
+ step: 1,
+ /**
+ * Option: mode
+ * Whether this is a vertical or horizontal slider
+ */
+ mode: 'horizontal',
+ /**
+ * Option: wheel
+ * Whether the slider reacts to the scroll wheel.
+ */
+ wheel: true,
+ /**
+ * Option: snap
+ * whether to snap to each step
+ */
+ snap: true,
+ /**
+ * Option: startAt
+ * The value, or step, to put the slider at initially
+ */
+ startAt: 0,
+ /**
+ * Option: offset
+ *
+ */
+ offset: 0,
+ onChange: $empty,
+ onComplete: $empty
},
+
+ slider: null,
+ knob: null,
+ sliderOpts: null,
/**
- * Method: update
- * Update the CSS of the TreeFolder's DOM element in case it has changed
- * position.
- *
- * Parameters:
- * shouldDescend - {Boolean} propagate changes to child nodes?
+ * APIMethod: render
+ * Create the slider but does not start it up due to issues with it
+ * having to be visible before it will work properly.
*/
- update : function(shouldDescend) {
- /* avoid update if not attached to tree yet */
- if (!this.parent) return;
- var isLast = false;
- if (arguments.length > 1) {
- isLast = arguments[1];
- } else {
- isLast = (this.owner && this.owner.isLastNode(this));
+ render: function () {
+ this.parent();
+
+ var els = this.processTemplate(this.options.template, ['jxSliderContainer', 'jxSliderKnob']);
+
+ if (!els.has('jxSliderContainer')) {
+ return;
}
- var c = 'jxTree'+this.options.type;
- c += isLast ? 'Last' : '';
- c += this.options.open ? 'Open' : 'Closed';
- this.domObj.className = c;
+ this.domObj = els.get('jxSliderContainer');
+ this.knob = els.get('jxSliderKnob');
- if (isLast) {
- this.subDomObj.className = 'jxTree';
- } else {
- this.subDomObj.className = 'jxTree jxTreeNest';
+ this.sliderOpts = {
+ range: [this.options.min, this.options.max],
+ snap: this.options.snap,
+ mode: this.options.mode,
+ wheel: this.options.wheel,
+ steps: (this.options.max - this.options.min) / this.options.step,
+ offset: this.options.offset,
+ onChange: this.change.bind(this),
+ onComplete: this.complete.bind(this)
+ };
+
+ },
+ /**
+ * Method: change
+ * Called when the slider moves
+ */
+ change: function (step) {
+ this.fireEvent('change', [step, this]);
+ },
+ /**
+ * Method: complete
+ * Called when the slider stops movingand the mouse button is released.
+ */
+ complete: function (step) {
+ this.fireEvent('complete', [step, this]);
+ },
+ /**
+ * APIMethod: start
+ * Call this method after the slider has been rendered in the DOM to start
+ * it up and position the slider at the startAt poisition.
+ */
+ start: function () {
+ if (!$defined(this.slider)) {
+ this.slider = new Slider(this.domObj, this.knob, this.sliderOpts);
}
+ this.slider.set(this.options.startAt);
+ }
+}); // $Id: $
+/**
+ * Class: Jx.Formatter
+ *
+ * Extends: <Jx.Object>
+ *
+ * Base class used for specific implementations to coerce data into specific formats
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Formatter = new Class({
+
+ Extends: Jx.Object,
+
+ /**
+ * APIMethod: format
+ * Empty method that must be overridden by subclasses to provide
+ * the needed formatting functionality.
+ */
+ format: $empty
+});// $Id: $
+/**
+ * Class: Jx.Formatter.Number
+ *
+ * Extends: <Jx.Formatter>
+ *
+ * This class formats numbers. You can have it do the following
+ *
+ * o replace the decimal separator
+ * o use/add a thousands separator
+ * o change the precision (number of decimal places)
+ * o format negative numbers with parenthesis
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Formatter.Number = new Class({
+
+ Extends: Jx.Formatter,
+
+ options: {
+ /**
+ * Option: decimalSeparator
+ * Character to use as the decimal separator
+ */
+ decimalSeparator: '.',
+ /**
+ * Option: thousandSeparator
+ * Character to use as the thousands separator
+ */
+ thousandsSeparator: ',',
+ /**
+ * Option: precision
+ * The number of decimal places to round to
+ */
+ precision: 2,
+ /**
+ * Option: useParens
+ * Whether negative numbers should be done with parenthesis
+ */
+ useParens: true,
+ /**
+ * Option: useThousands
+ * Whether to use the thousands separator
+ */
+ useThousands: true
+ },
+ /**
+ * APIMethod: format
+ * Formats the provided number
+ *
+ * Parameters:
+ * value - the raw number to format
+ */
+ format : function (value) {
+ //first set the decimal
+ if (Jx.type(value) === 'string') {
+ //remove commas from the string
+ var p = value.split(',');
+ value = p.join('');
+ value = value.toFloat();
+ }
+ value = value.toFixed(this.options.precision);
- if (this.nodes && shouldDescend) {
- var that = this;
- this.nodes.each(function(n,i){
- n.update(false, i==that.nodes.length-1);
- });
+ //split on the decimalSeparator
+ var parts = value.split('.');
+ var dec = true;
+ if (parts.length === 1) {
+ dec = false;
}
+ //check for negative
+ var neg = false;
+ var main;
+ var ret = '';
+ if (parts[0].contains('-')) {
+ neg = true;
+ main = parts[0].substring(1, parts[0].length);
+ } else {
+ main = parts[0];
+ }
+
+ if (this.options.useThousands) {
+ var l = main.length;
+ var left = l % 3;
+ var j = 0;
+ for (var i = 0; i < l; i++) {
+ ret = ret + main.charAt(i);
+ if (i === left - 1 && i !== l - 1) {
+ ret = ret + this.options.thousandsSeparator;
+ } else if (i >= left) {
+ j++;
+ if (j === 3 && i !== l - 1) {
+ ret = ret + this.options.thousandsSeparator;
+ j = 0;
+ }
+ }
+
+ }
+ } else {
+ ret = parts[0];
+ }
+
+ if (dec) {
+ ret = ret + this.options.decimalSeparator + parts[1];
+ }
+ if (neg && this.options.useParens) {
+ ret = "(" + ret + ")";
+ } else if (neg && !this.options.useParens) {
+ ret = "-" + ret;
+ }
+
+ return ret;
+ }
+});// $Id: $
+/**
+ * Class: Jx.Formatter.Currency
+ *
+ * Extends: <Jx.Formatter.Number>
+ *
+ * This class formats numbers as US currency. It actually
+ * runs the value through Jx.Formatter.Number first and then
+ * updates the returned value as currency.
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Formatter.Currency = new Class({
+
+ Extends: Jx.Formatter.Number,
+
+ options: {
+ /**
+ * Option: sign
+ * The sign to use for this currency. Defaults to
+ * the US '$'.
+ */
+ sign: "$"
},
/**
- * Method: append
- * append a node at the end of the sub-tree
- *
+ * APIMethod: format
+ * Takes a number and formats it as currency.
+ *
* Parameters:
- * node - {Object} the node to append.
+ * value - the number to format
*/
- append : function( node ) {
- node.owner = this;
- this.nodes.push(node);
- this.subDomObj.appendChild( node.domObj );
- this.update(true);
- return this;
+ format: function (value) {
+
+ this.options.precision = 2;
+
+ value = this.parent(value);
+
+ //check for negative
+ var neg = false;
+ if (value.contains('(') || value.contains('-')) {
+ neg = true;
+ }
+
+ var ret;
+ if (neg && !this.options.useParens) {
+ ret = "-" + this.options.sign + value.substring(1, value.length);
+ } else {
+ ret = this.options.sign + value;
+ }
+
+ return ret;
+ }
+});// $Id: $
+/**
+ * Class: Jx.Formatter.Date
+ *
+ * Extends: <Jx.Formatter>
+ *
+ * This class formats dates using the mootools-more's
+ * Date extensions. See the -more docs for details of
+ * supported formats for parsing and formatting.
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Formatter.Date = new Class({
+
+ Extends: Jx.Formatter,
+
+ options: {
+ /**
+ * Option: format
+ * The format to use. See the mootools-more Date
+ * extension documentation for details on supported
+ * formats
+ */
+ format: '%B %d, %Y'
},
/**
- * Method: insert
- * insert a node after refNode. If refNode is null, insert at beginning
- *
+ * APIMethod: format
+ * Does the work of formatting dates
+ *
* Parameters:
- * node - {Object} the node to insert
- * refNode - {Object} the node to insert before
+ * value - the text to format
*/
- insert : function( node, refNode ) {
- node.owner = this;
- //if refNode is not supplied, insert at the beginning.
- if (!refNode) {
- this.nodes.unshift(node);
- //sanity check to make sure there is actually something there
- if (this.subDomObj.childNodes.length ==0) {
- this.subDomObj.appendChild(node.domObj);
+ format: function (value) {
+ var d = Date.parse(value);
+ return d.format(this.options.format);
+ }
+});// $Id: $
+/**
+ * Class: Jx.Formatter.Boolean
+ *
+ * Extends: <Jx.Formatter>
+ *
+ * This class formats boolean values. You supply the
+ * text values for true and false in the options.
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Formatter.Boolean = new Class({
+
+ Extends: Jx.Formatter,
+
+ options: {
+ /**
+ * Option: true
+ * The text to display for true values
+ */
+ 'true': 'Yes',
+ /**
+ * Option: false
+ * The text to display for false values
+ */
+ 'false': 'No'
+ },
+ /**
+ * APIMethod: format
+ * Takes a value, determines boolean equivalent and
+ * displays the appropriate text value.
+ *
+ * Parameters:
+ * value - the text to format
+ */
+ format : function (value) {
+ var b = false;
+ var t = Jx.type(value);
+ switch (t) {
+ case 'string':
+ if (value === 'true') {
+ b = true;
+ }
+ break;
+ case 'number':
+ if (value !== 0) {
+ b = true;
+ }
+ break;
+ case 'boolean':
+ b = value;
+ break;
+ default:
+ b = true;
+ }
+ return b ? this.options['true'] : this.options['false'];
+ }
+
+});// $Id: $
+/**
+ * Class: Jx.Formatter.Phone
+ *
+ * Extends: <Jx.Formatter>
+ *
+ * Formats data as phone numbers. Currently only US-style phone numbers
+ * are supported.
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Formatter.Phone = new Class({
+
+ Extends: Jx.Formatter,
+
+ options: {
+ /**
+ * Option: useParens
+ * Whether to use parenthesis () around the area code.
+ * Defaults to true
+ */
+ useParens: true,
+ /**
+ * Option: separator
+ * The character to use as a separator in the phone number.
+ * Defaults to a dash '-'.
+ */
+ separator: "-"
+ },
+ /**
+ * APIMethod: format
+ * Format the input as a phone number. This will strip all non-numeric
+ * characters and apply the current default formatting
+ *
+ * Parameters:
+ * value - the text to format
+ */
+ format : function (value) {
+ //first strip any non-numeric characters
+ var sep = this.options.separator;
+ var v = '' + value;
+ v = v.replace(/[^0-9]/g, '');
+
+ //now check the length. For right now, we only do US phone numbers
+ var ret = '';
+ if (v.length === 11) {
+ //do everything including the leading 1
+ ret = v.charAt(0);
+ v = v.substring(1);
+ }
+ if (v.length === 10) {
+ //do the area code
+ if (this.options.useParens) {
+ ret = ret + "(" + v.substring(0, 3) + ")";
} else {
- this.subDomObj.insertBefore(node.domObj, this.subDomObj.childNodes[0]);
+ ret = ret + sep + v.substring(0, 3) + sep;
}
- } else {
- //walk all nodes looking for the ref node. Track if it actually
- //happens so we can append if it fails.
- var b = false;
- for(var i=0;i<this.nodes.length;i++) {
- if (this.nodes[i] == refNode) {
- //increment to append after ref node. If this pushes us
- //past the end, it'll get appended below anyway
- i = i + 1;
- if (i < this.nodes.length) {
- this.nodes.splice(i, 0, node);
- this.subDomObj.insertBefore(node.domObj, this.subDomObj.childNodes[i]);
- b = true;
- break;
- }
- }
+ v = v.substring(3);
+ }
+ //do the rest of the number
+ ret = ret + v.substring(0, 3) + sep + v.substring(3);
+ return ret;
+ }
+});// $Id: $
+/**
+ * Class: Jx.Fieldset
+ *
+ * Extends: <Jx.Widget>
+ *
+ * This class represents a fieldset. It can be used to group fields together.
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ *
+ */
+Jx.Fieldset = new Class({
+
+ Extends : Jx.Widget,
+
+ options : {
+ /**
+ * Option: legend
+ * The text for the legend of a fieldset. Default is null
+ * or no legend.
+ */
+ legend : null,
+ /**
+ * Option: id
+ * The id to assign to this element
+ */
+ id : null,
+ /**
+ * Option: fieldsetClass
+ * A CSS class to assign to the fieldset. Useful for custom styling of
+ * the element
+ */
+ fieldsetClass : null,
+ /**
+ * Option: legendClass
+ * A CSS class to assign to the legend. Useful for custom styling of
+ * the element
+ */
+ legendClass : null,
+ /**
+ * Option: template
+ * a template for how this element should be rendered
+ */
+ template : '<fieldset class="jxFieldset"><legend><span class="jxFieldsetLegend"></span></legend></fieldset>',
+ /**
+ * Option: form
+ * The <Jx.Form> that this fieldset should be added to
+ */
+ form : null
+ },
+ /**
+ * Property: legend
+ * a holder for the legend Element
+ */
+ legend : null,
+
+ /**
+ * APIMethod: render
+ * Creates a fieldset.
+ */
+ render : function () {
+ this.parent();
+
+ this.id = this.options.id;
+
+ if ($defined(this.options.form)
+ && this.options.form instanceof Jx.Form) {
+ this.form = this.options.form;
+ }
+
+ var els = this.processTemplate(this.options.template, ['jxFieldset', 'jxFieldsetLegend']);
+
+ //FIELDSET
+ if (els.has('jxFieldset')) {
+ this.domObj = els.get('jxFieldset');
+ if ($defined(this.options.id)) {
+ this.domObj.set('id', this.options.id);
}
- //if the node wasn't inserted, it is because refNode didn't exist
- //and so the fallback is to just append the node.
- if (!b) {
- this.nodes.push(node);
- this.subDomObj.appendChild(node.domObj);
+ if ($defined(this.options.fieldsetClass)) {
+ this.domObj.addClass(this.options.fieldsetClass);
}
}
- this.update(true);
- return this;
+
+ if (els.has('jxFieldsetLegend')) {
+ this.legend = els.get('jxFieldsetLegend');
+ if ($defined(this.options.legend)) {
+ this.legend.set('html', this.options.legend);
+ if ($defined(this.options.legendClass)) {
+ this.legend.addClass(this.options.legendClass);
+ }
+ } else {
+ this.legend.destroy();
+ }
+ }
},
/**
- * Method: remove
- * remove the specified node from the tree
- *
+ * APIMethod: add
+ * Adds fields to this fieldset
+ *
* Parameters:
- * node - {Object} the node to remove
+ * pass as many fields to this method as you like. They should be
+ * <Jx.Field> objects
*/
- remove : function(node) {
- node.owner = null;
- for(var i=0;i<this.nodes.length;i++) {
- if (this.nodes[i] == node) {
- this.nodes.splice(i, 1);
- this.subDomObj.removeChild(this.subDomObj.childNodes[i]);
- break;
+ add : function () {
+ var field;
+ for (var x = 0; x < arguments.length; x++) {
+ field = arguments[x];
+ //add form to the field and field to the form if not already there
+ if (!$defined(field.form) && $defined(this.form)) {
+ field.form = this.form;
+ this.form.addField(field);
}
+ this.domObj.grab(field);
}
- this.update(true);
return this;
+ }
+});
+// $Id: $
+/**
+ * Class: Jx.Field.Check
+ *
+ * Extends: <Jx.Field>
+ *
+ * This class represents a radio input field.
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ *
+ */
+Jx.Field.Checkbox = new Class({
+
+ Extends : Jx.Field,
+
+ options : {
+ /**
+ * Option: template
+ * The template used for rendering this field
+ */
+ template : '<input class="jxInputCheck" type="checkbox" name="{name}"/><label class="jxInputLabel"></label><span class="jxInputTag"></span>',
+ /**
+ * Option: checked
+ * Whether this field is checked or not
+ */
+ checked : false,
+
+ labelSeparator: ''
},
/**
- * Method: replace
- * Replace a node with another node
- *
- * Parameters:
- * newNode - {Object} the node to put into the tree
- * refNode - {Object} the node to replace
- *
- * Returns:
- * {Boolean} true if the replacement was successful.
+ * Property: type
+ * The type of this field
*/
- replace: function( newNode, refNode ) {
- //walk all nodes looking for the ref node.
- var b = false;
- for(var i=0;i<this.nodes.length;i++) {
- if (this.nodes[i] == refNode) {
- if (i < this.nodes.length) {
- newNode.owner = this;
- this.nodes.splice(i, 1, newNode);
- this.subDomObj.replaceChild(newNode.domObj, refNode.domObj);
- return true;
+ type : 'Check',
+
+ /**
+ * APIMethod: render
+ * Creates a checkbox input field.
+ */
+ render : function () {
+ this.parent();
+
+ if ($defined(this.options.checked) && this.options.checked) {
+ if (Browser.Engine.trident) {
+ var parent = this.field.getParent();
+ var sibling;
+ if (parent) {
+ sibling = this.field.getPrevious();
}
+ this.field.setStyle('visibility','hidden');
+ this.field.inject($(document.body));
+ this.field.checked = true;
+ this.field.defaultChecked = true;
+ this.field.dispose();
+ this.field.setStyle('visibility','visible');
+ if (sibling) {
+ this.field.inject(sibling, 'after');
+ } else if (parent) {
+ this.field.inject(parent, 'top');
+ }
+ } else {
+ this.field.set("checked", "checked");
+ this.field.set("defaultChecked", "checked");
}
}
- return false;
},
/**
- * Method: clicked
- * handle the user clicking on this folder by expanding or
- * collapsing it.
- *
+ * APIMethod: setValue
+ * Sets the value property of the field
+ *
* Parameters:
- * e - {Event} the event object
+ * v - The value to set the field to, "checked" if it should be checked.
*/
- clicked : function(e) {
- if (this.options.open) {
- this.collapse();
+ setValue : function (v) {
+ if (v === 'checked') {
+ this.field.set('checked', "checked");
} else {
- this.expand();
+ this.field.erase('checked');
}
},
+
/**
- * Method: expand
- * Expands the folder
+ * APIMethod: getValue
+ * Returns the current value of the field. The field must be
+ * "checked" in order to return a value. Otherwise it returns null.
*/
- expand : function() {
- this.options.open = true;
- this.subDomObj.setStyle('display', 'block');
- this.update(true);
- this.fireEvent('disclosed', this);
+ getValue : function () {
+ if (this.field.get("checked")) {
+ return this.field.get("value");
+ } else {
+ return null;
+ }
},
+
/**
- * Method: collapse
- * Collapses the folder
+ * APIMethod: reset
+ * Sets the field back to the value passed in the original
+ * options. no IE hack is implemented because the field should
+ * already be in the DOM when this is called.
*/
- collapse : function() {
- this.options.open = false;
- this.subDomObj.setStyle('display', 'none');
- this.update(true);
- this.fireEvent('disclosed', this);
+ reset : function () {
+ if (this.options.checked) {
+ this.field.set('checked', "checked");
+ } else {
+ this.field.erase('checked');
+ }
+ }
+
+});
+// $Id: $
+/**
+ * Class: Jx.Field.Radio
+ *
+ * Extends: <Jx.Field>
+ *
+ * This class represents a radio input field.
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Field.Radio = new Class({
+
+ Extends: Jx.Field,
+
+ options: {
+ /**
+ * Option: template
+ * The template used to create this field
+ */
+ template: '<input class="jxInputRadio" type="radio" name="{name}"/><label class="jxInputLabel"></label><span class="jxInputTag"></span>',
+ /**
+ * Option: checked
+ * whether this radio button is checked or not
+ */
+ checked: false,
+
+ labelSeparator: ''
},
/**
- * Method: findChild
- * Get a reference to a child node by recursively searching the tree
+ * Property: type
+ * What kind of field this is
+ */
+ type: 'Radio',
+
+ /**
+ * APIMethod: render
+ * Creates a radiobutton input field.
+ */
+ render: function () {
+ this.parent();
+
+ if ($defined(this.options.checked) && this.options.checked) {
+ if (Browser.Engine.trident) {
+ var parent = this.field.getParent();
+ var sibling;
+ if (parent) {
+ sibling = this.field.getPrevious();
+ }
+ this.field.setStyle('visibility','hidden');
+ this.field.inject($(document.body));
+ this.field.checked = true;
+ this.field.defaultChecked = true;
+ this.field.dispose();
+ this.field.setStyle('visibility','visible');
+ if (sibling) {
+ this.field.inject(sibling, 'after');
+ } else if (parent) {
+ this.field.inject(parent, 'top');
+ }
+ } else {
+ this.field.set("checked", "checked");
+ this.field.set("defaultChecked", "checked");
+ }
+ }
+ },
+
+ /**
+ * APIMethod: setValue
+ * Sets the value property of the field
*
* Parameters:
- * path - {Array} an array of labels of nodes to search for
- *
- * Returns:
- * {Object} the node or null if the path was not found
+ * v - The value to set the field to, "checked" it should be checked.
*/
- findChild : function(path) {
- //path is empty - we are asking for this node
- if (path.length == 0)
- return this;
-
- //path has only one thing in it - looking for something in this folder
- if (path.length == 1)
- {
- for (var i=0; i<this.nodes.length; i++)
- {
- if (this.nodes[i].getName() == path[0])
- return this.nodes[i];
- }
+ setValue: function (v) {
+ if (v === 'checked') {
+ this.field.set('checked', "checked");
+ } else {
+ this.field.erase('checked');
+ }
+ },
+
+ /**
+ * APIMethod: getValue
+ * Returns the current value of the field. The field must be "checked"
+ * in order to return a value. Otherwise it returns null.
+ */
+ getValue: function () {
+ if (this.field.get("checked")) {
+ return this.field.get("value");
+ } else {
return null;
}
- //path has more than one thing in it, find a folder and descend into it
- var childName = path.shift();
- for (var i=0; i<this.nodes.length; i++)
- {
- if (this.nodes[i].getName() == childName && this.nodes[i].findChild)
- return this.nodes[i].findChild(path);
+ },
+
+ /**
+ * Method: reset
+ * Sets the field back to the value passed in the original
+ * options
+ */
+ reset: function () {
+ if (this.options.checked) {
+ this.field.set('checked', "checked");
+ } else {
+ this.field.erase('checked');
}
- return null;
}
-});// $Id: tree.js 424 2009-05-12 12:51:44Z pagameba $
+
+});
+
+
+
+
+// $Id: $
/**
- * Class: Jx.Tree
- *
- * Extends: Jx.TreeFolder
- *
- * Implements: <Jx.Addable>
- *
- * Jx.Tree displays hierarchical data in a tree structure of folders and nodes.
- *
+ * Class: Jx.Field.Select
+ *
+ * Extends: <Jx.Field>
+ *
+ * This class represents a form select field.
+ *
+ * These fields are rendered as below.
+ *
+ * (code)
+ * <div id='' class=''>
+ * <label for=''>A label for the field</label>
+ * <select id='' name=''>
+ * <option value='' selected=''>text</option>
+ * </select>
+ * </div>
+ * (end)
+ *
* Example:
* (code)
* (end)
*
- * Extends: <Jx.TreeFolder>
- *
* License:
- * Copyright (c) 2008, DM Solutions Group Inc.
+ * Copyright (c) 2009, Jon Bomgardner.
*
* This file is licensed under an MIT style license
+ *
*/
-Jx.Tree = new Class({
- Extends: Jx.TreeFolder,
- Implements: [Jx.Addable],
- Family: 'Jx.Tree',
+
+Jx.Field.Select = new Class({
+
+ Extends: Jx.Field,
+
+ options: {
+ /**
+ * Option: comboOpts
+ * Optional, defaults to null. if not null, this should be an array of objects
+ * formated like [{value:'', selected: true|false, text:''},...]
+ */
+ comboOpts: null,
+ /**
+ * Option: optGroups
+ * Optional, defaults to null. if not null this should be an array of objects
+ * defining option groups for this select. The comboOpts and optGroups options
+ * are mutually exclusive. optGroups will always be shown if defined.
+ *
+ * define them like [{name: '', options: [{value:'', selected: '', text: ''}...]},...]
+ */
+ optGroups: null,
+ /**
+ * Option: template
+ * The template for creating this select input
+ */
+ template: '<label class="jxInputLabel"></label><select class="jxInputSelect" name="{name}"></select><span class="jxInputTag"></span>'
+ },
/**
- * Constructor: Jx.Tree
- * Create a new instance of Jx.Tree
- *
- * Parameters:
- * options: options for <Jx.Addable>
+ * Property: type
+ * Indictes this type of field.
*/
- initialize : function( options ) {
- this.parent(options);
- this.subDomObj = new Element('ul',{
- 'class':'jxTreeRoot'
- });
+ type: 'Select',
+
+ /**
+ * APIMethod: render
+ * Creates a select field.
+ */
+ render: function () {
+ this.parent();
- this.nodes = [];
- this.isOpen = true;
-
- this.addable = this.subDomObj;
-
- if (this.options.parent) {
- this.addTo(this.options.parent);
+ if ($defined(this.options.optGroups)) {
+ this.options.optGroups.each(function(group){
+ var gr = new Element('optGroup');
+ gr.set('label',group.name);
+ group.options.each(function(option){
+ var opt = new Element('option', {
+ 'value': option.value,
+ 'html': option.text
+ });
+ if ($defined(option.selected) && option.selected) {
+ opt.set("selected", "selected");
+ }
+ gr.grab(opt);
+ },this);
+ this.field.grab(gr);
+ },this);
+ } else if ($defined(this.options.comboOpts)) {
+ this.options.comboOpts.each(function (item) {
+ var opt = new Element('option', {
+ 'value': item.value,
+ 'html': item.text
+ });
+ if ($defined(item.selected) && item.selected) {
+ opt.set("selected", "selected");
+ }
+ this.field.grab(opt);
+ }, this);
}
},
/**
- * Method: finalize
- * Clean up a Jx.Tree instance
+ * Method: setValue
+ * Sets the value property of the field
+ *
+ * Parameters:
+ * v - The value to set the field to.
*/
- finalize: function() {
- this.clear();
- this.subDomObj.parentNode.removeChild(this.subDomObj);
+ setValue: function (v) {
+ //loop through the options and set the one that matches v
+ $$(this.field.options).each(function (opt) {
+ if (opt.get('value') === v) {
+ document.id(opt).set("selected", true);
+ }
+ }, this);
},
+
/**
- * Method: clear
- * Clear the tree of all child nodes
+ * Method: getValue
+ * Returns the current value of the field.
*/
- clear: function() {
- for (var i=this.nodes.length-1; i>=0; i--) {
- this.subDomObj.removeChild(this.nodes[i].domObj);
- this.nodes[i].finalize();
- this.nodes.pop();
+ getValue: function () {
+ var index = this.field.selectedIndex;
+ //check for a set "value" attribute. If not there return the text
+ var ret = this.field.options[index].get("value");
+ if (!$defined(ret)) {
+ ret = this.field.options[index].get("text");
}
+ return ret;
+ }
+});// $Id: $
+/**
+ * Class: Jx.Field.Textarea
+ *
+ * Extends: <Jx.Field>
+ *
+ * This class represents a textarea field.
+ *
+ * These fields are rendered as below.
+ *
+ * (code)
+ * <div id='' class=''>
+ * <label for=''>A label for the field</label>
+ * <textarea id='' name='' rows='' cols=''>
+ * value/ext
+ * </textarea>
+ * </div>
+ * (end)
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ *
+ */
+Jx.Field.Textarea = new Class({
+
+ Extends: Jx.Field,
+
+ options: {
+ /**
+ * Option: rows
+ * the number of rows to show
+ */
+ rows: null,
+ /**
+ * Option: columns
+ * the number of columns to show
+ */
+ columns: null,
+ /**
+ * Option: template
+ * the template used to render this field
+ */
+ template: '<label class="jxInputLabel"></label><textarea class="jxInputTextarea" name="{name}"></textarea><span class="jxInputTag"></span>'
},
/**
- * Method: update
- * Update the CSS of the Tree's DOM element in case it has changed
- * position
- *
- * Parameters:
- * shouldDescend - {Boolean} propagate changes to child nodes?
+ * Property: type
+ * The type of field this is.
*/
- update: function(shouldDescend) {
- var bLast = true;
- if (this.subDomObj)
- {
- if (bLast) {
- this.subDomObj.removeClass('jxTreeNest');
- } else {
- this.subDomObj.addClass('jxTreeNest');
- }
+ type: 'Textarea',
+ /**
+ * Property: errorClass
+ * The class applied to error elements
+ */
+ errorClass: 'jxFormErrorTextarea',
+
+ /**
+ * APIMethod: render
+ * Creates the input.
+ */
+ render: function () {
+ this.parent();
+
+ if ($defined(this.options.rows)) {
+ this.field.set('rows', this.options.rows);
}
- if (this.nodes && shouldDescend) {
- this.nodes.each(function(n){n.update(false);});
+ if ($defined(this.options.columns)) {
+ this.field.set('cols', this.options.columns);
}
+
+ //TODO: Do we need to use OverText here as well??
+
+ }
+});/**
+ * Class: Jx.Field.Button
+ *
+ * Extends: <Jx.Field>
+ *
+ * This class represents a button.
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, DM Solutions Group
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Field.Button = new Class({
+
+ Extends: Jx.Field,
+
+ options: {
+ /**
+ * Option: buttonOptions
+ */
+ buttonOptions: {},
+ /**
+ * Option: template
+ * The template used to render this field
+ */
+ template: '<label class="jxInputLabel"></label><div class="jxInputButton"></div><span class="jxInputTag"></span>'
},
/**
- * Method: append
- * Append a node at the end of the sub-tree
- *
- * Parameters:
- * node - {Object} the node to append.
+ * Property: type
+ * The type of this field
*/
- append: function( node ) {
- node.owner = this;
- this.nodes.push(node);
- this.subDomObj.appendChild( node.domObj );
- this.update(true);
- return this;
+ type: 'Button',
+
+ processTemplate: function(template, classes, container) {
+ var h = this.parent(template, classes, container);
+ var b = new Jx.Button(this.options.buttonOptions);
+ var c = h.get('jxInputButton');
+ if (c) {
+ b.domObj.replaces(c);
+ }
+ return h;
}
-});
-
+
+});// $Id: $
+/**
+ * Class: Jx.Field.Password
+ *
+ * Extends: <Jx.Field.Text>
+ *
+ * This class represents a password input field.
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Field.Password = new Class({
+
+ Extends: Jx.Field,
+
+ options: {
+ template: '<label class="jxInputLabel" ></label><input class="jxInputPassword" type="password" name="{name}"/><span class="jxInputTag"></span>'
+ },
+
+ type: 'Password'
+});
\ No newline at end of file
More information about the fusion-commits
mailing list