summaryrefslogtreecommitdiff
path: root/frontend/beta/js/YUI-extensions
Unidiff
Diffstat (limited to 'frontend/beta/js/YUI-extensions') (more/less context) (ignore whitespace changes)
-rw-r--r--frontend/beta/js/YUI-extensions/Bench.js40
-rw-r--r--frontend/beta/js/YUI-extensions/CSS.js208
-rw-r--r--frontend/beta/js/YUI-extensions/CompositeElement.js140
-rw-r--r--frontend/beta/js/YUI-extensions/CustomTagReader.js40
-rw-r--r--frontend/beta/js/YUI-extensions/Date.js407
-rw-r--r--frontend/beta/js/YUI-extensions/DomHelper.js416
-rw-r--r--frontend/beta/js/YUI-extensions/Element.js2157
-rw-r--r--frontend/beta/js/YUI-extensions/EventManager.js456
-rw-r--r--frontend/beta/js/YUI-extensions/JSON.js132
-rw-r--r--frontend/beta/js/YUI-extensions/KeyMap.js135
-rw-r--r--frontend/beta/js/YUI-extensions/Layer.js246
-rw-r--r--frontend/beta/js/YUI-extensions/MixedCollection.js344
-rw-r--r--frontend/beta/js/YUI-extensions/State.js264
-rw-r--r--frontend/beta/js/YUI-extensions/UpdateManager.js484
-rw-r--r--frontend/beta/js/YUI-extensions/anim/Actor.js759
-rw-r--r--frontend/beta/js/YUI-extensions/anim/Animator.js482
-rw-r--r--frontend/beta/js/YUI-extensions/data/AbstractDataModel.js226
-rw-r--r--frontend/beta/js/YUI-extensions/data/DefaultDataModel.js339
-rw-r--r--frontend/beta/js/YUI-extensions/data/JSONDataModel.js81
-rw-r--r--frontend/beta/js/YUI-extensions/data/LoadableDataModel.js330
-rw-r--r--frontend/beta/js/YUI-extensions/data/Tree.js412
-rw-r--r--frontend/beta/js/YUI-extensions/data/XMLDataModel.js274
-rw-r--r--frontend/beta/js/YUI-extensions/dd/DragSource.js218
-rw-r--r--frontend/beta/js/YUI-extensions/dd/DragZone.js64
-rw-r--r--frontend/beta/js/YUI-extensions/dd/DropTarget.js45
-rw-r--r--frontend/beta/js/YUI-extensions/dd/DropZone.js81
-rw-r--r--frontend/beta/js/YUI-extensions/dd/Registry.js80
-rw-r--r--frontend/beta/js/YUI-extensions/dd/ScrollManager.js171
-rw-r--r--frontend/beta/js/YUI-extensions/dd/StatusProxy.js110
-rw-r--r--frontend/beta/js/YUI-extensions/grid/AbstractColumnModel.js131
-rw-r--r--frontend/beta/js/YUI-extensions/grid/DefaultColumnModel.js325
-rw-r--r--frontend/beta/js/YUI-extensions/grid/EditorGrid.js16
-rw-r--r--frontend/beta/js/YUI-extensions/grid/EditorSelectionModel.js182
-rw-r--r--frontend/beta/js/YUI-extensions/grid/Grid.js965
-rw-r--r--frontend/beta/js/YUI-extensions/grid/GridDD.js101
-rw-r--r--frontend/beta/js/YUI-extensions/grid/GridView.js790
-rw-r--r--frontend/beta/js/YUI-extensions/grid/PagedGridView.js194
-rw-r--r--frontend/beta/js/YUI-extensions/grid/SelectionModel.js445
-rw-r--r--frontend/beta/js/YUI-extensions/grid/editor/CellEditor.js91
-rw-r--r--frontend/beta/js/YUI-extensions/grid/editor/CheckboxEditor.js60
-rw-r--r--frontend/beta/js/YUI-extensions/grid/editor/DateEditor.js268
-rw-r--r--frontend/beta/js/YUI-extensions/grid/editor/NumberEditor.js166
-rw-r--r--frontend/beta/js/YUI-extensions/grid/editor/SelectEditor.js37
-rw-r--r--frontend/beta/js/YUI-extensions/grid/editor/TextEditor.js110
-rw-r--r--frontend/beta/js/YUI-extensions/layout/BasicLayoutRegion.js265
-rw-r--r--frontend/beta/js/YUI-extensions/layout/BorderLayout.js281
-rw-r--r--frontend/beta/js/YUI-extensions/layout/BorderLayoutRegions.js207
-rw-r--r--frontend/beta/js/YUI-extensions/layout/ContentPanels.js325
-rw-r--r--frontend/beta/js/YUI-extensions/layout/LayoutManager.js135
-rw-r--r--frontend/beta/js/YUI-extensions/layout/LayoutRegion.js496
-rw-r--r--frontend/beta/js/YUI-extensions/layout/LayoutStateManager.js68
-rw-r--r--frontend/beta/js/YUI-extensions/layout/SplitLayoutRegion.js282
-rw-r--r--frontend/beta/js/YUI-extensions/tree/AsyncTreeNode.js58
-rw-r--r--frontend/beta/js/YUI-extensions/tree/TreeDragZone.js43
-rw-r--r--frontend/beta/js/YUI-extensions/tree/TreeDropZone.js228
-rw-r--r--frontend/beta/js/YUI-extensions/tree/TreeFilter.js105
-rw-r--r--frontend/beta/js/YUI-extensions/tree/TreeLoader.js107
-rw-r--r--frontend/beta/js/YUI-extensions/tree/TreeNode.js300
-rw-r--r--frontend/beta/js/YUI-extensions/tree/TreeNodeUI.js452
-rw-r--r--frontend/beta/js/YUI-extensions/tree/TreePanel.js213
-rw-r--r--frontend/beta/js/YUI-extensions/tree/TreeSelectionModel.js195
-rw-r--r--frontend/beta/js/YUI-extensions/tree/TreeSorter.js49
-rw-r--r--frontend/beta/js/YUI-extensions/widgets/BasicDialog.js1046
-rw-r--r--frontend/beta/js/YUI-extensions/widgets/Button.js185
-rw-r--r--frontend/beta/js/YUI-extensions/widgets/DatePicker.js344
-rw-r--r--frontend/beta/js/YUI-extensions/widgets/InlineEditor.js216
-rw-r--r--frontend/beta/js/YUI-extensions/widgets/MessageBox.js230
-rw-r--r--frontend/beta/js/YUI-extensions/widgets/QuickTips.js311
-rw-r--r--frontend/beta/js/YUI-extensions/widgets/Resizable.js586
-rw-r--r--frontend/beta/js/YUI-extensions/widgets/SplitBar.js468
-rw-r--r--frontend/beta/js/YUI-extensions/widgets/TabPanel.js756
-rw-r--r--frontend/beta/js/YUI-extensions/widgets/TaskPanel.js0
-rw-r--r--frontend/beta/js/YUI-extensions/widgets/TemplateView.js766
-rw-r--r--frontend/beta/js/YUI-extensions/widgets/Toolbar.js296
-rw-r--r--frontend/beta/js/YUI-extensions/yutil.js637
75 files changed, 22672 insertions, 0 deletions
diff --git a/frontend/beta/js/YUI-extensions/Bench.js b/frontend/beta/js/YUI-extensions/Bench.js
new file mode 100644
index 0000000..6921131
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/Bench.js
@@ -0,0 +1,40 @@
1// @deprecated
2// Use YAHOO.timer() instead
3YAHOO.ext.util.Bench = function(){
4 this.timers = {};
5 this.lastKey = null;
6};
7YAHOO.ext.util.Bench.prototype = {
8 start : function(key){
9 this.lastKey = key;
10 this.timers[key] = {};
11 this.timers[key].startTime = new Date().getTime();
12 },
13
14 stop : function(key){
15 key = key || this.lastKey;
16 this.timers[key].endTime = new Date().getTime();
17 },
18
19 getElapsed : function(key){
20 key = key || this.lastKey;
21 return this.timers[key].endTime - this.timers[key].startTime;
22 },
23
24 toString : function(html){
25 var results = "";
26 for(var key in this.timers){
27 if(typeof this.timers[key] != 'function'){
28 results += key + ":\t" + (this.getElapsed(key) / 1000) + " seconds\n";
29 }
30 }
31 if(html){
32 results = results.replace("\n", '<br>');
33 }
34 return results;
35 },
36
37 show : function(){
38 alert(this.toString());
39 }
40};
diff --git a/frontend/beta/js/YUI-extensions/CSS.js b/frontend/beta/js/YUI-extensions/CSS.js
new file mode 100644
index 0000000..4fba37c
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/CSS.js
@@ -0,0 +1,208 @@
1/**
2 * @class YAHOO.ext.util.CSS
3 * Class for manipulating CSS Rules
4 * @singleton
5 */
6YAHOO.ext.util.CSS = new function(){
7 var rules = null;
8
9 var toCamel = function(property) {
10 var convert = function(prop) {
11 var test = /(-[a-z])/i.exec(prop);
12 return prop.replace(RegExp.$1, RegExp.$1.substr(1).toUpperCase());
13 };
14 while(property.indexOf('-') > -1) {
15 property = convert(property);
16 }
17 return property;
18 };
19
20 /**
21 * Very simple dynamic creation of stylesheets from a text blob of rules.
22 * @param {String} cssText The text containing the css rules
23 * @return {StyleSheet}
24 */
25 this.createStyleSheet = function(cssText){
26 var ss;
27 if(YAHOO.ext.util.Browser.isIE){
28 ss = document.createStyleSheet();
29 ss.cssText = cssText;
30 }else{
31 var head = document.getElementsByTagName("head")[0];
32 var rules = document.createElement('style');
33 rules.setAttribute('type', 'text/css');
34 try{
35 rules.appendChild(document.createTextNode(cssText));
36 }catch(e){
37 rules.cssText = cssText;
38 }
39 head.appendChild(rules);
40 ss = document.styleSheets[document.styleSheets.length-1];
41 }
42 this.cacheStyleSheet(ss);
43 return ss;
44 };
45
46 this.removeStyleSheet = function(id){
47 var existing = document.getElementById(id);
48 if(existing){
49 existing.parentNode.removeChild(existing);
50 }
51 };
52
53 this.swapStyleSheet = function(id, url){
54 this.removeStyleSheet(id);
55 var ss = document.createElement('link');
56 ss.setAttribute('rel', 'stylesheet');
57 ss.setAttribute('type', 'text/css');
58 ss.setAttribute('id', id);
59 ss.setAttribute('href', url);
60 document.getElementsByTagName("head")[0].appendChild(ss);
61 };
62
63 /**
64 * Refresh the rule cache if you have dynamically added stylesheets
65 * @return {Object} An object (hash) of rules indexed by selector
66 */
67 this.refreshCache = function(){
68 return this.getRules(true);
69 };
70
71 this.cacheStyleSheet = function(ss){
72 try{// try catch for cross domain access issue
73 var ssRules = ss.cssRules || ss.rules;
74 for(var j = ssRules.length-1; j >= 0; --j){
75 rules[ssRules[j].selectorText] = ssRules[j];
76 }
77 }catch(e){}
78 };
79
80 /**
81 * Gets all css rules for the document
82 * @param {Boolean} refreshCache true to refresh the internal cache
83 * @return {Object} An object (hash) of rules indexed by selector
84 */
85 this.getRules = function(refreshCache){
86 if(rules == null || refreshCache){
87 rules = {};
88 var ds = document.styleSheets;
89 for(var i =0, len = ds.length; i < len; i++){
90 try{
91 this.cacheStyleSheet(ds[i]);
92 }catch(e){}
93 }
94 }
95 return rules;
96 };
97
98 /**
99 * Gets an an individual CSS rule by selector(s)
100 * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
101 * @param {Boolean} refreshCache true to refresh the internal cache
102 * @return {CSSRule} The CSS rule or null if one is not found
103 */
104 this.getRule = function(selector, refreshCache){
105 var rs = this.getRules(refreshCache);
106 if(!(selector instanceof Array)){
107 return rs[selector];
108 }
109 for(var i = 0; i < selector.length; i++){
110 if(rs[selector[i]]){
111 return rs[selector[i]];
112 }
113 }
114 return null;
115 };
116
117
118 /**
119 * Updates a rule property
120 * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
121 * @param {String} property The css property
122 * @param {String} value The new value for the property
123 * @return {Boolean} true if a rule was found and updated
124 */
125 this.updateRule = function(selector, property, value){
126 if(!(selector instanceof Array)){
127 var rule = this.getRule(selector);
128 if(rule){
129 rule.style[toCamel(property)] = value;
130 return true;
131 }
132 }else{
133 for(var i = 0; i < selector.length; i++){
134 if(this.updateRule(selector[i], property, value)){
135 return true;
136 }
137 }
138 }
139 return false;
140 };
141
142 /**
143 * Applies a rule to an element without adding the class
144 * @param {HTMLElement} el The element
145 * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
146 * @return {Boolean} true if a rule was found and applied
147 */
148 this.apply = function(el, selector){
149 if(!(selector instanceof Array)){
150 var rule = this.getRule(selector);
151 if(rule){
152 var s = rule.style;
153 for(var key in s){
154 if(typeof s[key] != 'function'){
155 if(s[key] && String(s[key]).indexOf(':') < 0 && s[key] != 'false'){
156 try{el.style[key] = s[key];}catch(e){}
157 }
158 }
159 }
160 return true;
161 }
162 }else{
163 for(var i = 0; i < selector.length; i++){
164 if(this.apply(el, selector[i])){
165 return true;
166 }
167 }
168 }
169 return false;
170 };
171
172 this.applyFirst = function(el, id, selector){
173 var selectors = [
174 '#' + id + ' ' + selector,
175 selector
176 ];
177 return this.apply(el, selectors);
178 };
179
180 this.revert = function(el, selector){
181 if(!(selector instanceof Array)){
182 var rule = this.getRule(selector);
183 if(rule){
184 for(key in rule.style){
185 if(rule.style[key] && String(rule.style[key]).indexOf(':') < 0 && rule.style[key] != 'false'){
186 try{el.style[key] = '';}catch(e){}
187 }
188 }
189 return true;
190 }
191 }else{
192 for(var i = 0; i < selector.length; i++){
193 if(this.revert(el, selector[i])){
194 return true;
195 }
196 }
197 }
198 return false;
199 };
200
201 this.revertFirst = function(el, id, selector){
202 var selectors = [
203 '#' + id + ' ' + selector,
204 selector
205 ];
206 return this.revert(el, selectors);
207 };
208}();
diff --git a/frontend/beta/js/YUI-extensions/CompositeElement.js b/frontend/beta/js/YUI-extensions/CompositeElement.js
new file mode 100644
index 0000000..7b9c875
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/CompositeElement.js
@@ -0,0 +1,140 @@
1/**
2 * @class YAHOO.ext.CompositeElement
3 * Standard composite class. Creates a YAHOO.ext.Element for every element in the collection.
4 * <br><br>
5 * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of YAHOO.ext.Element. All YAHOO.ext.Element
6 * actions will be performed on all the elements in this collection.</b>
7 * <br><br>
8 * All methods return <i>this</i> and can be chained.
9 <pre><code>
10 var els = getEls('#some-el div.some-class');
11 // or
12 var els = YAHOO.ext.Element.select('#some-el div.some-class');
13 els.setWidth(100); // all elements become 100 width
14 els.hide(true); // all elements fade out and hide
15 // or
16 els.setWidth(100).hide(true);
17 </code></pre>
18 */
19YAHOO.ext.CompositeElement = function(els){
20 this.elements = [];
21 this.addElements(els);
22};
23YAHOO.ext.CompositeElement.prototype = {
24 isComposite: true,
25 addElements : function(els){
26 if(!els) return this;
27 var yels = this.elements;
28 var index = yels.length-1;
29 for(var i = 0, len = els.length; i < len; i++) {
30 yels[++index] = getEl(els[i], true);
31 }
32 return this;
33 },
34 invoke : function(fn, args){
35 var els = this.elements;
36 for(var i = 0, len = els.length; i < len; i++) {
37 YAHOO.ext.Element.prototype[fn].apply(els[i], args);
38 }
39 return this;
40 },
41 /**
42 * Adds elements to this composite.
43 * @param {String/Array} els A string CSS selector, an array of elements or an element
44 * @return {CompositeElement} this
45 */
46 add : function(els){
47 if(typeof els == 'string'){
48 this.addElements(YAHOO.ext.Element.selectorFunction(string));
49 }else if(els instanceof Array){
50 this.addElements(els);
51 }else{
52 this.addElements([els]);
53 }
54 return this;
55 },
56 /**
57 * Calls the passed function passing (el, this, index) for each element in this composite.
58 * @param {Function} fn The function to call
59 * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
60 * @return {CompositeElement} this
61 */
62 each : function(fn, scope){
63 var els = this.elements;
64 for(var i = 0, len = els.length; i < len; i++){
65 fn.call(scope || els[i], els[i], this, i);
66 }
67 return this;
68 }
69};
70/**
71 * @class YAHOO.ext.CompositeElementLite
72 * @extends YAHOO.ext.CompositeElement
73 * Flyweight composite class. Reuses the same YAHOO.ext.Element for element operations.
74 * <br><br>
75 * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of YAHOO.ext.Element. All YAHOO.ext.Element
76 * actions will be performed on all the elements in this collection.</b>
77 */
78YAHOO.ext.CompositeElementLite = function(els){
79 YAHOO.ext.CompositeElementLite.superclass.constructor.call(this, els);
80 this.el = YAHOO.ext.Element.get(this.elements[0], true);
81};
82YAHOO.extendX(YAHOO.ext.CompositeElementLite, YAHOO.ext.CompositeElement, {
83 addElements : function(els){
84 if(els){
85 this.elements = this.elements.concat(els);
86 }
87 return this;
88 },
89 invoke : function(fn, args){
90 var els = this.elements;
91 var el = this.el;
92 for(var i = 0, len = els.length; i < len; i++) {
93 el.dom = els[i];
94 YAHOO.ext.Element.prototype[fn].apply(el, args);
95 }
96 return this;
97 }
98});
99YAHOO.ext.CompositeElement.createCall = function(proto, fnName){
100 if(!proto[fnName]){
101 proto[fnName] = function(){
102 return this.invoke(fnName, arguments);
103 };
104 }
105};
106for(var fnName in YAHOO.ext.Element.prototype){
107 if(typeof YAHOO.ext.Element.prototype[fnName] == 'function'){
108 YAHOO.ext.CompositeElement.createCall(YAHOO.ext.CompositeElement.prototype, fnName);
109 }
110}
111if(typeof cssQuery == 'function'){// Dean Edwards cssQuery
112 YAHOO.ext.Element.selectorFunction = cssQuery;
113}else if(typeof document.getElementsBySelector == 'function'){ // Simon Willison's getElementsBySelector
114 YAHOO.ext.Element.selectorFunction = document.getElementsBySelector.createDelegate(document);
115}
116/**
117 * @member YAHOO.ext.Element
118* Selects elements based on the passed CSS selector to enable working on them as 1.
119* @param {String/Array} selector The CSS selector or an array of elements
120* @param {Boolean} unique (optional) true to create a unique YAHOO.ext.Element for each element (defaults to a shared flyweight object)
121* @return {CompositeElementLite/CompositeElement}
122* @method @static
123*/
124YAHOO.ext.Element.select = function(selector, unique){
125 var els;
126 if(typeof selector == 'string'){
127 els = YAHOO.ext.Element.selectorFunction(selector);
128 }else if(selector instanceof Array){
129 els = selector;
130 }else{
131 throw 'Invalid selector';
132 }
133 if(unique === true){
134 return new YAHOO.ext.CompositeElement(els);
135 }else{
136 return new YAHOO.ext.CompositeElementLite(els);
137 }
138};
139
140var getEls = YAHOO.ext.Element.select;
diff --git a/frontend/beta/js/YUI-extensions/CustomTagReader.js b/frontend/beta/js/YUI-extensions/CustomTagReader.js
new file mode 100644
index 0000000..12faaa9
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/CustomTagReader.js
@@ -0,0 +1,40 @@
1/**
2 * @class YAHOO.ext.CustomTagReader
3 * Utility class to normalize reading of custom tags across browsers.
4 */
5YAHOO.ext.CustomTagReader = function(namespace){
6 this.namespace = namespace;
7};
8YAHOO.ext.CustomTagReader.prototype = {
9 getAttribute : function(el, name, defaultValue){
10 return (this.useNS ?
11 v = el.getAttributeNS(this.namespace, name) : null) ||
12 el.getAttribute(this.namespace+':'+name) ||
13 el.getAttribute(name);
14 },
15
16 getElements : function(tagName, targetEl){
17 targetEl = targetEl || document.body;
18 var els;
19 if(this.useNS){ // no namespaces in IE
20 els = targetEl.getElementsByTagNameNS(this.namespace, tagName);
21 }
22 if(!els || els.length < 1){ // ie6, firefox 1.5, firefox 2 depending on doc type
23 els = targetEl.getElementsByTagName(this.namespace+':'+tagName);
24 }
25 if(!els || els.length < 1){ // everyone else
26 els = targetEl.getElementsByTagName(tagName);
27 }
28 return els;
29 },
30
31 eachElement : function(tagName, targetEl, fn, scope){
32 var els = this.getElements(tagName, targetEl);
33 for(var i = 0, len = els.length; i < len; i++) {
34 var el = els[i];
35 fn.call(scope || el, el);
36 }
37 },
38
39 useNS : (!YAHOO.ext.util.Browser.isIE && document.getElementsByTagNameNS) ? true : false
40};
diff --git a/frontend/beta/js/YUI-extensions/Date.js b/frontend/beta/js/YUI-extensions/Date.js
new file mode 100644
index 0000000..f79c8a5
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/Date.js
@@ -0,0 +1,407 @@
1/*
2 * All the Date functions below are the excellent work of Baron Schwartz
3 * They generate precompiled functions from date formats instead of parsing and processing
4 * the format everytime you do something with a date.
5 */
6/** @ignore */
7Date.parseFunctions = {count:0};
8/** @ignore */
9Date.parseRegexes = [];
10/** @ignore */
11Date.formatFunctions = {count:0};
12
13/**
14 * Formats a date given to the supplied format - the format syntax is the same as <a href="http://www.php.net/date">PHP's date() function</a>.
15 */
16Date.prototype.dateFormat = function(format) {
17 if (Date.formatFunctions[format] == null) {
18 Date.createNewFormat(format);
19 }
20 var func = Date.formatFunctions[format];
21 return this[func]();
22};
23
24/**
25 * Same as {@link #dateFormat}
26 */
27Date.prototype.format = Date.prototype.dateFormat;
28
29/** @ignore */
30Date.createNewFormat = function(format) {
31 var funcName = "format" + Date.formatFunctions.count++;
32 Date.formatFunctions[format] = funcName;
33 var code = "Date.prototype." + funcName + " = function(){return ";
34 var special = false;
35 var ch = '';
36 for (var i = 0; i < format.length; ++i) {
37 ch = format.charAt(i);
38 if (!special && ch == "\\") {
39 special = true;
40 }
41 else if (special) {
42 special = false;
43 code += "'" + String.escape(ch) + "' + ";
44 }
45 else {
46 code += Date.getFormatCode(ch);
47 }
48 }
49 eval(code.substring(0, code.length - 3) + ";}");
50};
51
52/** @ignore */
53Date.getFormatCode = function(character) {
54 switch (character) {
55 case "d":
56 return "String.leftPad(this.getDate(), 2, '0') + ";
57 case "D":
58 return "Date.dayNames[this.getDay()].substring(0, 3) + ";
59 case "j":
60 return "this.getDate() + ";
61 case "l":
62 return "Date.dayNames[this.getDay()] + ";
63 case "S":
64 return "this.getSuffix() + ";
65 case "w":
66 return "this.getDay() + ";
67 case "z":
68 return "this.getDayOfYear() + ";
69 case "W":
70 return "this.getWeekOfYear() + ";
71 case "F":
72 return "Date.monthNames[this.getMonth()] + ";
73 case "m":
74 return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
75 case "M":
76 return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
77 case "n":
78 return "(this.getMonth() + 1) + ";
79 case "t":
80 return "this.getDaysInMonth() + ";
81 case "L":
82 return "(this.isLeapYear() ? 1 : 0) + ";
83 case "Y":
84 return "this.getFullYear() + ";
85 case "y":
86 return "('' + this.getFullYear()).substring(2, 4) + ";
87 case "a":
88 return "(this.getHours() < 12 ? 'am' : 'pm') + ";
89 case "A":
90 return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
91 case "g":
92 return "((this.getHours() %12) ? this.getHours() % 12 : 12) + ";
93 case "G":
94 return "this.getHours() + ";
95 case "h":
96 return "String.leftPad((this.getHours() %12) ? this.getHours() % 12 : 12, 2, '0') + ";
97 case "H":
98 return "String.leftPad(this.getHours(), 2, '0') + ";
99 case "i":
100 return "String.leftPad(this.getMinutes(), 2, '0') + ";
101 case "s":
102 return "String.leftPad(this.getSeconds(), 2, '0') + ";
103 case "O":
104 return "this.getGMTOffset() + ";
105 case "T":
106 return "this.getTimezone() + ";
107 case "Z":
108 return "(this.getTimezoneOffset() * -60) + ";
109 default:
110 return "'" + String.escape(character) + "' + ";
111 };
112};
113
114/**
115 * Parses a date given the supplied format - the format syntax is the same as <a href="http://www.php.net/date">PHP's date() function</a>.
116 */
117Date.parseDate = function(input, format) {
118 if (Date.parseFunctions[format] == null) {
119 Date.createParser(format);
120 }
121 var func = Date.parseFunctions[format];
122 return Date[func](input);
123};
124
125/** @ignore */
126Date.createParser = function(format) {
127 var funcName = "parse" + Date.parseFunctions.count++;
128 var regexNum = Date.parseRegexes.length;
129 var currentGroup = 1;
130 Date.parseFunctions[format] = funcName;
131
132 var code = "Date." + funcName + " = function(input){\n"
133 + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1;\n"
134 + "var d = new Date();\n"
135 + "y = d.getFullYear();\n"
136 + "m = d.getMonth();\n"
137 + "d = d.getDate();\n"
138 + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
139 + "if (results && results.length > 0) {"
140 var regex = "";
141
142 var special = false;
143 var ch = '';
144 for (var i = 0; i < format.length; ++i) {
145 ch = format.charAt(i);
146 if (!special && ch == "\\") {
147 special = true;
148 }
149 else if (special) {
150 special = false;
151 regex += String.escape(ch);
152 }
153 else {
154 obj = Date.formatCodeToRegex(ch, currentGroup);
155 currentGroup += obj.g;
156 regex += obj.s;
157 if (obj.g && obj.c) {
158 code += obj.c;
159 }
160 }
161 }
162
163 code += "if (y > 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
164 + "{return new Date(y, m, d, h, i, s);}\n"
165 + "else if (y > 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
166 + "{return new Date(y, m, d, h, i);}\n"
167 + "else if (y > 0 && m >= 0 && d > 0 && h >= 0)\n"
168 + "{return new Date(y, m, d, h);}\n"
169 + "else if (y > 0 && m >= 0 && d > 0)\n"
170 + "{return new Date(y, m, d);}\n"
171 + "else if (y > 0 && m >= 0)\n"
172 + "{return new Date(y, m);}\n"
173 + "else if (y > 0)\n"
174 + "{return new Date(y);}\n"
175 + "}return null;}";
176
177 Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
178 eval(code);
179};
180
181/** @ignore */
182Date.formatCodeToRegex = function(character, currentGroup) {
183 switch (character) {
184 case "D":
185 return {g:0,
186 c:null,
187 s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
188 case "j":
189 case "d":
190 return {g:1,
191 c:"d = parseInt(results[" + currentGroup + "], 10);\n",
192 s:"(\\d{1,2})"};
193 case "l":
194 return {g:0,
195 c:null,
196 s:"(?:" + Date.dayNames.join("|") + ")"};
197 case "S":
198 return {g:0,
199 c:null,
200 s:"(?:st|nd|rd|th)"};
201 case "w":
202 return {g:0,
203 c:null,
204 s:"\\d"};
205 case "z":
206 return {g:0,
207 c:null,
208 s:"(?:\\d{1,3})"};
209 case "W":
210 return {g:0,
211 c:null,
212 s:"(?:\\d{2})"};
213 case "F":
214 return {g:1,
215 c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
216 s:"(" + Date.monthNames.join("|") + ")"};
217 case "M":
218 return {g:1,
219 c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
220 s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
221 case "n":
222 case "m":
223 return {g:1,
224 c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
225 s:"(\\d{1,2})"};
226 case "t":
227 return {g:0,
228 c:null,
229 s:"\\d{1,2}"};
230 case "L":
231 return {g:0,
232 c:null,
233 s:"(?:1|0)"};
234 case "Y":
235 return {g:1,
236 c:"y = parseInt(results[" + currentGroup + "], 10);\n",
237 s:"(\\d{4})"};
238 case "y":
239 return {g:1,
240 c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
241 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
242 s:"(\\d{1,2})"};
243 case "a":
244 return {g:1,
245 c:"if (results[" + currentGroup + "] == 'am') {\n"
246 + "if (h == 12) { h = 0; }\n"
247 + "} else { if (h < 12) { h += 12; }}",
248 s:"(am|pm)"};
249 case "A":
250 return {g:1,
251 c:"if (results[" + currentGroup + "] == 'AM') {\n"
252 + "if (h == 12) { h = 0; }\n"
253 + "} else { if (h < 12) { h += 12; }}",
254 s:"(AM|PM)"};
255 case "g":
256 case "G":
257 case "h":
258 case "H":
259 return {g:1,
260 c:"h = parseInt(results[" + currentGroup + "], 10);\n",
261 s:"(\\d{1,2})"};
262 case "i":
263 return {g:1,
264 c:"i = parseInt(results[" + currentGroup + "], 10);\n",
265 s:"(\\d{2})"};
266 case "s":
267 return {g:1,
268 c:"s = parseInt(results[" + currentGroup + "], 10);\n",
269 s:"(\\d{2})"};
270 case "O":
271 return {g:0,
272 c:null,
273 s:"[+-]\\d{4}"};
274 case "T":
275 return {g:0,
276 c:null,
277 s:"[A-Z]{3}"};
278 case "Z":
279 return {g:0,
280 c:null,
281 s:"[+-]\\d{1,5}"};
282 default:
283 return {g:0,
284 c:null,
285 s:String.escape(character)};
286 }
287};
288
289Date.prototype.getTimezone = function() {
290 return this.toString().replace(
291 /^.*? ([A-Z]{3}) [0-9]{4}.*$/, "$1").replace(
292 /^.*?\(([A-Z])[a-z]+ ([A-Z])[a-z]+ ([A-Z])[a-z]+\)$/, "$1$2$3");
293};
294
295Date.prototype.getGMTOffset = function() {
296 return (this.getTimezoneOffset() > 0 ? "-" : "+")
297 + String.leftPad(Math.floor(this.getTimezoneOffset() / 60), 2, "0")
298 + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
299};
300
301Date.prototype.getDayOfYear = function() {
302 var num = 0;
303 Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
304 for (var i = 0; i < this.getMonth(); ++i) {
305 num += Date.daysInMonth[i];
306 }
307 return num + this.getDate() - 1;
308};
309
310Date.prototype.getWeekOfYear = function() {
311 // Skip to Thursday of this week
312 var now = this.getDayOfYear() + (4 - this.getDay());
313 // Find the first Thursday of the year
314 var jan1 = new Date(this.getFullYear(), 0, 1);
315 var then = (7 - jan1.getDay() + 4);
316 return String.leftPad(((now - then) / 7) + 1, 2, "0");
317};
318
319Date.prototype.isLeapYear = function() {
320 var year = this.getFullYear();
321 return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
322};
323
324Date.prototype.getFirstDayOfMonth = function() {
325 var day = (this.getDay() - (this.getDate() - 1)) % 7;
326 return (day < 0) ? (day + 7) : day;
327};
328
329Date.prototype.getLastDayOfMonth = function() {
330 var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
331 return (day < 0) ? (day + 7) : day;
332};
333
334Date.prototype.getDaysInMonth = function() {
335 Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
336 return Date.daysInMonth[this.getMonth()];
337};
338
339/** @ignore */
340Date.prototype.getSuffix = function() {
341 switch (this.getDate()) {
342 case 1:
343 case 21:
344 case 31:
345 return "st";
346 case 2:
347 case 22:
348 return "nd";
349 case 3:
350 case 23:
351 return "rd";
352 default:
353 return "th";
354 }
355};
356
357/** @ignore */
358Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
359
360/**
361 * Override these values for international dates, for example...
362 * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
363 */
364Date.monthNames =
365 ["January",
366 "February",
367 "March",
368 "April",
369 "May",
370 "June",
371 "July",
372 "August",
373 "September",
374 "October",
375 "November",
376 "December"];
377
378/**
379 * Override these values for international dates, for example...
380 * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
381 */
382Date.dayNames =
383 ["Sunday",
384 "Monday",
385 "Tuesday",
386 "Wednesday",
387 "Thursday",
388 "Friday",
389 "Saturday"];
390
391/** @ignore */
392Date.y2kYear = 50;
393
394/** @ignore */
395Date.monthNumbers = {
396 Jan:0,
397 Feb:1,
398 Mar:2,
399 Apr:3,
400 May:4,
401 Jun:5,
402 Jul:6,
403 Aug:7,
404 Sep:8,
405 Oct:9,
406 Nov:10,
407 Dec:11};
diff --git a/frontend/beta/js/YUI-extensions/DomHelper.js b/frontend/beta/js/YUI-extensions/DomHelper.js
new file mode 100644
index 0000000..d9e7484
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/DomHelper.js
@@ -0,0 +1,416 @@
1/**
2 * @class YAHOO.ext.DomHelper
3 * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4 * For more information see <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
5 * @singleton
6 */
7YAHOO.ext.DomHelper = new function(){
8 /**@private*/
9 var d = document;
10 var tempTableEl = null;
11 /** True to force the use of DOM instead of html fragments @type Boolean */
12 this.useDom = false;
13 var emptyTags = /^(?:base|basefont|br|frame|hr|img|input|isindex|link|meta|nextid|range|spacer|wbr|audioscope|area|param|keygen|col|limittext|spot|tab|over|right|left|choose|atop|of)$/i;
14 /**
15 * Applies a style specification to an element
16 * @param {String/HTMLElement} el The element to apply styles to
17 * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
18 * a function which returns such a specification.
19 */
20 this.applyStyles = function(el, styles){
21 if(styles){
22 var D = YAHOO.util.Dom;
23 if (typeof styles == "string"){
24 var re = /\s?([a-z\-]*)\:([^;]*);?/gi;
25 var matches;
26 while ((matches = re.exec(styles)) != null){
27 D.setStyle(el, matches[1], matches[2]);
28 }
29 }else if (typeof styles == "object"){
30 for (var style in styles){
31 D.setStyle(el, style, styles[style]);
32 }
33 }else if (typeof styles == "function"){
34 YAHOO.ext.DomHelper.applyStyles(el, styles.call());
35 }
36 }
37 };
38
39 // build as innerHTML where available
40 /** @ignore */
41 var createHtml = function(o){
42 var b = '';
43 b += '<' + o.tag;
44 for(var attr in o){
45 if(attr == 'tag' || attr == 'children' || attr == 'html' || typeof o[attr] == 'function') continue;
46 if(attr == 'style'){
47 var s = o['style'];
48 if(typeof s == 'function'){
49 s = s.call();
50 }
51 if(typeof s == 'string'){
52 b += ' style="' + s + '"';
53 }else if(typeof s == 'object'){
54 b += ' style="';
55 for(var key in s){
56 if(typeof s[key] != 'function'){
57 b += key + ':' + s[key] + ';';
58 }
59 }
60 b += '"';
61 }
62 }else{
63 if(attr == 'cls'){
64 b += ' class="' + o['cls'] + '"';
65 }else if(attr == 'htmlFor'){
66 b += ' for="' + o['htmlFor'] + '"';
67 }else{
68 b += ' ' + attr + '="' + o[attr] + '"';
69 }
70 }
71 }
72 if(emptyTags.test(o.tag)){
73 b += ' />';
74 }else{
75 b += '>';
76 if(o.children){
77 for(var i = 0, len = o.children.length; i < len; i++) {
78 b += createHtml(o.children[i], b);
79 }
80 }
81 if(o.html){
82 b += o.html;
83 }
84 b += '</' + o.tag + '>';
85 }
86 return b;
87 }
88
89 // build as dom
90 /** @ignore */
91 var createDom = function(o, parentNode){
92 var el = d.createElement(o.tag);
93 var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
94 for(var attr in o){
95 if(attr == 'tag' || attr == 'children' || attr == 'html' || attr == 'style' || typeof o[attr] == 'function') continue;
96 if(attr=='cls'){
97 el.className = o['cls'];
98 }else{
99 if(useSet) el.setAttribute(attr, o[attr]);
100 else el[attr] = o[attr];
101 }
102 }
103 YAHOO.ext.DomHelper.applyStyles(el, o.style);
104 if(o.children){
105 for(var i = 0, len = o.children.length; i < len; i++) {
106 createDom(o.children[i], el);
107 }
108 }
109 if(o.html){
110 el.innerHTML = o.html;
111 }
112 if(parentNode){
113 parentNode.appendChild(el);
114 }
115 return el;
116 };
117
118 /**
119 * @ignore
120 * Nasty code for IE's broken table implementation
121 */
122 var insertIntoTable = function(tag, where, el, html){
123 if(!tempTableEl){
124 tempTableEl = document.createElement('div');
125 }
126 var node;
127 if(tag == 'table' || tag == 'tbody'){
128 tempTableEl.innerHTML = '<table><tbody>'+html+'</tbody></table>';
129 node = tempTableEl.firstChild.firstChild.firstChild;
130 }else{
131 tempTableEl.innerHTML = '<table><tbody><tr>'+html+'</tr></tbody></table>';
132 node = tempTableEl.firstChild.firstChild.firstChild.firstChild;
133 }
134 if(where == 'beforebegin'){
135 el.parentNode.insertBefore(node, el);
136 return node;
137 }else if(where == 'afterbegin'){
138 el.insertBefore(node, el.firstChild);
139 return node;
140 }else if(where == 'beforeend'){
141 el.appendChild(node);
142 return node;
143 }else if(where == 'afterend'){
144 el.parentNode.insertBefore(node, el.nextSibling);
145 return node;
146 }
147 }
148
149 /**
150 * Inserts an HTML fragment into the Dom
151 * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
152 * @param {HTMLElement} el The context element
153 * @param {String} html The HTML fragmenet
154 * @return {HTMLElement} The new node
155 */
156 this.insertHtml = function(where, el, html){
157 where = where.toLowerCase();
158 if(el.insertAdjacentHTML){
159 var tag = el.tagName.toLowerCase();
160 if(tag == 'table' || tag == 'tbody' || tag == 'tr'){
161 return insertIntoTable(tag, where, el, html);
162 }
163 switch(where){
164 case 'beforebegin':
165 el.insertAdjacentHTML(where, html);
166 return el.previousSibling;
167 case 'afterbegin':
168 el.insertAdjacentHTML(where, html);
169 return el.firstChild;
170 case 'beforeend':
171 el.insertAdjacentHTML(where, html);
172 return el.lastChild;
173 case 'afterend':
174 el.insertAdjacentHTML(where, html);
175 return el.nextSibling;
176 }
177 throw 'Illegal insertion point -> "' + where + '"';
178 }
179 var range = el.ownerDocument.createRange();
180 var frag;
181 switch(where){
182 case 'beforebegin':
183 range.setStartBefore(el);
184 frag = range.createContextualFragment(html);
185 el.parentNode.insertBefore(frag, el);
186 return el.previousSibling;
187 case 'afterbegin':
188 if(el.firstChild){ // faster
189 range.setStartBefore(el.firstChild);
190 }else{
191 range.selectNodeContents(el);
192 range.collapse(true);
193 }
194 frag = range.createContextualFragment(html);
195 el.insertBefore(frag, el.firstChild);
196 return el.firstChild;
197 case 'beforeend':
198 if(el.lastChild){
199 range.setStartAfter(el.lastChild); // faster
200 }else{
201 range.selectNodeContents(el);
202 range.collapse(false);
203 }
204 frag = range.createContextualFragment(html);
205 el.appendChild(frag);
206 return el.lastChild;
207 case 'afterend':
208 range.setStartAfter(el);
209 frag = range.createContextualFragment(html);
210 el.parentNode.insertBefore(frag, el.nextSibling);
211 return el.nextSibling;
212 }
213 throw 'Illegal insertion point -> "' + where + '"';
214 };
215
216 /**
217 * Creates new Dom element(s) and inserts them before el
218 * @param {String/HTMLElement/Element} el The context element
219 * @param {Object} o The Dom object spec (and children)
220 * @param {<i>Boolean</i>} returnElement (optional) true to return a YAHOO.ext.Element
221 * @return {HTMLElement} The new node
222 */
223 this.insertBefore = function(el, o, returnElement){
224 el = el.dom ? el.dom : YAHOO.util.Dom.get(el);
225 var newNode;
226 if(this.useDom){
227 newNode = createDom(o, null);
228 el.parentNode.insertBefore(newNode, el);
229 }else{
230 var html = createHtml(o);
231 newNode = this.insertHtml('beforeBegin', el, html);
232 }
233 return returnElement ? YAHOO.ext.Element.get(newNode, true) : newNode;
234 };
235
236 /**
237 * Creates new Dom element(s) and inserts them after el
238 * @param {String/HTMLElement/Element} el The context element
239 * @param {Object} o The Dom object spec (and children)
240 * @param {<i>Boolean</i>} returnElement (optional) true to return a YAHOO.ext.Element
241 * @return {HTMLElement} The new node
242 */
243 this.insertAfter = function(el, o, returnElement){
244 el = el.dom ? el.dom : YAHOO.util.Dom.get(el);
245 var newNode;
246 if(this.useDom){
247 newNode = createDom(o, null);
248 el.parentNode.insertBefore(newNode, el.nextSibling);
249 }else{
250 var html = createHtml(o);
251 newNode = this.insertHtml('afterEnd', el, html);
252 }
253 return returnElement ? YAHOO.ext.Element.get(newNode, true) : newNode;
254 };
255
256 /**
257 * Creates new Dom element(s) and appends them to el
258 * @param {String/HTMLElement/Element} el The context element
259 * @param {Object} o The Dom object spec (and children)
260 * @param {<i>Boolean</i>} returnElement (optional) true to return a YAHOO.ext.Element
261 * @return {HTMLElement} The new node
262 */
263 this.append = function(el, o, returnElement){
264 el = el.dom ? el.dom : YAHOO.util.Dom.get(el);
265 var newNode;
266 if(this.useDom){
267 newNode = createDom(o, null);
268 el.appendChild(newNode);
269 }else{
270 var html = createHtml(o);
271 newNode = this.insertHtml('beforeEnd', el, html);
272 }
273 return returnElement ? YAHOO.ext.Element.get(newNode, true) : newNode;
274 };
275
276 /**
277 * Creates new Dom element(s) and overwrites the contents of el with them
278 * @param {String/HTMLElement/Element} el The context element
279 * @param {Object} o The Dom object spec (and children)
280 * @param {<i>Boolean</i>} returnElement (optional) true to return a YAHOO.ext.Element
281 * @return {HTMLElement} The new node
282 */
283 this.overwrite = function(el, o, returnElement){
284 el = el.dom ? el.dom : YAHOO.util.Dom.get(el);
285 el.innerHTML = createHtml(o);
286 return returnElement ? YAHOO.ext.Element.get(el.firstChild, true) : el.firstChild;
287 };
288
289 /**
290 * Creates a new YAHOO.ext.DomHelper.Template from the Dom object spec
291 * @param {Object} o The Dom object spec (and children)
292 * @return {YAHOO.ext.DomHelper.Template} The new template
293 */
294 this.createTemplate = function(o){
295 var html = createHtml(o);
296 return new YAHOO.ext.DomHelper.Template(html);
297 };
298}();
299
300/**
301* @class YAHOO.ext.DomHelper.Template
302* Represents an HTML fragment template.
303* For more information see <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
304* <br>
305* <b>This class is also available as YAHOO.ext.Template</b>.
306* @constructor
307* @param {String/Array} html The HTML fragment or an array of fragments to join('') or multiple arguments to join('')
308*/
309YAHOO.ext.DomHelper.Template = function(html){
310 if(html instanceof Array){
311 html = html.join('');
312 }else if(arguments.length > 1){
313 html = Array.prototype.join.call(arguments, '');
314 }
315 /**@private*/
316 this.html = html;
317};
318YAHOO.ext.DomHelper.Template.prototype = {
319 /**
320 * Returns an HTML fragment of this template with the specified values applied
321 * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
322 * @return {String}
323 */
324 applyTemplate : function(values){
325 if(this.compiled){
326 return this.compiled(values);
327 }
328 var empty = '';
329 var fn = function(match, index){
330 if(typeof values[index] != 'undefined'){
331 return values[index];
332 }else{
333 return empty;
334 }
335 }
336 return this.html.replace(this.re, fn);
337 },
338
339 /**
340 * The regular expression used to match template variables
341 * @type RegExp
342 * @property
343 */
344 re : /\{([\w|-]+)\}/g,
345
346 /**
347 * Compiles the template into an internal function, eliminating the RegEx overhead
348 */
349 compile : function(){
350 var body = ["this.compiled = function(values){ return ['"];
351 body.push(this.html.replace(this.re, "', values['$1'], '"));
352 body.push("'].join('');};");
353 eval(body.join(''));
354 return this;
355 },
356
357 /**
358 * Applies the supplied values to the template and inserts the new node(s) before el
359 * @param {String/HTMLElement/Element} el The context element
360 * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
361 * @param {<i>Boolean</i>} returnElement (optional) true to return a YAHOO.ext.Element
362 * @return {HTMLElement} The new node
363 */
364 insertBefore: function(el, values, returnElement){
365 el = el.dom ? el.dom : YAHOO.util.Dom.get(el);
366 var newNode = YAHOO.ext.DomHelper.insertHtml('beforeBegin', el, this.applyTemplate(values));
367 return returnElement ? YAHOO.ext.Element.get(newNode, true) : newNode;
368 },
369
370 /**
371 * Applies the supplied values to the template and inserts the new node(s) after el
372 * @param {String/HTMLElement/Element} el The context element
373 * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
374 * @param {<i>Boolean</i>} returnElement (optional) true to return a YAHOO.ext.Element
375 * @return {HTMLElement} The new node
376 */
377 insertAfter : function(el, values, returnElement){
378 el = el.dom ? el.dom : YAHOO.util.Dom.get(el);
379 var newNode = YAHOO.ext.DomHelper.insertHtml('afterEnd', el, this.applyTemplate(values));
380 return returnElement ? YAHOO.ext.Element.get(newNode, true) : newNode;
381 },
382
383 /**
384 * Applies the supplied values to the template and append the new node(s) to el
385 * @param {String/HTMLElement/Element} el The context element
386 * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
387 * @param {<i>Boolean</i>} returnElement (optional) true to return a YAHOO.ext.Element
388 * @return {HTMLElement} The new node
389 */
390 append : function(el, values, returnElement){
391 el = el.dom ? el.dom : YAHOO.util.Dom.get(el);
392 var newNode = YAHOO.ext.DomHelper.insertHtml('beforeEnd', el, this.applyTemplate(values));
393 return returnElement ? YAHOO.ext.Element.get(newNode, true) : newNode;
394 },
395
396 /**
397 * Applies the supplied values to the template and overwrites the content of el with the new node(s)
398 * @param {String/HTMLElement/Element} el The context element
399 * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
400 * @param {<i>Boolean</i>} returnElement (optional) true to return a YAHOO.ext.Element
401 * @return {HTMLElement} The new node
402 */
403 overwrite : function(el, values, returnElement){
404 el = el.dom ? el.dom : YAHOO.util.Dom.get(el);
405 el.innerHTML = '';
406 var newNode = YAHOO.ext.DomHelper.insertHtml('beforeEnd', el, this.applyTemplate(values));
407 return returnElement ? YAHOO.ext.Element.get(newNode, true) : newNode;
408 }
409};
410/**
411 * Alias for applyTemplate
412 * @method
413 */
414YAHOO.ext.DomHelper.Template.prototype.apply = YAHOO.ext.DomHelper.Template.prototype.applyTemplate;
415
416YAHOO.ext.Template = YAHOO.ext.DomHelper.Template;
diff --git a/frontend/beta/js/YUI-extensions/Element.js b/frontend/beta/js/YUI-extensions/Element.js
new file mode 100644
index 0000000..4019923
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/Element.js
@@ -0,0 +1,2157 @@
1/**
2 * @class YAHOO.ext.Element
3 * Wraps around a DOM element and provides convenient access to Yahoo
4 * UI library functionality (and more).<br><br>
5 * Usage:<br>
6 * <pre><code>
7 * var el = YAHOO.ext.Element.get('myElementId');
8 * // or the shorter
9 * var el = getEl('myElementId');
10 * </code></pre>
11 * Using YAHOO.ext.Element.get() instead of calling the constructor directly ensures you get the same object
12 * each call instead of constructing a new one.<br><br>
13 * For working with collections of Elements, see <a href="YAHOO.ext.CompositeElement.html">YAHOO.ext.CompositeElement</a>
14 * @requires YAHOO.util.Dom
15 * @requires YAHOO.util.Event
16 * @requires YAHOO.util.CustomEvent
17 * @requires YAHOO.util.Anim (optional) to support animation
18 * @requires YAHOO.util.Motion (optional) to support animation
19 * @requires YAHOO.util.Easing (optional) to support animation
20 * @constructor Create a new Element directly.
21 * @param {String/HTMLElement} element
22 * @param {<i>Boolean</i>} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
23 */
24YAHOO.ext.Element = function(element, forceNew){
25 var dom = typeof element == 'string' ?
26 document.getElementById(element) : element;
27 if(!dom){ // invalid id/element
28 return null;
29 }
30 if(!forceNew && YAHOO.ext.Element.cache[dom.id]){ // element object already exists
31 return YAHOO.ext.Element.cache[dom.id];
32 }
33 /**
34 * The DOM element
35 * @type HTMLElement
36 */
37 this.dom = dom;
38
39 /**
40 * The DOM element ID
41 * @type String
42 */
43 this.id = dom.id;
44
45 /**
46 * The element's default display mode @type String
47 */
48 this.originalDisplay = YAHOO.util.Dom.getStyle(dom, 'display') || '';
49 if(this.autoDisplayMode){
50 if(this.originalDisplay == 'none'){
51 this.setVisibilityMode(YAHOO.ext.Element.DISPLAY);
52 }
53 }
54 if(this.originalDisplay == 'none'){
55 this.originalDisplay = '';
56 }
57}
58
59YAHOO.ext.Element.prototype = {
60 visibilityMode : 1,
61 /**
62 * The default unit to append to CSS values where a unit isn't provided (Defaults to px).
63 * @type String
64 */
65 defaultUnit : 'px',
66 /**
67 * Sets the elements visibility mode. When setVisible() is called it
68 * will use this to determine whether to set the visibility or the display property.
69 * @param visMode Element.VISIBILITY or Element.DISPLAY
70 * @return {YAHOO.ext.Element} this
71 */
72 setVisibilityMode : function(visMode){
73 this.visibilityMode = visMode;
74 return this;
75 },
76 /**
77 * Convenience method for setVisibilityMode(Element.DISPLAY)
78 * @param {String} display (optional) What to set display to when visible
79 * @return {YAHOO.ext.Element} this
80 */
81 enableDisplayMode : function(display){
82 this.setVisibilityMode(YAHOO.ext.Element.DISPLAY);
83 if(typeof display != 'undefined') this.originalDisplay = display;
84 return this;
85 },
86
87 /**
88 * Perform Yahoo UI animation on this element.
89 * @param {Object} args The YUI animation control args
90 * @param {<i>Float</i>} duration (optional) How long the animation lasts. (Defaults to .35 seconds)
91 * @param {<i>Function</i>} onComplete (optional) Function to call when animation completes.
92 * @param {<i>Function</i>} easing (optional) YAHOO.util.Easing method to use. (Defaults to YAHOO.util.Easing.easeBoth)
93 * @param {<i>Function</i>} animType (optional) YAHOO.util.Anim subclass to use. For example: YAHOO.util.Motion
94 * @return {YAHOO.ext.Element} this
95 */
96 animate : function(args, duration, onComplete, easing, animType, stopAnims){
97 this.anim(args, duration, onComplete, easing, animType);
98 return this;
99 },
100
101 /**
102 * @private Internal animation call
103 */
104 anim : function(args, duration, onComplete, easing, animType){
105 animType = animType || YAHOO.util.Anim;
106 var anim = new animType(this.dom, args, duration || .35,
107 easing || YAHOO.util.Easing.easeBoth);
108 if(onComplete){
109 anim.onComplete.subscribe(function(){
110 if(typeof onComplete == 'function'){
111 onComplete.call(this);
112 }else if(onComplete instanceof Array){
113 for(var i = 0; i < onComplete.length; i++){
114 var fn = onComplete[i];
115 if(fn) fn.call(this);
116 }
117 }
118 }, this, true);
119 }
120 anim.animate();
121 return anim;
122 },
123
124 /**
125 * Scrolls this element into view within the passed container.
126 * @param {<i>String/HTMLElement/Element</i>} container (optional) The container element to scroll (defaults to document.body)
127 * @return {YAHOO.ext.Element} this
128 */
129 scrollIntoView : function(container){
130 var c = getEl(container || document.body, true);
131 var cp = c.getStyle('position');
132 var restorePos = false;
133 if(cp != 'relative' && cp != 'absolute'){
134 c.setStyle('position', 'relative');
135 restorePos = true;
136 }
137 var el = this.dom;
138 var childTop = parseInt(el.offsetTop, 10);
139 var childBottom = childTop + el.offsetHeight;
140 var containerTop = parseInt(c.dom.scrollTop, 10); // parseInt for safari bug
141 var containerBottom = containerTop + c.dom.clientHeight;
142 if(childTop < containerTop){
143 c.dom.scrollTop = childTop;
144 }else if(childBottom > containerBottom){
145 c.dom.scrollTop = childBottom-c.dom.clientHeight;
146 }
147 if(restorePos){
148 c.setStyle('position', cp);
149 }
150 return this;
151 },
152
153 /**
154 * Measures the elements content height and updates height to match. Note, this function uses setTimeout and
155 * the new height may not be available immediately.
156 * @param {<i>Boolean</i>} animate (optional) Animate the transition (Default is false)
157 * @param {<i>Float</i>} duration (optional) Length of the animation. (Defaults to .35 seconds)
158 * @param {<i>Function</i>} onComplete (optional) Function to call when animation completes.
159 * @param {<i>Function</i>} easing (optional) YAHOO.util.Easing method to use. (Defaults to YAHOO.util.Easing.easeOut for hiding or YAHOO.util.Easing.easeIn for showing)
160 * @return {YAHOO.ext.Element} this
161 */
162 autoHeight : function(animate, duration, onComplete, easing){
163 var oldHeight = this.getHeight();
164 this.clip();
165 this.setHeight(1); // force clipping
166 setTimeout(function(){
167 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
168 if(!animate){
169 this.setHeight(height);
170 this.unclip();
171 if(typeof onComplete == 'function'){
172 onComplete();
173 }
174 }else{
175 this.setHeight(oldHeight); // restore original height
176 this.setHeight(height, animate, duration, function(){
177 this.unclip();
178 if(typeof onComplete == 'function') onComplete();
179 }.createDelegate(this), easing);
180 }
181 }.createDelegate(this), 0);
182 return this;
183 },
184
185 contains : function(el){
186 if(!el){return false;}
187 return YAHOO.util.Dom.isAncestor(this.dom, el.dom ? el.dom : el);
188 },
189
190 /**
191 * Checks whether the element is currently visible using both visibility and display properties.
192 * @param {<i>Boolean</i>} deep True to walk the dom and see if parent elements are hidden.
193 * @return {Boolean} true if the element is currently visible
194 */
195 isVisible : function(deep) {
196 var vis = YAHOO.util.Dom.getStyle(this.dom, 'visibility') != 'hidden'
197 && YAHOO.util.Dom.getStyle(this.dom, 'display') != 'none';
198 if(!deep || !vis){
199 return vis;
200 }
201 var p = this.dom.parentNode;
202 while(p && p.tagName.toLowerCase() != 'body'){
203 if(YAHOO.util.Dom.getStyle(p, 'visibility') == 'hidden' || YAHOO.util.Dom.getStyle(p, 'display') == 'none'){
204 return false;
205 }
206 p = p.parentNode;
207 }
208 return true;
209 },
210
211 /**
212 * Selects child nodes based on the passed CSS selector (the selector should not contain an id)
213 * @param {String} selector The CSS selector
214 * @param {Boolean} unique true to create a unique YAHOO.ext.Element for each child (defaults to a shared flyweight object)
215 * @return {CompositeElement/CompositeElementLite} The composite element
216 */
217 select : function(selector, unique){
218 return YAHOO.ext.Element.select('#' + this.dom.id + ' ' + selector, unique);
219 },
220
221 /**
222 * Initializes a YAHOO.util.DD object for this element.
223 * @param {String} group The group the DD object is member of
224 * @param {Object} config The DD config object
225 * @param {Object} overrides An object containing methods to override/implement on the DD object
226 * @return {YAHOO.util.DD} The DD object
227 */
228 initDD : function(group, config, overrides){
229 var dd = new YAHOO.util.DD(YAHOO.util.Dom.generateId(this.dom), group, config);
230 return YAHOO.ext.util.Config.apply(dd, overrides);
231 },
232
233 /**
234 * Initializes a YAHOO.util.DDProxy object for this element.
235 * @param {String} group The group the DDProxy object is member of
236 * @param {Object} config The DDProxy config object
237 * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
238 * @return {YAHOO.util.DDProxy} The DDProxy object
239 */
240 initDDProxy : function(group, config, overrides){
241 var dd = new YAHOO.util.DDProxy(YAHOO.util.Dom.generateId(this.dom), group, config);
242 return YAHOO.ext.util.Config.apply(dd, overrides);
243 },
244
245 /**
246 * Initializes a YAHOO.util.DDTarget object for this element.
247 * @param {String} group The group the DDTarget object is member of
248 * @param {Object} config The DDTarget config object
249 * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
250 * @return {YAHOO.util.DDTarget} The DDTarget object
251 */
252 initDDTarget : function(group, config, overrides){
253 var dd = new YAHOO.util.DDTarget(YAHOO.util.Dom.generateId(this.dom), group, config);
254 return YAHOO.ext.util.Config.apply(dd, overrides);
255 },
256
257 /**
258 * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
259 * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
260 * @param {Boolean} visible Whether the element is visible
261 * @param {<i>Boolean</i>} animate (optional) Fade the element in or out (Default is false)
262 * @param {<i>Float</i>} duration (optional) How long the fade effect lasts. (Defaults to .35 seconds)
263 * @param {<i>Function</i>} onComplete (optional) Function to call when animation completes.
264 * @param {<i>Function</i>} easing (optional) YAHOO.util.Easing method to use. (Defaults to YAHOO.util.Easing.easeOut for hiding or YAHOO.util.Easing.easeIn for showing)
265 * @return {YAHOO.ext.Element} this
266 */
267 setVisible : function(visible, animate, duration, onComplete, easing){
268 //if(this.isVisible() == visible) return; // nothing to do
269 if(!animate || !YAHOO.util.Anim){
270 if(this.visibilityMode == YAHOO.ext.Element.DISPLAY){
271 this.setDisplayed(visible);
272 }else{
273 YAHOO.util.Dom.setStyle(this.dom, 'visibility', visible ? 'visible' : 'hidden');
274 }
275 }else{
276 // make sure they can see the transition
277 this.setOpacity(visible?0:1);
278 YAHOO.util.Dom.setStyle(this.dom, 'visibility', 'visible');
279 if(this.visibilityMode == YAHOO.ext.Element.DISPLAY){
280 this.setDisplayed(true);
281 }
282 var args = {opacity: { from: (visible?0:1), to: (visible?1:0) }};
283 var anim = new YAHOO.util.Anim(this.dom, args, duration || .35,
284 easing || (visible ? YAHOO.util.Easing.easeIn : YAHOO.util.Easing.easeOut));
285 anim.onComplete.subscribe((function(){
286 if(this.visibilityMode == YAHOO.ext.Element.DISPLAY){
287 this.setDisplayed(visible);
288 }else{
289 YAHOO.util.Dom.setStyle(this.dom, 'visibility', visible ? 'visible' : 'hidden');
290 }
291 }).createDelegate(this));
292 if(onComplete){
293 anim.onComplete.subscribe(onComplete);
294 }
295 anim.animate();
296 }
297 return this;
298 },
299
300 /**
301 * Returns true if display is not "none"
302 * @return {Boolean}
303 */
304 isDisplayed : function() {
305 return YAHOO.util.Dom.getStyle(this.dom, 'display') != 'none';
306 },
307
308 /**
309 * Toggles the elements visibility or display, depending on visibility mode.
310 * @param {<i>Boolean</i>} animate (optional) Fade the element in or out (Default is false)
311 * @param {<i>float</i>} duration (optional) How long the fade effect lasts. (Defaults to .35 seconds)
312 * @param {<i>Function</i>} onComplete (optional) Function to call when animation completes.
313 * @param {<i>Function</i>} easing (optional) YAHOO.util.Easing method to use. (Defaults to YAHOO.util.Easing.easeOut for hiding or YAHOO.util.Easing.easeIn for showing)
314 * @return {YAHOO.ext.Element} this
315 */
316 toggle : function(animate, duration, onComplete, easing){
317 this.setVisible(!this.isVisible(), animate, duration, onComplete, easing);
318 return this;
319 },
320
321 /**
322 * Sets the css display. Uses originalDisplay if value is a boolean true.
323 * @param {Boolean} value Boolean to display the element using it's default display or a string to set the display directly
324 * @return {YAHOO.ext.Element} this
325 */
326 setDisplayed : function(value) {
327 if(typeof value == 'boolean'){
328 value = value ? this.originalDisplay : 'none';
329 }
330 YAHOO.util.Dom.setStyle(this.dom, 'display', value);
331 return this;
332 },
333
334 /**
335 * Tries to focus the element. Any exceptions are caught.
336 * @return {YAHOO.ext.Element} this
337 */
338 focus : function() {
339 try{
340 this.dom.focus();
341 }catch(e){}
342 return this;
343 },
344
345 /**
346 * Tries to blur the element. Any exceptions are caught.
347 * @return {YAHOO.ext.Element} this
348 */
349 blur : function() {
350 try{
351 this.dom.blur();
352 }catch(e){}
353 return this;
354 },
355
356 /**
357 * Add a CSS class to the element.
358 * @param {String/Array} className The CSS class to add or an array of classes
359 * @return {YAHOO.ext.Element} this
360 */
361 addClass : function(className){
362 if(className instanceof Array){
363 for(var i = 0, len = className.length; i < len; i++) {
364 this.addClass(className[i]);
365 }
366 }else{
367 if(!this.hasClass(className)){
368 this.dom.className = this.dom.className + ' ' + className;
369 }
370 }
371 return this;
372 },
373
374 /**
375 * Adds the passed className to this element and removes the class from all siblings
376 * @param {String} className The className to add
377 * @return {YAHOO.ext.Element} this
378 */
379 radioClass : function(className){
380 var siblings = this.dom.parentNode.childNodes;
381 for(var i = 0; i < siblings.length; i++) {
382 var s = siblings[i];
383 if(s.nodeType == 1){
384 YAHOO.util.Dom.removeClass(s, className);
385 }
386 }
387 this.addClass(className);
388 return this;
389 },
390 /**
391 * Removes a CSS class from the element.
392 * @param {String/Array} className The CSS class to remove or an array of classes
393 * @return {YAHOO.ext.Element} this
394 */
395 removeClass : function(className){
396 if(!className){
397 return this;
398 }
399 if(className instanceof Array){
400 for(var i = 0, len = className.length; i < len; i++) {
401 this.removeClass(className[i]);
402 }
403 }else{
404 var re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', 'g');
405 var c = this.dom.className;
406 if(re.test(c)){
407 this.dom.className = c.replace(re, ' ');
408 }
409 }
410 return this;
411 },
412
413 /**
414 * Toggles (adds or removes) the passed class.
415 * @param {String} className
416 * @return {YAHOO.ext.Element} this
417 */
418 toggleClass : function(className){
419 if(this.hasClass(className)){
420 this.removeClass(className);
421 }else{
422 this.addClass(className);
423 }
424 return this;
425 },
426
427 /**
428 * Checks if a CSS class is in use by the element.
429 * @param {String} className The CSS class to check
430 * @return {Boolean} true or false
431 */
432 hasClass : function(className){
433 var re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)');
434 return re.test(this.dom.className);
435 },
436
437 /**
438 * Replaces a CSS class on the element with another.
439 * @param {String} oldClassName The CSS class to replace
440 * @param {String} newClassName The replacement CSS class
441 * @return {YAHOO.ext.Element} this
442 */
443 replaceClass : function(oldClassName, newClassName){
444 this.removeClass(oldClassName);
445 this.addClass(newClassName);
446 return this;
447 },
448
449 /**
450 * Normalizes currentStyle and ComputedStyle.
451 * @param {String} property The style property whose value is returned.
452 * @return {String} The current value of the style property for this element.
453 */
454 getStyle : function(name){
455 return YAHOO.util.Dom.getStyle(this.dom, name);
456 },
457
458 /**
459 * Wrapper for setting style properties, also takes single object parameter of multiple styles
460 * @param {String/Object} property The style property to be set or an object of multiple styles.
461 * @param {String} val (optional) The value to apply to the given property or null if an object was passed.
462 * @return {YAHOO.ext.Element} this
463 */
464 setStyle : function(name, value){
465 if(typeof name == 'string'){
466 YAHOO.util.Dom.setStyle(this.dom, name, value);
467 }else{
468 var D = YAHOO.util.Dom;
469 for(var style in name){
470 if(typeof name[style] != 'function'){
471 D.setStyle(this.dom, style, name[style]);
472 }
473 }
474 }
475 return this;
476 },
477
478 /**
479 * More flexible version of {@link #setStyle} for setting style properties.
480 * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
481 * a function which returns such a specification.
482 * @return {YAHOO.ext.Element} this
483 */
484 applyStyles : function(style){
485 YAHOO.ext.DomHelper.applyStyles(this.dom, style);
486 },
487
488 /**
489 * Gets the current X position of the element based on page coordinates. Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
490 @ return {Number} The X position of the element
491 */
492 getX : function(){
493 return YAHOO.util.Dom.getX(this.dom);
494 },
495
496 /**
497 * Gets the current Y position of the element based on page coordinates. Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
498 @ return {Number} The Y position of the element
499 */
500 getY : function(){
501 return YAHOO.util.Dom.getY(this.dom);
502 },
503
504 /**
505 * Gets the current position of the element based on page coordinates. Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
506 @ return {Array} The XY position of the element
507 */
508 getXY : function(){
509 return YAHOO.util.Dom.getXY(this.dom);
510 },
511
512 /**
513 * Sets the X position of the element based on page coordinates. Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
514 @param {Number} The X position of the element
515 * @param {<i>Boolean</i>} animate (optional) Animate the transition (Default is false)
516 * @param {<i>float</i>} duration (optional) How long the animation lasts. (Defaults to .35 seconds)
517 * @param {<i>Function</i>} onComplete (optional) Function to call when animation completes.
518 * @param {<i>Function</i>} easing (optional) YAHOO.util.Easing method to use. (Defaults to YAHOO.util.Easing.easeBoth)
519 * @return {YAHOO.ext.Element} this
520 */
521 setX : function(x, animate, duration, onComplete, easing){
522 if(!animate || !YAHOO.util.Anim){
523 YAHOO.util.Dom.setX(this.dom, x);
524 }else{
525 this.setXY([x, this.getY()], animate, duration, onComplete, easing);
526 }
527 return this;
528 },
529
530 /**
531 * Sets the Y position of the element based on page coordinates. Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
532 @param {Number} The Y position of the element
533 * @param {<i>Boolean</i>} animate (optional) Animate the transition (Default is false)
534 * @param {<i>float</i>} duration (optional) How long the animation lasts. (Defaults to .35 seconds)
535 * @param {<i>Function</i>} onComplete (optional) Function to call when animation completes.
536 * @param {<i>Function</i>} easing (optional) YAHOO.util.Easing method to use. (Defaults to YAHOO.util.Easing.easeBoth)
537 * @return {YAHOO.ext.Element} this
538 */
539 setY : function(y, animate, duration, onComplete, easing){
540 if(!animate || !YAHOO.util.Anim){
541 YAHOO.util.Dom.setY(this.dom, y);
542 }else{
543 this.setXY([this.getX(), y], animate, duration, onComplete, easing);
544 }
545 return this;
546 },
547
548 /**
549 * Set the element's left position directly using CSS style (instead of setX())
550 * @param {String} left The left CSS property value
551 * @return {YAHOO.ext.Element} this
552 */
553 setLeft : function(left){
554 YAHOO.util.Dom.setStyle(this.dom, 'left', this.addUnits(left));
555 return this;
556 },
557
558 /**
559 * Set the element's top position directly using CSS style (instead of setY())
560 * @param {String} top The top CSS property value
561 * @return {YAHOO.ext.Element} this
562 */
563 setTop : function(top){
564 YAHOO.util.Dom.setStyle(this.dom, 'top', this.addUnits(top));
565 return this;
566 },
567
568 /**
569 * Set the element's css right style
570 * @param {String} right The right CSS property value
571 * @return {YAHOO.ext.Element} this
572 */
573 setRight : function(right){
574 YAHOO.util.Dom.setStyle(this.dom, 'right', this.addUnits(right));
575 return this;
576 },
577
578 /**
579 * Set the element's css bottom style
580 * @param {String} bottom The bottom CSS property value
581 * @return {YAHOO.ext.Element} this
582 */
583 setBottom : function(bottom){
584 YAHOO.util.Dom.setStyle(this.dom, 'bottom', this.addUnits(bottom));
585 return this;
586 },
587
588 /**
589 * Set the position of the element in page coordinates, regardless of how the element is positioned.
590 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
591 * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
592 * @param {<i>Boolean</i>} animate (optional) Animate the transition (Default is false)
593 * @param {<i>float</i>} duration (optional) How long the animation lasts. (Defaults to .35 seconds)
594 * @param {<i>Function</i>} onComplete (optional) Function to call when animation completes.
595 * @param {<i>Function</i>} easing (optional) YAHOO.util.Easing method to use. (Defaults to YAHOO.util.Easing.easeBoth)
596 * @return {YAHOO.ext.Element} this
597 */
598 setXY : function(pos, animate, duration, onComplete, easing){
599 if(!animate || !YAHOO.util.Anim){
600 YAHOO.util.Dom.setXY(this.dom, pos);
601 }else{
602 this.anim({points: {to: pos}}, duration, onComplete, easing, YAHOO.util.Motion);
603 }
604 return this;
605 },
606
607 /**
608 * Set the position of the element in page coordinates, regardless of how the element is positioned.
609 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
610 * @param {Number} x X value for new position (coordinates are page-based)
611 * @param {Number} y Y value for new position (coordinates are page-based)
612 * @param {<i>Boolean</i>} animate (optional) Animate the transition (Default is false)
613 * @param {<i>float</i>} duration (optional) How long the animation lasts. (Defaults to .35 seconds)
614 * @param {<i>Function</i>} onComplete (optional) Function to call when animation completes.
615 * @param {<i>Function</i>} easing (optional) YAHOO.util.Easing method to use. (Defaults to YAHOO.util.Easing.easeBoth)
616 * @return {YAHOO.ext.Element} this
617 */
618 setLocation : function(x, y, animate, duration, onComplete, easing){
619 this.setXY([x, y], animate, duration, onComplete, easing);
620 return this;
621 },
622
623 /**
624 * Set the position of the element in page coordinates, regardless of how the element is positioned.
625 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
626 * @param {Number} x X value for new position (coordinates are page-based)
627 * @param {Number} y Y value for new position (coordinates are page-based)
628 * @param {<i>Boolean</i>} animate (optional) Animate the transition (Default is false)
629 * @param {<i>float</i>} duration (optional) How long the animation lasts. (Defaults to .35 seconds)
630 * @param {<i>Function</i>} onComplete (optional) Function to call when animation completes.
631 * @param {<i>Function</i>} easing (optional) YAHOO.util.Easing method to use. (Defaults to YAHOO.util.Easing.easeBoth)
632 * @return {YAHOO.ext.Element} this
633 */
634 moveTo : function(x, y, animate, duration, onComplete, easing){
635 //YAHOO.util.Dom.setStyle(this.dom, 'left', this.addUnits(x));
636 //YAHOO.util.Dom.setStyle(this.dom, 'top', this.addUnits(y));
637 this.setXY([x, y], animate, duration, onComplete, easing);
638 return this;
639 },
640
641 /**
642 * Returns the region of the given element.
643 * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
644 * @return {Region} A YAHOO.util.Region containing "top, left, bottom, right" member data.
645 */
646 getRegion : function(){
647 return YAHOO.util.Dom.getRegion(this.dom);
648 },
649
650 /**
651 * Returns the offset height of the element
652 * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
653 * @return {Number} The element's height
654 */
655 getHeight : function(contentHeight){
656 var h = this.dom.offsetHeight;
657 return contentHeight !== true ? h : h-this.getBorderWidth('tb')-this.getPadding('tb');
658 },
659
660 /**
661 * Returns the offset width of the element
662 * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
663 * @return {Number} The element's width
664 */
665 getWidth : function(contentWidth){
666 var w = this.dom.offsetWidth;
667 return contentWidth !== true ? w : w-this.getBorderWidth('lr')-this.getPadding('lr');
668 },
669
670 /**
671 * Returns the size of the element
672 * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
673 * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
674 */
675 getSize : function(contentSize){
676 return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
677 },
678
679 /** @private */
680 adjustWidth : function(width){
681 if(typeof width == 'number'){
682 if(this.autoBoxAdjust && !this.isBorderBox()){
683 width -= (this.getBorderWidth('lr') + this.getPadding('lr'));
684 }
685 if(width < 0){
686 width = 0;
687 }
688 }
689 return width;
690 },
691
692 /** @private */
693 adjustHeight : function(height){
694 if(typeof height == 'number'){
695 if(this.autoBoxAdjust && !this.isBorderBox()){
696 height -= (this.getBorderWidth('tb') + this.getPadding('tb'));
697 }
698 if(height < 0){
699 height = 0;
700 }
701 }
702 return height;
703 },
704
705 /**
706 * Set the width of the element
707 * @param {Number} width The new width
708 * @param {<i>Boolean</i>} animate (optional) Animate the transition (Default is false)
709 * @param {<i>float</i>} duration (optional) How long the animation lasts. (Defaults to .35 seconds)
710 * @param {<i>Function</i>} onComplete (optional) Function to call when animation completes.
711 * @param {<i>Function</i>} easing (optional) YAHOO.util.Easing method to use. (Defaults to YAHOO.util.Easing.easeOut if width is larger or YAHOO.util.Easing.easeIn if it is smaller)
712 * @return {YAHOO.ext.Element} this
713 */
714 setWidth : function(width, animate, duration, onComplete, easing){
715 width = this.adjustWidth(width);
716 if(!animate || !YAHOO.util.Anim){
717 this.dom.style.width = this.addUnits(width);
718 //YAHOO.util.Dom.setStyle(this.dom, 'width', this.addUnits(width));
719 }else{
720 this.anim({width: {to: width}}, duration, onComplete,
721 easing || (width > this.getWidth() ? YAHOO.util.Easing.easeOut : YAHOO.util.Easing.easeIn));
722 }
723 return this;
724 },
725
726 /**
727 * Set the height of the element
728 * @param {Number} height The new height
729 * @param {<i>Boolean</i>} animate (optional) Animate the transition (Default is false)
730 * @param {<i>float</i>} duration (optional) How long the animation lasts. (Defaults to .35 seconds)
731 * @param {<i>Function</i>} onComplete (optional) Function to call when animation completes.
732 * @param {<i>Function</i>} easing (optional) YAHOO.util.Easing method to use. (Defaults to YAHOO.util.Easing.easeOut if height is larger or YAHOO.util.Easing.easeIn if it is smaller)
733 * @return {YAHOO.ext.Element} this
734 */
735 setHeight : function(height, animate, duration, onComplete, easing){
736 height = this.adjustHeight(height);
737 if(!animate || !YAHOO.util.Anim){
738 this.dom.style.height = this.addUnits(height);
739 //YAHOO.util.Dom.setStyle(this.dom, 'height', this.addUnits(height));
740 }else{
741 this.anim({height: {to: height}}, duration, onComplete,
742 easing || (height > this.getHeight() ? YAHOO.util.Easing.easeOut : YAHOO.util.Easing.easeIn));
743 }
744 return this;
745 },
746
747 /**
748 * Set the size of the element. If animation is true, both width an height will be animated concurrently.
749 * @param {Number} width The new width
750 * @param {Number} height The new height
751 * @param {<i>Boolean</i>} animate (optional) Animate the transition (Default is false)
752 * @param {<i>float</i>} duration (optional) How long the animation lasts. (Defaults to .35 seconds)
753 * @param {<i>Function</i>} onComplete (optional) Function to call when animation completes.
754 * @param {<i>Function</i>} easing (optional) YAHOO.util.Easing method to use. (Defaults to YAHOO.util.Easing.easeBoth)
755 * @return {YAHOO.ext.Element} this
756 */
757 setSize : function(width, height, animate, duration, onComplete, easing){
758 width = this.adjustWidth(width); height = this.adjustHeight(height);
759 if(!animate || !YAHOO.util.Anim){
760 this.dom.style.width = this.addUnits(width);
761 this.dom.style.height = this.addUnits(height);
762 }else{
763 this.anim({width: {to: width}, height: {to: height}}, duration, onComplete, easing);
764 }
765 return this;
766 },
767
768 /**
769 * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
770 * @param {Number} x X value for new position (coordinates are page-based)
771 * @param {Number} y Y value for new position (coordinates are page-based)
772 * @param {Number} width The new width
773 * @param {Number} height The new height
774 * @param {<i>Boolean</i>} animate (optional) Animate the transition (Default is false)
775 * @param {<i>float</i>} duration (optional) How long the animation lasts. (Defaults to .35 seconds)
776 * @param {<i>Function</i>} onComplete (optional) Function to call when animation completes.
777 * @param {<i>Function</i>} easing (optional) YAHOO.util.Easing method to use. (Defaults to YAHOO.util.Easing.easeBoth)
778 * @return {YAHOO.ext.Element} this
779 */
780 setBounds : function(x, y, width, height, animate, duration, onComplete, easing){
781 if(!animate || !YAHOO.util.Anim){
782 this.setSize(width, height);
783 this.setLocation(x, y);
784 }else{
785 width = this.adjustWidth(width); height = this.adjustHeight(height);
786 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}}, duration, onComplete, easing, YAHOO.util.Motion);
787 }
788 return this;
789 },
790
791 /**
792 * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
793 * @param {YAHOO.util.Region} region The region to fill
794 * @param {<i>Boolean</i>} animate (optional) Animate the transition (Default is false)
795 * @param {<i>float</i>} duration (optional) How long the animation lasts. (Defaults to .35 seconds)
796 * @param {<i>Function</i>} onComplete (optional) Function to call when animation completes.
797 * @param {<i>Function</i>} easing (optional) YAHOO.util.Easing method to use. (Defaults to YAHOO.util.Easing.easeBoth)
798 * @return {YAHOO.ext.Element} this
799 */
800 setRegion : function(region, animate, duration, onComplete, easing){
801 this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, animate, duration, onComplete, easing);
802 return this;
803 },
804
805 /**
806 * Appends an event handler to this element
807 * @param {String} eventName The type of event to listen for
808 * @param {Function} handler The method the event invokes
809 * @param {<i>Object</i>} scope (optional) An arbitrary object that will be
810 * passed as a parameter to the handler
811 * @param {<i>boolean</i>} override (optional) If true, the obj passed in becomes
812 * the execution scope of the listener
813 * @return {YAHOO.ext.Element} this
814 */
815 addListener : function(eventName, handler, scope, override){
816 YAHOO.util.Event.addListener(this.dom, eventName, handler, scope || this, true);
817 return this;
818 },
819 /**
820 * Appends an event handler to this element that is buffered. If the event is triggered more than once
821 * in the specified time-frame, only the last one actually fires.
822 * @param {String} eventName The type of event to listen for
823 * @param {Function} handler The method the event invokes
824 * @param {<i>Object</i>} scope (optional) The scope (this object) for the handler
825 * @param {<i>Number</i>} millis (optional) The number of milliseconds to buffer (defaults to 250)
826 * @return {Function} The wrapped function that was created (can be used to remove the listener)
827 */
828 bufferedListener : function(eventName, fn, scope, millis){
829 var task = new YAHOO.ext.util.DelayedTask();
830 scope = scope || this;
831 var newFn = function(e){
832 task.delay(millis || 250, fn, scope, Array.prototype.slice.call(arguments, 0));
833 }
834 this.addListener(eventName, newFn);
835 return newFn;
836 },
837
838
839 /**
840 * Appends an event handler to this element. The difference between this function and addListener is this
841 * function prevents the default action, and if set stops propagation (bubbling) as well
842 * @param {String} eventName The type of event to listen for
843 * @param {Boolean} stopPropagation Whether to also stopPropagation (bubbling)
844 * @param {Function} handler The method the event invokes
845 * @param {<i>Object</i>} scope (optional) An arbitrary object that will be
846 * passed as a parameter to the handler
847 * @param {<i>boolean</i>} override (optional) If true, the obj passed in becomes
848 * the execution scope of the listener
849 * @return {YAHOO.ext.Element} this
850 */
851 addHandler : function(eventName, stopPropagation, handler, scope, override){
852 var fn = YAHOO.ext.Element.createStopHandler(stopPropagation, handler, scope || this, true);
853 YAHOO.util.Event.addListener(this.dom, eventName, fn);
854 return fn;
855 },
856
857 /**
858 * Appends an event handler to this element (Same as addListener)
859 * @param {String} eventName The type of event to listen for
860 * @param {Function} handler The method the event invokes
861 * @param {<i>Object</i>} scope (optional) An arbitrary object that will be
862 * passed as a parameter to the handler
863 * @param {<i>boolean</i>} override (optional) If true, the obj passed in becomes
864 * the execution scope of the listener
865 * @return {YAHOO.ext.Element} this
866 */
867 on : function(eventName, handler, scope, override){
868 YAHOO.util.Event.addListener(this.dom, eventName, handler, scope || this, true);
869 return this;
870 },
871
872 /**
873 * Append a managed listener - See {@link YAHOO.ext.EventObject} for more details. Use mon() for a shorter version.
874 * @param {String} eventName The type of event to listen for
875 * @param {Function} fn The method the event invokes
876 * @param {<i>Object</i>} scope (optional) An arbitrary object that will be
877 * passed as a parameter to the handler
878 * @param {<i>boolean</i>} override (optional) If true, the obj passed in becomes
879 * the execution scope of the listener
880 * @return {Function} The EventManager wrapped function that can be used to remove the listener
881 */
882 addManagedListener : function(eventName, fn, scope, override){
883 return YAHOO.ext.EventManager.on(this.dom, eventName, fn, scope || this, true);
884 },
885
886 /**
887 * Append a managed listener (shorthanded for {@link #addManagedListener})
888 * @param {String} eventName The type of event to listen for
889 * @param {Function} fn The method the event invokes
890 * @param {<i>Object</i>} scope (optional) An arbitrary object that will be
891 * passed as a parameter to the handler
892 * @param {<i>boolean</i>} override (optional) If true, the obj passed in becomes
893 * the execution scope of the listener
894 * @return {Function} The EventManager wrapped function that can be used to remove the listener
895 */
896 mon : function(eventName, fn, scope, override){
897 return YAHOO.ext.EventManager.on(this.dom, eventName, fn, scope || this, true);
898 },
899 /**
900 * Removes an event handler from this element
901 * @param {String} sType the type of event to remove
902 * @param {Function} fn the method the event invokes
903 * @param {Object} scope
904 * @return {YAHOO.ext.Element} this
905 */
906 removeListener : function(eventName, handler, scope){
907 YAHOO.util.Event.removeListener(this.dom, eventName, handler);
908 return this;
909 },
910
911 /**
912 * Removes all previous added listeners from this element
913 * @return {YAHOO.ext.Element} this
914 */
915 removeAllListeners : function(){
916 YAHOO.util.Event.purgeElement(this.dom);
917 return this;
918 },
919
920
921 /**
922 * Set the opacity of the element
923 * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
924 * @param {<i>Boolean</i>} animate (optional) Animate (fade) the transition (Default is false)
925 * @param {<i>Float</i>} duration (optional) How long the animation lasts. (Defaults to .35 seconds)
926 * @param {<i>Function</i>} onComplete (optional) Function to call when animation completes.
927 * @param {<i>Function</i>} easing (optional) YAHOO.util.Easing method to use. (Defaults to YAHOO.util.Easing.easeOut if height is larger or YAHOO.util.Easing.easeIn if it is smaller)
928 * @return {YAHOO.ext.Element} this
929 */
930 setOpacity : function(opacity, animate, duration, onComplete, easing){
931 if(!animate || !YAHOO.util.Anim){
932 YAHOO.util.Dom.setStyle(this.dom, 'opacity', opacity);
933 }else{
934 this.anim({opacity: {to: opacity}}, duration, onComplete, easing);
935 }
936 return this;
937 },
938
939 /**
940 * Gets the left X coordinate
941 * @param {Boolean} local True to get the local css position instead of page coordinate
942 * @return {Number}
943 */
944 getLeft : function(local){
945 if(!local){
946 return this.getX();
947 }else{
948 return parseInt(this.getStyle('left'), 10) || 0;
949 }
950 },
951
952 /**
953 * Gets the right X coordinate of the element (element X position + element width)
954 * @param {Boolean} local True to get the local css position instead of page coordinate
955 * @return {Number}
956 */
957 getRight : function(local){
958 if(!local){
959 return this.getX() + this.getWidth();
960 }else{
961 return (this.getLeft(true) + this.getWidth()) || 0;
962 }
963 },
964
965 /**
966 * Gets the top Y coordinate
967 * @param {Boolean} local True to get the local css position instead of page coordinate
968 * @return {Number}
969 */
970 getTop : function(local) {
971 if(!local){
972 return this.getY();
973 }else{
974 return parseInt(this.getStyle('top'), 10) || 0;
975 }
976 },
977
978 /**
979 * Gets the bottom Y coordinate of the element (element Y position + element height)
980 * @param {Boolean} local True to get the local css position instead of page coordinate
981 * @return {Number}
982 */
983 getBottom : function(local){
984 if(!local){
985 return this.getY() + this.getHeight();
986 }else{
987 return (this.getTop(true) + this.getHeight()) || 0;
988 }
989 },
990
991 /**
992 * Set the element as absolute positioned with the specified z-index
993 * @param {<i>Number</i>} zIndex (optional)
994 * @return {YAHOO.ext.Element} this
995 */
996 setAbsolutePositioned : function(zIndex){
997 this.setStyle('position', 'absolute');
998 if(zIndex){
999 this.setStyle('z-index', zIndex);
1000 }
1001 return this;
1002 },
1003
1004 /**
1005 * Set the element as relative positioned with the specified z-index
1006 * @param {<i>Number</i>} zIndex (optional)
1007 * @return {YAHOO.ext.Element} this
1008 */
1009 setRelativePositioned : function(zIndex){
1010 this.setStyle('position', 'relative');
1011 if(zIndex){
1012 this.setStyle('z-index', zIndex);
1013 }
1014 return this;
1015 },
1016
1017 /**
1018 * Clear positioning back to the default when the document was loaded
1019 * @return {YAHOO.ext.Element} this
1020 */
1021 clearPositioning : function(){
1022 this.setStyle('position', '');
1023 this.setStyle('left', '');
1024 this.setStyle('right', '');
1025 this.setStyle('top', '');
1026 this.setStyle('bottom', '');
1027 return this;
1028 },
1029
1030 /**
1031 * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
1032 * snapshot before performing an update and then restoring the element.
1033 * @return {Object}
1034 */
1035 getPositioning : function(){
1036 return {
1037 'position' : this.getStyle('position'),
1038 'left' : this.getStyle('left'),
1039 'right' : this.getStyle('right'),
1040 'top' : this.getStyle('top'),
1041 'bottom' : this.getStyle('bottom')
1042 };
1043 },
1044
1045 /**
1046 * Gets the width of the border(s) for the specified side(s)
1047 * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
1048 * passing lr would get the border (l)eft width + the border (r)ight width.
1049 * @return {Number} The width of the sides passed added together
1050 */
1051 getBorderWidth : function(side){
1052 return this.addStyles(side, YAHOO.ext.Element.borders);
1053 },
1054
1055 /**
1056 * Gets the width of the padding(s) for the specified side(s)
1057 * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
1058 * passing lr would get the padding (l)eft + the padding (r)ight.
1059 * @return {Number} The padding of the sides passed added together
1060 */
1061 getPadding : function(side){
1062 return this.addStyles(side, YAHOO.ext.Element.paddings);
1063 },
1064
1065 /**
1066 * Set positioning with an object returned by getPositioning().
1067 * @param {Object} posCfg
1068 * @return {YAHOO.ext.Element} this
1069 */
1070 setPositioning : function(positionCfg){
1071 if(positionCfg.position)this.setStyle('position', positionCfg.position);
1072 if(positionCfg.left)this.setLeft(positionCfg.left);
1073 if(positionCfg.right)this.setRight(positionCfg.right);
1074 if(positionCfg.top)this.setTop(positionCfg.top);
1075 if(positionCfg.bottom)this.setBottom(positionCfg.bottom);
1076 return this;
1077 },
1078
1079
1080 /**
1081 * Quick set left and top adding default units
1082 * @return {YAHOO.ext.Element} this
1083 */
1084 setLeftTop : function(left, top){
1085 this.dom.style.left = this.addUnits(left);
1086 this.dom.style.top = this.addUnits(top);
1087 return this;
1088 },
1089
1090 /**
1091 * Move this element relative to it's current position.
1092 * @param {String} direction Possible values are: 'l','left' - 'r','right' - 't','top','up' - 'b','bottom','down'.
1093 * @param {Number} distance How far to move the element in pixels
1094 * @param {<i>Boolean</i>} animate (optional) Animate the movement (Default is false)
1095 * @param {<i>Float</i>} duration (optional) How long the animation lasts. (Defaults to .35 seconds)
1096 * @param {<i>Function</i>} onComplete (optional) Function to call when animation completes.
1097 * @param {<i>Function</i>} easing (optional) YAHOO.util.Easing method to use.
1098 * @return {YAHOO.ext.Element} this
1099 */
1100 move : function(direction, distance, animate, duration, onComplete, easing){
1101 var xy = this.getXY();
1102 direction = direction.toLowerCase();
1103 switch(direction){
1104 case 'l':
1105 case 'left':
1106 this.moveTo(xy[0]-distance, xy[1], animate, duration, onComplete, easing);
1107 break;
1108 case 'r':
1109 case 'right':
1110 this.moveTo(xy[0]+distance, xy[1], animate, duration, onComplete, easing);
1111 break;
1112 case 't':
1113 case 'top':
1114 case 'up':
1115 this.moveTo(xy[0], xy[1]-distance, animate, duration, onComplete, easing);
1116 break;
1117 case 'b':
1118 case 'bottom':
1119 case 'down':
1120 this.moveTo(xy[0], xy[1]+distance, animate, duration, onComplete, easing);
1121 break;
1122 }
1123 return this;
1124 },
1125
1126 /**
1127 * Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
1128 * @return {YAHOO.ext.Element} this
1129 */
1130 clip : function(){
1131 if(!this.isClipped){
1132 this.isClipped = true;
1133 this.originalClip = {
1134 'o': this.getStyle('overflow'),
1135 'x': this.getStyle('overflow-x'),
1136 'y': this.getStyle('overflow-y')
1137 };
1138 this.setStyle('overflow', 'hidden');
1139 this.setStyle('overflow-x', 'hidden');
1140 this.setStyle('overflow-y', 'hidden');
1141 }
1142 return this;
1143 },
1144
1145 /**
1146 * Return clipping (overflow) to original clipping before clip() was called
1147 * @return {YAHOO.ext.Element} this
1148 */
1149 unclip : function(){
1150 if(this.isClipped){
1151 this.isClipped = false;
1152 var o = this.originalClip;
1153 if(o.o){this.setStyle('overflow', o.o);}
1154 if(o.x){this.setStyle('overflow-x', o.x);}
1155 if(o.y){this.setStyle('overflow-y', o.y);}
1156 }
1157 return this;
1158 },
1159
1160 /**
1161 * Align this element with another element.
1162 * @param {String/HTMLElement/YAHOO.ext.Element} element The element to align to.
1163 * @param {String} position The position to align to. Possible values are 'tl' - top left, 'tr' - top right, 'bl' - bottom left, and 'br' - bottom right.
1164 * @param {<i>Array</i>} offsets (optional) Offset the positioning by [x, y]
1165 * @param {<i>Boolean</i>} animate (optional) Animate the movement (Default is false)
1166 * @param {<i>Float</i>} duration (optional) How long the animation lasts. (Defaults to .35 seconds)
1167 * @param {<i>Function</i>} onComplete (optional) Function to call when animation completes.
1168 * @param {<i>Function</i>} easing (optional) YAHOO.util.Easing method to use.
1169 * @return {YAHOO.ext.Element} this
1170 */
1171 alignTo : function(element, position, offsets, animate, duration, onComplete, easing){
1172 var otherEl = getEl(element);
1173 if(!otherEl){
1174 return this; // must not exist
1175 }
1176 offsets = offsets || [0, 0];
1177 var r = otherEl.getRegion();
1178 position = position.toLowerCase();
1179 switch(position){
1180 case 'bl':
1181 this.moveTo(r.left + offsets[0], r.bottom + offsets[1],
1182 animate, duration, onComplete, easing);
1183 break;
1184 case 'br':
1185 this.moveTo(r.right + offsets[0], r.bottom + offsets[1],
1186 animate, duration, onComplete, easing);
1187 break;
1188 case 'tl':
1189 this.moveTo(r.left + offsets[0], r.top + offsets[1],
1190 animate, duration, onComplete, easing);
1191 break;
1192 case 'tr':
1193 this.moveTo(r.right + offsets[0], r.top + offsets[1],
1194 animate, duration, onComplete, easing);
1195 break;
1196 }
1197 return this;
1198 },
1199
1200 /**
1201 * Clears any opacity settings from this element. Required in some cases for IE.
1202 * @return {YAHOO.ext.Element} this
1203 */
1204 clearOpacity : function(){
1205 if (window.ActiveXObject) {
1206 this.dom.style.filter = '';
1207 } else {
1208 this.dom.style.opacity = '';
1209 this.dom.style['-moz-opacity'] = '';
1210 this.dom.style['-khtml-opacity'] = '';
1211 }
1212 return this;
1213 },
1214
1215 /**
1216 * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
1217 * @param {<i>Boolean</i>} animate (optional) Animate (fade) the transition (Default is false)
1218 * @param {<i>Float</i>} duration (optional) How long the animation lasts. (Defaults to .35 seconds)
1219 * @param {<i>Function</i>} onComplete (optional) Function to call when animation completes.
1220 * @param {<i>Function</i>} easing (optional) YAHOO.util.Easing method to use. (Defaults to YAHOO.util.Easing.easeBoth)
1221 * @return {YAHOO.ext.Element} this
1222 */
1223 hide : function(animate, duration, onComplete, easing){
1224 this.setVisible(false, animate, duration, onComplete, easing);
1225 return this;
1226 },
1227
1228 /**
1229 * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
1230 * @param {<i>Boolean</i>} animate (optional) Animate (fade in) the transition (Default is false)
1231 * @param {<i>Float</i>} duration (optional) How long the animation lasts. (Defaults to .35 seconds)
1232 * @param {<i>Function</i>} onComplete (optional) Function to call when animation completes.
1233 * @param {<i>Function</i>} easing (optional) YAHOO.util.Easing method to use. (Defaults to YAHOO.util.Easing.easeBoth)
1234 * @return {YAHOO.ext.Element} this
1235 */
1236 show : function(animate, duration, onComplete, easing){
1237 this.setVisible(true, animate, duration, onComplete, easing);
1238 return this;
1239 },
1240
1241 /**
1242 * @private Test if size has a unit, otherwise appends the default
1243 */
1244 addUnits : function(size){
1245 if(size === '' || size == 'auto' || typeof size == 'undefined'){
1246 return size;
1247 }
1248 if(typeof size == 'number' || !YAHOO.ext.Element.unitPattern.test(size)){
1249 return size + this.defaultUnit;
1250 }
1251 return size;
1252 },
1253
1254 /**
1255 * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
1256 * @return {YAHOO.ext.Element} this
1257 */
1258 beginMeasure : function(){
1259 var el = this.dom;
1260 if(el.offsetWidth || el.offsetHeight){
1261 return this; // offsets work already
1262 }
1263 var changed = [];
1264 var p = this.dom; // start with this element
1265 while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p.tagName.toLowerCase() != 'body'){
1266 if(YAHOO.util.Dom.getStyle(p, 'display') == 'none'){
1267 changed.push({el: p, visibility: YAHOO.util.Dom.getStyle(p, 'visibility')});
1268 p.style.visibility = 'hidden';
1269 p.style.display = 'block';
1270 }
1271 p = p.parentNode;
1272 }
1273 this._measureChanged = changed;
1274 return this;
1275
1276 },
1277
1278 /**
1279 * Restores displays to before beginMeasure was called
1280 * @return {YAHOO.ext.Element} this
1281 */
1282 endMeasure : function(){
1283 var changed = this._measureChanged;
1284 if(changed){
1285 for(var i = 0, len = changed.length; i < len; i++) {
1286 var r = changed[i];
1287 r.el.style.visibility = r.visibility;
1288 r.el.style.display = 'none';
1289 }
1290 this._measureChanged = null;
1291 }
1292 return this;
1293 },
1294
1295 /**
1296 * Update the innerHTML of this element, optionally searching for and processing scripts
1297 * @param {String} html The new HTML
1298 * @param {<i>Boolean</i>} loadScripts (optional) true to look for and process scripts
1299 * @param {Function} callback For async script loading you can be noticed when the update completes
1300 * @return {YAHOO.ext.Element} this
1301 */
1302 update : function(html, loadScripts, callback){
1303 if(typeof html == 'undefined'){
1304 html = '';
1305 }
1306 if(loadScripts !== true){
1307 this.dom.innerHTML = html;
1308 if(typeof callback == 'function'){
1309 callback();
1310 }
1311 return this;
1312 }
1313 var id = YAHOO.util.Dom.generateId();
1314 var dom = this.dom;
1315
1316 html += '<span id="' + id + '"></span>';
1317
1318 YAHOO.util.Event.onAvailable(id, function(){
1319 var hd = document.getElementsByTagName("head")[0];
1320 var re = /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/img;
1321 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
1322 var match;
1323 while(match = re.exec(html)){
1324 var srcMatch = match[0].match(srcRe);
1325 if(srcMatch && srcMatch[2]){
1326 var s = document.createElement("script");
1327 s.src = srcMatch[2];
1328 hd.appendChild(s);
1329 }else if(match[1] && match[1].length > 0){
1330 eval(match[1]);
1331 }
1332 }
1333 var el = document.getElementById(id);
1334 if(el){el.parentNode.removeChild(el);}
1335 if(typeof callback == 'function'){
1336 callback();
1337 }
1338 });
1339 dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/img, '');
1340 return this;
1341 },
1342
1343 /**
1344 * Direct access to the UpdateManager update() method (takes the same parameters).
1345 * @param {String/Function} url The url for this request or a function to call to get the url
1346 * @param {<i>String/Object</i>} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
1347 * @param {<i>Function</i>} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
1348 * @param {<i>Boolean</i>} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
1349 * @return {YAHOO.ext.Element} this
1350 */
1351 load : function(){
1352 var um = this.getUpdateManager();
1353 um.update.apply(um, arguments);
1354 return this;
1355 },
1356
1357 /**
1358 * Gets this elements UpdateManager
1359 * @return {YAHOO.ext.UpdateManager} The UpdateManager
1360 */
1361 getUpdateManager : function(){
1362 if(!this.updateManager){
1363 this.updateManager = new YAHOO.ext.UpdateManager(this);
1364 }
1365 return this.updateManager;
1366 },
1367
1368 /**
1369 * Disables text selection for this element (normalized across browsers)
1370 * @return {YAHOO.ext.Element} this
1371 */
1372 unselectable : function(){
1373 this.dom.unselectable = 'on';
1374 this.swallowEvent('selectstart', true);
1375 this.applyStyles('-moz-user-select:none;-khtml-user-select:none;');
1376 return this;
1377 },
1378
1379 /**
1380 * Calculates the x, y to center this element on the screen
1381 * @param {Boolean} offsetScroll True to offset the documents current scroll position
1382 * @return {Array} The x, y values [x, y]
1383 */
1384 getCenterXY : function(offsetScroll){
1385 var centerX = Math.round((YAHOO.util.Dom.getViewportWidth()-this.getWidth())/2);
1386 var centerY = Math.round((YAHOO.util.Dom.getViewportHeight()-this.getHeight())/2);
1387 if(!offsetScroll){
1388 return [centerX, centerY];
1389 }else{
1390 var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft || 0;
1391 var scrollY = document.documentElement.scrollTop || document.body.scrollTop || 0;
1392 return[centerX + scrollX, centerY + scrollY];
1393 }
1394 },
1395
1396 /**
1397 * Centers the Element in either the viewport, or another Element.
1398 * @param {String/HTMLElement/YAHOO.ext.Element} centerIn (optional) The element in which to center the element.
1399 */
1400 center : function(centerIn) {
1401 if(!centerIn){
1402 this.setXY(this.getCenterXY(true));
1403 }else{
1404 var box = YAHOO.ext.Element.get(centerIn).getBox();
1405 this.setXY([box.x + (box.width / 2) - (this.getWidth() / 2),
1406 box.y + (box.height / 2) - (this.getHeight() / 2)]);
1407 }
1408 return this;
1409 },
1410
1411 /**
1412 * Gets an array of child YAHOO.ext.Element objects by tag name
1413 * @param {String} tagName
1414 * @return {Array} The children
1415 */
1416 getChildrenByTagName : function(tagName){
1417 var children = this.dom.getElementsByTagName(tagName);
1418 var len = children.length;
1419 var ce = new Array(len);
1420 for(var i = 0; i < len; ++i){
1421 ce[i] = YAHOO.ext.Element.get(children[i], true);
1422 }
1423 return ce;
1424 },
1425
1426 /**
1427 * Gets an array of child YAHOO.ext.Element objects by class name and optional tagName
1428 * @param {String} className
1429 * @param {<i>String</i>} tagName (optional)
1430 * @return {Array} The children
1431 */
1432 getChildrenByClassName : function(className, tagName){
1433 var children = YAHOO.util.Dom.getElementsByClassName(className, tagName, this.dom);
1434 var len = children.length;
1435 var ce = new Array(len);
1436 for(var i = 0; i < len; ++i){
1437 ce[i] = YAHOO.ext.Element.get(children[i], true);
1438 }
1439 return ce;
1440 },
1441
1442 /**
1443 * Tests various css rules/browsers to determine if this element uses a border box
1444 * @return {Boolean}
1445 */
1446 isBorderBox : function(){
1447 if(typeof this.bbox == 'undefined'){
1448 var el = this.dom;
1449 var b = YAHOO.ext.util.Browser;
1450 var strict = YAHOO.ext.Strict;
1451 this.bbox = ((b.isIE && !strict && el.style.boxSizing != 'content-box') ||
1452 (b.isGecko && YAHOO.util.Dom.getStyle(el, "-moz-box-sizing") == 'border-box') ||
1453 (!b.isSafari && YAHOO.util.Dom.getStyle(el, "box-sizing") == 'border-box'));
1454 }
1455 return this.bbox;
1456 },
1457
1458 /**
1459 * Return a box {x, y, width, height} that can be used to set another elements
1460 * size/location to match this element.
1461 * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
1462 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
1463 * @return {Object}
1464 */
1465 getBox : function(contentBox, local){
1466 var xy;
1467 if(!local){
1468 xy = this.getXY();
1469 }else{
1470 var left = parseInt(YAHOO.util.Dom.getStyle('left'), 10) || 0;
1471 var top = parseInt(YAHOO.util.Dom.getStyle('top'), 10) || 0;
1472 xy = [left, top];
1473 }
1474 var el = this.dom;
1475 var w = el.offsetWidth;
1476 var h = el.offsetHeight;
1477 if(!contentBox){
1478 return {x: xy[0], y: xy[1], width: w, height: h};
1479 }else{
1480 var l = this.getBorderWidth('l')+this.getPadding('l');
1481 var r = this.getBorderWidth('r')+this.getPadding('r');
1482 var t = this.getBorderWidth('t')+this.getPadding('t');
1483 var b = this.getBorderWidth('b')+this.getPadding('b');
1484 return {x: xy[0]+l, y: xy[1]+t, width: w-(l+r), height: h-(t+b)};
1485 }
1486 },
1487
1488 /**
1489 * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
1490 * @param {Object} box The box to fill {x, y, width, height}
1491 * @param {<i>Boolean</i>} adjust (optional) Whether to adjust for box-model issues automatically
1492 * @param {<i>Boolean</i>} animate (optional) Animate the transition (Default is false)
1493 * @param {<i>float</i>} duration (optional) How long the animation lasts. (Defaults to .35 seconds)
1494 * @param {<i>Function</i>} onComplete (optional) Function to call when animation completes.
1495 * @param {<i>Function</i>} easing (optional) YAHOO.util.Easing method to use. (Defaults to YAHOO.util.Easing.easeBoth)
1496 * @return {YAHOO.ext.Element} this
1497 */
1498 setBox : function(box, adjust, animate, duration, onComplete, easing){
1499 var w = box.width, h = box.height;
1500 if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
1501 w -= (this.getBorderWidth('lr') + this.getPadding('lr'));
1502 h -= (this.getBorderWidth('tb') + this.getPadding('tb'));
1503 }
1504 this.setBounds(box.x, box.y, w, h, animate, duration, onComplete, easing);
1505 return this;
1506 },
1507
1508 /**
1509 * Forces the browser to repaint this element
1510 * @return {YAHOO.ext.Element} this
1511 */
1512 repaint : function(){
1513 var dom = this.dom;
1514 YAHOO.util.Dom.addClass(dom, 'yui-ext-repaint');
1515 setTimeout(function(){
1516 YAHOO.util.Dom.removeClass(dom, 'yui-ext-repaint');
1517 }, 1);
1518 return this;
1519 },
1520
1521 /**
1522 * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
1523 * then it returns the calculated width of the sides (see getPadding)
1524 * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
1525 * @return {Object/Number}
1526 */
1527 getMargins : function(side){
1528 if(!side){
1529 return {
1530 top: parseInt(this.getStyle('margin-top'), 10) || 0,
1531 left: parseInt(this.getStyle('margin-left'), 10) || 0,
1532 bottom: parseInt(this.getStyle('margin-bottom'), 10) || 0,
1533 right: parseInt(this.getStyle('margin-right'), 10) || 0
1534 };
1535 }else{
1536 return this.addStyles(side, YAHOO.ext.Element.margins);
1537 }
1538 },
1539
1540 addStyles : function(sides, styles){
1541 var val = 0;
1542 for(var i = 0, len = sides.length; i < len; i++){
1543 var w = parseInt(this.getStyle(styles[sides.charAt(i)]), 10);
1544 if(!isNaN(w)) val += w;
1545 }
1546 return val;
1547 },
1548
1549 /**
1550 * Creates a proxy element of this element
1551 * @param {String/Object} config The class name of the proxy element or a DomHelper config object
1552 * @param {<i>String/HTMLElement</i>} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
1553 * @param {<i>Boolean</i>} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
1554 * @return {YAHOO.ext.Element} The new proxy element
1555 */
1556 createProxy : function(config, renderTo, matchBox){
1557 if(renderTo){
1558 renderTo = YAHOO.util.Dom.get(renderTo);
1559 }else{
1560 renderTo = document.body;
1561 }
1562 config = typeof config == 'object' ?
1563 config : {tag : 'div', cls: config};
1564 var proxy = YAHOO.ext.DomHelper.append(renderTo, config, true);
1565 if(matchBox){
1566 proxy.setBox(this.getBox());
1567 }
1568 return proxy;
1569 },
1570
1571 /**
1572 * Puts a mask over this element to disable user interaction. Requires core.css.
1573 * This method can only be applied to elements which accept child nodes.
1574 * @return {Element} The message element
1575 */
1576 mask : function(){
1577 if(this.getStyle('position') == 'static'){
1578 this.setStyle('position', 'relative');
1579 }
1580 if(!this._mask){
1581 this._mask = YAHOO.ext.DomHelper.append(this.dom, {tag:'div', cls:'ext-el-mask'}, true);
1582 }
1583 this.addClass('ext-masked');
1584 this._mask.setDisplayed(true);
1585 return this._mask;
1586 },
1587
1588 /**
1589 * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
1590 * it is cached for reuse.
1591 */
1592 unmask : function(removeEl){
1593 if(this._mask){
1594 removeEl === true ?
1595 this._mask.remove() : this._mask.setDisplayed(false);
1596 }
1597 this.removeClass('ext-masked');
1598 },
1599
1600 /**
1601 * Creates an iframe shim for this element to keep selects and other windowed objects from
1602 * showing through.
1603 * @return {YAHOO.ext.Element} The new shim element
1604 */
1605 createShim : function(){
1606 var config = {
1607 tag : 'iframe',
1608 frameBorder:'no',
1609 cls: 'yiframe-shim',
1610 style: 'position:absolute;visibility:hidden;left:0;top:0;overflow:hidden;',
1611 src: YAHOO.ext.SSL_SECURE_URL
1612 };
1613 var shim = YAHOO.ext.DomHelper.insertBefore(this.dom, config, true);
1614 shim.setOpacity(.01);
1615 shim.setBox(this.getBox());
1616 return shim;
1617 },
1618
1619 /**
1620 * Removes this element from the DOM and deletes it from the cache
1621 */
1622 remove : function(){
1623 this.dom.parentNode.removeChild(this.dom);
1624 delete YAHOO.ext.Element.cache[this.dom.id];
1625 },
1626
1627 /**
1628 * Sets up event handlers to add and remove a css class when the mouse is over this element
1629 * @param {String} className
1630 * @return {YAHOO.ext.Element} this
1631 */
1632 addClassOnOver : function(className){
1633 this.on('mouseover', function(){
1634 this.addClass(className);
1635 }, this, true);
1636 this.on('mouseout', function(){
1637 this.removeClass(className);
1638 }, this, true);
1639 return this;
1640 },
1641
1642 /**
1643 * Stops the specified event from bubbling and optionally prevent's the default action
1644 * @param {String} eventName
1645 * @param {Boolean} preventDefault (optional) true to prevent the default action too
1646 * @return {YAHOO.ext.Element} this
1647 */
1648 swallowEvent : function(eventName, preventDefault){
1649 var fn = function(e){
1650 e.stopPropagation();
1651 if(preventDefault){
1652 e.preventDefault();
1653 }
1654 };
1655 this.mon(eventName, fn);
1656 return this;
1657 },
1658
1659 /**
1660 * Sizes this element to it's parent element's dimensions performing
1661 * neccessary box adjustments.
1662 * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
1663 * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
1664 * @return {YAHOO.ext.Element} this
1665 */
1666 fitToParent : function(monitorResize, targetParent){
1667 var p = getEl(targetParent || this.dom.parentNode);
1668 p.beginMeasure(); // in case parent is display:none
1669 var box = p.getBox(true, true);
1670 p.endMeasure();
1671 this.setSize(box.width, box.height);
1672 if(monitorResize === true){
1673 YAHOO.ext.EventManager.onWindowResize(this.fitToParent, this, true);
1674 }
1675 return this;
1676 },
1677
1678 /**
1679 * Gets the next sibling, skipping text nodes
1680 * @return {HTMLElement} The next sibling or null
1681 */
1682 getNextSibling : function(){
1683 var n = this.dom.nextSibling;
1684 while(n && n.nodeType != 1){
1685 n = n.nextSibling;
1686 }
1687 return n;
1688 },
1689
1690 /**
1691 * Gets the previous sibling, skipping text nodes
1692 * @return {HTMLElement} The previous sibling or null
1693 */
1694 getPrevSibling : function(){
1695 var n = this.dom.previousSibling;
1696 while(n && n.nodeType != 1){
1697 n = n.previousSibling;
1698 }
1699 return n;
1700 },
1701
1702
1703 /**
1704 * Appends the passed element(s) to this element
1705 * @param {String/HTMLElement/Array/Element/CompositeElement} el
1706 * @return {YAHOO.ext.Element} this
1707 */
1708 appendChild: function(el){
1709 el = getEl(el);
1710 el.appendTo(this);
1711 return this;
1712 },
1713
1714 /**
1715 * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
1716 * @param {Object} config DomHelper element config object
1717 * @param {<i>HTMLElement</i>} insertBefore (optional) a child element of this element
1718 * @return {YAHOO.ext.Element} The new child element
1719 */
1720 createChild: function(config, insertBefore){
1721 var c;
1722 if(insertBefore){
1723 c = YAHOO.ext.DomHelper.insertBefore(insertBefore, config, true);
1724 }else{
1725 c = YAHOO.ext.DomHelper.append(this.dom, config, true);
1726 }
1727 return c;
1728 },
1729
1730 /**
1731 * Appends this element to the passed element
1732 * @param {String/HTMLElement/Element} el The new parent element
1733 * @return {YAHOO.ext.Element} this
1734 */
1735 appendTo: function(el){
1736 var node = getEl(el).dom;
1737 node.appendChild(this.dom);
1738 return this;
1739 },
1740
1741 /**
1742 * Inserts this element before the passed element in the DOM
1743 * @param {String/HTMLElement/Element} el The element to insert before
1744 * @return {YAHOO.ext.Element} this
1745 */
1746 insertBefore: function(el){
1747 var node = getEl(el).dom;
1748 node.parentNode.insertBefore(this.dom, node);
1749 return this;
1750 },
1751
1752 /**
1753 * Inserts this element after the passed element in the DOM
1754 * @param {String/HTMLElement/Element} el The element to insert after
1755 * @return {YAHOO.ext.Element} this
1756 */
1757 insertAfter: function(el){
1758 var node = getEl(el).dom;
1759 node.parentNode.insertBefore(this.dom, node.nextSibling);
1760 return this;
1761 },
1762
1763 /**
1764 * Creates and wraps this element with another element
1765 * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
1766 * @return {Element} The newly created wrapper element
1767 */
1768 wrap: function(config){
1769 if(!config){
1770 config = {tag: 'div'};
1771 }
1772 var newEl = YAHOO.ext.DomHelper.insertBefore(this.dom, config, true);
1773 newEl.dom.appendChild(this.dom);
1774 return newEl;
1775 },
1776
1777 /**
1778 * Replaces the passed element with this element
1779 * @param {String/HTMLElement/Element} el The element to replace
1780 * @return {YAHOO.ext.Element} this
1781 */
1782 replace: function(el){
1783 el = getEl(el);
1784 this.insertBefore(el);
1785 el.remove();
1786 return this;
1787 },
1788
1789 /**
1790 * Inserts an html fragment into this element
1791 * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
1792 * @param {String} html The HTML fragment
1793 * @return {HTMLElement} The inserted node (or nearest related if more than 1 inserted)
1794 */
1795 insertHtml : function(where, html){
1796 return YAHOO.ext.DomHelper.insertHtml(where, this.dom, html);
1797 },
1798
1799 /**
1800 * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
1801 * @param {Object} o The object with the attributes
1802 * @return {YAHOO.ext.Element} this
1803 */
1804 set : function(o){
1805 var el = this.dom;
1806 var useSet = el.setAttribute ? true : false;
1807 for(var attr in o){
1808 if(attr == 'style' || typeof o[attr] == 'function') continue;
1809 if(attr=='cls'){
1810 el.className = o['cls'];
1811 }else{
1812 if(useSet) el.setAttribute(attr, o[attr]);
1813 else el[attr] = o[attr];
1814 }
1815 }
1816 YAHOO.ext.DomHelper.applyStyles(el, o.style);
1817 return this;
1818 },
1819
1820 /**
1821 * Convenience method for constructing a KeyMap
1822 * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
1823 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
1824 * @param {Function} fn The function to call
1825 * @param {Object} scope (optional) The scope of the function
1826 * @return {YAHOO.ext.KeyMap} The KeyMap created
1827 */
1828 addKeyListener : function(key, fn, scope){
1829 var config;
1830 if(typeof key != 'object' || key instanceof Array){
1831 config = {
1832 key: key,
1833 fn: fn,
1834 scope: scope
1835 };
1836 }else{
1837 config = {
1838 key : key.key,
1839 shift : key.shift,
1840 ctrl : key.ctrl,
1841 alt : key.alt,
1842 fn: fn,
1843 scope: scope
1844 };
1845 }
1846 var map = new YAHOO.ext.KeyMap(this, config);
1847 return map;
1848 },
1849
1850 /**
1851 * Creates a KeyMap for this element
1852 * @param {Object} config The KeyMap config. See {@link YAHOO.ext.KeyMap} for more details
1853 * @return {YAHOO.ext.KeyMap} The KeyMap created
1854 */
1855 addKeyMap : function(config){
1856 return new YAHOO.ext.KeyMap(this, config);
1857 },
1858
1859 /**
1860 * Returns true if this element is scrollable.
1861 * @return {Boolean}
1862 */
1863 isScrollable : function(){
1864 var dom = this.dom;
1865 return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
1866 },
1867
1868 /**
1869 * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
1870 * @param {String} side Either 'left' for scrollLeft values or 'top' for scrollTop values.
1871 * @param {Number} value The new scroll value
1872 * @param {<i>Boolean</i>} animate (optional) Animate the scroll (Default is false)
1873 * @param {<i>Float</i>} duration (optional) How long the animation lasts. (Defaults to .35 seconds)
1874 * @param {<i>Function</i>} onComplete (optional) Function to call when animation completes.
1875 * @param {<i>Function</i>} easing (optional) YAHOO.util.Easing method to use.
1876 * @return {Element} this
1877 */
1878
1879 scrollTo : function(side, value, animate, duration, onComplete, easing){
1880 var prop = side.toLowerCase() == 'left' ? 'scrollLeft' : 'scrollTop';
1881 if(!animate || !YAHOO.util.Anim){
1882 this.dom[prop] = value;
1883 }else{
1884 var to = prop == 'scrollLeft' ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
1885 this.anim({scroll: {'to': to}}, duration, onComplete, easing || YAHOO.util.Easing.easeOut, YAHOO.util.Scroll);
1886 }
1887 return this;
1888 },
1889
1890 /**
1891 * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
1892 * within this elements scrollable range.
1893 * @param {String} direction Possible values are: 'l','left' - 'r','right' - 't','top','up' - 'b','bottom','down'.
1894 * @param {Number} distance How far to scroll the element in pixels
1895 * @param {<i>Boolean</i>} animate (optional) Animate the scroll (Default is false)
1896 * @param {<i>Float</i>} duration (optional) How long the animation lasts. (Defaults to .35 seconds)
1897 * @param {<i>Function</i>} onComplete (optional) Function to call when animation completes.
1898 * @param {<i>Function</i>} easing (optional) YAHOO.util.Easing method to use.
1899 * @return {Boolean} Returns true if a scroll was triggered or false if the element
1900 * was scrolled as far as it could go.
1901 */
1902 scroll : function(direction, distance, animate, duration, onComplete, easing){
1903 if(!this.isScrollable()){
1904 return;
1905 }
1906 var el = this.dom;
1907 var l = el.scrollLeft, t = el.scrollTop;
1908 var w = el.scrollWidth, h = el.scrollHeight;
1909 var cw = el.clientWidth, ch = el.clientHeight;
1910 direction = direction.toLowerCase();
1911 var scrolled = false;
1912 switch(direction){
1913 case 'l':
1914 case 'left':
1915 if(w - l > cw){
1916 var v = Math.min(l + distance, w-cw);
1917 this.scrollTo('left', v, animate, duration, onComplete, easing);
1918 scrolled = true;
1919 }
1920 break;
1921 case 'r':
1922 case 'right':
1923 if(l > 0){
1924 var v = Math.max(l - distance, 0);
1925 this.scrollTo('left', v, animate, duration, onComplete, easing);
1926 scrolled = true;
1927 }
1928 break;
1929 case 't':
1930 case 'top':
1931 case 'up':
1932 if(t > 0){
1933 var v = Math.max(t - distance, 0);
1934 this.scrollTo('top', v, animate, duration, onComplete, easing);
1935 scrolled = true;
1936 }
1937 break;
1938 case 'b':
1939 case 'bottom':
1940 case 'down':
1941 if(h - t > ch){
1942 var v = Math.min(t + distance, h-ch);
1943 this.scrollTo('top', v, animate, duration, onComplete, easing);
1944 scrolled = true;
1945 }
1946 break;
1947 }
1948 return scrolled;
1949 },
1950
1951 /**
1952 * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
1953 * are convert to standard 6 digit hex color.
1954 * @param {String} attr The css attribute
1955 * @param {String} defaultValue The default value to use when a valid color isn't found
1956 * @param {String} prefix (optional) defaults to #. Use an empty string when working with
1957 * YUI color anims.
1958 */
1959 getColor : function(attr, defaultValue, prefix){
1960 var v = this.getStyle(attr);
1961 if(!v || v == 'transparent' || v == 'inherit') {
1962 return defaultValue;
1963 }
1964 var color = typeof prefix == 'undefined' ? '#' : prefix;
1965 if(v.substr(0, 4) == 'rgb('){
1966 var rvs = v.slice(4, v.length -1).split(',');
1967 for(var i = 0; i < 3; i++){
1968 var h = parseInt(rvs[i]).toString(16);
1969 if(h < 16){
1970 h = '0' + h;
1971 }
1972 color += h;
1973 }
1974 } else {
1975 if(v.substr(0, 1) == '#'){
1976 if(v.length == 4) {
1977 for(var i = 1; i < 4; i++){
1978 var c = v.charAt(i);
1979 color += c + c;
1980 }
1981 }else if(v.length == 7){
1982 color += v.slice(1, 6);
1983 }
1984 }
1985 }
1986 return(color.length > 5 ? color.toLowerCase() : defaultValue);
1987 },
1988
1989 /**
1990 * Highlights the Element by setting a color (defaults to background-color) and then
1991 * fading back to the original color. If no original color is available, you should
1992 * provide an "endColor" option which will be cleared after the animation. The available options
1993 * for the "options" parameter are listed below (with their default values): <br/>
1994<pre><code>
1995el.highlight('ff0000', {<br/>
1996 attr: 'background-color',<br/>
1997 endColor: (current color) or 'ffffff'<br/>
1998 callback: yourFunction,<br/>
1999 scope: yourObject,<br/>
2000 easing: YAHOO.util.Easing.easeNone, <br/>
2001 duration: .75<br/>
2002});
2003</code></pre>
2004 * @param {String} color (optional) The highlight color. Should be a 6 char hex color (no #). (defaults to ffff9c)
2005 * @param {Object} options (optional) Object literal with any of the options listed above
2006 */
2007 highlight : function(color, options){
2008 color = color || 'ffff9c';
2009 options = options || {};
2010 attr = options.attr || 'background-color';
2011 var origColor = this.getColor(attr);
2012 endColor = (options.endColor || origColor) || 'ffffff';
2013 var dom = this.dom;
2014 var cb = function(){
2015 YAHOO.util.Dom.setStyle(dom, attr, origColor || '');
2016 if(options.callback){
2017 options.callback.call(options.scope || window);
2018 }
2019 };
2020 var o = {};
2021 o[attr] = {from: color, to: endColor};
2022 this.anim(o, options.duration || .75, cb, options.easing || YAHOO.util.Easing.easeNone, YAHOO.util.ColorAnim);
2023 return this;
2024 }
2025};
2026
2027/**
2028 * true to automatically adjust width and height settings for box-model issues (default to true)
2029 */
2030YAHOO.ext.Element.prototype.autoBoxAdjust = true;
2031/**
2032 * true to automatically detect display mode and use display instead of visibility with show()/hide() (defaults to false).
2033 * To enable this globally:<pre><code>YAHOO.ext.Element.prototype.autoDisplayMode = true;</code></pre>
2034 */
2035YAHOO.ext.Element.prototype.autoDisplayMode = true;
2036
2037YAHOO.ext.Element.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
2038/**
2039 * Visibility mode constant - Use visibility to hide element
2040 * @static
2041 * @type Number
2042 */
2043YAHOO.ext.Element.VISIBILITY = 1;
2044/**
2045 * Visibility mode constant - Use display to hide element
2046 * @static
2047 * @type Number
2048 */
2049YAHOO.ext.Element.DISPLAY = 2;
2050
2051YAHOO.ext.Element.blockElements = /^(?:address|blockquote|center|dir|div|dl|fieldset|form|h\d|hr|isindex|menu|ol|ul|p|pre|table|dd|dt|li|tbody|tr|td|thead|tfoot|iframe)$/i;
2052YAHOO.ext.Element.borders = {l: 'border-left-width', r: 'border-right-width', t: 'border-top-width', b: 'border-bottom-width'};
2053YAHOO.ext.Element.paddings = {l: 'padding-left', r: 'padding-right', t: 'padding-top', b: 'padding-bottom'};
2054YAHOO.ext.Element.margins = {l: 'margin-left', r: 'margin-right', t: 'margin-top', b: 'margin-bottom'};
2055
2056/**
2057 * @private Call out to here so we make minimal closure
2058 */
2059YAHOO.ext.Element.createStopHandler = function(stopPropagation, handler, scope, override){
2060 return function(e){
2061 if(e){
2062 if(stopPropagation){
2063 YAHOO.util.Event.stopEvent(e);
2064 }else {
2065 YAHOO.util.Event.preventDefault(e);
2066 }
2067 }
2068 handler.call(override && scope ? scope : window, e, scope);
2069 };
2070};
2071
2072/**
2073 * @private
2074 */
2075YAHOO.ext.Element.cache = {};
2076
2077/**
2078 * Static method to retreive Element objects. Uses simple caching to consistently return the same object.
2079 * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
2080 * @param {String/HTMLElement/Element} el The id of the element or the element to wrap (must have an id). If you pass in an element, it is returned
2081 * @return {Element} The element object
2082 * @static
2083 */
2084YAHOO.ext.Element.get = function(){
2085 var doc = document; // prevent IE dom lookup on every call to getEl
2086 var docEl;
2087 var E = YAHOO.ext.Element;
2088 var D = YAHOO.util.Dom;
2089
2090 return function(el){
2091 if(!el){ return null; }
2092 if(el instanceof E){
2093 if(el != docEl){
2094 el.dom = doc.getElementById(el.id); // refresh dom element in case no longer valid
2095 E.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
2096 }
2097 return el;
2098 }else if(el.isComposite){
2099 return el;
2100 }else if(el instanceof Array){
2101 return E.select(el);
2102 }else if(el == doc){
2103 // create a bogus element object representing the document object
2104 if(!docEl){
2105 var f = function(){};
2106 f.prototype = E.prototype;
2107 docEl = new f();
2108 docEl.dom = doc;
2109 }
2110 return docEl;
2111 }
2112 var key = el;
2113 if(typeof el != 'string'){ // must be an element
2114 D.generateId(el, 'elgen-');
2115 key = el.id;
2116 }
2117 var element = E.cache[key];
2118 if(!element){
2119 element = new E(key);
2120 if(!element.dom) return null;
2121 E.cache[key] = element;
2122 }else{
2123 element.dom = doc.getElementById(key);
2124 }
2125 return element;
2126 };
2127}();
2128
2129/*
2130 * Gets the globally shared flyweight Element. Use sparingly for
2131 * bulk operations where a unique instance isn't needed.
2132 * Do not store a reference to this element - the dom node
2133 * can be overwritten by other code.
2134 */
2135YAHOO.ext.Element.fly = function(el){
2136 var E = YAHOO.ext.Element;
2137 if(typeof el == 'string'){
2138 el = document.getElementById(el);
2139 }
2140 if(!E._flyweight){
2141 var f = function(){};
2142 f.prototype = E.prototype;
2143 E._flyweight = new f();
2144 }
2145 E._flyweight.dom = el;
2146 return E._flyweight;
2147}
2148
2149/*
2150 * Shorthand function for YAHOO.ext.Element.get()
2151 */
2152getEl = YAHOO.ext.Element.get;
2153
2154YAHOO.util.Event.addListener(window, 'unload', function(){
2155 YAHOO.ext.Element.cache = null;
2156});
2157
diff --git a/frontend/beta/js/YUI-extensions/EventManager.js b/frontend/beta/js/YUI-extensions/EventManager.js
new file mode 100644
index 0000000..f9db759
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/EventManager.js
@@ -0,0 +1,456 @@
1
2/**
3 * @class YAHOO.ext.EventManager
4 * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides
5 * several useful events directly.
6 * See {@link YAHOO.ext.EventObject} for more details on normalized event objects.
7 * @singleton
8 */
9YAHOO.ext.EventManager = new function(){
10 var docReadyEvent;
11 var docReadyProcId;
12 var docReadyState = false;
13 this.ieDeferSrc = false;
14 var resizeEvent;
15 var resizeTask;
16
17 var fireDocReady = function(){
18 if(!docReadyState){
19 docReadyState = true;
20 if(docReadyProcId){
21 clearInterval(docReadyProcId);
22 }
23 if(docReadyEvent){
24 docReadyEvent.fire();
25 }
26 }
27 };
28
29 var initDocReady = function(){
30 docReadyEvent = new YAHOO.util.CustomEvent('documentready');
31 if(document.addEventListener) {
32 YAHOO.util.Event.on(document, "DOMContentLoaded", fireDocReady);
33 }else if(YAHOO.ext.util.Browser.isIE){
34 // inspired by http://www.thefutureoftheweb.com/blog/2006/6/adddomloadevent
35 document.write('<s'+'cript id="ie-deferred-loader" defer="defer" src="' +
36 (YAHOO.ext.EventManager.ieDeferSrc || YAHOO.ext.SSL_SECURE_URL) + '"></s'+'cript>');
37 YAHOO.util.Event.on('ie-deferred-loader', 'readystatechange', function(){
38 if(this.readyState == 'complete'){
39 fireDocReady();
40 }
41 });
42 }else if(YAHOO.ext.util.Browser.isSafari){
43 docReadyProcId = setInterval(function(){
44 var rs = document.readyState;
45 if(rs == 'loaded' || rs == 'complete') {
46 fireDocReady();
47 }
48 }, 10);
49 }
50 // no matter what, make sure it fires on load
51 YAHOO.util.Event.on(window, 'load', fireDocReady);
52 };
53 /**
54 * Places a simple wrapper around an event handler to override the browser event
55 * object with a YAHOO.ext.EventObject
56 * @param {Function} fn The method the event invokes
57 * @param {Object} scope An object that becomes the scope of the handler
58 * @param {boolean} override If true, the obj passed in becomes
59 * the execution scope of the listener
60 * @return {Function} The wrapped function
61 */
62 this.wrap = function(fn, scope, override){
63 var wrappedFn = function(e){
64 YAHOO.ext.EventObject.setEvent(e);
65 fn.call(override ? scope || window : window, YAHOO.ext.EventObject, scope);
66 };
67 return wrappedFn;
68 };
69
70 /**
71 * Appends an event handler
72 *
73 * @param {Object} element The html element to assign the
74 * event to
75 * @param {String} eventName The type of event to append
76 * @param {Function} fn The method the event invokes
77 * @param {Object} scope An object that becomes the scope of the handler
78 * @param {boolean} override If true, the obj passed in becomes
79 * the execution scope of the listener
80 * @return {Function} The wrapper function created (to be used to remove the listener if necessary)
81 */
82 this.addListener = function(element, eventName, fn, scope, override){
83 var wrappedFn = this.wrap(fn, scope, override);
84 YAHOO.util.Event.addListener(element, eventName, wrappedFn);
85 return wrappedFn;
86 };
87
88 /**
89 * Removes an event handler
90 *
91 * @param {Object} element The html element to remove the
92 * event from
93 * @param {String} eventName The type of event to append
94 * @param {Function} wrappedFn The wrapper method returned when adding the listener
95 * @return {Boolean} True if a listener was actually removed
96 */
97 this.removeListener = function(element, eventName, wrappedFn){
98 return YAHOO.util.Event.removeListener(element, eventName, wrappedFn);
99 };
100
101 /**
102 * Appends an event handler (shorthand for addListener)
103 *
104 * @param {Object} element The html element to assign the
105 * event to
106 * @param {String} eventName The type of event to append
107 * @param {Function} fn The method the event invokes
108 * @param {Object} scope An arbitrary object that will be
109 * passed as a parameter to the handler
110 * @param {boolean} override If true, the obj passed in becomes
111 * the execution scope of the listener
112 * @return {Function} The wrapper function created (to be used to remove the listener if necessary)
113 * @method
114 */
115 this.on = this.addListener;
116
117 /**
118 * Fires when the document is ready (before onload and before images are loaded). Can be
119 * accessed shorthanded Ext.onReady().
120 * @param {Function} fn The method the event invokes
121 * @param {Object} scope An object that becomes the scope of the handler
122 * @param {boolean} override If true, the obj passed in becomes
123 * the execution scope of the listener
124 */
125 this.onDocumentReady = function(fn, scope, override){
126 if(docReadyState){ // if it already fired
127 fn.call(override? scope || window : window, scope);
128 return;
129 }
130 if(!docReadyEvent){
131 initDocReady();
132 }
133 docReadyEvent.subscribe(fn, scope, override);
134 }
135
136 /**
137 * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
138 * @param {Function} fn The method the event invokes
139 * @param {Object} scope An object that becomes the scope of the handler
140 * @param {boolean} override If true, the obj passed in becomes
141 * the execution scope of the listener
142 */
143 this.onWindowResize = function(fn, scope, override){
144 if(!resizeEvent){
145 resizeEvent = new YAHOO.util.CustomEvent('windowresize');
146 resizeTask = new YAHOO.ext.util.DelayedTask(function(){
147 resizeEvent.fireDirect(YAHOO.util.Dom.getViewportWidth(), YAHOO.util.Dom.getViewportHeight());
148 });
149 YAHOO.util.Event.on(window, 'resize', function(){
150 resizeTask.delay(50);
151 });
152 }
153 resizeEvent.subscribe(fn, scope, override);
154 };
155
156 /**
157 * Removes the passed window resize listener.
158 * @param {Function} fn The method the event invokes
159 * @param {Object} scope The scope of handler
160 */
161 this.removeResizeListener = function(fn, scope){
162 if(resizeEvent){
163 resizeEvent.unsubscribe(fn, scope);
164 }
165 };
166
167 this.fireResize = function(){
168 if(resizeEvent){
169 resizeEvent.fireDirect(YAHOO.util.Dom.getViewportWidth(), YAHOO.util.Dom.getViewportHeight());
170 }
171 };
172};
173
174YAHOO.ext.onReady = YAHOO.ext.EventManager.onDocumentReady;
175
176/**
177 * @class YAHOO.ext.EventObject
178 * EventObject exposes the Yahoo! UI Event functionality directly on the object
179 * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code
180 * (All the YAHOO.util.Event methods throw javascript errors if the passed event is null).
181 * To get an EventObject instead of the standard browser event,
182 * your must register your listener thru the {@link YAHOO.ext.EventManager} or directly on an Element
183 * with {@link YAHOO.ext.Element#addManagedListener} or the shorthanded equivalent {@link YAHOO.ext.Element#mon}.<br>
184 * Example:
185 * <pre><code>
186 fu<>nction handleClick(e){ // e is not a standard event object, it is a YAHOO.ext.EventObject
187 e.preventDefault();
188 var target = e.getTarget();
189 ...
190 }
191 var myDiv = getEl('myDiv');
192 myDiv.mon('click', handleClick);
193 //or
194 YAHOO.ext.EventManager.on('myDiv', 'click', handleClick);
195 YAHOO.ext.EventManager.addListener('myDiv', 'click', handleClick);
196 </code></pre>
197 * @singleton
198 */
199YAHOO.ext.EventObject = new function(){
200 /** The normal browser event */
201 this.browserEvent = null;
202 /** The button pressed in a mouse event */
203 this.button = -1;
204 /** True if the shift key was down during the event */
205 this.shiftKey = false;
206 /** True if the control key was down during the event */
207 this.ctrlKey = false;
208 /** True if the alt key was down during the event */
209 this.altKey = false;
210
211 /** Key constant @type Number */
212 this.BACKSPACE = 8;
213 /** Key constant @type Number */
214 this.TAB = 9;
215 /** Key constant @type Number */
216 this.RETURN = 13;
217 /** Key constant @type Number */
218 this.ESC = 27;
219 /** Key constant @type Number */
220 this.SPACE = 32;
221 /** Key constant @type Number */
222 this.PAGEUP = 33;
223 /** Key constant @type Number */
224 this.PAGEDOWN = 34;
225 /** Key constant @type Number */
226 this.END = 35;
227 /** Key constant @type Number */
228 this.HOME = 36;
229 /** Key constant @type Number */
230 this.LEFT = 37;
231 /** Key constant @type Number */
232 this.UP = 38;
233 /** Key constant @type Number */
234 this.RIGHT = 39;
235 /** Key constant @type Number */
236 this.DOWN = 40;
237 /** Key constant @type Number */
238 this.DELETE = 46;
239 /** Key constant @type Number */
240 this.F5 = 116;
241
242 /** @private */
243 this.setEvent = function(e){
244 if(e == this){ // already wrapped
245 return this;
246 }
247 this.browserEvent = e;
248 if(e){
249 this.button = e.button;
250 this.shiftKey = e.shiftKey;
251 this.ctrlKey = e.ctrlKey;
252 this.altKey = e.altKey;
253 }else{
254 this.button = -1;
255 this.shiftKey = false;
256 this.ctrlKey = false;
257 this.altKey = false;
258 }
259 return this;
260 };
261
262 /**
263 * Stop the event. Calls YAHOO.util.Event.stopEvent() if the event is not null.
264 */
265 this.stopEvent = function(){
266 if(this.browserEvent){
267 YAHOO.util.Event.stopEvent(this.browserEvent);
268 }
269 };
270
271 /**
272 * Prevents the browsers default handling of the event. Calls YAHOO.util.Event.preventDefault() if the event is not null.
273 */
274 this.preventDefault = function(){
275 if(this.browserEvent){
276 YAHOO.util.Event.preventDefault(this.browserEvent);
277 }
278 };
279
280 /** @private */
281 this.isNavKeyPress = function(){
282 return (this.browserEvent.keyCode && this.browserEvent.keyCode >= 33 && this.browserEvent.keyCode <= 40);
283 };
284
285 /**
286 * Cancels bubbling of the event. Calls YAHOO.util.Event.stopPropagation() if the event is not null.
287 */
288 this.stopPropagation = function(){
289 if(this.browserEvent){
290 YAHOO.util.Event.stopPropagation(this.browserEvent);
291 }
292 };
293
294 /**
295 * Gets the key code for the event. Returns value from YAHOO.util.Event.getCharCode() if the event is not null.
296 * @return {Number}
297 */
298 this.getCharCode = function(){
299 if(this.browserEvent){
300 return YAHOO.util.Event.getCharCode(this.browserEvent);
301 }
302 return null;
303 };
304
305 /**
306 * Returns a browsers key for a keydown event
307 * @return {Number} The key code
308 */
309 this.getKey = function(){
310 if(this.browserEvent){
311 return this.browserEvent.keyCode || this.browserEvent.charCode;
312 }
313 return null;
314 };
315
316 /**
317 * Gets the x coordinate of the event. Returns value from YAHOO.util.Event.getPageX() if the event is not null.
318 * @return {Number}
319 */
320 this.getPageX = function(){
321 if(this.browserEvent){
322 return YAHOO.util.Event.getPageX(this.browserEvent);
323 }
324 return null;
325 };
326
327 /**
328 * Gets the y coordinate of the event. Returns value from YAHOO.util.Event.getPageY() if the event is not null.
329 * @return {Number}
330 */
331 this.getPageY = function(){
332 if(this.browserEvent){
333 return YAHOO.util.Event.getPageY(this.browserEvent);
334 }
335 return null;
336 };
337
338 /**
339 * Gets the time of the event. Returns value from YAHOO.util.Event.getTime() if the event is not null.
340 * @return {Number}
341 */
342 this.getTime = function(){
343 if(this.browserEvent){
344 return YAHOO.util.Event.getTime(this.browserEvent);
345 }
346 return null;
347 };
348
349 /**
350 * Gets the page coordinates of the event. Returns value from YAHOO.util.Event.getXY() if the event is not null.
351 * @return {Array} The xy values like [x, y]
352 */
353 this.getXY = function(){
354 if(this.browserEvent){
355 return YAHOO.util.Event.getXY(this.browserEvent);
356 }
357 return [];
358 };
359
360 /**
361 * Gets the target for the event. Returns value from YAHOO.util.Event.getTarget() if the event is not null.
362 * @return {HTMLelement}
363 */
364 this.getTarget = function(){
365 if(this.browserEvent){
366 return YAHOO.util.Event.getTarget(this.browserEvent);
367 }
368 return null;
369 };
370
371 /**
372 * Walk up the DOM looking for a particular target - if the default target matches, it is returned.
373 * @param {String} className The class name to look for or null
374 * @param {String} tagName (optional) The tag name to look for
375 * @return {HTMLelement}
376 */
377 this.findTarget = function(className, tagName){
378 if(tagName) tagName = tagName.toLowerCase();
379 if(this.browserEvent){
380 function isMatch(el){
381 if(!el){
382 return false;
383 }
384 if(className && !YAHOO.util.Dom.hasClass(el, className)){
385 return false;
386 }
387 if(tagName && el.tagName.toLowerCase() != tagName){
388 return false;
389 }
390 return true;
391 };
392
393 var t = this.getTarget();
394 if(!t || isMatch(t)){
395 return t;
396 }
397 var p = t.parentNode;
398 var b = document.body;
399 while(p && p != b){
400 if(isMatch(p)){
401 return p;
402 }
403 p = p.parentNode;
404 }
405 }
406 return null;
407 };
408 /**
409 * Gets the related target. Returns value from YAHOO.util.Event.getRelatedTarget() if the event is not null.
410 * @return {HTMLElement}
411 */
412 this.getRelatedTarget = function(){
413 if(this.browserEvent){
414 return YAHOO.util.Event.getRelatedTarget(this.browserEvent);
415 }
416 return null;
417 };
418
419 /**
420 * Normalizes mouse wheel delta across browsers
421 * @return {Number} The delta
422 */
423 this.getWheelDelta = function(){
424 var e = this.browserEvent;
425 var delta = 0;
426 if(e.wheelDelta){ /* IE/Opera. */
427 delta = e.wheelDelta/120;
428 /* In Opera 9, delta differs in sign as compared to IE. */
429 if(window.opera) delta = -delta;
430 }else if(e.detail){ /* Mozilla case. */
431 delta = -e.detail/3;
432 }
433 return delta;
434 };
435
436 /**
437 * Returns true if the control, shift or alt key was pressed during this event.
438 * @return {Boolean}
439 */
440 this.hasModifier = function(){
441 return this.ctrlKey || this.altKey || this.shiftKey;
442 };
443
444 /**
445 * Returns true if the target of this event equals el or is a child of el
446 * @param {String/HTMLElement/Element} el
447 * @return {Boolean}
448 */
449 this.within = function(el){
450 el = getEl(el);
451 var t = this.getTarget();
452 return t && el && (el.dom == t || YAHOO.util.Dom.isAncestor(el.dom, t));
453 }
454}();
455
456
diff --git a/frontend/beta/js/YUI-extensions/JSON.js b/frontend/beta/js/YUI-extensions/JSON.js
new file mode 100644
index 0000000..ffc9573
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/JSON.js
@@ -0,0 +1,132 @@
1/**
2 * @class YAHOO.ext.util.JSON
3 * Modified version of Douglas Crockford's json.js that doesn't
4 * mess with the Object prototype
5 * http://www.json.org/js.html
6 * @singleton
7 */
8YAHOO.ext.util.JSON = new function(){
9 var useHasOwn = {}.hasOwnProperty ? true : false;
10
11 // crashes Safari in some instances
12 //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13
14 var pad = function(n) {
15 return n < 10 ? '0' + n : n;
16 };
17
18 var m = {
19 '\b': '\\b',
20 '\t': '\\t',
21 '\n': '\\n',
22 '\f': '\\f',
23 '\r': '\\r',
24 '"' : '\\"',
25 '\\': '\\\\'
26 };
27
28 var encodeString = function(s){
29 if (/["\\\x00-\x1f]/.test(s)) {
30 return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
31 var c = m[b];
32 if(c){
33 return c;
34 }
35 c = b.charCodeAt();
36 return '\\u00' +
37 Math.floor(c / 16).toString(16) +
38 (c % 16).toString(16);
39 }) + '"';
40 }
41 return '"' + s + '"';
42 };
43
44 var encodeArray = function(o){
45 var a = ['['], b, i, l = o.length, v;
46 for (i = 0; i < l; i += 1) {
47 v = o[i];
48 switch (typeof v) {
49 case 'undefined':
50 case 'function':
51 case 'unknown':
52 break;
53 default:
54 if (b) {
55 a.push(',');
56 }
57 a.push(v === null ? "null" : YAHOO.ext.util.JSON.encode(v));
58 b = true;
59 }
60 }
61 a.push(']');
62 return a.join('');
63 };
64
65 var encodeDate = function(o){
66 return '"' + o.getFullYear() + '-' +
67 pad(o.getMonth() + 1) + '-' +
68 pad(o.getDate()) + 'T' +
69 pad(o.getHours()) + ':' +
70 pad(o.getMinutes()) + ':' +
71 pad(o.getSeconds()) + '"';
72 };
73
74 /**
75 * Encodes an Object, Array or other value
76 * @param {Mixed} o The variable to encode
77 * @return {String} The JSON string
78 */
79 this.encode = function(o){
80 if(typeof o == 'undefined' || o === null){
81 return 'null';
82 }else if(o instanceof Array){
83 return encodeArray(o);
84 }else if(o instanceof Date){
85 return encodeDate(o);
86 }else if(typeof o == 'string'){
87 return encodeString(o);
88 }else if(typeof o == 'number'){
89 return isFinite(o) ? String(o) : "null";
90 }else if(typeof o == 'boolean'){
91 return String(o);
92 }else {
93 var a = ['{'], b, i, v;
94 for (var i in o) {
95 if(!useHasOwn || o.hasOwnProperty(i)) {
96 v = o[i];
97 switch (typeof v) {
98 case 'undefined':
99 case 'function':
100 case 'unknown':
101 break;
102 default:
103 if(b){
104 a.push(',');
105 }
106 a.push(this.encode(i), ':',
107 v === null ? "null" : this.encode(v));
108 b = true;
109 }
110 }
111 }
112 a.push('}');
113 return a.join('');
114 }
115 };
116
117 /**
118 * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
119 * @param {String} json The JSON string
120 * @return {Object} The resulting object
121 */
122 this.decode = function(json){
123 // although crockford had a good idea, this line crashes safari in some instances
124 //try{
125 //if(validRE.test(json)) {
126 return eval('(' + json + ')');
127 // }
128 // }catch(e){
129 // }
130 // throw new SyntaxError("parseJSON");
131 };
132}();
diff --git a/frontend/beta/js/YUI-extensions/KeyMap.js b/frontend/beta/js/YUI-extensions/KeyMap.js
new file mode 100644
index 0000000..c5af567
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/KeyMap.js
@@ -0,0 +1,135 @@
1/**
2 * @class YAHOO.ext.KeyMap
3 * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
4 * A KeyMap can also handle a string representation of keys.<br />
5 * Usage:
6 <pre><code>
7 // map one key by key code
8 var map = new YAHOO.ext.KeyMap('my-element', {
9 key: 13,
10 fn: myHandler,
11 scope: myObject
12 });
13
14 // map multiple keys to one action by string
15 var map = new YAHOO.ext.KeyMap('my-element', {
16 key: "a\r\n\t",
17 fn: myHandler,
18 scope: myObject
19 });
20
21 // map multiple keys to multiple actions by strings and array of codes
22 var map = new YAHOO.ext.KeyMap('my-element', [
23 {
24 key: [10,13],
25 fn: function(){ alert('Return was pressed'); }
26 }, {
27 key: "abc",
28 fn: function(){ alert('a, b or c was pressed'); }
29 }, {
30 key: "\t",
31 ctrl:true,
32 shift:true,
33 fn: function(){ alert('Control + shift + tab was pressed.'); }
34 }
35]);
36 </code></pre>
37* <b>Note: A KepMap starts enabled</b>
38* @constructor
39* @param {String/HTMLElement/YAHOO.ext.Element} el The element to bind to
40* @param {Object} config The config
41* @param {String} eventName (optional) The event to bind to (Defaults to "keydown").
42 */
43YAHOO.ext.KeyMap = function(el, config, eventName){
44 this.el = getEl(el);
45 this.eventName = eventName || 'keydown';
46 this.bindings = [];
47 if(config instanceof Array){
48 for(var i = 0, len = config.length; i < len; i++){
49 this.addBinding(config[i]);
50 }
51 }else{
52 this.addBinding(config);
53 }
54 this.keyDownDelegate = YAHOO.ext.EventManager.wrap(this.handleKeyDown, this, true);
55 this.enable();
56}
57
58YAHOO.ext.KeyMap.prototype = {
59 /**
60 * Add a new binding to this KeyMap
61 * @param {Object} config A single KeyMap config
62 */
63 addBinding : function(config){
64 var keyCode = config.key,
65 shift = config.shift,
66 ctrl = config.ctrl,
67 alt = config.alt,
68 fn = config.fn,
69 scope = config.scope;
70 if(typeof keyCode == 'string'){
71 var ks = [];
72 var keyString = keyCode.toUpperCase();
73 for(var j = 0, len = keyString.length; j < len; j++){
74 ks.push(keyString.charCodeAt(j));
75 }
76 keyCode = ks;
77 }
78 var keyArray = keyCode instanceof Array;
79 var handler = function(e){
80 if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) && (!alt || e.altKey)){
81 var k = e.getKey();
82 if(keyArray){
83 for(var i = 0, len = keyCode.length; i < len; i++){
84 if(keyCode[i] == k){
85 fn.call(scope || window, k, e);
86 return;
87 }
88 }
89 }else{
90 if(k == keyCode){
91 fn.call(scope || window, k, e);
92 }
93 }
94 }
95 };
96 this.bindings.push(handler);
97 },
98
99 handleKeyDown : function(e){
100 if(this.enabled){ //just in case
101 var b = this.bindings;
102 for(var i = 0, len = b.length; i < len; i++){
103 b[i](e);
104 }
105 }
106 },
107
108 /**
109 * Returns true if this KepMap is enabled
110 * @return {Boolean}
111 */
112 isEnabled : function(){
113 return this.enabled;
114 },
115
116 /**
117 * Enable this KeyMap
118 */
119 enable: function(){
120 if(!this.enabled){
121 this.el.on(this.eventName, this.keyDownDelegate);
122 this.enabled = true;
123 }
124 },
125
126 /**
127 * Disable this KeyMap
128 */
129 disable: function(){
130 if(this.enabled){
131 this.el.removeListener(this.eventName, this.keyDownDelegate);
132 this.enabled = false;
133 }
134 }
135};
diff --git a/frontend/beta/js/YUI-extensions/Layer.js b/frontend/beta/js/YUI-extensions/Layer.js
new file mode 100644
index 0000000..9eeaf7c
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/Layer.js
@@ -0,0 +1,246 @@
1/**
2 * @class YAHOO.ext.Layer
3 * @extends YAHOO.ext.Element
4 * An extended Element object that supports a shadow and shim, constrain to viewport and
5 * automatic maintaining of shadow/shim positions.
6 * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7 * @cfg {String/Boolean} shadow True to create a shadow element with default class "ylayer-shadow" or
8 * you can pass a string with a css class name. False turns off the shadow.
9 * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: 'div', cls: 'ylayer'}).
10 * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
11 * @cfg {String} cls CSS class to add to the element
12 * @cfg {Number} zindex Starting z-index (defaults to 11000!)
13 * @cfg {Number} shadowOffset Offset for the shadow (defaults to 3)
14 * @constructor
15 * @param {Object} config
16 * @param {String/HTMLElement} existingEl (optional) Uses an existing dom element. If the element is not found it creates it.
17 */
18YAHOO.ext.Layer = function(config, existingEl){
19 config = config || {};
20 var dh = YAHOO.ext.DomHelper;
21 if(existingEl){
22 this.dom = YAHOO.util.Dom.get(existingEl);
23 }
24 if(!this.dom){
25 var o = config.dh || {tag: 'div', cls: 'ylayer'};
26 this.dom = dh.insertBefore(document.body.firstChild, o);
27 }
28 if(config.cls){
29 this.addClass(config.cls);
30 }
31 this.constrain = config.constrain !== false;
32 this.visibilityMode = YAHOO.ext.Element.VISIBILITY;
33 this.id = YAHOO.util.Dom.generateId(this.dom);
34 var zindex = (config.zindex || parseInt(this.getStyle('z-index'), 10)) || 11000;
35 this.setAbsolutePositioned(zindex);
36 if(config.shadow){
37 var cls = (typeof config.shadow == 'string' ? config.shadow : 'ylayer-shadow');
38 this.shadow = dh.insertBefore(this.dom,
39 {tag: 'div', cls: cls}, true);
40 this.shadowOffset = config.shadowOffset || 3;
41 this.shadow.setAbsolutePositioned(zindex-1);
42 }else{
43 this.shadowOffset = 0;
44 }
45 var b = YAHOO.ext.util.Browser;
46 if(config.shim !== false && (b.isIE || (b.isGecko && b.isMac))){
47 this.shim = this.createShim();
48 this.shim.setOpacity(0);
49 this.shim.setAbsolutePositioned(zindex-2);
50 }
51 this.hide();
52};
53YAHOO.extendX(YAHOO.ext.Layer, YAHOO.ext.Element, {
54 sync : function(doShow){
55 if(this.isVisible() && (this.shadow || this.shim)){
56 var b = this.getBox();
57 if(this.shim){
58 if(doShow){
59 this.shim.show();
60 }
61 this.shim.setBox(b);
62 }
63 if(this.shadow){
64 if(doShow){
65 this.shadow.show();
66 }
67 b.x += this.shadowOffset;
68 b.y += this.shadowOffset;
69 this.shadow.setBox(b);
70 }
71 }
72 },
73
74 syncLocalXY : function(){
75 var l = this.getLeft(true);
76 var t = this.getTop(true);
77 if(this.shim){
78 this.shim.setLeftTop(l, t);
79 }
80 if(this.shadow){
81 this.shadow.setLeftTop(l + this.shadowOffset,
82 t + this.shadowOffset);
83 }
84 },
85
86 hideUnders : function(negOffset){
87 if(this.shadow){
88 this.shadow.hide();
89 if(negOffset){
90 this.shadow.setLeftTop(-10000,-10000);
91 }
92 }
93 if(this.shim){
94 this.shim.hide();
95 if(negOffset){
96 this.shim.setLeftTop(-10000,-10000);
97 }
98 }
99 },
100
101 constrainXY : function(){
102 if(this.constrain){
103 var vw = YAHOO.util.Dom.getViewportWidth(),
104 vh = YAHOO.util.Dom.getViewportHeight();
105 var xy = this.getXY();
106 var x = xy[0], y = xy[1];
107 var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
108 // only move it if it needs it
109 var moved = false;
110 // first validate right/bottom
111 if(x + w > vw){
112 x = vw - w;
113 moved = true;
114 }
115 if(y + h > vh){
116 y = vh - h;
117 moved = true;
118 }
119 // then make sure top/left isn't negative
120 if(x < 0){
121 x = 0;
122 moved = true;
123 }
124 if(y < 0){
125 y = 0;
126 moved = true;
127 }
128 if(moved){
129 xy = [x, y];
130 this.lastXY = xy;
131 this.beforeAction();
132 YAHOO.ext.Layer.superclass.setXY.call(this, xy);
133 this.sync(true);
134 }
135 }
136 },
137
138 setVisible : function(v, a, d, c, e){
139 if(this.lastXY){
140 YAHOO.ext.Layer.superclass.setXY.call(this, this.lastXY);
141 }
142 if(a && v){
143 var cb = function(){
144 this.sync(true);
145 if(c){
146 c();
147 }
148 }.createDelegate(this);
149 YAHOO.ext.Layer.superclass.setVisible.call(this, true, true, d, cb, e);
150 }else{
151 if(!v){
152 this.hideUnders(true);
153 }
154 var cb = c;
155 if(a){
156 cb = function(){
157 this.setLeftTop(-10000,-10000);
158 if(c){
159 c();
160 }
161 }.createDelegate(this);
162 }
163 YAHOO.ext.Layer.superclass.setVisible.call(this, v, a, d, cb, e);
164 if(v){
165 this.sync(true);
166 }else if(!a){
167 this.setLeftTop(-10000,-10000);
168 }
169 }
170 },
171
172 beforeAction : function(){
173 if(this.shadow){
174 this.shadow.hide();
175 }
176 },
177
178 setXY : function(xy, a, d, c, e){
179 this.lastXY = xy;
180 this.beforeAction();
181 var cb = this.createCB(c);
182 YAHOO.ext.Layer.superclass.setXY.call(this, xy, a, d, cb, e);
183 if(!a){
184 cb();
185 }
186 },
187
188 createCB : function(c){
189 var el = this;
190 return function(){
191 el.constrainXY();
192 el.sync(true);
193 if(c){
194 c();
195 }
196 };
197 },
198
199 setX : function(x, a, d, c, e){
200 this.setXY([x, this.getY()], a, d, c, e);
201 },
202
203 setY : function(y, a, d, c, e){
204 this.setXY([this.getX(), y], a, d, c, e);
205 },
206
207 setSize : function(w, h, a, d, c, e){
208 this.beforeAction();
209 var cb = this.createCB(c);
210 YAHOO.ext.Layer.superclass.setSize.call(this, w, h, a, d, cb, e);
211 if(!a){
212 cb();
213 }
214 },
215
216 setWidth : function(w, a, d, c, e){
217 this.beforeAction();
218 var cb = this.createCB(c);
219 YAHOO.ext.Layer.superclass.setWidth.call(this, w, a, d, cb, e);
220 if(!a){
221 cb();
222 }
223 },
224
225 setHeight : function(h, a, d, c, e){
226 this.beforeAction();
227 var cb = this.createCB(c);
228 YAHOO.ext.Layer.superclass.setHeight.call(this, h, a, d, cb, e);
229 if(!a){
230 cb();
231 }
232 },
233
234 setBounds : function(x, y, w, h, a, d, c, e){
235 this.beforeAction();
236 var cb = this.createCB(c);
237 if(!a){
238 YAHOO.ext.Layer.superclass.setXY.call(this, [x, y]);
239 YAHOO.ext.Layer.superclass.setSize.call(this, w, h, a, d, cb, e);
240 cb();
241 }else{
242 YAHOO.ext.Layer.superclass.setBounds.call(this, x, y, w, h, a, d, cb, e);
243 }
244 return this;
245 }
246});
diff --git a/frontend/beta/js/YUI-extensions/MixedCollection.js b/frontend/beta/js/YUI-extensions/MixedCollection.js
new file mode 100644
index 0000000..2e3d88a
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/MixedCollection.js
@@ -0,0 +1,344 @@
1/**
2 * @class YAHOO.ext.util.MixedCollection
3 * A Collection class that maintains both numeric indexes and keys and exposes events.<br>
4 * @constructor
5 * @param {Boolean} allowFunctions True if the addAll function should add function references
6 * to the collection.
7 */
8YAHOO.ext.util.MixedCollection = function(allowFunctions){
9 this.items = [];
10 this.keys = [];
11 this.events = {
12 /**
13 * @event clear
14 * Fires when the collection is cleared.
15 */
16 'clear' : new YAHOO.util.CustomEvent('clear'),
17 /**
18 * @event add
19 * Fires when an item is added to the collection.
20 * @param {Number} index The index at which the item was added.
21 * @param {Object} o The item added.
22 * @param {String} key The key associated with the added item.
23 */
24 'add' : new YAHOO.util.CustomEvent('add'),
25 /**
26 * @event replace
27 * Fires when an item is replaced in the collection.
28 * @param {String} key he key associated with the new added.
29 * @param {Object} old The item being replaced.
30 * @param {Object} new The new item.
31 */
32 'replace' : new YAHOO.util.CustomEvent('replace'),
33 /**
34 * @event remove
35 * Fires when an item is removed from the collection.
36 * @param {Object} o The item being removed.
37 * @param {String} key (optional) The key associated with the removed item.
38 */
39 'remove' : new YAHOO.util.CustomEvent('remove')
40 }
41 this.allowFunctions = allowFunctions === true;
42};
43
44YAHOO.extendX(YAHOO.ext.util.MixedCollection, YAHOO.ext.util.Observable, {
45 allowFunctions : false,
46
47/**
48 * Adds an item to the collection.
49 * @param {String} key The key to associate with the item
50 * @param {Object} o The item to add.
51 * @return {Object} The item added.
52 */
53 add : function(key, o){
54 if(arguments.length == 1){
55 o = arguments[0];
56 key = this.getKey(o);
57 }
58 this.items.push(o);
59 if(typeof key != 'undefined' && key != null){
60 this.items[key] = o;
61 this.keys.push(key);
62 }
63 this.fireEvent('add', this.items.length-1, o, key);
64 return o;
65 },
66
67/**
68 * MixedCollection has a generic way to fetch keys if you implement getKey.
69 <pre><code>
70 // normal way
71 var mc = new YAHOO.ext.util.MixedCollection();
72 mc.add(someEl.dom.id, someEl);
73 mc.add(otherEl.dom.id, otherEl);
74 //and so on
75
76 // using getKey
77 var mc = new YAHOO.ext.util.MixedCollection();
78 mc.getKey = function(el){
79 return el.dom.id;
80 }
81 mc.add(someEl);
82 mc.add(otherEl);
83 // etc
84 </code>
85 * @param o {Object} The item for which to find the key.
86 * @return {Object} The key for the passed item.
87 */
88 getKey : function(o){
89 return null;
90 },
91
92/**
93 * Replaces an item in the collection.
94 * @param {String} key The key associated with the item to replace, or the item to replace.
95 * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
96 * @return {Object} The new item.
97 */
98 replace : function(key, o){
99 if(arguments.length == 1){
100 o = arguments[0];
101 key = this.getKey(o);
102 }
103 if(typeof this.items[key] == 'undefined'){
104 return this.add(key, o);
105 }
106 var old = this.items[key];
107 if(typeof key == 'number'){ // array index key
108 this.items[key] = o;
109 }else{
110 var index = this.indexOfKey(key);
111 this.items[index] = o;
112 this.items[key] = o;
113 }
114 this.fireEvent('replace', key, old, o);
115 return o;
116 },
117
118/**
119 * Adds all elements of an Array or an Object to the collection.
120 * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
121 * an Array of values, each of which are added to the collection.
122 */
123 addAll : function(objs){
124 if(arguments.length > 1 || objs instanceof Array){
125 var args = arguments.length > 1 ? arguments : objs;
126 for(var i = 0, len = args.length; i < len; i++){
127 this.add(args[i]);
128 }
129 }else{
130 for(var key in objs){
131 if(this.allowFunctions || typeof objs[key] != 'function'){
132 this.add(objs[key], key);
133 }
134 }
135 }
136 },
137
138/**
139 * Executes the specified function once for every item in the collection, passing each
140 * item as the first and only parameter.
141 * @param {Function} fn The function to execute for each item.
142 * @param {Object} scope (optional) The scope in which to execute the function.
143 */
144 each : function(fn, scope){
145 for(var i = 0, len = this.items.length; i < len; i++){
146 fn.call(scope || window, this.items[i]);
147 }
148 },
149
150/**
151 * Executes the specified function once for every key in the collection, passing each
152 * key, and its associated item as the first two parameters.
153 * @param {Function} fn The function to execute for each item.
154 * @param {Object} scope (optional) The scope in which to execute the function.
155 */
156 eachKey : function(fn, scope){
157 for(var i = 0, len = this.keys.length; i < len; i++){
158 fn.call(scope || window, this.keys[i], this.items[i]);
159 }
160 },
161
162/**
163 * Returns the first item in the collection which elicits a true return value from the
164 * passed selection function.
165 * @param {Function} fn The selection function to execute for each item.
166 * @param {Object} scope (optional) The scope in which to execute the function.
167 * @return {Object} The first item in the collection which returned true from the selection function.
168 */
169 find : function(fn, scope){
170 for(var i = 0, len = this.items.length; i < len; i++){
171 if(fn.call(scope || window, this.items[i])){
172 return this.items[i];
173 }
174 }
175 return null;
176 },
177
178/**
179 * Inserts an item at the specified index in the collection.
180 * @param {Number} index The index to insert the item at.
181 * @param {String} key The key to associate with the new item, or the item itself.
182 * @param {Object} o (optional) If the second parameter was a key, the new item.
183 * @return {Object} The item inserted.
184 */
185 insert : function(index, key, o){
186 if(arguments.length == 2){
187 o = arguments[1];
188 key = this.getKey(o);
189 }
190 if(index >= this.items.length){
191 return this.add(o, key);
192 }
193 this.items.splice(index, 0, o);
194 if(typeof key != 'undefined' && key != null){
195 this.items[key] = o;
196 this.keys.splice(index, 0, key);
197 }
198 this.fireEvent('add', index, o, key);
199 return o;
200 },
201
202/**
203 * Removed an item from the collection.
204 * @param {Object} o The item to remove.
205 * @return {Object} The item removed.
206 */
207 remove : function(o){
208 var index = this.indexOf(o);
209 this.items.splice(index, 1);
210 if(typeof this.keys[index] != 'undefined'){
211 var key = this.keys[index];
212 this.keys.splice(index, 1);
213 delete this.items[key];
214 }
215 this.fireEvent('remove', o);
216 return o;
217 },
218
219/**
220 * Remove an item from a specified index in the collection.
221 * @param {Number} index The index within the collection of the item to remove.
222 */
223 removeAt : function(index){
224 this.items.splice(index, 1);
225 var key = this.keys[index];
226 if(typeof key != 'undefined'){
227 this.keys.splice(index, 1);
228 delete this.items[key];
229 }
230 this.fireEvent('remove', o, key);
231 },
232
233/**
234 * Removed an item associated with the passed key fom the collection.
235 * @param {String} key The key of the item to remove.
236 */
237 removeKey : function(key){
238 var o = this.items[key];
239 var index = this.indexOf(o);
240 this.items.splice(index, 1);
241 this.keys.splice(index, 1);
242 delete this.items[key];
243 this.fireEvent('remove', o, key);
244 },
245
246/**
247 * Returns the number of items in the collection.
248 * @return {Number} the number of items in the collection.
249 */
250 getCount : function(){
251 return this.items.length;
252 },
253
254/**
255 * Returns index within the collection of the passed Object.
256 * @param {Object} o The item to find the index of.
257 * @return {Number} index of the item.
258 */
259 indexOf : function(o){
260 if(!this.items.indexOf){
261 for(var i = 0, len = this.items.length; i < len; i++){
262 if(this.items[i] == o) return i;
263 }
264 return -1;
265 }else{
266 return this.items.indexOf(o);
267 }
268 },
269
270/**
271 * Returns index within the collection of the passed key.
272 * @param {String} key The key to find the index of.
273 * @return {Number} index of the key.
274 */
275 indexOfKey : function(key){
276 if(!this.keys.indexOf){
277 for(var i = 0, len = this.keys.length; i < len; i++){
278 if(this.keys[i] == key) return i;
279 }
280 return -1;
281 }else{
282 return this.keys.indexOf(key);
283 }
284 },
285
286/**
287 * Returns the item associated with the passed key.
288 * @param {String/Number} key The key or index of the item.
289 * @return {Object} The item associated with the passed key.
290 */
291 item : function(key){
292 return this.items[key];
293 },
294
295/**
296 * Returns true if the collection contains the passed Object as an item.
297 * @param {Object} o The Object to look for in the collection.
298 * @return {Boolean} True if the collection contains the Object as an item.
299 */
300 contains : function(o){
301 return this.indexOf(o) != -1;
302 },
303
304/**
305 * Returns true if the collection contains the passed Object as a key.
306 * @param {String} key The key to look for in the collection.
307 * @return {Boolean} True if the collection contains the Object as a key.
308 */
309 containsKey : function(key){
310 return typeof this.items[key] != 'undefined';
311 },
312
313/**
314 * Removes all items from the collection.
315 */
316 clear : function(o){
317 this.items = [];
318 this.keys = [];
319 this.fireEvent('clear');
320 },
321
322/**
323 * Returns the first item in the collection.
324 * @return {Object} the first item in the collection..
325 */
326 first : function(){
327 return this.items[0];
328 },
329
330/**
331 * Returns the last item in the collection.
332 * @return {Object} the last item in the collection..
333 */
334 last : function(){
335 return this.items[this.items.length];
336 }
337});
338/**
339 * Returns the item associated with the passed key or index.
340 * @method
341 * @param {String/Number} key The key or index of the item.
342 * @return {Object} The item associated with the passed key.
343 */
344YAHOO.ext.util.MixedCollection.prototype.get = YAHOO.ext.util.MixedCollection.prototype.item;
diff --git a/frontend/beta/js/YUI-extensions/State.js b/frontend/beta/js/YUI-extensions/State.js
new file mode 100644
index 0000000..76a9618
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/State.js
@@ -0,0 +1,264 @@
1YAHOO.namespace('ext.state');
2/**
3 * @class YAHOO.ext.state.Provider
4 * Abstract base class for provider implementations. This class provides methods
5 * for encoding and decoding <b>typed</b> variables including dates and defines the
6 * Provider interface.
7 */
8YAHOO.ext.state.Provider = function(){
9 YAHOO.ext.state.Provider.superclass.constructor.call(this);
10 /**
11 * @event statechange
12 * Fires when a state change occurs.
13 * @param {Provider} this
14 * @param {String} key The state key which was changed
15 * @param {String} value The encoded value for the state
16 */
17 this.events = {
18 'statechange': new YAHOO.util.CustomEvent('statechange')
19 };
20 this.state = {};
21};
22YAHOO.extendX(YAHOO.ext.state.Provider, YAHOO.ext.util.Observable, {
23 /**
24 * Get the current value for a key.
25 * @param {String} name
26 * @param {Mixed} defaultValue
27 * @return {Mixed}
28 */
29 get : function(name, defaultValue){
30 return typeof this.state[name] == 'undefined' ?
31 defaultValue : this.state[name];
32 },
33
34 /**
35 * Clear a value from the state.
36 */
37 clear : function(name){
38 delete this.state[name];
39 this.fireEvent('statechange', this, name, null);
40 },
41
42 /**
43 * Set the value for a key.
44 * @param {String} name
45 * @param {Mixed} value
46 */
47 set : function(name, value){
48 this.state[name] = value;
49 this.fireEvent('statechange', this, name, value);
50 },
51
52 /**
53 * Decodes a string previously encoded with {@link #encodeValue}.
54 * @param {String} value
55 * @return {Mixed} The value
56 */
57 decodeValue : function(cookie){
58 var re = /^(a|n|d|b|s|o)\:(.*)$/;
59 var matches = re.exec(unescape(cookie));
60 if(!matches || !matches[1]) return; // non state cookie
61 var type = matches[1];
62 var v = matches[2];
63 switch(type){
64 case 'n':
65 return parseFloat(v);
66 case 'd':
67 return new Date(Date.parse(v));
68 case 'b':
69 return (v == '1');
70 case 'a':
71 var all = [];
72 var values = v.split('^');
73 for(var i = 0, len = values.length; i < len; i++){
74 all.push(this.decodeValue(values[i]))
75 }
76 return all;
77 case 'o':
78 var all = {};
79 var values = v.split('^');
80 for(var i = 0, len = values.length; i < len; i++){
81 var kv = values[i].split('=');
82 all[kv[0]] = this.decodeValue(kv[1]);
83 }
84 return all;
85 default:
86 return v;
87 }
88 },
89
90 /**
91 * Encode a value including type information.
92 * @param {Mixed} value
93 * @return {String}
94 */
95 encodeValue : function(v){
96 var enc;
97 if(typeof v == 'number'){
98 enc = 'n:' + v;
99 }else if(typeof v == 'boolean'){
100 enc = 'b:' + (v ? '1' : '0');
101 }else if(v instanceof Date){
102 enc = 'd:' + v.toGMTString();
103 }else if(v instanceof Array){
104 var flat = '';
105 for(var i = 0, len = v.length; i < len; i++){
106 flat += this.encodeValue(v[i]);
107 if(i != len-1) flat += '^';
108 }
109 enc = 'a:' + flat;
110 }else if(typeof v == 'object'){
111 var flat = '';
112 for(var key in v){
113 if(typeof v[key] != 'function'){
114 flat += key + '=' + this.encodeValue(v[key]) + '^';
115 }
116 }
117 enc = 'o:' + flat.substring(0, flat.length-1);
118 }else{
119 enc = 's:' + v;
120 }
121 return escape(enc);
122 }
123});
124
125/**
126 * @class YAHOO.ext.state.Manager
127 * This is the global state manager. By default all components that are "state aware" check this class
128 * for state information if you don't pass them a custom state provider. In order for this class
129 * to be useful, it must be initialized with a provider when your application initializes.
130 <pre><code>
131// in your initialization function
132init : function(){
133 YAHOO.ext.state.Manager.setProvider(new YAHOO.ext.state.CookieProvider());
134 ...
135 // supposed you have a {@link YAHOO.ext.BorderLayout}
136 var layout = new YAHOO.ext.BorderLayout(...);
137 layout.restoreState();
138 // or a {YAHOO.ext.BasicDialog}
139 var dialog = new YAHOO.ext.BasicDialog(...);
140 dialog.restoreState();
141 </code></pre>
142 * @singleton
143 */
144YAHOO.ext.state.Manager = new function(){
145 var provider = new YAHOO.ext.state.Provider();
146
147 return {
148 /**
149 * Configures the default provider for your application.
150 * @param {Provider} stateProvider
151 */
152 setProvider : function(stateProvider){
153 provider = stateProvider;
154 },
155
156 /**
157 * Get the current value for a key.
158 * @param {String} name
159 * @param {Mixed} defaultValue
160 * @return {Mixed}
161 */
162 get : function(key, defaultValue){
163 return provider.get(key, defaultValue);
164 },
165
166 /**
167 * Set the value for a key.
168 * @param {String} name
169 * @param {Mixed} value
170 */
171 set : function(key, value){
172 provider.set(key, value);
173 },
174
175 /**
176 * Clear a value from the state.
177 */
178 clear : function(key){
179 provider.clear(key);
180 },
181
182 /**
183 * Gets the currently configured provider.
184 * @return {Provider}
185 */
186 getProvider : function(){
187 return provider;
188 }
189 };
190}();
191
192/**
193 * @class YAHOO.ext.state.CookieProvider
194 * @extends YAHOO.ext.state.Provider
195 * The default Provider implementation. The example below includes all valid configuration options and their
196 * default values.
197 <pre><code>
198 var cp = new YAHOO.ext.state.CookieProvider({
199 path: '/',
200 expires: new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
201 domain: null,
202 secure: false
203 })
204 YAHOO.ext.state.Manager.setProvider(cp);
205 </code></pre>
206 * @constructor
207 * Create a new CookieProvider
208 * @param {Object} config The configuration object
209 */
210YAHOO.ext.state.CookieProvider = function(config){
211 YAHOO.ext.state.CookieProvider.superclass.constructor.call(this);
212 this.path = '/';
213 this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
214 this.domain = null;
215 this.secure = false;
216 YAHOO.ext.util.Config.apply(this, config);
217 this.state = this.readCookies();
218};
219
220YAHOO.extendX(YAHOO.ext.state.CookieProvider, YAHOO.ext.state.Provider, {
221 set : function(name, value){
222 if(typeof value == 'undefined' || value === null){
223 this.clear(name);
224 return;
225 }
226 this.setCookie(name, value);
227 YAHOO.ext.state.CookieProvider.superclass.set.call(this, name, value);
228 },
229
230 clear : function(name){
231 this.clearCookie(name);
232 YAHOO.ext.state.CookieProvider.superclass.clear.call(this, name);
233 },
234
235 readCookies : function(){
236 var cookies = {};
237 var c = document.cookie + ';';
238 var re = /\s?(.*?)=(.*?);/g;
239 var matches;
240 while((matches = re.exec(c)) != null){
241 var name = matches[1];
242 var value = matches[2];
243 if(name && name.substring(0,3) == 'ys-'){
244 cookies[name.substr(3)] = this.decodeValue(value);
245 }
246 }
247 return cookies;
248 },
249
250 setCookie : function(name, value){
251 document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
252 ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
253 ((this.path == null) ? "" : ("; path=" + this.path)) +
254 ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
255 ((this.secure == true) ? "; secure" : "");
256 },
257
258 clearCookie : function(name){
259 document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
260 ((this.path == null) ? "" : ("; path=" + this.path)) +
261 ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
262 ((this.secure == true) ? "; secure" : "");
263 }
264});
diff --git a/frontend/beta/js/YUI-extensions/UpdateManager.js b/frontend/beta/js/YUI-extensions/UpdateManager.js
new file mode 100644
index 0000000..c2eb23f
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/UpdateManager.js
@@ -0,0 +1,484 @@
1/**
2 * @class YAHOO.ext.UpdateManager
3 * @extends YAHOO.ext.util.Observable
4 * Provides AJAX-style update for Element object using Yahoo
5 * UI library YAHOO.util.Connect functionality.<br><br>
6 * Usage:<br>
7 * <pre><code>
8 * // Get it from a YAHOO.ext.Element object
9 * var el = getEl('foo');
10 * var mgr = el.getUpdateManager();
11 * mgr.update('http://myserver.com/index.php', 'param1=1&amp;param2=2');
12 * ...
13 * mgr.formUpdate('myFormId', 'http://myserver.com/index.php');
14 * <br>
15 * // or directly (returns the same UpdateManager instance)
16 * var mgr = new YAHOO.ext.UpdateManager('myElementId');
17 * mgr.startAutoRefresh(60, 'http://myserver.com/index.php');
18 * mgr.on('update', myFcnNeedsToKnow);
19 * <br>
20 * </code></pre>
21 * @requires YAHOO.ext.Element
22 * @requires YAHOO.util.Dom
23 * @requires YAHOO.util.Event
24 * @requires YAHOO.util.CustomEvent
25 * @requires YAHOO.util.Connect
26 * @constructor
27 * Create new UpdateManager directly.
28 * @param {String/HTMLElement/YAHOO.ext.Element} el The element to update
29 * @param {<i>Boolean</i>} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
30 */
31YAHOO.ext.UpdateManager = function(el, forceNew){
32 el = YAHOO.ext.Element.get(el);
33 if(!forceNew && el.updateManager){
34 return el.updateManager;
35 }
36 /**
37 * The Element object
38 * @type YAHOO.ext.Element
39 */
40 this.el = el;
41 /**
42 * Cached url to use for refreshes. Overwritten every time update() is called unless 'discardUrl' param is set to true.
43 * @type String
44 */
45 this.defaultUrl = null;
46 this.beforeUpdate = new YAHOO.util.CustomEvent('UpdateManager.beforeUpdate');
47 this.onUpdate = new YAHOO.util.CustomEvent('UpdateManager.onUpdate');
48 this.onFailure = new YAHOO.util.CustomEvent('UpdateManager.onFailure');
49
50 this.events = {
51 /**
52 * @event beforeupdate
53 * Fired before an update is made, return false from your handler and the update is cancelled.
54 * @param {YAHOO.ext.Element} el
55 * @param {String/Object/Function} url
56 * @param {String/Object} params
57 */
58 'beforeupdate': this.beforeUpdate,
59 /**
60 * @event update
61 * Fired after successful update is made.
62 * @param {YAHOO.ext.Element} el
63 * @param {Object} oResponseObject The YAHOO.util.Connect response Object
64 */
65 'update': this.onUpdate,
66 /**
67 * @event failure
68 * Fired on update failure. Uses fireDirect with signature: (oElement, oResponseObject)
69 * @param {YAHOO.ext.Element} el
70 * @param {Object} oResponseObject The YAHOO.util.Connect response Object
71 */
72 'failure': this.onFailure
73 };
74
75 /**
76 * Blank page URL to use with SSL file uploads (Defaults to YAHOO.ext.UpdateManager.defaults.sslBlankUrl or 'about:blank').
77 * @type String
78 */
79 this.sslBlankUrl = YAHOO.ext.UpdateManager.defaults.sslBlankUrl;
80 /**
81 * Whether to append unique parameter on get request to disable caching (Defaults to YAHOO.ext.UpdateManager.defaults.disableCaching or false).
82 * @type Boolean
83 */
84 this.disableCaching = YAHOO.ext.UpdateManager.defaults.disableCaching;
85 /**
86 * Text for loading indicator (Defaults to YAHOO.ext.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
87 * @type String
88 */
89 this.indicatorText = YAHOO.ext.UpdateManager.defaults.indicatorText;
90 /**
91 * Whether to show indicatorText when loading (Defaults to YAHOO.ext.UpdateManager.defaults.showLoadIndicator or true).
92 * @type String
93 */
94 this.showLoadIndicator = YAHOO.ext.UpdateManager.defaults.showLoadIndicator;
95 /**
96 * Timeout for requests or form posts in seconds (Defaults to YAHOO.ext.UpdateManager.defaults.timeout or 30 seconds).
97 * @type Number
98 */
99 this.timeout = YAHOO.ext.UpdateManager.defaults.timeout;
100
101 /**
102 * True to process scripts in the output (Defaults to YAHOO.ext.UpdateManager.defaults.loadScripts (false)).
103 * @type Boolean
104 */
105 this.loadScripts = YAHOO.ext.UpdateManager.defaults.loadScripts;
106
107 /**
108 * YAHOO.util.Connect transaction object of current executing transaction
109 */
110 this.transaction = null;
111
112 /**
113 * @private
114 */
115 this.autoRefreshProcId = null;
116 /**
117 * Delegate for refresh() prebound to 'this', use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
118 * @type Function
119 */
120 this.refreshDelegate = this.refresh.createDelegate(this);
121 /**
122 * Delegate for update() prebound to 'this', use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
123 * @type Function
124 */
125 this.updateDelegate = this.update.createDelegate(this);
126 /**
127 * Delegate for formUpdate() prebound to 'this', use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
128 * @type Function
129 */
130 this.formUpdateDelegate = this.formUpdate.createDelegate(this);
131 /**
132 * @private
133 */
134 this.successDelegate = this.processSuccess.createDelegate(this);
135 /**
136 * @private
137 */
138 this.failureDelegate = this.processFailure.createDelegate(this);
139
140 /**
141 * The renderer for this UpdateManager. Defaults to {@link YAHOO.ext.UpdateManager.BasicRenderer}.
142 */
143 this.renderer = new YAHOO.ext.UpdateManager.BasicRenderer();
144};
145
146YAHOO.ext.UpdateManager.prototype = {
147 fireEvent : YAHOO.ext.util.Observable.prototype.fireEvent,
148 on : YAHOO.ext.util.Observable.prototype.on,
149 addListener : YAHOO.ext.util.Observable.prototype.addListener,
150 delayedListener : YAHOO.ext.util.Observable.prototype.delayedListener,
151 removeListener : YAHOO.ext.util.Observable.prototype.removeListener,
152 purgeListeners : YAHOO.ext.util.Observable.prototype.purgeListeners,
153 bufferedListener : YAHOO.ext.util.Observable.prototype.bufferedListener,
154 /**
155 * Get the Element this UpdateManager is bound to
156 * @return {YAHOO.ext.Element} The element
157 */
158 getEl : function(){
159 return this.el;
160 },
161
162 /**
163 * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
164 * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
165<pre><code>
166um.update({<br/>
167 url: 'your-url.php',<br/>
168 params: {param1: 'foo', param2: 'bar'}, // or a URL encoded string<br/>
169 callback: yourFunction,<br/>
170 scope: yourObject, //(optional scope) <br/>
171 discardUrl: false, <br/>
172 nocache: false,<br/>
173 text: 'Loading...',<br/>
174 timeout: 30,<br/>
175 scripts: false<br/>
176});
177</code></pre>
178 * The only required property is url. The optional properties nocache, text and scripts
179 * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
180 * @param {<i>String/Object</i>} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
181 * @param {<i>Function</i>} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
182 * @param {<i>Boolean</i>} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
183 */
184 update : function(url, params, callback, discardUrl){
185 if(this.beforeUpdate.fireDirect(this.el, url, params) !== false){
186 if(typeof url == 'object'){ // must be config object
187 var cfg = url;
188 url = cfg.url;
189 params = params || cfg.params;
190 callback = callback || cfg.callback;
191 discardUrl = discardUrl || cfg.discardUrl;
192 if(callback && cfg.scope){
193 callback = callback.createDelegate(cfg.scope);
194 }
195 if(typeof cfg.nocache != 'undefined'){this.disableCaching = cfg.nocache};
196 if(typeof cfg.text != 'undefined'){this.indicatorText = '<div class="loading-indicator">'+cfg.text+'</div>'};
197 if(typeof cfg.scripts != 'undefined'){this.loadScripts = cfg.scripts};
198 if(typeof cfg.timeout != 'undefined'){this.timeout = cfg.timeout};
199 }
200 this.showLoading();
201 if(!discardUrl){
202 this.defaultUrl = url;
203 }
204 if(typeof url == 'function'){
205 url = url();
206 }
207 if(typeof params == 'function'){
208 params = params();
209 }
210 if(params && typeof params != 'string'){ // must be object
211 var buf = [];
212 for(var key in params){
213 if(typeof params[key] != 'function'){
214 buf.push(encodeURIComponent(key), '=', encodeURIComponent(params[key]), '&');
215 }
216 }
217 delete buf[buf.length-1];
218 params = buf.join('');
219 }
220 var callback = {
221 success: this.successDelegate,
222 failure: this.failureDelegate,
223 timeout: (this.timeout*1000),
224 argument: {'url': url, 'form': null, 'callback': callback, 'params': params}
225 };
226 var method = params ? 'POST' : 'GET';
227 if(method == 'GET'){
228 url = this.prepareUrl(url);
229 }
230 this.transaction = YAHOO.util.Connect.asyncRequest(method, url, callback, params);
231 }
232 },
233
234 /**
235 * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
236 * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning. See YUI docs for more info.
237 * @param {String/HTMLElement} form The form Id or form element
238 * @param {<i>String</i>} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
239 * @param {<i>Boolean</i>} reset (optional) Whether to try to reset the form after the update
240 * @param {<i>Function</i>} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
241 */
242 formUpdate : function(form, url, reset, callback){
243 if(this.beforeUpdate.fireDirect(this.el, form, url) !== false){
244 formEl = YAHOO.util.Dom.get(form);
245 this.showLoading();
246 if(typeof url == 'function'){
247 url = url();
248 }
249 if(typeof params == 'function'){
250 params = params();
251 }
252 url = url || formEl.action;
253 var callback = {
254 success: this.successDelegate,
255 failure: this.failureDelegate,
256 timeout: (this.timeout*1000),
257 argument: {'url': url, 'form': form, 'callback': callback, 'reset': reset}
258 };
259 var isUpload = false;
260 var enctype = formEl.getAttribute('enctype');
261 if(enctype && enctype.toLowerCase() == 'multipart/form-data'){
262 isUpload = true;
263 }
264 YAHOO.util.Connect.setForm(form, isUpload, this.sslBlankUrl);
265 this.transaction = YAHOO.util.Connect.asyncRequest('POST', url, callback);
266 }
267 },
268
269 /**
270 * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
271 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
272 */
273 refresh : function(callback){
274 if(this.defaultUrl == null){
275 return;
276 }
277 this.update(this.defaultUrl, null, callback, true);
278 },
279
280 /**
281 * Set this element to auto refresh.
282 * @param {Number} interval How often to update (in seconds).
283 * @param {<i>String/Function</i>} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
284 * @param {<i>String/Object</i>} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
285 * @param {<i>Function</i>} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
286 * @param {<i>Boolean</i>} refreshNow (optional) Whether to execute the refresh now, or wait the interval
287 */
288 startAutoRefresh : function(interval, url, params, callback, refreshNow){
289 if(refreshNow){
290 this.update(url || this.defaultUrl, params, callback, true);
291 }
292 if(this.autoRefreshProcId){
293 clearInterval(this.autoRefreshProcId);
294 }
295 this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
296 },
297
298 /**
299 * Stop auto refresh on this element.
300 */
301 stopAutoRefresh : function(){
302 if(this.autoRefreshProcId){
303 clearInterval(this.autoRefreshProcId);
304 }
305 },
306
307 /**
308 * Called to update the element to "Loading" state. Override to perform custom action.
309 */
310 showLoading : function(){
311 if(this.showLoadIndicator){
312 this.el.update(this.indicatorText);
313 }
314 },
315
316 /**
317 * Adds unique parameter to query string if disableCaching = true
318 * @private
319 */
320 prepareUrl : function(url){
321 if(this.disableCaching){
322 var append = '_dc=' + (new Date().getTime());
323 if(url.indexOf('?') !== -1){
324 url += '&' + append;
325 }else{
326 url += '?' + append;
327 }
328 }
329 return url;
330 },
331
332 /**
333 * @private
334 */
335 processSuccess : function(response){
336 this.transaction = null;
337 if(response.argument.form && response.argument.reset){
338 try{ // put in try/catch since some older FF releases had problems with this
339 response.argument.form.reset();
340 }catch(e){}
341 }
342 if(this.loadScripts){
343 this.renderer.render(this.el, response, this,
344 this.updateComplete.createDelegate(this, [response]));
345 }else{
346 this.renderer.render(this.el, response, this);
347 this.updateComplete(response);
348 }
349 },
350
351 updateComplete : function(response){
352 this.fireEvent('update', this.el, response);
353 if(typeof response.argument.callback == 'function'){
354 response.argument.callback(this.el, true, response);
355 }
356 },
357
358 /**
359 * @private
360 */
361 processFailure : function(response){
362 this.transaction = null;
363 this.onFailure.fireDirect(this.el, response);
364 if(typeof response.argument.callback == 'function'){
365 response.argument.callback(this.el, false, response);
366 }
367 },
368
369 /**
370 * Set the content renderer for this UpdateManager. See {@link YAHOO.ext.UpdateManager.BasicRenderer#render} for more details.
371 * @param {Object} renderer The object implementing the render() method
372 */
373 setRenderer : function(renderer){
374 this.renderer = renderer;
375 },
376
377 getRenderer : function(){
378 return this.renderer;
379 },
380
381 /**
382 * Set the defaultUrl used for updates
383 * @param {String/Function} defaultUrl The url or a function to call to get the url
384 */
385 setDefaultUrl : function(defaultUrl){
386 this.defaultUrl = defaultUrl;
387 },
388
389 /**
390 * Aborts the executing transaction
391 */
392 abort : function(){
393 if(this.transaction){
394 YAHOO.util.Connect.abort(this.transaction);
395 }
396 },
397
398 /**
399 * Returns true if an update is in progress
400 * @return {Boolean}
401 */
402 isUpdating : function(){
403 if(this.transaction){
404 return YAHOO.util.Connect.isCallInProgress(this.transaction);
405 }
406 return false;
407 }
408};
409
410/**
411 * @class YAHOO.ext.UpdateManager.defaults
412 * The defaults collection enables customizing the default properties of UpdateManager
413 */
414 YAHOO.ext.UpdateManager.defaults = {
415 /**
416 * Timeout for requests or form posts in seconds (Defaults 30 seconds).
417 * @type Number
418 */
419 timeout : 30,
420
421 /**
422 * True to process scripts by default (Defaults to false).
423 * @type Boolean
424 */
425 loadScripts : false,
426
427 /**
428 * Blank page URL to use with SSL file uploads (Defaults to 'javascript:false').
429 * @type String
430 */
431 sslBlankUrl : (YAHOO.ext.SSL_SECURE_URL || 'javascript:false'),
432 /**
433 * Whether to append unique parameter on get request to disable caching (Defaults to false).
434 * @type Boolean
435 */
436 disableCaching : false,
437 /**
438 * Whether to show indicatorText when loading (Defaults to true).
439 * @type Boolean
440 */
441 showLoadIndicator : true,
442 /**
443 * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
444 * @type String
445 */
446 indicatorText : '<div class="loading-indicator">Loading...</div>'
447 };
448
449/**
450 * Static convenience method, Usage:
451 * <pre><code>YAHOO.ext.UpdateManager.updateElement('my-div', 'stuff.php');</code></pre>
452 * @param {String/HTMLElement/YAHOO.ext.Element} el The element to update
453 * @param {String} url The url
454 * @param {<i>String/Object</i>} params (optional) Url encoded param string or an object of name/value pairs
455 * @param {<i>Object</i>} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: 'Loading data...'}
456 * @static
457 */
458YAHOO.ext.UpdateManager.updateElement = function(el, url, params, options){
459 var um = getEl(el, true).getUpdateManager();
460 YAHOO.ext.util.Config.apply(um, options);
461 um.update(url, params, options ? options.callback : null);
462};
463// alias for backwards compat
464YAHOO.ext.UpdateManager.update = YAHOO.ext.UpdateManager.updateElement;
465/**
466 * @class YAHOO.ext.UpdateManager.BasicRenderer
467 * Default Content renderer. Updates the elements innerHTML with the responseText.
468 */
469YAHOO.ext.UpdateManager.BasicRenderer = function(){};
470
471YAHOO.ext.UpdateManager.BasicRenderer.prototype = {
472 /**
473 * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
474 * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
475 * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
476 * @param {YAHOO.ext.Element} el The element being rendered
477 * @param {Object} response The YUI Connect response object
478 * @param {UpdateManager} updateManager The calling update manager
479 * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
480 */
481 render : function(el, response, updateManager, callback){
482 el.update(response.responseText, updateManager.loadScripts, callback);
483 }
484};
diff --git a/frontend/beta/js/YUI-extensions/anim/Actor.js b/frontend/beta/js/YUI-extensions/anim/Actor.js
new file mode 100644
index 0000000..f5574e6
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/anim/Actor.js
@@ -0,0 +1,759 @@
1
2/**
3 * @class YAHOO.ext.Actor
4 * Provides support for syncing and chaining of Element Yahoo! UI based animation and some common effects. Actors support "self-play" without an Animator.<br><br>
5 * <b>Note: Along with the animation methods defined below, this class inherits and captures all of the "set" or animation methods of {@link YAHOO.ext.Element}. "get" methods are not captured and execute immediately.</b>
6 * <br><br>Usage:<br>
7 * <pre><code>
8 * var actor = new YAHOO.ext.Actor('myElementId');
9 * actor.startCapture(true);
10 * actor.moveTo(100, 100, true);
11 * actor.squish();
12 * actor.play();
13 * <br>
14 * // or to start capturing immediately, with no Animator (the null second param)
15 * <br>
16 * var actor = new YAHOO.ext.Actor('myElementId', null, true);
17 * actor.moveTo(100, 100, true);
18 * actor.squish();
19 * actor.play();
20 * </code></pre>
21 * @extends YAHOO.ext.Element
22 * @requires YAHOO.ext.Element
23 * @requires YAHOO.util.Dom
24 * @requires YAHOO.util.Event
25 * @requires YAHOO.util.CustomEvent
26 * @requires YAHOO.util.Anim
27 * @requires YAHOO.util.ColorAnim
28 * @requires YAHOO.util.Motion
29 * @className YAHOO.ext.Actor
30 * @constructor
31 * Create new Actor.
32 * @param {String/HTMLElement} el The dom element or element id
33 * @param {<i>YAHOO.ext.Animator</i>} animator (optional) The Animator that will capture this Actor's actions
34 * @param {<i>Boolean</i>} selfCapture (optional) Whether this actor should capture it's own actions to support self playback without an animator (defaults to false)
35 */
36YAHOO.ext.Actor = function(element, animator, selfCapture){
37 this.el = YAHOO.ext.Element.get(element, true); // cache el object for playback
38 YAHOO.ext.Actor.superclass.constructor.call(this, element, true);
39 this.onCapture = new YAHOO.util.CustomEvent('Actor.onCapture');
40 if(animator){
41 /**
42 * The animator used to sync this actor with other actors
43 * @member YAHOO.ext.Actor
44 */
45 animator.addActor(this);
46 }
47 /**
48 * Whether this actor is currently capturing
49 * @member YAHOO.ext.Actor
50 */
51 this.capturing = selfCapture;
52 this.playlist = selfCapture ? new YAHOO.ext.Animator.AnimSequence() : null;
53};
54
55YAHOO.extendX(YAHOO.ext.Actor, YAHOO.ext.Element);
56
57/**
58 * Captures an action for this actor. Generally called internally but can be called directly.
59 * @param {YAHOO.ext.Actor.Action} action
60 */
61YAHOO.ext.Actor.prototype.capture = function(action){
62 if(this.playlist != null){
63 this.playlist.add(action);
64 }
65 this.onCapture.fireDirect(this, action);
66 return action;
67};
68
69/** @ignore */
70YAHOO.ext.Actor.overrideAnimation = function(method, animParam, onParam){
71 return function(){
72 if(!this.capturing){
73 return method.apply(this, arguments);
74 }
75 var args = Array.prototype.slice.call(arguments, 0);
76 if(args[animParam] === true){
77 return this.capture(new YAHOO.ext.Actor.AsyncAction(this, method, args, onParam));
78 }else{
79 return this.capture(new YAHOO.ext.Actor.Action(this, method, args));
80 }
81 };
82}
83
84/** @ignore */
85YAHOO.ext.Actor.overrideBasic = function(method){
86 return function(){
87 if(!this.capturing){
88 return method.apply(this, arguments);
89 }
90 var args = Array.prototype.slice.call(arguments, 0);
91 return this.capture(new YAHOO.ext.Actor.Action(this, method, args));
92 };
93}
94
95// All of these methods below are marked "ignore" because JSDoc treats them as fields, not function. How brilliant. The Element methods are documented anyway though.
96/** Capturing override - See {@link YAHOO.ext.Element#setVisibilityMode} for method details.
97 * @method */
98YAHOO.ext.Actor.prototype.setVisibilityMode = YAHOO.ext.Actor.overrideBasic(YAHOO.ext.Actor.superclass.setVisibilityMode);
99/** Capturing override - See {@link YAHOO.ext.Element#enableDisplayMode} for method details.
100 * @method */
101YAHOO.ext.Actor.prototype.enableDisplayMode = YAHOO.ext.Actor.overrideBasic(YAHOO.ext.Actor.superclass.enableDisplayMode);
102/** Capturing override - See {@link YAHOO.ext.Element#focus} for method details.
103 * @method */
104YAHOO.ext.Actor.prototype.focus = YAHOO.ext.Actor.overrideBasic(YAHOO.ext.Actor.superclass.focus);
105/** Capturing override - See {@link YAHOO.ext.Element#addClass} for method details.
106 * @method */
107YAHOO.ext.Actor.prototype.addClass = YAHOO.ext.Actor.overrideBasic(YAHOO.ext.Actor.superclass.addClass);
108/** Capturing override - See {@link YAHOO.ext.Element#removeClass} for method details.
109 * @method */
110YAHOO.ext.Actor.prototype.removeClass = YAHOO.ext.Actor.overrideBasic(YAHOO.ext.Actor.superclass.removeClass);
111/** Capturing override - See {@link YAHOO.ext.Element#replaceClass} for method details.
112 * @method */
113YAHOO.ext.Actor.prototype.replaceClass = YAHOO.ext.Actor.overrideBasic(YAHOO.ext.Actor.superclass.replaceClass);
114/** Capturing override - See {@link YAHOO.ext.Element#setStyle} for method details.
115 * @method */
116YAHOO.ext.Actor.prototype.setStyle = YAHOO.ext.Actor.overrideBasic(YAHOO.ext.Actor.superclass.setStyle);
117/** Capturing override - See {@link YAHOO.ext.Element#setLeft} for method details.
118 * @method */
119YAHOO.ext.Actor.prototype.setLeft = YAHOO.ext.Actor.overrideBasic(YAHOO.ext.Actor.superclass.setLeft);
120/** Capturing override - See {@link YAHOO.ext.Element#setTop} for method details.
121 * @method */
122YAHOO.ext.Actor.prototype.setTop = YAHOO.ext.Actor.overrideBasic(YAHOO.ext.Actor.superclass.setTop);
123/** Capturing override - See {@link YAHOO.ext.Element#setAbsolutePositioned} for method details.
124 * @method */
125YAHOO.ext.Actor.prototype.setAbsolutePositioned = YAHOO.ext.Actor.overrideBasic(YAHOO.ext.Actor.superclass.setAbsolutePositioned);
126/** Capturing override - See {@link YAHOO.ext.Element#setRelativePositioned} for method details.
127 * @method */
128YAHOO.ext.Actor.prototype.setRelativePositioned = YAHOO.ext.Actor.overrideBasic(YAHOO.ext.Actor.superclass.setRelativePositioned);
129/** Capturing override - See {@link YAHOO.ext.Element#clearPositioning} for method details.
130 * @method */
131YAHOO.ext.Actor.prototype.clearPositioning = YAHOO.ext.Actor.overrideBasic(YAHOO.ext.Actor.superclass.clearPositioning);
132/** Capturing override - See {@link YAHOO.ext.Element#setPositioning} for method details.
133 * @method */
134YAHOO.ext.Actor.prototype.setPositioning = YAHOO.ext.Actor.overrideBasic(YAHOO.ext.Actor.superclass.setPositioning);
135/** Capturing override - See {@link YAHOO.ext.Element#clip} for method details.
136 * @method */
137YAHOO.ext.Actor.prototype.clip = YAHOO.ext.Actor.overrideBasic(YAHOO.ext.Actor.superclass.clip);
138/** Capturing override - See {@link YAHOO.ext.Element#unclip} for method details.
139 * @method */
140YAHOO.ext.Actor.prototype.unclip = YAHOO.ext.Actor.overrideBasic(YAHOO.ext.Actor.superclass.unclip);
141/** Capturing override - See {@link YAHOO.ext.Element#clearOpacity} for method details.
142 * @method */
143YAHOO.ext.Actor.prototype.clearOpacity = YAHOO.ext.Actor.overrideBasic(YAHOO.ext.Actor.superclass.clearOpacity);
144/** Capturing override - See {@link YAHOO.ext.Element#update} for method details.
145 * @method */
146YAHOO.ext.Actor.prototype.update = YAHOO.ext.Actor.overrideBasic(YAHOO.ext.Actor.superclass.update);
147/** Capturing override - See {@link YAHOO.ext.Element#remove} for method details.
148 * @method */
149YAHOO.ext.Actor.prototype.remove = YAHOO.ext.Actor.overrideBasic(YAHOO.ext.Actor.superclass.remove);
150YAHOO.ext.Actor.prototype.fitToParent = YAHOO.ext.Actor.overrideBasic(YAHOO.ext.Actor.superclass.fitToParent);
151YAHOO.ext.Actor.prototype.appendChild = YAHOO.ext.Actor.overrideBasic(YAHOO.ext.Actor.superclass.appendChild);
152YAHOO.ext.Actor.prototype.createChild = YAHOO.ext.Actor.overrideBasic(YAHOO.ext.Actor.superclass.createChild);
153YAHOO.ext.Actor.prototype.appendTo = YAHOO.ext.Actor.overrideBasic(YAHOO.ext.Actor.superclass.appendTo);
154YAHOO.ext.Actor.prototype.insertBefore = YAHOO.ext.Actor.overrideBasic(YAHOO.ext.Actor.superclass.insertBefore);
155YAHOO.ext.Actor.prototype.insertAfter = YAHOO.ext.Actor.overrideBasic(YAHOO.ext.Actor.superclass.insertAfter);
156YAHOO.ext.Actor.prototype.wrap = YAHOO.ext.Actor.overrideBasic(YAHOO.ext.Actor.superclass.wrap);
157YAHOO.ext.Actor.prototype.replace = YAHOO.ext.Actor.overrideBasic(YAHOO.ext.Actor.superclass.replace);
158YAHOO.ext.Actor.prototype.insertHtml = YAHOO.ext.Actor.overrideBasic(YAHOO.ext.Actor.superclass.insertHtml);
159YAHOO.ext.Actor.prototype.set = YAHOO.ext.Actor.overrideBasic(YAHOO.ext.Actor.superclass.set);
160
161/**Capturing and animation syncing override - See {@link YAHOO.ext.Element#load} for method details.
162 * @method */
163YAHOO.ext.Actor.prototype.load = function(){
164 if(!this.capturing){
165 return YAHOO.ext.Actor.superclass.load.apply(this, arguments);
166 }
167 var args = Array.prototype.slice.call(arguments, 0);
168 return this.capture(new YAHOO.ext.Actor.AsyncAction(this, YAHOO.ext.Actor.superclass.load,
169 args, 2));
170};
171
172/**Capturing and animation syncing override - See {@link YAHOO.ext.Element#animate} for method details.
173 * @method */
174YAHOO.ext.Actor.prototype.animate = function(args, duration, onComplete, easing, animType){
175 if(!this.capturing){
176 return YAHOO.ext.Actor.superclass.animate.apply(this, arguments);
177 }
178 return this.capture(new YAHOO.ext.Actor.AsyncAction(this, YAHOO.ext.Actor.superclass.animate,
179 [args, duration, onComplete, easing, animType], 2));
180};
181
182/** Capturing and animation syncing override - See {@link YAHOO.ext.Element#setVisible} for method details.
183 * @method */
184YAHOO.ext.Actor.prototype.setVisible = YAHOO.ext.Actor.overrideAnimation(YAHOO.ext.Actor.superclass.setVisible, 1, 3);
185/**Capturing and animation syncing override - See {@link YAHOO.ext.Element#toggle} for method details.
186 * @method */
187YAHOO.ext.Actor.prototype.toggle = YAHOO.ext.Actor.overrideAnimation(YAHOO.ext.Actor.superclass.toggle, 0, 2);
188/**Capturing and animation syncing override - See {@link YAHOO.ext.Element#setXY} for method details.
189 * @method */
190YAHOO.ext.Actor.prototype.setXY = YAHOO.ext.Actor.overrideAnimation(YAHOO.ext.Actor.superclass.setXY, 1, 3);
191/**Capturing and animation syncing override - See {@link YAHOO.ext.Element#setLocation} for method details.
192 * @method */
193YAHOO.ext.Actor.prototype.setLocation = YAHOO.ext.Actor.overrideAnimation(YAHOO.ext.Actor.superclass.setLocation, 2, 4);
194/**Capturing and animation syncing override - See {@link YAHOO.ext.Element#setWidth} for method details.
195 * @method */
196YAHOO.ext.Actor.prototype.setWidth = YAHOO.ext.Actor.overrideAnimation(YAHOO.ext.Actor.superclass.setWidth, 1, 3);
197/**Capturing and animation syncing override - See {@link YAHOO.ext.Element#setHeight} for method details.
198 * @method */
199YAHOO.ext.Actor.prototype.setHeight = YAHOO.ext.Actor.overrideAnimation(YAHOO.ext.Actor.superclass.setHeight, 1, 3);
200/**Capturing and animation syncing override - See {@link YAHOO.ext.Element#setSize} for method details.
201 * @method */
202YAHOO.ext.Actor.prototype.setSize = YAHOO.ext.Actor.overrideAnimation(YAHOO.ext.Actor.superclass.setSize, 2, 4);
203/**Capturing and animation syncing override - See {@link YAHOO.ext.Element#setBounds} for method details.
204 * @method */
205YAHOO.ext.Actor.prototype.setBounds = YAHOO.ext.Actor.overrideAnimation(YAHOO.ext.Actor.superclass.setBounds, 4, 6);
206/**Capturing and animation syncing override - See {@link YAHOO.ext.Element#setOpacity} for method details.
207 * @method */
208YAHOO.ext.Actor.prototype.setOpacity = YAHOO.ext.Actor.overrideAnimation(YAHOO.ext.Actor.superclass.setOpacity, 1, 3);
209/**Capturing and animation syncing override - See {@link YAHOO.ext.Element#moveTo} for method details.
210 * @method */
211YAHOO.ext.Actor.prototype.moveTo = YAHOO.ext.Actor.overrideAnimation(YAHOO.ext.Actor.superclass.moveTo, 2, 4);
212/**Capturing and animation syncing override - See {@link YAHOO.ext.Element#move} for method details.
213 * @method */
214YAHOO.ext.Actor.prototype.move = YAHOO.ext.Actor.overrideAnimation(YAHOO.ext.Actor.superclass.move, 2, 4);
215/**Capturing and animation syncing override - See {@link YAHOO.ext.Element#alignTo} for method details.
216 * @method */
217YAHOO.ext.Actor.prototype.alignTo = YAHOO.ext.Actor.overrideAnimation(YAHOO.ext.Actor.superclass.alignTo, 3, 5);
218/**Capturing and animation syncing override - See {@link YAHOO.ext.Element#hide} for method details.
219 * @method */
220YAHOO.ext.Actor.prototype.hide = YAHOO.ext.Actor.overrideAnimation(YAHOO.ext.Actor.superclass.hide, 0, 2);
221/**Capturing and animation syncing override - See {@link YAHOO.ext.Element#show} for method details.
222 * @method */
223YAHOO.ext.Actor.prototype.show = YAHOO.ext.Actor.overrideAnimation(YAHOO.ext.Actor.superclass.show, 0, 2);
224
225/**Capturing and animation syncing override - See {@link YAHOO.ext.Element#setBox} for method details.
226 * @method */
227YAHOO.ext.Actor.prototype.setBox = YAHOO.ext.Actor.overrideAnimation(YAHOO.ext.Actor.superclass.setBox, 2, 4);
228
229/**Capturing and animation syncing override - See {@link YAHOO.ext.Element#autoHeight} for method details.
230 * @method */
231YAHOO.ext.Actor.prototype.autoHeight = YAHOO.ext.Actor.overrideAnimation(YAHOO.ext.Actor.superclass.autoHeight, 0, 2);
232/** Capturing override - See {@link YAHOO.ext.Element#setX} for method details.
233 * @method */
234YAHOO.ext.Actor.prototype.setX = YAHOO.ext.Actor.overrideAnimation(YAHOO.ext.Actor.superclass.setX, 1, 3);
235/** Capturing override - See {@link YAHOO.ext.Element#setY} for method details.
236 * @method */
237YAHOO.ext.Actor.prototype.setY = YAHOO.ext.Actor.overrideAnimation(YAHOO.ext.Actor.superclass.setY, 1, 3);
238
239/**
240 * Start self capturing calls on this Actor. All subsequent calls are captured and executed when play() is called.
241 */
242YAHOO.ext.Actor.prototype.startCapture = function(){
243 this.capturing = true;
244 this.playlist = new YAHOO.ext.Animator.AnimSequence();
245 };
246
247 /**
248 * Stop self capturing calls on this Actor.
249 */
250 YAHOO.ext.Actor.prototype.stopCapture = function(){
251 this.capturing = false;
252 };
253
254/**
255 * Clears any calls that have been self captured.
256 */
257YAHOO.ext.Actor.prototype.clear = function(){
258 this.playlist = new YAHOO.ext.Animator.AnimSequence();
259};
260
261/**
262 * Starts playback of self captured calls.
263 * @param {<i>Function</i>} oncomplete (optional) Callback to execute when playback has completed
264 */
265YAHOO.ext.Actor.prototype.play = function(oncomplete){
266 this.capturing = false;
267 if(this.playlist){
268 this.playlist.play(oncomplete);
269 }
270 };
271
272/**
273 * Capture a function call.
274 * @param {Function} fcn The function to call
275 * @param {<i>Array</i>} args (optional) The arguments to call the function with
276 * @param {<i>Object</i>} scope (optional) The scope of the function
277 */
278YAHOO.ext.Actor.prototype.addCall = function(fcn, args, scope){
279 if(!this.capturing){
280 fcn.apply(scope || this, args || []);
281 }else{
282 this.capture(new YAHOO.ext.Actor.Action(scope, fcn, args || []));
283 }
284};
285
286/**
287 * Capture an async function call.
288 * @param {Function} fcn The function to call
289 * @param {Number} callbackIndex The index of the callback parameter on the passed function. A CALLBACK IS REQUIRED.
290 * @param {<i>Array</i>} args The arguments to call the function with
291 * @param {<i>Object</i>} scope (optional) The scope of the function
292 */
293YAHOO.ext.Actor.prototype.addAsyncCall = function(fcn, callbackIndex, args, scope){
294 if(!this.capturing){
295 fcn.apply(scope || this, args || []);
296 }else{
297 this.capture(new YAHOO.ext.Actor.AsyncAction(scope, fcn, args || [], callbackIndex));
298 }
299 },
300
301/**
302 * Capture a pause (in seconds).
303 * @param {Number} seconds The seconds to pause
304 */
305YAHOO.ext.Actor.prototype.pause = function(seconds){
306 this.capture(new YAHOO.ext.Actor.PauseAction(seconds));
307 };
308
309/**
310* Shake this element from side to side
311*/
312YAHOO.ext.Actor.prototype.shake = function(){
313 this.move('left', 20, true, .05);
314 this.move('right', 40, true, .05);
315 this.move('left', 40, true, .05);
316 this.move('right', 20, true, .05);
317};
318
319/**
320* Bounce this element from up and down
321*/
322YAHOO.ext.Actor.prototype.bounce = function(){
323 this.move('up', 20, true, .05);
324 this.move('down', 40, true, .05);
325 this.move('up', 40, true, .05);
326 this.move('down', 20, true, .05);
327};
328
329/**
330* Show the element using a "blinds" effect
331* @param {String} anchor The part of the element that it should appear to exapand from.
332 The short/long options currently are t/top, l/left
333* @param {<i>Number</i>} newSize (optional) The size to animate to. (Default to current size)
334* @param {<i>Float</i>} duration (optional) How long the effect lasts (in seconds)
335* @param {<i>Function</i>} easing (optional) YAHOO.util.Easing method to use. (Defaults to YAHOO.util.Easing.easeOut)
336*/
337YAHOO.ext.Actor.prototype.blindShow = function(anchor, newSize, duration, easing){
338 var size = this.getSize();
339 this.clip();
340 anchor = anchor.toLowerCase();
341 switch(anchor){
342 case 't':
343 case 'top':
344 this.setHeight(1);
345 this.setVisible(true);
346 this.setHeight(newSize || size.height, true, duration || .5, null, easing || YAHOO.util.Easing.easeOut);
347 break;
348 case 'l':
349 case 'left':
350 this.setWidth(1);
351 this.setVisible(true);
352 this.setWidth(newSize || size.width, true, duration || .5, null, easing || YAHOO.util.Easing.easeOut);
353 break;
354 }
355 this.unclip();
356 return size;
357};
358
359/**
360* Hide the element using a "blinds" effect
361* @param {String} anchor The part of the element that it should appear to collapse to.
362 The short/long options are t/top, l/left, b/bottom, r/right.
363* @param {<i>Float</i>} duration (optional) How long the effect lasts (in seconds)
364* @param {<i>Function</i>} easing (optional) YAHOO.util.Easing method to use. (Defaults to YAHOO.util.Easing.easeIn)
365*/
366YAHOO.ext.Actor.prototype.blindHide = function(anchor, duration, easing){
367 var size = this.getSize();
368 this.clip();
369 anchor = anchor.toLowerCase();
370 switch(anchor){
371 case 't':
372 case 'top':
373 this.setSize(size.width, 1, true, duration || .5, null, easing || YAHOO.util.Easing.easeIn);
374 this.setVisible(false);
375 break;
376 case 'l':
377 case 'left':
378 this.setSize(1, size.height, true, duration || .5, null, easing || YAHOO.util.Easing.easeIn);
379 this.setVisible(false);
380 break;
381 case 'r':
382 case 'right':
383 this.animate({width: {to: 1}, points: {by: [size.width, 0]}},
384 duration || .5, null, YAHOO.util.Easing.easeIn, YAHOO.util.Motion);
385 this.setVisible(false);
386 break;
387 case 'b':
388 case 'bottom':
389 this.animate({height: {to: 1}, points: {by: [0, size.height]}},
390 duration || .5, null, YAHOO.util.Easing.easeIn, YAHOO.util.Motion);
391 this.setVisible(false);
392 break;
393 }
394 return size;
395};
396
397/**
398* Show the element using a "slide in" effect - In order for this effect to work the element MUST have a child element container that can be "slid" otherwise a blindShow effect is rendered.
399* @param {String} anchor The part of the element that it should appear to slide from.
400 The short/long options currently are t/top, l/left
401* @param {<i>Number</i>} newSize (optional) The size to animate to. (Default to current size)
402* @param {<i>Float</i>} duration (optional) How long the effect lasts (in seconds)
403* @param {<i>Function</i>} easing (optional) YAHOO.util.Easing method to use. (Defaults to YAHOO.util.Easing.easeOuth)
404*/
405YAHOO.ext.Actor.prototype.slideShow = function(anchor, newSize, duration, easing, clearPositioning){
406 var size = this.getSize();
407 this.clip();
408 var firstChild = this.dom.firstChild;
409 if(!firstChild || (firstChild.nodeName && "#TEXT" == firstChild.nodeName.toUpperCase())) { // can't do a slide with only a textnode
410 this.blindShow(anchor, newSize, duration, easing);
411 return;
412 }
413 var child = YAHOO.ext.Element.get(firstChild, true);
414 var pos = child.getPositioning();
415 this.addCall(child.setAbsolutePositioned, null, child);
416 this.setVisible(true);
417 anchor = anchor.toLowerCase();
418 switch(anchor){
419 case 't':
420 case 'top':
421 this.addCall(child.setStyle, ['right', ''], child);
422 this.addCall(child.setStyle, ['top', ''], child);
423 this.addCall(child.setStyle, ['left', '0px'], child);
424 this.addCall(child.setStyle, ['bottom', '0px'], child);
425 this.setHeight(1);
426 this.setHeight(newSize || size.height, true, duration || .5, null, easing || YAHOO.util.Easing.easeOut);
427 break;
428 case 'l':
429 case 'left':
430 this.addCall(child.setStyle, ['left', ''], child);
431 this.addCall(child.setStyle, ['bottom', ''], child);
432 this.addCall(child.setStyle, ['right', '0px'], child);
433 this.addCall(child.setStyle, ['top', '0px'], child);
434 this.setWidth(1);
435 this.setWidth(newSize || size.width, true, duration || .5, null, easing || YAHOO.util.Easing.easeOut);
436 break;
437 case 'r':
438 case 'right':
439 this.addCall(child.setStyle, ['left', '0px'], child);
440 this.addCall(child.setStyle, ['top', '0px'], child);
441 this.addCall(child.setStyle, ['right', ''], child);
442 this.addCall(child.setStyle, ['bottom', ''], child);
443 this.setWidth(1);
444 this.setWidth(newSize || size.width, true, duration || .5, null, easing || YAHOO.util.Easing.easeOut);
445 break;
446 case 'b':
447 case 'bottom':
448 this.addCall(child.setStyle, ['right', ''], child);
449 this.addCall(child.setStyle, ['top', '0px'], child);
450 this.addCall(child.setStyle, ['left', '0px'], child);
451 this.addCall(child.setStyle, ['bottom', ''], child);
452 this.setHeight(1);
453 this.setHeight(newSize || size.height, true, duration || .5, null, easing || YAHOO.util.Easing.easeOut);
454 break;
455 }
456 if(clearPositioning !== false){
457 this.addCall(child.setPositioning, [pos], child);
458 }
459 this.unclip();
460 return size;
461};
462
463/**
464* Hide the element using a "slide in" effect - In order for this effect to work the element MUST have a child element container that can be "slid" otherwise a blindHide effect is rendered.
465* @param {String} anchor The part of the element that it should appear to slide to.
466 The short/long options are t/top, l/left, b/bottom, r/right.
467* @param {<i>Float</i>} duration (optional) How long the effect lasts (in seconds)
468* @param {<i>Function</i>} easing (optional) YAHOO.util.Easing method to use. (Defaults to YAHOO.util.Easing.easeIn)
469*/
470YAHOO.ext.Actor.prototype.slideHide = function(anchor, duration, easing){
471 var size = this.getSize();
472 this.clip();
473 var firstChild = this.dom.firstChild;
474 if(!firstChild || (firstChild.nodeName && "#TEXT" == firstChild.nodeName.toUpperCase())) { // can't do a slide with only a textnode
475 this.blindHide(anchor, duration, easing);
476 return;
477 }
478 var child = YAHOO.ext.Element.get(firstChild, true);
479 var pos = child.getPositioning();
480 this.addCall(child.setAbsolutePositioned, null, child);
481 anchor = anchor.toLowerCase();
482 switch(anchor){
483 case 't':
484 case 'top':
485 this.addCall(child.setStyle, ['right', ''], child);
486 this.addCall(child.setStyle, ['top', ''], child);
487 this.addCall(child.setStyle, ['left', '0px'], child);
488 this.addCall(child.setStyle, ['bottom', '0px'], child);
489 this.setSize(size.width, 1, true, duration || .5, null, easing || YAHOO.util.Easing.easeIn);
490 this.setVisible(false);
491 break;
492 case 'l':
493 case 'left':
494 this.addCall(child.setStyle, ['left', ''], child);
495 this.addCall(child.setStyle, ['bottom', ''], child);
496 this.addCall(child.setStyle, ['right', '0px'], child);
497 this.addCall(child.setStyle, ['top', '0px'], child);
498 this.setSize(1, size.height, true, duration || .5, null, easing || YAHOO.util.Easing.easeIn);
499 this.setVisible(false);
500 break;
501 case 'r':
502 case 'right':
503 this.addCall(child.setStyle, ['right', ''], child);
504 this.addCall(child.setStyle, ['bottom', ''], child);
505 this.addCall(child.setStyle, ['left', '0px'], child);
506 this.addCall(child.setStyle, ['top', '0px'], child);
507 this.setSize(1, size.height, true, duration || .5, null, easing || YAHOO.util.Easing.easeIn);
508 this.setVisible(false);
509 break;
510 case 'b':
511 case 'bottom':
512 this.addCall(child.setStyle, ['right', ''], child);
513 this.addCall(child.setStyle, ['top', '0px'], child);
514 this.addCall(child.setStyle, ['left', '0px'], child);
515 this.addCall(child.setStyle, ['bottom', ''], child);
516 this.setSize(size.width, 1, true, duration || .5, null, easing || YAHOO.util.Easing.easeIn);
517 this.setVisible(false);
518 break;
519 }
520 this.addCall(child.setPositioning, [pos], child);
521 return size;
522};
523
524/**
525* Hide the element by "squishing" it into the corner
526* @param {<i>Float</i>} duration (optional) How long the effect lasts (in seconds)
527*/
528YAHOO.ext.Actor.prototype.squish = function(duration){
529 var size = this.getSize();
530 this.clip();
531 this.setSize(1, 1, true, duration || .5);
532 this.setVisible(false);
533 return size;
534};
535
536/**
537* Fade an element in
538* @param {<i>Float</i>} duration (optional) How long the effect lasts (in seconds)
539*/
540YAHOO.ext.Actor.prototype.appear = function(duration){
541 this.setVisible(true, true, duration);
542};
543
544/**
545* Fade an element out
546* @param {<i>Float</i>} duration (optional) How long the effect lasts (in seconds)
547*/
548YAHOO.ext.Actor.prototype.fade = function(duration){
549 this.setVisible(false, true, duration);
550};
551
552/**
553* Blink the element as if it was clicked and then collapse on it's center
554* @param {<i>Float</i>} duration (optional) How long the effect lasts (in seconds)
555*/
556YAHOO.ext.Actor.prototype.switchOff = function(duration){
557 this.clip();
558 this.setVisible(false, true, .1);
559 this.clearOpacity();
560 this.setVisible(true);
561 this.animate({height: {to: 1}, points: {by: [0, this.getHeight()/2]}},
562 duration || .5, null, YAHOO.util.Easing.easeOut, YAHOO.util.Motion);
563 this.setVisible(false);
564};
565
566/**
567* Highlight the element using a background color (or passed attribute) animation
568* @param {String} color (optional) The color to use for the highlight
569* @param {<i>String</i>} fromColor (optional) If the element does not currently have a background color, you will need to pass in a color to animate from
570* @param {<i>Float</i>} duration (optional) How long the effect lasts (in seconds)
571* @param {<i>String</i>} attribute (optional) Specify a CSS attribute to use other than background color - camelCase
572*/
573YAHOO.ext.Actor.prototype.highlight = function(color, fromColor, duration, attribute){
574 attribute = attribute || 'background-color';
575 var original = this.getStyle(attribute);
576 fromColor = fromColor || ((original && original != '' && original != 'transparent') ? original : '#FFFFFF');
577 var cfg = {};
578 cfg[attribute] = {to: color, from: fromColor};
579 this.setVisible(true);
580 this.animate(cfg, duration || .5, null, YAHOO.util.Easing.bounceOut, YAHOO.util.ColorAnim);
581 this.setStyle(attribute, original);
582};
583
584/**
585* Fade the element in and out the specified amount of times
586* @param {<i>Number</i>} count (optional) How many times to pulse (Defaults to 3)
587* @param {<i>Float</i>} duration (optional) How long the effect lasts (in seconds)
588*/
589YAHOO.ext.Actor.prototype.pulsate = function(count, duration){
590 count = count || 3;
591 for(var i = 0; i < count; i++){
592 this.toggle(true, duration || .25);
593 this.toggle(true, duration || .25);
594 }
595};
596
597/**
598* Fade the element as it is falling from it's current position
599* @param {<i>Float</i>} duration (optional) How long the effect lasts (in seconds)
600*/
601YAHOO.ext.Actor.prototype.dropOut = function(duration){
602 this.animate({opacity: {to: 0}, points: {by: [0, this.getHeight()]}},
603 duration || .5, null, YAHOO.util.Easing.easeIn, YAHOO.util.Motion);
604 this.setVisible(false);
605};
606
607/**
608* Hide the element in a way that it appears as if it is flying off the screen
609* @param {String} anchor The part of the page that the element should appear to move to.
610 The short/long options are t/top, l/left, b/bottom, r/right, tl/top-left,
611 tr/top-right, bl/bottom-left or br/bottom-right.
612* @param {<i>Float</i>} duration (optional) How long the effect lasts (in seconds)
613* @param {<i>Function</i>} easing (optional) YAHOO.util.Easing method to use. (Defaults to YAHOO.util.Easing.easeIn)
614*/
615YAHOO.ext.Actor.prototype.moveOut = function(anchor, duration, easing){
616 var Y = YAHOO.util;
617 var vw = Y.Dom.getViewportWidth();
618 var vh = Y.Dom.getViewportHeight();
619 var cpoints = this.getCenterXY()
620 var centerX = cpoints[0];
621 var centerY = cpoints[1];
622 var anchor = anchor.toLowerCase();
623 var p;
624 switch(anchor){
625 case 't':
626 case 'top':
627 p = [centerX, -this.getHeight()];
628 break;
629 case 'l':
630 case 'left':
631 p = [-this.getWidth(), centerY];
632 break;
633 case 'r':
634 case 'right':
635 p = [vw+this.getWidth(), centerY];
636 break;
637 case 'b':
638 case 'bottom':
639 p = [centerX, vh+this.getHeight()];
640 break;
641 case 'tl':
642 case 'top-left':
643 p = [-this.getWidth(), -this.getHeight()];
644 break;
645 case 'bl':
646 case 'bottom-left':
647 p = [-this.getWidth(), vh+this.getHeight()];
648 break;
649 case 'br':
650 case 'bottom-right':
651 p = [vw+this.getWidth(), vh+this.getHeight()];
652 break;
653 case 'tr':
654 case 'top-right':
655 p = [vw+this.getWidth(), -this.getHeight()];
656 break;
657 }
658 this.moveTo(p[0], p[1], true, duration || .35, null, easing || Y.Easing.easeIn);
659 this.setVisible(false);
660};
661
662/**
663* Show the element in a way that it appears as if it is flying onto the screen
664* @param {String} anchor The part of the page that the element should appear to move from.
665 The short/long options are t/top, l/left, b/bottom, r/right, tl/top-left,
666 tr/top-right, bl/bottom-left or br/bottom-right.
667* @param {<i>Array</i>} to (optional) Array of x and y position to move to like [x, y] (Defaults to center screen)
668* @param {<i>Float</i>} duration (optional) How long the effect lasts (in seconds)
669* @param {<i>Function</i>} easing (optional) YAHOO.util.Easing method to use. (Defaults to YAHOO.util.Easing.easeOut)
670*/
671YAHOO.ext.Actor.prototype.moveIn = function(anchor, to, duration, easing){
672 to = to || this.getCenterXY();
673 this.moveOut(anchor, .01);
674 this.setVisible(true);
675 this.setXY(to, true, duration || .35, null, easing || YAHOO.util.Easing.easeOut);
676};
677/**
678* Show a ripple of exploding, attenuating borders to draw attention to an Element.
679* @param {<i>Number<i>} color (optional) The color of the border.
680* @param {<i>Number</i>} count (optional) How many ripples.
681* @param {<i>Float</i>} duration (optional) How long each ripple takes to expire
682*/
683YAHOO.ext.Actor.prototype.frame = function(color, count, duration){
684 color = color || "red";
685 count = count || 3;
686 duration = duration || .5;
687 var frameFn = function(callback){
688 var box = this.getBox();
689 var animFn = function(){
690 var proxy = this.createProxy({
691 tag:"div",
692 style:{
693 visbility:"hidden",
694 position:"absolute",
695 'z-index':"35000", // yee haw
696 border:"0px solid " + color
697 }
698 });
699 var scale = proxy.isBorderBox() ? 2 : 1;
700 proxy.animate({
701 top:{from:box.y, to:box.y - 20},
702 left:{from:box.x, to:box.x - 20},
703 borderWidth:{from:0, to:10},
704 opacity:{from:1, to:0},
705 height:{from:box.height, to:(box.height + (20*scale))},
706 width:{from:box.width, to:(box.width + (20*scale))}
707 }, duration, function(){
708 proxy.remove();
709 });
710 if(--count > 0){
711 animFn.defer((duration/2)*1000, this);
712 }else{
713 if(typeof callback == 'function'){
714 callback();
715 }
716 }
717 }
718 animFn.call(this);
719 }
720 this.addAsyncCall(frameFn, 0, null, this);
721};
722
723YAHOO.ext.Actor.Action = function(actor, method, args){
724 this.actor = actor;
725 this.method = method;
726 this.args = args;
727 }
728
729YAHOO.ext.Actor.Action.prototype = {
730 play : function(onComplete){
731 this.method.apply(this.actor || window, this.args);
732 onComplete();
733 }
734};
735
736
737YAHOO.ext.Actor.AsyncAction = function(actor, method, args, onIndex){
738 YAHOO.ext.Actor.AsyncAction.superclass.constructor.call(this, actor, method, args);
739 this.onIndex = onIndex;
740 this.originalCallback = this.args[onIndex];
741}
742YAHOO.extendX(YAHOO.ext.Actor.AsyncAction, YAHOO.ext.Actor.Action);
743
744YAHOO.ext.Actor.AsyncAction.prototype.play = function(onComplete){
745 var callbackArg = this.originalCallback ?
746 this.originalCallback.createSequence(onComplete) : onComplete;
747 this.args[this.onIndex] = callbackArg;
748 this.method.apply(this.actor, this.args);
749};
750
751
752YAHOO.ext.Actor.PauseAction = function(seconds){
753 this.seconds = seconds;
754};
755YAHOO.ext.Actor.PauseAction.prototype = {
756 play : function(onComplete){
757 setTimeout(onComplete, this.seconds * 1000);
758 }
759};
diff --git a/frontend/beta/js/YUI-extensions/anim/Animator.js b/frontend/beta/js/YUI-extensions/anim/Animator.js
new file mode 100644
index 0000000..ed250fb
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/anim/Animator.js
@@ -0,0 +1,482 @@
1/**
2 * @class YAHOO.ext.Animator
3 * Provides support for syncing animations for multiple {@link YAHOO.ext.Actor}s.<br><br>
4* <br><br>This example can be seen in action <a href="http://www.jackslocum.com/yui/2006/08/19/a-splitbar-component-for-yahoo-ui/" target="_new">here</a>
5* by clicking on "Click here and I will point it out" at the end of the first paragraph.<br>
6 * <pre><code>
7var animator = new YAHOO.ext.Animator();
8var cursor = new YAHOO.ext.Actor('cursor-img', animator);
9var click = new YAHOO.ext.Actor('click-img', animator);
10var resize = new YAHOO.ext.Actor('resize-img', animator);
11
12// start capturing
13animator.startCapture();
14
15// these animations will be run in sequence
16cursor.show();
17cursor.moveTo(500,400);
18cursor.moveTo(20, getEl('navbar').getY()+10, true, .75);
19click.show();
20click.alignTo(cursor, 'tl', [-4, -4]);
21
22// Add an async function call, pass callback to argument 1
23animator.addAsyncCall(Blog.navbar.undockDelegate, 1);
24
25// pause .5 seconds
26animator.pause(.5);
27
28// again, these animations will be run in sequence
29click.hide(true, .7);
30cursor.alignTo('splitter', 'tr', [0, +100], true, 1);
31resize.alignTo('splitter', 'tr', [-12, +100]);
32
33// start sync block: these animations will run at the same time
34animator.beginSync();
35cursor.hide();
36resize.show();
37animator.endSync();
38
39// play the captured animation sequences, call myCallback when done
40animator.play(myCallback);
41 * </code></pre>
42 * @requires YAHOO.ext.Element
43 * @requires YAHOO.util.Dom
44 * @requires YAHOO.util.Event
45 * @requires YAHOO.util.CustomEvent
46 * @requires YAHOO.util.Anim
47 * @requires YAHOO.util.ColorAnim
48 * @requires YAHOO.util.Motion
49 * @constructor
50 * @param {String/HTMLElement} el The dom element or element id
51 * @param {<i>YAHOO.ext.Animator</i>} animator (optional) The Animator that will capture this Actor's actions
52 * @param {<i>Boolean</i>} selfCapture (optional) Whether this actor should capture it's own actions to support self playback without an animator (defaults to false)
53 */
54 YAHOO.ext.Animator = function(/*Actors...*/){
55 this.actors = [];
56 this.playlist = new YAHOO.ext.Animator.AnimSequence();
57 this.captureDelegate = this.capture.createDelegate(this);
58 this.playDelegate = this.play.createDelegate(this);
59 this.syncing = false;
60 this.stopping = false;
61 this.playing = false;
62 for(var i = 0; i < arguments.length; i++){
63 this.addActor(arguments[i]);
64 }
65 };
66
67 YAHOO.ext.Animator.prototype = {
68
69 capture : function(actor, action){
70 if(this.syncing){
71 if(!this.syncMap[actor.id]){
72 this.syncMap[actor.id] = new YAHOO.ext.Animator.AnimSequence();
73 }
74 this.syncMap[actor.id].add(action);
75 }else{
76 this.playlist.add(action);
77 }
78 },
79
80 /**
81 * Add an actor. The actor is also set to capturing = true.
82 * @param {YAHOO.ext.Actor} actor
83 */
84 addActor : function(actor){
85 actor.onCapture.subscribe(this.captureDelegate);
86 this.actors.push(actor);
87 },
88
89
90 /**
91 * Start capturing actions on the added actors.
92 * @param {<i>Boolean</i>} clearPlaylist Whether to also create a new playlist
93 */
94 startCapture : function(clearPlaylist){
95 for(var i = 0; i < this.actors.length; i++){
96 var a = this.actors[i];
97 if(!this.isCapturing(a)){
98 a.onCapture.subscribe(this.captureDelegate);
99 }
100 a.capturing = true;
101 }
102 if(clearPlaylist){
103 this.playlist = new YAHOO.ext.Animator.AnimSequence();
104 }
105 },
106
107 /**
108 * Checks whether this animator is listening to a specific actor.
109 * @param {YAHOO.ext.Actor} actor
110 */
111 isCapturing : function(actor){
112 var subscribers = actor.onCapture.subscribers;
113 if(subscribers){
114 for(var i = 0; i < subscribers.length; i++){
115 if(subscribers[i] && subscribers[i].contains(this.captureDelegate)){
116 return true;
117 }
118 }
119 }
120 return false;
121 },
122
123 /**
124 * Stop capturing on all added actors.
125 */
126 stopCapture : function(){
127 for(var i = 0; i < this.actors.length; i++){
128 var a = this.actors[i];
129 a.onCapture.unsubscribe(this.captureDelegate);
130 a.capturing = false;
131 }
132 },
133
134 /**
135 * Start a multi-actor sync block. By default all animations are run in sequence. While in the sync block
136 * each actor's own animations will still be sequenced, but all actors will animate at the same time.
137 */
138 beginSync : function(){
139 this.syncing = true;
140 this.syncMap = {};
141 },
142
143 /**
144 * End the multi-actor sync block
145 */
146 endSync : function(){
147 this.syncing = false;
148 var composite = new YAHOO.ext.Animator.CompositeSequence();
149 for(key in this.syncMap){
150 if(typeof this.syncMap[key] != 'function'){
151 composite.add(this.syncMap[key]);
152 }
153 }
154 this.playlist.add(composite);
155 this.syncMap = null;
156 },
157
158 /**
159 * Starts playback of the playlist, also stops any capturing. To start capturing again call {@link #startCapture}.
160 * @param {<i>Function</i>} oncomplete (optional) Callback to execute when playback has completed
161 */
162 play : function(oncomplete){
163 if(this.playing) return; // can't play the same animation twice at once
164 this.stopCapture();
165 this.playlist.play(oncomplete);
166 },
167
168 /**
169 * Stop at the next available stopping point
170 */
171 stop : function(){
172 this.playlist.stop();
173 },
174
175 /**
176 * Check if this animator is currently playing
177 */
178 isPlaying : function(){
179 return this.playlist.isPlaying();
180 },
181 /**
182 * Clear the playlist
183 */
184 clear : function(){
185 this.playlist = new YAHOO.ext.Animator.AnimSequence();
186 },
187
188 /**
189 * Add a function call to the playlist.
190 * @param {Function} fcn The function to call
191 * @param {<i>Array</i>} args The arguments to call the function with
192 * @param {<i>Object</i>} scope (optional) The scope of the function
193 */
194 addCall : function(fcn, args, scope){
195 this.playlist.add(new YAHOO.ext.Actor.Action(scope, fcn, args || []));
196 },
197
198 /**
199 * Add an async function call to the playlist.
200 * @param {Function} fcn The function to call
201 * @param {Number} callbackIndex The index of the callback parameter on the passed function. A CALLBACK IS REQUIRED.
202 * @param {<i>Array</i>} args The arguments to call the function with
203 * @param {<i>Object</i>} scope (optional) The scope of the function
204 */
205 addAsyncCall : function(fcn, callbackIndex, args, scope){
206 this.playlist.add(new YAHOO.ext.Actor.AsyncAction(scope, fcn, args || [], callbackIndex));
207 },
208
209 /**
210 * Add a pause to the playlist (in seconds)
211 * @param {Number} seconds The number of seconds to pause.
212 */
213 pause : function(seconds){
214 this.playlist.add(new YAHOO.ext.Actor.PauseAction(seconds));
215 }
216
217 };
218/**
219 * Static function to build a AnimatorComposite from a css selector (requires YAHOO.ext.Element.selectorFunction be defined)
220 * @param {String/Array} selector The css selector or an array of nodes to animate
221 * @method @static
222 */
223YAHOO.ext.Animator.select = function(selector){
224 var els;
225 if(typeof selector == 'string'){
226 els = YAHOO.ext.Element.selectorFunction(selector);
227 }else if(selector instanceof Array){
228 els = selector;
229 }else{
230 throw 'Invalid selector';
231 }
232 return new YAHOO.ext.AnimatorComposite(els);
233};
234var getActors = YAHOO.ext.Animator.select;
235
236/**
237 * @class YAHOO.ext.AnimatorComposite
238 * Composite class with synchronized animations. This is the class returned by getActors(selector) or YAHOO.ext.Animator.select().
239 */
240YAHOO.ext.AnimatorComposite = function(els){
241 this.animator = new YAHOO.ext.Animator();
242 this.addElements(els);
243 this.syncAnims = true;
244};
245YAHOO.ext.AnimatorComposite.prototype = {
246 isComposite: true,
247 /**
248 * Adds elements to this composite.
249 * @param {Array} els An array of elements to add
250 * @return {AnimatorComposite} this
251 */
252 addElements : function(els){
253 if(!els) return this;
254 var anim = this.animator;
255 for(var i = 0, len = els.length; i < len; i++) {
256 anim.addActor(new YAHOO.ext.Actor(els[i]));
257 }
258 anim.startCapture();
259 return this;
260 },
261 /**
262 * Operations called after sequence() will be performed one by one on each element in this composite.
263 * @return {AnimatorComposite} this
264 */
265 sequence : function(){
266 this.syncAnims = false;
267 return this;
268 },
269 /**
270 * Operations called after sync() will be performed at the same time on each element in this composite.
271 * @return {AnimatorComposite} this
272 */
273 sync : function(){
274 this.syncAnims = true;
275 return this;
276 },
277 invoke : function(fn, args){
278 var els = this.animator.actors;
279 if(this.syncAnims) this.animator.beginSync();
280 for(var i = 0, len = els.length; i < len; i++) {
281 YAHOO.ext.Actor.prototype[fn].apply(els[i], args);
282 }
283 if(this.syncAnims) this.animator.endSync();
284 return this;
285 },
286 /**
287 * Play the actions queued in this composite.
288 * @param {Function} callback (optional) callback is called when all animations have compelted
289 * @return {AnimatorComposite} this
290 */
291 play : function(callback){
292 this.animator.play(callback);
293 return this;
294 },
295 /**
296 * Clear all actions in the queue.
297 * @param {Function} callback (optional) callback is called when all animations have compelted
298 * @return {AnimatorComposite} this
299 */
300 reset : function(callback){
301 this.animator.startCapture(true);
302 return this;
303 },
304 /**
305 * Add a pause
306 * @param {Number} seconds
307 * @return {AnimatorComposite} this
308 */
309 pause : function(seconds){
310 this.animator.pause(seconds);
311 return this;
312 },
313 /**
314 * Get the YAHOO.ext.Animator that controls the animations for this composite.
315 * @return {YAHOO.ext.Animator}
316 */
317 getAnimator : function(){
318 return this.animator;
319 },
320 /**
321 * Calls the passed function passing (el, this, index) for each element in this composite.
322 * @param {Function} fn The function to call
323 * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
324 * @return {AnimatorComposite} this
325 */
326 each : function(fn, scope){
327 var els = this.animator.actors;
328 if(this.syncAnims) this.animator.beginSync();
329 for(var i = 0, len = els.length; i < len; i++){
330 fn.call(scope || els[i], els[i], this, i);
331 }
332 if(this.syncAnims) this.animator.endSync();
333 return this;
334 },
335 /**
336 * Add a function call to the playlist.
337 * @param {Function} fcn The function to call
338 * @param {<i>Array</i>} args (optional) The arguments to call the function with
339 * @param {<i>Object</i>} scope (optional) The scope of the function
340 * @return {AnimatorComposite} this
341 */
342 addCall : function(fcn, args, scope){
343 this.animator.addCall(fcn, args, scope);
344 return this;
345 },
346 /**
347 * Add an async function call to the playlist.
348 * @param {Function} fcn The function to call
349 * @param {Number} callbackIndex The index of the callback parameter on the passed function. <b>A CALLBACK IS REQUIRED</b>.
350 * @param {<i>Array</i>} args (optional) The arguments to call the function with
351 * @param {<i>Object</i>} scope (optional) The scope of the function
352 * @return {AnimatorComposite} this
353 */
354 addAsyncCall : function(fcn, callbackIndex, args, scope){
355 this.animator.addAsyncCall(fcn, callbackIndex, args, scope);
356 return this;
357 }
358};
359for(var fnName in YAHOO.ext.Actor.prototype){
360 if(typeof YAHOO.ext.Actor.prototype[fnName] == 'function'){
361 YAHOO.ext.CompositeElement.createCall(YAHOO.ext.AnimatorComposite.prototype, fnName);
362 }
363}
364
365
366YAHOO.ext.Animator.AnimSequence = function(){
367 this.actions = [];
368 this.nextDelegate = this.next.createDelegate(this);
369 this.playDelegate = this.play.createDelegate(this);
370 this.oncomplete = null;
371 this.playing = false;
372 this.stopping = false;
373 this.actionIndex = -1;
374 };
375
376 YAHOO.ext.Animator.AnimSequence.prototype = {
377
378 add : function(action){
379 this.actions.push(action);
380 },
381
382 next : function(){
383 if(this.stopping){
384 this.playing = false;
385 if(this.oncomplete){
386 this.oncomplete(this, false);
387 }
388 return;
389 }
390 var nextAction = this.actions[++this.actionIndex];
391 if(nextAction){
392 nextAction.play(this.nextDelegate);
393 }else{
394 this.playing = false;
395 if(this.oncomplete){
396 this.oncomplete(this, true);
397 }
398 }
399 },
400
401 play : function(oncomplete){
402 if(this.playing) return; // can't play the same sequence twice at once
403 this.oncomplete = oncomplete;
404 this.stopping = false;
405 this.playing = true;
406 this.actionIndex = -1;
407 this.next();
408 },
409
410 stop : function(){
411 this.stopping = true;
412 },
413
414 isPlaying : function(){
415 return this.playing;
416 },
417
418 clear : function(){
419 this.actions = [];
420 },
421
422 addCall : function(fcn, args, scope){
423 this.actions.push(new YAHOO.ext.Actor.Action(scope, fcn, args || []));
424 },
425
426 addAsyncCall : function(fcn, callbackIndex, args, scope){
427 this.actions.push(new YAHOO.ext.Actor.AsyncAction(scope, fcn, args || [], callbackIndex));
428 },
429
430 pause : function(seconds){
431 this.actions.push(new YAHOO.ext.Actor.PauseAction(seconds));
432 }
433
434 };
435
436YAHOO.ext.Animator.CompositeSequence = function(){
437 this.sequences = [];
438 this.completed = 0;
439 this.trackDelegate = this.trackCompletion.createDelegate(this);
440}
441
442YAHOO.ext.Animator.CompositeSequence.prototype = {
443 add : function(sequence){
444 this.sequences.push(sequence);
445 },
446
447 play : function(onComplete){
448 this.completed = 0;
449 if(this.sequences.length < 1){
450 if(onComplete)onComplete();
451 return;
452 }
453 this.onComplete = onComplete;
454 for(var i = 0; i < this.sequences.length; i++){
455 this.sequences[i].play(this.trackDelegate);
456 }
457 },
458
459 trackCompletion : function(){
460 ++this.completed;
461 if(this.completed >= this.sequences.length && this.onComplete){
462 this.onComplete();
463 }
464 },
465
466 stop : function(){
467 for(var i = 0; i < this.sequences.length; i++){
468 this.sequences[i].stop();
469 }
470 },
471
472 isPlaying : function(){
473 for(var i = 0; i < this.sequences.length; i++){
474 if(this.sequences[i].isPlaying()){
475 return true;
476 }
477 }
478 return false;
479 }
480};
481
482
diff --git a/frontend/beta/js/YUI-extensions/data/AbstractDataModel.js b/frontend/beta/js/YUI-extensions/data/AbstractDataModel.js
new file mode 100644
index 0000000..092ea75
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/data/AbstractDataModel.js
@@ -0,0 +1,226 @@
1/**
2 * @class YAHOO.ext.grid.AbstractDataModel
3 * @extends YAHOO.ext.util.Observable
4 * This abstract class provides default implementations of the events required by the Grid.
5 It takes care of the creating the CustomEvents and provides some convenient methods for firing the events. <br><br>
6 * @constructor
7*/
8YAHOO.ext.grid.AbstractDataModel = function(){
9 /** Fires when a cell is updated - fireDirect sig: (this, rowIndex, columnIndex)
10 * @private
11 * @type YAHOO.util.CustomEvent
12 * @deprecated Use addListener instead of accessing directly
13 */
14 this.onCellUpdated = new YAHOO.util.CustomEvent('onCellUpdated');
15 /** Fires when all data needs to be revalidated - fireDirect sig: (thisd)
16 * @private
17 * @type YAHOO.util.CustomEvent
18 * @deprecated Use addListener instead of accessing directly
19 */
20 this.onTableDataChanged = new YAHOO.util.CustomEvent('onTableDataChanged');
21 /** Fires when rows are deleted - fireDirect sig: (this, firstRowIndex, lastRowIndex)
22 * @private
23 * @type YAHOO.util.CustomEvent
24 * @deprecated Use addListener instead of accessing directly
25 */
26 this.onRowsDeleted = new YAHOO.util.CustomEvent('onRowsDeleted');
27 /** Fires when a rows are inserted - fireDirect sig: (this, firstRowIndex, lastRowIndex)
28 * @private
29 * @type YAHOO.util.CustomEvent
30 * @deprecated Use addListener instead of accessing directly
31 */
32 this.onRowsInserted = new YAHOO.util.CustomEvent('onRowsInserted');
33 /** Fires when a rows are updated - fireDirect sig: (this, firstRowIndex, lastRowIndex)
34 * @private
35 * @type YAHOO.util.CustomEvent
36 * @deprecated Use addListener instead of accessing directly
37 */
38 this.onRowsUpdated = new YAHOO.util.CustomEvent('onRowsUpdated');
39 /** Fires when a sort has reordered the rows - fireDirect sig: (this, sortColumnIndex,
40 * @private
41 * sortDirection = 'ASC' or 'DESC')
42 * @type YAHOO.util.CustomEvent
43 * @deprecated Use addListener instead of accessing directly
44 */
45 this.onRowsSorted = new YAHOO.util.CustomEvent('onRowsSorted');
46
47 this.events = {
48 /**
49 * @event cellupdated
50 * Fires when a cell is updated
51 * @param {DataModel} this
52 * @param {Number} rowIndex
53 * @param {Number} columnIndex
54 */
55 'cellupdated' : this.onCellUpdated,
56 /**
57 * @event datachanged
58 * Fires when the entire data structure has changed
59 * @param {DataModel} this
60 */
61 'datachanged' : this.onTableDataChanged,
62 /**
63 * @event rowsdeleted
64 * Fires when a range of rows have been deleted
65 * @param {DataModel} this
66 * @param {Number} firstRowIndex
67 * @param {Number} lastRowIndex
68 */
69 'rowsdeleted' : this.onRowsDeleted,
70 /**
71 * @event rowsinserted
72 * Fires when a range of rows have been inserted
73 * @param {DataModel} this
74 * @param {Number} firstRowIndex
75 * @param {Number} lastRowIndex
76 */
77 'rowsinserted' : this.onRowsInserted,
78 /**
79 * @event rowsupdated
80 * Fires when a range of rows have been updated
81 * @param {DataModel} this
82 * @param {Number} firstRowIndex
83 * @param {Number} lastRowIndex
84 */
85 'rowsupdated' : this.onRowsUpdated,
86 /**
87 * @event rowssorted
88 * Fires when the data has been sorted
89 * @param {DataModel} this
90 */
91 'rowssorted' : this.onRowsSorted
92 };
93};
94
95YAHOO.ext.grid.AbstractDataModel.prototype = {
96
97 fireEvent : YAHOO.ext.util.Observable.prototype.fireEvent,
98 on : YAHOO.ext.util.Observable.prototype.on,
99 addListener : YAHOO.ext.util.Observable.prototype.addListener,
100 delayedListener : YAHOO.ext.util.Observable.prototype.delayedListener,
101 removeListener : YAHOO.ext.util.Observable.prototype.removeListener,
102 purgeListeners : YAHOO.ext.util.Observable.prototype.purgeListeners,
103 bufferedListener : YAHOO.ext.util.Observable.prototype.bufferedListener,
104
105 /**
106 * Notifies listeners that the value of the cell at [row, col] has been updated
107 * @deprecated
108 * @private
109 */
110 fireCellUpdated : function(row, col){
111 this.onCellUpdated.fireDirect(this, row, col);
112 },
113
114 /**
115 * Notifies listeners that all data for the grid may have changed - use as a last resort. This
116 * also wipes out all selections a user might have made.
117 * @deprecated
118 * @private
119 */
120 fireTableDataChanged : function(){
121 this.onTableDataChanged.fireDirect(this);
122 },
123
124 /**
125 * Notifies listeners that rows in the range [firstRow, lastRow], inclusive, have been deleted
126 * @deprecated
127 * @private
128 */
129 fireRowsDeleted : function(firstRow, lastRow){
130 this.onRowsDeleted.fireDirect(this, firstRow, lastRow);
131 },
132
133 /**
134 * Notifies listeners that rows in the range [firstRow, lastRow], inclusive, have been inserted
135 * @deprecated
136 * @private
137 */
138 fireRowsInserted : function(firstRow, lastRow){
139 this.onRowsInserted.fireDirect(this, firstRow, lastRow);
140 },
141
142 /**
143 * Notifies listeners that rows in the range [firstRow, lastRow], inclusive, have been updated
144 * @deprecated
145 * @private
146 */
147 fireRowsUpdated : function(firstRow, lastRow){
148 this.onRowsUpdated.fireDirect(this, firstRow, lastRow);
149 },
150
151 /**
152 * Notifies listeners that rows have been sorted and any indexes may be invalid
153 * @deprecated
154 * @private
155 */
156 fireRowsSorted : function(sortColumnIndex, sortDir, noRefresh){
157 this.onRowsSorted.fireDirect(this, sortColumnIndex, sortDir, noRefresh);
158 },
159
160 /**
161 * Empty interface method - Classes which extend AbstractDataModel should implement this method.
162 * See {@link YAHOO.ext.DefaultDataModel#sort} for an example implementation.
163 * @private
164 */
165 sort : function(sortInfo, columnIndex, direction, suppressEvent){
166
167 },
168
169 /**
170 * Interface method to supply info regarding the Grid's current sort state - if overridden,
171 * this should return an object like this {column: this.sortColumn, direction: this.sortDir}.
172 * @return {Object}
173 */
174 getSortState : function(){
175 return {column: this.sortColumn, direction: this.sortDir};
176 },
177
178 /**
179 * Empty interface method - Classes which extend AbstractDataModel should implement this method.
180 * See {@link YAHOO.ext.DefaultDataModel} for an example implementation.
181 * @private
182 */
183 getRowCount : function(){
184
185 },
186
187 /**
188 * Empty interface method - Classes which extend AbstractDataModel should implement this method to support virtual row counts.
189 * @private
190 */
191 getTotalRowCount : function(){
192 return this.getRowCount();
193 },
194
195
196 /**
197 * Empty interface method - Classes which extend AbstractDataModel should implement this method.
198 * See {@link YAHOO.ext.DefaultDataModel} for an example implementation.
199 * @private
200 */
201 getRowId : function(rowIndex){
202
203 },
204
205 /**
206 * Empty interface method - Classes which extend AbstractDataModel should implement this method.
207 * See {@link YAHOO.ext.DefaultDataModel} for an example implementation.
208 * @private
209 */
210 getValueAt : function(rowIndex, colIndex){
211
212 },
213
214 /**
215 * Empty interface method - Classes which extend AbstractDataModel should implement this method.
216 * See {@link YAHOO.ext.DefaultDataModel} for an example implementation.
217 * @private
218 */
219 setValueAt : function(value, rowIndex, colIndex){
220
221 },
222
223 isPaged : function(){
224 return false;
225 }
226};
diff --git a/frontend/beta/js/YUI-extensions/data/DefaultDataModel.js b/frontend/beta/js/YUI-extensions/data/DefaultDataModel.js
new file mode 100644
index 0000000..57a022a
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/data/DefaultDataModel.js
@@ -0,0 +1,339 @@
1
2/**
3 * @class YAHOO.ext.grid.DefaultDataModel
4 * This is the default implementation of a DataModel used by the Grid. It works
5 * with multi-dimensional array based data. Using the event system in the base class
6 * {@link YAHOO.ext.grid.AbstractDataModel}, all updates to this DataModel are automatically
7 * reflected in the user interface.
8 * <br>Usage:<br>
9 * <pre><code>
10 * var myData = [
11 ["MSFT","Microsoft Corporation", "314,571.156", "32,187.000", "55000"],
12 ["ORCL", "Oracle Corporation", "62,615.266", "9,519.000", "40650"]
13 * ];
14 * var dataModel = new YAHOO.ext.grid.DefaultDataModel(myData);
15 * </code></pre>
16 * @extends YAHOO.ext.grid.AbstractDataModel
17 * @constructor
18 * @param {Array} data
19*/
20YAHOO.ext.grid.DefaultDataModel = function(data){
21 YAHOO.ext.grid.DefaultDataModel.superclass.constructor.call(this);
22 /**@private*/
23 this.data = data;
24};
25YAHOO.extendX(YAHOO.ext.grid.DefaultDataModel, YAHOO.ext.grid.AbstractDataModel, {
26 /**
27 * Returns the number of rows in the dataset
28 * @return {Number}
29 */
30 getRowCount : function(){
31 return this.data.length;
32 },
33
34 /**
35 * Returns the ID of the specified row. By default it return the value of the first column.
36 * Override to provide more advanced ID handling.
37 * @return {Number}
38 */
39 getRowId : function(rowIndex){
40 return this.data[rowIndex][0];
41 },
42
43 /**
44 * Returns the column data for the specified row.
45 * @return {Array}
46 */
47 getRow : function(rowIndex){
48 return this.data[rowIndex];
49 },
50
51 /**
52 * Returns the column data for the specified rows as a
53 * multi-dimensional array: rows[3][0] would give you the value of row 4, column 0.
54 * @param {Array} indexes The row indexes to fetch
55 * @return {Array}
56 */
57 getRows : function(indexes){
58 var data = this.data;
59 var r = [];
60 for(var i = 0; i < indexes.length; i++){
61 r.push(data[indexes[i]]);
62 }
63 return r;
64 },
65
66 /**
67 * Returns the value at the specified data position
68 * @param {Number} rowIndex
69 * @param {Number} colIndex
70 * @return {Object}
71 */
72 getValueAt : function(rowIndex, colIndex){
73 return this.data[rowIndex][colIndex];
74 },
75
76 /**
77 * Sets the specified value at the specified data position
78 * @param {Object} value The new value
79 * @param {Number} rowIndex
80 * @param {Number} colIndex
81 */
82 setValueAt: function(value, rowIndex, colIndex){
83 this.data[rowIndex][colIndex] = value;
84 this.fireCellUpdated(rowIndex, colIndex);
85 },
86
87 /**
88 * @private
89 * Removes the specified range of rows.
90 * @param {Number} startIndex
91 * @param {<i>Number</i>} endIndex (optional) Defaults to startIndex
92 */
93 removeRows: function(startIndex, endIndex){
94 endIndex = endIndex || startIndex;
95 this.data.splice(startIndex, endIndex-startIndex+1);
96 this.fireRowsDeleted(startIndex, endIndex);
97 },
98
99 /**
100 * Remove a row.
101 * @param {Number} index
102 */
103 removeRow: function(index){
104 this.data.splice(index, 1);
105 this.fireRowsDeleted(index, index);
106 },
107
108 /**
109 * @private
110 * Removes all rows.
111 */
112 removeAll: function(){
113 var count = this.getRowCount();
114 if(count > 0){
115 this.removeRows(0, count-1);
116 }
117 },
118
119 /**
120 * Query the DataModel rows by the filters defined in spec, for example...
121 * <pre><code>
122 * // column 1 starts with Jack, column 2 filtered by myFcn, column 3 equals 'Fred'
123 * dataModel.filter({1: /^Jack.+/i}, 2: myFcn, 3: 'Fred'});
124 * </code></pre>
125 * @param {Object} spec The spec is generally an object literal consisting of
126 * column index and filter type. The filter type can be a string/number (exact match),
127 * a regular expression to test using String.search() or a function to call. If it's a function,
128 * it will be called with the value for the specified column and an array of the all column
129 * values for that row: yourFcn(value, columnData). If it returns anything other than true,
130 * the row is not a match. If you have modified Object.prototype this method may fail.
131 * @param {Boolean} returnUnmatched True to return rows which <b>don't</b> match the query instead
132 * of rows that do match
133 * @return {Array} An array of row indexes that match
134 */
135 query: function(spec, returnUnmatched){
136 var d = this.data;
137 var r = [];
138 for(var i = 0; i < d.length; i++){
139 var row = d[i];
140 var isMatch = true;
141 for(var col in spec){
142 //if(typeof spec[col] != 'function'){
143 if(!isMatch) continue;
144 var filter = spec[col];
145 switch(typeof filter){
146 case 'string':
147 case 'number':
148 case 'boolean':
149 if(row[col] != filter){
150 isMatch = false;
151 }
152 break;
153 case 'function':
154 if(!filter(row[col], row)){
155 isMatch = false;
156 }
157 break;
158 case 'object':
159 if(filter instanceof RegExp){
160 if(String(row[col]).search(filter) === -1){
161 isMatch = false;
162 }
163 }
164 break;
165 }
166 //}
167 }
168 if(isMatch && !returnUnmatched){
169 r.push(i);
170 }else if(!isMatch && returnUnmatched){
171 r.push(i);
172 }
173 }
174 return r;
175 },
176
177 /**
178 * Filter the DataModel rows by the query defined in spec, see {@link #query} for more details
179 * on the query spec.
180 * @param {Object} query The query spec {@link #query}
181 * @return {Number} The number of rows removed
182 */
183 filter: function(query){
184 var matches = this.query(query, true);
185 var data = this.data;
186 // go thru the data setting matches to deleted
187 // while not disturbing row indexes
188 for(var i = 0; i < matches.length; i++){
189 data[matches[i]]._deleted = true;
190 }
191 for(var i = 0; i < data.length; i++){
192 while(data[i] && data[i]._deleted === true){
193 this.removeRow(i);
194 }
195 }
196 return matches.length;
197 },
198
199 /**
200 * Adds a row to the dataset.
201 * @param {Array} cellValues The array of values for the new row
202 * @return {Number} The index of the added row
203 */
204 addRow: function(cellValues){
205 this.data.push(cellValues);
206 var newIndex = this.data.length-1;
207 this.fireRowsInserted(newIndex, newIndex);
208 this.applySort();
209 return newIndex;
210 },
211
212 /**
213 * @private
214 * Adds a set of rows.
215 * @param {Array} rowData This should be an array of arrays like the constructor takes
216 */
217 addRows: function(rowData){
218 this.data = this.data.concat(rowData);
219 var firstIndex = this.data.length-rowData.length;
220 this.fireRowsInserted(firstIndex, firstIndex+rowData.length-1);
221 this.applySort();
222 },
223
224 /**
225 * Inserts a row a the specified location in the dataset.
226 * @param {Number} index The index where the row should be inserted
227 * @param {Array} cellValues The array of values for the new row
228 * @return {Number} The index the row was inserted in
229 */
230 insertRow: function(index, cellValues){
231 this.data.splice(index, 0, cellValues);
232 this.fireRowsInserted(index, index);
233 this.applySort();
234 return index;
235 },
236
237 /**
238 * @private
239 * Inserts a set of rows.
240 * @param {Number} index The index where the rows should be inserted
241 * @param {Array} rowData This should be an array of arrays like the constructor takes
242 */
243 insertRows: function(index, rowData){
244 /*
245 if(index == this.data.length){ // try these two first since they are faster
246 this.data = this.data.concat(rowData);
247 }else if(index == 0){
248 this.data = rowData.concat(this.data);
249 }else{
250 var newData = this.data.slice(0, index);
251 newData.concat(rowData);
252 newData.concat(this.data.slice(index));
253 this.data = newData;
254 }*/
255 var args = rowData.concat();
256 args.splice(0, 0, index, 0);
257 this.data.splice.apply(this.data, args);
258 this.fireRowsInserted(index, index+rowData.length-1);
259 this.applySort();
260 },
261
262 /**
263 * Applies the last used sort to the current data.
264 */
265 applySort: function(suppressEvent){
266 if(typeof this.sortColumn != 'undefined'){
267 this.sort(this.sortInfo, this.sortColumn, this.sortDir, suppressEvent);
268 }
269 },
270
271 /**
272 * Sets the default sort info. Note: this function does not actually apply the sort.
273 * @param {Function/Object} sortInfo A sort comparison function or null to use the default or A object that has a method getSortType(index) that returns a function like
274 * a grid column model.
275 * @param {Number} columnIndex The column index to sort by
276 * @param {String} direction The direction of the sort ('DESC' or 'ASC')
277 */
278 setDefaultSort: function(sortInfo, columnIndex, direction){
279 this.sortInfo = sortInfo;
280 this.sortColumn = columnIndex;
281 this.sortDir = direction;
282 },
283 /**
284 * Sorts the data by the specified column - Uses the sortType specified for the column in the passed columnModel.
285 * @param {Function/Object} sortInfo A sort comparison function or null to use the default or A object that has a method getSortType(index) that returns a function like
286 * a grid column model.
287 * @param {Number} columnIndex The column index to sort by
288 * @param {String} direction The direction of the sort ('DESC' or 'ASC')
289 */
290 sort: function(sortInfo, columnIndex, direction, suppressEvent){
291 // store these so we can maintain sorting when we load new data
292 this.sortInfo = sortInfo;
293 this.sortColumn = columnIndex;
294 this.sortDir = direction;
295
296 var dsc = (direction && direction.toUpperCase() == 'DESC');
297 var sortType = null;
298 if(sortInfo != null){
299 if(typeof sortInfo == 'function'){
300 sortType = sortInfo;
301 }else if(typeof sortInfo == 'object'){
302 sortType = sortInfo.getSortType(columnIndex);;
303 }
304 }
305 var fn = function(cells, cells2){
306 var v1 = sortType ? sortType(cells[columnIndex], cells) : cells[columnIndex];
307 var v2 = sortType ? sortType(cells2[columnIndex], cells2) : cells2[columnIndex];
308 if(v1 < v2)
309 return dsc ? +1 : -1;
310 if(v1 > v2)
311 return dsc ? -1 : +1;
312 return 0;
313 };
314 this.data.sort(fn);
315 if(!suppressEvent){
316 this.fireRowsSorted(columnIndex, direction);
317 }
318 },
319
320 /**
321 * Calls passed function with each rows data - if the function returns false it stops.
322 * @param {Function} fn
323 * @param {Object} scope (optional)
324 */
325 each: function(fn, scope){
326 var d = this.data;
327 for(var i = 0, len = d.length; i < len; i++){
328 if(fn.call(scope || window, d[i], i) === false) break;
329 }
330 }
331});
332
333/**
334 * Alias to YAHOO.ext.grid.DefaultColumnModel.sortTypes
335 * @static
336 */
337if(YAHOO.ext.grid.DefaultColumnModel){
338 YAHOO.ext.grid.DefaultDataModel.sortTypes = YAHOO.ext.grid.DefaultColumnModel.sortTypes;
339}
diff --git a/frontend/beta/js/YUI-extensions/data/JSONDataModel.js b/frontend/beta/js/YUI-extensions/data/JSONDataModel.js
new file mode 100644
index 0000000..ca48dce
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/data/JSONDataModel.js
@@ -0,0 +1,81 @@
1
2/**
3 * @class YAHOO.ext.grid.JSONDataModel
4 * This is an implementation of a DataModel used by the Grid. It works
5 * with JSON data.
6 * <br>Example schema:
7 * <pre><code>
8 * var schema = {
9 * root: 'Results.Result',
10 * id: 'ASIN',
11 * fields: ['Author', 'Title', 'Manufacturer', 'ProductGroup']
12 * };
13 * </code></pre>
14 * @extends YAHOO.ext.grid.LoadableDataModel
15 * @constructor
16*/
17YAHOO.ext.grid.JSONDataModel = function(schema){
18 YAHOO.ext.grid.JSONDataModel.superclass.constructor.call(this, YAHOO.ext.grid.LoadableDataModel.JSON);
19 /**@private*/
20 this.schema = schema;
21};
22YAHOO.extendX(YAHOO.ext.grid.JSONDataModel, YAHOO.ext.grid.LoadableDataModel, {
23 /**
24 * Overrides loadData in LoadableDataModel to process JSON data
25 * @param {Object} data The JSON object to load
26 * @param {Function} callback
27 */
28 loadData : function(data, callback, keepExisting){
29 var idField = this.schema.id;
30 var fields = this.schema.fields;
31 try{
32 if(this.schema.totalProperty){
33 var v = parseInt(eval('data.' + this.schema.totalProperty), 10);
34 if(!isNaN(v)){
35 this.totalCount = v;
36 }
37 }
38 var rowData = [];
39 var root = eval('data.' + this.schema.root);
40 for(var i = 0; i < root.length; i++){
41 var node = root[i];
42 var colData = [];
43 colData.node = node;
44 colData.id = (typeof node[idField] != 'undefined' && node[idField] !== '' ? node[idField] : String(i));
45 for(var j = 0; j < fields.length; j++) {
46 var val = node[fields[j]];
47 if(typeof val == 'undefined'){
48 val = '';
49 }
50 if(this.preprocessors[j]){
51 val = this.preprocessors[j](val);
52 }
53 colData.push(val);
54 }
55 rowData.push(colData);
56 }
57 if(keepExisting !== true){
58 this.removeAll();
59 }
60 this.addRows(rowData);
61 if(typeof callback == 'function'){
62 callback(this, true);
63 }
64 this.fireLoadEvent();
65 }catch(e){
66 this.fireLoadException(e, null);
67 if(typeof callback == 'function'){
68 callback(this, false);
69 }
70 }
71 },
72
73 /**
74 * Overrides getRowId in DefaultDataModel to return the ID value of the specified node.
75 * @param {Number} rowIndex
76 * @return {Number}
77 */
78 getRowId : function(rowIndex){
79 return this.data[rowIndex].id;
80 }
81});
diff --git a/frontend/beta/js/YUI-extensions/data/LoadableDataModel.js b/frontend/beta/js/YUI-extensions/data/LoadableDataModel.js
new file mode 100644
index 0000000..07def44
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/data/LoadableDataModel.js
@@ -0,0 +1,330 @@
1/**
2 * @class YAHOO.ext.grid.LoadableDataModel
3 * This class extends DefaultDataModel and adds the core functionality to load data remotely. Generally you will want to use one of it's subclasses.<br><br>
4 * @extends YAHOO.ext.grid.DefaultDataModel
5 * @constructor
6 * @param {String} dataType YAHOO.ext.grid.LoadableDataModel.XML, YAHOO.ext.grid.LoadableDataModel.TEXT or YAHOO.ext.grid.JSON
7*/
8YAHOO.ext.grid.LoadableDataModel = function(dataType){
9 YAHOO.ext.grid.LoadableDataModel.superclass.constructor.call(this, []);
10
11 /** Fires when a successful load is completed - fireDirect sig: (this)
12 * @type YAHOO.util.CustomEvent
13 * @deprecated Use addListener instead of accessing directly
14 * @private
15 */
16 this.onLoad = new YAHOO.util.CustomEvent('load');
17 /** Fires when a load fails - fireDirect sig: (this, errorMsg, responseObj)
18 * @type YAHOO.util.CustomEvent
19 * @deprecated Use addListener instead of accessing directly
20 * @private
21 */
22 this.onLoadException = new YAHOO.util.CustomEvent('loadException');
23 /**
24 * @event load
25 * Fires when new data has successfully been loaded
26 * @param {DataModel} this
27 */
28 this.events['load'] = this.onLoad;
29 /**
30 * @event beforeload
31 * Fires before a load takes place
32 * @param {DataModel} this
33 */
34 this.events['beforeload'] = new YAHOO.util.CustomEvent('beforeload');
35 /**
36 * @event loadexception
37 * Fires when there's an error loading data
38 * @param {DataModel} this
39 * @param {Exception} e The exception object or null
40 * @param {Object} response The Connect response object
41 */
42 this.events['loadexception'] = this.onLoadException;
43
44 /**@private*/
45 this.dataType = dataType;
46 /**@private*/
47 this.preprocessors = [];
48 /**@private*/
49 this.postprocessors = [];
50
51 // paging info
52 /** The active page @type Number*/
53 this.loadedPage = 1;
54 /** True to use remote sorting, initPaging automatically sets this to true @type Boolean */
55 this.remoteSort = false;
56 /** The number of records per page @type Number*/
57 this.pageSize = 0;
58 /** The script/page to call to provide paged/sorted data @type String*/
59 this.pageUrl = null;
60 /** An object of key/value pairs to be passed as parameters
61 * when loading pages/sorting @type Object*/
62 this.baseParams = {};
63 /** Maps named params to url parameters - Override to specify your own param names */
64 this.paramMap = {'page':'page', 'pageSize':'pageSize', 'sortColumn':'sortColumn', 'sortDir':'sortDir'};
65
66};
67YAHOO.extendX(YAHOO.ext.grid.LoadableDataModel, YAHOO.ext.grid.DefaultDataModel, {
68
69 /** @ignore */
70 setLoadedPage: function(pageNum, userCallback){
71 this.loadedPage = pageNum;
72 if(typeof userCallback == 'function'){
73 userCallback();
74 }
75 },
76
77 /** Returns true if this model uses paging @return Boolean */
78 isPaged: function(){
79 return this.pageSize > 0;
80 },
81
82 /** Returns the total number of records available, override if needed @return {Number} */
83 getTotalRowCount: function(){
84 return this.totalCount || this.getRowCount();
85 },
86
87 /** Returns the number of records per page @return Number */
88 getPageSize: function(){
89 return this.pageSize;
90 },
91
92 /** Returns the total number of pages available @return Number */
93 getTotalPages: function(){
94 if(this.getPageSize() == 0 || this.getTotalRowCount() == 0){
95 return 1;
96 }
97 return Math.ceil(this.getTotalRowCount()/this.getPageSize());
98 },
99
100 /** Initializes paging for this model.
101 * @param {String} url
102 * @param {Number} pageSize
103 * @param {Object} baseParams (optional) Object containing key/value pairs to add to all requests
104 */
105 initPaging: function(url, pageSize, baseParams){
106 this.pageUrl = url;
107 this.pageSize = pageSize;
108 this.remoteSort = true;
109 if(baseParams) this.baseParams = baseParams;
110 },
111
112 /** @ignore */
113 createParams: function(pageNum, sortColumn, sortDir){
114 var params = {}, map = this.paramMap;
115 for(var key in this.baseParams){
116 if(typeof this.baseParams[key] != 'function'){
117 params[key] = this.baseParams[key];
118 }
119 }
120 params[map['page']] = pageNum;
121 params[map['pageSize']] = this.getPageSize();
122 params[map['sortColumn']] = (typeof sortColumn == 'undefined' ? '' : sortColumn);
123 params[map['sortDir']] = sortDir || '';
124 return params;
125 },
126
127 /**
128 * Loads a page of data.
129 * @param {Number} pageNum Which page to load. The first page is 1.
130 * @param {Function} callback (optional) Optional callback when loading is complete
131 * @param {Boolean} keepExisting (optional) true to keep existing data and append the new data
132 */
133 loadPage: function(pageNum, callback, keepExisting){
134 var sort = this.getSortState();
135 var params = this.createParams(pageNum, sort.column, sort.direction);
136 this.load(this.pageUrl, params, this.setLoadedPage.createDelegate(this, [pageNum, callback]),
137 keepExisting ? (pageNum-1) * this.pageSize : null);
138 },
139
140 /** @ignore */
141 applySort: function(suppressEvent){
142 if(!this.remoteSort){
143 YAHOO.ext.grid.LoadableDataModel.superclass.applySort.apply(this, arguments);
144 }else if(!suppressEvent){
145 var sort = this.getSortState();
146 if(sort.column){
147 this.fireRowsSorted(sort.column, sort.direction, true);
148 }
149 }
150 },
151
152 /** @ignore */
153 resetPaging: function(){
154 this.loadedPage = 1;
155 },
156
157 /* Overridden sort method to use remote sorting if turned on */
158 sort: function(sortInfo, columnIndex, direction, suppressEvent){
159 if(!this.remoteSort){
160 YAHOO.ext.grid.LoadableDataModel.superclass.sort.apply(this, arguments);
161 }else{
162 this.sortInfo = sortInfo;
163 this.sortColumn = columnIndex;
164 this.sortDir = direction;
165 var params = this.createParams(this.loadedPage, columnIndex, direction);
166 this.load(this.pageUrl, params, this.fireRowsSorted.createDelegate(this, [columnIndex, direction, true]));
167 }
168 },
169
170 /**
171 * Initiates the loading of the data from the specified URL - Failed load attempts will
172 * fire the {@link #loadexception} event.
173 * @param {Object/String} url The url from which the data can be loaded
174 * @param {<i>String/Object</i>} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or as an object {param1: 1, param2: 2}
175 * @param {<i>Function</i>} callback (optional) Callback when load is complete - called with signature (this, true for success, false for failure)
176 * @param {<i>Number</i>} insertIndex (optional) if present, loaded data is inserted at the specified index instead of overwriting existing data
177 */
178 load: function(url, params, callback, insertIndex){
179 this.fireEvent('beforeload', this);
180 if(params && typeof params != 'string'){ // must be object
181 var buf = [];
182 for(var key in params){
183 if(typeof params[key] != 'function'){
184 buf.push(encodeURIComponent(key), '=', encodeURIComponent(params[key]), '&');
185 }
186 }
187 delete buf[buf.length-1];
188 params = buf.join('');
189 }
190 var cb = {
191 success: this.processResponse,
192 failure: this.processException,
193 scope: this,
194 argument: {callback: callback, insertIndex: insertIndex}
195 };
196 var method = params ? 'POST' : 'GET';
197 this.transId = YAHOO.util.Connect.asyncRequest(method, url, cb, params);
198 },
199
200 /**@private*/
201 processResponse: function(response){
202 var cb = response.argument.callback;
203 var keepExisting = (typeof response.argument.insertIndex == 'number');
204 var insertIndex = response.argument.insertIndex;
205 switch(this.dataType){
206 case YAHOO.ext.grid.LoadableDataModel.XML:
207 this.loadData(response.responseXML, cb, keepExisting, insertIndex);
208 break;
209 case YAHOO.ext.grid.LoadableDataModel.JSON:
210 var rtext = response.responseText;
211 try { // this code is a modified version of Yahoo! UI DataSource JSON parsing
212 // Trim leading spaces
213 while(rtext.substring(0,1) == " ") {
214 rtext = rtext.substring(1, rtext.length);
215 }
216 // Invalid JSON response
217 if(rtext.indexOf("{") < 0) {
218 throw "Invalid JSON response";
219 }
220
221 // Empty (but not invalid) JSON response
222 if(rtext.indexOf("{}") === 0) {
223 this.loadData({}, response.argument.callback);
224 return;
225 }
226
227 // Turn the string into an object literal...
228 // ...eval is necessary here
229 var jsonObjRaw = eval("(" + rtext + ")");
230 if(!jsonObjRaw) {
231 throw "Error evaling JSON response";
232 }
233 this.loadData(jsonObjRaw, cb, keepExisting, insertIndex);
234 } catch(e) {
235 this.fireLoadException(e, response);
236 if(typeof cb == 'function'){
237 cb(this, false);
238 }
239 }
240 break;
241 case YAHOO.ext.grid.LoadableDataModel.TEXT:
242 this.loadData(response.responseText, cb, keepExisting, insertIndex);
243 break;
244 };
245 },
246
247 /**@private*/
248 processException: function(response){
249 this.fireLoadException(null, response);
250 if(typeof response.argument.callback == 'function'){
251 response.argument.callback(this, false);
252 }
253 },
254
255 fireLoadException: function(e, responseObj){
256 this.onLoadException.fireDirect(this, e, responseObj);
257 },
258
259 fireLoadEvent: function(){
260 this.fireEvent('load', this.loadedPage, this.getTotalPages());
261 },
262
263 /**
264 * Adds a preprocessor function to parse data before it is added to the Model - ie. Date.parse to parse dates.
265 * @param {Number} columnIndex
266 * @param {Function} fn
267 */
268 addPreprocessor: function(columnIndex, fn){
269 this.preprocessors[columnIndex] = fn;
270 },
271
272 /**
273 * Gets the preprocessor function for the specified column.
274 * @param {Number} columnIndex
275 * @return {Function}
276 */
277 getPreprocessor: function(columnIndex){
278 return this.preprocessors[columnIndex];
279 },
280
281 /**
282 * Removes a preprocessor function.
283 * @param {Number} columnIndex
284 */
285 removePreprocessor: function(columnIndex){
286 this.preprocessors[columnIndex] = null;
287 },
288
289 /**
290 * Adds a postprocessor function to format data before updating the underlying data source (ie. convert date to string before updating XML document).
291 * @param {Number} columnIndex
292 * @param {Function} fn
293 */
294 addPostprocessor: function(columnIndex, fn){
295 this.postprocessors[columnIndex] = fn;
296 },
297
298 /**
299 * Gets the postprocessor function for the specified column.
300 * @param {Number} columnIndex
301 * @return {Function}
302 */
303 getPostprocessor: function(columnIndex){
304 return this.postprocessors[columnIndex];
305 },
306
307 /**
308 * Removes a postprocessor function.
309 * @param {Number} columnIndex
310 */
311 removePostprocessor: function(columnIndex){
312 this.postprocessors[columnIndex] = null;
313 },
314 /**
315 * Empty interface method - Called to process the data returned by the XHR - Classes which extend LoadableDataModel should implement this method.
316 * See {@link YAHOO.ext.XMLDataModel} for an example implementation.
317 */
318 loadData: function(data, callback, keepExisting, insertIndex){
319
320 }
321});
322
323YAHOO.ext.grid.LoadableDataModel.XML = 'xml';
324YAHOO.ext.grid.LoadableDataModel.JSON = 'json';
325YAHOO.ext.grid.LoadableDataModel.TEXT = 'text';
326
327
328
329
330
diff --git a/frontend/beta/js/YUI-extensions/data/Tree.js b/frontend/beta/js/YUI-extensions/data/Tree.js
new file mode 100644
index 0000000..afa5b20
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/data/Tree.js
@@ -0,0 +1,412 @@
1YAHOO.namespace('ext.data');
2
3/**
4 * @class YAHOO.ext.data.Tree
5 * @extends YAHOO.ext.util.Observable
6 * The class represents a tree data structure and bubbles all the events for it's nodes. The nodes
7 * in the tree have most standard DOM functionality.
8 * @constructor
9 * @param {Node} root (optional) The root node
10 */
11YAHOO.ext.data.Tree = function(root){
12 this.nodeHash = {};
13 this.root = null;
14 if(root){
15 this.setRootNode(root);
16 }
17 this.events = {
18 'append' : true,
19 'remove' : true,
20 'move' : true,
21 'insert' : true,
22 'beforeappend' : true,
23 'beforeremove' : true,
24 'beforemove' : true,
25 'beforeinsert' : true
26 };
27};
28
29YAHOO.extendX(YAHOO.ext.data.Tree, YAHOO.ext.util.Observable, {
30 pathSeparator: '/',
31
32 getRootNode : function(){
33 return this.root;
34 },
35
36 setRootNode : function(node){
37 this.root = node;
38 node.ownerTree = this;
39 node.isRoot = true;
40 return node;
41 },
42
43 getNodeById : function(id){
44 return this.nodeHash[id];
45 },
46
47 registerNode : function(node){
48 this.nodeHash[node.id] = node;
49 },
50
51 unregisterNode : function(node){
52 delete this.nodeHash[node.id];
53 },
54
55 toString : function(){
56 return '[Tree'+(this.id?' '+this.id:'')+']';
57 }
58});
59
60/**
61 * @class YAHOO.ext.tree.Node
62 * @extends YAHOO.ext.util.Observable
63 * @cfg {String} text The text for this node
64 * @cfg {String} id The id for this node
65 * @constructor
66 * @param {Object} attributes The attributes/config for the node
67 */
68YAHOO.ext.data.Node = function(attributes){
69 this.attributes = attributes || {};
70 this.leaf = this.attributes.leaf;
71 this.id = this.attributes.id;
72 if(!this.id){
73 this.id = YAHOO.util.Dom.generateId(null, 'ynode-');
74 this.attributes.id = this.id;
75 }
76
77 this.childNodes = [];
78 if(!this.childNodes.indexOf){ // indexOf is a must
79 this.childNodes.indexOf = function(o){
80 for(var i = 0, len = this.length; i < len; i++){
81 if(this[i] == o) return i;
82 }
83 return -1;
84 };
85 }
86 this.parentNode = null;
87 this.firstChild = null;
88 this.lastChild = null;
89 this.previousSibling = null;
90 this.nextSibling = null;
91
92 this.events = {
93 'append' : true,
94 'remove' : true,
95 'move' : true,
96 'insert' : true,
97 'beforeappend' : true,
98 'beforeremove' : true,
99 'beforemove' : true,
100 'beforeinsert' : true
101 };
102};
103
104YAHOO.extendX(YAHOO.ext.data.Node, YAHOO.ext.util.Observable, {
105 fireEvent : function(evtName){
106 // first do standard event for this node
107 if(YAHOO.ext.data.Node.superclass.fireEvent.apply(this, arguments) === false){
108 return false;
109 }
110 // then bubble it up to the tree if the event wasn't cancelled
111 if(this.ownerTree){
112 if(this.ownerTree.fireEvent.apply(this.ownerTree, arguments) === false){
113 return false;
114 }
115 }
116 return true;
117 },
118
119 isLeaf : function(){
120 return this.leaf === true;
121 },
122
123 setFirstChild : function(node){
124 this.firstChild = node;
125 },
126
127 setLastChild : function(node){
128 this.lastChild = node;
129 },
130
131 isLast : function(){
132 return (!this.parentNode ? true : this.parentNode.lastChild == this);
133 },
134
135 isFirst : function(){
136 return (!this.parentNode ? true : this.parentNode.firstChild == this);
137 },
138
139 hasChildNodes : function(){
140 return !this.isLeaf() && this.childNodes.length > 0;
141 },
142
143 appendChild : function(node){
144 var multi = false;
145 if(node instanceof Array){
146 multi = node;
147 }else if(arguments.length > 1){
148 multi = arguments;
149 }
150 // if passed an array or multiple args do them one by one
151 if(multi){
152 for(var i = 0, len = multi.length; i < len; i++) {
153 this.appendChild(multi[i]);
154 }
155 }else{
156 if(this.fireEvent('beforeappend', this.ownerTree, this, node) === false){
157 return false;
158 }
159 var index = this.childNodes.length;
160 var oldParent = node.parentNode;
161 // it's a move, make sure we move it cleanly
162 if(oldParent){
163 if(node.fireEvent('beforemove', node.getOwnerTree(), node, oldParent, this, index) === false){
164 return false;
165 }
166 oldParent.removeChild(node);
167 }
168 var index = this.childNodes.length;
169 if(index == 0){
170 this.setFirstChild(node);
171 }
172 this.childNodes.push(node);
173 node.parentNode = this;
174 var ps = this.childNodes[index-1];
175 if(ps){
176 node.previousSibling = ps;
177 ps.nextSibling = node;
178 }
179 this.setLastChild(node);
180 node.setOwnerTree(this.getOwnerTree());
181 this.fireEvent('append', this.ownerTree, this, node, index);
182 if(oldParent){
183 node.fireEvent('move', this.ownerTree, node, oldParent, this, index);
184 }
185 return node;
186 }
187 },
188
189 removeChild : function(node){
190 var index = this.childNodes.indexOf(node);
191 if(index == -1){
192 return false;
193 }
194 if(this.fireEvent('beforeremove', this.ownerTree, this, node) === false){
195 return false;
196 }
197
198 // remove it from childNodes collection
199 this.childNodes.splice(index, 1);
200
201 // update siblings
202 if(node.previousSibling){
203 node.previousSibling.nextSibling = node.nextSibling;
204 }
205 if(node.nextSibling){
206 node.nextSibling.previousSibling = node.previousSibling;
207 }
208
209 // update child refs
210 if(this.firstChild == node){
211 this.setFirstChild(node.nextSibling);
212 }
213 if(this.lastChild == node){
214 this.setLastChild(node.previousSibling);
215 }
216
217 node.setOwnerTree(null);
218 // clear any references from the node
219 node.parentNode = null;
220 node.previousSibling = null;
221 node.nextSibling = null;
222 this.fireEvent('remove', this.ownerTree, this, node);
223 return node;
224 },
225
226 insertBefore : function(node, refNode){
227 if(!refNode){ // like standard Dom, refNode can be null for append
228 return this.appendChild(node);
229 }
230 // nothing to do
231 if(node == refNode){
232 return false;
233 }
234
235 if(this.fireEvent('beforeinsert', this.ownerTree, this, node, refNode) === false){
236 return false;
237 }
238 var index = this.childNodes.indexOf(refNode);
239 var oldParent = node.parentNode;
240 var refIndex = index;
241
242 // when moving internally, indexes will change after remove
243 if(oldParent == this && this.childNodes.indexOf(node) < index){
244 refIndex--;
245 }
246
247 // it's a move, make sure we move it cleanly
248 if(oldParent){
249 if(node.fireEvent('beforemove', node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
250 return false;
251 }
252 oldParent.removeChild(node);
253 }
254 if(refIndex == 0){
255 this.setFirstChild(node);
256 }
257 this.childNodes.splice(refIndex, 0, node);
258 node.parentNode = this;
259 var ps = this.childNodes[refIndex-1];
260 if(ps){
261 node.previousSibling = ps;
262 ps.nextSibling = node;
263 }
264 node.nextSibling = refNode;
265 node.setOwnerTree(this.getOwnerTree());
266 this.fireEvent('insert', this.ownerTree, this, node, refNode);
267 if(oldParent){
268 node.fireEvent('move', this.ownerTree, node, oldParent, this, refIndex, refNode);
269 }
270 return node;
271 },
272
273 item : function(index){
274 return this.childNodes[index];
275 },
276
277 replaceChild : function(newChild, oldChild){
278 this.insertBefore(newChild, oldChild);
279 this.removeChild(oldChild);
280 return oldChild;
281 },
282
283 indexOf : function(child){
284 return this.childNodes.indexOf(child);
285 },
286
287 getOwnerTree : function(){
288 // if it doesn't have one, look for one
289 if(!this.ownerTree){
290 var p = this;
291 while(p){
292 if(p.ownerTree){
293 this.ownerTree = p.ownerTree;
294 break;
295 }
296 p = p.parentNode;
297 }
298 }
299 return this.ownerTree;
300 },
301
302 setOwnerTree : function(tree){
303 // if it's move, we need to update everyone
304 if(tree != this.ownerTree){
305 if(this.ownerTree){
306 this.ownerTree.unregisterNode(this);
307 }
308 this.ownerTree = tree;
309 var cs = this.childNodes;
310 for(var i = 0, len = cs.length; i < len; i++) {
311 cs[i].setOwnerTree(tree);
312 }
313 if(tree){
314 tree.registerNode(this);
315 }
316 }
317 },
318
319 getPath : function(attr){
320 attr = attr || 'id';
321 var p = this.parentNode;
322 var b = [this.attributes[attr]];
323 while(p){
324 b.unshift(p.attributes[attr]);
325 p = p.parentNode;
326 }
327 var sep = this.getOwnerTree().pathSeparator;
328 return sep + b.join(sep);
329 },
330
331 bubble : function(fn, scope, args){
332 var p = this;
333 while(p){
334 if(fn.call(scope || p, args || p) === false){
335 break;
336 }
337 p = p.parentNode;
338 }
339 },
340
341 cascade : function(fn, scope, args){
342 if(fn.call(scope || this, args || this) !== false){
343 var cs = this.childNodes;
344 for(var i = 0, len = cs.length; i < len; i++) {
345 cs[i].cascade(fn, scope, args);
346 }
347 }
348 },
349
350 eachChild : function(fn, scope, args){
351 var cs = this.childNodes;
352 for(var i = 0, len = cs.length; i < len; i++) {
353 if(fn.call(scope || this, args || cs[i]) === false){
354 break;
355 }
356 }
357 },
358
359 findChild : function(attribute, value){
360 var cs = this.childNodes;
361 for(var i = 0, len = cs.length; i < len; i++) {
362 if(cs[i].attributes[attribute] == value){
363 return cs[i];
364 }
365 }
366 return null;
367 },
368
369 /**
370 * Sorts this nodes children using the supplied sort function
371 * @param {Function} fn
372 * @param {Object} scope
373 */
374 sort : function(fn, scope){
375 var cs = this.childNodes;
376 var len = cs.length;
377 if(len > 0){
378 var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
379 cs.sort(sortFn);
380 for(var i = 0; i < len; i++){
381 var n = cs[i];
382 n.previousSibling = cs[i-1];
383 n.nextSibling = cs[i+1];
384 if(i == 0){
385 this.setFirstChild(n);
386 }
387 if(i == len-1){
388 this.setLastChild(n);
389 }
390 }
391 }
392 },
393
394 contains : function(node){
395 return node.isAncestor(this);
396 },
397
398 isAncestor : function(node){
399 var p = this.parentNode;
400 while(p){
401 if(p == node){
402 return true;
403 }
404 p = p.parentNode;
405 }
406 return false;
407 },
408
409 toString : function(){
410 return '[Node'+(this.id?' '+this.id:'')+']';
411 }
412});
diff --git a/frontend/beta/js/YUI-extensions/data/XMLDataModel.js b/frontend/beta/js/YUI-extensions/data/XMLDataModel.js
new file mode 100644
index 0000000..e312a9e
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/data/XMLDataModel.js
@@ -0,0 +1,274 @@
1/**
2 * @class YAHOO.ext.grid.XMLDataModel
3 * This is an implementation of a DataModel used by the Grid. It works
4 * with XML data.
5 * <br>Example schema from Amazon search:
6 * <pre><code>
7 * var schema = {
8 * tagName: 'Item',
9 * id: 'ASIN',
10 * fields: ['Author', 'Title', 'Manufacturer', 'ProductGroup']
11 * };
12 * </code></pre>
13 * @extends YAHOO.ext.grid.LoadableDataModel
14 * @constructor
15 * @param {Object} schema The schema to use
16 * @param {XMLDocument} xml An XML document to load immediately
17*/
18YAHOO.ext.grid.XMLDataModel = function(schema, xml){
19 YAHOO.ext.grid.XMLDataModel.superclass.constructor.call(this, YAHOO.ext.grid.LoadableDataModel.XML);
20 /**@private*/
21 this.schema = schema;
22 this.xml = xml;
23 if(xml){
24 this.loadData(xml);
25 }
26 this.idSeed = 0;
27};
28YAHOO.extendX(YAHOO.ext.grid.XMLDataModel, YAHOO.ext.grid.LoadableDataModel, {
29
30 getDocument: function(){
31 return this.xml;
32 },
33
34 /**
35 * Overrides loadData in LoadableDataModel to process XML
36 * @param {XMLDocument} doc The document to load
37 * @param {<i>Function</i>} callback (optional) callback to call when loading is complete
38 * @param {<i>Boolean</i>} keepExisting (optional) true to keep existing data
39 * @param {<i>Number</i>} insertIndex (optional) if present, loaded data is inserted at the specified index instead of overwriting existing data
40 */
41 loadData: function(doc, callback, keepExisting, insertIndex){
42 this.xml = doc;
43 var idField = this.schema.id;
44 var fields = this.schema.fields;
45 if(this.schema.totalTag){
46 this.totalCount = null;
47 var totalNode = doc.getElementsByTagName(this.schema.totalTag);
48 if(totalNode && totalNode.item(0) && totalNode.item(0).firstChild) {
49 var v = parseInt(totalNode.item(0).firstChild.nodeValue, 10);
50 if(!isNaN(v)){
51 this.totalCount = v;
52 }
53 }
54 }
55 var rowData = [];
56 var nodes = doc.getElementsByTagName(this.schema.tagName);
57 if(nodes && nodes.length > 0) {
58 for(var i = 0; i < nodes.length; i++) {
59 var node = nodes.item(i);
60 var colData = [];
61 colData.node = node;
62 colData.id = this.getNamedValue(node, idField, String(++this.idSeed));
63 for(var j = 0; j < fields.length; j++) {
64 var val = this.getNamedValue(node, fields[j], "");
65 if(this.preprocessors[j]){
66 val = this.preprocessors[j](val);
67 }
68 colData.push(val);
69 }
70 rowData.push(colData);
71 }
72 }
73 if(keepExisting !== true){
74 YAHOO.ext.grid.XMLDataModel.superclass.removeAll.call(this);
75 }
76 if(typeof insertIndex != 'number'){
77 insertIndex = this.getRowCount();
78 }
79 YAHOO.ext.grid.XMLDataModel.superclass.insertRows.call(this, insertIndex, rowData);
80 if(typeof callback == 'function'){
81 callback(this, true);
82 }
83 this.fireLoadEvent();
84 },
85
86 /**
87 * Adds a row to this DataModel and syncs the XML document
88 * @param {String} id The id of the row, if null the next row index is used
89 * @param {Array} cellValues The cell values for this row
90 * @return {Number} The index of the new row (if the model is sorted this index may not be accurate)
91 */
92 addRow: function(id, cellValues){
93 var node = this.createNode(this.xml, id, cellValues);
94 cellValues.id = id || ++this.idSeed;
95 cellValues.node = node;
96 return YAHOO.ext.grid.XMLDataModel.superclass.addRow.call(this, cellValues);
97 },
98
99 /**
100 * Inserts a row into this DataModel and syncs the XML document
101 * @param {Number} index The index to insert the row
102 * @param {String} id The id of the row, if null the next row index is used
103 * @param {Array} cellValues The cell values for this row
104 * @return {Number} The index of the new row (if the model is sorted this index may not be accurate)
105 */
106 insertRow: function(index, id, cellValues){
107 var node = this.createNode(this.xml, id, cellValues);
108 cellValues.id = id || ++this.idSeed;
109 cellValues.node = node;
110 return YAHOO.ext.grid.XMLDataModel.superclass.insertRow.call(this, index, cellValues);
111 },
112
113 /**
114 * Removes the row from DataModel and syncs the XML document
115 * @param {Number} index The index of the row to remove
116 */
117 removeRow: function(index){
118 var node = this.data[index].node;
119 node.parentNode.removeChild(node);
120 YAHOO.ext.grid.XMLDataModel.superclass.removeRow.call(this, index, index);
121 },
122
123 getNode: function(rowIndex){
124 return this.data[rowIndex].node;
125 },
126
127 /**
128 * Override this method to define your own node creation routine for when new rows are added.
129 * By default this method clones the first node and sets the column values in the newly cloned node.
130 * In many instances this will not work and you will have to create the node manually.
131 * @param {XMLDocument} xmlDoc The xml document being used by this model
132 * @param {String/Number} id The row id
133 * @param {Array} colData The column data for the new node
134 * @return {XMLNode} The created node
135 */
136 createNode: function(xmlDoc, id, colData){
137 var template = this.data[0].node;
138 var newNode = template.cloneNode(true);
139 var fields = this.schema.fields;
140 for(var i = 0, len = fields.length; i < len; i++){
141 var nodeValue = colData[i];
142 if(this.postprocessors[i]){
143 nodeValue = this.postprocessors[i](nodeValue);
144 }
145 this.setNamedValue(newNode, fields[i], nodeValue);
146 }
147 if(id){
148 this.setNamedValue(newNode, this.schema.idField, id);
149 }
150 template.parentNode.appendChild(newNode);
151 return newNode;
152 },
153
154 /**
155 * @private
156 * Convenience function looks for value in attributes, then in children tags - also
157 * normalizes namespace matches (ie matches ns:tag, FireFox matches tag and not ns:tag).
158 */
159 getNamedValue: function(node, name, defaultValue){
160 if(!node || !name){
161 return defaultValue;
162 }
163 var nodeValue = defaultValue;
164 var attrNode = node.attributes.getNamedItem(name);
165 if(attrNode) {
166 nodeValue = attrNode.value;
167 } else {
168 var childNode = node.getElementsByTagName(name);
169 if(childNode && childNode.item(0) && childNode.item(0).firstChild) {
170 nodeValue = childNode.item(0).firstChild.nodeValue;
171 }else{
172 // try to strip namespace for FireFox
173 var index = name.indexOf(':');
174 if(index > 0){
175 return this.getNamedValue(node, name.substr(index+1), defaultValue);
176 }
177 }
178 }
179 return nodeValue;
180 },
181
182 /**
183 * @private
184 * Convenience function set a value in the underlying xml node.
185 */
186 setNamedValue: function(node, name, value){
187 if(!node || !name){
188 return;
189 }
190 var attrNode = node.attributes.getNamedItem(name);
191 if(attrNode) {
192 attrNode.value = value;
193 return;
194 }
195 var childNode = node.getElementsByTagName(name);
196 if(childNode && childNode.item(0) && childNode.item(0).firstChild) {
197 childNode.item(0).firstChild.nodeValue = value;
198 }else{
199 // try to strip namespace for FireFox
200 var index = name.indexOf(':');
201 if(index > 0){
202 this.setNamedValue(node, name.substr(index+1), value);
203 }
204 }
205 },
206
207 /**
208 * Overrides DefaultDataModel.setValueAt to update the underlying XML Document
209 * @param {Object} value The new value
210 * @param {Number} rowIndex
211 * @param {Number} colIndex
212 */
213 setValueAt: function(value, rowIndex, colIndex){
214 var node = this.data[rowIndex].node;
215 if(node){
216 var nodeValue = value;
217 if(this.postprocessors[colIndex]){
218 nodeValue = this.postprocessors[colIndex](value);
219 }
220 this.setNamedValue(node, this.schema.fields[colIndex], nodeValue);
221 }
222 YAHOO.ext.grid.XMLDataModel.superclass.setValueAt.call(this, value, rowIndex, colIndex);
223 },
224
225 /**
226 * Overrides getRowId in DefaultDataModel to return the ID value of the specified node.
227 * @param {Number} rowIndex
228 * @return {Number}
229 */
230 getRowId: function(rowIndex){
231 return this.data[rowIndex].id;
232 },
233
234 addRows : function(rowData){
235 for(var j = 0, len = rowData.length; j < len; j++){
236 var cellValues = rowData[j];
237 var id = ++this.idSeed;
238 var node = this.createNode(this.xml, id, cellValues);
239 cellValues.node=node;
240 cellValues.id = cellValues.id || id;
241 YAHOO.ext.grid.XMLDataModel.superclass.addRow.call(this,cellValues);
242 }
243 },
244
245 insertRows : function(index, rowData){
246 // copy original array so it is not reversed
247 rowData = rowData.slice(0).reverse();
248 for(var j = 0, len = rowData.length; j < len; j++){
249 var cellValues = rowData[j];
250 var id = ++this.idSeed;
251 var node = this.createNode(this.xml, id, cellValues);
252 cellValues.id = cellValues.id || id;
253 cellValues.node = node;
254 YAHOO.ext.grid.XMLDataModel.superclass.insertRow.call(this, index, cellValues);
255 }
256 }
257});
258
259YAHOO.ext.grid.XMLQueryDataModel = function(){
260 YAHOO.ext.grid.XMLQueryDataModel.superclass.constructor.apply(this, arguments);
261};
262YAHOO.extendX(YAHOO.ext.grid.XMLQueryDataModel, YAHOO.ext.grid.XMLDataModel, {
263 getNamedValue: function(node, name, defaultValue){
264 if(!node || !name){
265 return defaultValue;
266 }
267 var nodeValue = defaultValue;
268 var childNode = cssQuery(name, node);
269 if(childNode && childNode[0]) {
270 nodeValue = childNode[0].firstChild.nodeValue;
271 }
272 return nodeValue;
273 }
274});
diff --git a/frontend/beta/js/YUI-extensions/dd/DragSource.js b/frontend/beta/js/YUI-extensions/dd/DragSource.js
new file mode 100644
index 0000000..efee2d6
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/dd/DragSource.js
@@ -0,0 +1,218 @@
1// kill drag drop dependency
2if(YAHOO.util.DragDrop){
3
4YAHOO.ext.dd.DragSource = function(el, config){
5 this.el = getEl(el);
6 this.dragData = {};
7
8 YAHOO.ext.util.Config.apply(this, config);
9
10 if(!this.proxy){
11 this.proxy = new YAHOO.ext.dd.StatusProxy();
12 }
13 this.el.on('mouseup', this.handleMouseUp);
14 YAHOO.ext.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
15 {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
16
17 this.dragging = false;
18};
19
20YAHOO.extendX(YAHOO.ext.dd.DragSource, YAHOO.util.DDProxy, {
21 dropAllowed : 'ydd-drop-ok',
22 dropNotAllowed : 'ydd-drop-nodrop',
23
24 getDragData : function(e){
25 return this.dragData;
26 },
27
28 onDragEnter : function(e, id){
29 var target = YAHOO.util.DragDropMgr.getDDById(id);
30 this.cachedTarget = target;
31 if(this.beforeDragEnter(target, e, id) !== false){
32 if(target.isNotifyTarget){
33 var status = target.notifyEnter(this, e, this.dragData);
34 this.proxy.setStatus(status);
35 }else{
36 this.proxy.setStatus(this.dropAllowed);
37 }
38
39 if(this.afterDragEnter){
40 this.afterDragEnter(target, e, id);
41 }
42 }
43 },
44
45 beforeDragEnter : function(target, e, id){
46 return true;
47 },
48
49 alignElWithMouse: function() {
50 YAHOO.ext.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
51 this.proxy.sync();
52 },
53
54 onDragOver : function(e, id){
55 var target = this.cachedTarget || YAHOO.util.DragDropMgr.getDDById(id);
56 if(this.beforeDragOver(target, e, id) !== false){
57 if(target.isNotifyTarget){
58 var status = target.notifyOver(this, e, this.dragData);
59 this.proxy.setStatus(status);
60 }
61
62 if(this.afterDragOver){
63 this.afterDragOver(target, e, id);
64 }
65 }
66 },
67
68 beforeDragOver : function(target, e, id){
69 return true;
70 },
71
72 onDragOut : function(e, id){
73 var target = this.cachedTarget || YAHOO.util.DragDropMgr.getDDById(id);
74 if(this.beforeDragOut(target, e, id) !== false){
75 if(target.isNotifyTarget){
76 target.notifyOut(this, e, this.dragData);
77 }
78 this.proxy.reset();
79 if(this.afterDragOut){
80 this.afterDragOut(target, e, id);
81 }
82 }
83 this.cachedTarget = null;
84 },
85
86 beforeDragOut : function(target, e, id){
87 return true;
88 },
89
90
91 onDragDrop : function(e, id){
92 var target = this.cachedTarget || YAHOO.util.DragDropMgr.getDDById(id);
93 if(this.beforeDragDrop(target, e, id) !== false){
94 if(target.isNotifyTarget){
95 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
96 this.onValidDrop(target, e, id);
97 }else{
98 this.onInvalidDrop(target, e, id);
99 }
100 }else{
101 this.onValidDrop(target, e, id);
102 }
103
104 if(this.afterDragDrop){
105 this.afterDragDrop(target, e, id);
106 }
107 }
108 },
109
110 beforeDragDrop : function(target, e, id){
111 return true;
112 },
113
114 onValidDrop : function(target, e, id){
115 this.hideProxy();
116 },
117
118 getRepairXY : function(e, data){
119 return this.el.getXY();
120 },
121
122 onInvalidDrop : function(target, e, id){
123 this.beforeInvalidDrop(target, e, id);
124 if(this.cachedTarget){
125 if(this.cachedTarget.isNotifyTarget){
126 this.cachedTarget.notifyOut(this, e, this.dragData);
127 }
128 this.cacheTarget = null;
129 }
130 this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
131 if(this.afterInvalidDrop){
132 this.afterInvalidDrop(e, id);
133 }
134 },
135
136 afterRepair : function(){
137 this.el.highlight(this.hlColor || 'c3daf9');
138 this.dragging = false;
139 },
140
141 beforeInvalidDrop : function(target, e, id){
142 return true;
143 },
144
145 handleMouseDown : function(e){
146 if(this.dragging) {
147 return;
148 }
149 if(YAHOO.ext.QuickTips){
150 YAHOO.ext.QuickTips.disable();
151 }
152 var data = this.getDragData(e);
153 if(data && this.onBeforeDrag(data, e) !== false){
154 this.dragData = data;
155 this.proxy.stop();
156 YAHOO.ext.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
157 }
158 },
159
160 handleMouseUp : function(e){
161 if(YAHOO.ext.QuickTips){
162 YAHOO.ext.QuickTips.enable();
163 }
164 },
165
166 onBeforeDrag : function(data, e){
167 return true;
168 },
169
170 startDrag : function(e){
171 this.proxy.reset();
172 this.dragging = true;
173 this.proxy.update('');
174 this.onInitDrag(e);
175 this.proxy.show();
176 },
177
178 onInitDrag : function(e){
179 var clone = this.el.dom.cloneNode(true);
180 clone.id = YAHOO.util.Dom.generateId(); // prevent duplicate ids
181 this.proxy.update(clone);
182 return true;
183 },
184
185
186 getProxy : function(){
187 return this.proxy;
188 },
189
190 hideProxy : function(){
191 this.proxy.hide();
192 this.proxy.reset(true);
193 this.dragging = false;
194 },
195
196 triggerCacheRefresh : function(){
197 YAHOO.util.DDM.refreshCache(this.groups);
198 },
199
200 // override to prevent hiding
201 b4EndDrag: function(e) {
202 },
203
204 // override to prevent moving
205 endDrag : function(e){
206 this.onEndDrag(this.dragData, e);
207 },
208
209 onEndDrag : function(data, e){
210
211 },
212
213 // pin to cursor
214 autoOffset : function(x, y) {
215 this.setDelta(-12, -20);
216 }
217});
218}
diff --git a/frontend/beta/js/YUI-extensions/dd/DragZone.js b/frontend/beta/js/YUI-extensions/dd/DragZone.js
new file mode 100644
index 0000000..7a6edb6
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/dd/DragZone.js
@@ -0,0 +1,64 @@
1// kill drag drop dependency
2if(YAHOO.util.DragDrop){
3/**
4 * @class YAHOO.ext.dd.DragZone
5 * @extends YAHOO.ext.dd.Source
6 * This class provides a container DD instance that proxies for multiple child node sources.<br />
7 * By default, this class requires that draggable child nodes are registered with
8 * {@link YAHOO.ext.dd.Registry}.
9 * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
10 * for auto scrolling during drag operations.
11 * @constructor
12 * @param {String/HTMLElement/Element} el The container element
13 * @param {Object} config
14 */
15YAHOO.ext.dd.DragZone = function(el, config){
16 YAHOO.ext.dd.DragZone.superclass.constructor.call(this, el, config);
17 if(this.containerScroll){
18 YAHOO.ext.dd.ScrollManager.register(this.el);
19 }
20};
21
22YAHOO.extendX(YAHOO.ext.dd.DragZone, YAHOO.ext.dd.DragSource, {
23 /**
24 * Called when a mousedown occurs in this container. Looks in {@link YAHOO.ext.dd.Registry}
25 * for a valid target to drag based on the mouse down. Override this method
26 * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
27 * object has a "ddel" attribute (with an HTML Element) for other functions to work.
28 * @param {EventObject} e The mouse down event
29 * @return {Object} The dragData
30 */
31 getDragData : function(e){
32 return YAHOO.ext.dd.Registry.getHandleFromEvent(e);
33 },
34
35 /**
36 * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
37 * this.dragData.ddel
38 * @param {EventObject} e The current event
39 * @return {Boolean} true to continue the drag, false to cancel
40 */
41 onInitDrag : function(e){
42 this.proxy.update(this.dragData.ddel.cloneNode(true));
43 return true;
44 },
45
46 /**
47 * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel
48 */
49 afterRepair : function(){
50 YAHOO.ext.Element.fly(this.dragData.ddel).highlight(this.hlColor || 'c3daf9');
51 this.dragging = false;
52 },
53
54 /**
55 * Called before a repair of an invalid drop to get the XY to animate to. By default returns
56 * the XY of this.dragData.ddel
57 * @param {EventObject} e The mouse up event
58 * @return {Array} The xy location (e.g. [100, 200])
59 */
60 getRepairXY : function(e){
61 return YAHOO.ext.Element.fly(this.dragData.ddel).getXY();
62 }
63});
64}
diff --git a/frontend/beta/js/YUI-extensions/dd/DropTarget.js b/frontend/beta/js/YUI-extensions/dd/DropTarget.js
new file mode 100644
index 0000000..30e59cd
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/dd/DropTarget.js
@@ -0,0 +1,45 @@
1// kill drag drop dependency
2if(YAHOO.util.DragDrop){
3
4YAHOO.ext.dd.DropTarget = function(el, config){
5 this.el = getEl(el);
6
7 YAHOO.ext.util.Config.apply(this, config);
8
9 if(this.containerScroll){
10 YAHOO.ext.dd.ScrollManager.register(this.el);
11 }
12
13 YAHOO.ext.dd.DropTarget.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
14 {isTarget: true});
15
16};
17
18YAHOO.extendX(YAHOO.ext.dd.DropTarget, YAHOO.util.DDTarget, {
19 isTarget : true,
20 isNotifyTarget : true,
21 dropAllowed : 'ydd-drop-ok',
22 dropNotAllowed : 'ydd-drop-nodrop',
23
24 notifyEnter : function(dd, e, data){
25 if(this.overClass){
26 this.el.addClass(this.overClass);
27 }
28 return this.dropAllowed;
29 },
30
31 notifyOver : function(dd, e, data){
32 return this.dropAllowed;
33 },
34
35 notifyOut : function(dd, e, data){
36 if(this.overClass){
37 this.el.removeClass(this.overClass);
38 }
39 },
40
41 notifyDrop : function(dd, e, data){
42 return false;
43 }
44});
45}
diff --git a/frontend/beta/js/YUI-extensions/dd/DropZone.js b/frontend/beta/js/YUI-extensions/dd/DropZone.js
new file mode 100644
index 0000000..ce446fb
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/dd/DropZone.js
@@ -0,0 +1,81 @@
1// kill drag drop dependency
2if(YAHOO.util.DragDrop){
3YAHOO.ext.dd.DropZone = function(el, config){
4 YAHOO.ext.dd.DropZone.superclass.constructor.call(this, el, config);
5};
6
7YAHOO.extendX(YAHOO.ext.dd.DropZone, YAHOO.ext.dd.DropTarget, {
8 getTargetFromEvent : function(e){
9 return YAHOO.ext.dd.Registry.getTargetFromEvent(e);
10 },
11
12 onNodeEnter : function(n, dd, e, data){
13
14 },
15
16 onNodeOver : function(n, dd, e, data){
17 return this.dropAllowed;
18 },
19
20 onNodeOut : function(n, dd, e, data){
21
22 },
23
24 onNodeDrop : function(n, dd, e, data){
25 return false;
26 },
27
28 onContainerOver : function(n, dd, e, data){
29 return this.dropNotAllowed;
30 },
31
32 onContainerDrop : function(n, dd, e, data){
33 return false;
34 },
35
36 notifyEnter : function(dd, e, data){
37 return this.dropNotAllowed;
38 },
39
40 notifyOver : function(dd, e, data){
41 var n = this.getTargetFromEvent(e);
42 if(!n){ // not over valid drop target
43 if(this.lastOverNode){
44 this.onNodeOut(this.lastOverNode, dd, e, data);
45 this.lastOverNode = null;
46 }
47 return this.onContainerOver(dd, e, data);
48 }
49 if(this.lastOverNode != n){
50 if(this.lastOverNode){
51 this.onNodeOut(this.lastOverNode, dd, e, data);
52 }
53 this.onNodeEnter(n, dd, e, data);
54 this.lastOverNode = n;
55 }
56 return this.onNodeOver(n, dd, e, data);
57 },
58
59 notifyOut : function(dd, e, data){
60 if(this.lastOverNode){
61 this.onNodeOut(this.lastOverNode, dd, e, data);
62 this.lastOverNode = null;
63 }
64 },
65
66 notifyDrop : function(dd, e, data){
67 if(this.lastOverNode){
68 this.onNodeOut(this.lastOverNode, dd, e, data);
69 this.lastOverNode = null;
70 }
71 var n = this.getTargetFromEvent(e);
72 return n ?
73 this.onNodeDrop(n, dd, e, data) :
74 this.onContainerDrop(n, dd, e, data);
75 },
76
77 triggerCacheRefresh : function(){
78 YAHOO.util.DDM.refreshCache(this.groups);
79 }
80});
81}
diff --git a/frontend/beta/js/YUI-extensions/dd/Registry.js b/frontend/beta/js/YUI-extensions/dd/Registry.js
new file mode 100644
index 0000000..983b874
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/dd/Registry.js
@@ -0,0 +1,80 @@
1// kill drag drop dependency
2if(YAHOO.util.DragDrop){
3
4YAHOO.ext.dd.Registry = function(){
5 var elements = {};
6 var handles = {};
7 var autoIdSeed = 0;
8 var doc = document; // local reference for IE
9
10 var getId = function(el, autogen){
11 if(typeof el == 'string'){
12 return el;
13 }
14 var id = el.id;
15 if(!id && autogen !== false){
16 id = 'yddgen-' + (++autoIdSeed);
17 el.id = id;
18 }
19 return id;
20 };
21
22 return {
23 register : function(el, data){
24 data = data || {};
25 if(typeof el == 'string'){
26 el = doc.getElementById(el);
27 }
28 data.ddel = el;
29 elements[getId(el)] = data;
30 if(data.isHandle !== false){
31 handles[data.ddel.id] = data;
32 }
33 if(data.handles){
34 var hs = data.handles;
35 for(var i = 0, len = hs.length; i < len; i++){
36 handles[getId(hs[i])] = data;
37 }
38 }
39 },
40
41 unregister : function(el){
42 var id = getId(el, false);
43 var data = elements[id];
44 if(data){
45 delete elements[id];
46 if(data.handles){
47 var hs = data.handles;
48 for(var i = 0, len = hs.length; i < len; i++){
49 delete handles[getId(hs[i], false)];
50 }
51 }
52 }
53 },
54
55 getHandle : function(id){
56 if(typeof id != 'string'){ // must be element?
57 id = id.id;
58 }
59 return handles[id];
60 },
61
62 getHandleFromEvent : function(e){
63 var t = YAHOO.util.Event.getTarget(e);
64 return t ? handles[t.id] : null;
65 },
66
67 getTarget : function(id){
68 if(typeof id != 'string'){ // must be element?
69 id = id.id;
70 }
71 return elements[id];
72 },
73
74 getTargetFromEvent : function(e){
75 var t = YAHOO.util.Event.getTarget(e);
76 return t ? elements[t.id] || handles[t.id] : null;
77 }
78 };
79}();
80}
diff --git a/frontend/beta/js/YUI-extensions/dd/ScrollManager.js b/frontend/beta/js/YUI-extensions/dd/ScrollManager.js
new file mode 100644
index 0000000..615aadf
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/dd/ScrollManager.js
@@ -0,0 +1,171 @@
1// kill dependency issue
2if(YAHOO.util.DragDrop){
3/**
4 * @class YAHOO.ext.dd.ScrollManager
5 * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
6 * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
7 * @singleton
8 */
9YAHOO.ext.dd.ScrollManager = function(){
10 var ddm = YAHOO.util.DragDropMgr;
11 var els = {};
12 var dragEl = null;
13 var proc = {};
14
15 var onStop = function(e){
16 dragEl = null;
17 clearProc();
18 };
19
20 var triggerRefresh = function(){
21 if(ddm.dragCurrent){
22 ddm.refreshCache(ddm.dragCurrent.groups);
23 }
24 }
25
26 var doScroll = function(){
27 if(ddm.dragCurrent){
28 var dds = YAHOO.ext.dd.ScrollManager;
29 if(!dds.animate || !YAHOO.util.Scroll){
30 if(proc.el.scroll(proc.dir, dds.increment)){
31 triggerRefresh();
32 }
33 }else{
34 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
35 }
36 }
37 };
38
39 var clearProc = function(){
40 if(proc.id){
41 clearInterval(proc.id);
42 }
43 proc.id = 0;
44 proc.el = null;
45 proc.dir = '';
46 };
47
48 var startProc = function(el, dir){
49 clearProc();
50 proc.el = el;
51 proc.dir = dir;
52 proc.id = setInterval(doScroll, YAHOO.ext.dd.ScrollManager.frequency);
53 };
54
55 var onFire = function(e, isDrop){
56 if(isDrop || !ddm.dragCurrent){ return; }
57 var dds = YAHOO.ext.dd.ScrollManager;
58 if(!dragEl || dragEl != ddm.dragCurrent){
59 dragEl = ddm.dragCurrent;
60 // refresh regions on drag start
61 dds.refreshCache();
62 }
63
64 var xy = YAHOO.util.Event.getXY(e);
65 var pt = new YAHOO.util.Point(xy[0], xy[1]);
66 for(var id in els){
67 var el = els[id], r = el._region;
68 if(r.contains(pt) && el.isScrollable()){
69 if(r.bottom - pt.y <= dds.thresh){
70 if(proc.el != el){
71 startProc(el, 'down');
72 }
73 return;
74 }else if(r.right - pt.x <= dds.thresh){
75 if(proc.el != el){
76 startProc(el, 'left');
77 }
78 return;
79 }else if(pt.y - r.top <= dds.thresh){
80 if(proc.el != el){
81 startProc(el, 'up');
82 }
83 return;
84 }else if(pt.x - r.left <= dds.thresh){
85 if(proc.el != el){
86 startProc(el, 'right');
87 }
88 return;
89 }
90 }
91 }
92 clearProc();
93 };
94
95 ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
96 ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
97
98 return {
99 /**
100 * Registers new overflow element(s) to auto scroll
101 * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
102 */
103 register : function(el){
104 if(el instanceof Array){
105 for(var i = 0, len = el.length; i < len; i++) {
106 this.register(el[i]);
107 }
108 }else{
109 el = getEl(el);
110 els[el.id] = el;
111 }
112 },
113
114 /**
115 * Unregisters overflow element(s) so they are no longer scrolled
116 * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
117 */
118 unregister : function(el){
119 if(el instanceof Array){
120 for(var i = 0, len = el.length; i < len; i++) {
121 this.unregister(el[i]);
122 }
123 }else{
124 el = getEl(el);
125 delete els[el.id];
126 }
127 },
128
129 /**
130 * The number of pixels from the edge of a container the pointer needs to be to
131 * trigger scrolling (defaults to 25)
132 * @type Number
133 */
134 thresh : 25,
135
136 /**
137 * The number of pixels to scroll in each scroll increment (defaults to 50)
138 * @type Number
139 */
140 increment : 100,
141
142 /**
143 * The frequency of scrolls in milliseconds (defaults to 500)
144 * @type Number
145 */
146 frequency : 500,
147
148 /**
149 * True to animate the scroll (defaults to true)
150 * @type Boolean
151 */
152 animate: true,
153
154 /**
155 * The animation duration in seconds -
156 * MUST BE less than YAHOO.ext.dd.ScrollManager.frequency! (defaults to .4)
157 * @type Number
158 */
159 animDuration: .4,
160
161 /**
162 * Manually trigger a cache refresh.
163 */
164 refreshCache : function(){
165 for(var id in els){
166 els[id]._region = els[id].getRegion();
167 }
168 }
169 }
170}();
171}
diff --git a/frontend/beta/js/YUI-extensions/dd/StatusProxy.js b/frontend/beta/js/YUI-extensions/dd/StatusProxy.js
new file mode 100644
index 0000000..97de4d9
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/dd/StatusProxy.js
@@ -0,0 +1,110 @@
1YAHOO.ext.dd.StatusProxy = function(config){
2 YAHOO.ext.util.Config.apply(this, config);
3 this.id = this.id || YAHOO.util.Dom.generateId();
4 this.el = new YAHOO.ext.Layer({
5 dh: {
6 id: this.id, tag: 'div', cls: 'ydd-drag-proxy '+this.dropNotAllowed, children: [
7 {tag: 'div', cls: 'ydd-drop-icon'},
8 {tag: 'div', cls: 'ydd-drag-ghost'}
9 ]
10 },
11 shadow: !config || config.shadow !== false
12 });
13 /*this.el = YAHOO.ext.DomHelper.insertBefore(document.body.firstChild, {
14 id: this.id, tag: 'div', cls: 'ydd-drag-proxy '+this.dropNotAllowed, children: [
15 {tag: 'div', cls: 'ydd-drop-icon'},
16 {tag: 'div', cls: 'ydd-drag-ghost'}
17 ]
18 }, true);*/
19 this.ghost = getEl(this.el.dom.childNodes[1]);
20 this.dropStatus = this.dropNotAllowed;
21};
22
23YAHOO.ext.dd.StatusProxy.prototype = {
24 dropAllowed : 'ydd-drop-ok',
25 dropNotAllowed : 'ydd-drop-nodrop',
26 /**
27 * Updates the DD visual element to allow/not allow a drop
28 * @param {String} cssClass The css class for the new drop status indicator image
29 */
30 setStatus : function(cssClass){
31 cssClass = cssClass || this.dropNotAllowed;
32 if(this.dropStatus != cssClass){
33 this.el.replaceClass(this.dropStatus, cssClass);
34 this.dropStatus = cssClass;
35 }
36 },
37
38 reset : function(clearGhost){
39 this.el.dom.className = 'ydd-drag-proxy ' + this.dropNotAllowed;
40 this.dropStatus = this.dropNotAllowed;
41 if(clearGhost){
42 this.ghost.update('');
43 }
44 },
45
46 update : function(html){
47 if(typeof html == 'string'){
48 this.ghost.update(html);
49 }else{
50 this.ghost.update('');
51 html.style.margin = '0';
52 this.ghost.dom.appendChild(html);
53 }
54 },
55
56 getEl : function(){
57 return this.el;
58 },
59
60 getGhost : function(){
61 return this.ghost;
62 },
63
64 hide : function(clear){
65 this.el.hide();
66 if(clear){
67 this.reset(true);
68 }
69 },
70
71 stop : function(){
72 if(this.anim && this.anim.isAnimated()){
73 this.anim.stop();
74 }
75 },
76
77 show : function(){
78 this.el.show();
79 },
80
81 sync : function(){
82 this.el.syncLocalXY();
83 },
84
85 repair : function(xy, callback, scope){
86 this.callback = callback;
87 this.scope = scope;
88 if(xy && this.animRepair !== false && YAHOO.util.Anim){
89 this.el.addClass('ydd-drag-repair');
90 this.el.hideUnders(true);
91 if(!this.anim){
92 this.anim = new YAHOO.util.Motion(this.el.dom, {}, this.repairDuration || .5, YAHOO.util.Easing.easeOut);
93 this.anim.onComplete.subscribe(this.afterRepair, this, true);
94 }
95 this.anim.attributes = {points: {to:xy}};
96 this.anim.animate();
97 }else{
98 this.afterRepair();
99 }
100 },
101
102 afterRepair : function(){
103 this.hide(true);
104 if(typeof this.callback == 'function'){
105 this.callback.call(this.scope || this);
106 }
107 this.callback == null;
108 this.scope == null;
109 }
110};
diff --git a/frontend/beta/js/YUI-extensions/grid/AbstractColumnModel.js b/frontend/beta/js/YUI-extensions/grid/AbstractColumnModel.js
new file mode 100644
index 0000000..1f93590
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/grid/AbstractColumnModel.js
@@ -0,0 +1,131 @@
1/**
2 * @class YAHOO.ext.grid.AbstractColumnModel
3 * @extends YAHOO.ext.util.Observable
4 * This abstract class defines the ColumnModel interface and provides default implementations of the events required by the Grid.
5 * @constructor
6*/
7YAHOO.ext.grid.AbstractColumnModel = function(){
8 // legacy events
9 this.onWidthChange = new YAHOO.util.CustomEvent('widthChanged');
10 this.onHeaderChange = new YAHOO.util.CustomEvent('headerChanged');
11 this.onHiddenChange = new YAHOO.util.CustomEvent('hiddenChanged');
12
13 this.events = {
14 /**
15 * @event widthchange
16 * Fires when the width of a column changes
17 * @param {ColumnModel} this
18 * @param {Number} columnIndex The column index
19 * @param {Number} newWidth The new width
20 */
21 'widthchange': this.onWidthChange,
22 /**
23 * @event headerchange
24 * Fires when the text of a header changes
25 * @param {ColumnModel} this
26 * @param {Number} columnIndex The column index
27 * @param {Number} newText The new header text
28 */
29 'headerchange': this.onHeaderChange,
30 /**
31 * @event hiddenchange
32 * Fires when a column is hidden or "unhidden"
33 * @param {ColumnModel} this
34 * @param {Number} columnIndex The column index
35 * @param {Number} hidden true if hidden, false otherwise
36 */
37 'hiddenchange': this.onHiddenChange
38 };
39};
40
41YAHOO.ext.grid.AbstractColumnModel.prototype = {
42 fireEvent : YAHOO.ext.util.Observable.prototype.fireEvent,
43 on : YAHOO.ext.util.Observable.prototype.on,
44 addListener : YAHOO.ext.util.Observable.prototype.addListener,
45 delayedListener : YAHOO.ext.util.Observable.prototype.delayedListener,
46 removeListener : YAHOO.ext.util.Observable.prototype.removeListener,
47 purgeListeners : YAHOO.ext.util.Observable.prototype.purgeListeners,
48 bufferedListener : YAHOO.ext.util.Observable.prototype.bufferedListener,
49
50 fireWidthChange : function(colIndex, newWidth){
51 this.onWidthChange.fireDirect(this, colIndex, newWidth);
52 },
53
54 fireHeaderChange : function(colIndex, newHeader){
55 this.onHeaderChange.fireDirect(this, colIndex, newHeader);
56 },
57
58 fireHiddenChange : function(colIndex, hidden){
59 this.onHiddenChange.fireDirect(this, colIndex, hidden);
60 },
61
62 /**
63 * Interface method - Returns the number of columns.
64 * @return {Number}
65 */
66 getColumnCount : function(){
67 return 0;
68 },
69
70 /**
71 * Interface method - Returns true if the specified column is sortable.
72 * @param {Number} col The column index
73 * @return {Boolean}
74 */
75 isSortable : function(col){
76 return false;
77 },
78
79 /**
80 * Interface method - Returns true if the specified column is hidden.
81 * @param {Number} col The column index
82 * @return {Boolean}
83 */
84 isHidden : function(col){
85 return false;
86 },
87
88 /**
89 * Interface method - Returns the sorting comparison function defined for the column (defaults to sortTypes.none).
90 * @param {Number} col The column index
91 * @return {Function}
92 */
93 getSortType : function(col){
94 return YAHOO.ext.grid.DefaultColumnModel.sortTypes.none;
95 },
96
97 /**
98 * Interface method - Returns the rendering (formatting) function defined for the column.
99 * @param {Number} col The column index
100 * @return {Function}
101 */
102 getRenderer : function(col){
103 return YAHOO.ext.grid.DefaultColumnModel.defaultRenderer;
104 },
105
106 /**
107 * Interface method - Returns the width for the specified column.
108 * @param {Number} col The column index
109 * @return {Number}
110 */
111 getColumnWidth : function(col){
112 return 0;
113 },
114
115 /**
116 * Interface method - Returns the total width of all columns.
117 * @return {Number}
118 */
119 getTotalWidth : function(){
120 return 0;
121 },
122
123 /**
124 * Interface method - Returns the header for the specified column.
125 * @param {Number} col The column index
126 * @return {String}
127 */
128 getColumnHeader : function(col){
129 return '';
130 }
131};
diff --git a/frontend/beta/js/YUI-extensions/grid/DefaultColumnModel.js b/frontend/beta/js/YUI-extensions/grid/DefaultColumnModel.js
new file mode 100644
index 0000000..fbdba26
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/grid/DefaultColumnModel.js
@@ -0,0 +1,325 @@
1/**
2 * @class YAHOO.ext.grid.DefaultColumnModel
3 * @extends YAHOO.ext.grid.AbstractColumnModel
4 * This is the default implementation of a ColumnModel used by the Grid. It defines
5 * the columns in the grid.
6 * <br>Usage:<br>
7 <pre><code>
8 var sort = YAHOO.ext.grid.DefaultColumnModel.sortTypes;
9 var myColumns = [
10 {header: "Ticker", width: 60, sortable: true, sortType: sort.asUCString},
11 {header: "Company Name", width: 150, sortable: true, sortType: sort.asUCString},
12 {header: "Market Cap.", width: 100, sortable: true, sortType: sort.asFloat},
13 {header: "$ Sales", width: 100, sortable: true, sortType: sort.asFloat, renderer: money},
14 {header: "Employees", width: 100, sortable: true, sortType: sort.asFloat}
15 ];
16 var colModel = new YAHOO.ext.grid.DefaultColumnModel(myColumns);
17 </code></pre>
18 * @constructor
19 * @param {Object} config The config object
20*/
21YAHOO.ext.grid.DefaultColumnModel = function(config){
22 YAHOO.ext.grid.DefaultColumnModel.superclass.constructor.call(this);
23 /**
24 * The config passed into the constructor
25 */
26 this.config = config;
27
28 /**
29 * The width of columns which have no width specified (defaults to 100)
30 * @type Number
31 */
32 this.defaultWidth = 100;
33 /**
34 * Default sortable of columns which have no sortable specified (defaults to false)
35 * @type Boolean
36 */
37 this.defaultSortable = false;
38};
39YAHOO.extendX(YAHOO.ext.grid.DefaultColumnModel, YAHOO.ext.grid.AbstractColumnModel, {
40
41 /**
42 * Returns the number of columns.
43 * @return {Number}
44 */
45 getColumnCount : function(){
46 return this.config.length;
47 },
48
49 /**
50 * Returns true if the specified column is sortable.
51 * @param {Number} col The column index
52 * @return {Boolean}
53 */
54 isSortable : function(col){
55 if(typeof this.config[col].sortable == 'undefined'){
56 return this.defaultSortable;
57 }
58 return this.config[col].sortable;
59 },
60
61 /**
62 * Returns the sorting comparison function defined for the column (defaults to sortTypes.none).
63 * @param {Number} col The column index
64 * @return {Function}
65 */
66 getSortType : function(col){
67 if(!this.dataMap){
68 // build a lookup so we don't search every time
69 var map = [];
70 for(var i = 0, len = this.config.length; i < len; i++){
71 map[this.getDataIndex(i)] = i;
72 }
73 this.dataMap = map;
74 }
75 col = this.dataMap[col];
76 if(!this.config[col].sortType){
77 return YAHOO.ext.grid.DefaultColumnModel.sortTypes.none;
78 }
79 return this.config[col].sortType;
80 },
81
82 /**
83 * Sets the sorting comparison function for a column.
84 * @param {Number} col The column index
85 * @param {Function} fn
86 */
87 setSortType : function(col, fn){
88 this.config[col].sortType = fn;
89 },
90
91
92 /**
93 * Returns the rendering (formatting) function defined for the column.
94 * @param {Number} col The column index
95 * @return {Function}
96 */
97 getRenderer : function(col){
98 if(!this.config[col].renderer){
99 return YAHOO.ext.grid.DefaultColumnModel.defaultRenderer;
100 }
101 return this.config[col].renderer;
102 },
103
104 /**
105 * Sets the rendering (formatting) function for a column.
106 * @param {Number} col The column index
107 * @param {Function} fn
108 */
109 setRenderer : function(col, fn){
110 this.config[col].renderer = fn;
111 },
112
113 /**
114 * Returns the width for the specified column.
115 * @param {Number} col The column index
116 * @return {Number}
117 */
118 getColumnWidth : function(col){
119 return this.config[col].width || this.defaultWidth;
120 },
121
122 /**
123 * Sets the width for a column.
124 * @param {Number} col The column index
125 * @param {Number} width The new width
126 */
127 setColumnWidth : function(col, width, suppressEvent){
128 this.config[col].width = width;
129 this.totalWidth = null;
130 if(!suppressEvent){
131 this.onWidthChange.fireDirect(this, col, width);
132 }
133 },
134
135 /**
136 * Returns the total width of all columns.
137 * @param {Boolean} includeHidden True to include hidden column widths
138 * @return {Number}
139 */
140 getTotalWidth : function(includeHidden){
141 if(!this.totalWidth){
142 this.totalWidth = 0;
143 for(var i = 0; i < this.config.length; i++){
144 if(includeHidden || !this.isHidden(i)){
145 this.totalWidth += this.getColumnWidth(i);
146 }
147 }
148 }
149 return this.totalWidth;
150 },
151
152 /**
153 * Returns the header for the specified column.
154 * @param {Number} col The column index
155 * @return {String}
156 */
157 getColumnHeader : function(col){
158 return this.config[col].header;
159 },
160
161 /**
162 * Sets the header for a column.
163 * @param {Number} col The column index
164 * @param {String} header The new header
165 */
166 setColumnHeader : function(col, header){
167 this.config[col].header = header;
168 this.onHeaderChange.fireDirect(this, col, header);
169 },
170
171 /**
172 * Returns the tooltip for the specified column.
173 * @param {Number} col The column index
174 * @return {String}
175 */
176 getColumnTooltip : function(col){
177 return this.config[col].tooltip;
178 },
179 /**
180 * Sets the tooltip for a column.
181 * @param {Number} col The column index
182 * @param {String} tooltip The new tooltip
183 */
184 setColumnTooltip : function(col, header){
185 this.config[col].tooltip = tooltip;
186 },
187
188 /**
189 * Returns the dataIndex for the specified column.
190 * @param {Number} col The column index
191 * @return {Number}
192 */
193 getDataIndex : function(col){
194 if(typeof this.config[col].dataIndex != 'number'){
195 return col;
196 }
197 return this.config[col].dataIndex;
198 },
199
200 /**
201 * Sets the dataIndex for a column.
202 * @param {Number} col The column index
203 * @param {Number} dataIndex The new dataIndex
204 */
205 setDataIndex : function(col, dataIndex){
206 this.config[col].dataIndex = dataIndex;
207 },
208 /**
209 * Returns true if the cell is editable.
210 * @param {Number} colIndex The column index
211 * @param {Number} rowIndex The row index
212 * @return {Boolean}
213 */
214 isCellEditable : function(colIndex, rowIndex){
215 return this.config[colIndex].editable || (typeof this.config[colIndex].editable == 'undefined' && this.config[colIndex].editor);
216 },
217
218 /**
219 * Returns the editor defined for the cell/column.
220 * @param {Number} colIndex The column index
221 * @param {Number} rowIndex The row index
222 * @return {Object}
223 */
224 getCellEditor : function(colIndex, rowIndex){
225 return this.config[colIndex].editor;
226 },
227
228 /**
229 * Sets if a column is editable.
230 * @param {Number} col The column index
231 * @param {Boolean} editable True if the column is editable
232 */
233 setEditable : function(col, editable){
234 this.config[col].editable = editable;
235 },
236
237
238 /**
239 * Returns true if the column is hidden.
240 * @param {Number} colIndex The column index
241 * @return {Boolean}
242 */
243 isHidden : function(colIndex){
244 return this.config[colIndex].hidden;
245 },
246
247
248 /**
249 * Returns true if the column width cannot be changed
250 */
251 isFixed : function(colIndex){
252 return this.config[colIndex].fixed;
253 },
254
255 /**
256 * Returns true if the column cannot be resized
257 * @return {Boolean}
258 */
259 isResizable : function(colIndex){
260 return this.config[colIndex].resizable !== false;
261 },
262 /**
263 * Sets if a column is hidden.
264 * @param {Number} colIndex The column index
265 */
266 setHidden : function(colIndex, hidden){
267 this.config[colIndex].hidden = hidden;
268 this.totalWidth = null;
269 this.fireHiddenChange(colIndex, hidden);
270 },
271
272 /**
273 * Sets the editor for a column.
274 * @param {Number} col The column index
275 * @param {Object} editor The editor object
276 */
277 setEditor : function(col, editor){
278 this.config[col].editor = editor;
279 }
280});
281
282/**
283 * Defines the default sorting (casting?) comparison functions used when sorting data:
284 * <br>&nbsp;&nbsp;sortTypes.none - sorts data as it is without casting or parsing (the default)
285 * <br>&nbsp;&nbsp;sortTypes.asUCString - case insensitive string
286 * <br>&nbsp;&nbsp;sortTypes.asDate - attempts to parse data as a date
287 * <br>&nbsp;&nbsp;sortTypes.asFloat
288 * <br>&nbsp;&nbsp;sortTypes.asInt
289 * @static
290 */
291YAHOO.ext.grid.DefaultColumnModel.sortTypes = {
292 none : function(s) {
293 return s;
294 },
295
296 asUCString : function(s) {
297 return String(s).toUpperCase();
298 },
299
300 asDate : function(s) {
301 if(s instanceof Date){
302 return s.getTime();
303 }
304 return Date.parse(String(s));
305 },
306
307 asFloat : function(s) {
308 var val = parseFloat(String(s).replace(/,/g, ''));
309 if(isNaN(val)) val = 0;
310 return val;
311 },
312
313 asInt : function(s) {
314 var val = parseInt(String(s).replace(/,/g, ''));
315 if(isNaN(val)) val = 0;
316 return val;
317 }
318};
319
320YAHOO.ext.grid.DefaultColumnModel.defaultRenderer = function(value){
321 if(typeof value == 'string' && value.length < 1){
322 return '&#160;';
323 }
324 return value;
325}
diff --git a/frontend/beta/js/YUI-extensions/grid/EditorGrid.js b/frontend/beta/js/YUI-extensions/grid/EditorGrid.js
new file mode 100644
index 0000000..e7405a0
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/grid/EditorGrid.js
@@ -0,0 +1,16 @@
1/**
2 * @class YAHOO.ext.grid.EditorGrid
3 * @extends YAHOO.ext.grid.Grid
4 * Shortcut class for creating and editable grid.
5 * @param {String/HTMLElement/YAHOO.ext.Element} container The element into which this grid will be rendered -
6 * The container MUST have some type of size defined for the grid to fill. The container will be
7 * automatically set to position relative if it isn't already.
8 * @param {Object} dataModel The data model to bind to
9 * @param {Object} colModel The column model with info about this grid's columns
10 */
11YAHOO.ext.grid.EditorGrid = function(container, dataModel, colModel){
12 YAHOO.ext.grid.EditorGrid.superclass.constructor.call(this, container, dataModel,
13 colModel, new YAHOO.ext.grid.EditorSelectionModel());
14 this.container.addClass('yeditgrid');
15};
16YAHOO.extendX(YAHOO.ext.grid.EditorGrid, YAHOO.ext.grid.Grid);
diff --git a/frontend/beta/js/YUI-extensions/grid/EditorSelectionModel.js b/frontend/beta/js/YUI-extensions/grid/EditorSelectionModel.js
new file mode 100644
index 0000000..c1cb240
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/grid/EditorSelectionModel.js
@@ -0,0 +1,182 @@
1
2/**
3 @class YAHOO.ext.grid.EditorSelectionModel
4 * Extends {@link YAHOO.ext.grid.DefaultSelectionModel} to enable cell navigation. <br><br>
5 @extends YAHOO.ext.grid.DefaultSelectionModel
6 @constructor
7 */
8YAHOO.ext.grid.EditorSelectionModel = function(){
9 YAHOO.ext.grid.EditorSelectionModel.superclass.constructor.call(this);
10 /** Number of clicks to activate a cell (for editing) - valid values are 1 or 2
11 * @type Number */
12 this.clicksToActivateCell = 1;
13 this.events['cellactivate'] = new YAHOO.util.CustomEvent('cellactivate');
14};
15
16YAHOO.extendX(YAHOO.ext.grid.EditorSelectionModel, YAHOO.ext.grid.DefaultSelectionModel);
17
18YAHOO.ext.grid.EditorSelectionModel.prototype.disableArrowNavigation = false;
19YAHOO.ext.grid.EditorSelectionModel.prototype.controlForArrowNavigation = false;
20
21/** @ignore */
22YAHOO.ext.grid.EditorSelectionModel.prototype.initEvents = function(){
23 this.grid.addListener("cellclick", this.onCellClick, this, true);
24 this.grid.addListener("celldblclick", this.onCellDblClick, this, true);
25 this.grid.addListener("keydown", this.keyDown, this, true);
26};
27
28YAHOO.ext.grid.EditorSelectionModel.prototype.onCellClick = function(grid, rowIndex, colIndex){
29 if(this.clicksToActivateCell == 1){
30 var row = this.grid.getRow(rowIndex);
31 var cell = row.childNodes[colIndex];
32 if(cell){
33 this.activate(row, cell);
34 }
35 }
36};
37
38YAHOO.ext.grid.EditorSelectionModel.prototype.activate = function(row, cell){
39 this.fireEvent('cellactivate', this, row, cell);
40 this.grid.doEdit(row, cell);
41};
42
43YAHOO.ext.grid.EditorSelectionModel.prototype.onCellDblClick = function(grid, rowIndex, colIndex){
44 if(this.clicksToActivateCell == 2){
45 var row = this.grid.getRow(rowIndex);
46 var cell = row.childNodes[colIndex];
47 if(cell){
48 this.activate(row, cell);
49 }
50 }
51};
52
53/** @ignore */
54YAHOO.ext.grid.EditorSelectionModel.prototype.setRowState = function(row, selected){
55 YAHOO.ext.grid.EditorSelectionModel.superclass.setRowState.call(this, row, false, false);
56};
57/** @ignore */
58YAHOO.ext.grid.EditorSelectionModel.prototype.focusRow = function(row, selected){
59};
60
61YAHOO.ext.grid.EditorSelectionModel.prototype.getEditorCellAfter = function(cell, spanRows){
62 var g = this.grid;
63 var next = g.getCellAfter(cell);
64 while(next && !g.colModel.isCellEditable(next.columnIndex)){
65 next = g.getCellAfter(next);
66 }
67 if(!next && spanRows){
68 var row = g.getRowAfter(g.getRowFromChild(cell));
69 if(row){
70 next = g.getFirstCell(row);
71 if(!g.colModel.isCellEditable(next.columnIndex)){
72 next = this.getEditorCellAfter(next);
73 }
74 }
75 }
76 return next;
77};
78
79YAHOO.ext.grid.EditorSelectionModel.prototype.getEditorCellBefore = function(cell, spanRows){
80 var g = this.grid;
81 var prev = g.getCellBefore(cell);
82 while(prev && !g.colModel.isCellEditable(prev.columnIndex)){
83 prev = g.getCellBefore(prev);
84 }
85 if(!prev && spanRows){
86 var row = g.getRowBefore(g.getRowFromChild(cell));
87 if(row){
88 prev = g.getLastCell(row);
89 if(!g.colModel.isCellEditable(prev.columnIndex)){
90 prev = this.getEditorCellBefore(prev);
91 }
92 }
93 }
94 return prev;
95};
96
97YAHOO.ext.grid.EditorSelectionModel.prototype.allowArrowNav = function(e){
98 return (!this.disableArrowNavigation && (!this.controlForArrowNavigation || e.ctrlKey));
99}
100/** @ignore */
101YAHOO.ext.grid.EditorSelectionModel.prototype.keyDown = function(e){
102 var g = this.grid, cm = g.colModel, cell = g.getEditingCell();
103 if(!cell) return;
104 var newCell;
105 switch(e.browserEvent.keyCode){
106 case e.TAB:
107 if(e.shiftKey){
108 newCell = this.getEditorCellBefore(cell, true);
109 }else{
110 newCell = this.getEditorCellAfter(cell, true);
111 }
112 e.preventDefault();
113 break;
114 case e.DOWN:
115 if(this.allowArrowNav(e)){
116 var next = g.getRowAfter(g.getRowFromChild(cell));
117 if(next){
118 newCell = next.childNodes[cell.columnIndex];
119 }
120 }
121 break;
122 case e.UP:
123 if(this.allowArrowNav(e)){
124 var prev = g.getRowBefore(g.getRowFromChild(cell));
125 if(prev){
126 newCell = prev.childNodes[cell.columnIndex];
127 }
128 }
129 break;
130 case e.RETURN:
131 if(e.shiftKey){
132 var prev = g.getRowBefore(g.getRowFromChild(cell));
133 if(prev){
134 newCell = prev.childNodes[cell.columnIndex];
135 }
136 }else{
137 var next = g.getRowAfter(g.getRowFromChild(cell));
138 if(next){
139 newCell = next.childNodes[cell.columnIndex];
140 }
141 }
142 break;
143 case e.RIGHT:
144 if(this.allowArrowNav(e)){
145 newCell = this.getEditorCellAfter(cell);
146 }
147 break;
148 case e.LEFT:
149 if(this.allowArrowNav(e)){
150 newCell = this.getEditorCellBefore(cell);
151 }
152 break;
153 };
154 if(newCell){
155 this.activate(g.getRowFromChild(newCell), newCell);
156 e.stopEvent();
157 }
158};
159
160/**
161 * @class YAHOO.ext.grid.EditorAndSelectionModel
162 */
163YAHOO.ext.grid.EditorAndSelectionModel = function(){
164 YAHOO.ext.grid.EditorAndSelectionModel.superclass.constructor.call(this);
165 this.events['cellactivate'] = new YAHOO.util.CustomEvent('cellactivate');
166};
167
168YAHOO.extendX(YAHOO.ext.grid.EditorAndSelectionModel, YAHOO.ext.grid.DefaultSelectionModel);
169
170YAHOO.ext.grid.EditorAndSelectionModel.prototype.initEvents = function(){
171 YAHOO.ext.grid.EditorAndSelectionModel.superclass.initEvents.call(this);
172 this.grid.addListener("celldblclick", this.onCellDblClick, this, true);
173};
174
175YAHOO.ext.grid.EditorAndSelectionModel.prototype.onCellDblClick = function(grid, rowIndex, colIndex){
176 var row = this.grid.getRow(rowIndex);
177 var cell = row.childNodes[colIndex];
178 if(cell){
179 this.fireEvent('cellactivate', this, row, cell);
180 this.grid.doEdit(row, cell);
181 }
182};
diff --git a/frontend/beta/js/YUI-extensions/grid/Grid.js b/frontend/beta/js/YUI-extensions/grid/Grid.js
new file mode 100644
index 0000000..46d5de4
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/grid/Grid.js
@@ -0,0 +1,965 @@
1/**
2 * @class YAHOO.ext.grid.Grid
3 * @extends YAHOO.ext.util.Observable
4 * This class represents the primary interface of a component based grid control.
5 * <br><br>Usage:<pre><code>
6 var grid = new YAHOO.ext.grid.Grid('my-container-id', dataModel, columnModel);
7 // set any options
8 grid.render();
9 // or using a config
10 var grid = new YAHOO.ext.grid.Grid('my-container-id', {
11 dataModel: myDataModel,
12 colModel: myColModel,
13 selModel: mySelectionModel,
14 autoSizeColumns: true,
15 monitorWindowResize: false,
16 trackMouseOver: true
17 }).render();
18 * </code></pre>
19 * <b>Common Problems:</b><br/>
20 * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
21 * element will correct this<br/>
22 * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
23 * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
24 * are unpredictable.<br/>
25 * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
26 * grid to calculate dimensions/offsets.<br/>
27 * @requires YAHOO.util.Dom
28 * @requires YAHOO.util.Event
29 * @requires YAHOO.util.CustomEvent
30 * @requires YAHOO.ext.Element
31 * @requires YAHOO.ext.util.Browser
32 * @requires YAHOO.ext.util.CSS
33 * @requires YAHOO.ext.SplitBar
34 * @requires YAHOO.ext.EventObject
35 * @constructor
36 * @param {String/HTMLElement/YAHOO.ext.Element} container The element into which this grid will be rendered -
37 * The container MUST have some type of size defined for the grid to fill. The container will be
38 * automatically set to position relative if it isn't already.
39 * @param {Object} config A config object that sets properties on this grid OR the data model to bind to
40 * @param {Object} colModel (optional) The column model with info about this grid's columns
41 * @param {Object} selectionModel (optional) The selection model for this grid (defaults to DefaultSelectionModel)
42 */
43YAHOO.ext.grid.Grid = function(container, config, colModel, selectionModel){
44 /** @private */
45 this.container = YAHOO.ext.Element.get(container);
46 this.container.update('');
47 this.container.setStyle('overflow', 'hidden');
48 this.id = this.container.id;
49 this.rows = [];
50 this.rowCount = 0;
51 this.fieldId = null;
52 var dataModel = config; // for legacy pre config support
53 this.dataModel = dataModel;
54 this.colModel = colModel;
55 this.selModel = selectionModel;
56 this.activeEditor = null;
57 this.editingCell = null;
58
59
60 if(typeof config == 'object' && !config.getRowCount){// must be config object
61 YAHOO.ext.util.Config.apply(this, config);
62 }
63
64 /** @private */
65 this.setValueDelegate = this.setCellValue.createDelegate(this);
66
67 /** @private */
68 this.events = {
69 // raw events
70 /**
71 * @event click
72 * The raw click event for the entire grid.
73 * @param {YAHOO.ext.EventObject} e
74 */
75 'click' : true,
76 /**
77 * @event dblclick
78 * The raw dblclick event for the entire grid.
79 * @param {YAHOO.ext.EventObject} e
80 */
81 'dblclick' : true,
82 /**
83 * @event mousedown
84 * The raw mousedown event for the entire grid.
85 * @param {YAHOO.ext.EventObject} e
86 */
87 'mousedown' : true,
88 /**
89 * @event mouseup
90 * The raw mouseup event for the entire grid.
91 * @param {YAHOO.ext.EventObject} e
92 */
93 'mouseup' : true,
94 /**
95 * @event mouseover
96 * The raw mouseover event for the entire grid.
97 * @param {YAHOO.ext.EventObject} e
98 */
99 'mouseover' : true,
100 /**
101 * @event mouseout
102 * The raw mouseout event for the entire grid.
103 * @param {YAHOO.ext.EventObject} e
104 */
105 'mouseout' : true,
106 /**
107 * @event keypress
108 * The raw keypress event for the entire grid.
109 * @param {YAHOO.ext.EventObject} e
110 */
111 'keypress' : true,
112 /**
113 * @event keydown
114 * The raw keydown event for the entire grid.
115 * @param {YAHOO.ext.EventObject} e
116 */
117 'keydown' : true,
118
119 // custom events
120
121 /**
122 * @event cellclick
123 * Fires when a cell is clicked
124 * @param {Grid} this
125 * @param {Number} rowIndex
126 * @param {Number} columnIndex
127 * @param {YAHOO.ext.EventObject} e
128 */
129 'cellclick' : true,
130 /**
131 * @event celldblclick
132 * Fires when a cell is double clicked
133 * @param {Grid} this
134 * @param {Number} rowIndex
135 * @param {Number} columnIndex
136 * @param {YAHOO.ext.EventObject} e
137 */
138 'celldblclick' : true,
139 /**
140 * @event rowclick
141 * Fires when a row is clicked
142 * @param {Grid} this
143 * @param {Number} rowIndex
144 * @param {YAHOO.ext.EventObject} e
145 */
146 'rowclick' : true,
147 /**
148 * @event rowdblclick
149 * Fires when a row is double clicked
150 * @param {Grid} this
151 * @param {Number} rowIndex
152 * @param {YAHOO.ext.EventObject} e
153 */
154 'rowdblclick' : true,
155 /**
156 * @event headerclick
157 * Fires when a header is clicked
158 * @param {Grid} this
159 * @param {Number} columnIndex
160 * @param {YAHOO.ext.EventObject} e
161 */
162 'headerclick' : true,
163 /**
164 * @event rowcontextmenu
165 * Fires when a row is right clicked
166 * @param {Grid} this
167 * @param {Number} rowIndex
168 * @param {YAHOO.ext.EventObject} e
169 */
170 'rowcontextmenu' : true,
171 /**
172 * @event cellcontextmenu
173 * Fires when a cell is right clicked
174 * @param {Grid} this
175 * @param {Number} rowIndex
176 * @param {Number} cellIndex
177 * @param {YAHOO.ext.EventObject} e
178 */
179 'cellcontextmenu' : true,
180 /**
181 * @event headercontextmenu
182 * Fires when a header is right clicked
183 * @param {Grid} this
184 * @param {Number} columnIndex
185 * @param {YAHOO.ext.EventObject} e
186 */
187 'headercontextmenu' : true,
188 /**
189 * @event beforeedit
190 * Fires before a cell is edited
191 * @param {Grid} this
192 * @param {Number} rowIndex
193 * @param {Number} columnIndex
194 */
195 'beforeedit' : true,
196 /**
197 * @event afteredit
198 * Fires after a cell is edited
199 * @param {Grid} this
200 * @param {Number} rowIndex
201 * @param {Number} columnIndex
202 */
203 'afteredit' : true,
204 /**
205 * @event bodyscroll
206 * Fires when the body element is scrolled
207 * @param {Number} scrollLeft
208 * @param {Number} scrollTop
209 */
210 'bodyscroll' : true,
211 /**
212 * @event columnresize
213 * Fires when the user resizes a column
214 * @param {Number} columnIndex
215 * @param {Number} newSize
216 */
217 'columnresize' : true,
218 /**
219 * @event startdrag
220 * Fires when row(s) start being dragged
221 * @param {Grid} this
222 * @param {YAHOO.ext.GridDD} dd The drag drop object
223 * @param {event} e The raw browser event
224 */
225 'startdrag' : true,
226 /**
227 * @event enddrag
228 * Fires when a drag operation is complete
229 * @param {Grid} this
230 * @param {YAHOO.ext.GridDD} dd The drag drop object
231 * @param {event} e The raw browser event
232 */
233 'enddrag' : true,
234 /**
235 * @event dragdrop
236 * Fires when dragged row(s) are dropped on a valid DD target
237 * @param {Grid} this
238 * @param {YAHOO.ext.GridDD} dd The drag drop object
239 * @param {String} targetId The target drag drop object
240 * @param {event} e The raw browser event
241 */
242 'dragdrop' : true,
243 /**
244 * @event dragover
245 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
246 * @param {Grid} this
247 * @param {YAHOO.ext.GridDD} dd The drag drop object
248 * @param {String} targetId The target drag drop object
249 * @param {event} e The raw browser event
250 */
251 'dragover' : true,
252 /**
253 * @event dragenter
254 * Fires when the dragged row(s) first cross another DD target while being dragged
255 * @param {Grid} this
256 * @param {YAHOO.ext.GridDD} dd The drag drop object
257 * @param {String} targetId The target drag drop object
258 * @param {event} e The raw browser event
259 */
260 'dragenter' : true,
261 /**
262 * @event dragout
263 * Fires when the dragged row(s) leave another DD target while being dragged
264 * @param {Grid} this
265 * @param {YAHOO.ext.GridDD} dd The drag drop object
266 * @param {String} targetId The target drag drop object
267 * @param {event} e The raw browser event
268 */
269 'dragout' : true
270 };
271};
272
273YAHOO.ext.grid.Grid.prototype = {
274 /** The minimum width a column can be resized to. (Defaults to 25)
275 * @type Number */
276 minColumnWidth : 25,
277
278 /** True to automatically resize the columns to fit their content <b>on initial render</b>
279 * @type Boolean */
280 autoSizeColumns : false,
281
282 /** True to measure headers with column data when auto sizing columns
283 * @type Boolean */
284 autoSizeHeaders : false,
285
286 /**
287 * True to autoSize the grid when the window resizes - defaults to true
288 */
289 monitorWindowResize : true,
290
291 /** If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
292 * rows measured to get a columns size - defaults to 0 (all rows).
293 * @type Number */
294 maxRowsToMeasure : 0,
295
296 /** True to highlight rows when the mouse is over (default is false)
297 * @type Boolean */
298 trackMouseOver : false,
299
300 /** True to enable drag and drop of rows
301 * @type Boolean */
302 enableDragDrop : false,
303
304 /** True to stripe the rows (default is true)
305 * @type Boolean */
306 stripeRows : true,
307 /** True to fit the height of the grid container to the height of the data (defaults to false)
308 * @type Boolean */
309 autoHeight : false,
310
311 /** True to fit the width of the grid container to the width of the columns (defaults to false)
312 * @type Boolean */
313 autoWidth : false,
314
315 /**
316 * The view used by the grid. This can be set before a call to render().
317 * Defaults to a YAHOO.ext.grid.GridView or PagedGridView depending on the data model.
318 * @type Object
319 */
320 view : null,
321
322 /** A regular expression defining tagNames
323 * allowed to have text selection (Defaults to <code>/INPUT|TEXTAREA|SELECT/i</code>) */
324 allowTextSelectionPattern : /INPUT|TEXTAREA|SELECT/i,
325
326 /**
327 * Called once after all setup has been completed and the grid is ready to be rendered.
328 * @return {YAHOO.ext.grid.Grid} this
329 */
330 render : function(){
331 if((!this.container.dom.offsetHeight || this.container.dom.offsetHeight < 20)
332 || this.container.getStyle('height') == 'auto'){
333 this.autoHeight = true;
334 }
335 if((!this.container.dom.offsetWidth || this.container.dom.offsetWidth < 20)){
336 this.autoWidth = true;
337 }
338 if(!this.view){
339 if(this.dataModel.isPaged()){
340 this.view = new YAHOO.ext.grid.PagedGridView();
341 }else{
342 this.view = new YAHOO.ext.grid.GridView();
343 }
344 }
345 this.view.init(this);
346 this.el = getEl(this.view.render(), true);
347 var c = this.container;
348 c.mon("click", this.onClick, this, true);
349 c.mon("dblclick", this.onDblClick, this, true);
350 c.mon("contextmenu", this.onContextMenu, this, true);
351 c.mon("selectstart", this.cancelTextSelection, this, true);
352 c.mon("mousedown", this.cancelTextSelection, this, true);
353 c.mon("mousedown", this.onMouseDown, this, true);
354 c.mon("mouseup", this.onMouseUp, this, true);
355 if(this.trackMouseOver){
356 this.el.mon("mouseover", this.onMouseOver, this, true);
357 this.el.mon("mouseout", this.onMouseOut, this, true);
358 }
359 c.mon("keypress", this.onKeyPress, this, true);
360 c.mon("keydown", this.onKeyDown, this, true);
361 this.init();
362 return this;
363 },
364
365 init : function(){
366 this.rows = this.el.dom.rows;
367 if(!this.disableSelection){
368 if(!this.selModel){
369 this.selModel = new YAHOO.ext.grid.DefaultSelectionModel(this);
370 }
371 this.selModel.init(this);
372 this.selModel.onSelectionChange.subscribe(this.updateField, this, true);
373 }else{
374 this.selModel = new YAHOO.ext.grid.DisableSelectionModel(this);
375 this.selModel.init(this);
376 }
377
378 if(this.enableDragDrop){
379 this.dd = new YAHOO.ext.grid.GridDD(this, this.container.dom);
380 }
381 },
382
383 /**
384 * Resets the grid for use with a new configuration and/or data and column models. After calling this function
385 * you will need to call render() again. Any listeners for this grid will be retained.
386 * Warning: any listeners manually attached (not through the grid) to the grid's container
387 * element will be removed.
388 * @param {Object} config Standard config object with properties to set on this grid
389 * @return {YAHOO.ext.grid.Grid} this
390 */
391 reset : function(config){
392 this.destroy(false, true);
393 YAHOO.ext.util.Config.apply(this, config);
394 return this;
395 },
396
397 /**
398 * Destroy this grid.
399 * @param {Boolean} removeEl True to remove the element
400 */
401 destroy : function(removeEl, keepListeners){
402 var c = this.container;
403 c.removeAllListeners();
404 this.view.destroy();
405 YAHOO.ext.EventManager.removeResizeListener(this.view.onWindowResize, this.view);
406 this.view = null;
407 this.colModel.purgeListeners();
408 if(!keepListeners){
409 this.purgeListeners();
410 }
411 c.update('');
412 if(removeEl === true){
413 c.remove();
414 }
415 },
416
417 /**
418 * Replace the current data model with a new one (experimental)
419 * @param {DataModel} dm The new data model
420 * @pram {Boolean} rerender true to render the grid rows from scratch
421 */
422 setDataModel : function(dm, rerender){
423 this.view.unplugDataModel(this.dataModel);
424 this.dataModel = dm;
425 this.view.plugDataModel(dm);
426 if(rerender){
427 dm.fireEvent('datachanged');
428 }
429 },
430
431 onMouseDown : function(e){
432 this.fireEvent('mousedown', e);
433 },
434
435 onMouseUp : function(e){
436 this.fireEvent('mouseup', e);
437 },
438
439 onMouseOver : function(e){
440 this.fireEvent('mouseover', e);
441 },
442
443 onMouseOut : function(e){
444 this.fireEvent('mouseout', e);
445 },
446
447 onKeyPress : function(e){
448 this.fireEvent('keypress', e);
449 },
450
451 onKeyDown : function(e){
452 this.fireEvent('keydown', e);
453 },
454
455 fireEvent : YAHOO.ext.util.Observable.prototype.fireEvent,
456 on : YAHOO.ext.util.Observable.prototype.on,
457 addListener : YAHOO.ext.util.Observable.prototype.addListener,
458 delayedListener : YAHOO.ext.util.Observable.prototype.delayedListener,
459 removeListener : YAHOO.ext.util.Observable.prototype.removeListener,
460 purgeListeners : YAHOO.ext.util.Observable.prototype.purgeListeners,
461 bufferedListener : YAHOO.ext.util.Observable.prototype.bufferedListener,
462
463 onClick : function(e){
464 this.fireEvent('click', e);
465 var target = e.getTarget();
466 var row = this.getRowFromChild(target);
467 var cell = this.getCellFromChild(target);
468 var header = this.getHeaderFromChild(target);
469 if(cell){
470 this.fireEvent('cellclick', this, row.rowIndex, cell.columnIndex, e);
471 }
472 if(row){
473 this.fireEvent('rowclick', this, row.rowIndex, e);
474 }
475 if(header){
476 this.fireEvent('headerclick', this, header.columnIndex, e);
477 }
478 },
479
480 onContextMenu : function(e){
481 var target = e.getTarget();
482 var row = this.getRowFromChild(target);
483 var cell = this.getCellFromChild(target);
484 var header = this.getHeaderFromChild(target);
485 if(cell){
486 this.fireEvent('cellcontextmenu', this, row.rowIndex, cell.columnIndex, e);
487 }
488 if(row){
489 this.fireEvent('rowcontextmenu', this, row.rowIndex, e);
490 }
491 if(header){
492 this.fireEvent('headercontextmenu', this, header.columnIndex, e);
493 }
494 e.preventDefault();
495 },
496
497 onDblClick : function(e){
498 this.fireEvent('dblclick', e);
499 var target = e.getTarget();
500 var row = this.getRowFromChild(target);
501 var cell = this.getCellFromChild(target);
502 if(row){
503 this.fireEvent('rowdblclick', this, row.rowIndex, e);
504 }
505 if(cell){
506 this.fireEvent('celldblclick', this, row.rowIndex, cell.columnIndex, e);
507 }
508 },
509
510 /**
511 * Starts editing the specified for the specified row/column
512 * @param {Number} rowIndex
513 * @param {Number} colIndex
514 */
515 startEditing : function(rowIndex, colIndex){
516 var row = this.rows[rowIndex];
517 var cell = row.childNodes[colIndex];
518 this.stopEditing();
519 setTimeout(this.doEdit.createDelegate(this, [row, cell]), 10);
520 },
521
522 /**
523 * Stops any active editing
524 */
525 stopEditing : function(){
526 if(this.activeEditor){
527 this.activeEditor.stopEditing();
528 }
529 },
530
531 /** @ignore */
532 doEdit : function(row, cell){
533 if(!row || !cell) return;
534 var cm = this.colModel;
535 var dm = this.dataModel;
536 var colIndex = cell.columnIndex;
537 var rowIndex = row.rowIndex;
538 if(cm.isCellEditable(colIndex, rowIndex)){
539 var ed = cm.getCellEditor(colIndex, rowIndex);
540 if(ed){
541 if(this.activeEditor){
542 this.activeEditor.stopEditing();
543 }
544 this.fireEvent('beforeedit', this, rowIndex, colIndex);
545 this.activeEditor = ed;
546 this.editingCell = cell;
547 this.view.ensureVisible(row, true);
548 try{
549 cell.focus();
550 }catch(e){}
551 ed.init(this, this.el.dom.parentNode, this.setValueDelegate);
552 var value = dm.getValueAt(rowIndex, cm.getDataIndex(colIndex));
553 // set timeout so firefox stops editing before starting a new edit
554 setTimeout(ed.startEditing.createDelegate(ed, [value, row, cell]), 1);
555 }
556 }
557 },
558
559 setCellValue : function(value, rowIndex, colIndex){
560 this.dataModel.setValueAt(value, rowIndex, this.colModel.getDataIndex(colIndex));
561 this.fireEvent('afteredit', this, rowIndex, colIndex);
562 },
563
564 /** @ignore Called when text selection starts or mousedown to prevent default */
565 cancelTextSelection : function(e){
566 var target = e.getTarget();
567 if(target && target != this.el.dom.parentNode && !this.allowTextSelectionPattern.test(target.tagName)){
568 e.preventDefault();
569 }
570 },
571
572 /**
573 * Causes the grid to manually recalculate it's dimensions. Generally this is done automatically,
574 * but if manual update is required this method will initiate it.
575 */
576 autoSize : function(){
577 this.view.updateWrapHeight();
578 this.view.adjustForScroll();
579 },
580
581 /**
582 * Scrolls the grid to the specified row
583 * @param {Number/HTMLElement} row The row object or index of the row
584 */
585 scrollTo : function(row){
586 if(typeof row == 'number'){
587 row = this.rows[row];
588 }
589 this.view.ensureVisible(row, true);
590 },
591
592 /** @private */
593 getEditingCell : function(){
594 return this.editingCell;
595 },
596
597 /**
598 * Binds this grid to the field with the specified id. Initially reads and parses the comma
599 * delimited ids in the field and selects those items. All selections made in the grid
600 * will be persisted to the field by their ids comma delimited.
601 * @param {String} The id of the field to bind to
602 */
603 bindToField : function(fieldId){
604 this.fieldId = fieldId;
605 this.readField();
606 },
607
608 /** @private */
609 updateField : function(){
610 if(this.fieldId){
611 var field = YAHOO.util.Dom.get(this.fieldId);
612 field.value = this.getSelectedRowIds().join(',');
613 }
614 },
615
616 /**
617 * Causes the grid to read and select the ids from the bound field - See {@link #bindToField}.
618 */
619 readField : function(){
620 if(this.fieldId){
621 var field = YAHOO.util.Dom.get(this.fieldId);
622 var values = field.value.split(',');
623 var rows = this.getRowsById(values);
624 this.selModel.selectRows(rows, false);
625 }
626 },
627
628 /**
629 * Returns the table row at the specified index
630 * @param {Number} index
631 * @return {HTMLElement}
632 */
633 getRow : function(index){
634 return this.rows[index];
635 },
636
637 /**
638 * Returns the rows that have the specified id(s). The id value for a row is provided
639 * by the DataModel. See {@link YAHOO.ext.grid.DefaultDataModel#getRowId}.
640 * @param {String/Array} An id to find or an array of ids
641 * @return {HtmlElement/Array} If one id was passed in, it returns one result.
642 * If an array of ids was specified, it returns an Array of HTMLElements
643 */
644 getRowsById : function(id){
645 var dm = this.dataModel;
646 if(!(id instanceof Array)){
647 for(var i = 0; i < this.rows.length; i++){
648 if(dm.getRowId(i) == id){
649 return this.rows[i];
650 }
651 }
652 return null;
653 }
654 var found = [];
655 var re = "^(?:";
656 for(var i = 0; i < id.length; i++){
657 re += id[i];
658 if(i != id.length-1) re += "|";
659 }
660 var regex = new RegExp(re + ")$");
661 for(var i = 0; i < this.rows.length; i++){
662 if(regex.test(dm.getRowId(i))){
663 found.push(this.rows[i]);
664 }
665 }
666 return found;
667 },
668
669 /**
670 * Returns the row that comes after the specified row - text nodes are skipped.
671 * @param {HTMLElement} row
672 * @return {HTMLElement}
673 */
674 getRowAfter : function(row){
675 return this.getSibling('next', row);
676 },
677
678 /**
679 * Returns the row that comes before the specified row - text nodes are skipped.
680 * @param {HTMLElement} row
681 * @return {HTMLElement}
682 */
683 getRowBefore : function(row){
684 return this.getSibling('previous', row);
685 },
686
687 /**
688 * Returns the cell that comes after the specified cell - text nodes are skipped.
689 * @param {HTMLElement} cell
690 * @param {Boolean} includeHidden
691 * @return {HTMLElement}
692 */
693 getCellAfter : function(cell, includeHidden){
694 var next = this.getSibling('next', cell);
695 if(next && !includeHidden && this.colModel.isHidden(next.columnIndex)){
696 return this.getCellAfter(next);
697 }
698 return next;
699 },
700
701 /**
702 * Returns the cell that comes before the specified cell - text nodes are skipped.
703 * @param {HTMLElement} cell
704 * @param {Boolean} includeHidden
705 * @return {HTMLElement}
706 */
707 getCellBefore : function(cell, includeHidden){
708 var prev = this.getSibling('previous', cell);
709 if(prev && !includeHidden && this.colModel.isHidden(prev.columnIndex)){
710 return this.getCellBefore(prev);
711 }
712 return prev;
713 },
714
715 /**
716 * Returns the last cell for the row - text nodes and hidden columns are skipped.
717 * @param {HTMLElement} row
718 * @param {Boolean} includeHidden
719 * @return {HTMLElement}
720 */
721 getLastCell : function(row, includeHidden){
722 var cell = this.getElement('previous', row.lastChild);
723 if(cell && !includeHidden && this.colModel.isHidden(cell.columnIndex)){
724 return this.getCellBefore(cell);
725 }
726 return cell;
727 },
728
729 /**
730 * Returns the first cell for the row - text nodes and hidden columns are skipped.
731 * @param {HTMLElement} row
732 * @param {Boolean} includeHidden
733 * @return {HTMLElement}
734 */
735 getFirstCell : function(row, includeHidden){
736 var cell = this.getElement('next', row.firstChild);
737 if(cell && !includeHidden && this.colModel.isHidden(cell.columnIndex)){
738 return this.getCellAfter(cell);
739 }
740 return cell;
741 },
742
743 /**
744 * @private
745 * Gets siblings, skipping text nodes
746 * @param {String} type The direction to walk: 'next' or 'previous'
747 * @param {HTMLElement} node
748 */
749 getSibling : function(type, node){
750 if(!node) return null;
751 type += 'Sibling';
752 var n = node[type];
753 while(n && n.nodeType != 1){
754 n = n[type];
755 }
756 return n;
757 },
758
759 /**
760 * Returns node if node is an HTMLElement else walks the siblings in direction looking for
761 * a node that is an element
762 * @param {String} direction The direction to walk: 'next' or 'previous'
763 * @private
764 */
765 getElement : function(direction, node){
766 if(!node || node.nodeType == 1) return node;
767 else return this.getSibling(direction, node);
768 },
769
770 /**
771 * @private
772 */
773 getElementFromChild : function(childEl, parentClass){
774 if(!childEl || (YAHOO.util.Dom.hasClass(childEl, parentClass))){
775 return childEl;
776 }
777 var p = childEl.parentNode;
778 var b = document.body;
779 while(p && p != b){
780 if(YAHOO.util.Dom.hasClass(p, parentClass)){
781 return p;
782 }
783 p = p.parentNode;
784 }
785 return null;
786 },
787
788 /**
789 * Returns the row that contains the specified child element.
790 * @param {HTMLElement} childEl
791 * @return {HTMLElement}
792 */
793 getRowFromChild : function(childEl){
794 return this.getElementFromChild(childEl, 'ygrid-row');
795 },
796
797 /**
798 * Returns the cell that contains the specified child element.
799 * @param {HTMLElement} childEl
800 * @return {HTMLElement}
801 */
802 getCellFromChild : function(childEl){
803 return this.getElementFromChild(childEl, 'ygrid-col');
804 },
805
806
807 /**
808 * Returns the header element that contains the specified child element.
809 * @param {HTMLElement} childEl
810 * @return {HTMLElement}
811 */
812 getHeaderFromChild : function(childEl){
813 return this.getElementFromChild(childEl, 'ygrid-hd');
814 },
815
816 /**
817 * Convenience method for getSelectionModel().getSelectedRows() -
818 * See <small>{@link YAHOO.ext.grid.DefaultSelectionModel#getSelectedRows}</small> for more details.
819 * @return {Array}
820 */
821 getSelectedRows : function(){
822 return this.selModel.getSelectedRows();
823 },
824
825 /**
826 * Convenience method for getSelectionModel().getSelectedRows()[0] -
827 * See <small>{@link YAHOO.ext.grid.DefaultSelectionModel#getSelectedRows}</small> for more details.
828 * @return {HTMLElement}
829 */
830 getSelectedRow : function(){
831 if(this.selModel.hasSelection()){
832 return this.selModel.getSelectedRows()[0];
833 }
834 return null;
835 },
836
837 /**
838 * Get the selected row indexes
839 * @return {Array} Array of indexes
840 */
841 getSelectedRowIndexes : function(){
842 var a = [];
843 var rows = this.selModel.getSelectedRows();
844 for(var i = 0; i < rows.length; i++) {
845 a[i] = rows[i].rowIndex;
846 }
847 return a;
848 },
849
850 /**
851 * Gets the first selected row or -1 if none are selected
852 * @return {Number}
853 */
854 getSelectedRowIndex : function(){
855 if(this.selModel.hasSelection()){
856 return this.selModel.getSelectedRows()[0].rowIndex;
857 }
858 return -1;
859 },
860
861 /**
862 * Convenience method for getSelectionModel().getSelectedRowIds()[0] -
863 * See <small>{@link YAHOO.ext.grid.DefaultSelectionModel#getSelectedRowIds}</small> for more details.
864 * @return {String}
865 */
866 getSelectedRowId : function(){
867 if(this.selModel.hasSelection()){
868 return this.selModel.getSelectedRowIds()[0];
869 }
870 return null;
871 },
872
873 /**
874 * Convenience method for getSelectionModel().getSelectedRowIds() -
875 * See <small>{@link YAHOO.ext.grid.DefaultSelectionModel#getSelectedRowIds}</small> for more details.
876 * @return {Array}
877 */
878 getSelectedRowIds : function(){
879 return this.selModel.getSelectedRowIds();
880 },
881
882 /**
883 * Convenience method for getSelectionModel().clearSelections() -
884 * See <small>{@link YAHOO.ext.grid.DefaultSelectionModel#clearSelections}</small> for more details.
885 */
886 clearSelections : function(){
887 this.selModel.clearSelections();
888 },
889
890
891 /**
892 * Convenience method for getSelectionModel().selectAll() -
893 * See <small>{@link YAHOO.ext.grid.DefaultSelectionModel#selectAll}</small> for more details.
894 */
895 selectAll : function(){
896 this.selModel.selectAll();
897 },
898
899
900 /**
901 * Convenience method for getSelectionModel().getCount() -
902 * See <small>{@link YAHOO.ext.grid.DefaultSelectionModel#getCount}</small> for more details.
903 * @return {Number}
904 */
905 getSelectionCount : function(){
906 return this.selModel.getCount();
907 },
908
909 /**
910 * Convenience method for getSelectionModel().hasSelection() -
911 * See <small>{@link YAHOO.ext.grid.DefaultSelectionModel#hasSelection}</small> for more details.
912 * @return {Boolean}
913 */
914 hasSelection : function(){
915 return this.selModel.hasSelection();
916 },
917
918 /**
919 * Returns the grid's SelectionModel.
920 * @return {SelectionModel}
921 */
922 getSelectionModel : function(){
923 if(!this.selModel){
924 this.selModel = new DefaultSelectionModel();
925 }
926 return this.selModel;
927 },
928
929 /**
930 * Returns the grid's DataModel.
931 * @return {DataModel}
932 */
933 getDataModel : function(){
934 return this.dataModel;
935 },
936
937 /**
938 * Returns the grid's ColumnModel.
939 * @return {ColumnModel}
940 */
941 getColumnModel : function(){
942 return this.colModel;
943 },
944
945 /**
946 * Returns the grid's GridView object.
947 * @return {GridView}
948 */
949 getView : function(){
950 return this.view;
951 },
952 /**
953 * Called to get grid's drag proxy text, by default returns this.ddText.
954 * @return {String}
955 */
956 getDragDropText : function(){
957 return this.ddText.replace('%0', this.selModel.getCount());
958 }
959};
960/**
961 * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
962 * %0 is replaced with the number of selected rows.
963 * @type String
964 */
965YAHOO.ext.grid.Grid.prototype.ddText = "%0 selected row(s)";
diff --git a/frontend/beta/js/YUI-extensions/grid/GridDD.js b/frontend/beta/js/YUI-extensions/grid/GridDD.js
new file mode 100644
index 0000000..cdcaf39
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/grid/GridDD.js
@@ -0,0 +1,101 @@
1
2// kill dependency issue
3if(YAHOO.util.DDProxy){
4/**
5 * @class YAHOO.ext.grid.GridDD
6 * Custom implementation of YAHOO.util.DDProxy used internally by the grid
7 * @extends YAHOO.util.DDProxy
8 */
9YAHOO.ext.grid.GridDD = function(grid, bwrap){
10 this.grid = grid;
11 var ddproxy = document.createElement('div');
12 ddproxy.id = grid.container.id + '-ddproxy';
13 ddproxy.className = 'ygrid-drag-proxy';
14 document.body.insertBefore(ddproxy, document.body.firstChild);
15 YAHOO.util.Dom.setStyle(ddproxy, 'opacity', .80);
16 var ddicon = document.createElement('span');
17 ddicon.className = 'ygrid-drop-icon ygrid-drop-nodrop';
18 ddproxy.appendChild(ddicon);
19 var ddtext = document.createElement('span');
20 ddtext.className = 'ygrid-drag-text';
21 ddtext.innerHTML = "&#160;";
22 ddproxy.appendChild(ddtext);
23
24 this.ddproxy = ddproxy;
25 this.ddtext = ddtext;
26 this.ddicon = ddicon;
27 YAHOO.util.Event.on(bwrap, 'click', this.handleClick, this, true);
28 YAHOO.ext.grid.GridDD.superclass.constructor.call(this, bwrap.id, 'GridDD',
29 {dragElId : ddproxy.id, resizeFrame: false});
30
31 this.unlockDelegate = grid.selModel.unlock.createDelegate(grid.selModel);
32};
33YAHOO.extendX(YAHOO.ext.grid.GridDD, YAHOO.util.DDProxy);
34
35YAHOO.ext.grid.GridDD.prototype.handleMouseDown = function(e){
36 var row = this.grid.getRowFromChild(YAHOO.util.Event.getTarget(e));
37 if(!row) return;
38 if(this.grid.selModel.isSelected(row)){
39 YAHOO.ext.grid.GridDD.superclass.handleMouseDown.call(this, e);
40 }else {
41 this.grid.selModel.unlock();
42 YAHOO.ext.EventObject.setEvent(e);
43 this.grid.selModel.rowClick(this.grid, row.rowIndex, YAHOO.ext.EventObject);
44 YAHOO.ext.grid.GridDD.superclass.handleMouseDown.call(this, e);
45 this.grid.selModel.lock();
46 }
47};
48
49YAHOO.ext.grid.GridDD.prototype.handleClick = function(e){
50 if(this.grid.selModel.isLocked()){
51 setTimeout(this.unlockDelegate, 1);
52 YAHOO.util.Event.stopEvent(e);
53 }
54};
55
56/**
57 * Updates the DD visual element to allow/not allow a drop
58 * @param {Boolean} dropStatus True if drop is allowed on the target
59 */
60YAHOO.ext.grid.GridDD.prototype.setDropStatus = function(dropStatus){
61 if(dropStatus === true){
62 YAHOO.util.Dom.replaceClass(this.ddicon, 'ygrid-drop-nodrop', 'ygrid-drop-ok');
63 }else{
64 YAHOO.util.Dom.replaceClass(this.ddicon, 'ygrid-drop-ok', 'ygrid-drop-nodrop');
65 }
66};
67
68YAHOO.ext.grid.GridDD.prototype.startDrag = function(e){
69 this.ddtext.innerHTML = this.grid.getDragDropText();
70 this.setDropStatus(false);
71 this.grid.selModel.lock();
72 this.grid.fireEvent('startdrag', this.grid, this, e);
73};
74
75YAHOO.ext.grid.GridDD.prototype.endDrag = function(e){
76 YAHOO.util.Dom.setStyle(this.ddproxy, 'visibility', 'hidden');
77 this.grid.fireEvent('enddrag', this.grid, this, e);
78};
79
80YAHOO.ext.grid.GridDD.prototype.autoOffset = function(iPageX, iPageY) {
81 this.setDelta(-12, -20);
82};
83
84YAHOO.ext.grid.GridDD.prototype.onDragEnter = function(e, id) {
85 this.setDropStatus(true);
86 this.grid.fireEvent('dragenter', this.grid, this, id, e);
87};
88
89YAHOO.ext.grid.GridDD.prototype.onDragDrop = function(e, id) {
90 this.grid.fireEvent('dragdrop', this.grid, this, id, e);
91};
92
93YAHOO.ext.grid.GridDD.prototype.onDragOver = function(e, id) {
94 this.grid.fireEvent('dragover', this.grid, this, id, e);
95};
96
97YAHOO.ext.grid.GridDD.prototype.onDragOut = function(e, id) {
98 this.setDropStatus(false);
99 this.grid.fireEvent('dragout', this.grid, this, id, e);
100};
101};
diff --git a/frontend/beta/js/YUI-extensions/grid/GridView.js b/frontend/beta/js/YUI-extensions/grid/GridView.js
new file mode 100644
index 0000000..dbd47e3
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/grid/GridView.js
@@ -0,0 +1,790 @@
1/**
2 * @class YAHOO.ext.grid.GridView
3 * Default UI code used internally by the Grid. This is the object returned by {@link YAHOO.ext.grid.Grid#getView}.
4 * @constructor
5 */
6YAHOO.ext.grid.GridView = function(){
7 this.grid = null;
8 this.lastFocusedRow = null;
9 this.onScroll = new YAHOO.util.CustomEvent('onscroll');
10 this.adjustScrollTask = new YAHOO.ext.util.DelayedTask(this._adjustForScroll, this);
11 this.ensureVisibleTask = new YAHOO.ext.util.DelayedTask();
12};
13
14YAHOO.ext.grid.GridView.prototype = {
15 init: function(grid){
16 this.grid = grid;
17 },
18
19 fireScroll: function(scrollLeft, scrollTop){
20 this.onScroll.fireDirect(this.grid, scrollLeft, scrollTop);
21 },
22
23 /**
24 * @private
25 * Utility method that gets an array of the cell renderers
26 */
27 getColumnRenderers : function(){
28 var renderers = [];
29 var cm = this.grid.colModel;
30 var colCount = cm.getColumnCount();
31 for(var i = 0; i < colCount; i++){
32 renderers.push(cm.getRenderer(i));
33 }
34 return renderers;
35 },
36
37 buildIndexMap : function(){
38 var colToData = {};
39 var dataToCol = {};
40 var cm = this.grid.colModel;
41 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
42 var di = cm.getDataIndex(i);
43 colToData[i] = di;
44 dataToCol[di] = i;
45 }
46 return {'colToData': colToData, 'dataToCol': dataToCol};
47 },
48
49 getDataIndexes : function(){
50 if(!this.indexMap){
51 this.indexMap = this.buildIndexMap();
52 }
53 return this.indexMap.colToData;
54 },
55
56 getColumnIndexByDataIndex : function(dataIndex){
57 if(!this.indexMap){
58 this.indexMap = this.buildIndexMap();
59 }
60 return this.indexMap.dataToCol[dataIndex];
61 },
62
63 updateHeaders : function(){
64 var colModel = this.grid.colModel;
65 var hcells = this.headers;
66 var colCount = colModel.getColumnCount();
67 for(var i = 0; i < colCount; i++){
68 hcells[i].textNode.innerHTML = colModel.getColumnHeader(i);
69 }
70 },
71
72 adjustForScroll : function(disableDelay){
73 if(!disableDelay){
74 this.adjustScrollTask.delay(50);
75 }else{
76 this._adjustForScroll();
77 }
78 },
79
80 /**
81 * Returns the rowIndex/columnIndex of the cell found at the passed page coordinates
82 * @param {Number} x
83 * @param {Number} y
84 * @return {Array} [rowIndex, columnIndex]
85 */
86 getCellAtPoint : function(x, y){
87 var colIndex = null;
88 var rowIndex = null;
89
90 // translate page coordinates to local coordinates
91 var xy = YAHOO.util.Dom.getXY(this.wrap);
92 x = (x - xy[0]) + this.wrap.scrollLeft;
93 y = (y - xy[1]) + this.wrap.scrollTop;
94
95 var colModel = this.grid.colModel;
96 var pos = 0;
97 var colCount = colModel.getColumnCount();
98 for(var i = 0; i < colCount; i++){
99 if(colModel.isHidden(i)) continue;
100 var width = colModel.getColumnWidth(i);
101 if(x >= pos && x < pos+width){
102 colIndex = i;
103 break;
104 }
105 pos += width;
106 }
107 if(colIndex != null){
108 rowIndex = (y == 0 ? 0 : Math.floor(y / this.getRowHeight()));
109 if(rowIndex >= this.grid.dataModel.getRowCount()){
110 return null;
111 }
112 return [colIndex, rowIndex];
113 }
114 return null;
115 },
116
117 /** @private */
118 _adjustForScroll : function(){
119 this.forceScrollUpdate();
120 if(this.scrollbarMode == YAHOO.ext.grid.GridView.SCROLLBARS_OVERLAP){
121 var adjustment = 0;
122 if(this.wrap.clientWidth && this.wrap.clientWidth !== 0){
123 adjustment = this.wrap.offsetWidth - this.wrap.clientWidth;
124 }
125 this.hwrap.setWidth(this.wrap.offsetWidth-adjustment);
126 }else{
127 this.hwrap.setWidth(this.wrap.offsetWidth);
128 }
129 this.bwrap.setWidth(Math.max(this.grid.colModel.getTotalWidth(), this.wrap.clientWidth));
130 },
131
132 /**
133 * Focuses the specified row. The preferred way to scroll to a row is {@link #ensureVisible}.
134 * @param {Number/HTMLElement} row The index of a row or the row itself
135 */
136 focusRow : function(row){
137 if(typeof row == 'number'){
138 row = this.getBodyTable().childNodes[row];
139 }
140 if(!row) return;
141 var left = this.wrap.scrollLeft;
142 try{ // try catch for IE occasional focus bug
143 row.childNodes.item(0).hideFocus = true;
144 row.childNodes.item(0).focus();
145 }catch(e){}
146 this.ensureVisible(row);
147 this.wrap.scrollLeft = left;
148 this.handleScroll();
149 this.lastFocusedRow = row;
150 },
151
152 /**
153 * Scrolls the specified row into view. This call is automatically buffered (delayed), to disable
154 * the delay, pass true for disableDelay.
155 * @param {Number/HTMLElement} row The index of a row or the row itself
156 * @param {Boolean} disableDelay
157 */
158 ensureVisible : function(row, disableDelay){
159 if(!disableDelay){
160 this.ensureVisibleTask.delay(50, this._ensureVisible, this, [row]);
161 }else{
162 this._ensureVisible(row);
163 }
164 },
165
166 /** @ignore */
167 _ensureVisible : function(row){
168 if(typeof row == 'number'){
169 row = this.getBodyTable().childNodes[row];
170 }
171 if(!row) return;
172 var left = this.wrap.scrollLeft;
173 var rowTop = parseInt(row.offsetTop, 10); // parseInt for safari bug
174 var rowBottom = rowTop + row.offsetHeight;
175 var clientTop = parseInt(this.wrap.scrollTop, 10); // parseInt for safari bug
176 var clientBottom = clientTop + this.wrap.clientHeight;
177 if(rowTop < clientTop){
178 this.wrap.scrollTop = rowTop;
179 }else if(rowBottom > clientBottom){
180 this.wrap.scrollTop = rowBottom-this.wrap.clientHeight;
181 }
182 this.wrap.scrollLeft = left;
183 this.handleScroll();
184 },
185
186 updateColumns : function(){
187 this.grid.stopEditing();
188 var colModel = this.grid.colModel;
189 var hcols = this.headers;
190 var colCount = colModel.getColumnCount();
191 var pos = 0;
192 var totalWidth = colModel.getTotalWidth();
193 for(var i = 0; i < colCount; i++){
194 if(colModel.isHidden(i)) continue;
195 var width = colModel.getColumnWidth(i);
196 hcols[i].style.width = width + 'px';
197 hcols[i].style.left = pos + 'px';
198 hcols[i].split.style.left = (pos+width-3) + 'px';
199 this.setCSSWidth(i, width, pos);
200 pos += width;
201 }
202 this.lastWidth = totalWidth;
203 if(this.grid.autoWidth){
204 this.grid.container.setWidth(totalWidth+this.grid.container.getBorderWidth('lr'));
205 this.grid.autoSize();
206 }
207 this.bwrap.setWidth(Math.max(totalWidth, this.wrap.clientWidth));
208 if(!YAHOO.ext.util.Browser.isIE){ // fix scrolling prob in gecko and opera
209 this.wrap.scrollLeft = this.hwrap.dom.scrollLeft;
210 }
211 this.syncScroll();
212 this.forceScrollUpdate();
213 if(this.grid.autoHeight){
214 this.autoHeight();
215 this.updateWrapHeight();
216 }
217 },
218
219 setCSSWidth : function(colIndex, width, pos){
220 var selector = ["#" + this.grid.id + " .ygrid-col-" + colIndex, ".ygrid-col-" + colIndex];
221 YAHOO.ext.util.CSS.updateRule(selector, 'width', width + 'px');
222 if(typeof pos == 'number'){
223 YAHOO.ext.util.CSS.updateRule(selector, 'left', pos + 'px');
224 }
225 },
226
227 /**
228 * Set a css style for a column dynamically.
229 * @param {Number} colIndex The index of the column
230 * @param {String} name The css property name
231 * @param {String} value The css value
232 */
233 setCSSStyle : function(colIndex, name, value){
234 var selector = ["#" + this.grid.id + " .ygrid-col-" + colIndex, ".ygrid-col-" + colIndex];
235 YAHOO.ext.util.CSS.updateRule(selector, name, value);
236 },
237
238 handleHiddenChange : function(colModel, colIndex, hidden){
239 if(hidden){
240 this.hideColumn(colIndex);
241 }else{
242 this.unhideColumn(colIndex);
243 }
244 this.updateColumns();
245 },
246
247 hideColumn : function(colIndex){
248 var selector = ["#" + this.grid.id + " .ygrid-col-" + colIndex, ".ygrid-col-" + colIndex];
249 YAHOO.ext.util.CSS.updateRule(selector, 'position', 'absolute');
250 YAHOO.ext.util.CSS.updateRule(selector, 'visibility', 'hidden');
251
252 this.headers[colIndex].style.display = 'none';
253 this.headers[colIndex].split.style.display = 'none';
254 },
255
256 unhideColumn : function(colIndex){
257 var selector = ["#" + this.grid.id + " .ygrid-col-" + colIndex, ".ygrid-col-" + colIndex];
258 YAHOO.ext.util.CSS.updateRule(selector, 'position', '');
259 YAHOO.ext.util.CSS.updateRule(selector, 'visibility', 'visible');
260
261 this.headers[colIndex].style.display = '';
262 this.headers[colIndex].split.style.display = '';
263 },
264
265 getBodyTable : function(){
266 return this.bwrap.dom;
267 },
268
269 updateRowIndexes : function(firstRow, lastRow){
270 var stripeRows = this.grid.stripeRows;
271 var bt = this.getBodyTable();
272 var nodes = bt.childNodes;
273 firstRow = firstRow || 0;
274 lastRow = lastRow || nodes.length-1;
275 var re = /^(?:ygrid-row ygrid-row-alt|ygrid-row)/;
276 for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
277 var node = nodes[rowIndex];
278 if(stripeRows && (rowIndex+1) % 2 == 0){
279 node.className = node.className.replace(re, 'ygrid-row ygrid-row-alt');
280 }else{
281 node.className = node.className.replace(re, 'ygrid-row');
282 }
283 node.rowIndex = rowIndex;
284 nodes[rowIndex].style.top = (rowIndex * this.rowHeight) + 'px';
285 }
286 },
287
288 insertRows : function(dataModel, firstRow, lastRow){
289 this.updateBodyHeight();
290 this.adjustForScroll(true);
291 var renderers = this.getColumnRenderers();
292 var dindexes = this.getDataIndexes();
293 var colCount = this.grid.colModel.getColumnCount();
294 var beforeRow = null;
295 var bt = this.getBodyTable();
296 if(firstRow < bt.childNodes.length){
297 beforeRow = bt.childNodes[firstRow];
298 }
299 for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
300 var row = document.createElement('span');
301 row.className = 'ygrid-row';
302 row.style.top = (rowIndex * this.rowHeight) + 'px';
303 this.renderRow(dataModel, row, rowIndex, colCount, renderers, dindexes);
304 if(beforeRow){
305 bt.insertBefore(row, beforeRow);
306 }else{
307 bt.appendChild(row);
308 }
309 }
310 this.updateRowIndexes(firstRow);
311 this.adjustForScroll(true);
312 },
313
314 renderRow : function(dataModel, row, rowIndex, colCount, renderers, dindexes){
315 for(var colIndex = 0; colIndex < colCount; colIndex++){
316 var td = document.createElement('span');
317 td.className = 'ygrid-col ygrid-col-' + colIndex + (colIndex == colCount-1 ? ' ygrid-col-last' : '');
318 td.columnIndex = colIndex;
319 td.tabIndex = 0;
320 var span = document.createElement('span');
321 span.className = 'ygrid-cell-text';
322 td.appendChild(span);
323 var val = renderers[colIndex](dataModel.getValueAt(rowIndex, dindexes[colIndex]), rowIndex, colIndex, td, dataModel);
324 if(typeof val == 'undefined' || val === '') val = '&#160;';
325 span.innerHTML = val;
326 row.appendChild(td);
327 }
328 },
329
330 deleteRows : function(dataModel, firstRow, lastRow){
331 this.updateBodyHeight();
332 // first make sure they are deselected
333 this.grid.selModel.deselectRange(firstRow, lastRow);
334 var bt = this.getBodyTable();
335 var rows = []; // get references because the rowIndex will change
336 for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
337 rows.push(bt.childNodes[rowIndex]);
338 }
339 for(var i = 0; i < rows.length; i++){
340 bt.removeChild(rows[i]);
341 rows[i] = null;
342 }
343 rows = null;
344 this.updateRowIndexes(firstRow);
345 this.adjustForScroll();
346 },
347
348 updateRows : function(dataModel, firstRow, lastRow){
349 var bt = this.getBodyTable();
350 var dindexes = this.getDataIndexes();
351 var renderers = this.getColumnRenderers();
352 var colCount = this.grid.colModel.getColumnCount();
353 for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
354 var row = bt.rows[rowIndex];
355 var cells = row.childNodes;
356 for(var colIndex = 0; colIndex < colCount; colIndex++){
357 var td = cells[colIndex];
358 var val = renderers[colIndex](dataModel.getValueAt(rowIndex, dindexes[colIndex]), rowIndex, colIndex, td, dataModel);
359 if(typeof val == 'undefined' || val === '') val = '&#160;';
360 td.firstChild.innerHTML = val;
361 }
362 }
363 },
364
365 handleSort : function(dataModel, sortColumnIndex, sortDir, noRefresh){
366 varselectedRows;
367 this.grid.selModel.syncSelectionsToIds();
368 if(!noRefresh){
369 this.updateRows(dataModel, 0, dataModel.getRowCount()-1);
370 }
371 this.updateHeaderSortState();
372 selectedRows = this.grid.selModel.getSelectedRows();
373 if (selectedRows.length > 0) {
374 this.focusRow(selectedRows[0]);
375 }
376 },
377
378 syncScroll : function(){
379 this.hwrap.dom.scrollLeft = this.wrap.scrollLeft;
380 },
381
382 handleScroll : function(){
383 this.syncScroll();
384 this.fireScroll(this.wrap.scrollLeft, this.wrap.scrollTop);
385 this.grid.fireEvent('bodyscroll', this.wrap.scrollLeft, this.wrap.scrollTop);
386 },
387
388 getRowHeight : function(){
389 if(!this.rowHeight){
390 var rule = YAHOO.ext.util.CSS.getRule(["#" + this.grid.id + " .ygrid-row", ".ygrid-row"]);
391 if(rule && rule.style.height){
392 this.rowHeight = parseInt(rule.style.height, 10);
393 }else{
394 this.rowHeight = 21;
395 }
396 }
397 return this.rowHeight;
398 },
399
400 renderRows : function(dataModel){
401 this.grid.stopEditing();
402 if(this.grid.selModel){
403 this.grid.selModel.clearSelections();
404 }
405 var bt = this.getBodyTable();
406 bt.innerHTML = '';
407 this.rowHeight = this.getRowHeight();
408 this.insertRows(dataModel, 0, dataModel.getRowCount()-1);
409 },
410
411 updateCell : function(dataModel, rowIndex, dataIndex){
412 var colIndex = this.getColumnIndexByDataIndex(dataIndex);
413 if(typeof colIndex == 'undefined'){ // not present in grid
414 return;
415 }
416 var bt = this.getBodyTable();
417 var row = bt.childNodes[rowIndex];
418 var cell = row.childNodes[colIndex];
419 var renderer = this.grid.colModel.getRenderer(colIndex);
420 var val = renderer(dataModel.getValueAt(rowIndex, dataIndex), rowIndex, colIndex, cell, dataModel);
421 if(typeof val == 'undefined' || val === '') val = '&#160;';
422 cell.firstChild.innerHTML = val;
423 },
424
425 calcColumnWidth : function(colIndex, maxRowsToMeasure){
426 var maxWidth = 0;
427 var bt = this.getBodyTable();
428 var rows = bt.childNodes;
429 var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
430 if(this.grid.autoSizeHeaders){
431 var h = this.headers[colIndex];
432 var curWidth = h.style.width;
433 h.style.width = this.grid.minColumnWidth+'px';
434 maxWidth = Math.max(maxWidth, h.scrollWidth);
435 h.style.width = curWidth;
436 }
437 for(var i = 0; i < stopIndex; i++){
438 var cell = rows[i].childNodes[colIndex].firstChild;
439 maxWidth = Math.max(maxWidth, cell.scrollWidth);
440 }
441 return maxWidth + /*margin for error in IE*/ 5;
442 },
443
444 /**
445 * Autofit a column to it's content.
446 * @param {Number} colIndex
447 * @param {Boolean} forceMinSize true to force the column to go smaller if possible
448 */
449 autoSizeColumn : function(colIndex, forceMinSize){
450 if(forceMinSize){
451 this.setCSSWidth(colIndex, this.grid.minColumnWidth);
452 }
453 var newWidth = this.calcColumnWidth(colIndex);
454 this.grid.colModel.setColumnWidth(colIndex,
455 Math.max(this.grid.minColumnWidth, newWidth));
456 this.grid.fireEvent('columnresize', colIndex, newWidth);
457 },
458
459 /**
460 * Autofits all columns to their content and then expands to fit any extra space in the grid
461 */
462 autoSizeColumns : function(){
463 var colModel = this.grid.colModel;
464 var colCount = colModel.getColumnCount();
465 var wrap = this.wrap;
466 for(var i = 0; i < colCount; i++){
467 this.setCSSWidth(i, this.grid.minColumnWidth);
468 colModel.setColumnWidth(i, this.calcColumnWidth(i, this.grid.maxRowsToMeasure), true);
469 }
470 if(colModel.getTotalWidth() < wrap.clientWidth){
471 var diff = Math.floor((wrap.clientWidth - colModel.getTotalWidth()) / colCount);
472 for(var i = 0; i < colCount; i++){
473 colModel.setColumnWidth(i, colModel.getColumnWidth(i) + diff, true);
474 }
475 }
476 this.updateColumns();
477 },
478
479 /**
480 * Autofits all columns to the grid's width proportionate with their current size
481 */
482 fitColumns : function(){
483 var cm = this.grid.colModel;
484 var colCount = cm.getColumnCount();
485 var cols = [];
486 var width = 0;
487 var i, w;
488 for (i = 0; i < colCount; i++){
489 if(!cm.isHidden(i) && !cm.isFixed(i)){
490 w = cm.getColumnWidth(i);
491 cols.push(i);
492 cols.push(w);
493 width += w;
494 }
495 }
496 var frac = (this.wrap.clientWidth - cm.getTotalWidth())/width;
497 while (cols.length){
498 w = cols.pop();
499 i = cols.pop();
500 cm.setColumnWidth(i, Math.floor(w + w*frac), true);
501 }
502 this.updateColumns();
503 },
504
505 onWindowResize : function(){
506 if(this.grid.monitorWindowResize){
507 this.adjustForScroll();
508 this.updateWrapHeight();
509 this.adjustForScroll();
510 }
511 },
512
513 updateWrapHeight : function(){
514 this.grid.container.beginMeasure();
515 this.autoHeight();
516 var box = this.grid.container.getSize(true);
517 this.wrapEl.setHeight(box.height-this.footerHeight-parseInt(this.wrap.offsetTop, 10));
518 this.pwrap.setSize(box.width, box.height);
519 this.grid.container.endMeasure();
520 },
521
522 forceScrollUpdate : function(){
523 var wrap = this.wrapEl;
524 wrap.setWidth(wrap.getWidth(true));
525 setTimeout(function(){ // set timeout so FireFox works
526 wrap.setWidth('');
527 }, 1);
528 },
529
530 updateHeaderSortState : function(){
531 var state = this.grid.dataModel.getSortState();
532 if(!state || typeof state.column == 'undefined') return;
533 var sortColumn = this.getColumnIndexByDataIndex(state.column);
534 var sortDir = state.direction;
535 for(var i = 0, len = this.headers.length; i < len; i++){
536 var h = this.headers[i];
537 if(i != sortColumn){
538 h.sortDesc.style.display = 'none';
539 h.sortAsc.style.display = 'none';
540 YAHOO.util.Dom.removeClass(h, 'ygrid-sort-col');
541 }else{
542 h.sortDesc.style.display = sortDir == 'DESC' ? 'block' : 'none';
543 h.sortAsc.style.display = sortDir == 'ASC' ? 'block' : 'none';
544 YAHOO.util.Dom.addClass(h, 'ygrid-sort-col');
545 }
546 }
547 },
548
549 unplugDataModel : function(dm){
550 dm.removeListener('cellupdated', this.updateCell, this);
551 dm.removeListener('datachanged', this.renderRows, this);
552 dm.removeListener('rowsdeleted', this.deleteRows, this);
553 dm.removeListener('rowsinserted', this.insertRows, this);
554 dm.removeListener('rowsupdated', this.updateRows, this);
555 dm.removeListener('rowssorted', this.handleSort, this);
556 },
557
558 plugDataModel : function(dm){
559 dm.on('cellupdated', this.updateCell, this, true);
560 dm.on('datachanged', this.renderRows, this, true);
561 dm.on('rowsdeleted', this.deleteRows, this, true);
562 dm.on('rowsinserted', this.insertRows, this, true);
563 dm.on('rowsupdated', this.updateRows, this, true);
564 dm.on('rowssorted', this.handleSort, this, true);
565 },
566
567 destroy : function(){
568 this.unplugDataModel(this.grid.dataModel);
569 var sp = this.splitters;
570 if(sp){
571 for(var i in sp){
572 if(sp[i] && typeof sp[i] != 'function'){
573 sp[i].destroy(true);
574 }
575 }
576 }
577 },
578
579 render : function(){
580 var grid = this.grid;
581 var container = grid.container.dom;
582 var dataModel = grid.dataModel;
583 this.plugDataModel(dataModel);
584
585 var colModel = grid.colModel;
586 colModel.onWidthChange.subscribe(this.updateColumns, this, true);
587 colModel.onHeaderChange.subscribe(this.updateHeaders, this, true);
588 colModel.onHiddenChange.subscribe(this.handleHiddenChange, this, true);
589
590 if(grid.monitorWindowResize === true){
591 YAHOO.ext.EventManager.onWindowResize(this.onWindowResize, this, true);
592 }
593 var autoSizeDelegate = this.autoSizeColumn.createDelegate(this);
594
595 var colCount = colModel.getColumnCount();
596
597 var dh = YAHOO.ext.DomHelper;
598 this.pwrap = dh.append(container,
599 {tag: 'div', cls: 'ygrid-positioner',
600 style: 'position:relative;width:100%;height:100%;left:0;top:0;overflow:hidden;'}, true);
601 var pos = this.pwrap.dom;
602
603 //create wrapper elements that handle offsets and scrolling
604 var wrap = dh.append(pos, {tag: 'div', cls: 'ygrid-wrap'});
605 this.wrap = wrap;
606 this.wrapEl = getEl(wrap, true);
607 YAHOO.ext.EventManager.on(wrap, 'scroll', this.handleScroll, this, true);
608
609 var hwrap = dh.append(pos, {tag: 'div', cls: 'ygrid-wrap-headers'});
610 this.hwrap = getEl(hwrap, true);
611
612 var bwrap = dh.append(wrap, {tag: 'div', cls: 'ygrid-wrap-body', id: container.id + '-body'});
613 this.bwrap = getEl(bwrap, true);
614 this.bwrap.setWidth(colModel.getTotalWidth());
615 bwrap.rows = bwrap.childNodes;
616
617 this.footerHeight = 0;
618 var foot = this.appendFooter(this.pwrap.dom);
619 if(foot){
620 this.footer = getEl(foot, true);
621 this.footerHeight = this.footer.getHeight();
622 }
623 this.updateWrapHeight();
624
625 var hrow = dh.append(hwrap, {tag: 'span', cls: 'ygrid-hrow'});
626 this.hrow = hrow;
627
628 if(!YAHOO.ext.util.Browser.isGecko){
629 // IE doesn't like iframes, we will leave this alone
630 var iframe = document.createElement('iframe');
631 iframe.className = 'ygrid-hrow-frame';
632 iframe.frameBorder = 0;
633 iframe.src = YAHOO.ext.SSL_SECURE_URL;
634 hwrap.appendChild(iframe);
635 }
636 this.headerCtrl = new YAHOO.ext.grid.HeaderController(this.grid);
637 this.headers = [];
638 this.cols = [];
639 this.splitters = [];
640
641 var htemplate = dh.createTemplate({
642 tag: 'span', cls: 'ygrid-hd ygrid-header-{0}', children: [{
643 tag: 'span',
644 cls: 'ygrid-hd-body',
645 html: '<table border="0" cellpadding="0" cellspacing="0" title="{2}">' +
646 '<tbody><tr><td><span>{1}</span></td>' +
647 '<td><span class="sort-desc"></span><span class="sort-asc"></span></td>' +
648 '</tr></tbody></table>'
649 }]
650 });
651 htemplate.compile();
652
653 var ruleBuf = [];
654
655 for(var i = 0; i < colCount; i++){
656 var hd = htemplate.append(hrow, [i, colModel.getColumnHeader(i), colModel.getColumnTooltip(i) || '']);
657 var spans = hd.getElementsByTagName('span');
658 hd.textNode = spans[1];
659 hd.sortDesc = spans[2];
660 hd.sortAsc = spans[3];
661 hd.columnIndex = i;
662 this.headers.push(hd);
663 if(colModel.isSortable(i)){
664 this.headerCtrl.register(hd);
665 }
666 var split = dh.append(hrow, {tag: 'span', cls: 'ygrid-hd-split'});
667 hd.split = split;
668
669 if(colModel.isResizable(i) && !colModel.isFixed(i)){
670 YAHOO.util.Event.on(split, 'dblclick', autoSizeDelegate.createCallback(i+0, true));
671 var sb = new YAHOO.ext.SplitBar(split, hd, null, YAHOO.ext.SplitBar.LEFT);
672 sb.columnIndex = i;
673 sb.minSize = grid.minColumnWidth;
674 sb.onMoved.subscribe(this.onColumnSplitterMoved, this, true);
675 YAHOO.util.Dom.addClass(sb.proxy, 'ygrid-column-sizer');
676 YAHOO.util.Dom.setStyle(sb.proxy, 'background-color', '');
677 sb.dd._resizeProxy = function(){
678 var el = this.getDragEl();
679 YAHOO.util.Dom.setStyle(el, 'height', (hwrap.clientHeight+wrap.clientHeight-2) +'px');
680 };
681 this.splitters[i] = sb;
682 }else{
683 split.style.cursor = 'default';
684 }
685 ruleBuf.push('#', container.id, ' .ygrid-col-', i, ' {\n}\n');
686 }
687
688 YAHOO.ext.util.CSS.createStyleSheet(ruleBuf.join(''));
689
690 if(grid.autoSizeColumns){
691 this.renderRows(dataModel);
692 this.autoSizeColumns();
693 }else{
694 this.updateColumns();
695 this.renderRows(dataModel);
696 }
697
698 for(var i = 0; i < colCount; i++){
699 if(colModel.isHidden(i)){
700 this.hideColumn(i);
701 }
702 }
703 this.updateHeaderSortState();
704 return this.bwrap;
705 },
706
707 onColumnSplitterMoved : function(splitter, newSize){
708 this.grid.colModel.setColumnWidth(splitter.columnIndex, newSize);
709 this.grid.fireEvent('columnresize', splitter.columnIndex, newSize);
710 },
711
712 appendFooter : function(parentEl){
713 return null;
714 },
715
716 autoHeight : function(){
717 if(this.grid.autoHeight){
718 var h = this.getBodyHeight();
719 var c = this.grid.container;
720 var total = h + (parseInt(this.wrap.offsetTop, 10)||0) +
721 this.footerHeight + c.getBorderWidth('tb') + c.getPadding('tb')
722 + (this.wrap.offsetHeight - this.wrap.clientHeight);
723 c.setHeight(total);
724
725 }
726 },
727
728 getBodyHeight : function(){
729 return this.grid.dataModel.getRowCount() * this.getRowHeight();;
730 },
731
732 updateBodyHeight : function(){
733 this.getBodyTable().style.height = this.getBodyHeight() + 'px';
734 if(this.grid.autoHeight){
735 this.autoHeight();
736 this.updateWrapHeight();
737 }
738 }
739};
740YAHOO.ext.grid.GridView.SCROLLBARS_UNDER = 0;
741YAHOO.ext.grid.GridView.SCROLLBARS_OVERLAP = 1;
742YAHOO.ext.grid.GridView.prototype.scrollbarMode = YAHOO.ext.grid.GridView.SCROLLBARS_UNDER;
743
744YAHOO.ext.grid.GridView.prototype.fitColumnsToContainer = YAHOO.ext.grid.GridView.prototype.fitColumns;
745
746YAHOO.ext.grid.HeaderController = function(grid){
747 this.grid = grid;
748 this.headers = [];
749};
750
751YAHOO.ext.grid.HeaderController.prototype = {
752 register : function(header){
753 this.headers.push(header);
754 YAHOO.ext.EventManager.on(header, 'selectstart', this.cancelTextSelection, this, true);
755 YAHOO.ext.EventManager.on(header, 'mousedown', this.cancelTextSelection, this, true);
756 YAHOO.ext.EventManager.on(header, 'mouseover', this.headerOver, this, true);
757 YAHOO.ext.EventManager.on(header, 'mouseout', this.headerOut, this, true);
758 YAHOO.ext.EventManager.on(header, 'click', this.headerClick, this, true);
759 },
760
761 headerClick : function(e){
762 var grid = this.grid, cm = grid.colModel, dm = grid.dataModel;
763 grid.stopEditing();
764 var header = grid.getHeaderFromChild(e.getTarget());
765 var state = dm.getSortState();
766 var direction = header.sortDir || 'ASC';
767 if(typeof state.column != 'undefined' &&
768 grid.getView().getColumnIndexByDataIndex(state.column) == header.columnIndex){
769 direction = (state.direction == 'ASC' ? 'DESC' : 'ASC');
770 }
771 header.sortDir = direction;
772 dm.sort(cm, cm.getDataIndex(header.columnIndex), direction);
773 },
774
775 headerOver : function(e){
776 var header = this.grid.getHeaderFromChild(e.getTarget());
777 YAHOO.util.Dom.addClass(header, 'ygrid-hd-over');
778 //YAHOO.ext.util.CSS.applyFirst(header, this.grid.id, '.ygrid-hd-over');
779 },
780
781 headerOut : function(e){
782 var header = this.grid.getHeaderFromChild(e.getTarget());
783 YAHOO.util.Dom.removeClass(header, 'ygrid-hd-over');
784 //YAHOO.ext.util.CSS.revertFirst(header, this.grid.id, '.ygrid-hd-over');
785 },
786
787 cancelTextSelection : function(e){
788 e.preventDefault();
789 }
790}; \ No newline at end of file
diff --git a/frontend/beta/js/YUI-extensions/grid/PagedGridView.js b/frontend/beta/js/YUI-extensions/grid/PagedGridView.js
new file mode 100644
index 0000000..ecaece2
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/grid/PagedGridView.js
@@ -0,0 +1,194 @@
1/**
2 * @class YAHOO.ext.grid.PagedGridView
3 * @extends YAHOO.ext.grid.GridView
4 * Extends the default GridView to add a paging interface.
5 * @constructor
6 * This class is created for you automatically if your data model is set to use paging.
7 */
8YAHOO.ext.grid.PagedGridView = function(){
9 YAHOO.ext.grid.PagedGridView.superclass.constructor.call(this);
10 this.cursor = 1;
11};
12
13YAHOO.extendX(YAHOO.ext.grid.PagedGridView, YAHOO.ext.grid.GridView, {
14 appendFooter : function(parentEl){
15 var fwrap = document.createElement('div');
16 fwrap.className = 'ygrid-wrap-footer';
17 var fbody = document.createElement('span');
18 fbody.className = 'ygrid-footer';
19 fwrap.appendChild(fbody);
20 parentEl.appendChild(fwrap);
21 this.createPagingToolbar(fbody);
22 return fwrap;
23 },
24
25 createPagingToolbar : function(container){
26 var tb = new YAHOO.ext.Toolbar(container);
27 this.pageToolbar = tb;
28 this.first = tb.addButton({
29 tooltip: this.firstText,
30 className: 'ygrid-page-first',
31 disabled: true,
32 click: this.onClick.createDelegate(this, ['first'])
33 });
34 this.prev = tb.addButton({
35 tooltip: this.prevText,
36 className: 'ygrid-page-prev',
37 disabled: true,
38 click: this.onClick.createDelegate(this, ['prev'])
39 });
40 tb.addSeparator();
41 tb.add(this.beforePageText);
42 var pageBox = document.createElement('input');
43 pageBox.type = 'text';
44 pageBox.size = 3;
45 pageBox.value = '1';
46 pageBox.className = 'ygrid-page-number';
47 tb.add(pageBox);
48 this.field = getEl(pageBox, true);
49 this.field.mon('keydown', this.onEnter, this, true);
50 this.field.on('focus', function(){pageBox.select();});
51 this.afterTextEl = tb.addText(this.afterPageText.replace('%0', '1'));
52 this.field.setHeight(18);
53 tb.addSeparator();
54 this.next = tb.addButton({
55 tooltip: this.nextText,
56 className: 'ygrid-page-next',
57 disabled: true,
58 click: this.onClick.createDelegate(this, ['next'])
59 });
60 this.last = tb.addButton({
61 tooltip: this.lastText,
62 className: 'ygrid-page-last',
63 disabled: true,
64 click: this.onClick.createDelegate(this, ['last'])
65 });
66 tb.addSeparator();
67 this.loading = tb.addButton({
68 tooltip: this.refreshText,
69 className: 'ygrid-loading',
70 disabled: true,
71 click: this.onClick.createDelegate(this, ['refresh'])
72 });
73 this.onPageLoaded(1, this.grid.dataModel.getTotalPages());
74 },
75
76 /**
77 * Returns the toolbar used for paging so you can add new buttons.
78 * @return {YAHOO.ext.Toolbar}
79 */
80 getPageToolbar : function(){
81 return this.pageToolbar;
82 },
83
84 onPageLoaded : function(pageNum, totalPages){
85 this.cursor = pageNum;
86 this.lastPage = totalPages;
87 this.afterTextEl.innerHTML = this.afterPageText.replace('%0', totalPages);
88 this.field.dom.value = pageNum;
89 this.first.setDisabled(pageNum == 1);
90 this.prev.setDisabled(pageNum == 1);
91 this.next.setDisabled(pageNum == totalPages);
92 this.last.setDisabled(pageNum == totalPages);
93 this.loading.enable();
94 },
95
96 onLoadError : function(){
97 this.loading.enable();
98 },
99
100 onEnter : function(e){
101 if(e.browserEvent.keyCode == e.RETURN){
102 var v = this.field.dom.value;
103 if(!v){
104 this.field.dom.value = this.cursor;
105 return;
106 }
107 var pageNum = parseInt(v, 10);
108 if(isNaN(pageNum)){
109 this.field.dom.value = this.cursor;
110 return;
111 }
112 pageNum = Math.min(Math.max(1, pageNum), this.lastPage);
113 this.grid.dataModel.loadPage(pageNum);
114 e.stopEvent();
115 }
116 },
117
118 beforeLoad : function(){
119 this.grid.stopEditing();
120 if(this.loading){
121 this.loading.disable();
122 }
123 },
124
125 onClick : function(which){
126 switch(which){
127 case 'first':
128 this.grid.dataModel.loadPage(1);
129 break;
130 case 'prev':
131 this.grid.dataModel.loadPage(this.cursor -1);
132 break;
133 case 'next':
134 this.grid.dataModel.loadPage(this.cursor + 1);
135 break;
136 case 'last':
137 this.grid.dataModel.loadPage(this.lastPage);
138 break;
139 case 'refresh':
140 this.grid.dataModel.loadPage(this.cursor);
141 break;
142 }
143 },
144
145 unplugDataModel : function(dm){
146 dm.removeListener('beforeload', this.beforeLoad, this);
147 dm.removeListener('load', this.onPageLoaded, this);
148 dm.removeListener('loadexception', this.onLoadError, this);
149 YAHOO.ext.grid.PagedGridView.superclass.unplugDataModel.call(this, dm);
150 },
151
152 plugDataModel : function(dm){
153 dm.on('beforeload', this.beforeLoad, this, true);
154 dm.on('load', this.onPageLoaded, this, true);
155 dm.on('loadexception', this.onLoadError, this);
156 YAHOO.ext.grid.PagedGridView.superclass.plugDataModel.call(this, dm);
157 },
158
159 /**
160 * Customizable piece of the default paging text (defaults to "Page")
161 * @type String
162 */
163 beforePageText : "Page",
164 /**
165 * Customizable piece of the default paging text (defaults to "of %0")
166 * @type String
167 */
168 afterPageText : "of %0",
169 /**
170 * Customizable piece of the default paging text (defaults to "First Page")
171 * @type String
172 */
173 firstText : "First Page",
174 /**
175 * Customizable piece of the default paging text (defaults to "Previous Page")
176 * @type String
177 */
178 prevText : "Previous Page",
179 /**
180 * Customizable piece of the default paging text (defaults to "Next Page")
181 * @type String
182 */
183 nextText : "Next Page",
184 /**
185 * Customizable piece of the default paging text (defaults to "Last Page")
186 * @type String
187 */
188 lastText : "Last Page",
189 /**
190 * Customizable piece of the default paging text (defaults to "Refresh")
191 * @type String
192 */
193 refreshText : "Refresh"
194});
diff --git a/frontend/beta/js/YUI-extensions/grid/SelectionModel.js b/frontend/beta/js/YUI-extensions/grid/SelectionModel.js
new file mode 100644
index 0000000..6981440
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/grid/SelectionModel.js
@@ -0,0 +1,445 @@
1/**
2 @class YAHOO.ext.grid.DefaultSelectionModel
3 * @extends YAHOO.ext.util.Observable
4 * The default SelectionModel used by {@link YAHOO.ext.grid.Grid}.
5 It supports multiple selections and keyboard selection/navigation. <br><br>
6 @constructor
7 */
8YAHOO.ext.grid.DefaultSelectionModel = function(){
9 this.selectedRows = [];
10 this.selectedRowIds = [];
11 this.lastSelectedRow = null;
12
13 this.onRowSelect = new YAHOO.util.CustomEvent('SelectionTable.rowSelected');
14 this.onSelectionChange = new YAHOO.util.CustomEvent('SelectionTable.selectionChanged');
15
16 this.events = {
17 /**
18 * @event selectionchange
19 * Fires when the selection changes
20 * @param {SelectionModel} this
21 * @param {Array} rows Array of row elements that are selected
22 * @param {String} ids Array of ids that are selected
23 */
24 'selectionchange' : this.onSelectionChange,
25 /**
26 * @event rowselect
27 * Fires when a row is selected or deselected
28 * @param {SelectionModel} this
29 * @param {HTMLElement} row The row element
30 * @param {Boolean} selected true if the row was selected, false if deselected
31 */
32 'rowselect' : this.onRowSelect
33 };
34
35 this.locked = false;
36};
37
38YAHOO.ext.grid.DefaultSelectionModel.prototype = {
39 /** @ignore Called by the grid automatically. Do not call directly. */
40 init : function(grid){
41 this.grid = grid;
42 this.initEvents();
43 },
44
45 /**
46 * Lock the selections
47 */
48 lock : function(){
49 this.locked = true;
50 },
51
52 /**
53 * Unlock the selections
54 */
55 unlock : function(){
56 this.locked = false;
57 },
58
59 /**
60 * Returns true if the selections are locked
61 * @return {Boolean}
62 */
63 isLocked : function(){
64 return this.locked;
65 },
66
67 /** @ignore */
68 initEvents : function(){
69 if(this.grid.trackMouseOver){
70 this.grid.addListener("mouseover", this.handleOver, this, true);
71 this.grid.addListener("mouseout", this.handleOut, this, true);
72 }
73 this.grid.addListener("rowclick", this.rowClick, this, true);
74 this.grid.addListener("keydown", this.keyDown, this, true);
75 },
76
77 fireEvent : YAHOO.ext.util.Observable.prototype.fireEvent,
78 on : YAHOO.ext.util.Observable.prototype.on,
79 addListener : YAHOO.ext.util.Observable.prototype.addListener,
80 delayedListener : YAHOO.ext.util.Observable.prototype.delayedListener,
81 removeListener : YAHOO.ext.util.Observable.prototype.removeListener,
82 purgeListeners : YAHOO.ext.util.Observable.prototype.purgeListeners,
83 bufferedListener : YAHOO.ext.util.Observable.prototype.bufferedListener,
84
85 /** @ignore Syncs selectedRows with the correct row by looking it up by id.
86 Used after a sort moves data around. */
87 syncSelectionsToIds : function(){
88 if(this.getCount() > 0){
89 var ids = this.selectedRowIds.concat();
90 this.clearSelections();
91 this.selectRowsById(ids, true);
92 }
93 },
94
95 /**
96 * Set the selected rows by their ID(s). IDs must match what is returned by the DataModel getRowId(index).
97 * @param {String/Array} id The id(s) to select
98 * @param {<i>Boolean</i>} keepExisting (optional) True to retain existing selections
99 */
100 selectRowsById : function(id, keepExisting){
101 var rows = this.grid.getRowsById(id);
102 if (!(rows instanceof Array)){
103 this.selectRow(rows, keepExisting);
104 return;
105 }
106 this.selectRows(rows, keepExisting);
107 },
108
109 /**
110 * Gets the number of selected rows.
111 * @return {Number}
112 */
113 getCount : function(){
114 return this.selectedRows.length;
115 },
116
117 /**
118 * Selects the first row in the grid.
119 */
120 selectFirstRow : function(){
121 for(var j = 0; j < this.grid.rows.length; j++){
122 if(this.isSelectable(this.grid.rows[j])){
123 this.focusRow(this.grid.rows[j]);
124 this.setRowState(this.grid.rows[j], true);
125 return;
126 }
127 }
128 },
129
130 /**
131 * Selects the row immediately following the last selected row.
132 * @param {<i>Boolean</i>} keepExisting (optional) True to retain existing selections
133 */
134 selectNext : function(keepExisting){
135 if(this.lastSelectedRow){
136 for(var j = (this.lastSelectedRow.rowIndex+1); j < this.grid.rows.length; j++){
137 var row = this.grid.rows[j];
138 if(this.isSelectable(row)){
139 this.focusRow(row);
140 this.setRowState(row, true, keepExisting);
141 return;
142 }
143 }
144 }
145 },
146
147 /**
148 * Selects the row that precedes the last selected row.
149 * @param {<i>Boolean</i>} keepExisting (optional) True to retain existing selections
150 */
151 selectPrevious : function(keepExisting){
152 if(this.lastSelectedRow){
153 for(var j = (this.lastSelectedRow.rowIndex-1); j >= 0; j--){
154 var row = this.grid.rows[j];
155 if(this.isSelectable(row)){
156 this.focusRow(row);
157 this.setRowState(row, true, keepExisting);
158 return;
159 }
160 }
161 }
162 },
163
164 /**
165 * Returns the selected rows.
166 * @return {Array} Array of DOM row elements
167 */
168 getSelectedRows : function(){
169 return this.selectedRows;
170 },
171
172 /**
173 * Returns the selected row ids.
174 * @return {Array} Array of String ids
175 */
176 getSelectedRowIds : function(){
177 return this.selectedRowIds;
178 },
179
180 /**
181 * Clears all selections.
182 */
183 clearSelections : function(){
184 if(this.isLocked()) return;
185 var oldSelections = this.selectedRows.concat();
186 for(var j = 0; j < oldSelections.length; j++){
187 this.setRowState(oldSelections[j], false);
188 }
189 this.selectedRows = [];
190 this.selectedRowIds = [];
191 },
192
193
194 /**
195 * Selects all rows.
196 */
197 selectAll : function(){
198 if(this.isLocked()) return;
199 this.selectedRows = [];
200 this.selectedRowIds = [];
201 for(var j = 0, len = this.grid.rows.length; j < len; j++){
202 this.setRowState(this.grid.rows[j], true, true);
203 }
204 },
205
206 /**
207 * Returns True if there is a selection.
208 * @return {Boolean}
209 */
210 hasSelection : function(){
211 return this.selectedRows.length > 0;
212 },
213
214 /**
215 * Returns True if the specified row is selected.
216 * @param {HTMLElement} row The row to check
217 * @return {Boolean}
218 */
219 isSelected : function(row){
220 return row && (row.selected === true || row.getAttribute('selected') == 'true');
221 },
222
223 /**
224 * Returns True if the specified row is selectable.
225 * @param {HTMLElement} row The row to check
226 * @return {Boolean}
227 */
228 isSelectable : function(row){
229 return row && row.getAttribute('selectable') != 'false';
230 },
231
232 /** @ignore */
233 rowClick : function(grid, rowIndex, e){
234 if(this.isLocked()) return;
235 var row = grid.getRow(rowIndex);
236 if(this.isSelectable(row)){
237 if(e.shiftKey && this.lastSelectedRow){
238 var lastIndex = this.lastSelectedRow.rowIndex;
239 this.selectRange(this.lastSelectedRow, row, e.ctrlKey);
240 this.lastSelectedRow = this.grid.el.dom.rows[lastIndex];
241 }else{
242 this.focusRow(row);
243 var rowState = e.ctrlKey ? !this.isSelected(row) : true;
244 this.setRowState(row, rowState, e.hasModifier());
245 }
246 }
247 },
248
249 /**
250 * Deprecated. Tries to focus the row and scroll it into view - Use grid.scrollTo or grid.getView().focusRow() instead.
251 * @deprecated
252 * @param {HTMLElement} row The row to focus
253 */
254 focusRow : function(row){
255 this.grid.view.focusRow(row);
256 },
257
258 /**
259 * Selects a row.
260 * @param {Number/HTMLElement} row The row or index of the row to select
261 * @param {<i>Boolean</i>} keepExisting (optional) True to retain existing selections
262 */
263 selectRow : function(row, keepExisting){
264 this.setRowState(this.getRow(row), true, keepExisting);
265 },
266
267 /**
268 * Selects multiple rows.
269 * @param {Array} rows Array of the rows or indexes of the row to select
270 * @param {<i>Boolean</i>} keepExisting (optional) True to retain existing selections
271 */
272 selectRows : function(rows, keepExisting){
273 if(!keepExisting){
274 this.clearSelections();
275 }
276 for(var i = 0; i < rows.length; i++){
277 this.selectRow(rows[i], true);
278 }
279 },
280
281 /**
282 * Deselects a row.
283 * @param {Number/HTMLElement} row The row or index of the row to deselect
284 */
285 deselectRow : function(row){
286 this.setRowState(this.getRow(row), false);
287 },
288
289 /** @ignore */
290 getRow : function(row){
291 if(typeof row == 'number'){
292 row = this.grid.rows[row];
293 }
294 return row;
295 },
296
297 /**
298 * Selects a range of rows. All rows in between startRow and endRow are also selected.
299 * @param {Number/HTMLElement} startRow The row or index of the first row in the range
300 * @param {Number/HTMLElement} endRow The row or index of the last row in the range
301 * @param {<i>Boolean</i>} keepExisting (optional) True to retain existing selections
302 */
303 selectRange : function(startRow, endRow, keepExisting){
304 startRow = this.getRow(startRow);
305 endRow = this.getRow(endRow);
306 this.setRangeState(startRow, endRow, true, keepExisting);
307 },
308
309 /**
310 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
311 * @param {Number/HTMLElement} startRow The row or index of the first row in the range
312 * @param {Number/HTMLElement} endRow The row or index of the last row in the range
313 */
314 deselectRange : function(startRow, endRow){
315 startRow = this.getRow(startRow);
316 endRow = this.getRow(endRow);
317 this.setRangeState(startRow, endRow, false, true);
318 },
319
320 /** @ignore */
321 setRowStateFromChild : function(childEl, selected, keepExisting){
322 var row = this.grid.getRowFromChild(childEl);
323 this.setRowState(row, selected, keepExisting);
324 },
325
326 /** @ignore */
327 setRangeState : function(startRow, endRow, selected, keepExisting){
328 if(this.isLocked()) return;
329 if(!keepExisting){
330 this.clearSelections();
331 }
332 var curRow = startRow;
333 while(curRow.rowIndex != endRow.rowIndex){
334 this.setRowState(curRow, selected, true);
335 curRow = (startRow.rowIndex < endRow.rowIndex ?
336 this.grid.getRowAfter(curRow) : this.grid.getRowBefore(curRow))
337 }
338 this.setRowState(endRow, selected, true);
339 },
340
341 /** @ignore */
342 setRowState : function(row, selected, keepExisting){
343 if(this.isLocked()) return;
344 if(this.isSelectable(row)){
345 if(selected){
346 if(!keepExisting){
347 this.clearSelections();
348 }
349 this.setRowClass(row, 'selected');
350 row.selected = true;
351 this.selectedRows.push(row);
352 this.selectedRowIds.push(this.grid.dataModel.getRowId(row.rowIndex));
353 this.lastSelectedRow = row;
354 }else{
355 this.setRowClass(row, '');
356 row.selected = false;
357 this._removeSelected(row);
358 }
359 this.fireEvent('rowselect', this, row, selected);
360 this.fireEvent('selectionchange', this, this.selectedRows, this.selectedRowIds);
361 }
362 },
363
364 /** @ignore */
365 handleOver : function(e){
366 var row = this.grid.getRowFromChild(e.getTarget());
367 if(this.isSelectable(row) && !this.isSelected(row)){
368 this.setRowClass(row, 'over');
369 }
370 },
371
372 /** @ignore */
373 handleOut : function(e){
374 var row = this.grid.getRowFromChild(e.getTarget());
375 if(this.isSelectable(row) && !this.isSelected(row)){
376 this.setRowClass(row, '');
377 }
378 },
379
380 /** @ignore */
381 keyDown : function(e){
382 if(e.browserEvent.keyCode == e.DOWN){
383 this.selectNext(e.shiftKey);
384 e.preventDefault();
385 }else if(e.browserEvent.keyCode == e.UP){
386 this.selectPrevious(e.shiftKey);
387 e.preventDefault();
388 }
389 },
390
391 /** @ignore */
392 setRowClass : function(row, cssClass){
393 if(this.isSelectable(row)){
394 if(cssClass == 'selected'){
395 YAHOO.util.Dom.removeClass(row, 'ygrid-row-over');
396 YAHOO.util.Dom.addClass(row, 'ygrid-row-selected');
397 }else if(cssClass == 'over'){
398 YAHOO.util.Dom.removeClass(row, 'ygrid-row-selected');
399 YAHOO.util.Dom.addClass(row, 'ygrid-row-over');
400 }else if(cssClass == ''){
401 YAHOO.util.Dom.removeClass(row, 'ygrid-row-selected');
402 YAHOO.util.Dom.removeClass(row, 'ygrid-row-over');
403 }
404 }
405 },
406
407 /** @ignore */
408 _removeSelected : function(row){
409 var sr = this.selectedRows;
410 for (var i = 0; i < sr.length; i++) {
411 if (sr[i] === row){
412 this.selectedRows.splice(i, 1);
413 this.selectedRowIds.splice(i, 1);
414 return;
415 }
416 }
417 }
418};
419
420/**
421 @class YAHOO.ext.grid.SingleSelectionModel
422 @extends YAHOO.ext.grid.DefaultSelectionModel
423 Allows only one row to be selected at a time.
424 @constructor
425 * Create new SingleSelectionModel
426 */
427YAHOO.ext.grid.SingleSelectionModel = function(){
428 YAHOO.ext.grid.SingleSelectionModel.superclass.constructor.call(this);
429};
430
431YAHOO.extendX(YAHOO.ext.grid.SingleSelectionModel, YAHOO.ext.grid.DefaultSelectionModel);
432
433/** @ignore */
434YAHOO.ext.grid.SingleSelectionModel.prototype.setRowState = function(row, selected){
435 YAHOO.ext.grid.SingleSelectionModel.superclass.setRowState.call(this, row, selected, false);
436};
437
438YAHOO.ext.grid.DisableSelectionModel = function(){
439 YAHOO.ext.grid.DisableSelectionModel.superclass.constructor.call(this);
440};
441
442YAHOO.extendX(YAHOO.ext.grid.DisableSelectionModel, YAHOO.ext.grid.DefaultSelectionModel);
443
444YAHOO.ext.grid.DisableSelectionModel.prototype.initEvents = function(){
445};
diff --git a/frontend/beta/js/YUI-extensions/grid/editor/CellEditor.js b/frontend/beta/js/YUI-extensions/grid/editor/CellEditor.js
new file mode 100644
index 0000000..7c51a48
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/grid/editor/CellEditor.js
@@ -0,0 +1,91 @@
1/**
2 * @class YAHOO.ext.grid.CellEditor
3 * Base class for all EditorGrid editors
4 */
5YAHOO.ext.grid.CellEditor = function(element){
6 this.colIndex = null;
7 this.rowIndex = null;
8 this.grid = null;
9 this.editing = false;
10 this.originalValue = null;
11 this.element = getEl(element, true);
12 this.element.addClass('ygrid-editor');
13 this.element.dom.tabIndex = 1;
14 this.initialized = false;
15 this.callback = null;
16};
17
18YAHOO.ext.grid.CellEditor.prototype = {
19 init : function(grid, bodyElement, callback){
20 // there's no way for the grid to know if multiple columns
21 // share the same editor so it will try to initialize the
22 // same one over and over
23 if(this.initialized) return;
24 this.initialized = true;
25 this.callback = callback;
26 this.grid = grid;
27 bodyElement.appendChild(this.element.dom);
28 this.initEvents();
29 },
30
31 initEvents : function(){
32 var stopOnEnter = function(e){
33 if(e.browserEvent.keyCode == e.RETURN){
34 this.stopEditing(true);
35 }else if(e.browserEvent.keyCode == e.ESC){
36 this.setValue(this.originalValue);
37 this.stopEditing(true);
38 }
39 }
40 this.element.mon('keydown', stopOnEnter, this, true);
41 this.element.on('blur', this.stopEditing, this, true);
42 },
43
44 startEditing : function(value, row, cell){
45 this.originalValue = value;
46 this.rowIndex = row.rowIndex;
47 this.colIndex = cell.columnIndex;
48 this.cell = cell;
49 this.setValue(value);
50 var cellbox = getEl(cell, true).getBox();
51 this.fitToCell(cellbox);
52 this.editing = true;
53 this.show();
54 },
55
56 stopEditing : function(focusCell){
57 if(this.editing){
58 this.editing = false;
59 var newValue = this.getValue();
60 this.hide();
61 //if(focusCell){try{this.cell.focus();}catch(e){}}; // try to give the cell focus so keyboard nav still works
62 if(this.originalValue != newValue){
63 this.callback(newValue, this.rowIndex, this.colIndex);
64 }
65 }
66 },
67
68 setValue : function(value){
69 this.element.dom.value = value;
70 },
71
72 getValue : function(){
73 return this.element.dom.value;
74 },
75
76 fitToCell : function(box){
77 this.element.setBox(box, true);
78 },
79
80 show : function(){
81 this.element.show();
82 this.element.focus();
83 },
84
85 hide : function(){
86 try{
87 this.element.dom.blur();
88 }catch(e){}
89 this.element.hide();
90 }
91};
diff --git a/frontend/beta/js/YUI-extensions/grid/editor/CheckboxEditor.js b/frontend/beta/js/YUI-extensions/grid/editor/CheckboxEditor.js
new file mode 100644
index 0000000..681b847
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/grid/editor/CheckboxEditor.js
@@ -0,0 +1,60 @@
1/**
2 * @class YAHOO.ext.grid.CheckboxEditor
3 * @extends YAHOO.ext.grid.CellEditor
4Provides a checkbox for editing boolean values. It currently has no configuration options.<br><br>
5For more information on using this editor, see <a href="http://www.jackslocum.com/yui/2006/09/10/adding-built-in-editing-support-to-the-yahoo-ui-extensions-grid/">this blog post</a>.
6* @constructor
7* Create a new CheckboxEditor
8 */
9YAHOO.ext.grid.CheckboxEditor = function(){
10 var div = document.createElement('span');
11 div.className = 'ygrid-editor ygrid-checkbox-editor';
12 var cb = document.createElement('input');
13 cb.type = 'checkbox';
14 cb.setAttribute('autocomplete', 'off');
15 div.appendChild(cb);
16 document.body.appendChild(div);
17 YAHOO.ext.grid.CheckboxEditor.superclass.constructor.call(this, div);
18 div.tabIndex = '';
19 cb.tabIndex = 1;
20 this.cb = getEl(cb, true);
21};
22
23YAHOO.extendX(YAHOO.ext.grid.CheckboxEditor, YAHOO.ext.grid.CellEditor);
24
25YAHOO.ext.grid.CheckboxEditor.prototype.fitToCell = function(box){
26 this.element.setBox(box, true);
27};
28
29YAHOO.ext.grid.CheckboxEditor.prototype.setValue = function(value){
30 this.cb.dom.checked = (value === true || value === 'true' || value === 1 || value === '1');
31};
32
33YAHOO.ext.grid.CheckboxEditor.prototype.getValue = function(){
34 return this.cb.dom.checked;
35};
36
37YAHOO.ext.grid.CheckboxEditor.prototype.show = function(){
38 this.element.show();
39 this.cb.focus();
40};
41
42YAHOO.ext.grid.CheckboxEditor.prototype.initEvents = function(){
43 var stopOnEnter = function(e){
44 if(e.browserEvent.keyCode == e.RETURN){
45 this.stopEditing(true);
46 }else if(e.browserEvent.keyCode == e.ESC){
47 this.setValue(this.originalValue);
48 this.stopEditing(true);
49 }
50 }
51 this.cb.mon('keydown', stopOnEnter, this, true);
52 this.cb.on('blur', this.stopEditing, this, true);
53};
54
55YAHOO.ext.grid.CheckboxEditor.prototype.hide = function(){
56 try{
57 this.cb.dom.blur();
58 }catch(e){}
59 this.element.hide();
60};
diff --git a/frontend/beta/js/YUI-extensions/grid/editor/DateEditor.js b/frontend/beta/js/YUI-extensions/grid/editor/DateEditor.js
new file mode 100644
index 0000000..303ad2b
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/grid/editor/DateEditor.js
@@ -0,0 +1,268 @@
1/**
2 * @class YAHOO.ext.grid.DateEditor
3 * @extends YAHOO.ext.grid.CellEditor
4Provides a date editor field, and optionally a DatePicker. The DateEditor provides a method to override (showCalendar) if you don't want to use the built in DatePicker control. The reason I chose to use my own DatePicker control rather than the nice YUI Calendar component is my control was very easy to override events to make it work well with the grid. It's also only 5k compressed, while the YUI Calendar is 40k compressed. The DatePicker supports left/right keys to move months, up/down keys to move years and the mouse wheel to quickly go through the months. The DateEditor supports the following configuration options:
5<ul class="list">
6<li><i>format</i> - The date format for the editor. The format is identical to <a href="http://www.php.net/date">PHP date()</a> and text is allowed. Credit for that goes to <a style="font-weight:normal;" href="http://www.xaprb.com/blog/2006/05/14/javascript-date-formatting-benchmarks/">this fantastic date library</a>. This format is for the editor only and doesn't affect the rendering of the cell when not in edit mode. Your rendering function can use any date format it wants.</li>
7<li><i>minValue</i> - The minimum allowed date. Can be either a Javascript date object or a string date in the specified format.</li>
8<li><i>maxValue</i> - The maximum allowed date. Can be either a Javascript date object or a string date in the specified format.</li>
9<li><i>minText</i> - The tooltip to display when the date in the cell is before minValue.</li>
10<li><i>maxText</i> - The tooltip to display when the date in the cell is after maxValue.</li>
11<li><i>invalidText</i> - The text to display when the date in the field is invalid (for example: 02/31/06)</li>
12<li><i>disabledDays</i> - An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday.</li>
13<li><i>disabledDaysText</i> - The tooltip to display when the date in the cell (or DatePicker) falls on a disabled day.</li>
14<li><i>disabledDates</i> - An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular expression so they are very powerful. For example, ["03/08/2003", "09/16/2003"] would disable those dates, but ["03/08", "09/16"] would disable them for every year. If you are using short years, you will want to use ^ to tell the regular expression to only match the beginning like ["^03/08"]. To disable March of 2006: ["03/../2006"] or every March ["^03"]. In order to support regular expressions, if you are using a date format that has "." in it, you will have to escape the dot when restricting dates. For example: ["03\\.08\\.03"].</li>
15<li><i>disabledDatesText</i> - The tooltip to display when the date in the cell (or DatePicker) falls on a disabled date.</li>
16<li><i>allowBlank</i> - True if the cell is allowed to be empty.</li>
17<li><i>blankText</i> - The tooltip (error message) to display when the cell is empty and is not allowed to be.</li>
18<li><i>validator</i> - Any custom validation function you want called. The function must return true if the data is valid or an error message otherwise.</li>
19<li><i>validationDelay</i> - The delay in milliseconds for validation. Each time the user types something the field is validated after a specified delay, setting this value allows you to customize that delay (for example, if your custom validation routine is slow).</li>
20</ul>
21For more information on using this editor, see <a href="http://www.jackslocum.com/yui/2006/09/10/adding-built-in-editing-support-to-the-yahoo-ui-extensions-grid/">this blog post</a>.
22* @constructor
23* Create a new DateEditor
24* @param {Object} config
25 */
26YAHOO.ext.grid.DateEditor = function(config){
27 var div = document.createElement('span');
28 div.className = 'ygrid-editor ygrid-editor-container';
29
30 var element = document.createElement('input');
31 element.type = 'text';
32 element.tabIndex = 1;
33 element.setAttribute('autocomplete', 'off');
34 div.appendChild(element);
35
36 var pick = document.createElement('span');
37 pick.className = 'pick-button';
38 div.appendChild(pick);
39
40 document.body.appendChild(div);
41
42 this.div = getEl(div, true);
43 this.element = getEl(element, true);
44 this.pick = getEl(pick, true);
45
46 this.colIndex = null;
47 this.rowIndex = null;
48 this.grid = null;
49 this.editing = false;
50 this.originalValue = null;
51 this.initialized = false;
52 this.callback = null;
53
54 this.cal = null;
55 this.mouseDownHandler = YAHOO.ext.EventManager.wrap(this.handleMouseDown, this, true);
56
57 YAHOO.ext.util.Config.apply(this, config);
58 if(typeof this.minValue == 'string') this.minValue = this.parseDate(this.minValue);
59 if(typeof this.maxValue == 'string') this.maxValue = this.parseDate(this.maxValue);
60 this.ddMatch = /ddnone/;
61 if(this.disabledDates){
62 var dd = this.disabledDates;
63 var re = "(?:";
64 for(var i = 0; i < dd.length; i++){
65 re += dd[i];
66 if(i != dd.length-1) re += "|";
67 }
68 this.ddMatch = new RegExp(re + ")");
69 }
70};
71
72YAHOO.ext.grid.DateEditor.prototype = {
73 init : function(grid, bodyElement, callback){
74 if(this.initialized) return;
75
76 this.initialized = true;
77 this.callback = callback;
78 this.grid = grid;
79 bodyElement.appendChild(this.div.dom);
80 this.initEvents();
81 },
82
83 initEvents : function(){
84 var stopOnEnter = function(e){
85 if(e.browserEvent.keyCode == e.RETURN){
86 this.stopEditing(true);
87 }else if(e.browserEvent.keyCode == e.ESC){
88 this.setValue(this.originalValue);
89 this.stopEditing(true);
90 }
91 }
92 this.element.mon('keydown', stopOnEnter, this, true);
93 var vtask = new YAHOO.ext.util.DelayedTask(this.validate, this);
94 this.element.mon('keyup', vtask.delay.createDelegate(vtask, [this.validationDelay]));
95 this.pick.on('click', this.showCalendar, this, true);
96 },
97
98 startEditing : function(value, row, cell){
99 this.originalValue = value;
100 this.rowIndex = row.rowIndex;
101 this.colIndex = cell.columnIndex;
102 this.cell = cell;
103 this.setValue(value);
104 this.validate();
105 var cellbox = getEl(cell, true).getBox();
106 this.div.setBox(cellbox, true);
107 this.element.setWidth(cellbox.width-this.pick.getWidth());
108 this.editing = true;
109 YAHOO.util.Event.on(document, "mousedown", this.mouseDownHandler);
110 this.show();
111 },
112
113 stopEditing : function(focusCell){
114 if(this.editing){
115 YAHOO.util.Event.removeListener(document, "mousedown", this.mouseDownHandler);
116 this.editing = false;
117 var newValue = this.getValue();
118 this.hide();
119 //if(focusCell){try{this.cell.focus();}catch(e){}}// try to give the cell focus so keyboard nav still works
120 if(this.originalValue != newValue){
121 this.callback(newValue, this.rowIndex, this.colIndex);
122 }
123 }
124 },
125
126 setValue : function(value){
127 this.element.dom.value = this.formatDate(value);
128 this.validate();
129 },
130
131 getValue : function(){
132 if(!this.validate()){
133 return this.originalValue;
134 }else{
135 var value = this.element.dom.value;
136 if(value.length < 1){
137 return value;
138 } else{
139 return this.parseDate(value);
140 }
141 }
142 },
143
144 show : function() {
145 this.div.show();
146 this.element.focus();
147 this.validate();
148 },
149
150 hide : function(){
151 try{
152 this.element.dom.blur();
153 }catch(e){}
154 this.div.hide();
155 },
156
157 validate : function(){
158 var dom = this.element.dom;
159 var value = dom.value;
160 if(value.length < 1){ // if it's blank
161 if(this.allowBlank){
162 dom.title = '';
163 this.element.removeClass('ygrid-editor-invalid');
164 return true;
165 }else{
166 dom.title = this.blankText;
167 this.element.addClass('ygrid-editor-invalid');
168 return false;
169 }
170 }
171 value = this.parseDate(value);
172 if(!value){
173 dom.title = this.invalidText.replace('%0', dom.value).replace('%1', this.format);
174 this.element.addClass('ygrid-editor-invalid');
175 return false;
176 }
177 var time = value.getTime();
178 if(this.minValue && time < this.minValue.getTime()){
179 dom.title = this.minText.replace('%0', this.formatDate(this.minValue));
180 this.element.addClass('ygrid-editor-invalid');
181 return false;
182 }
183 if(this.maxValue && time > this.maxValue.getTime()){
184 dom.title = this.maxText.replace('%0', this.formatDate(this.maxValue));
185 this.element.addClass('ygrid-editor-invalid');
186 return false;
187 }
188 if(this.disabledDays){
189 var day = value.getDay();
190 for(var i = 0; i < this.disabledDays.length; i++) {
191 if(day === this.disabledDays[i]){
192 dom.title = this.disabledDaysText;
193 this.element.addClass('ygrid-editor-invalid');
194 return false;
195 }
196 }
197 }
198 var fvalue = this.formatDate(value);
199 if(this.ddMatch.test(fvalue)){
200 dom.title = this.disabledDatesText.replace('%0', fvalue);
201 this.element.addClass('ygrid-editor-invalid');
202 return false;
203 }
204 var msg = this.validator(value);
205 if(msg !== true){
206 dom.title = msg;
207 this.element.addClass('ygrid-editor-invalid');
208 return false;
209 }
210 dom.title = '';
211 this.element.removeClass('ygrid-editor-invalid');
212 return true;
213 },
214
215 handleMouseDown : function(e){
216 var t = e.getTarget();
217 var dom = this.div.dom;
218 if(t != dom && !YAHOO.util.Dom.isAncestor(dom, t)){
219 this.stopEditing();
220 }
221 },
222
223 showCalendar : function(value){
224 if(this.cal == null){
225 this.cal = new YAHOO.ext.DatePicker(this.div.dom.parentNode.parentNode);
226 }
227 this.cal.minDate = this.minValue;
228 this.cal.maxDate = this.maxValue;
229 this.cal.disabledDatesRE = this.ddMatch;
230 this.cal.disabledDatesText = this.disabledDatesText;
231 this.cal.disabledDays = this.disabledDays;
232 this.cal.disabledDaysText = this.disabledDaysText;
233 this.cal.format = this.format;
234 if(this.minValue){
235 this.cal.minText = this.minText.replace('%0', this.formatDate(this.minValue));
236 }
237 if(this.maxValue){
238 this.cal.maxText = this.maxText.replace('%0', this.formatDate(this.maxValue));
239 }
240 var r = this.div.getRegion();
241 this.cal.show(r.left, r.bottom, this.getValue(), this.setValue.createDelegate(this));
242 },
243
244 parseDate : function(value){
245 if(!value || value instanceof Date) return value;
246 return Date.parseDate(value, this.format);
247 },
248
249 formatDate : function(date){
250 if(!date || !(date instanceof Date)) return date;
251 return date.format(this.format);
252 }
253};
254
255YAHOO.ext.grid.DateEditor.prototype.format = 'm/d/y';
256YAHOO.ext.grid.DateEditor.prototype.disabledDays = null;
257YAHOO.ext.grid.DateEditor.prototype.disabledDaysText = '';
258YAHOO.ext.grid.DateEditor.prototype.disabledDates = null;
259YAHOO.ext.grid.DateEditor.prototype.disabledDatesText = '';
260YAHOO.ext.grid.DateEditor.prototype.allowBlank = true;
261YAHOO.ext.grid.DateEditor.prototype.minValue = null;
262YAHOO.ext.grid.DateEditor.prototype.maxValue = null;
263YAHOO.ext.grid.DateEditor.prototype.minText = 'The date in this field must be after %0';
264YAHOO.ext.grid.DateEditor.prototype.maxText = 'The date in this field must be before %0';
265YAHOO.ext.grid.DateEditor.prototype.blankText = 'This field cannot be blank';
266YAHOO.ext.grid.DateEditor.prototype.invalidText = '%0 is not a valid date - it must be in the format %1';
267YAHOO.ext.grid.DateEditor.prototype.validationDelay = 200;
268YAHOO.ext.grid.DateEditor.prototype.validator = function(){return true;};
diff --git a/frontend/beta/js/YUI-extensions/grid/editor/NumberEditor.js b/frontend/beta/js/YUI-extensions/grid/editor/NumberEditor.js
new file mode 100644
index 0000000..f74d3d9
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/grid/editor/NumberEditor.js
@@ -0,0 +1,166 @@
1/**
2 * @class YAHOO.ext.grid.NumberEditor
3 * @extends YAHOO.ext.grid.CellEditor
4Provides a masked editor for numeric values. Invalid keys are ignored. It supports the following configuration options:
5<ul class="list">
6<li><i>allowDecimals</i> - True if the cell can have decimal values.</li>
7<li><i>decimalSeparator</i> - Character(s) to allow as the decimal separator.</li>
8<li><i>decimalPrecision</i> - Set the maximum decimal precision.</li>
9<li><i>decimalPrecisionFcn</i> - Define the function to call to remove extra precision (ie. Math.floor, Math.round, Math.ceil or your own function).</li>
10<li><i>allowNegative</i> - True if the cell allows negative values.</li>
11<li><i>selectOnFocus</i> - True to select the text when the editor is activated.</li>
12<li><i>minValue</i> - The minimum value the cell will allow.</li>
13<li><i>maxValue</i> - The maximum value the cell will allow.</li>
14<li><i>minText</i> - The tooltip to display when the value in the cell is below the minimum.</li>
15<li><i>maxText</i> - The tooltip to display when the value in the cell is above the maximum.</li>
16<li><i>nanText</i> - The tooltip to display when the value in the cell is not a valid number (for example, negatives are allowed and the value in the cell is just "-" with no numbers).</li>
17<li><i>allowBlank</i> - True if the cell is allowed to be empty.</li>
18<li><i>blankText</i> - The tooltip (error message) to display when the cell is empty and is not allowed to be.</li>
19<li><i>validator</i> - Any custom validation function you want called. The function must return true if the data is valid or an error message otherwise.</li>
20<li><i>validationDelay</i> - The delay in milliseconds for validation. Each time the user types something the field is validated after a specified delay, setting this value allows you to customize that delay (for example, if your custom validation routine is slow).</li>
21</ul>
22For more information on using this editor, see <a href="http://www.jackslocum.com/yui/2006/09/10/adding-built-in-editing-support-to-the-yahoo-ui-extensions-grid/">this blog post</a>.
23* @constructor
24* Create a new NumberEditor
25* @param {Object} config
26 */
27YAHOO.ext.grid.NumberEditor = function(config){
28 var element = document.createElement('input');
29 element.type = 'text';
30 element.className = 'ygrid-editor ygrid-num-editor';
31 element.setAttribute('autocomplete', 'off');
32 document.body.appendChild(element);
33 YAHOO.ext.grid.NumberEditor.superclass.constructor.call(this, element);
34 YAHOO.ext.util.Config.apply(this, config);
35};
36YAHOO.extendX(YAHOO.ext.grid.NumberEditor, YAHOO.ext.grid.CellEditor);
37
38YAHOO.ext.grid.NumberEditor.prototype.initEvents = function(){
39 var stopOnEnter = function(e){
40 if(e.browserEvent.keyCode == e.RETURN){
41 this.stopEditing(true);
42 }else if(e.browserEvent.keyCode == e.ESC){
43 this.setValue(this.originalValue);
44 this.stopEditing(true);
45 }
46 };
47
48 var allowed = "0123456789";
49 if(this.allowDecimals){
50 allowed += this.decimalSeparator;
51 }
52 if(this.allowNegative){
53 allowed += '-';
54 }
55 var keyPress = function(e){
56 var c = e.getCharCode();
57 if(c != e.BACKSPACE && allowed.indexOf(String.fromCharCode(c)) === -1){
58 e.stopEvent();
59 }
60 };
61 this.element.mon('keydown', stopOnEnter, this, true);
62 var vtask = new YAHOO.ext.util.DelayedTask(this.validate, this);
63 this.element.mon('keyup', vtask.delay.createDelegate(vtask, [this.validationDelay]));
64 this.element.mon('keypress', keyPress, this, true);
65 this.element.on('blur', this.stopEditing, this, true);
66};
67
68YAHOO.ext.grid.NumberEditor.prototype.validate = function(){
69 var dom = this.element.dom;
70 var value = dom.value;
71 if(value.length < 1){ // if it's blank
72 if(this.allowBlank){
73 dom.title = '';
74 this.element.removeClass('ygrid-editor-invalid');
75 return true;
76 }else{
77 dom.title = this.blankText;
78 this.element.addClass('ygrid-editor-invalid');
79 return false;
80 }
81 }
82 if(value.search(/\d+/) === -1){
83 dom.title = this.nanText.replace('%0', value);
84 this.element.addClass('ygrid-editor-invalid');
85 return false;
86 }
87 var num = this.parseValue(value);
88 if(num < this.minValue){
89 dom.title = this.minText.replace('%0', this.minValue);
90 this.element.addClass('ygrid-editor-invalid');
91 return false;
92 }
93 if(num > this.maxValue){
94 dom.title = this.maxText.replace('%0', this.maxValue);
95 this.element.addClass('ygrid-editor-invalid');
96 return false;
97 }
98 var msg = this.validator(value);
99 if(msg !== true){
100 dom.title = msg;
101 this.element.addClass('ygrid-editor-invalid');
102 return false;
103 }
104 dom.title = '';
105 this.element.removeClass('ygrid-editor-invalid');
106 return true;
107};
108
109YAHOO.ext.grid.NumberEditor.prototype.show = function(){
110 this.element.dom.title = '';
111 YAHOO.ext.grid.NumberEditor.superclass.show.call(this);
112 if(this.selectOnFocus){
113 try{
114 this.element.dom.select();
115 }catch(e){}
116 }
117 this.validate(this.element.dom.value);
118};
119
120YAHOO.ext.grid.NumberEditor.prototype.getValue = function(){
121 if(!this.validate()){
122 return this.originalValue;
123 }else{
124 var value = this.element.dom.value;
125 if(value.length < 1){
126 return value;
127 } else{
128 return this.fixPrecision(this.parseValue(value));
129 }
130 }
131};
132YAHOO.ext.grid.NumberEditor.prototype.parseValue = function(value){
133 return parseFloat(new String(value).replace(this.decimalSeparator, '.'));
134};
135
136YAHOO.ext.grid.NumberEditor.prototype.fixPrecision = function(value){
137 if(!this.allowDecimals || this.decimalPrecision == -1 || isNaN(value) || value == 0 || !value){
138 return value;
139 }
140 // this should work but doesn't due to precision error in JS
141 // var scale = Math.pow(10, this.decimalPrecision);
142 // var fixed = this.decimalPrecisionFcn(value * scale);
143 // return fixed / scale;
144 //
145 // so here's our workaround:
146 var scale = Math.pow(10, this.decimalPrecision+1);
147 var fixed = this.decimalPrecisionFcn(value * scale);
148 fixed = this.decimalPrecisionFcn(fixed/10);
149 return fixed / (scale/10);
150};
151
152YAHOO.ext.grid.NumberEditor.prototype.allowBlank = true;
153YAHOO.ext.grid.NumberEditor.prototype.allowDecimals = true;
154YAHOO.ext.grid.NumberEditor.prototype.decimalSeparator = '.';
155YAHOO.ext.grid.NumberEditor.prototype.decimalPrecision = 2;
156YAHOO.ext.grid.NumberEditor.prototype.decimalPrecisionFcn = Math.floor;
157YAHOO.ext.grid.NumberEditor.prototype.allowNegative = true;
158YAHOO.ext.grid.NumberEditor.prototype.selectOnFocus = true;
159YAHOO.ext.grid.NumberEditor.prototype.minValue = Number.NEGATIVE_INFINITY;
160YAHOO.ext.grid.NumberEditor.prototype.maxValue = Number.MAX_VALUE;
161YAHOO.ext.grid.NumberEditor.prototype.minText = 'The minimum value for this field is %0';
162YAHOO.ext.grid.NumberEditor.prototype.maxText = 'The maximum value for this field is %0';
163YAHOO.ext.grid.NumberEditor.prototype.blankText = 'This field cannot be blank';
164YAHOO.ext.grid.NumberEditor.prototype.nanText = '%0 is not a valid number';
165YAHOO.ext.grid.NumberEditor.prototype.validationDelay = 100;
166YAHOO.ext.grid.NumberEditor.prototype.validator = function(){return true;};
diff --git a/frontend/beta/js/YUI-extensions/grid/editor/SelectEditor.js b/frontend/beta/js/YUI-extensions/grid/editor/SelectEditor.js
new file mode 100644
index 0000000..200b8e3
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/grid/editor/SelectEditor.js
@@ -0,0 +1,37 @@
1/**
2 * @class YAHOO.ext.grid.SelectEditor
3 * @extends YAHOO.ext.grid.CellEditor
4Creates an editor out of an existing select field. You can create the select element through DOM in Javascript and pass it to the SelectEditor's constructor <b>or</b> an easier way is like this:
5<br><br>
6Define the select field in your document, giving it the ygrid-editor class.
7<pre><code>
8&lt;select id="light" class="ygrid-editor"&gt;
9 &lt;option value="Shade"&gt;Shade&lt;/option&gt;
10 &lt;option value="Mostly Shady"&gt;Mostly Shady&lt;/option&gt;
11 &lt;option value="Sun or Shade"&gt;Sun or Shade&lt;/option&gt;
12 &lt;option value="Mostly Sunny"&gt;Mostly Sunny&lt;/option&gt;
13 &lt;option value="Sunny"&gt;Sunny&lt;/option&gt;
14&lt;/select&gt;
15</code></pre>
16Create the SelectEditor object, passing in the id of your select field.
17<pre><code>
18var editor = new YAHOO.ext.grid.SelectEditor('light');
19</code></pre>
20For more information on using this editor, see <a href="http://www.jackslocum.com/yui/2006/09/10/adding-built-in-editing-support-to-the-yahoo-ui-extensions-grid/">this blog post</a>.
21* @constructor
22* Create a new SelectEditor
23* @param {HTMLElement/String} element
24 */
25YAHOO.ext.grid.SelectEditor = function(element){
26 element.hideFocus = true;
27 YAHOO.ext.grid.SelectEditor.superclass.constructor.call(this, element);
28 this.element.swallowEvent('click');
29};
30YAHOO.extendX(YAHOO.ext.grid.SelectEditor, YAHOO.ext.grid.CellEditor);
31
32YAHOO.ext.grid.SelectEditor.prototype.fitToCell = function(box){
33 if(YAHOO.ext.util.Browser.isGecko){
34 box.height -= 3;
35 }
36 this.element.setBox(box, true);
37};
diff --git a/frontend/beta/js/YUI-extensions/grid/editor/TextEditor.js b/frontend/beta/js/YUI-extensions/grid/editor/TextEditor.js
new file mode 100644
index 0000000..3c97acd
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/grid/editor/TextEditor.js
@@ -0,0 +1,110 @@
1/**
2 * @class YAHOO.ext.grid.TextEditor
3 * @extends YAHOO.ext.grid.CellEditor
4Provides basic text editing for a cells and supports the following configuration options:
5<ul class="list">
6<li><i>allowBlank</i> - True if the cell is allowed to be empty.</li>
7<li><i>minLength</i> - The minimum length the cell will accept.</li>
8<li><i>maxLength</i> - The maximum length the cell will allow.</li>
9<li><i>minText</i> - The tooltip to display when the length of the value in the cell is below the minimum.</li>
10<li><i>maxText</i> - The tooltip to display when the length of the value in the cell is above the maximum.</li>
11<li><i>selectOnFocus</i> - True to select the text when the editor is activated.</li>
12<li><i>blankText</i> - The tooltip (error message) to display when the cell is empty and is not allowed to be.</li>
13<li><i>regex</i> - A regular expression to match if the value is valid. If the regex.test(value) returns false, the value is considered invalid.</li>
14<li><i>regexText</i> - The tooltip (error message) to display when regex does not match.</li>
15<li><i>validator</i> - Any custom validation function you want called. The function must return true if the data is valid or an error message otherwise.</li>
16<li><i>validationDelay</i> - The delay in milliseconds for validation. Each time the user types something the field is validated after a specified delay, setting this value allows you to customize that delay (for example, if your custom validation routine is slow).</li>
17</ul>
18For more information on using this editor, see <a href="http://www.jackslocum.com/yui/2006/09/10/adding-built-in-editing-support-to-the-yahoo-ui-extensions-grid/">this blog post</a>.
19* @constructor
20* Create a new TextEditor
21* @param {Object} config
22 */
23YAHOO.ext.grid.TextEditor = function(config){
24 var element = document.createElement('input');
25 element.type = 'text';
26 element.className = 'ygrid-editor ygrid-text-editor';
27 element.setAttribute('autocomplete', 'off');
28 document.body.appendChild(element);
29 YAHOO.ext.grid.TextEditor.superclass.constructor.call(this, element);
30 YAHOO.ext.util.Config.apply(this, config);
31};
32YAHOO.extendX(YAHOO.ext.grid.TextEditor, YAHOO.ext.grid.CellEditor);
33
34YAHOO.ext.grid.TextEditor.prototype.validate = function(){
35 var dom = this.element.dom;
36 var value = dom.value;
37 if(value.length < 1){ // if it's blank
38 if(this.allowBlank){
39 dom.title = '';
40 this.element.removeClass('ygrid-editor-invalid');
41 return true;
42 }else{
43 dom.title = this.blankText;
44 this.element.addClass('ygrid-editor-invalid');
45 return false;
46 }
47 }
48 if(value.length < this.minLength){
49 dom.title = this.minText.replace('%0', this.minLength);
50 this.element.addClass('ygrid-editor-invalid');
51 return false;
52 }
53 if(value.length > this.maxLength){
54 dom.title = this.maxText.replace('%0', this.maxLength);
55 this.element.addClass('ygrid-editor-invalid');
56 return false;
57 }
58 var msg = this.validator(value);
59 if(msg !== true){
60 dom.title = msg;
61 this.element.addClass('ygrid-editor-invalid');
62 return false;
63 }
64 if(this.regex && !this.regex.test(value)){
65 dom.title = this.regexText;
66 this.element.addClass('ygrid-editor-invalid');
67 return false;
68 }
69 dom.title = '';
70 this.element.removeClass('ygrid-editor-invalid');
71 return true;
72};
73
74YAHOO.ext.grid.TextEditor.prototype.initEvents = function(){
75 YAHOO.ext.grid.TextEditor.superclass.initEvents.call(this);
76 var vtask = new YAHOO.ext.util.DelayedTask(this.validate, this);
77 this.element.mon('keyup', vtask.delay.createDelegate(vtask, [this.validationDelay]));
78};
79
80YAHOO.ext.grid.TextEditor.prototype.show = function(){
81 this.element.dom.title = '';
82 YAHOO.ext.grid.TextEditor.superclass.show.call(this);
83 this.element.focus();
84 if(this.selectOnFocus){
85 try{
86 this.element.dom.select();
87 }catch(e){}
88 }
89 this.validate(this.element.dom.value);
90};
91
92YAHOO.ext.grid.TextEditor.prototype.getValue = function(){
93 if(!this.validate()){
94 return this.originalValue;
95 }else{
96 return this.element.dom.value;
97 }
98};
99
100YAHOO.ext.grid.TextEditor.prototype.allowBlank = true;
101YAHOO.ext.grid.TextEditor.prototype.minLength = 0;
102YAHOO.ext.grid.TextEditor.prototype.maxLength = Number.MAX_VALUE;
103YAHOO.ext.grid.TextEditor.prototype.minText = 'The minimum length for this field is %0';
104YAHOO.ext.grid.TextEditor.prototype.maxText = 'The maximum length for this field is %0';
105YAHOO.ext.grid.TextEditor.prototype.selectOnFocus = true;
106YAHOO.ext.grid.TextEditor.prototype.blankText = 'This field cannot be blank';
107YAHOO.ext.grid.TextEditor.prototype.validator = function(){return true;};
108YAHOO.ext.grid.TextEditor.prototype.validationDelay = 200;
109YAHOO.ext.grid.TextEditor.prototype.regex = null;
110YAHOO.ext.grid.TextEditor.prototype.regexText = '';
diff --git a/frontend/beta/js/YUI-extensions/layout/BasicLayoutRegion.js b/frontend/beta/js/YUI-extensions/layout/BasicLayoutRegion.js
new file mode 100644
index 0000000..b7ea273
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/layout/BasicLayoutRegion.js
@@ -0,0 +1,265 @@
1/**
2 * @class YAHOO.ext.BasicLayoutRegion
3 * @extends YAHOO.ext.util.Observable
4 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
5 * and does not have a titlebar, tabs or any other features. All it does is size and position
6 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
7 */
8YAHOO.ext.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
9 this.mgr = mgr;
10 this.position = pos;
11 this.events = {
12 /**
13 * @event beforeremove
14 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
15 * @param {YAHOO.ext.LayoutRegion} this
16 * @param {YAHOO.ext.ContentPanel} panel The panel
17 * @param {Object} e The cancel event object
18 */
19 'beforeremove' : true,
20 /**
21 * @event invalidated
22 * Fires when the layout for this region is changed.
23 * @param {YAHOO.ext.LayoutRegion} this
24 */
25 'invalidated' : true,
26 /**
27 * @event visibilitychange
28 * Fires when this region is shown or hidden
29 * @param {YAHOO.ext.LayoutRegion} this
30 * @param {Boolean} visibility true or false
31 */
32 'visibilitychange' : true,
33 /**
34 * @event paneladded
35 * Fires when a panel is added.
36 * @param {YAHOO.ext.LayoutRegion} this
37 * @param {YAHOO.ext.ContentPanel} panel The panel
38 */
39 'paneladded' : true,
40 /**
41 * @event panelremoved
42 * Fires when a panel is removed.
43 * @param {YAHOO.ext.LayoutRegion} this
44 * @param {YAHOO.ext.ContentPanel} panel The panel
45 */
46 'panelremoved' : true,
47 /**
48 * @event collapsed
49 * Fires when this region is collapsed.
50 * @param {YAHOO.ext.LayoutRegion} this
51 */
52 'collapsed' : true,
53 /**
54 * @event expanded
55 * Fires when this region is expanded.
56 * @param {YAHOO.ext.LayoutRegion} this
57 */
58 'expanded' : true,
59 /**
60 * @event panelactivated
61 * Fires when a panel is activated.
62 * @param {YAHOO.ext.LayoutRegion} this
63 * @param {YAHOO.ext.ContentPanel} panel The activated panel
64 */
65 'panelactivated' : true,
66 /**
67 * @event resized
68 * Fires when the user resizes this region.
69 * @param {YAHOO.ext.LayoutRegion} this
70 * @param {Number} newSize The new size (width for east/west, height for north/south)
71 */
72 'resized' : true
73 };
74 /** A collection of panels in this region. @type YAHOO.ext.util.MixedCollection */
75 this.panels = new YAHOO.ext.util.MixedCollection();
76 this.panels.getKey = this.getPanelId.createDelegate(this);
77 this.box = null;
78 this.activePanel = null;
79 if(skipConfig !== true){
80 this.applyConfig(config);
81 }
82};
83
84YAHOO.extendX(YAHOO.ext.BasicLayoutRegion, YAHOO.ext.util.Observable, {
85 getPanelId : function(p){
86 return p.getId();
87 },
88
89 applyConfig : function(config){
90 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
91 this.config = config;
92 },
93
94 /**
95 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
96 * the width, for horizontal (north, south) the height.
97 * @param {Number} newSize The new width or height
98 */
99 resizeTo : function(newSize){
100 if(this.activePanel){
101 var el = this.activePanel.getEl();
102 switch(this.position){
103 case 'east':
104 case 'west':
105 el.setWidth(newSize);
106 this.fireEvent('resized', this, newSize);
107 break;
108 case 'north':
109 case 'south':
110 el.setHeight(newSize);
111 this.fireEvent('resized', this, newSize);
112 break;
113 }
114 }
115 },
116
117 getBox : function(){
118 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
119 },
120
121 getMargins : function(){
122 return this.margins;
123 },
124
125 updateBox : function(box){
126 this.box = box;
127 var el = this.activePanel.getEl();
128 el.dom.style.left = box.x + 'px';
129 el.dom.style.top = box.y + 'px';
130 el.setSize(box.width, box.height);
131 },
132
133 /**
134 * Returns the container element for this region.
135 * @return {YAHOO.ext.Element}
136 */
137 getEl : function(){
138 return this.activePanel;
139 },
140
141 /**
142 * Returns true if this region is currently visible.
143 * @return {Boolean}
144 */
145 isVisible : function(){
146 return this.activePanel ? true : false;
147 },
148
149 setActivePanel : function(panel){
150 panel = this.getPanel(panel);
151 if(this.activePanel && this.activePanel != panel){
152 this.activePanel.setActiveState(false);
153 this.activePanel.getEl().setStyle({left:-10000,right:-10000});
154 }
155 this.activePanel = panel;
156 panel.setActiveState(true);
157 if(this.box){
158 panel.setSize(this.box.width, this.box.height);
159 }
160 this.fireEvent('panelactivated', this, panel);
161 this.fireEvent('invalidated');
162 },
163
164 /**
165 * Show the specified panel.
166 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
167 * @return {YAHOO.ext.ContentPanel} The shown panel or null
168 */
169 showPanel : function(panel){
170 if(panel = this.getPanel(panel)){
171 this.setActivePanel(panel);
172 }
173 return panel;
174 },
175
176 /**
177 * Get the active panel for this region.
178 * @return {YAHOO.ext.ContentPanel} The active panel or null
179 */
180 getActivePanel : function(){
181 return this.activePanel;
182 },
183
184 /**
185 * Add the passed ContentPanel(s)
186 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
187 * @return {YAHOO.ext.ContentPanel} The panel added (if only one was added)
188 */
189 add : function(panel){
190 if(arguments.length > 1){
191 for(var i = 0, len = arguments.length; i < len; i++) {
192 this.add(arguments[i]);
193 }
194 return null;
195 }
196 if(this.hasPanel(panel)){
197 this.showPanel(panel);
198 return panel;
199 }
200 panel.setRegion(this);
201 this.panels.add(panel);
202 panel.getEl().setStyle('position', 'absolute');
203 if(!panel.background){
204 this.setActivePanel(panel);
205 if(this.config.initialSize && this.panels.getCount()==1){
206 this.resizeTo(this.config.initialSize);
207 }
208 }
209 this.fireEvent('paneladded', this, panel);
210 return panel;
211 },
212
213 /**
214 * Returns true if the panel is in this region.
215 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
216 * @return {Boolean}
217 */
218 hasPanel : function(panel){
219 if(typeof panel == 'object'){ // must be panel obj
220 panel = panel.getId();
221 }
222 return this.getPanel(panel) ? true : false;
223 },
224
225 /**
226 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
227 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
228 * @param {Boolean} preservePanel Overrides the config preservePanel option
229 * @return {YAHOO.ext.ContentPanel} The panel that was removed
230 */
231 remove : function(panel, preservePanel){
232 panel = this.getPanel(panel);
233 if(!panel){
234 return null;
235 }
236 var e = {};
237 this.fireEvent('beforeremove', this, panel, e);
238 if(e.cancel === true){
239 return null;
240 }
241 var panelId = panel.getId();
242 this.panels.removeKey(panelId);
243 return panel;
244 },
245
246 /**
247 * Returns the panel specified or null if it's not in this region.
248 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
249 * @return {YAHOO.ext.ContentPanel}
250 */
251 getPanel : function(id){
252 if(typeof id == 'object'){ // must be panel obj
253 return id;
254 }
255 return this.panels.get(id);
256 },
257
258 /**
259 * Returns this regions position (north/south/east/west/center).
260 * @return {String}
261 */
262 getPosition: function(){
263 return this.position;
264 }
265});
diff --git a/frontend/beta/js/YUI-extensions/layout/BorderLayout.js b/frontend/beta/js/YUI-extensions/layout/BorderLayout.js
new file mode 100644
index 0000000..0529c24
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/layout/BorderLayout.js
@@ -0,0 +1,281 @@
1/**
2 * @class YAHOO.ext.BorderLayout
3 * @extends YAHOO.ext.LayoutManager
4 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
5 * please see: <br><br>
6 * <a href="http://www.jackslocum.com/yui/2006/10/19/cross-browser-web-20-layouts-with-yahoo-ui/">Cross Browser Layouts - Part 1</a><br>
7 * <a href="http://www.jackslocum.com/yui/2006/10/28/cross-browser-web-20-layouts-part-2-ajax-feed-viewer-20/">Cross Browser Layouts - Part 2</a><br><br>
8 * Example:
9 <pre><code>
10 var layout = new YAHOO.ext.BorderLayout(document.body, {
11 north: {
12 initialSize: 25,
13 titlebar: false
14 },
15 west: {
16 split:true,
17 initialSize: 200,
18 minSize: 175,
19 maxSize: 400,
20 titlebar: true,
21 collapsible: true
22 },
23 east: {
24 split:true,
25 initialSize: 202,
26 minSize: 175,
27 maxSize: 400,
28 titlebar: true,
29 collapsible: true
30 },
31 south: {
32 split:true,
33 initialSize: 100,
34 minSize: 100,
35 maxSize: 200,
36 titlebar: true,
37 collapsible: true
38 },
39 center: {
40 titlebar: true,
41 autoScroll:true,
42 resizeTabs: true,
43 minTabWidth: 50,
44 preferredTabWidth: 150
45 }
46});
47
48// shorthand
49var CP = YAHOO.ext.ContentPanel;
50
51layout.beginUpdate();
52layout.add('north', new CP('north', 'North'));
53layout.add('south', new CP('south', {title: 'South', closable: true}));
54layout.add('west', new CP('west', {title: 'West'}));
55layout.add('east', new CP('autoTabs', {title: 'Auto Tabs', closable: true}));
56layout.add('center', new CP('center1', {title: 'Close Me', closable: true}));
57layout.add('center', new CP('center2', {title: 'Center Panel', closable: false}));
58layout.getRegion('center').showPanel('center1');
59layout.endUpdate();
60</code></pre>
61* @constructor
62* Create a new BorderLayout
63* @param {String/HTMLElement/Element} container The container this layout is bound to
64* @param {Object} config Configuration options
65 */
66YAHOO.ext.BorderLayout = function(container, config){
67 config = config || {};
68 YAHOO.ext.BorderLayout.superclass.constructor.call(this, container);
69 this.factory = config.factory || YAHOO.ext.BorderLayout.RegionFactory;
70 /**
71 * True to hide the center panel while performing layouts. This helps when the center region contains
72 * heavy components such as a yui-ext grid.
73 * @type Boolean
74 */
75 this.hideOnLayout = config.hideOnLayout || false;
76 for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
77 var target = this.factory.validRegions[i];
78 if(config[target]){
79 this.addRegion(target, config[target]);
80 }
81 }
82 //this.dragOverDelegate = YAHOO.ext.EventManager.wrap(this.onDragOver, this, true);
83};
84
85YAHOO.extendX(YAHOO.ext.BorderLayout, YAHOO.ext.LayoutManager, {
86 /**
87 * Creates and adds a new region if it doesn't already exist.
88 * @param {String} target The target region key (north, south, east, west or center).
89 * @param {Object} config The regions config object
90 * @return {BorderLayoutRegion} The new region
91 */
92 addRegion : function(target, config){
93 if(!this.regions[target]){
94 var r = this.factory.create(target, this, config);
95 this.regions[target] = r;
96 r.on('visibilitychange', this.layout, this, true);
97 r.on('paneladded', this.layout, this, true);
98 r.on('panelremoved', this.layout, this, true);
99 r.on('invalidated', this.layout, this, true);
100 r.on('resized', this.onRegionResized, this, true);
101 r.on('collapsed', this.onRegionCollapsed, this, true);
102 r.on('expanded', this.onRegionExpanded, this, true);
103 }
104 return this.regions[target];
105 },
106
107 /**
108 * Performs a layout update.
109 */
110 layout : function(){
111 if(this.updating) return;
112 //var bench = new YAHOO.ext.util.Bench();
113 //bench.start('Layout...');
114 var size = this.getViewSize();
115 var w = size.width, h = size.height;
116 var centerW = w, centerH = h, centerY = 0, centerX = 0;
117 var x = 0, y = 0;
118
119 var rs = this.regions;
120 var n = rs['north'], s = rs['south'], west = rs['west'], e = rs['east'], c = rs['center'];
121 if(this.hideOnLayout){
122 c.el.setStyle('display', 'none');
123 }
124 if(n && n.isVisible()){
125 var b = n.getBox();
126 var m = n.getMargins();
127 b.width = w - (m.left+m.right);
128 b.x = m.left;
129 b.y = m.top;
130 centerY = b.height + b.y + m.bottom;
131 centerH -= centerY;
132 n.updateBox(this.safeBox(b));
133 }
134 if(s && s.isVisible()){
135 var b = s.getBox();
136 var m = s.getMargins();
137 b.width = w - (m.left+m.right);
138 b.x = m.left;
139 var totalHeight = (b.height + m.top + m.bottom);
140 b.y = h - totalHeight + m.top;
141 centerH -= totalHeight;
142 s.updateBox(this.safeBox(b));
143 }
144 if(west && west.isVisible()){
145 var b = west.getBox();
146 var m = west.getMargins();
147 b.height = centerH - (m.top+m.bottom);
148 b.x = m.left;
149 b.y = centerY + m.top;
150 var totalWidth = (b.width + m.left + m.right);
151 centerX += totalWidth;
152 centerW -= totalWidth;
153 west.updateBox(this.safeBox(b));
154 }
155 if(e && e.isVisible()){
156 var b = e.getBox();
157 var m = e.getMargins();
158 b.height = centerH - (m.top+m.bottom);
159 var totalWidth = (b.width + m.left + m.right);
160 b.x = w - totalWidth + m.left;
161 b.y = centerY + m.top;
162 centerW -= totalWidth;
163 e.updateBox(this.safeBox(b));
164 }
165 if(c){
166 var m = c.getMargins();
167 var centerBox = {
168 x: centerX + m.left,
169 y: centerY + m.top,
170 width: centerW - (m.left+m.right),
171 height: centerH - (m.top+m.bottom)
172 };
173 if(this.hideOnLayout){
174 c.el.setStyle('display', 'block');
175 }
176 c.updateBox(this.safeBox(centerBox));
177 }
178 this.el.repaint();
179 this.fireEvent('layout', this);
180 //bench.stop();
181 //alert(bench.toString());
182 },
183
184 safeBox : function(box){
185 box.width = Math.max(0, box.width);
186 box.height = Math.max(0, box.height);
187 return box;
188 },
189
190 /**
191 * Adds a ContentPanel (or subclass) to this layout.
192 * @param {String} target The target region key (north, south, east, west or center).
193 * @param {YAHOO.ext.ContentPanel} panel The panel to add
194 * @return {YAHOO.ext.ContentPanel} The added panel
195 */
196 add : function(target, panel){
197 target = target.toLowerCase();
198 return this.regions[target].add(panel);
199 },
200
201 /**
202 * Adds a ContentPanel (or subclass) to this layout.
203 * @param {String} target The target region key (north, south, east, west or center).
204 * @param {Number/String/YAHOO.ext.ContentPanel} panel The index, id or panel to remove
205 * @return {YAHOO.ext.ContentPanel} The removed panel
206 */
207 remove : function(target, panel){
208 target = target.toLowerCase();
209 return this.regions[target].remove(panel);
210 },
211
212 /**
213 * Searches all regions for a panel with the specified id
214 * @param {String} panelId
215 * @return {YAHOO.ext.ContentPanel} The panel or null if it wasn't found
216 */
217 findPanel : function(panelId){
218 var rs = this.regions;
219 for(var target in rs){
220 if(typeof rs[target] != 'function'){
221 var p = rs[target].getPanel(panelId);
222 if(p){
223 return p;
224 }
225 }
226 }
227 return null;
228 },
229
230 /**
231 * Searches all regions for a panel with the specified id and activates (shows) it.
232 * @param {String/ContentPanel} panelId The panels id or the panel itself
233 * @return {YAHOO.ext.ContentPanel} The shown panel or null
234 */
235 showPanel : function(panelId) {
236 var rs = this.regions;
237 for(var target in rs){
238 var r = rs[target];
239 if(typeof r != 'function'){
240 if(r.hasPanel(panelId)){
241 return r.showPanel(panelId);
242 }
243 }
244 }
245 return null;
246 },
247
248 /**
249 * Restores this layouts state using YAHOO.ext.state.Manager or the state provided by the passed provider.
250 * @param {YAHOO.ext.state.Provider} provider (optional) An alternate state provider
251 */
252 restoreState : function(provider){
253 if(!provider){
254 provider = YAHOO.ext.state.Manager;
255 }
256 var sm = new YAHOO.ext.LayoutStateManager();
257 sm.init(this, provider);
258 }
259});
260
261YAHOO.ext.BorderLayout.RegionFactory = {};
262YAHOO.ext.BorderLayout.RegionFactory.validRegions = ['north','south','east','west','center'];
263YAHOO.ext.BorderLayout.RegionFactory.create = function(target, mgr, config){
264 target = target.toLowerCase();
265 if(config.lightweight || config.basic){
266 return new YAHOO.ext.BasicLayoutRegion(mgr, config, target);
267 }
268 switch(target){
269 case 'north':
270 return new YAHOO.ext.NorthLayoutRegion(mgr, config);
271 case 'south':
272 return new YAHOO.ext.SouthLayoutRegion(mgr, config);
273 case 'east':
274 return new YAHOO.ext.EastLayoutRegion(mgr, config);
275 case 'west':
276 return new YAHOO.ext.WestLayoutRegion(mgr, config);
277 case 'center':
278 return new YAHOO.ext.CenterLayoutRegion(mgr, config);
279 }
280 throw 'Layout region "'+target+'" not supported.';
281};
diff --git a/frontend/beta/js/YUI-extensions/layout/BorderLayoutRegions.js b/frontend/beta/js/YUI-extensions/layout/BorderLayoutRegions.js
new file mode 100644
index 0000000..9b4a09f
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/layout/BorderLayoutRegions.js
@@ -0,0 +1,207 @@
1/*
2 * These classes are private internal classes
3 */
4YAHOO.ext.CenterLayoutRegion = function(mgr, config){
5 YAHOO.ext.CenterLayoutRegion.superclass.constructor.call(this, mgr, config, 'center');
6 this.visible = true;
7 this.minWidth = config.minWidth || 20;
8 this.minHeight = config.minHeight || 20;
9};
10
11YAHOO.extendX(YAHOO.ext.CenterLayoutRegion, YAHOO.ext.LayoutRegion, {
12 hide : function(){
13 // center panel can't be hidden
14 },
15
16 show : function(){
17 // center panel can't be hidden
18 },
19
20 getMinWidth: function(){
21 return this.minWidth;
22 },
23
24 getMinHeight: function(){
25 return this.minHeight;
26 }
27});
28
29
30YAHOO.ext.NorthLayoutRegion = function(mgr, config){
31 YAHOO.ext.NorthLayoutRegion.superclass.constructor.call(this, mgr, config, 'north', 'n-resize');
32 if(this.split){
33 this.split.placement = YAHOO.ext.SplitBar.TOP;
34 this.split.orientation = YAHOO.ext.SplitBar.VERTICAL;
35 this.split.el.addClass('ylayout-split-v');
36 }
37 if(typeof config.initialSize != 'undefined'){
38 this.el.setHeight(config.initialSize);
39 }
40};
41YAHOO.extendX(YAHOO.ext.NorthLayoutRegion, YAHOO.ext.SplitLayoutRegion, {
42 getBox : function(){
43 if(this.collapsed){
44 return this.collapsedEl.getBox();
45 }
46 var box = this.el.getBox();
47 if(this.split){
48 box.height += this.split.el.getHeight();
49 }
50 return box;
51 },
52
53 updateBox : function(box){
54 if(this.split && !this.collapsed){
55 box.height -= this.split.el.getHeight();
56 this.split.el.setLeft(box.x);
57 this.split.el.setTop(box.y+box.height);
58 this.split.el.setWidth(box.width);
59 }
60 if(this.collapsed){
61 this.el.setWidth(box.width);
62 var bodyWidth = box.width - this.el.getBorderWidth('rl');
63 this.bodyEl.setWidth(bodyWidth);
64 if(this.activePanel && this.panelSize){
65 this.activePanel.setSize(bodyWidth, this.panelSize.height);
66 }
67 }
68 YAHOO.ext.NorthLayoutRegion.superclass.updateBox.call(this, box);
69 }
70});
71
72YAHOO.ext.SouthLayoutRegion = function(mgr, config){
73 YAHOO.ext.SouthLayoutRegion.superclass.constructor.call(this, mgr, config, 'south', 's-resize');
74 if(this.split){
75 this.split.placement = YAHOO.ext.SplitBar.BOTTOM;
76 this.split.orientation = YAHOO.ext.SplitBar.VERTICAL;
77 this.split.el.addClass('ylayout-split-v');
78 }
79 if(typeof config.initialSize != 'undefined'){
80 this.el.setHeight(config.initialSize);
81 }
82};
83YAHOO.extendX(YAHOO.ext.SouthLayoutRegion, YAHOO.ext.SplitLayoutRegion, {
84 getBox : function(){
85 if(this.collapsed){
86 return this.collapsedEl.getBox();
87 }
88 var box = this.el.getBox();
89 if(this.split){
90 var sh = this.split.el.getHeight();
91 box.height += sh;
92 box.y -= sh;
93 }
94 return box;
95 },
96
97 updateBox : function(box){
98 if(this.split && !this.collapsed){
99 var sh = this.split.el.getHeight();
100 box.height -= sh;
101 box.y += sh;
102 this.split.el.setLeft(box.x);
103 this.split.el.setTop(box.y-sh);
104 this.split.el.setWidth(box.width);
105 }
106 if(this.collapsed){
107 this.el.setWidth(box.width);
108 var bodyWidth = box.width - this.el.getBorderWidth('rl');
109 this.bodyEl.setWidth(bodyWidth);
110 if(this.activePanel && this.panelSize){
111 this.activePanel.setSize(bodyWidth, this.panelSize.height);
112 }
113 }
114 YAHOO.ext.SouthLayoutRegion.superclass.updateBox.call(this, box);
115 }
116});
117
118YAHOO.ext.EastLayoutRegion = function(mgr, config){
119 YAHOO.ext.EastLayoutRegion.superclass.constructor.call(this, mgr, config, 'east', 'e-resize');
120 if(this.split){
121 this.split.placement = YAHOO.ext.SplitBar.RIGHT;
122 this.split.orientation = YAHOO.ext.SplitBar.HORIZONTAL;
123 this.split.el.addClass('ylayout-split-h');
124 }
125 if(typeof config.initialSize != 'undefined'){
126 this.el.setWidth(config.initialSize);
127 }
128};
129YAHOO.extendX(YAHOO.ext.EastLayoutRegion, YAHOO.ext.SplitLayoutRegion, {
130 getBox : function(){
131 if(this.collapsed){
132 return this.collapsedEl.getBox();
133 }
134 var box = this.el.getBox();
135 if(this.split){
136 var sw = this.split.el.getWidth();
137 box.width += sw;
138 box.x -= sw;
139 }
140 return box;
141 },
142
143 updateBox : function(box){
144 if(this.split && !this.collapsed){
145 var sw = this.split.el.getWidth();
146 box.width -= sw;
147 this.split.el.setLeft(box.x);
148 this.split.el.setTop(box.y);
149 this.split.el.setHeight(box.height);
150 box.x += sw;
151 }
152 if(this.collapsed){
153 this.el.setHeight(box.height);
154 var bodyHeight = this.config.titlebar ? box.height - (this.titleEl.getHeight()||0) : box.height;
155 bodyHeight -= this.el.getBorderWidth('tb');
156 this.bodyEl.setHeight(bodyHeight);
157 if(this.activePanel && this.panelSize){
158 this.activePanel.setSize(this.panelSize.width, bodyHeight);
159 }
160 }
161 YAHOO.ext.EastLayoutRegion.superclass.updateBox.call(this, box);
162 }
163});
164
165YAHOO.ext.WestLayoutRegion = function(mgr, config){
166 YAHOO.ext.WestLayoutRegion.superclass.constructor.call(this, mgr, config, 'west', 'w-resize');
167 if(this.split){
168 this.split.placement = YAHOO.ext.SplitBar.LEFT;
169 this.split.orientation = YAHOO.ext.SplitBar.HORIZONTAL;
170 this.split.el.addClass('ylayout-split-h');
171 }
172 if(typeof config.initialSize != 'undefined'){
173 this.el.setWidth(config.initialSize);
174 }
175};
176YAHOO.extendX(YAHOO.ext.WestLayoutRegion, YAHOO.ext.SplitLayoutRegion, {
177 getBox : function(){
178 if(this.collapsed){
179 return this.collapsedEl.getBox();
180 }
181 var box = this.el.getBox();
182 if(this.split){
183 box.width += this.split.el.getWidth();
184 }
185 return box;
186 },
187
188 updateBox : function(box){
189 if(this.split && !this.collapsed){
190 var sw = this.split.el.getWidth();
191 box.width -= sw;
192 this.split.el.setLeft(box.x+box.width);
193 this.split.el.setTop(box.y);
194 this.split.el.setHeight(box.height);
195 }
196 if(this.collapsed){
197 this.el.setHeight(box.height);
198 var bodyHeight = this.config.titlebar ? box.height - (this.titleEl.getHeight()||0) : box.height;
199 bodyHeight -= this.el.getBorderWidth('tb');
200 this.bodyEl.setHeight(bodyHeight);
201 if(this.activePanel && this.panelSize){
202 this.activePanel.setSize(this.panelSize.width, bodyHeight);
203 }
204 }
205 YAHOO.ext.WestLayoutRegion.superclass.updateBox.call(this, box);
206 }
207});
diff --git a/frontend/beta/js/YUI-extensions/layout/ContentPanels.js b/frontend/beta/js/YUI-extensions/layout/ContentPanels.js
new file mode 100644
index 0000000..7cfdde7
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/layout/ContentPanels.js
@@ -0,0 +1,325 @@
1/**
2 * @class YAHOO.ext.ContentPanel
3 * @extends YAHOO.ext.util.Observable
4 * A basic ContentPanel element.
5 * @cfg {Boolean} fitToFrame True for this panel to manually adjust it's size when the region resizes (defaults to false)
6 * @cfg {Boolean/Object} autoCreate True to auto generate the DOM element for this panel, or a DomHelper config of the element to create
7 * @cfg {Boolean} closable True if the panel can be closed/removed
8 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
9 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if fitToFrame is true (instead of this panel's element)
10 * @cfg {Toolbar} toolbar A toolbar for this panel
11 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with fitToFrame)
12 * @cfg {String} title The title for this panel
13 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a fitToFrame (default is [0, 0])
14 * @constructor
15 * Create a new ContentPanel.
16 * @param {String/HTMLElement/Element} el The container element for this panel
17 * @param {String/Object} config A string to set only the title or a config object
18 * @param {String} content (optional) Set the HTML content for this panel
19 */
20YAHOO.ext.ContentPanel = function(el, config, content){
21 YAHOO.ext.ContentPanel.superclass.constructor.call(this);
22 this.el = getEl(el, true);
23 if(!this.el && config && config.autoCreate){
24 if(typeof config.autoCreate == 'object'){
25 if(!config.autoCreate.id){
26 config.autoCreate.id = el;
27 }
28 this.el = YAHOO.ext.DomHelper.append(document.body,
29 config.autoCreate, true);
30 }else{
31 this.el = YAHOO.ext.DomHelper.append(document.body,
32 {tag: 'div', cls: 'ylayout-inactive-content', id: el}, true);
33 }
34 }
35 this.closable = false;
36 this.loaded = false;
37 this.active = false;
38 if(typeof config == 'string'){
39 this.title = config;
40 }else{
41 YAHOO.ext.util.Config.apply(this, config);
42 }
43 if(this.resizeEl){
44 this.resizeEl = getEl(this.resizeEl, true);
45 }else{
46 this.resizeEl = this.el;
47 }
48 this.events = {
49 /**
50 * @event activate
51 * Fires when this panel is activated.
52 * @param {YAHOO.ext.ContentPanel} this
53 */
54 'activate' : new YAHOO.util.CustomEvent('activate'),
55 /**
56 * @event deactivate
57 * Fires when this panel is activated.
58 * @param {YAHOO.ext.ContentPanel} this
59 */
60 'deactivate' : new YAHOO.util.CustomEvent('deactivate')
61 };
62 if(this.autoScroll){
63 this.resizeEl.setStyle('overflow', 'auto');
64 }
65 if(content){
66 this.setContent(content);
67 }
68};
69
70YAHOO.extendX(YAHOO.ext.ContentPanel, YAHOO.ext.util.Observable, {
71 setRegion : function(region){
72 this.region = region;
73 if(region){
74 this.el.replaceClass('ylayout-inactive-content', 'ylayout-active-content');
75 }else{
76 this.el.replaceClass('ylayout-active-content', 'ylayout-inactive-content');
77 }
78 },
79
80 /**
81 * Returns the toolbar for this Panel if one was configured
82 * @return {YAHOO.ext.Toolbar}
83 */
84 getToolbar : function(){
85 return this.toolbar;
86 },
87
88 setActiveState : function(active){
89 this.active = active;
90 if(!active){
91 this.fireEvent('deactivate', this);
92 }else{
93 this.fireEvent('activate', this);
94 }
95 },
96 /**
97 * Updates this panel's element
98 * @param {String} content The new content
99 * @param {<i>Boolean</i>} loadScripts (optional) true to look for and process scripts
100 */
101 setContent : function(content, loadScripts){
102 this.el.update(content, loadScripts);
103 },
104
105 /**
106 * Get the {@link YAHOO.ext.UpdateManager} for this panel. Enables you to perform Ajax updates.
107 * @return {YAHOO.ext.UpdateManager} The UpdateManager
108 */
109 getUpdateManager : function(){
110 return this.el.getUpdateManager();
111 },
112
113 /**
114 * Set a URL to be used to load the content for this panel.
115 * @param {String/Function} url The url to load the content from or a function to call to get the url
116 * @param {<i>String/Object</i>} params (optional) The string params for the update call or an object of the params. See {@link YAHOO.ext.UpdateManager#update} for more details. (Defaults to null)
117 * @param {<i>Boolean</i>} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this panel is activated. (Defaults to false)
118 * @return {YAHOO.ext.UpdateManager} The UpdateManager
119 */
120 setUrl : function(url, params, loadOnce){
121 if(this.refreshDelegate){
122 this.removeListener('activate', this.refreshDelegate);
123 }
124 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
125 this.on('activate', this._handleRefresh.createDelegate(this, [url, params, loadOnce]));
126 return this.el.getUpdateManager();
127 },
128
129 _handleRefresh : function(url, params, loadOnce){
130 if(!loadOnce || !this.loaded){
131 var updater = this.el.getUpdateManager();
132 updater.update(url, params, this._setLoaded.createDelegate(this));
133 }
134 },
135
136 _setLoaded : function(){
137 this.loaded = true;
138 },
139
140 /**
141 * Returns this panel's id
142 * @return {String}
143 */
144 getId : function(){
145 return this.el.id;
146 },
147
148 /**
149 * Returns this panel's element
150 * @return {YAHOO.ext.Element}
151 */
152 getEl : function(){
153 return this.el;
154 },
155
156 adjustForComponents : function(width, height){
157 if(this.toolbar){
158 var te = this.toolbar.getEl();
159 height -= te.getHeight();
160 te.setWidth(width);
161 }
162 if(this.adjustments){
163 width += this.adjustments[0];
164 height += this.adjustments[1];
165 }
166 return {'width': width, 'height': height};
167 },
168
169 setSize : function(width, height){
170 if(this.fitToFrame){
171 var size = this.adjustForComponents(width, height);
172 this.resizeEl.setSize(this.autoWidth ? 'auto' : size.width, size.height);
173 }
174 },
175
176 /**
177 * Returns this panel's title
178 * @return {String}
179 */
180 getTitle : function(){
181 return this.title;
182 },
183
184 /**
185 * Set this panel's title
186 * @param {String} title
187 */
188 setTitle : function(title){
189 this.title = title;
190 if(this.region){
191 this.region.updatePanelTitle(this, title);
192 }
193 },
194
195 /**
196 * Returns true is this panel was configured to be closable
197 * @return {Boolean}
198 */
199 isClosable : function(){
200 return this.closable;
201 },
202
203 beforeSlide : function(){
204 this.el.clip();
205 this.resizeEl.clip();
206 },
207
208 afterSlide : function(){
209 this.el.unclip();
210 this.resizeEl.unclip();
211 },
212
213 /**
214 * Force a content refresh from the URL specified in the setUrl() method.
215 * Will fail silently if the setUrl method has not been called.
216 * This does not activate the panel, just updates its content.
217 */
218 refresh : function(){
219 if(this.refreshDelegate){
220 this.loaded = false;
221 this.refreshDelegate();
222 }
223 },
224
225 /**
226 * Destroys this panel
227 */
228 destroy : function(){
229 this.el.removeAllListeners();
230 var tempEl = document.createElement('span');
231 tempEl.appendChild(this.el.dom);
232 tempEl.innerHTML = '';
233 this.el = null;
234 }
235});
236
237/**
238 * @class YAHOO.ext.GridPanel
239 * @extends YAHOO.ext.ContentPanel
240 * @constructor
241 * Create a new GridPanel.
242 * @param {YAHOO.ext.grid.Grid} grid The grid for this panel
243 * @param {String/Object} config A string to set only the title or a config object
244 */
245YAHOO.ext.GridPanel = function(grid, config){
246 this.wrapper = YAHOO.ext.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
247 {tag: 'div', cls: 'ylayout-grid-wrapper ylayout-inactive-content'}, true);
248 this.wrapper.dom.appendChild(grid.container.dom);
249 YAHOO.ext.GridPanel.superclass.constructor.call(this, this.wrapper, config);
250 if(this.toolbar){
251 this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
252 }
253 grid.monitorWindowResize = false; // turn off autosizing
254 grid.autoHeight = false;
255 grid.autoWidth = false;
256 this.grid = grid;
257 this.grid.container.replaceClass('ylayout-inactive-content', 'ylayout-component-panel');
258};
259
260YAHOO.extendX(YAHOO.ext.GridPanel, YAHOO.ext.ContentPanel, {
261 getId : function(){
262 return this.grid.id;
263 },
264
265 /**
266 * Returns the grid for this panel
267 * @return {YAHOO.ext.grid.Grid}
268 */
269 getGrid : function(){
270 return this.grid;
271 },
272
273 setSize : function(width, height){
274 var grid = this.grid;
275 var size = this.adjustForComponents(width, height);
276 grid.container.setSize(size.width, size.height);
277 grid.autoSize();
278 },
279
280 beforeSlide : function(){
281 this.grid.getView().wrapEl.clip();
282 },
283
284 afterSlide : function(){
285 this.grid.getView().wrapEl.unclip();
286 },
287
288 destroy : function(){
289 this.grid.getView().unplugDataModel(this.grid.getDataModel());
290 this.grid.container.removeAllListeners();
291 YAHOO.ext.GridPanel.superclass.destroy.call(this);
292 }
293});
294
295
296/**
297 * @class YAHOO.ext.NestedLayoutPanel
298 * @extends YAHOO.ext.ContentPanel
299 * @constructor
300 * Create a new NestedLayoutPanel.
301 * @param {YAHOO.ext.BorderLayout} layout The layout for this panel
302 * @param {String/Object} config A string to set only the title or a config object
303 */
304YAHOO.ext.NestedLayoutPanel = function(layout, config){
305 YAHOO.ext.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
306 layout.monitorWindowResize = false; // turn off autosizing
307 this.layout = layout;
308 this.layout.getEl().addClass('ylayout-nested-layout');
309};
310
311YAHOO.extendX(YAHOO.ext.NestedLayoutPanel, YAHOO.ext.ContentPanel, {
312 setSize : function(width, height){
313 var size = this.adjustForComponents(width, height);
314 this.layout.getEl().setSize(size.width, size.height);
315 this.layout.layout();
316 },
317
318 /**
319 * Returns the nested BorderLayout for this panel
320 * @return {YAHOO.ext.BorderLayout}
321 */
322 getLayout : function(){
323 return this.layout;
324 }
325});
diff --git a/frontend/beta/js/YUI-extensions/layout/LayoutManager.js b/frontend/beta/js/YUI-extensions/layout/LayoutManager.js
new file mode 100644
index 0000000..c59bf0e
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/layout/LayoutManager.js
@@ -0,0 +1,135 @@
1/**
2 * @class YAHOO.ext.LayoutManager
3 * @extends YAHOO.ext.util.Observable
4 * Base class for layout managers.
5 */
6YAHOO.ext.LayoutManager = function(container){
7 YAHOO.ext.LayoutManager.superclass.constructor.call(this);
8 this.el = getEl(container, true);
9 // ie scrollbar fix
10 if(this.el.dom == document.body && YAHOO.ext.util.Browser.isIE){
11 document.body.scroll = 'no';
12 }
13 this.id = this.el.id;
14 this.el.addClass('ylayout-container');
15 /** false to disable window resize monitoring @type Boolean */
16 this.monitorWindowResize = true;
17 this.regions = {};
18 this.events = {
19 /**
20 * @event layout
21 * Fires when a layout is performed.
22 * @param {YAHOO.ext.LayoutManager} this
23 */
24 'layout' : new YAHOO.util.CustomEvent(),
25 /**
26 * @event regionresized
27 * Fires when the user resizes a region.
28 * @param {YAHOO.ext.LayoutRegion} region
29 * @param {Number} newSize The new size (width for east/west, height for north/south)
30 */
31 'regionresized' : new YAHOO.util.CustomEvent(),
32 /**
33 * @event regioncollapsed
34 * Fires when a region is collapsed.
35 * @param {YAHOO.ext.LayoutRegion} region
36 */
37 'regioncollapsed' : new YAHOO.util.CustomEvent(),
38 /**
39 * @event regionexpanded
40 * Fires when a region is expanded.
41 * @param {YAHOO.ext.LayoutRegion} region
42 */
43 'regionexpanded' : new YAHOO.util.CustomEvent()
44 };
45 this.updating = false;
46 YAHOO.ext.EventManager.onWindowResize(this.onWindowResize, this, true);
47};
48
49YAHOO.extendX(YAHOO.ext.LayoutManager, YAHOO.ext.util.Observable, {
50 /**
51 * Returns true if this layout is currently being updated
52 * @return {Boolean}
53 */
54 isUpdating : function(){
55 return this.updating;
56 },
57
58 /**
59 * Suspend the LayoutManager from doing auto-layouts while
60 * making multiple add or remove calls
61 */
62 beginUpdate : function(){
63 this.updating = true;
64 },
65
66 /**
67 * Restore auto-layouts and optionally disable the manager from performing a layout
68 * @param {Boolean} noLayout true to disable a layout update
69 */
70 endUpdate : function(noLayout){
71 this.updating = false;
72 if(!noLayout){
73 this.layout();
74 }
75 },
76
77 layout: function(){
78
79 },
80
81 onRegionResized : function(region, newSize){
82 this.fireEvent('regionresized', region, newSize);
83 this.layout();
84 },
85
86 onRegionCollapsed : function(region){
87 this.fireEvent('regioncollapsed', region);
88 },
89
90 onRegionExpanded : function(region){
91 this.fireEvent('regionexpanded', region);
92 },
93
94 /**
95 * Returns the size of the current view, This method normalizes document.body and element embedded layouts and
96 * performs box-model adjustments.
97 * @return {Object} The size as an object {width: (the width), height: (the height)}
98 */
99 getViewSize : function(){
100 var size;
101 if(this.el.dom != document.body){
102 this.el.beginMeasure();
103 size = this.el.getSize();
104 this.el.endMeasure();
105 }else{
106 size = {width: YAHOO.util.Dom.getViewportWidth(), height: YAHOO.util.Dom.getViewportHeight()};
107 }
108 size.width -= this.el.getBorderWidth('lr')-this.el.getPadding('lr');
109 size.height -= this.el.getBorderWidth('tb')-this.el.getPadding('tb');
110 return size;
111 },
112
113 /**
114 * Returns the element this layout is bound to.
115 * @return {YAHOO.ext.Element}
116 */
117 getEl : function(){
118 return this.el;
119 },
120
121 /**
122 * Returns the specified region.
123 * @param {String} target The region key
124 * @return {YAHOO.ext.LayoutRegion}
125 */
126 getRegion : function(target){
127 return this.regions[target.toLowerCase()];
128 },
129
130 onWindowResize : function(){
131 if(this.monitorWindowResize){
132 this.layout();
133 }
134 }
135});
diff --git a/frontend/beta/js/YUI-extensions/layout/LayoutRegion.js b/frontend/beta/js/YUI-extensions/layout/LayoutRegion.js
new file mode 100644
index 0000000..fa8a1b6
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/layout/LayoutRegion.js
@@ -0,0 +1,496 @@
1/**
2 * @class YAHOO.ext.LayoutRegion
3 * @extends YAHOO.ext.util.Observable
4 * This class represents a region in a layout manager.
5 * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
6 * @cfg {Boolean} floatable False to disable floating (defaults to true)
7 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
8 * @cfg {Object} cmargins Margins for the element when collapsed (defaults to: north/south {top: 2, left: 0, right:0, bottom: 2} or east/west {top: 0, left: 2, right:2, bottom: 0})
9 * @cfg {String} tabPosition 'top' or 'bottom' (defaults to 'bottom')
10 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when only 1 panel (defaults to false)
11 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
12 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
13 * @cfg {String} title The title for the region (overrides panel titles)
14 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
15 * @cfg {Float} duration The duration of the expand/collapse animation in seconds
16 * @cfg {Float} slideDuration The duration of the slide out/in when collapsed in seconds
17 * @cfg {Boolean} autoHide False to disable disable autoHide when the mouse leaves the "floated" region (defaults to true)
18 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
19 * @cfg {Boolean} closeOnTabs True to place the close icon on the tabs instead of the region titlebar (defaults to false)
20 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
21 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
22 * the space available, similar to FireFox 1.5 tabs (defaults to false)
23 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
24 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
25 */
26YAHOO.ext.LayoutRegion = function(mgr, config, pos){
27 YAHOO.ext.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
28 var dh = YAHOO.ext.DomHelper;
29 /** This regions container element @type YAHOO.ext.Element */
30 this.el = dh.append(mgr.el.dom, {tag: 'div', cls: 'ylayout-panel ylayout-panel-' + this.position}, true);
31 /** This regions title element @type YAHOO.ext.Element */
32 this.titleEl = dh.append(this.el.dom, {tag: 'div', unselectable: 'on', cls: 'yunselectable ylayout-panel-hd ylayout-title-'+this.position, children:[
33 {tag: 'span', cls: 'yunselectable ylayout-panel-hd-text', unselectable: 'on', html: '&#160;'},
34 {tag: 'div', cls: 'yunselectable ylayout-panel-hd-tools', unselectable: 'on'}
35 ]}, true);
36 this.titleEl.enableDisplayMode();
37 /** This regions title text element @type HTMLElement */
38 this.titleTextEl = this.titleEl.dom.firstChild;
39 this.tools = getEl(this.titleEl.dom.childNodes[1], true);
40 this.closeBtn = this.createTool(this.tools.dom, 'ylayout-close');
41 this.closeBtn.enableDisplayMode();
42 this.closeBtn.on('click', this.closeClicked, this, true);
43 this.closeBtn.hide();
44 /** This regions body element @type YAHOO.ext.Element */
45 this.bodyEl = dh.append(this.el.dom, {tag: 'div', cls: 'ylayout-panel-body'}, true);
46 this.visible = false;
47 this.collapsed = false;
48 this.hide();
49 this.on('paneladded', this.validateVisibility, this, true);
50 this.on('panelremoved', this.validateVisibility, this, true);
51
52 this.applyConfig(config);
53};
54
55YAHOO.extendX(YAHOO.ext.LayoutRegion, YAHOO.ext.BasicLayoutRegion, {
56 applyConfig : function(config){
57 if(config.collapsible && this.position != 'center' && !this.collapsedEl){
58 var dh = YAHOO.ext.DomHelper;
59 this.collapseBtn = this.createTool(this.tools.dom, 'ylayout-collapse-'+this.position);
60 this.collapseBtn.mon('click', this.collapse, this, true);
61 /** This regions collapsed element @type YAHOO.ext.Element */
62 this.collapsedEl = dh.append(this.mgr.el.dom, {tag: 'div', cls: 'ylayout-collapsed ylayout-collapsed-'+this.position, children:[
63 {tag: 'div', cls: 'ylayout-collapsed-tools'}
64 ]}, true);
65 if(config.floatable !== false){
66 this.collapsedEl.addClassOnOver('ylayout-collapsed-over');
67 this.collapsedEl.mon('click', this.collapseClick, this, true);
68 }
69 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild, 'ylayout-expand-'+this.position);
70 this.expandBtn.mon('click', this.expand, this, true);
71 }
72 if(this.collapseBtn){
73 this.collapseBtn.setVisible(config.collapsible == true);
74 }
75 this.cmargins = config.cmargins || this.cmargins ||
76 (this.position == 'west' || this.position == 'east' ?
77 {top: 0, left: 2, right:2, bottom: 0} :
78 {top: 2, left: 0, right:0, bottom: 2});
79 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
80 this.bottomTabs = config.tabPosition != 'top';
81 this.autoScroll = config.autoScroll || false;
82 if(this.autoScroll){
83 this.bodyEl.setStyle('overflow', 'auto');
84 }else{
85 this.bodyEl.setStyle('overflow', 'hidden');
86 }
87 if((!config.titlebar && !config.title) || config.titlebar === false){
88 this.titleEl.hide();
89 }else{
90 this.titleEl.show();
91 if(config.title){
92 this.titleTextEl.innerHTML = config.title;
93 }
94 }
95 this.duration = config.duration || .30;
96 this.slideDuration = config.slideDuration || .45;
97 this.config = config;
98 if(config.collapsed){
99 this.collapse(true);
100 }
101 },
102 /**
103 * Returns true if this region is currently visible.
104 * @return {Boolean}
105 */
106 isVisible : function(){
107 return this.visible;
108 },
109
110 getBox : function(){
111 var b;
112 if(!this.collapsed){
113 b = this.el.getBox(false, true);
114 }else{
115 b = this.collapsedEl.getBox(false, true);
116 }
117 return b;
118 },
119
120 getMargins : function(){
121 return this.collapsed ? this.cmargins : this.margins;
122 },
123
124 highlight : function(){
125 this.el.addClass('ylayout-panel-dragover');
126 },
127
128 unhighlight : function(){
129 this.el.removeClass('ylayout-panel-dragover');
130 },
131
132 updateBox : function(box){
133 this.box = box;
134 if(!this.collapsed){
135 this.el.dom.style.left = box.x + 'px';
136 this.el.dom.style.top = box.y + 'px';
137 this.el.setSize(box.width, box.height);
138 var bodyHeight = this.titleEl.isVisible() ? box.height - (this.titleEl.getHeight()||0) : box.height;
139 bodyHeight -= this.el.getBorderWidth('tb');
140 bodyWidth = box.width - this.el.getBorderWidth('rl');
141 this.bodyEl.setHeight(bodyHeight);
142 this.bodyEl.setWidth(bodyWidth);
143 var tabHeight = bodyHeight;
144 if(this.tabs){
145 tabHeight = this.tabs.syncHeight(bodyHeight);
146 if(YAHOO.ext.util.Browser.isIE) this.tabs.el.repaint();
147 }
148 this.panelSize = {width: bodyWidth, height: tabHeight};
149 if(this.activePanel){
150 this.activePanel.setSize(bodyWidth, tabHeight);
151 }
152 }else{
153 this.collapsedEl.dom.style.left = box.x + 'px';
154 this.collapsedEl.dom.style.top = box.y + 'px';
155 this.collapsedEl.setSize(box.width, box.height);
156 }
157 if(this.tabs){
158 this.tabs.autoSizeTabs();
159 }
160 },
161
162 /**
163 * Returns the container element for this region.
164 * @return {YAHOO.ext.Element}
165 */
166 getEl : function(){
167 return this.el;
168 },
169
170 /**
171 * Hides this region.
172 */
173 hide : function(){
174 if(!this.collapsed){
175 this.el.dom.style.left = '-2000px';
176 this.el.hide();
177 }else{
178 this.collapsedEl.dom.style.left = '-2000px';
179 this.collapsedEl.hide();
180 }
181 this.visible = false;
182 this.fireEvent('visibilitychange', this, false);
183 },
184
185 /**
186 * Shows this region if it was previously hidden.
187 */
188 show : function(){
189 if(!this.collapsed){
190 this.el.show();
191 }else{
192 this.collapsedEl.show();
193 }
194 this.visible = true;
195 this.fireEvent('visibilitychange', this, true);
196 },
197
198 closeClicked : function(){
199 if(this.activePanel){
200 this.remove(this.activePanel);
201 }
202 },
203
204 collapseClick : function(e){
205 if(this.isSlid){
206 e.stopPropagation();
207 this.slideIn();
208 }else{
209 e.stopPropagation();
210 this.slideOut();
211 }
212 },
213
214 /**
215 * Collapses this region.
216 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
217 */
218 collapse : function(skipAnim){
219 if(this.collapsed) return;
220 this.collapsed = true;
221 if(this.split){
222 this.split.el.hide();
223 }
224 if(this.config.animate && skipAnim !== true){
225 this.fireEvent('invalidated', this);
226 this.animateCollapse();
227 }else{
228 this.el.setLocation(-20000,-20000);
229 this.el.hide();
230 this.collapsedEl.show();
231 this.fireEvent('collapsed', this);
232 this.fireEvent('invalidated', this);
233 }
234 },
235
236 animateCollapse : function(){
237 // overridden
238 },
239
240 /**
241 * Expand this region if it was previously collapsed.
242 * @param {YAHOO.ext.EventObject} e The event that triggered the expand (or null if calling manually)
243 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
244 */
245 expand : function(e, skipAnim){
246 if(e) e.stopPropagation();
247 if(!this.collapsed) return;
248 if(this.isSlid){
249 this.slideIn(this.expand.createDelegate(this));
250 return;
251 }
252 this.collapsed = false;
253 this.el.show();
254 if(this.config.animate && skipAnim !== true){
255 this.animateExpand();
256 }else{
257 if(this.split){
258 this.split.el.show();
259 }
260 this.collapsedEl.setLocation(-2000,-2000);
261 this.collapsedEl.hide();
262 this.fireEvent('invalidated', this);
263 this.fireEvent('expanded', this);
264 }
265 },
266
267 animateExpand : function(){
268 // overridden
269 },
270
271 initTabs : function(){
272 this.bodyEl.setStyle('overflow', 'hidden');
273 var ts = new YAHOO.ext.TabPanel(this.bodyEl.dom, this.bottomTabs);
274 if(this.config.hideTabs){
275 ts.stripWrap.setDisplayed(false);
276 }
277 this.tabs = ts;
278 ts.resizeTabs = this.config.resizeTabs === true;
279 ts.minTabWidth = this.config.minTabWidth || 40;
280 ts.maxTabWidth = this.config.maxTabWidth || 250;
281 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
282 ts.monitorResize = false;
283 ts.bodyEl.setStyle('overflow', this.config.autoScroll ? 'auto' : 'hidden');
284 this.panels.each(this.initPanelAsTab, this);
285 },
286
287 initPanelAsTab : function(panel){
288 var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
289 this.config.closeOnTab && panel.isClosable());
290 ti.on('activate', function(){
291 this.setActivePanel(panel);
292 }, this, true);
293 if(this.config.closeOnTab){
294 ti.on('beforeclose', function(t, e){
295 e.cancel = true;
296 this.remove(panel);
297 }, this, true);
298 }
299 return ti;
300 },
301
302 updatePanelTitle : function(panel, title){
303 if(this.activePanel == panel){
304 this.updateTitle(title);
305 }
306 if(this.tabs){
307 this.tabs.getTab(panel.getEl().id).setText(title);
308 }
309 },
310
311 updateTitle : function(title){
312 if(this.titleTextEl && !this.config.title){
313 this.titleTextEl.innerHTML = (typeof title != 'undefined' && title.length > 0 ? title : "&#160;");
314 }
315 },
316
317 setActivePanel : function(panel){
318 panel = this.getPanel(panel);
319 if(this.activePanel && this.activePanel != panel){
320 this.activePanel.setActiveState(false);
321 }
322 this.activePanel = panel;
323 panel.setActiveState(true);
324 if(this.panelSize){
325 panel.setSize(this.panelSize.width, this.panelSize.height);
326 }
327 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
328 this.updateTitle(panel.getTitle());
329 this.fireEvent('panelactivated', this, panel);
330 },
331
332 /**
333 * Show the specified panel.
334 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
335 * @return {YAHOO.ext.ContentPanel} The shown panel or null
336 */
337 showPanel : function(panel){
338 if(panel = this.getPanel(panel)){
339 if(this.tabs){
340 this.tabs.activate(panel.getEl().id);
341 }else{
342 this.setActivePanel(panel);
343 }
344 }
345 return panel;
346 },
347
348 /**
349 * Get the active panel for this region.
350 * @return {YAHOO.ext.ContentPanel} The active panel or null
351 */
352 getActivePanel : function(){
353 return this.activePanel;
354 },
355
356 validateVisibility : function(){
357 if(this.panels.getCount() < 1){
358 this.updateTitle('&#160;');
359 this.closeBtn.hide();
360 this.hide();
361 }else{
362 if(!this.isVisible()){
363 this.show();
364 }
365 }
366 },
367
368 /**
369 * Add the passed ContentPanel(s)
370 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
371 * @return {YAHOO.ext.ContentPanel} The panel added (if only one was added)
372 */
373 add : function(panel){
374 if(arguments.length > 1){
375 for(var i = 0, len = arguments.length; i < len; i++) {
376 this.add(arguments[i]);
377 }
378 return null;
379 }
380 if(this.hasPanel(panel)){
381 this.showPanel(panel);
382 return panel;
383 }
384 panel.setRegion(this);
385 this.panels.add(panel);
386 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
387 this.bodyEl.dom.appendChild(panel.getEl().dom);
388 if(panel.background !== true){
389 this.setActivePanel(panel);
390 }
391 this.fireEvent('paneladded', this, panel);
392 return panel;
393 }
394 if(!this.tabs){
395 this.initTabs();
396 }else{
397 this.initPanelAsTab(panel);
398 }
399 if(panel.background !== true){
400 this.tabs.activate(panel.getEl().id);
401 }
402 this.fireEvent('paneladded', this, panel);
403 return panel;
404 },
405
406 /**
407 * Hides the tab for the specified panel.
408 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
409 */
410 hidePanel : function(panel){
411 if(this.tabs && (panel = this.getPanel(panel))){
412 this.tabs.hideTab(panel.getEl().id);
413 }
414 },
415
416 /**
417 * Unhides the tab for a previously hidden panel.
418 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
419 */
420 unhidePanel : function(panel){
421 if(this.tabs && (panel = this.getPanel(panel))){
422 this.tabs.unhideTab(panel.getEl().id);
423 }
424 },
425
426 clearPanels : function(){
427 while(this.panels.getCount() > 0){
428 this.remove(this.panels.first());
429 }
430 },
431
432 /**
433 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
434 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
435 * @param {Boolean} preservePanel Overrides the config preservePanel option
436 * @return {YAHOO.ext.ContentPanel} The panel that was removed
437 */
438 remove : function(panel, preservePanel){
439 panel = this.getPanel(panel);
440 if(!panel){
441 return null;
442 }
443 var e = {};
444 this.fireEvent('beforeremove', this, panel, e);
445 if(e.cancel === true){
446 return null;
447 }
448 preservePanel = (typeof preservePanel != 'undefined' ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
449 var panelId = panel.getId();
450 this.panels.removeKey(panelId);
451 if(preservePanel){
452 document.body.appendChild(panel.getEl().dom);
453 }
454 if(this.tabs){
455 this.tabs.removeTab(panel.getEl().id);
456 }else if (!preservePanel){
457 this.bodyEl.dom.removeChild(panel.getEl().dom);
458 }
459 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
460 var p = this.panels.first();
461 var tempEl = document.createElement('span'); // temp holder to keep IE from deleting the node
462 tempEl.appendChild(p.getEl().dom);
463 this.bodyEl.update('');
464 this.bodyEl.dom.appendChild(p.getEl().dom);
465 tempEl = null;
466 this.updateTitle(p.getTitle());
467 this.tabs = null;
468 this.bodyEl.setStyle('overflow', this.config.autoScroll ? 'auto' : 'hidden');
469 this.setActivePanel(p);
470 }
471 panel.setRegion(null);
472 if(this.activePanel == panel){
473 this.activePanel = null;
474 }
475 if(this.config.autoDestroy !== false && preservePanel !== true){
476 try{panel.destroy();}catch(e){}
477 }
478 this.fireEvent('panelremoved', this, panel);
479 return panel;
480 },
481
482 /**
483 * Returns the TabPanel component used by this region
484 * @return {YAHOO.ext.TabPanel}
485 */
486 getTabs : function(){
487 return this.tabs;
488 },
489
490 createTool : function(parentEl, className){
491 var btn = YAHOO.ext.DomHelper.append(parentEl, {tag: 'div', cls: 'ylayout-tools-button',
492 children: [{tag: 'div', cls: 'ylayout-tools-button-inner ' + className, html: '&#160;'}]}, true);
493 btn.addClassOnOver('ylayout-tools-button-over');
494 return btn;
495 }
496});
diff --git a/frontend/beta/js/YUI-extensions/layout/LayoutStateManager.js b/frontend/beta/js/YUI-extensions/layout/LayoutStateManager.js
new file mode 100644
index 0000000..ea22235
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/layout/LayoutStateManager.js
@@ -0,0 +1,68 @@
1/*
2 * Private internal class for reading and applying state
3 */
4YAHOO.ext.LayoutStateManager = function(layout){
5 // default empty state
6 this.state = {
7 north: {},
8 south: {},
9 east: {},
10 west: {}
11 };
12};
13
14YAHOO.ext.LayoutStateManager.prototype = {
15 init : function(layout, provider){
16 this.provider = provider;
17 var state = provider.get(layout.id+'-layout-state');
18 if(state){
19 var wasUpdating = layout.isUpdating();
20 if(!wasUpdating){
21 layout.beginUpdate();
22 }
23 for(var key in state){
24 if(typeof state[key] != 'function'){
25 var rstate = state[key];
26 var r = layout.getRegion(key);
27 if(r && rstate){
28 if(rstate.size){
29 r.resizeTo(rstate.size);
30 }
31 if(rstate.collapsed == true){
32 r.collapse(true);
33 }else{
34 r.expand(null, true);
35 }
36 }
37 }
38 }
39 if(!wasUpdating){
40 layout.endUpdate();
41 }
42 this.state = state;
43 }
44 this.layout = layout;
45 layout.on('regionresized', this.onRegionResized, this, true);
46 layout.on('regioncollapsed', this.onRegionCollapsed, this, true);
47 layout.on('regionexpanded', this.onRegionExpanded, this, true);
48 },
49
50 storeState : function(){
51 this.provider.set(this.layout.id+'-layout-state', this.state);
52 },
53
54 onRegionResized : function(region, newSize){
55 this.state[region.getPosition()].size = newSize;
56 this.storeState();
57 },
58
59 onRegionCollapsed : function(region){
60 this.state[region.getPosition()].collapsed = true;
61 this.storeState();
62 },
63
64 onRegionExpanded : function(region){
65 this.state[region.getPosition()].collapsed = false;
66 this.storeState();
67 }
68};
diff --git a/frontend/beta/js/YUI-extensions/layout/SplitLayoutRegion.js b/frontend/beta/js/YUI-extensions/layout/SplitLayoutRegion.js
new file mode 100644
index 0000000..6b8ce9e
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/layout/SplitLayoutRegion.js
@@ -0,0 +1,282 @@
1/**
2 * @class YAHOO.ext.SplitLayoutRegion
3 * @extends YAHOO.ext.LayoutRegion
4 * Adds a splitbar and other (private) useful functionality to a LayoutRegion
5 */
6YAHOO.ext.SplitLayoutRegion = function(mgr, config, pos, cursor){
7 this.cursor = cursor;
8 YAHOO.ext.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
9 if(config.split){
10 this.hide();
11 }
12};
13
14YAHOO.extendX(YAHOO.ext.SplitLayoutRegion, YAHOO.ext.LayoutRegion, {
15 applyConfig : function(config){
16 YAHOO.ext.SplitLayoutRegion.superclass.applyConfig.call(this, config);
17 if(config.split){
18 if(!this.split){
19 var splitEl = YAHOO.ext.DomHelper.append(this.mgr.el.dom,
20 {tag: 'div', id: this.el.id + '-split', cls: 'ylayout-split ylayout-split-'+this.position, html: '&#160;'});
21 /** The SplitBar for this region @type YAHOO.ext.SplitBar */
22 this.split = new YAHOO.ext.SplitBar(splitEl, this.el);
23 this.split.onMoved.subscribe(this.onSplitMove, this, true);
24 this.split.useShim = config.useShim === true;
25 YAHOO.util.Dom.setStyle([this.split.el.dom, this.split.proxy], 'cursor', this.cursor);
26 this.split.getMaximumSize = this.getMaxSize.createDelegate(this);
27 }
28 if(typeof config.minSize != 'undefined'){
29 this.split.minSize = config.minSize;
30 }
31 if(typeof config.maxSize != 'undefined'){
32 this.split.maxSize = config.maxSize;
33 }
34 }
35 },
36
37 getMaxSize : function(){
38 var cmax = this.config.maxSize || 10000;
39 var center = this.mgr.getRegion('center');
40 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
41 },
42
43 onSplitMove : function(split, newSize){
44 this.fireEvent('resized', this, newSize);
45 },
46
47 /**
48 * Returns the SplitBar for this region.
49 * @return {YAHOO.ext.SplitBar}
50 */
51 getSplitBar : function(){
52 return this.split;
53 },
54
55 hide : function(){
56 if(this.split){
57 this.split.el.setLocation(-2000,-2000);
58 this.split.el.hide();
59 }
60 YAHOO.ext.SplitLayoutRegion.superclass.hide.call(this);
61 },
62
63 show : function(){
64 if(this.split){
65 this.split.el.show();
66 }
67 YAHOO.ext.SplitLayoutRegion.superclass.show.call(this);
68 },
69
70 beforeSlide: function(){
71 if(YAHOO.ext.util.Browser.isGecko){// firefox overflow auto bug workaround
72 this.bodyEl.clip();
73 if(this.tabs) this.tabs.bodyEl.clip();
74 if(this.activePanel){
75 this.activePanel.getEl().clip();
76
77 if(this.activePanel.beforeSlide){
78 this.activePanel.beforeSlide();
79 }
80 }
81 }
82 },
83
84 afterSlide : function(){
85 if(YAHOO.ext.util.Browser.isGecko){// firefox overflow auto bug workaround
86 this.bodyEl.unclip();
87 if(this.tabs) this.tabs.bodyEl.unclip();
88 if(this.activePanel){
89 this.activePanel.getEl().unclip();
90 if(this.activePanel.afterSlide){
91 this.activePanel.afterSlide();
92 }
93 }
94 }
95 },
96
97 slideOut : function(){
98 if(!this.slideEl){
99 this.slideEl = new YAHOO.ext.Actor(
100 YAHOO.ext.DomHelper.append(this.mgr.el.dom, {tag: 'div', cls:'ylayout-slider'}));
101 if(this.config.autoHide !== false){
102 var slideInTask = new YAHOO.ext.util.DelayedTask(this.slideIn, this);
103 this.slideEl.mon('mouseout', function(e){
104 var to = e.getRelatedTarget();
105 if(to && to != this.slideEl.dom && !YAHOO.util.Dom.isAncestor(this.slideEl.dom, to)){
106 slideInTask.delay(500);
107 }
108 }, this, true);
109 this.slideEl.mon('mouseover', function(e){
110 slideInTask.cancel();
111 }, this, true);
112 }
113 }
114 var sl = this.slideEl, c = this.collapsedEl, cm = this.cmargins;
115 this.isSlid = true;
116 this.snapshot = {
117 'left': this.el.getLeft(true),
118 'top': this.el.getTop(true),
119 'colbtn': this.collapseBtn.isVisible(),
120 'closebtn': this.closeBtn.isVisible()
121 };
122 this.collapseBtn.hide();
123 this.closeBtn.hide();
124 this.el.show();
125 this.el.setLeftTop(0,0);
126 sl.startCapture(true);
127 var size;
128 switch(this.position){
129 case 'west':
130 sl.setLeft(c.getRight(true));
131 sl.setTop(c.getTop(true));
132 size = this.el.getWidth();
133 break;
134 case 'east':
135 sl.setRight(this.mgr.getViewSize().width-c.getLeft(true));
136 sl.setTop(c.getTop(true));
137 size = this.el.getWidth();
138 break;
139 case 'north':
140 sl.setLeft(c.getLeft(true));
141 sl.setTop(c.getBottom(true));
142 size = this.el.getHeight();
143 break;
144 case 'south':
145 sl.setLeft(c.getLeft(true));
146 sl.setBottom(this.mgr.getViewSize().height-c.getTop(true));
147 size = this.el.getHeight();
148 break;
149 }
150 sl.dom.appendChild(this.el.dom);
151 YAHOO.util.Event.on(document.body, 'click', this.slideInIf, this, true);
152 sl.setSize(this.el.getWidth(), this.el.getHeight());
153 this.beforeSlide();
154 if(this.activePanel){
155 this.activePanel.setSize(this.bodyEl.getWidth(), this.bodyEl.getHeight());
156 }
157 sl.slideShow(this.getAnchor(), size, this.slideDuration, null, false);
158 sl.play(function(){
159 this.afterSlide();
160 }.createDelegate(this));
161 },
162
163 slideInIf : function(e){
164 var t = YAHOO.util.Event.getTarget(e);
165 if(!YAHOO.util.Dom.isAncestor(this.el.dom, t)){
166 this.slideIn();
167 }
168 },
169
170 slideIn : function(callback){
171 if(this.isSlid && !this.slideEl.playlist.isPlaying()){
172 YAHOO.util.Event.removeListener(document.body, 'click', this.slideInIf, this, true);
173 this.slideEl.startCapture(true);
174 this.slideEl.slideHide(this.getAnchor(), this.slideDuration, null);
175 this.beforeSlide();
176 this.slideEl.play(function(){
177 this.isSlid = false;
178 this.el.setPositioning(this.snapshot);
179 this.collapseBtn.setVisible(this.snapshot.colbtn);
180 this.closeBtn.setVisible(this.snapshot.closebtn);
181 this.afterSlide();
182 this.mgr.el.dom.appendChild(this.el.dom);
183 if(typeof callback == 'function'){
184 callback();
185 }
186 }.createDelegate(this));
187 }
188 },
189
190 animateExpand : function(){
191 var em = this.margins, cm = this.cmargins;
192 var c = this.collapsedEl, el = this.el;
193 var direction, distance;
194 switch(this.position){
195 case 'west':
196 direction = 'right';
197 el.setLeft(-(el.getWidth() + (em.right+em.left)));
198 el.setTop(c.getTop(true)-cm.top+em.top);
199 distance = el.getWidth() + (em.right+em.left);
200 break;
201 case 'east':
202 direction = 'left';
203 el.setLeft(this.mgr.getViewSize().width + em.left);
204 el.setTop(c.getTop(true)-cm.top+em.top);
205 distance = el.getWidth() + (em.right+em.left);
206 break;
207 case 'north':
208 direction = 'down';
209 el.setLeft(em.left);
210 el.setTop(-(el.getHeight() + (em.top+em.bottom)));
211 distance = el.getHeight() + (em.top+em.bottom);
212 break;
213 case 'south':
214 direction = 'up';
215 el.setLeft(em.left);
216 el.setTop(this.mgr.getViewSize().height + em.top);
217 distance = el.getHeight() + (em.top+em.bottom);
218 break;
219 }
220 this.beforeSlide();
221 el.setStyle('z-index', '100');
222 el.show();
223 c.setLocation(-2000,-2000);
224 c.hide();
225 el.move(direction, distance, true, this.duration, function(){
226 this.afterSlide();
227 el.setStyle('z-index', '');
228 if(this.split){
229 this.split.el.show();
230 }
231 this.fireEvent('invalidated', this);
232 this.fireEvent('expanded', this);
233 }.createDelegate(this), this.config.easing || YAHOO.util.Easing.easeOut);
234 },
235
236 animateCollapse : function(){
237 var em = this.margins, cm = this.cmargins;
238 var c = this.collapsedEl, el = this.el;
239 var direction, distance;
240 switch(this.position){
241 case 'west':
242 direction = 'left';
243 distance = el.getWidth() + (em.right+em.left);
244 break;
245 case 'east':
246 direction = 'right';
247 distance = el.getWidth() + (em.right+em.left);
248 break;
249 case 'north':
250 direction = 'up';
251 distance = el.getHeight() + (em.top+em.bottom);
252 break;
253 case 'south':
254 direction = 'down';
255 distance = el.getHeight() + (em.top+em.bottom);
256 break;
257 }
258 this.el.setStyle('z-index', '100');
259 this.beforeSlide();
260 this.el.move(direction, distance, true, this.duration, function(){
261 this.afterSlide();
262 this.el.setStyle('z-index', '');
263 this.el.setLocation(-20000,-20000);
264 this.el.hide();
265 this.collapsedEl.show();
266 this.fireEvent('collapsed', this);
267 }.createDelegate(this), YAHOO.util.Easing.easeIn);
268 },
269
270 getAnchor : function(){
271 switch(this.position){
272 case 'west':
273 return 'left';
274 case 'east':
275 return 'right';
276 case 'north':
277 return 'top';
278 case 'south':
279 return 'bottom';
280 }
281 }
282});
diff --git a/frontend/beta/js/YUI-extensions/tree/AsyncTreeNode.js b/frontend/beta/js/YUI-extensions/tree/AsyncTreeNode.js
new file mode 100644
index 0000000..5d48b00
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/tree/AsyncTreeNode.js
@@ -0,0 +1,58 @@
1YAHOO.ext.tree.AsyncTreeNode = function(config){
2 this.loaded = false;
3 this.loading = false;
4 YAHOO.ext.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
5 this.events['beforeload'] = true;
6 this.events['load'] = true;
7};
8YAHOO.extendX(YAHOO.ext.tree.AsyncTreeNode, YAHOO.ext.tree.TreeNode, {
9 expand : function(deep, anim, callback){
10 if(this.loading){ // if an async load is already running, waiting til it's done
11 var timer;
12 var f = function(){
13 if(!this.loading){ // done loading
14 clearInterval(timer);
15 this.expand(deep, anim, callback);
16 }
17 }.createDelegate(this);
18 timer = setInterval(f, 200);
19 }
20 if(!this.loaded){
21 if(this.fireEvent('beforeload', this) === false){
22 return;
23 }
24 this.loading = true;
25 this.ui.beforeLoad(this);
26 var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
27 if(loader){
28 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
29 return;
30 }
31 }
32 YAHOO.ext.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33 },
34
35 isLoading : function(){
36 return this.loading;
37 },
38
39 loadComplete : function(deep, anim, callback){
40 this.loading = false;
41 this.loaded = true;
42 this.ui.afterLoad(this);
43 this.fireEvent('load', this);
44 this.expand(deep, anim, callback);
45 },
46
47 isLoaded : function(){
48 return this.loaded;
49 },
50
51 hasChildNodes : function(){
52 if(!this.isLeaf() && !this.loaded){
53 return true;
54 }else{
55 return YAHOO.ext.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
56 }
57 }
58});
diff --git a/frontend/beta/js/YUI-extensions/tree/TreeDragZone.js b/frontend/beta/js/YUI-extensions/tree/TreeDragZone.js
new file mode 100644
index 0000000..9b77b3c
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/tree/TreeDragZone.js
@@ -0,0 +1,43 @@
1YAHOO.ext.tree.TreeDragZone = function(tree, config){
2 YAHOO.ext.tree.TreeDragZone.superclass.constructor.call(this, tree.getEl(), config);
3 this.tree = tree;
4};
5
6YAHOO.extendX(YAHOO.ext.tree.TreeDragZone, YAHOO.ext.dd.DragZone, {
7 ddGroup : 'TreeDD',
8
9 onBeforeDrag : function(data, e){
10 var n = data.node;
11 return n && n.draggable && !n.disabled;
12 },
13
14 onInitDrag : function(e){
15 var data = this.dragData;
16 this.tree.getSelectionModel().select(data.node);
17 this.proxy.update('');
18 data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19 this.tree.fireEvent('startdrag', this.tree, data.node, e);
20 },
21
22 getRepairXY : function(e, data){
23 return data.node.ui.getDDRepairXY();
24 },
25
26 onEndDrag : function(data, e){
27 this.tree.fireEvent('enddrag', this.tree, data.node, e);
28 },
29
30 onValidDrop : function(dd, e, id){
31 this.tree.fireEvent('dragdrop', this.tree, this.dragData.node, dd, e);
32 this.hideProxy();
33 },
34
35 beforeInvalidDrop : function(e, id){
36 if(YAHOO.util.Anim){
37 // this scrolls the original position back into view
38 var sm = this.tree.getSelectionModel();
39 sm.clearSelections();
40 sm.select(this.dragData.node);
41 }
42 }
43});
diff --git a/frontend/beta/js/YUI-extensions/tree/TreeDropZone.js b/frontend/beta/js/YUI-extensions/tree/TreeDropZone.js
new file mode 100644
index 0000000..91c24e1
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/tree/TreeDropZone.js
@@ -0,0 +1,228 @@
1YAHOO.ext.tree.TreeDropZone = function(tree, config){
2 this.allowParentInsert = false;
3 this.allowContainerDrop = false;
4 this.appendOnly = false;
5 YAHOO.ext.tree.TreeDropZone.superclass.constructor.call(this, tree.container, config);
6 this.tree = tree;
7 this.lastInsertClass = 'ytree-no-status';
8 this.dragOverData = {};
9};
10
11YAHOO.extendX(YAHOO.ext.tree.TreeDropZone, YAHOO.ext.dd.DropZone, {
12 ddGroup : 'TreeDD',
13
14 expandDelay : 1000,
15
16 expandNode : function(node){
17 if(node.hasChildNodes() && !node.isExpanded()){
18 node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
19 }
20 },
21
22 queueExpand : function(node){
23 this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
24 },
25
26 cancelExpand : function(){
27 if(this.expandProcId){
28 clearTimeout(this.expandProcId);
29 this.expandProcId = false;
30 }
31 },
32
33 isValidDropPoint : function(n, pt, dd, e, data){
34 if(!n || !data){ return false; }
35 var targetNode = n.node;
36 var dropNode = data.node;
37 // default drop rules
38 if(!(targetNode && targetNode.isTarget && pt)){
39 return false;
40 }
41 if(pt == 'append' && targetNode.allowChildren === false){
42 return false;
43 }
44 if((pt == 'above' || pt == 'below') && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
45 return false;
46 }
47 if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
48 return false;
49 }
50 // reuse the object
51 var overEvent = this.dragOverData;
52 overEvent.tree = this.tree;
53 overEvent.target = targetNode;
54 overEvent.data = data;
55 overEvent.point = pt;
56 overEvent.source = dd;
57 overEvent.rawEvent = e;
58 overEvent.dropNode = dropNode;
59 overEvent.cancel = false;
60 var result = this.tree.fireEvent('nodedragover', overEvent);
61 return overEvent.cancel === false && result !== false;
62 },
63
64 getDropPoint : function(e, n, dd){
65 var tn = n.node;
66 if(tn.isRoot){
67 return tn.allowChildren !== false ? 'ap-pend' : false; // always append for root
68 }
69 var dragEl = n.ddel;
70 var t = YAHOO.util.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
71 var y = YAHOO.util.Event.getPageY(e);
72 var noAppend = tn.allowChildren === false || tn.isLeaf();
73 if(this.appendOnly || tn.parentNode.allowChildren === false){
74 return noAppend ? false : 'append';
75 }
76 var noBelow = false;
77 if(!this.allowParentInsert){
78 noBelow = tn.hasChildNodes() && tn.isExpanded();
79 }
80 var q = (b - t) / (noAppend ? 2 : 3);
81 if(y >= t && y < t + q){
82 return 'above';
83 }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
84 return 'below';
85 }else{
86 return 'append';
87 }
88 return false;
89 },
90
91 onNodeEnter : function(n, dd, e, data){
92 this.cancelExpand();
93 },
94
95 onNodeOver : function(n, dd, e, data){
96 var pt = this.getDropPoint(e, n, dd);
97 var node = n.node;
98
99 // auto node expand check
100 if(!this.expandProcId && pt == 'append' && node.hasChildNodes() && !n.node.isExpanded()){
101 this.queueExpand(node);
102 }else if(pt != 'append'){
103 this.cancelExpand();
104 }
105
106 // set the insert point style on the target node
107 var returnCls = this.dropNotAllowed;
108 if(this.isValidDropPoint(n, pt, dd, e, data)){
109 if(pt){
110 var el = n.ddel;
111 var cls, returnCls;
112 if(pt == 'above'){
113 returnCls = n.node.isFirst() ? 'ytree-drop-ok-above' : 'ytree-drop-ok-between';
114 cls = 'ytree-drag-insert-above';
115 }else if(pt == 'below'){
116 returnCls = n.node.isLast() ? 'ytree-drop-ok-below' : 'ytree-drop-ok-between';
117 cls = 'ytree-drag-insert-below';
118 }else{
119 returnCls = 'ytree-drop-ok-append';
120 cls = 'ytree-drag-append';
121 }
122 if(this.lastInsertClass != cls){
123 YAHOO.util.Dom.replaceClass(el, this.lastInsertClass, cls);
124 this.lastInsertClass = cls;
125 }
126 }
127 }
128 return returnCls;
129 },
130
131 onNodeOut : function(n, dd, e, data){
132 this.cancelExpand();
133 this.removeDropIndicators(n);
134 },
135
136 onNodeDrop : function(n, dd, e, data){
137 var point = this.getDropPoint(e, n, dd);
138 var targetNode = n.node;
139 targetNode.ui.startDrop();
140 if(!this.isValidDropPoint(n, point, dd, e, data)){
141 targetNode.ui.endDrop();
142 return false;
143 }
144 // first try to find the drop node
145 var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
146 var dropEvent = {
147 tree : this.tree,
148 target: targetNode,
149 data: data,
150 point: point,
151 source: dd,
152 rawEvent: e,
153 dropNode: dropNode,
154 cancel: dropNode ? false : true
155 };
156 var retval = this.tree.fireEvent('beforenodedrop', dropEvent);
157 if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
158 targetNode.ui.endDrop();
159 return false;
160 }
161 if(point == 'append' && !targetNode.isExpanded()){
162 targetNode.expand(false, null, function(){
163 this.completeDrop(dropEvent);
164 }.createDelegate(this));
165 }else{
166 this.completeDrop(dropEvent);
167 }
168 return true;
169 },
170
171 completeDrop : function(de){
172 var ns = de.dropNode, p = de.point, t = de.target;
173 if(!(ns instanceof Array)){
174 ns = [ns];
175 }
176 var n;
177 for(var i = 0, len = ns.length; i < len; i++){
178 n = ns[i];
179 if(p == 'above'){
180 t.parentNode.insertBefore(n, t);
181 }else if(p == 'below'){
182 t.parentNode.insertBefore(n, t.nextSibling);
183 }else{
184 t.appendChild(n);
185 }
186 }
187 n.select(); // select and highlight the last insert
188 if(this.tree.hlDrop){
189 n.ui.highlight();
190 }
191 t.ui.endDrop();
192 this.tree.fireEvent('nodedrop', de);
193 },
194
195 afterNodeMoved : function(dd, data, e, targetNode, dropNode){
196 if(this.tree.hlDrop){
197 dropNode.select();
198 dropNode.ui.highlight();
199 }
200 this.tree.fireEvent('nodedrop', this.tree, targetNode, data, dd, e);
201 },
202
203 getTree : function(){
204 return this.tree;
205 },
206
207 removeDropIndicators : function(n){
208 if(n && n.ddel){
209 var el = n.ddel;
210 YAHOO.util.Dom.removeClass(el, 'ytree-drag-insert-above');
211 YAHOO.util.Dom.removeClass(el, 'ytree-drag-insert-below');
212 YAHOO.util.Dom.removeClass(el, 'ytree-drag-append');
213 this.lastInsertClass = '_noclass';
214 }
215 },
216
217 beforeDragDrop : function(target, e, id){
218 this.cancelExpand();
219 return true;
220 },
221
222 afterRepair : function(data){
223 if(data){
224 data.node.ui.highlight();
225 }
226 this.hideProxy();
227 }
228});
diff --git a/frontend/beta/js/YUI-extensions/tree/TreeFilter.js b/frontend/beta/js/YUI-extensions/tree/TreeFilter.js
new file mode 100644
index 0000000..9eeb274
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/tree/TreeFilter.js
@@ -0,0 +1,105 @@
1/**
2 * This doesn't update the indent (lines) or expand collapse icons of the nodes
3 */
4YAHOO.ext.tree.TreeFilter = function(tree, config){
5 this.tree = tree;
6 this.filtered = {};
7 YAHOO.ext.util.Config.apply(this, config, {
8 clearBlank:false,
9 reverse:false,
10 autoClear:false,
11 remove:false
12 });
13};
14
15YAHOO.ext.tree.TreeFilter.prototype = {
16 /**
17 * Filter the data by a specific attribute.
18 * @param {String/RegExp} value Either string that the attribute value
19 * should start with or a RegExp to test against the attribute
20 * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
21 * @param {TreeNode} startNode (optional) The node to start the filter at.
22 */
23 filter : function(value, attr, startNode){
24 attr = attr || 'text';
25 var f;
26 if(typeof value == 'string'){
27 var vlen = value.length;
28 // auto clear empty filter
29 if(vlen == 0 && this.clearBlank){
30 this.clearFilter();
31 return;
32 }
33 value = value.toLowerCase();
34 f = function(n){
35 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36 }
37 }else if(value.exec){ // regex?
38 f = function(n){
39 return value.test(n.attributes[attr]);
40 }
41 }else{
42 throw 'Illegal filter type, must be string or regex';
43 }
44 this.filterBy(f, null, startNode);
45 },
46
47 /**
48 * Filter by a function. The passed function will be called with each
49 * node in the tree (or from the startNode). If the function returns true, the node is kept
50 * otherwise it is filtered. If a node is filtered, it's children are also filtered.
51 * @param {Function} fn The filter function
52 * @param {Object} scope (optional) The scope of the function (defaults to the current node)
53 */
54 filterBy : function(fn, scope, startNode){
55 startNode = startNode || this.tree.root;
56 if(this.autoClear){
57 this.clearFilter();
58 }
59 var af = this.filtered, rv = this.reverse;
60 var f = function(n){
61 if(n == startNode){
62 return true;
63 }
64 if(af[n.id]){
65 return false;
66 }
67 var m = fn.call(scope || n, n);
68 if(!m || rv){
69 af[n.id] = n;
70 n.ui.hide();
71 return false;
72 }
73 return true;
74 }
75 startNode.cascade(f);
76 if(this.remove){
77 for(var id in af){
78 if(typeof id != 'function'){
79 var n = af[id];
80 if(n && n.parentNode){
81 n.parentNode.removeChild(n);
82 }
83 }
84 }
85 }
86 },
87
88 /**
89 * Clears the current filter. Note: with the "remove" option
90 * set a filter cannot be cleared.
91 */
92 clear : function(){
93 var t = this.tree;
94 var af = this.filtered;
95 for(var id in af){
96 if(typeof id != 'function'){
97 var n = af[id];
98 if(n){
99 n.ui.show();
100 }
101 }
102 }
103 this.filtered = {};
104 }
105};
diff --git a/frontend/beta/js/YUI-extensions/tree/TreeLoader.js b/frontend/beta/js/YUI-extensions/tree/TreeLoader.js
new file mode 100644
index 0000000..34989bd
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/tree/TreeLoader.js
@@ -0,0 +1,107 @@
1YAHOO.ext.tree.TreeLoader = function(config){
2 this.baseParams = {};
3 this.requestMethod = 'POST';
4 YAHOO.ext.util.Config.apply(this, config);
5
6 this.events = {
7 'beforeload' : true,
8 'load' : true,
9 'loadexception' : true
10 };
11};
12
13YAHOO.extendX(YAHOO.ext.tree.TreeLoader, YAHOO.ext.util.Observable, {
14 load : function(node, callback){
15 if(node.attributes.children){ // preloaded json children
16 var cs = node.attributes.children;
17 for(var i = 0, len = cs.length; i < len; i++){
18 node.appendChild(this.createNode(cs[i]));
19 }
20 if(typeof callback == 'function'){
21 callback();
22 }
23 }else if(this.dataUrl){
24 this.requestData(node, callback);
25 }
26 },
27
28 getParams: function(node){
29 var buf = [], bp = this.baseParams;
30 for(var key in bp){
31 if(typeof bp[key] != 'function'){
32 buf.push(encodeURIComponent(key), '=', encodeURIComponent(bp[key]), '&');
33 }
34 }
35 buf.push('node=', encodeURIComponent(node.id));
36 return buf.join('');
37 },
38
39 requestData : function(node, callback){
40 if(this.fireEvent('beforeload', this, node, callback) !== false){
41 var params = this.getParams(node);
42 var cb = {
43 success: this.handleResponse,
44 failure: this.handleFailure,
45 scope: this,
46 argument: {callback: callback, node: node}
47 };
48 this.transId = YAHOO.util.Connect.asyncRequest(this.requestMethod, this.dataUrl, cb, params);
49 }else{
50 // if the load is cancelled, make sure we notify
51 // the node that we are done
52 if(typeof callback == 'function'){
53 callback();
54 }
55 }
56 },
57
58 isLoading : function(){
59 return this.transId ? true : false;
60 },
61
62 abort : function(){
63 if(this.isLoading()){
64 YAHOO.util.Connect.abort(this.transId);
65 }
66 },
67
68 createNode : function(attr){
69 if(this.applyLoader !== false){
70 attr.loader = this;
71 }
72 return(attr.leaf ?
73 new YAHOO.ext.tree.TreeNode(attr) :
74 new YAHOO.ext.tree.AsyncTreeNode(attr));
75 },
76
77 processResponse : function(response, node, callback){
78 var json = response.responseText;
79 try {
80 var o = eval('('+json+')');
81 for(var i = 0, len = o.length; i < len; i++){
82 node.appendChild(this.createNode(o[i]));
83 }
84 if(typeof callback == 'function'){
85 callback();
86 }
87 }catch(e){
88 this.handleFailure(response);
89 }
90 },
91
92 handleResponse : function(response){
93 this.transId = false;
94 var a = response.argument;
95 this.processResponse(response, a.node, a.callback);
96 this.fireEvent('load', this, a.node, response);
97 },
98
99 handleFailure : function(response){
100 this.transId = false;
101 var a = response.argument;
102 this.fireEvent('loadexception', this, a.node, response);
103 if(typeof a.callback == 'function'){
104 a.callback();
105 }
106 }
107});
diff --git a/frontend/beta/js/YUI-extensions/tree/TreeNode.js b/frontend/beta/js/YUI-extensions/tree/TreeNode.js
new file mode 100644
index 0000000..c676481
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/tree/TreeNode.js
@@ -0,0 +1,300 @@
1/**
2 * @class YAHOO.ext.tree.TreeNode
3 * @extends YAHOO.ext.data.Node
4 * @cfg {Boolean} leaf true if this node is a leaf and does not have or cannot have children
5 * @cfg {Boolean} expanded true to start the node expanded
6 * @cfg {Boolean} draggable false to make this node undraggable if DD is on (default to true)
7 * @cfg {Boolean} isTarget false if this node cannot be drop on
8 * @cfg {Boolean} disabled true to start the node disabled
9 * @constructor
10 * @param {Object} attributes The attributes/config for the node
11 */
12YAHOO.ext.tree.TreeNode = function(attributes){
13 attributes = attributes || {};
14 if(typeof attributes == 'string'){
15 attributes = {text: attributes};
16 }
17 this.el = null;
18 this.childrenRendered = false;
19 this.rendered = false;
20 YAHOO.ext.tree.TreeNode.superclass.constructor.call(this, attributes);
21 this.expanded = attributes.expanded === true;
22 this.isTarget = attributes.isTarget !== false;
23 this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
24 this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
25 this.text = attributes.text;
26 this.disabled = attributes.disabled === true;
27
28 YAHOO.ext.util.Config.apply(this.events, {
29 'textchange' : true,
30 'beforeexpand' : true,
31 'beforecollapse' : true,
32 'expand' : true,
33 'disabledchange' : true,
34 'collapse' : true,
35 'beforeclick':true,
36 'click':true,
37 'dblclick':true,
38 'contentmenu':true,
39 'beforechildrenrendered':true
40 });
41
42 var uiClass = this.attributes.uiProvider || YAHOO.ext.tree.TreeNodeUI;
43 this.ui = new uiClass(this);
44};
45YAHOO.extendX(YAHOO.ext.tree.TreeNode, YAHOO.ext.data.Node, {
46 isExpanded : function(){
47 return this.expanded;
48 },
49
50 getUI : function(){
51 return this.ui;
52 },
53
54 setFirstChild : function(node){
55 var of = this.firstChild;
56 YAHOO.ext.tree.TreeNode.superclass.setFirstChild.call(this, node);
57 if(this.childrenRendered && of && node != of){
58 of.renderIndent(true, true);
59 }
60 if(this.rendered){
61 this.renderIndent(true, true);
62 }
63 },
64
65 setLastChild : function(node){
66 var ol = this.lastChild;
67 YAHOO.ext.tree.TreeNode.superclass.setLastChild.call(this, node);
68 if(this.childrenRendered && ol && node != ol){
69 ol.renderIndent(true, true);
70 }
71 if(this.rendered){
72 this.renderIndent(true, true);
73 }
74 },
75
76 // these methods are overridden to provide lazy rendering support
77 appendChild : function(){
78 var node = YAHOO.ext.tree.TreeNode.superclass.appendChild.apply(this, arguments);
79 if(node && this.childrenRendered){
80 node.render();
81 }
82 this.ui.updateExpandIcon();
83 return node;
84 },
85
86 removeChild : function(node){
87 this.ownerTree.getSelectionModel().unselect(node);
88 YAHOO.ext.tree.TreeNode.superclass.removeChild.apply(this, arguments);
89 // if it's been rendered remove dom node
90 if(this.childrenRendered){
91 node.ui.remove();
92 }
93 if(this.childNodes.length < 1){
94 this.collapse(false, false);
95 }else{
96 this.ui.updateExpandIcon();
97 }
98 return node;
99 },
100
101 insertBefore : function(node, refNode){
102 var newNode = YAHOO.ext.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
103 if(newNode && refNode && this.childrenRendered){
104 node.render();
105 }
106 this.ui.updateExpandIcon();
107 return newNode;
108 },
109
110 setText : function(text){
111 var oldText = this.text;
112 this.text = text;
113 this.attributes.text = text;
114 if(this.rendered){ // event without subscribing
115 this.ui.onTextChange(this, text, oldText);
116 }
117 this.fireEvent('textchange', this, text, oldText);
118 },
119
120 select : function(){
121 this.getOwnerTree().getSelectionModel().select(this);
122 },
123
124 unselect : function(){
125 this.getOwnerTree().getSelectionModel().unselect(this);
126 },
127
128 isSelected : function(){
129 return this.getOwnerTree().getSelectionModel().isSelected(node);
130 },
131
132 expand : function(deep, anim, callback){
133 if(!this.expanded){
134 if(this.fireEvent('beforeexpand', this, deep, anim) === false){
135 return;
136 }
137 if(!this.childrenRendered){
138 this.renderChildren();
139 }
140 this.expanded = true;
141 if((this.getOwnerTree().animate && anim !== false) || anim){
142 this.ui.animExpand(function(){
143 this.fireEvent('expand', this);
144 if(typeof callback == 'function'){
145 callback(this);
146 }
147 if(deep === true){
148 this.expandChildNodes(true);
149 }
150 }.createDelegate(this));
151 return;
152 }else{
153 this.ui.expand();
154 this.fireEvent('expand', this);
155 if(typeof callback == 'function'){
156 callback(this);
157 }
158 }
159 }else{
160 if(typeof callback == 'function'){
161 callback(this);
162 }
163 }
164 if(deep === true){
165 this.expandChildNodes(true);
166 }
167 },
168
169 collapse : function(deep, anim){
170 if(this.expanded && (!this.isRoot || (this.isRoot && this.getOwnerTree().rootVisible))){
171 if(this.fireEvent('beforecollapse', this, deep, anim) === false){
172 return;
173 }
174 this.expanded = false;
175 if((this.getOwnerTree().animate && anim !== false) || anim){
176 this.ui.animCollapse(function(){
177 this.fireEvent('collapse', this);
178 if(deep === true){
179 this.collapseChildNodes(true);
180 }
181 }.createDelegate(this));
182 return;
183 }else{
184 this.ui.collapse();
185 this.fireEvent('collapse', this);
186 }
187 }
188 if(deep === true){
189 var cs = this.childNodes;
190 for(var i = 0, len = cs.length; i < len; i++) {
191 cs[i].collapse(true)
192 }
193 }
194 },
195
196 delayedExpand : function(delay){
197 if(!this.expandProcId){
198 this.expandProcId = this.expand.defer(delay, this);
199 }
200 },
201
202 cancelExpand : function(){
203 if(this.expandProcId){
204 clearTimeout(this.expandProcId);
205 }
206 this.expandProcId = false;
207 },
208
209 toggle : function(){
210 if(this.expanded){
211 this.collapse();
212 }else{
213 this.expand();
214 }
215 },
216
217 ensureVisible : function(){
218 if(this.parentNode){
219 this.parentNode.bubble(function(){
220 this.expand(false, false);
221 });
222 }
223 },
224
225 expandChildNodes : function(deep){
226 var cs = this.childNodes;
227 for(var i = 0, len = cs.length; i < len; i++) {
228 cs[i].expand(deep);
229 }
230 },
231
232 collapseChildNodes : function(deep){
233 var cs = this.childNodes;
234 for(var i = 0, len = cs.length; i < len; i++) {
235 cs[i].expand(deep);
236 }
237 },
238
239 disable : function(){
240 this.disabled = true;
241 this.unselect();
242 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
243 this.ui.onDisableChange(this, true);
244 }
245 this.fireEvent('disabledchange', this, true);
246 },
247
248 enable : function(){
249 this.disabled = false;
250 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
251 this.ui.onDisableChange(this, false);
252 }
253 this.fireEvent('disabledchange', this, false);
254 },
255
256 renderChildren : function(suppressEvent){
257 if(suppressEvent !== false){
258 this.fireEvent('beforechildrenrendered', this);
259 }
260 var cs = this.childNodes;
261 for(var i = 0, len = cs.length; i < len; i++){
262 cs[i].render(true);
263 }
264 this.childrenRendered = true;
265 },
266
267 sort : function(fn, scope){
268 YAHOO.ext.tree.TreeNode.superclass.sort.apply(this, arguments);
269 if(this.childrenRendered){
270 var cs = this.childNodes;
271 for(var i = 0, len = cs.length; i < len; i++){
272 cs[i].render(true);
273 }
274 }
275 },
276
277 render : function(bulkRender){
278 this.ui.render(bulkRender);
279 if(!this.rendered){
280 this.rendered = true;
281 if(this.expanded){
282 this.expanded = false;
283 this.expand(false, false);
284 }
285 }
286 },
287
288 renderIndent : function(deep, refresh){
289 if(refresh){
290 this.ui.childIndent = null;
291 }
292 this.ui.renderIndent();
293 if(deep === true && this.childrenRendered){
294 var cs = this.childNodes;
295 for(var i = 0, len = cs.length; i < len; i++){
296 cs[i].renderIndent(true, refresh);
297 }
298 }
299 }
300});
diff --git a/frontend/beta/js/YUI-extensions/tree/TreeNodeUI.js b/frontend/beta/js/YUI-extensions/tree/TreeNodeUI.js
new file mode 100644
index 0000000..80927f4
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/tree/TreeNodeUI.js
@@ -0,0 +1,452 @@
1/**
2 * The TreeNode UI implementation is separate from the
3 * tree implementation. Unless you are customizing the tree UI,
4 * you should never have to use this directly.
5 */
6YAHOO.ext.tree.TreeNodeUI = function(node){
7 this.node = node;
8 this.rendered = false;
9 this.animating = false;
10};
11
12YAHOO.ext.tree.TreeNodeUI.prototype = {
13 emptyIcon : Ext.BLANK_IMAGE_URL,
14
15 removeChild : function(node){
16 if(this.rendered){
17 this.ctNode.removeChild(node.ui.getEl());
18 }
19 },
20
21 beforeLoad : function(){
22 YAHOO.util.Dom.addClass(this.elNode, 'ytree-node-loading');
23 },
24
25 afterLoad : function(){
26 YAHOO.util.Dom.removeClass(this.elNode, 'ytree-node-loading');
27 },
28
29 onTextChange : function(node, text, oldText){
30 if(this.rendered){
31 this.textNode.innerHTML = text;
32 }
33 },
34
35 onDisableChange : function(node, state){
36 this.disabled = state;
37 if(state){
38 YAHOO.util.Dom.addClass(this.elNode, 'ytree-node-disabled');
39 }else{
40 YAHOO.util.Dom.removeClass(this.elNode, 'ytree-node-disabled');
41 }
42 },
43
44 onSelectedChange : function(state){
45 if(state){
46 this.focus();
47 YAHOO.util.Dom.addClass(this.elNode, 'ytree-selected');
48 }else{
49 this.blur();
50 YAHOO.util.Dom.removeClass(this.elNode, 'ytree-selected');
51 }
52 },
53
54 onMove : function(tree, node, oldParent, newParent, index, refNode){
55 this.childIndent = null;
56 if(this.rendered){
57 var targetNode = newParent.ui.getContainer();
58 if(!targetNode){//target not rendered
59 this.holder = document.createElement('div');
60 this.holder.appendChild(this.wrap);
61 return;
62 }
63 var insertBefore = refNode ? refNode.ui.getEl() : null;
64 if(insertBefore){
65 targetNode.insertBefore(this.wrap, insertBefore);
66 }else{
67 targetNode.appendChild(this.wrap);
68 }
69 this.node.renderIndent(true);
70 }
71 },
72
73 remove : function(){
74 if(this.rendered){
75 this.holder = document.createElement('div');
76 this.holder.appendChild(this.wrap);
77 }
78 },
79
80 fireEvent : function(){
81 this.node.fireEvent.apply(this.node, arguments);
82 },
83
84 initEvents : function(){
85 this.node.on('move', this.onMove, this, true);
86 //this.node.on('hiddenchange', this.onHiddenChange, this, true);
87
88 // these were optimized out but a custom UI could use them
89 //this.node.on('remove', this.onChildRemoved, this, true);
90 //this.node.on('selectedstatechange', this.onSelectedChange, this, true);
91 //this.node.on('disabledchange', this.onDisableChange, this, true);
92 //this.node.on('textchange', this.onTextChange, this, true);
93
94 var E = YAHOO.util.Event;
95 var a = this.anchor;
96
97 var el = YAHOO.ext.Element.fly(a);
98
99 if(YAHOO.ext.util.Browser.isOpera){ // opera render bug ignores the CSS
100 el.setStyle('text-decoration', 'none');
101 }
102
103 el.mon('click', this.onClick, this, true);
104 el.mon('dblclick', this.onDblClick, this, true);
105 el.mon('contextmenu', this.onContextMenu, this, true);
106
107 //el.on('focus', function(){
108 // this.node.getOwnerTree().getSelectionModel().select(this.node);
109 //}, this, true);
110
111 var icon = YAHOO.ext.Element.fly(this.iconNode);
112 icon.mon('click', this.onClick, this, true);
113 icon.mon('dblclick', this.onDblClick, this, true);
114 icon.mon('contextmenu', this.onContextMenu, this, true);
115 E.on(this.ecNode, 'click', this.ecClick, this, true);
116
117 if(this.node.disabled){
118 YAHOO.util.Dom.addClass(this.elNode, 'ytree-node-disabled');
119 }
120 if(this.node.hidden){
121 YAHOO.util.Dom.addClass(this.elNode, 'ytree-node-disabled');
122 }
123 var dd = this.node.ownerTree.enableDD || this.node.ownerTree.enableDrag || this.node.ownerTree.enableDrop;
124 if(dd && (!this.node.isRoot || this.node.ownerTree.rootVisible)){
125 YAHOO.ext.dd.Registry.register(this.elNode, {
126 node: this.node,
127 handles: [this.iconNode, this.textNode],
128 isHandle: false
129 });
130 }
131 },
132
133 hide : function(){
134 if(this.rendered){
135 this.wrap.style.display = 'none';
136 }
137 },
138
139 show : function(){
140 if(this.rendered){
141 this.wrap.style.display = '';
142 }
143 },
144
145 onContextMenu : function(e){
146 e.preventDefault();
147 this.focus();
148 this.fireEvent('contextmenu', this.node, e);
149 },
150
151 onClick : function(e){
152 if(this.dropping){
153 return;
154 }
155 if(this.fireEvent('beforeclick', this.node, e) !== false){
156 if(!this.disabled && this.node.attributes.href){
157 this.focus();
158 this.fireEvent('click', this.node, e);
159 return;
160 }
161 e.preventDefault();
162 if(this.disabled){
163 return;
164 }
165 this.focus();
166 this.fireEvent('click', this.node, e);
167 }else{
168 e.stopEvent();
169 }
170 },
171
172 onDblClick : function(e){
173 e.preventDefault();
174 if(this.disabled){
175 return;
176 }
177 if(!this.animating && this.node.hasChildNodes()){
178 this.node.toggle();
179 }
180 this.fireEvent('dblclick', this.node, e);
181 },
182
183 ecClick : function(e){
184 if(!this.animating && this.node.hasChildNodes()){
185 this.node.toggle();
186 }
187 },
188
189 startDrop : function(){
190 this.dropping = true;
191 },
192
193 // delayed drop so the click event doesn't get fired on a drop
194 endDrop : function(){
195 setTimeout(function(){
196 this.dropping = false;
197 }.createDelegate(this), 50);
198 },
199
200 expand : function(){
201 this.updateExpandIcon();
202 this.ctNode.style.display = '';
203 },
204
205 focus : function(){
206 try{
207 this.anchor.focus();
208 }catch(e){}
209 },
210
211 blur : function(){
212 try{
213 this.anchor.blur();
214 }catch(e){}
215 },
216
217 animExpand : function(callback){
218 if(this.animating && this.anim){
219 this.anim.stop();
220 }
221 this.animating = true;
222 this.updateExpandIcon();
223 var ct = this.ctNode;
224 var cs = ct.style;
225 cs.position = 'absolute';
226 cs.visibility = 'hidden';
227 cs.display = '';
228 var h = ct.clientHeight;
229 cs.overflow = 'hidden';
230 cs.height = '1px';
231 cs.position = '';
232 cs.visibility = '';
233 var anim = new YAHOO.util.Anim(ct, {
234 height: {to: h}
235 }, this.node.ownerTree.duration || .25, YAHOO.util.Easing.easeOut);
236 anim.onComplete.subscribe(function(){
237 cs.overflow = '';
238 cs.height = '';
239 this.animating = false;
240 this.anim = null;
241 if(typeof callback == 'function'){
242 callback();
243 }
244 }, this, true);
245 this.anim = anim;
246 anim.animate();
247 },
248
249 highlight : function(){
250 var tree = this.node.getOwnerTree();
251 var hlColor = tree.hlColor || 'C3DAF9';
252 var hlBaseColor = tree.hlBaseColor || 'FFFFFF';
253 var anim = new YAHOO.util.ColorAnim(this.wrap, {
254 backgroundColor: {from: hlColor, to: hlBaseColor}
255 }, .75, YAHOO.util.Easing.easeNone);
256 anim.onComplete.subscribe(function(){
257 YAHOO.util.Dom.setStyle(this.wrap, 'background-color', '');
258 }, this, true);
259 anim.animate();
260 },
261
262 collapse : function(){
263 this.updateExpandIcon();
264 this.ctNode.style.display = 'none';
265 },
266
267 animCollapse : function(callback){
268 if(this.animating && this.anim){
269 this.anim.stop();
270 }
271 this.animating = true;
272 this.updateExpandIcon();
273 var ct = this.ctNode;
274 var cs = ct.style;
275 cs.height = ct.offsetHeight +'px';
276 cs.overflow = 'hidden';
277 var anim = new YAHOO.util.Anim(ct, {
278 height: {to: 1}
279 }, this.node.ownerTree.duration || .25, YAHOO.util.Easing.easeOut);
280 anim.onComplete.subscribe(function(){
281 cs.display = 'none';
282 cs.overflow = '';
283 cs.height = '';
284 this.animating = false;
285 this.anim = null;
286 if(typeof callback == 'function'){
287 callback();
288 }
289 }, this, true);
290 this.anim = anim;
291 anim.animate();
292 },
293
294 getContainer : function(){
295 return this.ctNode;
296 },
297
298 getEl : function(){
299 return this.wrap;
300 },
301
302 appendDDGhost : function(ghostNode){
303 ghostNode.appendChild(this.elNode.cloneNode(true));
304 },
305
306 getDDRepairXY : function(){
307 return YAHOO.util.Dom.getXY(this.iconNode);
308 },
309
310 onRender : function(){
311 this.render();
312 },
313
314 render : function(bulkRender){
315 var n = this.node;
316 var targetNode = n.parentNode ?
317 n.parentNode.ui.getContainer() : n.ownerTree.container.dom;
318 if(!this.rendered){
319 this.rendered = true;
320 var a = n.attributes;
321
322 // add some indent caching, this helps performance when rendering a large tree
323 this.indentMarkup = '';
324 if(n.parentNode){
325 this.indentMarkup = n.parentNode.ui.getChildIndent();
326 }
327
328 var buf = ['<li class="ytree-node"><div class="ytree-node-el ', n.attributes.cls,'">',
329 '<span class="ytree-node-indent">',this.indentMarkup,'</span>',
330 '<img src="', this.emptyIcon, '" class="ytree-ec-icon">',
331 '<img src="', a.icon || this.emptyIcon, '" class="ytree-node-icon',(a.icon ? ' ytree-node-inline-icon' : ''),'" unselectable="on">',
332 '<a href="',a.href ? a.href : '#','" tabIndex="1" ',
333 a.hrefTarget ? ' target="'+a.hrefTarget+'"' : '','><span unselectable="on">',n.text,'</span></a></div>',
334 '<ul class="ytree-node-ct" style="display:none;"></ul>',
335 '</li>'];
336
337 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
338 this.wrap = YAHOO.ext.DomHelper.insertHtml('beforeBegin',
339 n.nextSibling.ui.getEl(), buf.join(''));
340 }else{
341 this.wrap = YAHOO.ext.DomHelper.insertHtml('beforeEnd', targetNode, buf.join(''));
342 }
343 this.elNode = this.wrap.childNodes[0];
344 this.ctNode = this.wrap.childNodes[1];
345 var cs = this.elNode.childNodes;
346 this.indentNode = cs[0];
347 this.ecNode = cs[1];
348 this.iconNode = cs[2];
349 this.anchor = cs[3];
350 this.textNode = cs[3].firstChild;
351 if(a.qtip){
352 if(this.textNode.setAttributeNS){
353 this.textNode.setAttributeNS('y', 'qtip', a.qtip);
354 if(a.qtipTitle){
355 this.textNode.setAttributeNS('y', 'qtitle', a.qtipTitle);
356 }
357 }else{
358 this.textNode.setAttribute('y:qtip', a.qtip);
359 if(a.qtipTitle){
360 this.textNode.setAttribute('y:qtitle', a.qtipTitle);
361 }
362 }
363 }
364 this.initEvents();
365 //this.renderIndent(); cached above now instead call updateExpandIcon
366 this.updateExpandIcon();
367 }else{
368 if(bulkRender === true) {
369 targetNode.appendChild(this.wrap);
370 }
371 }
372 },
373
374 getAnchor : function(){
375 return this.anchor;
376 },
377
378 getTextEl : function(){
379 return this.textNode;
380 },
381
382 getIconEl : function(){
383 return this.iconNode;
384 },
385
386 updateExpandIcon : function(){
387 if(this.rendered){
388 var n = this.node;
389 var cls = n.isLast() ? "ytree-elbow-end" : "ytree-elbow";
390 var hasChild = n.hasChildNodes();
391 if(hasChild){
392 cls += n.expanded ? '-minus' : '-plus';
393 var c1 = n.expanded ? 'ytree-node-collapsed' : 'ytree-node-expanded';
394 var c2 = n.expanded ? 'ytree-node-expanded' : 'ytree-node-collapsed';
395 YAHOO.util.Dom.removeClass(this.elNode, 'ytree-node-leaf');
396 YAHOO.util.Dom.replaceClass(this.elNode, c1, c2);
397 }else{
398 YAHOO.util.Dom.replaceClass(this.elNode, 'ytree-node-expanded', 'ytree-node-leaf');
399 }
400 this.ecNode.className = 'ytree-ec-icon '+cls;
401 }
402 },
403
404 getChildIndent : function(){
405 if(!this.childIndent){
406 var buf = [];
407 var p = this.node;
408 while(p){
409 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
410 if(!p.isLast()) {
411 buf.unshift('<img src="'+this.emptyIcon+'" class="ytree-elbow-line">');
412 } else {
413 buf.unshift('<img src="'+this.emptyIcon+'" class="ytree-icon">');
414 }
415 }
416 p = p.parentNode;
417 }
418 this.childIndent = buf.join('');
419 }
420 return this.childIndent;
421 },
422
423 renderIndent : function(){
424 if(this.rendered){
425 var indent = '';
426 var p = this.node.parentNode;
427 if(p){
428 indent = p.ui.getChildIndent();
429 }
430 if(this.indentMarkup != indent){ // don't rerender if not required
431 this.indentNode.innerHTML = indent;
432 this.indentMarkup = indent;
433 }
434 this.updateExpandIcon();
435 }
436 }
437};
438
439YAHOO.ext.tree.RootTreeNodeUI = function(){
440 YAHOO.ext.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
441};
442YAHOO.extendX(YAHOO.ext.tree.RootTreeNodeUI, YAHOO.ext.tree.TreeNodeUI);
443YAHOO.ext.tree.RootTreeNodeUI.prototype.render = function(){
444 if(!this.rendered){
445 var targetNode = this.node.ownerTree.container.dom;
446 this.node.expanded = true;
447 targetNode.innerHTML = '<div class="ytree-root-node"></div>';
448 this.wrap = this.ctNode = targetNode.firstChild;
449 }
450};
451YAHOO.ext.tree.RootTreeNodeUI.prototype.collapse = function(){
452};
diff --git a/frontend/beta/js/YUI-extensions/tree/TreePanel.js b/frontend/beta/js/YUI-extensions/tree/TreePanel.js
new file mode 100644
index 0000000..202c0d0
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/tree/TreePanel.js
@@ -0,0 +1,213 @@
1YAHOO.namespace('ext.tree');
2
3YAHOO.ext.tree.TreePanel = function(el, config){
4 YAHOO.ext.tree.TreePanel.superclass.constructor.call(this);
5 this.el = getEl(el);
6 this.id = this.el.id;
7 YAHOO.ext.util.Config.apply(this, config || {}, {
8 rootVisible : true,
9 lines : true,
10 enableDD : false,
11 hlDrop : true/*,
12 hlColor: null,
13 ddGroup : 'TreeDD'
14 hlBaseColor : 'FFFFFF'*/
15
16 });
17 YAHOO.ext.util.Config.apply(this.events, {
18 'beforeload' : true,
19 'load' : true,
20 'textchange' : true,
21 'beforeexpand' : true,
22 'beforecollapse' : true,
23 'expand' : true,
24 'collapse' : true,
25 'disabledchange' : true,
26 'beforeclick':true,
27 'click':true,
28 'dblclick':true,
29 'contentmenu':true,
30 'beforechildrenrendered':true,
31 /**
32 * @event startdrag
33 * Fires when a node starts being dragged
34 * @param {YAHOO.ext.tree.TreePanel} this
35 * @param {YAHOO.ext.tree.TreeNode} node
36 * @param {event} e The raw browser event
37 */
38 'startdrag' : true,
39 /**
40 * @event enddrag
41 * Fires when a drag operation is complete
42 * @param {YAHOO.ext.tree.TreePanel} this
43 * @param {YAHOO.ext.tree.TreeNode} node
44 * @param {event} e The raw browser event
45 */
46 'enddrag' : true,
47 /**
48 * @event dragdrop
49 * Fires when a dragged node is dropped on a valid DD target
50 * @param {YAHOO.ext.tree.TreePanel} this
51 * @param {YAHOO.ext.tree.TreeNode} node
52 * @param {DD} dd The dd it was dropped on
53 * @param {event} e The raw browser event
54 */
55 'dragdrop' : true,
56 /**
57 * @event beforenodedrop
58 * Fires when a DD object is dropped on a node in this tree for preprocessing. This event can cancel.
59 * @param {Object} dropEvent
60 */
61 'beforenodedrop' : true,
62 /**
63 * @event nodedrop
64 * Fires after a DD object is dropped on a node in this tree
65 * @param {Object} dropEvent
66 */
67 'nodedrop' : true,
68 /**
69 * @event nodedragover
70 * Fires when a tree node is being target
71 * @param {Object} dragOverEvent
72 */
73 'nodedragover' : true
74 });
75 if(this.singleExpand){
76 this.on('beforeexpand', this.restrictExpand, this, true);
77 }
78 // problem with safari and animation
79 // I am investigating
80 if(YAHOO.ext.util.Browser.isSafari){
81 this.animate = false;
82 }
83};
84YAHOO.extendX(YAHOO.ext.tree.TreePanel, YAHOO.ext.data.Tree, {
85 restrictExpand : function(node){
86 var p = node.parentNode;
87 if(p){
88 if(p.expandedChild && p.expandedChild.parentNode == p){
89 p.expandedChild.collapse();
90 }
91 p.expandedChild = node;
92 }
93 },
94
95 setRootNode : function(node){
96 YAHOO.ext.tree.TreePanel.superclass.setRootNode.call(this, node);
97 if(!this.rootVisible){
98 node.ui = new YAHOO.ext.tree.RootTreeNodeUI(node);
99 }
100 return node;
101 },
102
103 getEl : function(){
104 return this.el;
105 },
106
107 getLoader : function(){
108 return this.loader;
109 },
110
111 expandAll : function(){
112 this.root.expand(true);
113 },
114
115 collapseAll : function(){
116 this.root.collapse(true);
117 },
118
119 getSelectionModel : function(){
120 if(!this.selModel){
121 this.selModel = new YAHOO.ext.tree.DefaultSelectionModel();
122 }
123 return this.selModel;
124 },
125
126 expandPath : function(path, attr, callback){
127 attr = attr || 'id';
128 var keys = path.split(this.pathSeparator);
129 var curNode = this.root;
130 if(curNode.attributes[attr] != keys[1]){ // invalid root
131 if(callback){
132 callback(false, null);
133 }
134 return;
135 }
136 var index = 1;
137 var f = function(){
138 if(++index == keys.length){
139 if(callback){
140 callback(true, curNode);
141 }
142 return;
143 }
144 var c = curNode.findChild(attr, keys[index]);
145 if(!c){
146 if(callback){
147 callback(false, curNode);
148 }
149 return;
150 }
151 curNode = c;
152 c.expand(false, false, f);
153 }
154 curNode.expand(false, false, f);
155 },
156
157 selectPath : function(path, attr, callback){
158 attr = attr || 'id';
159 var keys = path.split(this.pathSeparator);
160 var v = keys.pop();
161 if(keys.length > 0){
162 var f = function(success, node){
163 if(success && node){
164 var n = node.findChild(attr, v);
165 if(n){
166 n.select();
167 if(callback){
168 callback(true, n);
169 }
170 }
171 }else{
172 if(callback){
173 callback(false, n);
174 }
175 }
176 };
177 this.expandPath(keys.join(this.pathSeparator), attr, f);
178 }else{
179 this.root.select();
180 if(callback){
181 callback(true, this.root);
182 }
183 }
184 },
185
186 render : function(){
187 this.container = this.el.createChild({tag:'ul',
188 cls:'ytree-root-ct ' +
189 (this.lines ? 'ytree-lines' : 'ytree-no-lines')});
190
191 if(this.containerScroll){
192 YAHOO.ext.dd.ScrollManager.register(this.el);
193 }
194
195 if((this.enableDD || this.enableDrop) && !this.dropZone){
196 this.dropZone = new YAHOO.ext.tree.TreeDropZone(this, this.dropConfig || {
197 ddGroup: this.ddGroup || 'TreeDD'
198 });
199 }
200 if((this.enableDD || this.enableDrag) && !this.dragZone){
201 this.dragZone = new YAHOO.ext.tree.TreeDragZone(this, this.dragConfig || {
202 ddGroup: this.ddGroup || 'TreeDD',
203 scroll: this.ddScroll
204 });
205 }
206 this.getSelectionModel().init(this);
207 this.root.render();
208 if(!this.rootVisible){
209 this.root.renderChildren();
210 }
211 return this;
212 }
213});
diff --git a/frontend/beta/js/YUI-extensions/tree/TreeSelectionModel.js b/frontend/beta/js/YUI-extensions/tree/TreeSelectionModel.js
new file mode 100644
index 0000000..4fed88e
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/tree/TreeSelectionModel.js
@@ -0,0 +1,195 @@
1YAHOO.ext.tree.DefaultSelectionModel = function(){
2 this.selNode = null;
3
4 this.events = {
5 'selectionchange' : true
6 };
7};
8
9YAHOO.extendX(YAHOO.ext.tree.DefaultSelectionModel, YAHOO.ext.util.Observable, {
10 init : function(tree){
11 this.tree = tree;
12 tree.el.mon('keydown', this.onKeyDown, this, true);
13 tree.on('click', this.onNodeClick, this, true);
14 },
15
16 onNodeClick : function(node, e){
17 this.select(node);
18 },
19
20 select : function(node){
21 if(this.selNode && this.selNode != node){
22 this.selNode.ui.onSelectedChange(false);
23 }
24 this.selNode = node;
25 node.ui.onSelectedChange(true);
26 this.fireEvent('selectionchange', this, node);
27 return node;
28 },
29
30 unselect : function(node){
31 if(this.selNode == node){
32 this.clearSelections();
33 }
34 },
35
36 clearSelections : function(){
37 var n = this.selNode;
38 if(n){
39 n.ui.onSelectedChange(false);
40 this.selNode = null;
41 this.fireEvent('selectionchange', this, null);
42 }
43 return n;
44 },
45
46 getSelectedNode : function(){
47 return this.selNode;
48 },
49
50 isSelected : function(node){
51 return this.selNode == node;
52 },
53
54 onKeyDown : function(e){
55 var s = this.selNode || this.lastSelNode;
56 // undesirable, but required
57 var sm = this;
58 if(!s){
59 return;
60 }
61 var k = e.getKey();
62 //alert(k)
63 switch(k){
64 case e.DOWN:
65 e.preventDefault();
66 if(s.firstChild && s.isExpanded()){
67 this.select(s.firstChild, e);
68 }else if(s.nextSibling){
69 this.select(s.nextSibling, e);
70 }else if(s.parentNode){
71 s.parentNode.bubble(function(){
72 if(this.nextSibling){
73 sm.select(this.nextSibling, e);
74 return false;
75 }
76 });
77 }
78 break;
79 case e.UP:
80 e.preventDefault();
81 var ps = s.previousSibling;
82 if(ps){
83 if(!ps.isExpanded()){
84 this.select(ps, e);
85 }else{
86 var lc = ps.lastChild;
87 while(lc && lc.isExpanded()){
88 lc = lc.lastChild;
89 }
90 this.select(lc, e);
91 }
92 }else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
93 this.select(s.parentNode, e);
94 }
95 break;
96 case e.RIGHT:
97 e.preventDefault();
98 if(s.hasChildNodes()){
99 if(!s.isExpanded()){
100 s.expand();
101 }else if(s.firstChild){
102 this.select(s.firstChild, e);
103 }
104 }
105 break;
106 case e.LEFT:
107 e.preventDefault();
108 if(s.hasChildNodes() && s.isExpanded()){
109 s.collapse();
110 }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
111 this.select(s.parentNode, e);
112 }
113 break;
114 };
115 }
116});
117
118YAHOO.ext.tree.MultiSelectionModel = function(){
119 this.selNodes = [];
120 this.selMap = {};
121 this.events = {
122 'selectionchange' : true
123 };
124};
125
126YAHOO.extendX(YAHOO.ext.tree.MultiSelectionModel, YAHOO.ext.util.Observable, {
127 init : function(tree){
128 this.tree = tree;
129 tree.el.mon('keydown', this.onKeyDown, this, true);
130 tree.on('click', this.onNodeClick, this, true);
131 },
132
133 onNodeClick : function(node, e){
134 this.select(node, e, e.ctrlKey);
135 },
136
137 select : function(node, e, keepExisting){
138 if(keepExisting !== true){
139 this.clearSelections(true);
140 }
141 this.selNodes.push(node);
142 this.selMap[node.id] = node;
143 this.lastSelNode = node;
144 node.ui.onSelectedChange(true);
145 this.fireEvent('selectionchange', this, this.selNodes);
146 return node;
147 },
148
149 unselect : function(node){
150 if(this.selMap[node.id]){
151 node.ui.onSelectedChange(false);
152 var sn = this.selNodes;
153 var index = -1;
154 if(sn.indexOf){
155 index = sn.indexOf(node);
156 }else{
157 for(var i = 0, len = sn.length; i < len; i++){
158 if(sn[i] == node){
159 index = i;
160 break;
161 }
162 }
163 }
164 if(index != -1){
165 this.selNodes.splice(index, 1);
166 }
167 delete this.selMap[node.id];
168 this.fireEvent('selectionchange', this, this.selNodes);
169 }
170 },
171
172 clearSelections : function(suppressEvent){
173 var sn = this.selNodes;
174 if(sn.length > 0){
175 for(var i = 0, len = sn.length; i < len; i++){
176 sn[i].ui.onSelectedChange(false);
177 }
178 this.selNodes = [];
179 this.selMap = {};
180 if(suppressEvent !== true){
181 this.fireEvent('selectionchange', this, this.selNodes);
182 }
183 }
184 },
185
186 isSelected : function(node){
187 return this.selMap[node.id] ? true : false;
188 },
189
190 getSelectedNodes : function(){
191 return this.selNodes;
192 },
193
194 onKeyDown : YAHOO.ext.tree.DefaultSelectionModel.prototype.onKeyDown
195});
diff --git a/frontend/beta/js/YUI-extensions/tree/TreeSorter.js b/frontend/beta/js/YUI-extensions/tree/TreeSorter.js
new file mode 100644
index 0000000..9960703
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/tree/TreeSorter.js
@@ -0,0 +1,49 @@
1YAHOO.ext.tree.TreeSorter = function(tree, config){
2 YAHOO.ext.util.Config.apply(this, config);
3 tree.on('beforechildrenrendered', this.doSort, this, true);
4 tree.on('append', this.updateSort, this, true);
5 tree.on('insert', this.updateSort, this, true);
6
7 var dsc = this.dir && this.dir.toLowerCase() == 'desc';
8 var p = this.property || 'text';
9 var sortType = this.sortType;
10 var fs = this.folderSort;
11 var cs = this.caseSensitive === true;
12
13 this.sortFn = function(n1, n2){
14 if(fs){
15 if(n1.leaf && !n2.leaf){
16 return 1;
17 }
18 if(!n1.leaf && n2.leaf){
19 return -1;
20 }
21 }
22 var v1 = sortType ? sortType(n1) : (cs ? n1[p] : n1[p].toUpperCase());
23 var v2 = sortType ? sortType(n2) : (cs ? n2[p] : n2[p].toUpperCase());
24 if(v1 < v2){
25 return dsc ? +1 : -1;
26 }else if(v1 > v2){
27 return dsc ? -1 : +1;
28 }else{
29 return 0;
30 }
31 };
32};
33
34YAHOO.ext.tree.TreeSorter.prototype = {
35 doSort : function(node){
36 node.sort(this.sortFn);
37 },
38
39 compareNodes : function(n1, n2){
40
41 return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
42 },
43
44 updateSort : function(tree, node){
45 if(node.childrenRendered){
46 this.doSort.defer(1, this, [node]);
47 }
48 }
49};
diff --git a/frontend/beta/js/YUI-extensions/widgets/BasicDialog.js b/frontend/beta/js/YUI-extensions/widgets/BasicDialog.js
new file mode 100644
index 0000000..3912568
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/widgets/BasicDialog.js
@@ -0,0 +1,1046 @@
1/**
2 * @class YAHOO.ext.BasicDialog
3 * @extends YAHOO.ext.util.Observable
4 * Lightweight Dialog Class.
5 *
6 * The code below lists all configuration options along with the default value.
7 * If the default value is what you want you can leave it out:
8 * <pre><code>
9 var dlg = new YAHOO.ext.BasicDialog('element-id', {
10 autoCreate: false, (true to auto create from scratch, or DomHelper Object)
11 title: null, (title to set at config time)
12 width: (css),
13 height: (css),
14 x: 200, //(defaults to center screen if blank)
15 y: 500, //(defaults to center screen if blank)
16 animateTarget: null,// (no animation) This is the id or element to animate from
17 resizable: true,
18 minHeight: 80,
19 minWidth: 200,
20 modal: false,
21 autoScroll: true,
22 closable: true,
23 constraintoviewport: true,
24 draggable: true,
25 autoTabs: false, (if true searches child nodes for elements with class ydlg-tab and converts them to tabs)
26 tabTag: 'div', // the tag name of tab elements
27 proxyDrag: false, (drag a proxy element rather than the dialog itself)
28 fixedcenter: false,
29 shadow: false,
30 buttonAlign: 'right',
31 minButtonWidth: 75,
32 shim: false // true to create an iframe shim to
33 // keep selects from showing through
34 });
35 </code></pre>
36 * @constructor
37 * Create a new BasicDialog.
38 * @param {String/HTMLElement/YAHOO.ext.Element} el The id of or container element
39 * @param {Object} config configuration options
40 */
41YAHOO.ext.BasicDialog = function(el, config){
42 this.el = getEl(el);
43 var dh = YAHOO.ext.DomHelper;
44 if(!this.el && config && config.autoCreate){
45 if(typeof config.autoCreate == 'object'){
46 if(!config.autoCreate.id){
47 config.autoCreate.id = el;
48 }
49 this.el = dh.append(document.body,
50 config.autoCreate, true);
51 }else{
52 this.el = dh.append(document.body,
53 {tag: 'div', id: el}, true);
54 }
55 }
56 el = this.el;
57 el.setDisplayed(true);
58 el.hide = this.hideAction;
59 this.id = el.id;
60 el.addClass('ydlg');
61
62 YAHOO.ext.util.Config.apply(this, config);
63
64 this.proxy = el.createProxy('ydlg-proxy');
65 this.proxy.hide = this.hideAction;
66 this.proxy.setOpacity(.5);
67 this.proxy.hide();
68
69 if(config.width){
70 el.setWidth(config.width);
71 }
72 if(config.height){
73 el.setHeight(config.height);
74 }
75 this.size = el.getSize();
76 if(typeof config.x != 'undefined' && typeof config.y != 'undefined'){
77 this.xy = [config.x,config.y];
78 }else{
79 this.xy = el.getCenterXY(true);
80 }
81 // find the header, body and footer
82 var cn = el.dom.childNodes;
83 for(var i = 0, len = cn.length; i < len; i++) {
84 var node = cn[i];
85 if(node && node.nodeType == 1){
86 if(YAHOO.util.Dom.hasClass(node, 'ydlg-hd')){
87 this.header = getEl(node, true);
88 }else if(YAHOO.util.Dom.hasClass(node, 'ydlg-bd')){
89 this.body = getEl(node, true);
90 }else if(YAHOO.util.Dom.hasClass(node, 'ydlg-ft')){
91 /**
92 * The footer element
93 * @type YAHOO.ext.Element
94 */
95 this.footer = getEl(node, true);
96 }
97 }
98 }
99
100 if(!this.header){
101 /**
102 * The header element
103 * @type YAHOO.ext.Element
104 */
105 this.header = this.body ?
106 dh.insertBefore(this.body.dom, {tag: 'div', cls:'ydlg-hd'}, true) :
107 dh.append(el.dom, {tag: 'div', cls:'ydlg-hd'}, true);
108 }
109 if(this.title){
110 this.header.update(this.title);
111 }
112 // this element allows the dialog to be focused for keyboard event
113 this.focusEl = dh.append(el.dom, {tag: 'a', href:'#', cls:'ydlg-focus', tabIndex:'-1'}, true);
114 this.focusEl.swallowEvent('click', true);
115 if(!this.body){
116 /**
117 * The body element
118 * @type YAHOO.ext.Element
119 */
120 this.body = dh.append(el.dom, {tag: 'div', cls:'ydlg-bd'}, true);
121 }
122 // wrap the header for special rendering
123 var hl = dh.insertBefore(this.header.dom, {tag: 'div', cls:'ydlg-hd-left'});
124 var hr = dh.append(hl, {tag: 'div', cls:'ydlg-hd-right'});
125 hr.appendChild(this.header.dom);
126
127 // wrap the body and footer for special rendering
128 this.bwrap = dh.insertBefore(this.body.dom, {tag: 'div', cls:'ydlg-dlg-body'}, true);
129 this.bwrap.dom.appendChild(this.body.dom);
130 if(this.footer) this.bwrap.dom.appendChild(this.footer.dom);
131
132 this.bg = this.el.createChild({
133 tag: 'div', cls:'ydlg-bg',
134 html: '<div class="ydlg-bg-left"><div class="ydlg-bg-right"><div class="ydlg-bg-center">&#160;</div></div></div>'
135 });
136 this.centerBg = getEl(this.bg.dom.firstChild.firstChild.firstChild);
137
138
139 if(this.autoScroll !== false && !this.autoTabs){
140 this.body.setStyle('overflow', 'auto');
141 }
142 if(this.closable !== false){
143 this.el.addClass('ydlg-closable');
144 this.close = dh.append(el.dom, {tag: 'div', cls:'ydlg-close'}, true);
145 this.close.mon('click', this.closeClick, this, true);
146 this.close.addClassOnOver('ydlg-close-over');
147 }
148 if(this.resizable !== false){
149 this.el.addClass('ydlg-resizable');
150 this.resizer = new YAHOO.ext.Resizable(el, {
151 minWidth: this.minWidth || 80,
152 minHeight:this.minHeight || 80,
153 handles: 'all',
154 pinned: true
155 });
156 this.resizer.on('beforeresize', this.beforeResize, this, true);
157 this.resizer.on('resize', this.onResize, this, true);
158 }
159 if(this.draggable !== false){
160 el.addClass('ydlg-draggable');
161 if (!this.proxyDrag) {
162 var dd = new YAHOO.util.DD(el.dom.id, 'WindowDrag');
163 }
164 else {
165 var dd = new YAHOO.util.DDProxy(el.dom.id, 'WindowDrag', {dragElId: this.proxy.id});
166 }
167 dd.setHandleElId(this.header.id);
168 dd.endDrag = this.endMove.createDelegate(this);
169 dd.startDrag = this.startMove.createDelegate(this);
170 dd.onDrag = this.onDrag.createDelegate(this);
171 this.dd = dd;
172 }
173 if(this.modal){
174 this.mask = dh.append(document.body, {tag: 'div', cls:'ydlg-mask'}, true);
175 this.mask.enableDisplayMode('block');
176 this.mask.hide();
177 this.el.addClass('ydlg-modal');
178 }
179 if(this.shadow){
180 this.shadow = el.createProxy({tag: 'div', cls:'ydlg-shadow'});
181 this.shadow.setOpacity(.3);
182 this.shadow.setVisibilityMode(YAHOO.ext.Element.VISIBILITY);
183 this.shadow.setDisplayed('block');
184 this.shadow.hide = this.hideAction;
185 this.shadow.hide();
186 }else{
187 this.shadowOffset = 0;
188 }
189 // adding an iframe shim to FF kills the cursor on the PC, but is needed on the Mac
190 // where it (luckily) does not kill the cursor
191 if(!YAHOO.ext.util.Browser.isGecko || YAHOO.ext.util.Browser.isMac){
192 if(this.shim){
193 this.shim = this.el.createShim();
194 this.shim.hide = this.hideAction;
195 this.shim.hide();
196 }
197 }else{
198 this.shim = false;
199 }
200 if(this.autoTabs){
201 this.initTabs();
202 }
203 this.syncBodyHeight();
204 this.events = {
205 /**
206 * @event keydown
207 * Fires when a key is pressed
208 * @param {YAHOO.ext.BasicDialog} this
209 * @param {YAHOO.ext.EventObject} e
210 */
211 'keydown' : true,
212 /**
213 * @event move
214 * Fires when this dialog is moved by the user.
215 * @param {YAHOO.ext.BasicDialog} this
216 * @param {Number} x The new page X
217 * @param {Number} y The new page Y
218 */
219 'move' : true,
220 /**
221 * @event resize
222 * Fires when this dialog is resized by the user.
223 * @param {YAHOO.ext.BasicDialog} this
224 * @param {Number} width The new width
225 * @param {Number} height The new height
226 */
227 'resize' : true,
228 /**
229 * @event beforehide
230 * Fires before this dialog is hidden.
231 * @param {YAHOO.ext.BasicDialog} this
232 */
233 'beforehide' : true,
234 /**
235 * @event hide
236 * Fires when this dialog is hidden.
237 * @param {YAHOO.ext.BasicDialog} this
238 */
239 'hide' : true,
240 /**
241 * @event beforeshow
242 * Fires before this dialog is shown.
243 * @param {YAHOO.ext.BasicDialog} this
244 */
245 'beforeshow' : true,
246 /**
247 * @event show
248 * Fires when this dialog is shown.
249 * @param {YAHOO.ext.BasicDialog} this
250 */
251 'show' : true
252 };
253 el.mon('keydown', this.onKeyDown, this, true);
254 el.mon("mousedown", this.toFront, this, true);
255
256 YAHOO.ext.EventManager.onWindowResize(this.adjustViewport, this, true);
257 this.el.hide();
258 YAHOO.ext.DialogManager.register(this);
259};
260
261YAHOO.extendX(YAHOO.ext.BasicDialog, YAHOO.ext.util.Observable, {
262 shadowOffset: 3,
263 minHeight: 80,
264 minWidth: 200,
265 minButtonWidth: 75,
266 defaultButton: null,
267 buttonAlign: 'right',
268 /**
269 * Sets the dialog title.
270 * @param {String} text
271 * @return {YAHOO.ext.BasicDialog} this
272 */
273 setTitle : function(text){
274 this.header.update(text);
275 return this;
276 },
277
278 closeClick : function(){
279 this.hide();
280 },
281
282 /**
283 * Reinitializes the tabs component, clearing out old tabs and finding new ones.
284 * @return {YAHOO.ext.TabPanel} tabs The tabs component
285 */
286 initTabs : function(){
287 var tabs = this.getTabs();
288 while(tabs.getTab(0)){
289 tabs.removeTab(0);
290 }
291 var tabEls = YAHOO.util.Dom.getElementsByClassName('ydlg-tab', this.tabTag || 'div', this.el.dom);
292 if(tabEls.length > 0){
293 for(var i = 0, len = tabEls.length; i < len; i++) {
294 var tabEl = tabEls[i];
295 tabs.addTab(YAHOO.util.Dom.generateId(tabEl), tabEl.title);
296 tabEl.title = '';
297 }
298 tabs.activate(0);
299 }
300 return tabs;
301 },
302
303 beforeResize : function(){
304 this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
305 },
306
307 onResize : function(){
308 this.refreshSize();
309 this.syncBodyHeight();
310 this.adjustAssets();
311 this.fireEvent('resize', this, this.size.width, this.size.height);
312 },
313
314 onKeyDown : function(e){
315 if(this.isVisible()){
316 this.fireEvent('keydown', this, e);
317 }
318 },
319
320 /**
321 * Resizes the dialog.
322 * @param {Number} width
323 * @param {Number} height
324 * @return {YAHOO.ext.BasicDialog} this
325 */
326 resizeTo : function(width, height){
327 this.el.setSize(width, height);
328 this.size = {width: width, height: height};
329 this.syncBodyHeight();
330 if(this.fixedcenter){
331 this.center();
332 }
333 if(this.isVisible()){
334 this.constrainXY();
335 this.adjustAssets();
336 }
337 this.fireEvent('resize', this, width, height);
338 return this;
339 },
340
341
342 /**
343 * Resizes the dialog to fit the specified content size.
344 * @param {Number} width
345 * @param {Number} height
346 * @return {YAHOO.ext.BasicDialog} this
347 */
348 setContentSize : function(w, h){
349 h += this.getHeaderFooterHeight() + this.body.getMargins('tb');
350 w += this.body.getMargins('lr') + this.bwrap.getMargins('lr') + this.centerBg.getPadding('lr');
351 //if(!this.el.isBorderBox()){
352 h += this.body.getPadding('tb') + this.bwrap.getBorderWidth('tb') + this.body.getBorderWidth('tb') + this.el.getBorderWidth('tb');
353 w += this.body.getPadding('lr') + this.bwrap.getBorderWidth('lr') + this.body.getBorderWidth('lr') + this.bwrap.getPadding('lr') + this.el.getBorderWidth('lr');
354 //}
355 if(this.tabs){
356 h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins('tb') + this.tabs.bodyEl.getPadding('tb');
357 w += this.tabs.bodyEl.getMargins('lr') + this.tabs.bodyEl.getPadding('lr');
358 }
359 this.resizeTo(w, h);
360 return this;
361 },
362
363 /**
364 * Adds a key listener for when this dialog is displayed
365 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
366 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
367 * @param {Function} fn The function to call
368 * @param {Object} scope (optional) The scope of the function
369 * @return {YAHOO.ext.BasicDialog} this
370 */
371 addKeyListener : function(key, fn, scope){
372 var keyCode, shift, ctrl, alt;
373 if(typeof key == 'object' && !(key instanceof Array)){
374 keyCode = key['key'];
375 shift = key['shift'];
376 ctrl = key['ctrl'];
377 alt = key['alt'];
378 }else{
379 keyCode = key;
380 }
381 var handler = function(dlg, e){
382 if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) && (!alt || e.altKey)){
383 var k = e.getKey();
384 if(keyCode instanceof Array){
385 for(var i = 0, len = keyCode.length; i < len; i++){
386 if(keyCode[i] == k){
387 fn.call(scope || window, dlg, k, e);
388 return;
389 }
390 }
391 }else{
392 if(k == keyCode){
393 fn.call(scope || window, dlg, k, e);
394 }
395 }
396 }
397 };
398 this.on('keydown', handler);
399 return this;
400 },
401
402 /**
403 * Returns the TabPanel component (if autoTabs)
404 * @return {YAHOO.ext.TabPanel}
405 */
406 getTabs : function(){
407 if(!this.tabs){
408 this.el.addClass('ydlg-auto-tabs');
409 this.body.addClass(this.tabPosition == 'bottom' ? 'ytabs-bottom' : 'ytabs-top');
410 this.tabs = new YAHOO.ext.TabPanel(this.body.dom, this.tabPosition == 'bottom');
411 }
412 return this.tabs;
413 },
414
415 /**
416 * Adds a button.
417 * @param {String/Object} config A string becomes the button text, an object is expected to be a valid YAHOO.ext.DomHelper element config
418 * @param {Function} handler The function called when the button is clicked
419 * @param {Object} scope (optional) The scope of the handler function
420 * @return {YAHOO.ext.Button}
421 */
422 addButton : function(config, handler, scope){
423 var dh = YAHOO.ext.DomHelper;
424 if(!this.footer){
425 this.footer = dh.append(this.bwrap.dom, {tag: 'div', cls:'ydlg-ft'}, true);
426 }
427 if(!this.btnContainer){
428 var tb = this.footer.createChild({
429 tag:'div',
430 cls:'ydlg-btns ydlg-btns-'+this.buttonAlign,
431 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table>'
432 });
433 this.btnContainer = tb.dom.firstChild.firstChild.firstChild;
434 }
435 var bconfig = {
436 handler: handler,
437 scope: scope,
438 minWidth: this.minButtonWidth
439 };
440 if(typeof config == 'string'){
441 bconfig.text = config;
442 }else{
443 bconfig.dhconfig = config;
444 }
445 var btn = new YAHOO.ext.Button(
446 this.btnContainer.appendChild(document.createElement('td')),
447 bconfig
448 );
449 this.syncBodyHeight();
450 if(!this.buttons){
451 this.buttons = [];
452 }
453 this.buttons.push(btn);
454 return btn;
455 },
456
457 /**
458 * Sets the default button to be focused when the dialog is displayed
459 * @param {YAHOO.ext.BasicDialog.Button} btn The button object returned by addButton
460 * @return {YAHOO.ext.BasicDialog} this
461 */
462 setDefaultButton : function(btn){
463 this.defaultButton = btn;
464 return this;
465 },
466
467 getHeaderFooterHeight : function(safe){
468 var height = 0;
469 if(this.header){
470 height += this.header.getHeight();
471 }
472 if(this.footer){
473 var fm = this.footer.getMargins();
474 height += (this.footer.getHeight()+fm.top+fm.bottom);
475 }
476 height += this.bwrap.getPadding('tb')+this.bwrap.getBorderWidth('tb');
477 height += this.centerBg.getPadding('tb');
478 return height;
479 },
480
481 syncBodyHeight : function(){
482 var height = this.size.height - this.getHeaderFooterHeight(false);
483 this.body.setHeight(height-this.body.getMargins('tb'));
484 if(this.tabs){
485 this.tabs.syncHeight();
486 }
487 var hh = this.header.getHeight();
488 var h = this.size.height-hh;
489 this.centerBg.setHeight(h);
490 this.bwrap.setLeftTop(this.centerBg.getPadding('l'), hh+this.centerBg.getPadding('t'));
491 this.bwrap.setHeight(h-this.centerBg.getPadding('tb'));
492 this.bwrap.setWidth(this.el.getWidth(true)-this.centerBg.getPadding('lr'));
493 this.body.setWidth(this.bwrap.getWidth(true));
494 },
495
496 /**
497 * Restores the previous state of the dialog if YAHOO.ext.state is configured
498 * @return {YAHOO.ext.BasicDialog} this
499 */
500 restoreState : function(){
501 var box = YAHOO.ext.state.Manager.get(this.stateId || (this.el.id + '-state'));
502 if(box && box.width){
503 this.xy = [box.x, box.y];
504 this.resizeTo(box.width, box.height);
505 }
506 return this;
507 },
508
509 beforeShow : function(){
510 if(this.fixedcenter) {
511 this.xy = this.el.getCenterXY(true);
512 }
513 if(this.modal){
514 YAHOO.util.Dom.addClass(document.body, 'masked');
515 this.mask.setSize(YAHOO.util.Dom.getDocumentWidth(), YAHOO.util.Dom.getDocumentHeight());
516 this.mask.show();
517 }
518 this.constrainXY();
519 },
520
521 animShow : function(){
522 var b = getEl(this.animateTarget, true).getBox();
523 this.proxy.setSize(b.width, b.height);
524 this.proxy.setLocation(b.x, b.y);
525 this.proxy.show();
526 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
527 true, .35, this.showEl.createDelegate(this));
528 },
529
530 /**
531 * Shows the dialog.
532 * @param {String/HTMLElement/YAHOO.ext.Element} animateTarget (optional) Reset the animation target
533 * @return {YAHOO.ext.BasicDialog} this
534 */
535 show : function(animateTarget){
536 if (this.fireEvent('beforeshow', this) === false){
537 return;
538 }
539 if(this.syncHeightBeforeShow){
540 this.syncBodyHeight();
541 }
542 this.animateTarget = animateTarget || this.animateTarget;
543 if(!this.el.isVisible()){
544 this.beforeShow();
545 if(this.animateTarget){
546 this.animShow();
547 }else{
548 this.showEl();
549 }
550 }
551 return this;
552 },
553
554 showEl : function(){
555 this.proxy.hide();
556 this.el.setXY(this.xy);
557 this.el.show();
558 this.adjustAssets(true);
559 this.toFront();
560 this.focus();
561 this.fireEvent('show', this);
562 },
563
564 focus : function(){
565 if(this.defaultButton){
566 this.defaultButton.focus();
567 }else{
568 this.focusEl.focus();
569 }
570 },
571
572 constrainXY : function(){
573 if(this.constraintoviewport !== false){
574 if(!this.viewSize){
575 if(this.container){
576 var s = this.container.getSize();
577 this.viewSize = [s.width, s.height];
578 }else{
579 this.viewSize = [YAHOO.util.Dom.getViewportWidth(),
580 YAHOO.util.Dom.getViewportHeight()];
581 }
582 }
583 var x = this.xy[0], y = this.xy[1];
584 var w = this.size.width, h = this.size.height;
585 var vw = this.viewSize[0], vh = this.viewSize[1];
586 // only move it if it needs it
587 var moved = false;
588 // first validate right/bottom
589 if(x + w > vw){
590 x = vw - w;
591 moved = true;
592 }
593 if(y + h > vh){
594 y = vh - h;
595 moved = true;
596 }
597 // then make sure top/left isn't negative
598 if(x < 0){
599 x = 0;
600 moved = true;
601 }
602 if(y < 0){
603 y = 0;
604 moved = true;
605 }
606 if(moved){
607 // cache xy
608 this.xy = [x, y];
609 if(this.isVisible()){
610 this.el.setLocation(x, y);
611 this.adjustAssets();
612 }
613 }
614 }
615 },
616
617 onDrag : function(){
618 if(!this.proxyDrag){
619 this.xy = this.el.getXY();
620 this.adjustAssets();
621 }
622 },
623
624 adjustAssets : function(doShow){
625 var x = this.xy[0], y = this.xy[1];
626 var w = this.size.width, h = this.size.height;
627 if(doShow === true){
628 if(this.shadow){
629 this.shadow.show();
630 }
631 if(this.shim){
632 this.shim.show();
633 }
634 }
635 if(this.shadow && this.shadow.isVisible()){
636 this.shadow.setBounds(x + this.shadowOffset, y + this.shadowOffset, w, h);
637 }
638 if(this.shim && this.shim.isVisible()){
639 this.shim.setBounds(x, y, w, h);
640 }
641 },
642
643
644 adjustViewport : function(w, h){
645 if(!w || !h){
646 w = YAHOO.util.Dom.getViewportWidth();
647 h = YAHOO.util.Dom.getViewportHeight();
648 }
649 // cache the size
650 this.viewSize = [w, h];
651 if(this.modal && this.mask.isVisible()){
652 this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
653 this.mask.setSize(YAHOO.util.Dom.getDocumentWidth(), YAHOO.util.Dom.getDocumentHeight());
654 }
655 if(this.isVisible()){
656 this.constrainXY();
657 }
658 },
659
660 /**
661 * Destroys this dialog
662 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
663 */
664 destroy : function(removeEl){
665 YAHOO.ext.EventManager.removeResizeListener(this.adjustViewport, this);
666 if(this.tabs){
667 this.tabs.destroy(removeEl);
668 }
669 if(this.shim){
670 this.shim.remove();
671 }
672 if(this.shadow){
673 this.shadow.remove();
674 }
675 if(this.proxy){
676 this.proxy.remove();
677 }
678 if(this.resizer){
679 this.resizer.destroy();
680 }
681 if(this.close){
682 this.close.removeAllListeners();
683 this.close.remove();
684 }
685 if(this.mask){
686 this.mask.remove();
687 }
688 if(this.dd){
689 this.dd.unreg();
690 }
691 if(this.buttons){
692 for(var i = 0, len = this.buttons.length; i < len; i++){
693 this.buttons[i].destroy();
694 }
695 }
696 this.el.removeAllListeners();
697 if(removeEl === true){
698 this.el.update('');
699 this.el.remove();
700 }
701 YAHOO.ext.DialogManager.unregister(this);
702 },
703
704 startMove : function(){
705 if(this.proxyDrag){
706 this.proxy.show();
707 }
708 if(this.constraintoviewport !== false){
709 this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
710 }
711 },
712
713 endMove : function(){
714 if(!this.proxyDrag){
715 YAHOO.util.DD.prototype.endDrag.apply(this.dd, arguments);
716 }else{
717 YAHOO.util.DDProxy.prototype.endDrag.apply(this.dd, arguments);
718 this.proxy.hide();
719 }
720 this.refreshSize();
721 this.adjustAssets();
722 this.fireEvent('move', this, this.xy[0], this.xy[1])
723 },
724
725 /**
726 * Brings this dialog to the front of any other visible dialogs
727 * @return {YAHOO.ext.BasicDialog} this
728 */
729 toFront : function(){
730 YAHOO.ext.DialogManager.bringToFront(this);
731 return this;
732 },
733
734 /**
735 * Sends this dialog to the back (under) of any other visible dialogs
736 * @return {YAHOO.ext.BasicDialog} this
737 */
738 toBack : function(){
739 YAHOO.ext.DialogManager.sendToBack(this);
740 return this;
741 },
742
743 /**
744 * Centers this dialog
745 * @return {YAHOO.ext.BasicDialog} this
746 */
747 center : function(){
748 var xy = this.el.getCenterXY(true);
749 this.moveTo(xy[0], xy[1]);
750 return this;
751 },
752
753 /**
754 * Moves the dialog to the specified point
755 * @param {Number} x
756 * @param {Number} y
757 * @return {YAHOO.ext.BasicDialog} this
758 */
759 moveTo : function(x, y){
760 this.xy = [x,y];
761 if(this.isVisible()){
762 this.el.setXY(this.xy);
763 this.adjustAssets();
764 }
765 return this;
766 },
767
768 /**
769 * Returns true if the dialog is visible
770 * @return {Boolean}
771 */
772 isVisible : function(){
773 return this.el.isVisible();
774 },
775
776 animHide : function(callback){
777 var b = getEl(this.animateTarget, true).getBox();
778 this.proxy.show();
779 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
780 this.el.hide();
781 this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
782 this.hideEl.createDelegate(this, [callback]));
783 },
784
785 /**
786 * Hides the dialog.
787 * @param {Function} callback (optional) Function to call when the dialog is hidden
788 * @return {YAHOO.ext.BasicDialog} this
789 */
790 hide : function(callback){
791 if (this.fireEvent('beforehide', this) === false)
792 return;
793
794 if(this.shadow){
795 this.shadow.hide();
796 }
797 if(this.shim) {
798 this.shim.hide();
799 }
800 if(this.animateTarget){
801 this.animHide(callback);
802 }else{
803 this.el.hide();
804 this.hideEl(callback);
805 }
806 return this;
807 },
808
809 hideEl : function(callback){
810 this.proxy.hide();
811 if(this.modal){
812 this.mask.hide();
813 YAHOO.util.Dom.removeClass(document.body, 'masked');
814 }
815 this.fireEvent('hide', this);
816 if(typeof callback == 'function'){
817 callback();
818 }
819 },
820
821 hideAction : function(){
822 this.setLeft('-10000px');
823 this.setTop('-10000px');
824 this.setStyle('visibility', 'hidden');
825 },
826
827 refreshSize : function(){
828 this.size = this.el.getSize();
829 this.xy = this.el.getXY();
830 YAHOO.ext.state.Manager.set(this.stateId || this.el.id + '-state', this.el.getBox());
831 },
832
833 setZIndex : function(index){
834 if(this.modal){
835 this.mask.setStyle('z-index', index);
836 }
837 if(this.shim){
838 this.shim.setStyle('z-index', ++index);
839 }
840 if(this.shadow){
841 this.shadow.setStyle('z-index', ++index);
842 }
843 this.el.setStyle('z-index', ++index);
844 if(this.proxy){
845 this.proxy.setStyle('z-index', ++index);
846 }
847 if(this.resizer){
848 this.resizer.proxy.setStyle('z-index', ++index);
849 }
850
851 this.lastZIndex = index;
852 },
853
854 /**
855 * Returns the element for this dialog
856 * @return {YAHOO.ext.Element}
857 */
858 getEl : function(){
859 return this.el;
860 }
861});
862
863/**
864 * @class YAHOO.ext.DialogManager
865 * Provides global access to BasicDialogs that have been created and
866 * support for z-indexing (layering) multiple open dialogs.
867 */
868YAHOO.ext.DialogManager = function(){
869 var list = {};
870 var accessList = [];
871 var front = null;
872
873 var sortDialogs = function(d1, d2){
874 return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
875 };
876
877 var orderDialogs = function(){
878 accessList.sort(sortDialogs);
879 var seed = YAHOO.ext.DialogManager.zseed;
880 for(var i = 0, len = accessList.length; i < len; i++){
881 if(accessList[i]){
882 accessList[i].setZIndex(seed + (i*10));
883 }
884 }
885 };
886
887 return {
888 /**
889 * The starting z-index for BasicDialogs - defaults to 10000
890 * @type Number
891 */
892 zseed : 10000,
893
894
895 register : function(dlg){
896 list[dlg.id] = dlg;
897 accessList.push(dlg);
898 },
899
900 unregister : function(dlg){
901 delete list[dlg.id];
902 if(!accessList.indexOf){
903 for(var i = 0, len = accessList.length; i < len; i++){
904 accessList.splice(i, 1);
905 return;
906 }
907 }else{
908 var i = accessList.indexOf(dlg);
909 if(i != -1){
910 accessList.splice(i, 1);
911 }
912 }
913 },
914
915 /**
916 * Gets a registered dialog by id
917 * @param {String/Object} id The id of the dialog or a dialog
918 * @return {YAHOO.ext.BasicDialog}
919 */
920 get : function(id){
921 return typeof id == 'object' ? id : list[id];
922 },
923
924 /**
925 * Brings the specified dialog to the front
926 * @param {String/Object} dlg The id of the dialog or a dialog
927 * @return {YAHOO.ext.BasicDialog}
928 */
929 bringToFront : function(dlg){
930 dlg = this.get(dlg);
931 if(dlg != front){
932 front = dlg;
933 dlg._lastAccess = new Date().getTime();
934 orderDialogs();
935 }
936 return dlg;
937 },
938
939 /**
940 * Sends the specified dialog to the back
941 * @param {String/Object} dlg The id of the dialog or a dialog
942 * @return {YAHOO.ext.BasicDialog}
943 */
944 sendToBack : function(dlg){
945 dlg = this.get(dlg);
946 dlg._lastAccess = -(new Date().getTime());
947 orderDialogs();
948 return dlg;
949 }
950 };
951}();
952
953/**
954 * @class YAHOO.ext.LayoutDialog
955 * @extends YAHOO.ext.BasicDialog
956 * Dialog which provides adjustments for working with a layout in a Dialog.
957 * Add your neccessary layout config options to the dialogs config.<br>
958 * Example Usage (including a nested layout):
959 * <pre><code> if(!dialog){
960 dialog = new YAHOO.ext.LayoutDialog("download-dlg", {
961 modal: true,
962 width:600,
963 height:450,
964 shadow:true,
965 minWidth:500,
966 minHeight:350,
967 autoTabs:true,
968 proxyDrag:true,
969 // layout config merges with the dialog config
970 center:{
971 tabPosition: 'top',
972 alwaysShowTabs: true
973 }
974 });
975 dialog.addKeyListener(27, dialog.hide, dialog);
976 dialog.setDefaultButton(dialog.addButton('Close', dialog.hide, dialog));
977 dialog.addButton('Build It!', this.getDownload, this);
978
979 // we can even add nested layouts
980 var innerLayout = new YAHOO.ext.BorderLayout('dl-inner', {
981 east: {
982 initialSize: 200,
983 autoScroll:true,
984 split:true
985 },
986 center: {
987 autoScroll:true
988 }
989 });
990 innerLayout.beginUpdate();
991 innerLayout.add('east', new YAHOO.ext.ContentPanel('dl-details'));
992 innerLayout.add('center', new YAHOO.ext.ContentPanel('selection-panel'));
993 innerLayout.endUpdate(true);
994
995 // when doing updates to the top level layout in a dialog, you need to
996 // use dialog.beginUpdate()/endUpdate() instead of layout.beginUpdate()/endUpdate()
997 var layout = dialog.getLayout();
998 dialog.beginUpdate();
999 layout.add('center', new YAHOO.ext.ContentPanel('standard-panel',
1000 {title: 'Download the Source', fitToFrame:true}));
1001 layout.add('center', new YAHOO.ext.NestedLayoutPanel(innerLayout,
1002 {title: 'Build your own yui-ext.js'}));
1003 layout.getRegion('center').showPanel(sp);
1004 dialog.endUpdate();</code></pre>
1005 * @constructor
1006 * @param {String/HTMLElement/YAHOO.ext.Element} el The id of or container element
1007 * @param {Object} config configuration options
1008 */
1009YAHOO.ext.LayoutDialog = function(el, config){
1010 config.autoTabs = false;
1011 YAHOO.ext.LayoutDialog.superclass.constructor.call(this, el, config);
1012 this.body.setStyle({overflow:'hidden', position:'relative'});
1013 this.layout = new YAHOO.ext.BorderLayout(this.body.dom, config);
1014 this.layout.monitorWindowResize = false;
1015 this.el.addClass('ydlg-auto-layout');
1016 // fix case when center region overwrites center function
1017 this.center = YAHOO.ext.BasicDialog.prototype.center;
1018 this.on('show', this.layout.layout, this.layout, true);
1019};
1020YAHOO.extendX(YAHOO.ext.LayoutDialog, YAHOO.ext.BasicDialog, {
1021 /**
1022 * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
1023 * @deprecated
1024 */
1025 endUpdate : function(){
1026 this.layout.endUpdate();
1027 },
1028 /**
1029 * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
1030 * @deprecated
1031 */
1032 beginUpdate : function(){
1033 this.layout.beginUpdate();
1034 },
1035 /**
1036 * Get the BorderLayout for this dialog
1037 * @return {YAHOO.ext.BorderLayout}
1038 */
1039 getLayout : function(){
1040 return this.layout;
1041 },
1042 syncBodyHeight : function(){
1043 YAHOO.ext.LayoutDialog.superclass.syncBodyHeight.call(this);
1044 if(this.layout)this.layout.layout();
1045 }
1046});
diff --git a/frontend/beta/js/YUI-extensions/widgets/Button.js b/frontend/beta/js/YUI-extensions/widgets/Button.js
new file mode 100644
index 0000000..5bf3dc3
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/widgets/Button.js
@@ -0,0 +1,185 @@
1/**
2 * @class YAHOO.ext.Button
3 * @extends YAHOO.ext.util.Observable
4 * Simple Button class
5 * @cfg {String} text The button text
6 * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
7 * @cfg {Object} scope The scope of the handler
8 * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
9 * @constructor
10 * Create a new button
11 * @param {String/HTMLElement/Element} renderTo The element to append the button to
12 * @param {Object} config The config object
13 */
14YAHOO.ext.Button = function(renderTo, config){
15 YAHOO.ext.util.Config.apply(this, config);
16 this.events = {
17 /**
18 * @event click
19 * Fires when this button is clicked
20 * @param {Button} this
21 * @param {EventObject} e The click event
22 */
23 'click' : true
24 };
25 if(renderTo){
26 this.render(renderTo);
27 }
28};
29
30YAHOO.extendX(YAHOO.ext.Button, YAHOO.ext.util.Observable, {
31 render : function(renderTo){
32 var btn;
33 if(!this.dhconfig){
34 if(!YAHOO.ext.Button.buttonTemplate){
35 // hideous table template
36 YAHOO.ext.Button.buttonTemplate = new YAHOO.ext.DomHelper.Template('<a href="#" class="ybtn-focus"><table border="0" cellpadding="0" cellspacing="0" class="ybtn-wrap"><tbody><tr><td class="ybtn-left">&#160;</td><td class="ybtn-center" unselectable="on">{0}</td><td class="ybtn-right">&#160;</td></tr></tbody></table></a>');
37 }
38 btn = YAHOO.ext.Button.buttonTemplate.append(
39 getEl(renderTo).dom, [this.text], true);
40 this.tbl = getEl(btn.dom.firstChild, true);
41 }else{
42 btn = YAHOO.ext.DomHelper.append(this.footer.dom, this.dhconfig, true);
43 }
44 this.el = btn;
45 this.autoWidth();
46 btn.addClass('ybtn');
47 btn.mon('click', this.onClick, this, true);
48 btn.on('mouseover', this.onMouseOver, this, true);
49 btn.on('mouseout', this.onMouseOut, this, true);
50 btn.on('mousedown', this.onMouseDown, this, true);
51 btn.on('mouseup', this.onMouseUp, this, true);
52 },
53 /**
54 * Returns the buttons element
55 * @return {YAHOO.ext.Element}
56 */
57 getEl : function(){
58 return this.el;
59 },
60
61 /**
62 * Destroys this Button.
63 */
64 destroy : function(){
65 this.el.removeAllListeners();
66 this.purgeListeners();
67 this.el.update('');
68 this.el.remove();
69 },
70
71 autoWidth : function(){
72 if(this.tbl){
73 this.el.setWidth('auto');
74 this.tbl.setWidth('auto');
75 if(this.minWidth){
76 if(this.tbl.getWidth() < this.minWidth){
77 this.tbl.setWidth(this.minWidth);
78 }
79 }
80 this.el.setWidth(this.tbl.getWidth());
81 }
82 },
83 /**
84 * Sets this buttons click handler
85 * @param {Function} handler The function to call when the button is clicked
86 * @param {Object} scope (optional) Scope for the function passed above
87 */
88 setHandler : function(handler, scope){
89 this.handler = handler;
90 this.scope = scope;
91 },
92
93 /**
94 * Set this buttons text
95 * @param {String} text
96 */
97 setText : function(text){
98 this.text = text;
99 this.el.dom.firstChild.firstChild.firstChild.childNodes[1].innerHTML = text;
100 this.autoWidth();
101 },
102
103 /**
104 * Get the text for this button
105 * @return {String}
106 */
107 getText : function(){
108 return this.text;
109 },
110
111 /**
112 * Show this button
113 */
114 show: function(){
115 this.el.setStyle('display', '');
116 },
117
118 /**
119 * Hide this button
120 */
121 hide: function(){
122 this.el.setStyle('display', 'none');
123 },
124
125 /**
126 * Convenience function for boolean show/hide
127 * @param {Boolean} visible true to show/false to hide
128 */
129 setVisible: function(visible){
130 if(visible) {
131 this.show();
132 }else{
133 this.hide();
134 }
135 },
136
137 /**
138 * Focus the button
139 */
140 focus : function(){
141 this.el.focus();
142 },
143
144 /**
145 * Disable this button
146 */
147 disable : function(){
148 this.el.addClass('ybtn-disabled');
149 this.disabled = true;
150 },
151
152 /**
153 * Enable this button
154 */
155 enable : function(){
156 this.el.removeClass('ybtn-disabled');
157 this.disabled = false;
158 },
159
160 onClick : function(e){
161 e.preventDefault();
162 if(!this.disabled){
163 this.fireEvent('click', this, e);
164 if(this.handler){
165 this.handler.call(this.scope || this, this, e);
166 }
167 }
168 },
169 onMouseOver : function(e){
170 if(!this.disabled){
171 this.el.addClass('ybtn-over');
172 }
173 },
174 onMouseOut : function(e){
175 this.el.removeClass('ybtn-over');
176 },
177 onMouseDown : function(){
178 if(!this.disabled){
179 this.el.addClass('ybtn-click');
180 }
181 },
182 onMouseUp : function(){
183 this.el.removeClass('ybtn-click');
184 }
185});
diff --git a/frontend/beta/js/YUI-extensions/widgets/DatePicker.js b/frontend/beta/js/YUI-extensions/widgets/DatePicker.js
new file mode 100644
index 0000000..eb1e06f
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/widgets/DatePicker.js
@@ -0,0 +1,344 @@
1YAHOO.ext.DatePicker = function(id, parentElement){
2 this.id = id;
3 this.selectedDate = new Date();
4 this.visibleDate = new Date();
5 this.element = null;
6 this.shadow = null;
7 this.callback = null;
8 this.buildControl(parentElement || document.body);
9 this.mouseDownHandler = YAHOO.ext.EventManager.wrap(this.handleMouseDown, this, true);
10 this.keyDownHandler = YAHOO.ext.EventManager.wrap(this.handleKeyDown, this, true);
11 this.wheelHandler = YAHOO.ext.EventManager.wrap(this.handleMouseWheel, this, true);
12};
13
14YAHOO.ext.DatePicker.prototype = {
15 show : function(x, y, value, callback){
16 this.hide();
17 this.selectedDate = value;
18 this.visibleDate = value;
19 this.callback = callback;
20 this.refresh();
21 this.element.show();
22 this.element.setXY(this.constrainToViewport ? this.constrainXY(x, y) : [x, y]);
23 this.shadow.show();
24 this.shadow.setRegion(this.element.getRegion());
25 this.element.dom.tabIndex = 1;
26 this.element.focus();
27 YAHOO.util.Event.on(document, "mousedown", this.mouseDownHandler);
28 YAHOO.util.Event.on(document, "keydown", this.keyDownHandler);
29 YAHOO.util.Event.on(document, "mousewheel", this.wheelHandler);
30 YAHOO.util.Event.on(document, "DOMMouseScroll", this.wheelHandler);
31 },
32
33 constrainXY : function(x, y){
34 var w = YAHOO.util.Dom.getViewportWidth();
35 var h = YAHOO.util.Dom.getViewportHeight();
36 var size = this.element.getSize();
37 return [
38 Math.min(w-size.width, x),
39 Math.min(h-size.height, y)
40 ];
41 },
42
43 hide : function(){
44 this.shadow.hide();
45 this.element.hide();
46 YAHOO.util.Event.removeListener(document, "mousedown", this.mouseDownHandler);
47 YAHOO.util.Event.removeListener(document, "keydown", this.keyDownHandler);
48 YAHOO.util.Event.removeListener(document, "mousewheel", this.wheelHandler);
49 YAHOO.util.Event.removeListener(document, "DOMMouseScroll", this.wheelHandler);
50 },
51
52 setSelectedDate : function(date){
53 this.selectedDate = date;
54 },
55
56 getSelectedDate : function(){
57 return this.selectedDate;
58 },
59
60 showPrevMonth : function(){
61 this.visibleDate = this.getPrevMonth(this.visibleDate);
62 this.refresh();
63 },
64
65 showNextMonth : function(){
66 this.visibleDate = this.getNextMonth(this.visibleDate);
67 this.refresh();
68 },
69
70 showPrevYear : function(){
71 var d = this.visibleDate;
72 this.visibleDate = new Date(d.getFullYear()-1, d.getMonth(), d.getDate());
73 this.refresh();
74 },
75
76 showNextYear : function(){
77 var d = this.visibleDate;
78 this.visibleDate = new Date(d.getFullYear()+1, d.getMonth(), d.getDate());
79 this.refresh();
80 },
81
82 handleMouseDown : function(e){
83 var target = e.getTarget();
84 if(target != this.element.dom && !YAHOO.util.Dom.isAncestor(this.element.dom, target)){
85 this.hide();
86 }
87 },
88
89 handleKeyDown : function(e){
90 switch(e.browserEvent.keyCode){
91 case e.LEFT:
92 this.showPrevMonth();
93 e.stopEvent();
94 break;
95 case e.RIGHT:
96 this.showNextMonth();
97 e.stopEvent();
98 break;
99 case e.DOWN:
100 this.showPrevYear();
101 e.stopEvent();
102 break;
103 case e.UP:
104 this.showNextYear();
105 e.stopEvent();
106 break;
107 }
108 },
109
110 handleMouseWheel : function(e){
111 var delta = e.getWheelDelta();
112 if(delta > 0){
113 this.showPrevMonth();
114 e.stopEvent();
115 } else if(delta < 0){
116 this.showNextMonth();
117 e.stopEvent();
118 }
119 },
120
121 handleClick : function(e){
122 var d = this.visibleDate;
123 var t = e.getTarget();
124 if(t && t.className){
125 var cls = t.className.split(' ')[0];
126 switch(cls){
127 case 'active':
128 this.handleSelection(new Date(d.getFullYear(), d.getMonth(), parseInt(t.innerHTML)));
129 break;
130 case 'prevday':
131 var p = this.getPrevMonth(d);
132 this.handleSelection(new Date(p.getFullYear(), p.getMonth(), parseInt(t.innerHTML)));
133 break;
134 case 'nextday':
135 var n = this.getNextMonth(d);
136 this.handleSelection(new Date(n.getFullYear(), n.getMonth(), parseInt(t.innerHTML)));
137 break;
138 case 'ypopcal-today':
139 this.handleSelection(new Date());
140 break;
141 case 'next-month':
142 this.showNextMonth();
143 break;
144 case 'prev-month':
145 this.showPrevMonth();
146 break;
147 }
148 }
149 e.stopEvent();
150 },
151
152 selectToday : function(){
153 this.handleSelection(new Date());
154 },
155
156 handleSelection: function(date){
157 this.selectedDate = date;
158 this.callback(date);
159 this.hide();
160 },
161
162 getPrevMonth : function(d){
163 var m = d.getMonth();var y = d.getFullYear();
164 return (m == 0 ? new Date(--y, 11, 1) : new Date(y, --m, 1));
165 },
166
167 getNextMonth : function(d){
168 var m = d.getMonth();var y = d.getFullYear();
169 return (m == 11 ? new Date(++y, 0, 1) : new Date(y, ++m, 1));
170 },
171
172 getDaysInMonth : function(m, y){
173 return (m == 1 || m == 3 || m == 5 || m == 7 || m == 8 || m == 10 || m == 12) ? 31 : (m == 4 || m == 6 || m == 9 || m == 11) ? 30 : this.isLeapYear(y) ? 29 : 28;
174 },
175
176 isLeapYear : function(y){
177 return (((y % 4) == 0) && ((y % 100) != 0) || ((y % 400) == 0));
178 },
179
180 clearTime : function(date){
181 if(date){
182 date.setHours(0);
183 date.setMinutes(0);
184 date.setSeconds(0);
185 date.setMilliseconds(0);
186 }
187 return date;
188 },
189
190 refresh : function(){
191 var d = this.visibleDate;
192 this.buildInnerCal(d);
193 this.calHead.update(this.monthNames[d.getMonth()] + ' ' + d.getFullYear());
194 if(this.element.isVisible()){
195 this.shadow.setRegion(this.element.getRegion());
196 }
197 }
198};
199
200/**
201 * This code is not pretty, but it is fast!
202 * @ignore
203 */
204YAHOO.ext.DatePicker.prototype.buildControl = function(parentElement){
205 var c = document.createElement('div');
206 c.style.position = 'absolute';
207 c.style.visibility = 'hidden';
208 document.body.appendChild(c);
209 var html = '<iframe id="'+this.id+'_shdw" frameborder="0" class="ypopcal-shadow" src="'+YAHOO.ext.SSL_SECURE_URL+'"></iframe>' +
210 '<div hidefocus="true" class="ypopcal" id="'+this.id+'">' +
211 '<table class="ypopcal-head" border=0 cellpadding=0 cellspacing=0><tbody><tr><td class="ypopcal-arrow"><div class="prev-month">&#160;</div></td><td class="ypopcal-month">&#160;</td><td class="ypopcal-arrow"><div class="next-month">&#160;</div></td></tr></tbody></table>' +
212 '<center><div class="ypopcal-inner">';
213 html += "<table border=0 cellspacing=0 class=\"ypopcal-table\"><thead><tr class=\"ypopcal-daynames\">";
214 var names = this.dayNames;
215 for(var i = 0; i < names.length; i++){
216 html += '<td>' + names[i].substr(0, 1) + '</td>';
217 }
218 html+= "</tr></thead><tbody><tr>";
219 for(var i = 0; i < 42; i++) {
220 if(i % 7 == 0 && i != 0){
221 html += '</tr><tr>';
222 }
223 html += "<td>&nbsp;</td>";
224 }
225 html += "</tr></tbody></table>";
226 html += '</div><button class="ypopcal-today">'+this.todayText+'</button></center></div>';
227 c.innerHTML = html;
228 this.shadow = getEl(c.childNodes[0], true);
229 this.shadow.enableDisplayMode('block');
230 this.element = getEl(c.childNodes[1], true);
231 this.element.enableDisplayMode('block');
232 document.body.appendChild(this.shadow.dom);
233 document.body.appendChild(this.element.dom);
234 document.body.removeChild(c);
235 this.element.on("selectstart", function(){return false;});
236 var tbody = this.element.dom.getElementsByTagName('tbody')[1];
237 this.cells = tbody.getElementsByTagName('td');
238 this.calHead = this.element.getChildrenByClassName('ypopcal-month', 'td')[0];
239 this.element.mon('mousedown', this.handleClick, this, true);
240};
241
242YAHOO.ext.DatePicker.prototype.buildInnerCal = function(dateVal){
243 var days = this.getDaysInMonth(dateVal.getMonth() + 1, dateVal.getFullYear());
244 var firstOfMonth = new Date(dateVal.getFullYear(), dateVal.getMonth(), 1);
245 var startingPos = firstOfMonth.getDay();
246 if(startingPos == 0) startingPos = 7;
247 var pm = this.getPrevMonth(dateVal);
248 var prevStart = this.getDaysInMonth(pm.getMonth()+1, pm.getFullYear())-startingPos;
249 var cells = this.cells;
250 days += startingPos;
251
252 // convert everything to numbers so it's fast
253 var day = 86400000;
254 var date = this.clearTime(new Date(pm.getFullYear(), pm.getMonth(), prevStart));
255 var today = this.clearTime(new Date()).getTime();
256 var sel = this.selectedDate ? this.clearTime(this.selectedDate).getTime() : today + 1; //today +1 will never match anything
257 var min = this.minDate ? this.clearTime(this.minDate).getTime() : Number.NEGATIVE_INFINITY;
258 var max = this.maxDate ? this.clearTime(this.maxDate).getTime() : Number.POSITIVE_INFINITY;
259 var ddMatch = this.disabledDatesRE;
260 var ddText = this.disabledDatesText;
261 var ddays = this.disabledDays;
262 var ddaysText = this.disabledDaysText;
263 var format = this.format;
264
265 var setCellClass = function(cal, cell, d){
266 cell.title = '';
267 var t = d.getTime();
268 if(t == today){
269 cell.className += ' today';
270 cell.title = cal.todayText;
271 }
272 if(t == sel){
273 cell.className += ' selected';
274 }
275 // disabling
276 if(t < min) {
277 cell.className = ' ypopcal-disabled';
278 cell.title = cal.minText;
279 return;
280 }
281 if(t > max) {
282 cell.className = ' ypopcal-disabled';
283 cell.title = cal.maxText;
284 return;
285 }
286 if(ddays){
287 var day = d.getDay();
288 for(var i = 0; i < ddays.length; i++) {
289 if(day === ddays[i]){
290 cell.title = ddaysText;
291 cell.className = ' ypopcal-disabled';
292 return;
293 }
294 }
295 }
296 if(ddMatch && format){
297 var fvalue = d.format(format);
298 if(ddMatch.test(fvalue)){
299 cell.title = ddText.replace('%0', fvalue);
300 cell.className = ' ypopcal-disabled';
301 return;
302 }
303 }
304 };
305
306 var i = 0;
307 for(; i < startingPos; i++) {
308 cells[i].innerHTML = (++prevStart);
309 date.setDate(date.getDate()+1);
310 cells[i].className = 'prevday';
311 setCellClass(this, cells[i], date);
312 }
313 for(; i < days; i++){
314 intDay = i - startingPos + 1;
315 cells[i].innerHTML = (intDay);
316 date.setDate(date.getDate()+1);
317 cells[i].className = 'active';
318 setCellClass(this, cells[i], date);
319 }
320 var extraDays = 0;
321 for(; i < 42; i++) {
322 cells[i].innerHTML = (++extraDays);
323 date.setDate(date.getDate()+1);
324 cells[i].className = 'nextday';
325 setCellClass(this, cells[i], date);
326 }
327};
328
329YAHOO.ext.DatePicker.prototype.todayText = "Today";
330YAHOO.ext.DatePicker.prototype.minDate = null;
331YAHOO.ext.DatePicker.prototype.maxDate = null;
332YAHOO.ext.DatePicker.prototype.minText = "This date is before the minimum date";
333YAHOO.ext.DatePicker.prototype.maxText = "This date is after the maximum date";
334YAHOO.ext.DatePicker.prototype.format = 'm/d/y';
335YAHOO.ext.DatePicker.prototype.disabledDays = null;
336YAHOO.ext.DatePicker.prototype.disabledDaysText = '';
337YAHOO.ext.DatePicker.prototype.disabledDatesRE = null;
338YAHOO.ext.DatePicker.prototype.disabledDatesText = '';
339YAHOO.ext.DatePicker.prototype.constrainToViewport = true;
340
341
342YAHOO.ext.DatePicker.prototype.monthNames = Date.monthNames;
343
344YAHOO.ext.DatePicker.prototype.dayNames = Date.dayNames;
diff --git a/frontend/beta/js/YUI-extensions/widgets/InlineEditor.js b/frontend/beta/js/YUI-extensions/widgets/InlineEditor.js
new file mode 100644
index 0000000..a498faa
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/widgets/InlineEditor.js
@@ -0,0 +1,216 @@
1YAHOO.ext.InlineEditor = function(config, existingEl){
2 YAHOO.ext.util.Config.apply(this, config);
3 var dh = YAHOO.ext.DomHelper;
4 this.wrap = dh.append(this.container || document.body, {
5 tag:'div',
6 cls:'yinline-editor-wrap'
7 }, true);
8
9 this.textSizeEl = dh.append(document.body, {
10 tag: 'div',
11 cls: 'yinline-editor-sizer ' + (this.cls || '')
12 });
13 if(YAHOO.ext.util.Browser.isSafari){ // extra padding for safari's textboxes
14 this.textSizeEl.style.padding = '4px';
15 YAHOO.util.Dom.setStyle(this.textSizeEl, 'padding-right', '10px');
16 }
17
18 if(!YAHOO.ext.util.Browser.isGecko){ // no one else needs FireFox cursor fix
19 this.wrap.setStyle('overflow', 'hidden');
20 }
21
22 if(existingEl){
23 this.el = getEl(existingEl);
24 }
25 if(!this.el){
26 this.id = this.id || YAHOO.util.Dom.generateId();
27 if(!this.multiline){
28 this.el = this.wrap.createChild({
29 tag: 'input',
30 name: this.name || this.id,
31 id: this.id,
32 type: this.type || 'text',
33 autocomplete: 'off',
34 value: this.value || '',
35 cls: 'yinline-editor ' + (this.cls || ''),
36 maxlength: this.maxLength || ''
37 });
38 }else{
39 this.el = this.wrap.createChild({
40 tag: 'textarea',
41 name: this.name || this.id,
42 id: this.id,
43 html: this.value || '',
44 cls: 'yinline-editor yinline-editor-multiline ' + (this.cls || ''),
45 wrap: 'none'
46 });
47 }
48 }else{
49 this.wrap.dom.appendChild(this.el.dom);
50 }
51 this.el.addKeyMap([{
52 key: [10, 13],
53 fn: this.onEnter,
54 scope: this
55 },{
56 key: 27,
57 fn: this.onEsc,
58 scope: this
59 }]);
60 this.el.mon('keyup', this.onKeyUp, this, true);
61 this.el.on('blur', this.onBlur, this, true);
62 this.el.swallowEvent('keydown');
63 this.events = {
64 'startedit' : true,
65 'beforecomplete' : true,
66 'complete' : true
67 };
68 this.editing = false;
69 this.autoSizeTask = new YAHOO.ext.util.DelayedTask(this.autoSize, this);
70};
71
72YAHOO.extendX(YAHOO.ext.InlineEditor, YAHOO.ext.util.Observable, {
73 onEnter : function(k, e){
74 if(this.multiline && (e.ctrlKey || e.shiftKey)){
75 return;
76 }else{
77 this.completeEdit();
78 e.stopEvent();
79 }
80 },
81
82 onEsc : function(){
83 if(this.ignoreNoChange){
84 this.revert(true);
85 }else{
86 this.revert(false);
87 this.completeEdit();
88 }
89 },
90
91 onBlur : function(){
92 if(this.editing && this.completeOnBlur !== false){
93 this.completeEdit();
94 }
95 },
96
97 startEdit : function(el, value){
98 this.boundEl = YAHOO.util.Dom.get(el);
99 if(this.hideEl !== false){
100 this.boundEl.style.visibility = 'hidden';
101 }
102 var v = value || this.boundEl.innerHTML;
103 this.startValue = v;
104 this.setValue(v);
105 this.moveTo(YAHOO.util.Dom.getXY(this.boundEl));
106 this.editing = true;
107 if(YAHOO.ext.QuickTips){
108 YAHOO.ext.QuickTips.disable();
109 }
110 this.show.defer(10, this);
111 },
112
113 onKeyUp : function(e){
114 var k = e.getKey();
115 if(this.editing && (k < 33 || k > 40) && k != 27){
116 this.autoSizeTask.delay(50);
117 }
118 },
119
120 completeEdit : function(){
121 var v = this.getValue();
122 if(this.revertBlank !== false && v.length < 1){
123 v = this.startValue;
124 this.revert();
125 }
126 if(v == this.startValue && this.ignoreNoChange){
127 this.hide();
128 }
129 if(this.fireEvent('beforecomplete', this, v, this.startValue) !== false){
130 if(this.updateEl !== false && this.boundEl){
131 this.boundEl.innerHTML = v;
132 }
133 this.hide();
134 this.fireEvent('complete', this, v, this.startValue);
135 }
136 },
137
138 revert : function(hide){
139 this.setValue(this.startValue);
140 if(hide){
141 this.hide();
142 }
143 },
144
145 show : function(){
146 this.autoSize();
147 this.wrap.show();
148 this.el.focus();
149 if(this.selectOnEdit !== false){
150 this.el.dom.select();
151 }
152 },
153
154 hide : function(){
155 this.editing = false;
156 this.wrap.hide();
157 this.wrap.setLeftTop(-10000,-10000);
158 this.el.blur();
159 if(this.hideEl !== false){
160 this.boundEl.style.visibility = 'visible';
161 }
162 if(YAHOO.ext.QuickTips){
163 YAHOO.ext.QuickTips.enable();
164 }
165 },
166
167 setValue : function(v){
168 this.el.dom.value = v;
169 },
170
171 getValue : function(){
172 return this.el.dom.value;
173 },
174
175 autoSize : function(){
176 var el = this.el;
177 var wrap = this.wrap;
178 var v = el.dom.value;
179 var ts = this.textSizeEl;
180 if(v.length < 1){
181 ts.innerHTML = "&#160;&#160;";
182 }else{
183 v = v.replace(/[<> ]/g, '&#160;');
184 if(this.multiline){
185 v = v.replace(/\n/g, '<br />&#160;');
186 }
187 ts.innerHTML = v;
188 }
189 var ww = wrap.dom.offsetWidth;
190 var wh = wrap.dom.offsetHeight;
191 var w = ts.offsetWidth;
192 var h = ts.offsetHeight;
193 // lots of magic numbers in this block - wtf?
194 // the logic is to prevent the scrollbars from flashing
195 // in firefox. Updates the right element first
196 // so there's never overflow.
197 if(ww > w+4){
198 el.setWidth(w+4);
199 wrap.setWidth(w+8);
200 }else{
201 wrap.setWidth(w+8);
202 el.setWidth(w+4);
203 }
204 if(wh > h+4){
205 el.setHeight(h);
206 wrap.setHeight(h+4);
207 }else{
208 wrap.setHeight(h+4);
209 el.setHeight(h);
210 }
211 },
212
213 moveTo : function(xy){
214 this.wrap.setXY(xy);
215 }
216});
diff --git a/frontend/beta/js/YUI-extensions/widgets/MessageBox.js b/frontend/beta/js/YUI-extensions/widgets/MessageBox.js
new file mode 100644
index 0000000..01b168d
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/widgets/MessageBox.js
@@ -0,0 +1,230 @@
1YAHOO.ext.MessageBox = function(){
2 var dlg, opt, mask;
3 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4 var buttons, activeTextEl, bwidth;
5
6 var handleButton = function(button){
7 if(typeof opt.fn == 'function'){
8 if(opt.fn.call(opt.scope||window, button, activeTextEl.dom.value) !== false){
9 dlg.hide();
10 }
11 }else{
12 dlg.hide();
13 }
14 };
15 var updateButtons = function(b){
16 var width = 0;
17 if(!b){
18 buttons['ok'].hide();
19 buttons['cancel'].hide();
20 buttons['yes'].hide();
21 buttons['no'].hide();
22 return width;
23 }
24 for(var k in buttons){
25 if(typeof buttons[k] != 'function'){
26 if(b[k]){
27 buttons[k].show();
28 buttons[k].setText(typeof b[k] == 'string' ? b[k] : YAHOO.ext.MessageBox.buttonText[k]);
29 width += buttons[k].el.getWidth()+15;
30 }else{
31 buttons[k].hide();
32 }
33 }
34 }
35 return width;
36 };
37
38 return {
39 getDialog : function(){
40 if(!dlg){
41 dlg = new YAHOO.ext.BasicDialog('mb-dlg', {
42 autoCreate : true,
43 shadow: true,
44 draggable: true,
45 resizable:false,
46 constraintoviewport:true,
47 fixedcenter:true,
48 shim:true,
49 modal: true,
50 width:400, height:100,
51 buttonAlign:'center',
52 closeClick : function(){
53 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
54 handleButton('no');
55 }else{
56 handleButton('cancel');
57 }
58 }
59 });
60 dlg.closeClick = function(){
61 alert('wtf');
62 };
63 mask = dlg.mask;
64 dlg.addKeyListener(27, dlg.hide, dlg);
65 buttons = {};
66 buttons['ok'] = dlg.addButton(this.buttonText['ok'], handleButton.createCallback('ok'));
67 buttons['yes'] = dlg.addButton(this.buttonText['yes'], handleButton.createCallback('yes'));
68 buttons['no'] = dlg.addButton(this.buttonText['no'], handleButton.createCallback('no'));
69 buttons['cancel'] = dlg.addButton(this.buttonText['cancel'], handleButton.createCallback('cancel'));
70 bodyEl = dlg.body.createChild({
71 tag:'div',
72 html:'<span class="ext-mb-text"></span><br /><input type="text" class="ext-mb-input"><textarea class="ext-mb-textarea"></textarea><div class="ext-mb-progress-wrap"><div class="ext-mb-progress"><div class="ext-mb-progress-bar">&#160;</div></div></div>'
73 });
74 msgEl = bodyEl.dom.firstChild;
75 textboxEl = getEl(bodyEl.dom.childNodes[2]);
76 textboxEl.enableDisplayMode();
77 textboxEl.addKeyListener([10,13], function(){
78 if(dlg.isVisible() && opt && opt.buttons){
79 if(opt.buttons.ok){
80 handleButton('ok');
81 }else if(opt.buttons.yes){
82 handleButton('yes');
83 }
84 }
85 });
86 textareaEl = getEl(bodyEl.dom.childNodes[3]);
87 textareaEl.enableDisplayMode();
88 progressEl = getEl(bodyEl.dom.childNodes[4]);
89 progressEl.enableDisplayMode();
90 pp = getEl(progressEl.dom.firstChild.firstChild);
91 }
92 return dlg;
93 },
94
95 updateText : function(text){
96 if(!dlg.isVisible() && !opt.width){
97 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
98 }
99 msgEl.innerHTML = text;
100 var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth),
101 Math.max(opt.minWidth || this.minWidth, bwidth));
102 if(opt.prompt){
103 activeTextEl.setWidth(w);
104 }
105 dlg.setContentSize(w, bodyEl.getHeight());
106 },
107
108 updateProgress : function(value, text){
109 if(text){
110 this.updateText(text);
111 }
112 pp.setWidth(value*progressEl.dom.firstChild.offsetWidth);
113 },
114
115 isVisible : function(){
116 return dlg && dlg.isVisible();
117 },
118
119 hide : function(){
120 if(this.isVisible()){
121 dlg.hide();
122 }
123 },
124
125 show : function(options){
126 var d = this.getDialog();
127 opt = options;
128 d.setTitle(opt.title || '&#160;');
129 d.close.setDisplayed(opt.closable !== false);
130 activeTextEl = textboxEl;
131 opt.prompt = opt.prompt || (opt.multiline ? true : false)
132 if(opt.prompt){
133 if(opt.multiline){
134 textboxEl.hide();
135 textareaEl.show();
136 textareaEl.setHeight(typeof opt.multiline == 'number' ?
137 opt.multiline : this.defaultTextHeight);
138 activeTextEl = textareaEl;
139 }else{
140 textboxEl.show();
141 textareaEl.hide();
142 }
143 }else{
144 textboxEl.hide();
145 textareaEl.hide();
146 }
147 progressEl.setDisplayed(opt.progress === true);
148 this.updateProgress(0);
149 activeTextEl.dom.value = opt.value || '';
150 if(opt.prompt){
151 dlg.setDefaultButton(activeTextEl);
152 }else{
153 var bs = opt.buttons;
154 var db = null;
155 if(bs && bs.ok){
156 db = buttons['ok'];
157 }else if(bs && bs.yes){
158 db = buttons['yes'];
159 }
160 dlg.setDefaultButton(db);
161 }
162 bwidth = updateButtons(opt.buttons);
163 this.updateText(opt.msg);
164 d.modal = opt.modal !== false;
165 d.mask = opt.modal !== false ? mask : false;
166 d.animateTarget = null;
167 d.show(options.animEl);
168 },
169
170 progress : function(title, msg){
171 this.show({
172 title : title,
173 msg : msg,
174 buttons: false,
175 progress:true,
176 closable:false
177 });
178 },
179
180 alert : function(title, msg, fn, scope){
181 this.show({
182 title : title,
183 msg : msg,
184 buttons: this.OK,
185 fn: fn,
186 scope : scope
187 });
188 },
189
190 confirm : function(title, msg, fn, scope){
191 this.show({
192 title : title,
193 msg : msg,
194 buttons: this.YESNO,
195 fn: fn,
196 scope : scope
197 });
198 },
199
200 prompt : function(title, msg, fn, scope, multiline){
201 this.show({
202 title : title,
203 msg : msg,
204 buttons: this.OKCANCEL,
205 fn: fn,
206 minWidth:250,
207 scope : scope,
208 prompt:true,
209 multiline: multiline
210 });
211 },
212
213 OK : {ok:true},
214 YESNO : {yes:true, no:true},
215 OKCANCEL : {ok:true, cancel:true},
216 YESNOCANCEL : {yes:true, no:true, cancel:true},
217
218 defaultTextHeight:75,
219 maxWidth : 500,
220 minWidth : 100,
221 buttonText : {
222 ok : 'OK',
223 cancel : 'Cancel',
224 yes : 'Yes',
225 no : 'No'
226 }
227 };
228}();
229
230YAHOO.ext.Msg = YAHOO.ext.MessageBox;
diff --git a/frontend/beta/js/YUI-extensions/widgets/QuickTips.js b/frontend/beta/js/YUI-extensions/widgets/QuickTips.js
new file mode 100644
index 0000000..6658574
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/widgets/QuickTips.js
@@ -0,0 +1,311 @@
1/**
2 * @class YAHOO.ext.QuickTips
3 * @singleton
4 */
5YAHOO.ext.QuickTips = function(){
6 var el, tipBody, tipTitle, tm, cfg, close, tagEls = {}, reader, esc, anim, removeCls = null;
7 var ce, bd, xy;
8 var visible = false, disabled = true, inited = false;
9 var showProc = hideProc = dismissProc = 1, locks = [];
10 var E = YAHOO.util.Event, dd;
11
12 var onOver = function(e){
13 if(disabled){
14 return;
15 }
16 var t = E.getTarget(e);
17 if(!t){
18 return;
19 }
20 if(ce && t == ce.el){
21 clearTimeout(hideProc);
22 return;
23 }
24 if(t && tagEls[t.id]){
25 tagEls[t.id].el = t;
26 showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
27 return;
28 }
29 var ttp = reader.getAttribute(t, cfg.attribute);
30 if(!ttp && tm.interceptTitles && t.title){
31 ttp = t.title;
32 t.title = '';
33 if(reader.useNS){
34 t.setAttributeNS('y', 'qtip', ttp);
35 }else{
36 t.setAttribute('qtip', ttp);
37 }
38 }
39 if(ttp){
40 xy = E.getXY(e);
41 xy[0] += 12; xy[1] += 20;
42 showProc = show.defer(tm.showDelay, tm, [{
43 el: t,
44 text: ttp,
45 width: reader.getAttribute(t, cfg.width),
46 autoHide: reader.getAttribute(t, cfg.hide) != 'user',
47 title: reader.getAttribute(t, cfg.title),
48 cls: reader.getAttribute(t, cfg.cls)
49 }]);
50 }
51 };
52
53 var onOut = function(e){
54 clearTimeout(showProc);
55 var t = E.getTarget(e);
56 if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
57 hideProc = setTimeout(hide, tm.hideDelay);
58 }
59 };
60
61 var onMove = function(e){
62 if(disabled){
63 return;
64 }
65 xy = E.getXY(e);
66 xy[0] += 12; xy[1] += 20;
67 if(tm.trackMouse && ce){
68 el.setXY(xy);
69 }
70 };
71
72 var onDown = function(e){
73 clearTimeout(showProc);
74 clearTimeout(hideProc);
75 if(!e.within(el)){
76 if(tm.hideOnClick && ce && ce.autoHide !== false){
77 hide();
78 tm.disable();
79 }
80 }
81 };
82
83 var onUp = function(e){
84 tm.enable();
85 }
86
87 var show = function(o){
88 if(disabled){
89 return;
90 }
91 clearTimeout(dismissProc);
92 stopAnim();
93 ce = o;
94 if(removeCls){ // in case manually hidden
95 el.removeClass(removeCls);
96 removeCls = null;
97 }
98 if(ce.cls){
99 el.addClass(ce.cls);
100 removeCls = ce.cls;
101 }
102 if(ce.title){
103 tipTitleText.update(ce.title);
104 tipTitle.show();
105 }else{
106 tipTitle.hide();
107 }
108 tipBody.update(o.text);
109 if(!ce.width){
110 if(tipBody.dom.style.width){
111 tipBody.dom.style.width = '';
112 }
113 if(tipBody.dom.offsetWidth > tm.maxWidth){
114 tipBody.setWidth(tm.maxWidth);
115 }
116 }else{
117 tipBody.setWidth(ce.width);
118 }
119 if(!ce.autoHide){
120 close.setDisplayed(true);
121 if(dd){
122 dd.unlock();
123 }
124 }else{
125 close.setDisplayed(false);
126 if(dd){
127 dd.lock();
128 }
129 }
130 if(xy){
131 el.setXY(xy);
132 }
133 if(tm.animate){
134 anim.attributes = {opacity:{to:1}};
135 el.setOpacity(.1);
136 el.setStyle('visibility', 'visible');
137 anim.animateX(afterShow);
138 }else{
139 afterShow();
140 }
141 };
142
143 var afterShow = function(){
144 if(ce){
145 el.show();
146 esc.enable();
147 if(tm.autoDismiss && ce.autoHide !== false){
148 dismissProc = setTimeout(hide, tm.autoDismissDelay);
149 }
150 }
151 }
152
153 var hide = function(noanim){
154 clearTimeout(dismissProc);
155 clearTimeout(hideProc);
156 ce = null;
157 if(el.isVisible()){
158 esc.disable();
159 stopAnim();
160 if(noanim !== true && tm.animate){
161 anim.attributes = {opacity:{to:.1}};
162 el.beforeAction();
163 anim.animateX(afterHide);
164 }else{
165 afterHide();
166 }
167 }
168 };
169
170 var afterHide = function(){
171 el.hide();
172 if(removeCls){
173 el.removeClass(removeCls);
174 removeCls = null;
175 }
176 }
177
178 var stopAnim = function(){
179 if(anim && anim.isAnimated()){
180 anim.stop();
181 }
182 }
183
184 return {
185 init : function(){
186 if(YAHOO.ext.util.Browser.isIE && !YAHOO.ext.util.Browser.isIE7){
187 return;
188 }
189 tm = YAHOO.ext.QuickTips;
190 cfg = tm.tagConfig;
191 reader = new YAHOO.ext.CustomTagReader(cfg.namespace);
192 if(!inited){
193 el = new YAHOO.ext.Layer({cls:'ytip', shadow:true, useDisplay: false});
194 el.update('<div class="ytip-hd-left"><div class="ytip-hd-right"><div class="ytip-hd"></div></div></div>');
195 tipTitle = getEl(el.dom.firstChild);
196 tipTitleText = getEl(el.dom.firstChild.firstChild.firstChild);
197 tipTitle.enableDisplayMode('block');
198 tipBody = el.createChild({tag:'div', cls:'ytip-bd'});
199 close = el.createChild({tag:'div', cls:'ytip-close'});
200 close.on('click', hide);
201 d = getEl(document);
202 d.mon('mousedown', onDown);
203 d.on('mouseup', onUp);
204 d.on('mouseover', onOver);
205 d.on('mouseout', onOut);
206 d.on('mousemove', onMove);
207 esc = d.addKeyListener(27, hide);
208 esc.disable();
209 if(tm.animate){
210 anim = new YAHOO.util.Anim(el.dom, {}, .1);
211 }
212 if(YAHOO.util.DD){
213 dd = el.initDD('default', null, {
214 onDrag : function(){
215 el.sync();
216 }
217 });
218 dd.setHandleElId(tipTitleText.id);
219 dd.lock();
220 }
221 inited = true;
222 }
223 this.scan(document.body);
224 this.enable();
225 },
226
227 tips : function(config){
228 var cs = config instanceof Array ? config : arguments;
229 for(var i = 0, len = cs.length; i < len; i++) {
230 var c = cs[i];
231 var target = c.target;
232 if(target){
233 if(target instanceof Array){
234 for(var j = 0, jlen = target.length; j < jlen; j++){
235 tagEls[target[j]] = c;
236 }
237 }else{
238 tagEls[target] = c;
239 }
240 }
241 }
242 },
243
244 enable : function(){
245 if(inited){
246 locks.pop();
247 if(locks.length < 1){
248 disabled = false;
249 }
250 }
251 },
252
253 disable : function(){
254 disabled = true;
255 clearTimeout(showProc);
256 clearTimeout(hideProc);
257 clearTimeout(dismissProc);
258 if(ce){
259 hide(true);
260 }
261 locks.push(1);
262 },
263
264 scan : function(toScan){
265 toScan = toScan.dom ? toScan.dom : YAHOO.util.Dom.get(toScan);
266 var found = [];
267 reader.eachElement(cfg.tag, toScan, function(el){
268 var t = reader.getAttribute(el, cfg.target);
269 if(t){
270 found.push({
271 target: t.indexOf(',') != -1 ? t.split(',') : t,
272 text: el.innerHTML,
273 autoHide: reader.getAttribute(el, cfg.hide) != 'user',
274 width: reader.getAttribute(el, cfg.width),
275 title: reader.getAttribute(el, cfg.title),
276 cls: reader.getAttribute(el, cfg.cls)
277 });
278 }
279 el.parentNode.removeChild(el);
280 });
281 this.tips(found);
282 },
283
284 tagConfig : {
285 namespace : 'y',
286 tag : 'qtip',
287 attribute : 'qtip',
288 width : 'width',
289 target : 'target',
290 title : 'qtitle',
291 hide : 'hide',
292 cls : 'qclass'
293 },
294
295 maxWidth : 300,
296 interceptTitles : true,
297 trackMouse : false,
298 hideOnClick : true,
299 showDelay : 500,
300 hideDelay : 200,
301 autoHide : true,
302 autoDismiss : true,
303 autoDismissDelay : 5000,
304 /**
305 * True to turn on fade animation. Defaults to true
306 * except in IE7 (ClearType/scrollbar flicker issues in IE7 with filters).
307 * @type Boolean
308 */
309 animate : YAHOO.util.Anim && !YAHOO.ext.util.Browser.isIE7
310 }
311}();
diff --git a/frontend/beta/js/YUI-extensions/widgets/Resizable.js b/frontend/beta/js/YUI-extensions/widgets/Resizable.js
new file mode 100644
index 0000000..6944683
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/widgets/Resizable.js
@@ -0,0 +1,586 @@
1/**
2 * @class YAHOO.ext.Resizable
3 * @extends YAHOO.ext.util.Observable
4 * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
5 * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
6 * the textarea in a div and set "resizeChild" to true (or the id of the textarea), <b>or</b> set wrap:true in your config and
7 * the element will be wrapped for you automatically.</p><br/>
8 * Here's a Resizable with every possible config option and it's default value:
9<pre><code>
10var resizer = new YAHOO.ext.Resizable('element-id', {
11 resizeChild : false,
12 adjustments : [0, 0],
13 minWidth : 5,
14 minHeight : 5,
15 maxWidth : 10000,
16 maxHeight : 10000,
17 enabled : true,
18 wrap: false, // true to wrap the element
19 width: null, // initial size
20 height: null, // initial size
21 animate : false,
22 duration : .35,
23 dynamic : false,
24 handles : false,
25 multiDirectional : false,
26 disableTrackOver : false,
27 easing : YAHOO.util.Easing ? YAHOO.util.Easing.easeOutStrong : null,
28 widthIncrement : 0,
29 heightIncrement : 0,
30 pinned : false,
31 width : null,
32 height : null,
33 preserveRatio : false,
34 transparent: false,
35 minX: 0,
36 minY: 0,
37 draggable: false
38});
39resizer.on('resize', myHandler);
40</code></pre>
41* <p>
42 * To hide a particular handle, set it's display to none in CSS, or through script:<br>
43 * resizer.east.setDisplayed(false);
44 * </p>
45 * @constructor
46 * Create a new resizable component
47 * @param {String/HTMLElement/YAHOO.ext.Element} el The id or element to resize
48 * @param {Object} config configuration options
49 */
50YAHOO.ext.Resizable = function(el, config){
51 this.el = getEl(el);
52
53 if(config && config.wrap){
54 config.resizeChild = this.el;
55 this.el = this.el.wrap(typeof config.wrap == 'object' ? config.wrap : null);
56 this.el.id = this.el.dom.id = config.resizeChild.id + '-rzwrap';
57 this.el.setStyle('overflow', 'hidden');
58 this.el.setPositioning(config.resizeChild.getPositioning());
59 config.resizeChild.clearPositioning();
60 if(!config.width || !config.height){
61 var csize = config.resizeChild.getSize();
62 //csize.width -= config.adjustments[0];
63 //csize.height -= config.adjustments[1];
64 this.el.setSize(csize.width, csize.height);
65 }
66 if(config.pinned && !config.adjustments){
67 config.adjustments = 'auto';
68 }
69 }
70
71 this.proxy = this.el.createProxy({tag: 'div', cls: 'yresizable-proxy', id: this.el.id + '-rzproxy'})
72 this.proxy.unselectable();
73
74 // the overlay traps mouse events while dragging and fixes iframe issue
75 this.overlay = this.el.createProxy({tag: 'div', cls: 'yresizable-overlay', html: '&#160;'});
76 this.overlay.unselectable();
77 this.overlay.enableDisplayMode('block');
78 this.overlay.mon('mousemove', this.onMouseMove, this, true);
79 this.overlay.mon('mouseup', this.onMouseUp, this, true);
80
81 YAHOO.ext.util.Config.apply(this, config, {
82 /** True to resizeSize the first child or id/element to resize @type YAHOO.ext.Element */
83 resizeChild : false,
84 /** String "auto" or an array [width, height] with values to be <b>added</b> to the resize operation's new size. @type Array/String */
85 adjustments : [0, 0],
86 /** The minimum width for the element @type Number */
87 minWidth : 5,
88 /** The minimum height for the element @type Number */
89 minHeight : 5,
90 /** The maximum width for the element @type Number */
91 maxWidth : 10000,
92 /** The maximum height for the element @type Number */
93 maxHeight : 10000,
94 /** false to disable resizing @type Boolean */
95 enabled : true,
96 /** True to animate the resize (not compatible with dynamic sizing) @type Boolean */
97 animate : false,
98 /** Animation duration @type Float */
99 duration : .35,
100 /** True to resize the element while dragging instead of using a proxy @type Boolean */
101 dynamic : false,
102 // these 3 are only available at config time
103 /** String consisting of the resize handles to display. Valid handles are
104 * n (north), s (south) e (east), w (west), ne (northeast), nw (northwest), se (southeast), sw (southwest)
105 * and all (which applies them all). If this is blank it defaults to "e,s,se". Handles can be delimited using
106 * a space, comma or semi-colon. This is only applied at config time. @type String*/
107 handles : false,
108 multiDirectional : false,
109 /** true to disable mouse tracking. This is only applied at config time. @type Boolean*/
110 disableTrackOver : false,
111 /** Animation easing @type YAHOO.util.Easing */
112 easing : YAHOO.util.Easing ? YAHOO.util.Easing.easeOutStrong : null,
113 /** The increment to snap the width resize in pixels (dynamic must be true) @type Number */
114 widthIncrement : 0,
115 /** The increment to snap the height resize in pixels (dynamic must be true) @type Number */
116 heightIncrement : 0,
117 /** true to pin the resize handles. This is only applied at config time. @type Boolean*/
118 pinned : false,
119 /** The initial width for the element @type Number */
120 width : null,
121 /** The initial height for the element @type Number */
122 height : null,
123 /** true to preserve the initial size ratio. @type Boolean*/
124 preserveRatio : false,
125 /** true for transparent handles. This is only applied at config time. @type Boolean*/
126 transparent: false,
127 /** The minimum allowed page X for the element (only used for west resizing, defaults to 0) @type Number */
128 minX: 0,
129 /** The minimum allowed page Y for the element (only used for north resizing, defaults to 0) @type Number */
130 minY: 0,
131 /** convenience to initialize drag drop. @type Boolean*/
132 draggable: false
133 });
134
135 if(this.pinned){
136 this.disableTrackOver = true;
137 this.el.addClass('yresizable-pinned');
138 }
139 // if the element isn't positioned, make it relative
140 var position = this.el.getStyle('position');
141 if(position != 'absolute' && position != 'fixed'){
142 this.el.setStyle('position', 'relative');
143 }
144 if(!this.handles){ // no handles passed, must be legacy style
145 this.handles = 's,e,se';
146 if(this.multiDirectional){
147 this.handles += ',n,w';
148 }
149 }
150 if(this.handles == 'all'){
151 this.handles = 'n s e w ne nw se sw';
152 }
153 var hs = this.handles.split(/\s*?[,;]\s*?| /);
154 var ps = YAHOO.ext.Resizable.positions;
155 for(var i = 0, len = hs.length; i < len; i++){
156 if(hs[i] && ps[hs[i]]){
157 var pos = ps[hs[i]];
158 this[pos] = new YAHOO.ext.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
159 }
160 }
161 // legacy
162 this.corner = this.southeast;
163
164 this.activeHandle = null;
165
166 if(this.resizeChild){
167 if(typeof this.resizeChild == 'boolean'){
168 this.resizeChild = YAHOO.ext.Element.get(this.el.dom.firstChild, true);
169 }else{
170 this.resizeChild = YAHOO.ext.Element.get(this.resizeChild, true);
171 }
172 }
173
174 if(this.adjustments == 'auto'){
175 var rc = this.resizeChild;
176 var hw = this.west, he = this.east, hn = this.north, hs = this.south;
177 if(rc && (hw || hn)){
178 rc.setRelativePositioned();
179 rc.setLeft(hw ? hw.el.getWidth() : 0);
180 rc.setTop(hn ? hn.el.getHeight() : 0);
181 }
182 this.adjustments = [
183 (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
184 (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
185 ];
186 }
187
188 if(this.draggable){
189 this.dd = this.dynamic ?
190 this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
191 this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
192 }
193
194 // public events
195 this.events = {
196 /**
197 * @event beforeresize
198 * Fired before resize is allowed. Set enabled to false to cancel resize.
199 * @param {YAHOO.ext.Resizable} this
200 * @param {YAHOO.ext.EventObject} e The mousedown event
201 */
202 'beforeresize' : new YAHOO.util.CustomEvent(),
203 /**
204 * @event resize
205 * Fired after a resize.
206 * @param {YAHOO.ext.Resizable} this
207 * @param {Number} width The new width
208 * @param {Number} height The new height
209 * @param {YAHOO.ext.EventObject} e The mouseup event
210 */
211 'resize' : new YAHOO.util.CustomEvent()
212 };
213
214 if(this.width !== null && this.height !== null){
215 this.resizeTo(this.width, this.height);
216 }else{
217 this.updateChildSize();
218 }
219};
220
221YAHOO.extendX(YAHOO.ext.Resizable, YAHOO.ext.util.Observable, {
222 /**
223 * Perform a manual resize
224 * @param {Number} width
225 * @param {Number} height
226 */
227 resizeTo : function(width, height){
228 this.el.setSize(width, height);
229 this.updateChildSize();
230 this.fireEvent('resize', this, width, height, null);
231 },
232
233 startSizing : function(e){
234 this.fireEvent('beforeresize', this, e);
235 if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
236 this.resizing = true;
237 this.startBox = this.el.getBox();
238 this.startPoint = e.getXY();
239 this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
240 (this.startBox.y + this.startBox.height) - this.startPoint[1]];
241 this.proxy.setBox(this.startBox);
242
243 this.overlay.setSize(YAHOO.util.Dom.getDocumentWidth(), YAHOO.util.Dom.getDocumentHeight());
244 this.overlay.show();
245
246 if(!this.dynamic){
247 this.proxy.show();
248 }
249 }
250 },
251
252 onMouseDown : function(handle, e){
253 if(this.enabled){
254 e.stopEvent();
255 this.activeHandle = handle;
256 this.overlay.setStyle('cursor', handle.el.getStyle('cursor'));
257 this.startSizing(e);
258 }
259 },
260
261 onMouseUp : function(e){
262 var size = this.resizeElement();
263 this.resizing = false;
264 this.handleOut();
265 this.overlay.hide();
266 this.fireEvent('resize', this, size.width, size.height, e);
267 },
268
269 updateChildSize : function(){
270 if(this.resizeChild){
271 var el = this.el;
272 var child = this.resizeChild;
273 var adj = this.adjustments;
274 if(el.dom.offsetWidth){
275 var b = el.getSize(true);
276 child.setSize(b.width+adj[0], b.height+adj[1]);
277 }
278 // Second call here for IE
279 // The first call enables instant resizing and
280 // the second call corrects scroll bars if they
281 // exist
282 if(YAHOO.ext.util.Browser.isIE){
283 setTimeout(function(){
284 if(el.dom.offsetWidth){
285 var b = el.getSize(true);
286 child.setSize(b.width+adj[0], b.height+adj[1]);
287 }
288 }, 10);
289 }
290 }
291 },
292
293 snap : function(value, inc, min){
294 if(!inc || !value) return value;
295 var newValue = value;
296 var m = value % inc;
297 if(m > 0){
298 if(m > (inc/2)){
299 newValue = value + (inc-m);
300 }else{
301 newValue = value - m;
302 }
303 }
304 return Math.max(min, newValue);
305 },
306
307 resizeElement : function(){
308 var box = this.proxy.getBox();
309 //box.width = this.snap(box.width, this.widthIncrement);
310 //box.height = this.snap(box.height, this.heightIncrement);
311 //if(this.multiDirectional){
312 this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
313 //}else{
314 // this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
315 //}
316 this.updateChildSize();
317 this.proxy.hide();
318 return box;
319 },
320
321 constrain : function(v, diff, m, mx){
322 if(v - diff < m){
323 diff = v - m;
324 }else if(v - diff > mx){
325 diff = mx - v;
326 }
327 return diff;
328 },
329
330 onMouseMove : function(e){
331 if(this.enabled){
332 try{// try catch so if something goes wrong the user doesn't get hung
333
334 //var curXY = this.startPoint;
335 var curSize = this.curSize || this.startBox;
336 var x = this.startBox.x, y = this.startBox.y;
337 var ox = x, oy = y;
338 var w = curSize.width, h = curSize.height;
339 var ow = w, oh = h;
340 var mw = this.minWidth, mh = this.minHeight;
341 var mxw = this.maxWidth, mxh = this.maxHeight;
342 var wi = this.widthIncrement;
343 var hi = this.heightIncrement;
344
345 var eventXY = e.getXY();
346 var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
347 var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
348
349 var pos = this.activeHandle.position;
350
351 switch(pos){
352 case 'east':
353 w += diffX;
354 w = Math.min(Math.max(mw, w), mxw);
355 break;
356 case 'south':
357 h += diffY;
358 h = Math.min(Math.max(mh, h), mxh);
359 break;
360 case 'southeast':
361 w += diffX;
362 h += diffY;
363 w = Math.min(Math.max(mw, w), mxw);
364 h = Math.min(Math.max(mh, h), mxh);
365 break;
366 case 'north':
367 diffY = this.constrain(h, diffY, mh, mxh);
368 y += diffY;
369 h -= diffY;
370 break;
371 case 'west':
372 diffX = this.constrain(w, diffX, mw, mxw);
373 x += diffX;
374 w -= diffX;
375 break;
376 case 'northeast':
377 w += diffX;
378 w = Math.min(Math.max(mw, w), mxw);
379 diffY = this.constrain(h, diffY, mh, mxh);
380 y += diffY;
381 h -= diffY;
382 break;
383 case 'northwest':
384 diffX = this.constrain(w, diffX, mw, mxw);
385 diffY = this.constrain(h, diffY, mh, mxh);
386 y += diffY;
387 h -= diffY;
388 x += diffX;
389 w -= diffX;
390 break;
391 case 'southwest':
392 diffX = this.constrain(w, diffX, mw, mxw);
393 h += diffY;
394 h = Math.min(Math.max(mh, h), mxh);
395 x += diffX;
396 w -= diffX;
397 break;
398 }
399
400 var sw = this.snap(w, wi, mw);
401 var sh = this.snap(h, hi, mh);
402 if(sw != w || sh != h){
403 switch(pos){
404 case 'northeast':
405 y -= sh - h;
406 break;
407 case 'north':
408 y -= sh - h;
409 break;
410 case 'southwest':
411 x -= sw - w;
412 break;
413 case 'west':
414 x -= sw - w;
415 break;
416 case 'northwest':
417 x -= sw - w;
418 y -= sh - h;
419 break;
420 }
421 w = sw;
422 h = sh;
423 }
424
425 if(this.preserveRatio){
426 switch(pos){
427 case 'southeast':
428 case 'east':
429 h = oh * (w/ow);
430 h = Math.min(Math.max(mh, h), mxh);
431 w = ow * (h/oh);
432 break;
433 case 'south':
434 w = ow * (h/oh);
435 w = Math.min(Math.max(mw, w), mxw);
436 h = oh * (w/ow);
437 break;
438 case 'northeast':
439 w = ow * (h/oh);
440 w = Math.min(Math.max(mw, w), mxw);
441 h = oh * (w/ow);
442 break;
443 case 'north':
444 var tw = w;
445 w = ow * (h/oh);
446 w = Math.min(Math.max(mw, w), mxw);
447 h = oh * (w/ow);
448 x += (tw - w) / 2;
449 break;
450 case 'southwest':
451 h = oh * (w/ow);
452 h = Math.min(Math.max(mh, h), mxh);
453 var tw = w;
454 w = ow * (h/oh);
455 x += tw - w;
456 break;
457 case 'west':
458 var th = h;
459 h = oh * (w/ow);
460 h = Math.min(Math.max(mh, h), mxh);
461 y += (th - h) / 2;
462 var tw = w;
463 w = ow * (h/oh);
464 x += tw - w;
465 break;
466 case 'northwest':
467 var tw = w;
468 var th = h;
469 h = oh * (w/ow);
470 h = Math.min(Math.max(mh, h), mxh);
471 w = ow * (h/oh);
472 y += th - h;
473 x += tw - w;
474 break;
475
476 }
477 }
478 this.proxy.setBounds(x, y, w, h);
479 if(this.dynamic){
480 this.resizeElement();
481 }
482 }catch(e){}
483 }
484 },
485
486 handleOver : function(){
487 if(this.enabled){
488 this.el.addClass('yresizable-over');
489 }
490 },
491
492 handleOut : function(){
493 if(!this.resizing){
494 this.el.removeClass('yresizable-over');
495 }
496 },
497
498 /**
499 * Returns the element this component is bound to.
500 * @return {YAHOO.ext.Element}
501 */
502 getEl : function(){
503 return this.el;
504 },
505
506 /**
507 * Returns the resizeChild element (or null).
508 * @return {YAHOO.ext.Element}
509 */
510 getResizeChild : function(){
511 return this.resizeChild;
512 },
513
514 /**
515 * Destroys this resizable. If the element was wrapped and
516 * removeEl is not true then the wrap remains.
517 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
518 */
519 destroy : function(removeEl){
520 this.proxy.remove();
521 this.overlay.removeAllListeners();
522 this.overlay.remove();
523 var ps = YAHOO.ext.Resizable.positions;
524 for(var k in ps){
525 if(typeof ps[k] != 'function' && this[ps[k]]){
526 var h = this[ps[k]];
527 h.el.removeAllListeners();
528 h.el.remove();
529 }
530 }
531 if(removeEl){
532 this.el.update('');
533 this.el.remove();
534 }
535 }
536});
537
538// hash to map config positions to true positions
539YAHOO.ext.Resizable.positions = {
540 n: 'north', s: 'south', e: 'east', w: 'west', se: 'southeast', sw: 'southwest', nw: 'northwest', ne: 'northeast'
541};
542
543
544YAHOO.ext.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
545 if(!this.tpl){
546 // only initialize the template if resizable is used
547 var tpl = YAHOO.ext.DomHelper.createTemplate(
548 {tag: 'div', cls: 'yresizable-handle yresizable-handle-{0}', html: '&#160;'}
549 );
550 tpl.compile();
551 YAHOO.ext.Resizable.Handle.prototype.tpl = tpl;
552 }
553 this.position = pos;
554 this.rz = rz;
555 this.el = this.tpl.append(rz.el.dom, [this.position], true);
556 this.el.unselectable();
557 if(transparent){
558 this.el.setOpacity(0);
559 }
560 this.el.mon('mousedown', this.onMouseDown, this, true);
561 if(!disableTrackOver){
562 this.el.mon('mouseover', this.onMouseOver, this, true);
563 this.el.mon('mouseout', this.onMouseOut, this, true);
564 }
565};
566
567YAHOO.ext.Resizable.Handle.prototype = {
568 afterResize : function(rz){
569 // do nothing
570 },
571
572 onMouseDown : function(e){
573 this.rz.onMouseDown(this, e);
574 },
575
576 onMouseOver : function(e){
577 this.rz.handleOver(this, e);
578 },
579
580 onMouseOut : function(e){
581 this.rz.handleOut(this, e);
582 }
583};
584
585
586
diff --git a/frontend/beta/js/YUI-extensions/widgets/SplitBar.js b/frontend/beta/js/YUI-extensions/widgets/SplitBar.js
new file mode 100644
index 0000000..855d138
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/widgets/SplitBar.js
@@ -0,0 +1,468 @@
1/*
2 * splitbar.js, version .7
3 * Copyright(c) 2006, Jack Slocum.
4 * Code licensed under the BSD License
5 */
6if(YAHOO.util.DragDropMgr){
7 YAHOO.util.DragDropMgr.clickTimeThresh = 350;
8}
9/**
10 * @class YAHOO.ext.SplitBar
11 * @extends YAHOO.ext.util.Observable
12 * Creates draggable splitter bar functionality from two elements.
13 * <br><br>
14 * Usage:
15 * <pre><code>
16var split = new YAHOO.ext.SplitBar('elementToDrag', 'elementToSize',
17 YAHOO.ext.SplitBar.HORIZONTAL, YAHOO.ext.SplitBar.LEFT);
18split.setAdapter(new YAHOO.ext.SplitBar.AbsoluteLayoutAdapter("container"));
19split.minSize = 100;
20split.maxSize = 600;
21split.animate = true;
22split.onMoved.subscribe(splitterMoved);
23</code></pre>
24 * @requires YAHOO.ext.Element
25 * @requires YAHOO.util.Dom
26 * @requires YAHOO.util.Event
27 * @requires YAHOO.util.CustomEvent
28 * @requires YAHOO.util.DDProxy
29 * @requires YAHOO.util.Anim (optional) to support animation
30 * @requires YAHOO.util.Easing (optional) to support animation
31 * @constructor
32 * Create a new SplitBar
33 * @param {String/HTMLElement/Element} dragElement The element to be dragged and act as the SplitBar.
34 * @param {String/HTMLElement/Element} resizingElement The element to be resized based on where the SplitBar element is dragged
35 * @param {Number} orientation (optional) Either YAHOO.ext.SplitBar.HORIZONTAL or YAHOO.ext.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36 * @param {Number} placement (optional) Either YAHOO.ext.SplitBar.LEFT or YAHOO.ext.SplitBar.RIGHT for horizontal or
37 YAHOO.ext.SplitBar.TOP or YAHOO.ext.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the intial position
38 position of the SplitBar).
39 */
40YAHOO.ext.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
41
42 /** @private */
43 this.el = YAHOO.ext.Element.get(dragElement, true);
44 this.el.dom.unselectable = 'on';
45 /** @private */
46 this.resizingEl = YAHOO.ext.Element.get(resizingElement, true);
47
48 /**
49 * @private
50 * The orientation of the split. Either YAHOO.ext.SplitBar.HORIZONTAL or YAHOO.ext.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
51 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
52 * @type Number
53 */
54 this.orientation = orientation || YAHOO.ext.SplitBar.HORIZONTAL;
55
56 /**
57 * The minimum size of the resizing element. (Defaults to 0)
58 * @type Number
59 */
60 this.minSize = 0;
61
62 /**
63 * The maximum size of the resizing element. (Defaults to 2000)
64 * @type Number
65 */
66 this.maxSize = 2000;
67
68 this.onMoved = new YAHOO.util.CustomEvent("SplitBarMoved", this);
69
70 /**
71 * Whether to animate the transition to the new size
72 * @type Boolean
73 */
74 this.animate = false;
75
76 /**
77 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
78 * @type Boolean
79 */
80 this.useShim = false;
81
82 /** @private */
83 this.shim = null;
84
85 if(!existingProxy){
86 /** @private */
87 this.proxy = YAHOO.ext.SplitBar.createProxy(this.orientation);
88 }else{
89 this.proxy = getEl(existingProxy).dom;
90 }
91 /** @private */
92 this.dd = new YAHOO.util.DDProxy(this.el.dom.id, "SplitBars", {dragElId : this.proxy.id});
93
94 /** @private */
95 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
96
97 /** @private */
98 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
99
100 /** @private */
101 this.dragSpecs = {};
102
103 /**
104 * @private The adapter to use to positon and resize elements
105 */
106 this.adapter = new YAHOO.ext.SplitBar.BasicLayoutAdapter();
107 this.adapter.init(this);
108
109 if(this.orientation == YAHOO.ext.SplitBar.HORIZONTAL){
110 /** @private */
111 this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? YAHOO.ext.SplitBar.LEFT : YAHOO.ext.SplitBar.RIGHT);
112 this.el.setStyle('cursor', 'e-resize');
113 }else{
114 /** @private */
115 this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? YAHOO.ext.SplitBar.TOP : YAHOO.ext.SplitBar.BOTTOM);
116 this.el.setStyle('cursor', 'n-resize');
117 }
118
119 this.events = {
120 /**
121 * @event resize
122 * Fires when the splitter is moved (alias for moved)
123 * @param {YAHOO.ext.SplitBar} this
124 * @param {Number} newSize the new width or height
125 */
126 'resize' : this.onMoved,
127 /**
128 * @event moved
129 * Fires when the splitter is moved
130 * @param {YAHOO.ext.SplitBar} this
131 * @param {Number} newSize the new width or height
132 */
133 'moved' : this.onMoved,
134 /**
135 * @event beforeresize
136 * Fires before the splitter is dragged
137 * @param {YAHOO.ext.SplitBar} this
138 */
139 'beforeresize' : new YAHOO.util.CustomEvent('beforeresize')
140 }
141}
142
143YAHOO.extendX(YAHOO.ext.SplitBar, YAHOO.ext.util.Observable, {
144 onStartProxyDrag : function(x, y){
145 this.fireEvent('beforeresize', this);
146 if(this.useShim){
147 if(!this.shim){
148 this.shim = YAHOO.ext.SplitBar.createShim();
149 }
150 this.shim.setVisible(true);
151 }
152 YAHOO.util.Dom.setStyle(this.proxy, 'display', 'block');
153 var size = this.adapter.getElementSize(this);
154 this.activeMinSize = this.getMinimumSize();;
155 this.activeMaxSize = this.getMaximumSize();;
156 var c1 = size - this.activeMinSize;
157 var c2 = Math.max(this.activeMaxSize - size, 0);
158 if(this.orientation == YAHOO.ext.SplitBar.HORIZONTAL){
159 this.dd.resetConstraints();
160 this.dd.setXConstraint(
161 this.placement == YAHOO.ext.SplitBar.LEFT ? c1 : c2,
162 this.placement == YAHOO.ext.SplitBar.LEFT ? c2 : c1
163 );
164 this.dd.setYConstraint(0, 0);
165 }else{
166 this.dd.resetConstraints();
167 this.dd.setXConstraint(0, 0);
168 this.dd.setYConstraint(
169 this.placement == YAHOO.ext.SplitBar.TOP ? c1 : c2,
170 this.placement == YAHOO.ext.SplitBar.TOP ? c2 : c1
171 );
172 }
173 this.dragSpecs.startSize = size;
174 this.dragSpecs.startPoint = [x, y];
175
176 YAHOO.util.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
177 },
178
179 /**
180 * @private Called after the drag operation by the DDProxy
181 */
182 onEndProxyDrag : function(e){
183 YAHOO.util.Dom.setStyle(this.proxy, 'display', 'none');
184 var endPoint = YAHOO.util.Event.getXY(e);
185 if(this.useShim){
186 this.shim.setVisible(false);
187 }
188 var newSize;
189 if(this.orientation == YAHOO.ext.SplitBar.HORIZONTAL){
190 newSize = this.dragSpecs.startSize +
191 (this.placement == YAHOO.ext.SplitBar.LEFT ?
192 endPoint[0] - this.dragSpecs.startPoint[0] :
193 this.dragSpecs.startPoint[0] - endPoint[0]
194 );
195 }else{
196 newSize = this.dragSpecs.startSize +
197 (this.placement == YAHOO.ext.SplitBar.TOP ?
198 endPoint[1] - this.dragSpecs.startPoint[1] :
199 this.dragSpecs.startPoint[1] - endPoint[1]
200 );
201 }
202 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
203 if(newSize != this.dragSpecs.startSize){
204 this.adapter.setElementSize(this, newSize);
205 this.onMoved.fireDirect(this, newSize);
206 }
207 },
208
209 /**
210 * Get the adapter this SplitBar uses
211 * @return The adapter object
212 */
213 getAdapter : function(){
214 return this.adapter;
215 },
216
217 /**
218 * Set the adapter this SplitBar uses
219 * @param {Object} adapter A SplitBar adapter object
220 */
221 setAdapter : function(adapter){
222 this.adapter = adapter;
223 this.adapter.init(this);
224 },
225
226 /**
227 * Gets the minimum size for the resizing element
228 * @return {Number} The minimum size
229 */
230 getMinimumSize : function(){
231 return this.minSize;
232 },
233
234 /**
235 * Sets the minimum size for the resizing element
236 * @param {Number} minSize The minimum size
237 */
238 setMinimumSize : function(minSize){
239 this.minSize = minSize;
240 },
241
242 /**
243 * Gets the maximum size for the resizing element
244 * @return {Number} The maximum size
245 */
246 getMaximumSize : function(){
247 return this.maxSize;
248 },
249
250 /**
251 * Sets the maximum size for the resizing element
252 * @param {Number} maxSize The maximum size
253 */
254 setMaximumSize : function(maxSize){
255 this.maxSize = maxSize;
256 },
257
258 /**
259 * Sets the initialize size for the resizing element
260 * @param {Number} size The initial size
261 */
262 setCurrentSize : function(size){
263 var oldAnimate = this.animate;
264 this.animate = false;
265 this.adapter.setElementSize(this, size);
266 this.animate = oldAnimate;
267 },
268
269 /**
270 * Destroy this splitbar.
271 * @param {Boolean} removeEl True to remove the element
272 */
273 destroy : function(removeEl){
274 if(this.shim){
275 this.shim.remove();
276 }
277 this.dd.unreg();
278 this.proxy.parentNode.removeChild(this.proxy);
279 if(removeEl){
280 this.el.remove();
281 }
282 }
283});
284
285/**
286 * @private static Create the shim to drag over iframes
287 */
288YAHOO.ext.SplitBar.createShim = function(){
289 var shim = document.createElement('div');
290 shim.unselectable = 'on';
291 YAHOO.util.Dom.generateId(shim, 'split-shim');
292 YAHOO.util.Dom.setStyle(shim, 'width', '100%');
293 YAHOO.util.Dom.setStyle(shim, 'height', '100%');
294 YAHOO.util.Dom.setStyle(shim, 'position', 'absolute');
295 YAHOO.util.Dom.setStyle(shim, 'background', 'white');
296 YAHOO.util.Dom.setStyle(shim, 'z-index', 11000);
297 window.document.body.appendChild(shim);
298 var shimEl = YAHOO.ext.Element.get(shim);
299 shimEl.setOpacity(.01);
300 shimEl.setXY([0, 0]);
301 return shimEl;
302};
303
304/**
305 * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
306 */
307YAHOO.ext.SplitBar.createProxy = function(orientation){
308 var proxy = document.createElement('div');
309 proxy.unselectable = 'on';
310 YAHOO.util.Dom.generateId(proxy, 'split-proxy');
311 YAHOO.util.Dom.setStyle(proxy, 'position', 'absolute');
312 YAHOO.util.Dom.setStyle(proxy, 'visibility', 'hidden');
313 YAHOO.util.Dom.setStyle(proxy, 'z-index', 11001);
314 YAHOO.util.Dom.setStyle(proxy, 'background-color', "#aaa");
315 if(orientation == YAHOO.ext.SplitBar.HORIZONTAL){
316 YAHOO.util.Dom.setStyle(proxy, 'cursor', 'e-resize');
317 }else{
318 YAHOO.util.Dom.setStyle(proxy, 'cursor', 'n-resize');
319 }
320 // the next 2 fix IE abs position div height problem
321 YAHOO.util.Dom.setStyle(proxy, 'line-height', '0px');
322 YAHOO.util.Dom.setStyle(proxy, 'font-size', '0px');
323 window.document.body.appendChild(proxy);
324 return proxy;
325};
326
327/**
328 * @class YAHOO.ext.SplitBar.BasicLayoutAdapter
329 * Default Adapter. It assumes the splitter and resizing element are not positioned
330 * elements and only gets/sets the width of the element. Generally used for table based layouts.
331 */
332YAHOO.ext.SplitBar.BasicLayoutAdapter = function(){
333};
334
335YAHOO.ext.SplitBar.BasicLayoutAdapter.prototype = {
336 // do nothing for now
337 init : function(s){
338
339 },
340 /**
341 * Called before drag operations to get the current size of the resizing element.
342 * @param {YAHOO.ext.SplitBar} s The SplitBar using this adapter
343 */
344 getElementSize : function(s){
345 if(s.orientation == YAHOO.ext.SplitBar.HORIZONTAL){
346 return s.resizingEl.getWidth();
347 }else{
348 return s.resizingEl.getHeight();
349 }
350 },
351
352 /**
353 * Called after drag operations to set the size of the resizing element.
354 * @param {YAHOO.ext.SplitBar} s The SplitBar using this adapter
355 * @param {Number} newSize The new size to set
356 * @param {Function} onComplete A function to be invoke when resizing is complete
357 */
358 setElementSize : function(s, newSize, onComplete){
359 if(s.orientation == YAHOO.ext.SplitBar.HORIZONTAL){
360 if(!YAHOO.util.Anim || !s.animate){
361 s.resizingEl.setWidth(newSize);
362 if(onComplete){
363 onComplete(s, newSize);
364 }
365 }else{
366 s.resizingEl.setWidth(newSize, true, .1, onComplete, YAHOO.util.Easing.easeOut);
367 }
368 }else{
369
370 if(!YAHOO.util.Anim || !s.animate){
371 s.resizingEl.setHeight(newSize);
372 if(onComplete){
373 onComplete(s, newSize);
374 }
375 }else{
376 s.resizingEl.setHeight(newSize, true, .1, onComplete, YAHOO.util.Easing.easeOut);
377 }
378 }
379 }
380};
381
382/**
383 *@class YAHOO.ext.SplitBar.AbsoluteLayoutAdapter
384 * @extends YAHOO.ext.SplitBar.BasicLayoutAdapter
385 * Adapter that moves the splitter element to align with the resized sizing element.
386 * Used with an absolute positioned SplitBar.
387 * @param {String/HTMLElement/Element} container The container that wraps around the absolute positioned content. If it's
388 * document.body, make sure you assign an id to the body element.
389 */
390YAHOO.ext.SplitBar.AbsoluteLayoutAdapter = function(container){
391 this.basic = new YAHOO.ext.SplitBar.BasicLayoutAdapter();
392 this.container = getEl(container);
393}
394
395YAHOO.ext.SplitBar.AbsoluteLayoutAdapter.prototype = {
396 init : function(s){
397 this.basic.init(s);
398 //YAHOO.util.Event.on(window, 'resize', this.moveSplitter.createDelegate(this, [s]));
399 },
400
401 getElementSize : function(s){
402 return this.basic.getElementSize(s);
403 },
404
405 setElementSize : function(s, newSize, onComplete){
406 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
407 },
408
409 moveSplitter : function(s){
410 var yes = YAHOO.ext.SplitBar;
411 switch(s.placement){
412 case yes.LEFT:
413 s.el.setX(s.resizingEl.getRight());
414 break;
415 case yes.RIGHT:
416 s.el.setStyle('right', (this.container.getWidth() - s.resizingEl.getLeft()) + 'px');
417 break;
418 case yes.TOP:
419 s.el.setY(s.resizingEl.getBottom());
420 break;
421 case yes.BOTTOM:
422 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
423 break;
424 }
425 }
426};
427
428/**
429 * Orientation constant - Create a vertical SplitBar
430 * @static
431 * @type Number
432 */
433YAHOO.ext.SplitBar.VERTICAL = 1;
434
435/**
436 * Orientation constant - Create a horizontal SplitBar
437 * @static
438 * @type Number
439 */
440YAHOO.ext.SplitBar.HORIZONTAL = 2;
441
442/**
443 * Placement constant - The resizing element is to the left of the splitter element
444 * @static
445 * @type Number
446 */
447YAHOO.ext.SplitBar.LEFT = 1;
448
449/**
450 * Placement constant - The resizing element is to the right of the splitter element
451 * @static
452 * @type Number
453 */
454YAHOO.ext.SplitBar.RIGHT = 2;
455
456/**
457 * Placement constant - The resizing element is positioned above the splitter element
458 * @static
459 * @type Number
460 */
461YAHOO.ext.SplitBar.TOP = 3;
462
463/**
464 * Placement constant - The resizing element is positioned under splitter element
465 * @static
466 * @type Number
467 */
468YAHOO.ext.SplitBar.BOTTOM = 4;
diff --git a/frontend/beta/js/YUI-extensions/widgets/TabPanel.js b/frontend/beta/js/YUI-extensions/widgets/TabPanel.js
new file mode 100644
index 0000000..25fd142
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/widgets/TabPanel.js
@@ -0,0 +1,756 @@
1/**
2 * @class YAHOO.ext.TabPanel
3 * @extends YAHOO.ext.util.Observable
4 * Creates a lightweight TabPanel component using Yahoo! UI.
5 * <br><br>
6 * Usage:
7 * <pre><code>
8 <font color="#008000">// basic tabs 1, built from existing content</font>
9 var tabs = new YAHOO.ext.TabPanel('tabs1');
10 tabs.addTab('script', "View Script");
11 tabs.addTab('markup', "View Markup");
12 tabs.activate('script');
13
14 <font color="#008000">// more advanced tabs, built from javascript</font>
15 var jtabs = new YAHOO.ext.TabPanel('jtabs');
16 jtabs.addTab('jtabs-1', "Normal Tab", "My content was added during construction.");
17
18 <font color="#008000">// set up the UpdateManager</font>
19 var tab2 = jtabs.addTab('jtabs-2', "Ajax Tab 1");
20 var updater = tab2.getUpdateManager();
21 updater.setDefaultUrl('ajax1.htm');
22 tab2.onActivate.subscribe(updater.refresh, updater, true);
23
24 <font color="#008000">// Use setUrl for Ajax loading</font>
25 var tab3 = jtabs.addTab('jtabs-3', "Ajax Tab 2");
26 tab3.setUrl('ajax2.htm', null, true);
27
28 <font color="#008000">// Disabled tab</font>
29 var tab4 = jtabs.addTab('tabs1-5', "Disabled Tab", "Can't see me cause I'm disabled");
30 tab4.disable();
31
32 jtabs.activate('jtabs-1');
33}
34 * </code></pre>
35 * @requires YAHOO.ext.Element
36 * @requires YAHOO.ext.UpdateManager
37 * @requires YAHOO.util.Dom
38 * @requires YAHOO.util.Event
39 * @requires YAHOO.util.CustomEvent
40 * @requires YAHOO.util.Connect (optional)
41 * @constructor
42 * Create new TabPanel.
43 * @param {String/HTMLElement/Element} container The id, DOM element or YAHOO.ext.Element container where this TabPanel is to be rendered.
44 * @param {Boolean} config Config object to set any properties for this TabPanel or true to render the tabs on the bottom.
45 */
46YAHOO.ext.TabPanel = function(container, config){
47 /**
48 * The container element for this TabPanel.
49 * @type YAHOO.ext.Element
50 */
51 this.el = getEl(container, true);
52 /** The position of the tabs. Can be 'top' or 'bottom' @type String */
53 this.tabPosition = 'top';
54 this.currentTabWidth = 0;
55 /** The minimum width of a tab (ignored if resizeTabs is not true). @type Number */
56 this.minTabWidth = 40;
57 /** The maximum width of a tab (ignored if resizeTabs is not true). @type Number */
58 this.maxTabWidth = 250;
59 /** The preferred (default) width of a tab (ignored if resizeTabs is not true). @type Number */
60 this.preferredTabWidth = 175;
61 /** Set this to true to enable dynamic tab resizing. @type Boolean */
62 this.resizeTabs = false;
63 /** Set this to true to turn on window resizing monitoring (ignored if resizeTabs is not true). @type Boolean */
64 this.monitorResize = true;
65
66 if(config){
67 if(typeof config == 'boolean'){
68 this.tabPosition = config ? 'bottom' : 'top';
69 }else{
70 YAHOO.ext.util.Config.apply(this, config);
71 }
72 }
73 if(this.tabPosition == 'bottom'){
74 this.bodyEl = getEl(this.createBody(this.el.dom));
75 this.el.addClass('ytabs-bottom');
76 }
77 this.stripWrap = getEl(this.createStrip(this.el.dom), true);
78 this.stripEl = getEl(this.createStripList(this.stripWrap.dom), true);
79 this.stripBody = getEl(this.stripWrap.dom.firstChild.firstChild, true);
80 if(YAHOO.ext.util.Browser.isIE){
81 YAHOO.util.Dom.setStyle(this.stripWrap.dom.firstChild, 'overflow-x', 'hidden');
82 }
83 if(this.tabPosition != 'bottom'){
84 /** The body element that contains TabPaneItem bodies.
85 * @type YAHOO.ext.Element
86 */
87 this.bodyEl = getEl(this.createBody(this.el.dom));
88 this.el.addClass('ytabs-top');
89 }
90 this.items = [];
91
92 this.bodyEl.setStyle('position', 'relative');
93
94 // add indexOf to array if it isn't present
95 if(!this.items.indexOf){
96 this.items.indexOf = function(o){
97 for(var i = 0, len = this.length; i < len; i++){
98 if(this[i] == o) return i;
99 }
100 return -1;
101 }
102 }
103 this.active = null;
104 this.onTabChange = new YAHOO.util.CustomEvent('TabItem.onTabChange');
105 this.activateDelegate = this.activate.createDelegate(this);
106
107 this.events = {
108 /**
109 * @event tabchange
110 * Fires when the active tab changes
111 * @param {YAHOO.ext.TabPanel} this
112 * @param {YAHOO.ext.TabPanelItem} activePanel The new active tab
113 */
114 'tabchange': this.onTabChange,
115 /**
116 * @event beforetabchange
117 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
118 * @param {YAHOO.ext.TabPanel} this
119 * @param {Object} e Set cancel to true on this object to cancel the tab change
120 * @param {YAHOO.ext.TabPanelItem} tab The tab being changed to
121 */
122 'beforetabchange' : new YAHOO.util.CustomEvent('beforechange')
123 };
124
125 YAHOO.ext.EventManager.onWindowResize(this.onResize, this, true);
126 this.cpad = this.el.getPadding('lr');
127 this.hiddenCount = 0;
128}
129
130YAHOO.ext.TabPanel.prototype = {
131 fireEvent : YAHOO.ext.util.Observable.prototype.fireEvent,
132 on : YAHOO.ext.util.Observable.prototype.on,
133 addListener : YAHOO.ext.util.Observable.prototype.addListener,
134 delayedListener : YAHOO.ext.util.Observable.prototype.delayedListener,
135 removeListener : YAHOO.ext.util.Observable.prototype.removeListener,
136 purgeListeners : YAHOO.ext.util.Observable.prototype.purgeListeners,
137 /**
138 * Creates a new TabPanelItem by looking for an existing element with the provided id - if it's not found it creates one.
139 * @param {String} id The id of the div to use <b>or create</b>
140 * @param {String} text The text for the tab
141 * @param {<i>String</i>} content (optional) Content to put in the TabPanelItem body
142 * @param {<i>Boolean</i>} closable (optional) True to create a close icon on the tab
143 * @return {YAHOO.ext.TabPanelItem} The created TabPanelItem
144 */
145 addTab : function(id, text, content, closable){
146 var item = new YAHOO.ext.TabPanelItem(this, id, text, closable);
147 this.addTabItem(item);
148 if(content){
149 item.setContent(content);
150 }
151 return item;
152 },
153
154 /**
155 * Returns the TabPanelItem with the specified id/index
156 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
157 * @return {YAHOO.ext.TabPanelItem}
158 */
159 getTab : function(id){
160 return this.items[id];
161 },
162
163 /**
164 * Hides the TabPanelItem with the specified id/index
165 * @param {String/Number} id The id or index of the TabPanelItem to hide.
166 */
167 hideTab : function(id){
168 var t = this.items[id];
169 if(!t.isHidden()){
170 t.setHidden(true);
171 this.hiddenCount++;
172 this.autoSizeTabs();
173 }
174 },
175
176 /**
177 * "Unhides" the TabPanelItem with the specified id/index
178 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
179 */
180 unhideTab : function(id){
181 var t = this.items[id];
182 if(t.isHidden()){
183 t.setHidden(false);
184 this.hiddenCount--;
185 this.autoSizeTabs();
186 }
187 },
188
189 /**
190 * Add an existing TabPanelItem.
191 * @param {YAHOO.ext.TabPanelItem} item The TabPanelItem to add
192 */
193 addTabItem : function(item){
194 this.items[item.id] = item;
195 this.items.push(item);
196 if(this.resizeTabs){
197 item.setWidth(this.currentTabWidth || this.preferredTabWidth)
198 this.autoSizeTabs();
199 }else{
200 item.autoSize();
201 }
202 },
203
204 /**
205 * Remove a TabPanelItem.
206 * @param {String/Number} id The id or index of the TabPanelItem to remove.
207 */
208 removeTab : function(id){
209 var items = this.items;
210 var tab = items[id];
211 if(!tab) return;
212 var index = items.indexOf(tab);
213 if(this.active == tab && items.length > 1){
214 var newTab = this.getNextAvailable(index);
215 if(newTab)newTab.activate();
216 }
217 this.stripEl.dom.removeChild(tab.pnode.dom);
218 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
219 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
220 }
221 items.splice(index, 1);
222 delete this.items[tab.id];
223 tab.fireEvent('close', tab);
224 tab.purgeListeners();
225 this.autoSizeTabs();
226 },
227
228 getNextAvailable : function(start){
229 var items = this.items;
230 var index = start;
231 // look for a next tab that will slide over to
232 // replace the one being removed
233 while(index < items.length){
234 var item = items[++index];
235 if(item && !item.isHidden()){
236 return item;
237 }
238 }
239 // if one isn't found select the previous tab (on the left)
240 var index = start;
241 while(index >= 0){
242 var item = items[--index];
243 if(item && !item.isHidden()){
244 return item;
245 }
246 }
247 return null;
248 },
249
250 /**
251 * Disable a TabPanelItem. <b>It cannot be the active tab, if it is this call is ignored.</b>.
252 * @param {String/Number} id The id or index of the TabPanelItem to disable.
253 */
254 disableTab : function(id){
255 var tab = this.items[id];
256 if(tab && this.active != tab){
257 tab.disable();
258 }
259 },
260
261 /**
262 * Enable a TabPanelItem that is disabled.
263 * @param {String/Number} id The id or index of the TabPanelItem to enable.
264 */
265 enableTab : function(id){
266 var tab = this.items[id];
267 tab.enable();
268 },
269
270 /**
271 * Activate a TabPanelItem. The currently active will be deactivated.
272 * @param {String/Number} id The id or index of the TabPanelItem to activate.
273 */
274 activate : function(id){
275 var tab = this.items[id];
276 if(tab == this.active){
277 return tab;
278 }
279 var e = {};
280 this.fireEvent('beforetabchange', this, e, tab);
281 if(e.cancel !== true && !tab.disabled){
282 if(this.active){
283 this.active.hide();
284 }
285 this.active = this.items[id];
286 this.active.show();
287 this.onTabChange.fireDirect(this, this.active);
288 }
289 return tab;
290 },
291
292 /**
293 * Get the active TabPanelItem
294 * @return {YAHOO.ext.TabPanelItem} The active TabPanelItem or null if none are active.
295 */
296 getActiveTab : function(){
297 return this.active;
298 },
299
300 /**
301 * Updates the tab body element to fit the height of the container element
302 * for overflow scrolling
303 * @param {Number} targetHeight (optional) Override the starting height from the elements height
304 */
305 syncHeight : function(targetHeight){
306 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth('tb')-this.el.getPadding('tb');
307 var bm = this.bodyEl.getMargins();
308 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
309 this.bodyEl.setHeight(newHeight);
310 return newHeight;
311 },
312
313 onResize : function(){
314 if(this.monitorResize){
315 this.autoSizeTabs();
316 }
317 },
318
319 /**
320 * Disables tab resizing while tabs are being added (if resizeTabs is false this does nothing)
321 */
322 beginUpdate : function(){
323 this.updating = true;
324 },
325
326 /**
327 * Stops an update and resizes the tabs (if resizeTabs is false this does nothing)
328 */
329 endUpdate : function(){
330 this.updating = false;
331 this.autoSizeTabs();
332 },
333
334 /**
335 * Manual call to resize the tabs (if resizeTabs is false this does nothing)
336 */
337 autoSizeTabs : function(){
338 var count = this.items.length;
339 var vcount = count - this.hiddenCount;
340 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
341 var w = Math.max(this.el.getWidth() - this.cpad, 10);
342 var availWidth = Math.floor(w / vcount);
343 var b = this.stripBody;
344 if(b.getWidth() > w){
345 var tabs = this.items;
346 this.setTabWidth(Math.max(availWidth, this.minTabWidth));
347 if(availWidth < this.minTabWidth){
348 /*if(!this.sleft){ // incomplete scrolling code
349 this.createScrollButtons();
350 }
351 this.showScroll();
352 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
353 }
354 }else{
355 if(this.currentTabWidth < this.preferredTabWidth){
356 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth));
357 }
358 }
359 },
360
361 /**
362 * Returns the number of tabs
363 * @return {Number}
364 */
365 getCount : function(){
366 return this.items.length;
367 },
368
369 /**
370 * Resizes all the tabs to the passed width
371 * @param {Number} The new width
372 */
373 setTabWidth : function(width){
374 this.currentTabWidth = width;
375 for(var i = 0, len = this.items.length; i < len; i++) {
376 if(!this.items[i].isHidden())this.items[i].setWidth(width);
377 }
378 },
379
380 /**
381 * Destroys this TabPanel
382 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well
383 */
384 destroy : function(removeEl){
385 YAHOO.ext.EventManager.removeResizeListener(this.onResize, this);
386 for(var i = 0, len = this.items.length; i < len; i++){
387 this.items[i].purgeListeners();
388 }
389 if(removeEl === true){
390 this.el.update('');
391 this.el.remove();
392 }
393 }
394};
395
396/**
397* @class YAHOO.ext.TabPanelItem
398* @extends YAHOO.ext.util.Observable
399*/
400YAHOO.ext.TabPanelItem = function(tabPanel, id, text, closable){
401 /**
402 * The TabPanel this TabPanelItem belongs to
403 * @type YAHOO.ext.TabPanel
404 */
405 this.tabPanel = tabPanel;
406 /**
407 * The id for this TabPanelItem
408 * @type String
409 */
410 this.id = id;
411 /** @private */
412 this.disabled = false;
413 /** @private */
414 this.text = text;
415 /** @private */
416 this.loaded = false;
417 this.closable = closable;
418
419 /**
420 * The body element for this TabPanelItem
421 * @type YAHOO.ext.Element
422 */
423 this.bodyEl = getEl(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
424 this.bodyEl.setVisibilityMode(YAHOO.ext.Element.VISIBILITY);
425 this.bodyEl.setStyle('display', 'block');
426 this.bodyEl.setStyle('zoom', '1');
427 this.hideAction();
428
429 var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
430 /** @private */
431 this.el = getEl(els.el, true);
432 this.inner = getEl(els.inner, true);
433 this.textEl = getEl(this.el.dom.firstChild.firstChild.firstChild, true);
434 this.pnode = getEl(els.el.parentNode, true);
435 this.el.mon('click', this.onTabClick, this, true);
436 /** @private */
437 if(closable){
438 var c = getEl(els.close, true);
439 c.dom.title = this.closeText;
440 c.addClassOnOver('close-over');
441 c.mon('click', this.closeClick, this, true);
442 }
443
444 // these two are now private and deprecated
445 this.onActivate = new YAHOO.util.CustomEvent('TabItem.onActivate');
446 this.onDeactivate = new YAHOO.util.CustomEvent('TabItem.onDeactivate');
447
448 this.events = {
449 /**
450 * @event activate
451 * Fires when this tab becomes the active tab
452 * @param {YAHOO.ext.TabPanel} tabPanel
453 * @param {YAHOO.ext.TabPanelItem} this
454 */
455 'activate': this.onActivate,
456 /**
457 * @event beforeclose
458 * Fires before this tab is closed. To cancal the close, set cancel to true on e. (e.cancel = true)
459 * @param {YAHOO.ext.TabPanelItem} this
460 * @param {Object} e Set cancel to true on this object to cancel the close.
461 */
462 'beforeclose': new YAHOO.util.CustomEvent('beforeclose'),
463 /**
464 * @event close
465 * Fires when this tab is closed
466 * @param {YAHOO.ext.TabPanelItem} this
467 */
468 'close': new YAHOO.util.CustomEvent('close'),
469 /**
470 * @event deactivate
471 * Fires when this tab is no longer the active tab
472 * @param {YAHOO.ext.TabPanel} tabPanel
473 * @param {YAHOO.ext.TabPanelItem} this
474 */
475 'deactivate' : this.onDeactivate
476 };
477 this.hidden = false;
478};
479
480YAHOO.ext.TabPanelItem.prototype = {
481 fireEvent : YAHOO.ext.util.Observable.prototype.fireEvent,
482 on : YAHOO.ext.util.Observable.prototype.on,
483 addListener : YAHOO.ext.util.Observable.prototype.addListener,
484 delayedListener : YAHOO.ext.util.Observable.prototype.delayedListener,
485 removeListener : YAHOO.ext.util.Observable.prototype.removeListener,
486 purgeListeners : function(){
487 YAHOO.ext.util.Observable.prototype.purgeListeners.call(this);
488 this.el.removeAllListeners();
489 },
490 /**
491 * Show this TabPanelItem - this <b>does not</b> deactivate the currently active TabPanelItem.
492 */
493 show : function(){
494 this.pnode.addClass('on');
495 this.showAction();
496 if(YAHOO.ext.util.Browser.isOpera){
497 this.tabPanel.stripWrap.repaint();
498 }
499 this.onActivate.fireDirect(this.tabPanel, this);
500 },
501
502 /**
503 * Returns true if this tab is the active tab
504 * @return {Boolean}
505 */
506 isActive : function(){
507 return this.tabPanel.getActiveTab() == this;
508 },
509
510 /**
511 * Hide this TabPanelItem - if you don't activate another TabPanelItem this could look odd.
512 */
513 hide : function(){
514 this.pnode.removeClass('on');
515 this.hideAction();
516 this.onDeactivate.fireDirect(this.tabPanel, this);
517 },
518
519 hideAction : function(){
520 this.bodyEl.setStyle('position', 'absolute');
521 this.bodyEl.setLeft('-20000px');
522 this.bodyEl.setTop('-20000px');
523 this.bodyEl.hide();
524 },
525
526 showAction : function(){
527 this.bodyEl.setStyle('position', 'relative');
528 this.bodyEl.setTop('');
529 this.bodyEl.setLeft('');
530 this.bodyEl.show();
531 this.tabPanel.el.repaint.defer(1);
532 },
533
534 /**
535 * Set the tooltip (title attribute) for the tab
536 * @param {String} tooltip
537 */
538 setTooltip : function(text){
539 this.textEl.dom.title = text;
540 },
541
542 onTabClick : function(e){
543 e.preventDefault();
544 this.tabPanel.activate(this.id);
545 },
546
547 getWidth : function(){
548 return this.inner.getWidth();
549 },
550
551 setWidth : function(width){
552 var iwidth = width - this.pnode.getPadding("lr");
553 this.inner.setWidth(iwidth);
554 this.textEl.setWidth(iwidth-this.inner.getPadding('lr'));
555 this.pnode.setWidth(width);
556 },
557
558 setHidden : function(hidden){
559 this.hidden = hidden;
560 this.pnode.setStyle('display', hidden ? 'none' : '');
561 },
562
563 /**
564 * Returns true if this tab is "hidden"
565 * @return {Boolean}
566 */
567 isHidden : function(){
568 return this.hidden;
569 },
570
571 /**
572 * Returns the text for this tab
573 * @return {String}
574 */
575 getText : function(){
576 return this.text;
577 },
578
579 autoSize : function(){
580 this.el.beginMeasure();
581 this.textEl.setWidth(1);
582 this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding('lr'));
583 this.el.endMeasure();
584 },
585
586 /**
587 * Sets the text for the tab (Note: this also sets the tooltip)
588 * @param {String} text
589 */
590 setText : function(text){
591 this.text = text;
592 this.textEl.update(text);
593 this.textEl.dom.title = text;
594 if(!this.tabPanel.resizeTabs){
595 this.autoSize();
596 }
597 },
598 /**
599 * Activate this TabPanelItem - this <b>does</b> deactivate the currently active TabPanelItem.
600 */
601 activate : function(){
602 this.tabPanel.activate(this.id);
603 },
604
605 /**
606 * Disable this TabPanelItem - this call is ignore if this is the active TabPanelItem.
607 */
608 disable : function(){
609 if(this.tabPanel.active != this){
610 this.disabled = true;
611 this.pnode.addClass('disabled');
612 }
613 },
614
615 /**
616 * Enable this TabPanelItem if it was previously disabled.
617 */
618 enable : function(){
619 this.disabled = false;
620 this.pnode.removeClass('disabled');
621 },
622
623 /**
624 * Set the content for this TabPanelItem.
625 * @param {String} content The content
626 * @param {Boolean} loadScripts true to look for and load scripts
627 */
628 setContent : function(content, loadScripts){
629 this.bodyEl.update(content, loadScripts);
630 },
631
632 /**
633 * Get the {@link YAHOO.ext.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
634 * @return {YAHOO.ext.UpdateManager} The UpdateManager
635 */
636 getUpdateManager : function(){
637 return this.bodyEl.getUpdateManager();
638 },
639
640 /**
641 * Set a URL to be used to load the content for this TabPanelItem.
642 * @param {String/Function} url The url to load the content from or a function to call to get the url
643 * @param {<i>String/Object</i>} params (optional) The string params for the update call or an object of the params. See {@link YAHOO.ext.UpdateManager#update} for more details. (Defaults to null)
644 * @param {<i>Boolean</i>} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
645 * @return {YAHOO.ext.UpdateManager} The UpdateManager
646 */
647 setUrl : function(url, params, loadOnce){
648 if(this.refreshDelegate){
649 this.onActivate.unsubscribe(this.refreshDelegate);
650 }
651 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
652 this.onActivate.subscribe(this.refreshDelegate);
653 return this.bodyEl.getUpdateManager();
654 },
655
656 /** @private */
657 _handleRefresh : function(url, params, loadOnce){
658 if(!loadOnce || !this.loaded){
659 var updater = this.bodyEl.getUpdateManager();
660 updater.update(url, params, this._setLoaded.createDelegate(this));
661 }
662 },
663
664 /**
665 * Force a content refresh from the URL specified in the setUrl() method.
666 * Will fail silently if the setUrl method has not been called.
667 * This does not activate the panel, just updates its content.
668 */
669 refresh : function(){
670 if(this.refreshDelegate){
671 this.loaded = false;
672 this.refreshDelegate();
673 }
674 },
675
676 /** @private */
677 _setLoaded : function(){
678 this.loaded = true;
679 },
680
681 /** @private */
682 closeClick : function(e){
683 var e = {};
684 this.fireEvent('beforeclose', this, e);
685 if(e.cancel !== true){
686 this.tabPanel.removeTab(this.id);
687 }
688 },
689 /**
690 * The text displayed in the tooltip for the close icon.
691 * @type String
692 */
693 closeText : 'Close this tab'
694};
695
696/** @private */
697YAHOO.ext.TabPanel.prototype.createStrip = function(container){
698 var strip = document.createElement('div');
699 strip.className = 'ytab-wrap';
700 container.appendChild(strip);
701 return strip;
702};
703/** @private */
704YAHOO.ext.TabPanel.prototype.createStripList = function(strip){
705 // div wrapper for retard IE
706 strip.innerHTML = '<div class="ytab-strip-wrap"><table class="ytab-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr></tr></tbody></table></div>';
707 return strip.firstChild.firstChild.firstChild.firstChild;
708};
709/** @private */
710YAHOO.ext.TabPanel.prototype.createBody = function(container){
711 var body = document.createElement('div');
712 YAHOO.util.Dom.generateId(body, 'tab-body');
713 YAHOO.util.Dom.addClass(body, 'yui-ext-tabbody');
714 container.appendChild(body);
715 return body;
716};
717/** @private */
718YAHOO.ext.TabPanel.prototype.createItemBody = function(bodyEl, id){
719 var body = YAHOO.util.Dom.get(id);
720 if(!body){
721 body = document.createElement('div');
722 body.id = id;
723 }
724 YAHOO.util.Dom.addClass(body, 'yui-ext-tabitembody');
725 bodyEl.insertBefore(body, bodyEl.firstChild);
726 return body;
727};
728/** @private */
729YAHOO.ext.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
730 var td = document.createElement('td');
731 stripEl.appendChild(td);
732 if(closable){
733 td.className = "ytab-closable";
734 if(!this.closeTpl){
735 this.closeTpl = new YAHOO.ext.Template(
736 '<a href="#" class="ytab-right"><span class="ytab-left"><em class="ytab-inner">' +
737 '<span unselectable="on" title="{text}" class="ytab-text">{text}</span>' +
738 '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
739 );
740 }
741 var el = this.closeTpl.overwrite(td, {'text': text});
742 var close = el.getElementsByTagName('div')[0];
743 var inner = el.getElementsByTagName('em')[0];
744 return {'el': el, 'close': close, 'inner': inner};
745 } else {
746 if(!this.tabTpl){
747 this.tabTpl = new YAHOO.ext.Template(
748 '<a href="#" class="ytab-right"><span class="ytab-left"><em class="ytab-inner">' +
749 '<span unselectable="on" title="{text}" class="ytab-text">{text}</span></em></span></a>'
750 );
751 }
752 var el = this.tabTpl.overwrite(td, {'text': text});
753 var inner = el.getElementsByTagName('em')[0];
754 return {'el': el, 'inner': inner};
755 }
756};
diff --git a/frontend/beta/js/YUI-extensions/widgets/TaskPanel.js b/frontend/beta/js/YUI-extensions/widgets/TaskPanel.js
new file mode 100644
index 0000000..e69de29
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/widgets/TaskPanel.js
diff --git a/frontend/beta/js/YUI-extensions/widgets/TemplateView.js b/frontend/beta/js/YUI-extensions/widgets/TemplateView.js
new file mode 100644
index 0000000..2100205
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/widgets/TemplateView.js
@@ -0,0 +1,766 @@
1/**
2 * @class YAHOO.ext.View
3 * @extends YAHOO.ext.util.Observable
4 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
5 * This class also supports single and multi selection modes. <br>
6 * Create a data model bound view:
7<pre><code>
8var dataModel = new YAHOO.ext.grid.XMLDataModel(...);
9var view = new YAHOO.ext.View('my-element',
10 '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
11 dataModel, {
12 singleSelect: true,
13 selectedClass: 'ydataview-selected'
14 });
15
16// listen for node click?
17view.on('click', function(vw, index, node, e){
18 alert('Node "' + node.id + '" at index: ' + index + ' was clicked.');
19});
20
21// load XML data
22dataModel.load('foobar.xml');
23</code></pre>
24For an example of creating a JSON/UpdateManager view, see {@link YAHOO.ext.JsonView}.
25 * <br><br>
26 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
27 * IE's limited insertion support with tables and Opera's faulty event bubbling.</b>
28 * @constructor
29 * Create a new View
30 * @param {String/HTMLElement/Element} container The container element where the view is to be rendered.
31 * @param {String/DomHelper.Template} tpl The rendering template or a string to create a template with
32 * @param {DataModel} dataModel The bound data model
33 * @param {Object} config The config object
34*/
35YAHOO.ext.View = function(container, tpl, dataModel, config){
36 this.el = getEl(container, true);
37 this.nodes = this.el.dom.childNodes;
38 if(typeof tpl == 'string'){
39 tpl = new YAHOO.ext.Template(tpl);
40 }
41 tpl.compile();
42 /**
43 * The template used by this View
44 * @type {YAHOO.ext.DomHelper.Template}
45 */
46 this.tpl = tpl;
47 this.setDataModel(dataModel);
48 var CE = YAHOO.util.CustomEvent;
49 /** @private */
50 this.events = {
51 /**
52 * @event beforeclick
53 * Fires before a click is processed. Returns false to cancel the default action.
54 * @param {YAHOO.ext.View} this
55 * @param {Number} index The index of the target node
56 * @param {HTMLElement} node The target node
57 * @param {YAHOO.ext.EventObject} e The raw event object
58 */
59 'beforeclick' : true,
60 /**
61 * @event click
62 * Fires when a template node is clicked.
63 * @param {YAHOO.ext.View} this
64 * @param {Number} index The index of the target node
65 * @param {HTMLElement} node The target node
66 * @param {YAHOO.ext.EventObject} e The raw event object
67 */
68 'click' : true,
69 /**
70 * @event dblclick
71 * Fires when a template node is double clicked.
72 * @param {YAHOO.ext.View} this
73 * @param {Number} index The index of the target node
74 * @param {HTMLElement} node The target node
75 * @param {YAHOO.ext.EventObject} e The raw event object
76 */
77 'dblclick' : true,
78 /**
79 * @event contextmenu
80 * Fires when a template node is right clicked.
81 * @param {YAHOO.ext.View} this
82 * @param {Number} index The index of the target node
83 * @param {HTMLElement} node The target node
84 * @param {YAHOO.ext.EventObject} e The raw event object
85 */
86 'contextmenu' : true,
87 /**
88 * @event selectionchange
89 * Fires when the selected nodes change.
90 * @param {YAHOO.ext.View} this
91 * @param {Array} selections Array of the selected nodes
92 */
93 'selectionchange' : true,
94
95 /**
96 * @event beforeselect
97 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
98 * @param {YAHOO.ext.View} this
99 * @param {HTMLElement} node The node to be selected
100 * @param {Array} selections Array of currently selected nodes
101 */
102 'beforeselect' : true
103 };
104 this.el.mon("click", this.onClick, this, true);
105 this.el.mon("dblclick", this.onDblClick, this, true);
106 this.el.mon("contextmenu", this.onContextMenu, this, true);
107
108 /**
109 * The css class to add to selected nodes
110 * @type {YAHOO.ext.DomHelper.Template}
111 */
112 this.selectedClass = 'ydataview-selected';
113
114 this.emptyText = '';
115
116 this.selections = [];
117
118 this.lastSelection = null;
119
120 /**
121 * The root property in the loaded json object that contains the data
122 * @type {String}
123 */
124 this.jsonRoot = null;
125 YAHOO.ext.util.Config.apply(this, config);
126 if(this.renderUpdates || this.jsonRoot){
127 var um = this.el.getUpdateManager();
128 um.setRenderer(this);
129 }
130};
131
132YAHOO.extendX(YAHOO.ext.View, YAHOO.ext.util.Observable, {
133 /**
134 * Returns the element this view is bound to.
135 * @return {YAHOO.ext.Element}
136 */
137 getEl : function(){
138 return this.el;
139 },
140
141 render : function(el, response){
142 this.clearSelections();
143 this.el.update('');
144 var o;
145 try{
146 o = YAHOO.ext.util.JSON.decode(response.responseText);
147 if(this.jsonRoot){
148 o = eval('o.' + this.jsonRoot);
149 }
150 }catch(e){}
151 /**
152 * The current json data or null
153 */
154 this.jsonData = o;
155 this.beforeRender();
156 this.refresh();
157 },
158
159 beforeRender : function(){
160
161 },
162
163 /**
164 * Refreshes the view.
165 */
166 refresh : function(){
167 this.clearSelections();
168 this.el.update('');
169 this.html = [];
170 if(this.renderUpdates || this.jsonRoot){
171 var o = this.jsonData;
172 if(o){
173 for(var i = 0, len = o.length; i < len; i++) {
174 this.renderEach(o[i]);
175 }
176 }
177 }else{
178 this.dataModel.each(this.renderEach, this);
179 }
180 var strHtml;
181 if(this.html.length > 0){
182 strHtml = this.html.join('');
183 }else{
184 strHtml = this.emptyText;
185 }
186 this.el.update(strHtml);
187 this.html = null;
188 this.nodes = this.el.dom.childNodes;
189 this.updateIndexes(0);
190 },
191
192 /**
193 * Function to override to reformat the data that is sent to
194 * the template for each node.
195 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
196 * a JSON object for an UpdateManager bound view).
197 * @param {Number} index The index of the data within the data model
198 */
199 prepareData : function(data, index){
200 return data;
201 },
202
203 renderEach : function(data){
204 this.html[this.html.length] = this.tpl.applyTemplate(this.prepareData(data));
205 },
206
207 /**
208 * Refresh an individual node.
209 * @param {Number} index
210 */
211 refreshNode : function(index){
212 this.refreshNodes(index, index);
213 },
214
215 refreshNodes : function(dm, startIndex, endIndex){
216 this.clearSelections();
217 var dm = this.dataModel;
218 var ns = this.nodes;
219 for(var i = startIndex; i <= endIndex; i++){
220 var d = this.prepareData(dm.getRow(i), i);
221 if(i < ns.length-1){
222 var old = ns[i];
223 this.tpl.insertBefore(old, d);
224 this.el.dom.removeChild(old);
225 }else{
226 this.tpl.append(this.el.dom, d);
227 }
228 }
229 this.updateIndexes(startIndex, endIndex);
230 },
231
232 deleteNodes : function(dm, startIndex, endIndex){
233 this.clearSelections();
234 if(startIndex == 0 && endIndex >= this.nodes.length-1){
235 this.el.update('');
236 }else{
237 var el = this.el.dom;
238 for(var i = startIndex; i <= endIndex; i++){
239 el.removeChild(this.nodes[startIndex]);
240 }
241 this.updateIndexes(startIndex);
242 }
243 },
244
245 insertNodes : function(dm, startIndex, endIndex){
246 if(this.nodes.length == 0){
247 this.refresh();
248 }else{
249 this.clearSelections();
250 var t = this.tpl;
251 var before = this.nodes[startIndex];
252 var dm = this.dataModel;
253 if(before){
254 for(var i = startIndex; i <= endIndex; i++){
255 t.insertBefore(before, this.prepareData(dm.getRow(i), i));
256 }
257 }else{
258 var el = this.el.dom;
259 for(var i = startIndex; i <= endIndex; i++){
260 t.append(el, this.prepareData(dm.getRow(i), i));
261 }
262 }
263 this.updateIndexes(startIndex);
264 }
265 },
266
267 updateIndexes : function(dm, startIndex, endIndex){
268 var ns = this.nodes;
269 startIndex = startIndex || 0;
270 endIndex = endIndex || ns.length-1;
271 for(var i = startIndex; i <= endIndex; i++){
272 ns[i].nodeIndex = i;
273 }
274 },
275
276 /**
277 * Changes the data model this view uses and refresh the view.
278 * @param {DataModel} dataModel
279 */
280 setDataModel : function(dm){
281 if(!dm) return;
282 this.unplugDataModel(this.dataModel);
283 this.dataModel = dm;
284 dm.on('cellupdated', this.refreshNode, this, true);
285 dm.on('datachanged', this.refresh, this, true);
286 dm.on('rowsdeleted', this.deleteNodes, this, true);
287 dm.on('rowsinserted', this.insertNodes, this, true);
288 dm.on('rowsupdated', this.refreshNodes, this, true);
289 dm.on('rowssorted', this.refresh, this, true);
290 this.refresh();
291 },
292
293 /**
294 * Unplug the data model and stop updates.
295 * @param {DataModel} dataModel
296 */
297 unplugDataModel : function(dm){
298 if(!dm) return;
299 dm.removeListener('cellupdated', this.refreshNode, this);
300 dm.removeListener('datachanged', this.refresh, this);
301 dm.removeListener('rowsdeleted', this.deleteNodes, this);
302 dm.removeListener('rowsinserted', this.insertNodes, this);
303 dm.removeListener('rowsupdated', this.refreshNodes, this);
304 dm.removeListener('rowssorted', this.refresh, this);
305 this.dataModel = null;
306 },
307
308 /**
309 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
310 * @param {HTMLElement} node
311 * @return {HTMLElement} The template node
312 */
313 findItemFromChild : function(node){
314 var el = this.el.dom;
315 if(!node || node.parentNode == el){
316 return node;
317 }
318 var p = node.parentNode;
319 while(p && p != el){
320 if(p.parentNode == el){
321 return p;
322 }
323 p = p.parentNode;
324 }
325 return null;
326 },
327
328 /** @ignore */
329 onClick : function(e){
330 var item = this.findItemFromChild(e.getTarget());
331 if(item){
332 var index = this.indexOf(item);
333 if(this.onItemClick(item, index, e) !== false){
334 this.fireEvent('click', this, index, item, e);
335 }
336 }else{
337 this.clearSelections();
338 }
339 },
340
341 /** @ignore */
342 onContextMenu : function(e){
343 var item = this.findItemFromChild(e.getTarget());
344 if(item){
345 this.fireEvent('contextmenu', this, this.indexOf(item), item, e);
346 }
347 },
348
349 /** @ignore */
350 onDblClick : function(e){
351 var item = this.findItemFromChild(e.getTarget());
352 if(item){
353 this.fireEvent('dblclick', this, this.indexOf(item), item, e);
354 }
355 },
356
357 onItemClick : function(item, index, e){
358 if(this.fireEvent('beforeclick', this, index, item, e) !== false){
359 if(this.multiSelect || this.singleSelect){
360 if(this.multiSelect && e.shiftKey && this.lastSelection){
361 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
362 }else{
363 this.select(item, this.multiSelect && e.ctrlKey);
364 this.lastSelection = item;
365 }
366 e.preventDefault();
367 }
368 return true;
369 }else{
370 return false;
371 }
372 },
373
374 /**
375 * Get the number of selected nodes.
376 * @return {Number}
377 */
378 getSelectionCount : function(){
379 return this.selections.length;
380 },
381
382 /**
383 * Get the currently selected nodes.
384 * @return {Array} An array of HTMLElements
385 */
386 getSelectedNodes : function(){
387 return this.selections;
388 },
389
390 /**
391 * Get the indexes of the selected nodes.
392 * @return {Array}
393 */
394 getSelectedIndexes : function(){
395 var indexes = [];
396 for(var i = 0, len = this.selections.length; i < len; i++) {
397 indexes.push(this.selections[i].nodeIndex);
398 }
399 return indexes;
400 },
401
402 /**
403 * Clear all selections
404 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
405 */
406 clearSelections : function(suppressEvent){
407 if(this.multiSelect || this.singleSelect){
408 YAHOO.util.Dom.removeClass(this.selections, this.selectedClass);
409 this.selections = [];
410 if(!suppressEvent){
411 this.fireEvent('selectionchange', this, this.selections);
412 }
413 }
414 },
415
416 /**
417 * Returns true if the passed node is selected
418 * @param {HTMLElement/Number} node The node or node index
419 * @return {Boolean}
420 */
421 isSelected : function(node){
422 node = this.getNode(node);
423 var s = this.selections;
424 if(s.length < 1){
425 return false;
426 }
427 if(s.indexOf){
428 return s.indexOf(node) !== -1;
429 }else{
430 for(var i = 0, len = s.length; i < len; i++){
431 if (s[i] == node){
432 return true;
433 }
434 }
435 return false;
436 }
437 },
438
439 /**
440 * Selects nodes.
441 * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
442 * @param {Boolean} keepExisting (optional) true to keep existing selections
443 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
444 */
445 select : function(nodeInfo, keepExisting, suppressEvent){
446 if(!keepExisting){
447 this.clearSelections(true);
448 }
449 if(nodeInfo instanceof Array){
450 for(var i = 0, len = nodeInfo.length; i < len; i++) {
451 this.select(nodeInfo[i], true, true);
452 }
453 }else{
454 var node = this.getNode(nodeInfo);
455 if(node && !this.isSelected(node)){
456 if(this.fireEvent('beforeselect', this, node, this.selections) !== false){
457 YAHOO.util.Dom.addClass(node, this.selectedClass);
458 this.selections.push(node);
459 }
460 }
461 }
462 if(!suppressEvent){
463 this.fireEvent('selectionchange', this, this.selections);
464 }
465 },
466
467 /**
468 * Gets a template node.
469 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
470 * @return {HTMLElement} The node or null if it wasn't found
471 */
472 getNode : function(nodeInfo){
473 if(typeof nodeInfo == 'object'){
474 return nodeInfo;
475 }else if(typeof nodeInfo == 'string'){
476 return document.getElementById(nodeInfo);
477 }else if(typeof nodeInfo == 'number'){
478 return this.nodes[nodeInfo];
479 }
480 return null;
481 },
482
483 /**
484 * Gets a range template nodes.
485 * @param {Number} startIndex
486 * @param {Number} endIndex
487 * @return {Array} An array of nodes
488 */
489 getNodes : function(start, end){
490 var ns = this.nodes;
491 start = start || 0;
492 end = typeof end == 'undefined' ? ns.length-1 : end;
493 var nodes = [];
494 if(start <= end){
495 for(var i = start; i <= end; i++) {
496 nodes.push(ns[i]);
497 }
498 }else{
499 for(var i = start; i >= end; i--) {
500 nodes.push(ns[i]);
501 }
502 }
503 return nodes;
504 },
505
506 /**
507 * Finds the index of the passed node
508 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
509 * @return {Number} The index of the node or -1
510 */
511 indexOf : function(node){
512 node = this.getNode(node);
513 if(typeof node.nodeIndex == 'number'){
514 return node.nodeIndex;
515 }
516 var ns = this.nodes;
517 for(var i = 0, len = ns.length; i < len; i++) {
518 if(ns[i] == node){
519 return i;
520 }
521 }
522 return -1;
523 }
524});
525
526/**
527 * @class YAHOO.ext.JsonView
528 * @extends YAHOO.ext.View
529 * Shortcut class to create a JSON + UpdateManager template view. Usage:
530<pre><code>
531var view = new YAHOO.ext.JsonView('my-element',
532 '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
533 { multiSelect: true, jsonRoot: 'data' });
534
535// listen for node click?
536view.on('click', function(vw, index, node, e){
537 alert('Node "' + node.id + '" at index: ' + index + ' was clicked.');
538});
539
540// direct load of JSON data
541view.load('foobar.php');
542
543
544// Example from my blog list
545var tpl = new YAHOO.ext.Template(
546 '&lt;div class="entry"&gt;' +
547 '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
548 '&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}' +
549 '&lt;/div&gt;&lt;hr /&gt;'
550);
551
552var moreView = new YAHOO.ext.JsonView('entry-list', tpl, {
553 jsonRoot: 'posts'
554});
555moreView.on('beforerender', this.sortEntries, this, true);
556moreView.load({
557 url:'/blog/get-posts.php',
558 params: 'allposts=true',
559 text:'Loading Blog Entries...'
560});
561</code></pre>
562 * @constructor
563 * Create a new JsonView
564 * @param {String/HTMLElement/Element} container The container element where the view is to be rendered.
565 * @param {DomHelper.Template} tpl The rendering template
566 * @param {Object} config The config object
567 */
568YAHOO.ext.JsonView = function(container, tpl, config){
569 var cfg = config || {};
570 cfg.renderUpdates = true;
571 YAHOO.ext.JsonView.superclass.constructor.call(this, container, tpl, null, cfg);
572 /**
573 * @event beforerender
574 * Fires before rendering of the downloaded json data.
575 * @param {YAHOO.ext.View} this
576 * @param {Object} data The json data loaded
577 */
578 this.events['beforerender'] = true;
579 /**
580 * @event load
581 * Fires when data is loaded.
582 * @param {YAHOO.ext.View} this
583 * @param {Object} data The json data loaded
584 * @param {Object} response The raw Connect response object
585 */
586 this.events['load'] = true;
587 /**
588 * @event loadexception
589 * Fires when loading fails.
590 * @param {YAHOO.ext.View} this
591 * @param {Object} response The raw Connect response object
592 */
593 this.events['loadexception'] = true;
594 this.el.getUpdateManager().on('update', this.onLoad, this, true);
595 this.el.getUpdateManager().on('failure', this.onLoadException, this, true);
596};
597YAHOO.extendX(YAHOO.ext.JsonView, YAHOO.ext.View, {
598 /**
599 * Performs an async request, loading the JSON from the response. If params are specified it uses POST, otherwise it uses GET.
600 * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
601<pre><code>
602view.load({
603 url: 'your-url.php',<br/>
604 params: {param1: 'foo', param2: 'bar'}, // or a URL encoded string<br/>
605 callback: yourFunction,<br/>
606 scope: yourObject, //(optional scope) <br/>
607 discardUrl: false, <br/>
608 nocache: false,<br/>
609 text: 'Loading...',<br/>
610 timeout: 30,<br/>
611 scripts: false<br/>
612});
613</code></pre>
614 * The only required property is url. The optional properties nocache, text and scripts
615 * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
616 * @param {<i>String/Object</i>} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
617 * @param {<i>Function</i>} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
618 * @param {<i>Boolean</i>} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
619 */
620 load : function(){
621 var um = this.el.getUpdateManager();
622 um.update.apply(um, arguments);
623 },
624
625 /**
626 * Get the number of records in the current JSON dataset
627 * @return {Number}
628 */
629 getCount : function(){
630 return this.jsonData ? this.jsonData.length : 0;
631 },
632
633 /**
634 * Returns the JSON object for the specified node(s)
635 * @param {HTMLElement/Array} node The node or an array of nodes
636 * @return {Object/Array} If you pass in an array, you get an array back, otherwise
637 * you get the JSON object for the node
638 */
639 getNodeData : function(node){
640 if(node instanceof Array){
641 var data = [];
642 for(var i = 0, len = node.length; i < len; i++){
643 data.push(this.getNodeData(node[i]));
644 }
645 return data;
646 }
647 return this.jsonData[this.indexOf(node)] || null;
648 },
649
650 beforeRender : function(){
651 this.snapshot = this.jsonData;
652 if(this.sortInfo){
653 this.sort.apply(this, this.sortInfo);
654 }
655 this.fireEvent('beforerender', this, this.jsonData);
656 },
657
658 onLoad : function(el, o){
659 this.fireEvent('load', this, this.jsonData, o);
660 },
661
662 onLoadException : function(el, o){
663 this.fireEvent('loadexception', this, o);
664 },
665
666 /**
667 * Filter the data by a specific property.
668 * @param {String} property A property on your JSON objects
669 * @param {String/RegExp} value Either string that the property values
670 * should start with or a RegExp to test against the property
671 */
672 filter : function(property, value){
673 if(this.jsonData){
674 var data = [];
675 var ss = this.snapshot;
676 if(typeof value == 'string'){
677 var vlen = value.length;
678 if(vlen == 0){
679 this.clearFilter();
680 return;
681 }
682 value = value.toLowerCase();
683 for(var i = 0, len = ss.length; i < len; i++){
684 var o = ss[i];
685 if(o[property].substr(0, vlen).toLowerCase() == value){
686 data.push(o);
687 }
688 }
689 }else if(value.exec){ // regex?
690 for(var i = 0, len = ss.length; i < len; i++){
691 var o = ss[i];
692 if(value.test(o[property])){
693 data.push(o);
694 }
695 }
696 }else{
697 return;
698 }
699 this.jsonData = data;
700 this.refresh();
701 }
702 },
703
704 /**
705 * Filter by a function. The passed function will be called with each
706 * object in the current dataset. If the function returns true, the value is kept
707 * otherwise it is filtered.
708 * @param {Function} fn
709 * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
710 */
711 filterBy : function(fn, scope){
712 if(this.jsonData){
713 var data = [];
714 var ss = this.snapshot;
715 for(var i = 0, len = ss.length; i < len; i++){
716 var o = ss[i];
717 if(fn.call(scope|| this, o)){
718 data.push(o);
719 }
720 }
721 this.jsonData = data;
722 this.refresh();
723 }
724 },
725
726 /**
727 * Clears the current filter.
728 */
729 clearFilter : function(){
730 if(this.snapshot && this.jsonData != this.snapshot){
731 this.jsonData = this.snapshot;
732 this.refresh();
733 }
734 },
735
736
737 /**
738 * Sorts the data for this view and refreshes it.
739 * @param {String} property A property on your JSON objects to sort on
740 * @param {String} direction (optional) desc or asc (defaults to asc)
741 * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
742 */
743 sort : function(property, dir, sortType){
744 this.sortInfo = Array.prototype.slice.call(arguments, 0);
745 if(this.jsonData){
746 var p = property;
747 var dsc = dir && dir.toLowerCase() == 'desc';
748 var f = function(o1, o2){
749 var v1 = sortType ? sortType(o1[p]) : o1[p];
750 var v2 = sortType ? sortType(o2[p]) : o2[p];;
751 if(v1 < v2){
752 return dsc ? +1 : -1;
753 }else if(v1 > v2){
754 return dsc ? -1 : +1;
755 }else{
756 return 0;
757 }
758 };
759 this.jsonData.sort(f);
760 this.refresh();
761 if(this.jsonData != this.snapshot){
762 this.snapshot.sort(f);
763 }
764 }
765 }
766});
diff --git a/frontend/beta/js/YUI-extensions/widgets/Toolbar.js b/frontend/beta/js/YUI-extensions/widgets/Toolbar.js
new file mode 100644
index 0000000..7c14753
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/widgets/Toolbar.js
@@ -0,0 +1,296 @@
1/**
2 * @class YAHOO.ext.Toolbar
3 * Basic Toolbar used by the Grid to create the paging toolbar. This class is reusable but functionality
4 * is limited. Look for more functionality in a future version.
5 * @constructor
6 * @param {String/HTMLElement/Element} container
7 * @param {Array} buttons (optional) array of button configs or elements to add
8 */
9 YAHOO.ext.Toolbar = function(container, buttons){
10 this.el = getEl(container, true);
11 var div = document.createElement('div');
12 div.className = 'ytoolbar';
13 var tb = document.createElement('table');
14 tb.border = 0;
15 tb.cellPadding = 0;
16 tb.cellSpacing = 0;
17 div.appendChild(tb);
18 var tbody = document.createElement('tbody');
19 tb.appendChild(tbody);
20 var tr = document.createElement('tr');
21 tbody.appendChild(tr);
22 this.el.dom.appendChild(div);
23 this.tr = tr;
24 if(buttons){
25 this.add.apply(this, buttons);
26 }
27};
28
29YAHOO.ext.Toolbar.prototype = {
30 /**
31 * Adds element(s) to the toolbar - this function takes a variable number of
32 * arguments of mixed type and adds them to the toolbar...
33 *
34 * @param {Mixed} arg If arg is a ToolbarButton, it is added. If arg is a string, it is wrapped
35 * in a ytb-text element and added unless the text is "separator" in which case a separator
36 * is added. Otherwise, it is assumed the element is an HTMLElement and it is added directly.
37 */
38 add : function(){
39 for(var i = 0; i < arguments.length; i++){
40 var el = arguments[i];
41 var td = document.createElement('td');
42 this.tr.appendChild(td);
43 if(el instanceof YAHOO.ext.ToolbarButton){
44 el.init(td);
45 }else if(el instanceof Array){
46 this.addButton(el);
47 }else if(typeof el == 'string'){
48 var span = document.createElement('span');
49 if(el == 'separator'){
50 span.className = 'ytb-sep';
51 }else{
52 span.innerHTML = el;
53 span.className = 'ytb-text';
54 }
55 td.appendChild(span);
56 }else if(typeof el == 'object' && el.nodeType){ // must be element?
57 td.appendChild(el);
58 }else if(typeof el == 'object'){ // must be button config?
59 this.addButton(el);
60 }
61 }
62 },
63
64 /**
65 * Returns the element for this toolbar
66 * @return {YAHOO.ext.Element}
67 */
68 getEl : function(){
69 return this.el;
70 },
71
72 /**
73 * Adds a separator
74 */
75 addSeparator : function(){
76 var td = document.createElement('td');
77 this.tr.appendChild(td);
78 var span = document.createElement('span');
79 span.className = 'ytb-sep';
80 td.appendChild(span);
81 },
82
83 /**
84 * Add a button (or buttons), see {@link YAHOO.ext.ToolbarButton} for more info on the config
85 * @param {Object/Array} config A button config or array of configs
86 * @return {YAHOO.ext.ToolbarButton/Array}
87 */
88 addButton : function(config){
89 if(config instanceof Array){
90 var buttons = [];
91 for(var i = 0, len = config.length; i < len; i++) {
92 buttons.push(this.addButton(config[i]));
93 }
94 return buttons;
95 }
96 var b = config;
97 if(!(config instanceof YAHOO.ext.ToolbarButton)){
98 b = new YAHOO.ext.ToolbarButton(config);
99 }
100 this.add(b);
101 return b;
102 },
103
104 /**
105 * Adds text to the toolbar
106 * @param {String} text The text to add
107 * @return {HTMLElement} The span element created which you can use to update the text.
108 */
109 addText : function(text){
110 var td = document.createElement('td');
111 this.tr.appendChild(td);
112 var span = document.createElement('span');
113 span.className = 'ytb-text';
114 span.innerHTML = text;
115 td.appendChild(span);
116 return span;
117 },
118
119 /**
120 * Inserts a button (or buttons) at the specified index
121 * @param {Number} index The index where the buttons are to be inserted
122 * @param {Object/Array} config A button config or array of configs
123 * @return {YAHOO.ext.ToolbarButton/Array}
124 */
125 insertButton : function(index, config){
126 if(config instanceof Array){
127 var buttons = [];
128 for(var i = 0, len = config.length; i < len; i++) {
129 buttons.push(this.insertButton(index + i, config[i]));
130 }
131 return buttons;
132 }
133 var b = new YAHOO.ext.ToolbarButton(config);
134 var td = document.createElement('td');
135 var nextSibling = this.tr.childNodes[index];
136 if (nextSibling)
137 this.tr.insertBefore(td, nextSibling);
138 else
139 this.tr.appendChild(td);
140 b.init(td);
141 return b;
142 }
143};
144
145/**
146 * @class YAHOO.ext.ToolbarButton
147 * A toolbar button. The config has the following options:
148 * <ul>
149 * <li>className - The CSS class for the button. Use this to attach a background image for an icon.</li>
150 * <li>text - The button's text</li>
151 * <li>tooltip - The buttons tooltip text</li>
152 * <li>click - function to call when the button is clicked</li>
153 * <li>mouseover - function to call when the mouse moves over the button</li>
154 * <li>mouseout - function to call when the mouse moves off the button</li>
155 * <li>scope - The scope of the above event handlers</li>
156 * <li></li>
157 * <li></li>
158 * @constructor
159 * @param {Object} config
160 */
161YAHOO.ext.ToolbarButton = function(config){
162 YAHOO.ext.util.Config.apply(this, config);
163};
164
165YAHOO.ext.ToolbarButton.prototype = {
166 /** @private */
167 init : function(appendTo){
168 var element = document.createElement('span');
169 element.className = 'ytb-button';
170 if(this.id){
171 element.id = this.id;
172 }
173 this.setDisabled(this.disabled === true);
174 var inner = document.createElement('span');
175 inner.className = 'ytb-button-inner ' + (this.className || this.cls);
176 inner.unselectable = 'on';
177 if(this.tooltip){
178 element.setAttribute('title', this.tooltip);
179 }
180 if(this.style){
181 YAHOO.ext.DomHelper.applyStyles(inner, this.style);
182 }
183 element.appendChild(inner);
184 appendTo.appendChild(element);
185 this.el = getEl(element, true);
186 this.el.unselectable();
187 inner.innerHTML = (this.text ? this.text : '&#160;');
188 this.inner = inner;
189 this.el.mon('click', this.onClick, this, true);
190 this.el.mon('mouseover', this.onMouseOver, this, true);
191 this.el.mon('mouseout', this.onMouseOut, this, true);
192 },
193
194 /**
195 * Sets this buttons click handler
196 * @param {Function} click The function to call when the button is clicked
197 * @param {Object} scope (optional) Scope for the function passed above
198 */
199 setHandler : function(click, scope){
200 this.click = click;
201 this.scope = scope;
202 },
203
204 /**
205 * Set this buttons text
206 * @param {String} text
207 */
208 setText : function(text){
209 this.inner.innerHTML = text;
210 },
211
212 /**
213 * Set this buttons tooltip text
214 * @param {String} text
215 */
216 setTooltip : function(text){
217 this.el.dom.title = text;
218 },
219
220 /**
221 * Show this button
222 */
223 show: function(){
224 this.el.dom.parentNode.style.display = '';
225 },
226
227 /**
228 * Hide this button
229 */
230 hide: function(){
231 this.el.dom.parentNode.style.display = 'none';
232 },
233
234 /**
235 * Disable this button
236 */
237 disable : function(){
238 this.disabled = true;
239 if(this.el){
240 this.el.addClass('ytb-button-disabled');
241 }
242 },
243
244 /**
245 * Enable this button
246 */
247 enable : function(){
248 this.disabled = false;
249 if(this.el){
250 this.el.removeClass('ytb-button-disabled');
251 }
252 },
253
254 /**
255 * Returns true if this button is disabled.
256 * @return {Boolean}
257 */
258 isDisabled : function(){
259 return this.disabled === true;
260 },
261
262 setDisabled : function(disabled){
263 if(disabled){
264 this.disable();
265 }else{
266 this.enable();
267 }
268 },
269
270 /** @private */
271 onClick : function(){
272 if(!this.disabled && this.click){
273 this.click.call(this.scope || window, this);
274 }
275 },
276
277 /** @private */
278 onMouseOver : function(){
279 if(!this.disabled){
280 this.el.addClass('ytb-button-over');
281 if(this.mouseover){
282 this.mouseover.call(this.scope || window, this);
283 }
284 }
285 },
286
287 /** @private */
288 onMouseOut : function(){
289 this.el.removeClass('ytb-button-over');
290 if(!this.disabled){
291 if(this.mouseout){
292 this.mouseout.call(this.scope || window, this);
293 }
294 }
295 }
296};
diff --git a/frontend/beta/js/YUI-extensions/yutil.js b/frontend/beta/js/YUI-extensions/yutil.js
new file mode 100644
index 0000000..a815397
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/yutil.js
@@ -0,0 +1,637 @@
1YAHOO.namespace('ext', 'ext.util', 'ext.grid', 'ext.dd', 'ext.tree', 'ext.data', 'ext.form');
2if(typeof Ext == 'undefined'){
3 Ext = YAHOO.ext;
4}
5YAHOO.ext.Strict = (document.compatMode == 'CSS1Compat');
6YAHOO.ext.SSL_SECURE_URL = 'javascript:false';
7YAHOO.ext.BLANK_IMAGE_URL = 'http:/'+'/www.yui-ext.com/blog/images/s.gif';
8
9// for old browsers
10window.undefined = undefined;
11/**
12 * @class Function
13 * These functions are available on every Function object (any javascript function).
14 */
15 //
16 /**
17 * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
18 * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
19 * Will create a function that is bound to those 2 args.
20 * @return {Function} The new function
21*/
22Function.prototype.createCallback = function(/*args...*/){
23 // make args available, in function below
24 var args = arguments;
25 var method = this;
26 return function() {
27 return method.apply(window, args);
28 };
29};
30
31/**
32 * Creates a delegate (callback) that sets the scope to obj.
33 * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
34 * Will create a function that is automatically scoped to this.
35 * @param {Object} obj (optional) The object for which the scope is set
36 * @param {<i>Array</i>} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
37 * @param {<i>Boolean/Number</i>} appendArgs (optional) if True args are appended to call args instead of overriding,
38 * if a number the args are inserted at the specified position
39 * @return {Function} The new function
40 */
41Function.prototype.createDelegate = function(obj, args, appendArgs){
42 var method = this;
43 return function() {
44 var callArgs = args || arguments;
45 if(appendArgs === true){
46 callArgs = Array.prototype.slice.call(arguments, 0);
47 callArgs = callArgs.concat(args);
48 }else if(typeof appendArgs == 'number'){
49 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
50 var applyArgs = [appendArgs, 0].concat(args); // create method call params
51 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
52 }
53 return method.apply(obj || window, callArgs);
54 };
55};
56
57/**
58 * Calls this function after the number of millseconds specified.
59 * @param {Number} millis The number of milliseconds for the setTimeout call
60 * @param {Object} obj (optional) The object for which the scope is set
61 * @param {<i>Array</i>} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
62 * @param {<i>Boolean/Number</i>} appendArgs (optional) if True args are appended to call args instead of overriding,
63 * if a number the args are inserted at the specified position
64 * @return {Number} The timeout id that can be used with clearTimeout
65 */
66Function.prototype.defer = function(millis, obj, args, appendArgs){
67 return setTimeout(this.createDelegate(obj, args, appendArgs), millis);
68};
69/**
70 * Create a combined function call sequence of the original function + the passed function.
71 * The resulting function returns the results of the original function.
72 * The passed fcn is called with the parameters of the original function
73 * @param {Function} fcn The function to sequence
74 * @param {<i>Object</i>} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
75 * @return {Function} The new function
76 */
77Function.prototype.createSequence = function(fcn, scope){
78 if(typeof fcn != 'function'){
79 return this;
80 }
81 var method = this;
82 return function() {
83 var retval = method.apply(this || window, arguments);
84 fcn.apply(scope || this || window, arguments);
85 return retval;
86 };
87};
88
89/*
90 * IE will leak if this isn't here
91 */
92YAHOO.util.Event.on(window, 'unload', function(){
93 var p = Function.prototype;
94 delete p.createSequence;
95 delete p.defer;
96 delete p.createDelegate;
97 delete p.createCallback;
98 delete p.createInterceptor;
99});
100
101/**
102 * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
103 * The resulting function returns the results of the original function.
104 * The passed fcn is called with the parameters of the original function.
105 * @addon
106 * @param {Function} fcn The function to call before the original
107 * @param {<i>Object</i>} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
108 * @return {Function} The new function
109 */
110Function.prototype.createInterceptor = function(fcn, scope){
111 if(typeof fcn != 'function'){
112 return this;
113 }
114 var method = this;
115 return function() {
116 fcn.target = this;
117 fcn.method = method;
118 if(fcn.apply(scope || this || window, arguments) === false){
119 return;
120 }
121 return method.apply(this || window, arguments);;
122 };
123};
124
125/**
126 * @class YAHOO.ext.util.Browser
127 * @singleton
128 */
129YAHOO.ext.util.Browser = new function(){
130 var ua = navigator.userAgent.toLowerCase();
131 /** @type Boolean */
132 this.isOpera = (ua.indexOf('opera') > -1);
133 /** @type Boolean */
134 this.isSafari = (ua.indexOf('webkit') > -1);
135 /** @type Boolean */
136 this.isIE = (window.ActiveXObject);
137 /** @type Boolean */
138 this.isIE7 = (ua.indexOf('msie 7') > -1);
139 /** @type Boolean */
140 this.isGecko = !this.isSafari && (ua.indexOf('gecko') > -1);
141
142 if(ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1){
143 /** @type Boolean */
144 this.isWindows = true;
145 }else if(ua.indexOf("macintosh") != -1){
146 /** @type Boolean */
147 this.isMac = true;
148 }
149 if(this.isIE && !this.isIE7){
150 try{
151 document.execCommand("BackgroundImageCache", false, true);
152 }catch(e){}
153 }
154}();
155
156 /**
157 * Enable custom handler signature and event cancelling. Using fireDirect() instead of fire() calls the subscribed event handlers
158 * with the exact parameters passed to fireDirect, instead of the usual (eventType, args[], obj). IMO this is more intuitive
159 * and promotes cleaner code. Also, if an event handler returns false, it is returned by fireDirect and no other handlers will be called.<br>
160 * Example:<br><br><pre><code>
161 * if(beforeUpdateEvent.fireDirect(myArg, myArg2) !== false){
162 * // do update
163 * }</code></pre>
164 */
165YAHOO.util.CustomEvent.prototype.fireDirect = function(){
166 var len=this.subscribers.length;
167 for (var i=0; i<len; ++i) {
168 var s = this.subscribers[i];
169 if(s){
170 var scope = (s.override) ? s.obj : this.scope;
171 if(s.fn.apply(scope, arguments) === false){
172 return false;
173 }
174 }
175 }
176 return true;
177};
178
179/**
180 * @class YAHOO-
181 * Additional functionality for the global YAHOO object.
182 * @singleton
183 */
184/**
185 * Prints all arguments to a resizable, movable, scrolling region without
186 * the need to include separate js or css. Double click it to hide it.
187 * @param {Mixed} arg1
188 * @param {Mixed} arg2
189 * @param {Mixed} etc
190 * @static
191 */
192YAHOO.print = function(arg1, arg2, etc){
193 if(!YAHOO.ext._console){
194 var cs = YAHOO.ext.DomHelper.insertBefore(document.body.firstChild,
195 {tag: 'div',style:'width:250px;height:350px;overflow:auto;border:3px solid #c3daf9;' +
196 'background:white;position:absolute;right:5px;top:5px;' +
197 'font:normal 8pt arial,verdana,helvetica;z-index:50000;padding:5px;'}, true);
198 if(YAHOO.ext.Resizable){
199 new YAHOO.ext.Resizable(cs, {
200 transparent:true,
201 handles: 'all',
202 pinned:true,
203 adjustments: [0,0],
204 wrap:true,
205 draggable:(YAHOO.util.DD ? true : false)
206 });
207 }
208 cs.on('dblclick', cs.hide);
209 YAHOO.ext._console = cs;
210 }
211 var m = '';
212 for(var i = 0, len = arguments.length; i < len; i++) {
213 m += (i == 0 ? '' : ', ') + arguments[i];
214 }
215 m += '<hr noshade style="color:#eeeeee;" size="1">'
216 var d = YAHOO.ext._console.dom;
217 d.innerHTML = m + d.innerHTML;
218 d.scrollTop = 0;
219 YAHOO.ext._console.show();
220};
221
222/**
223 * Applies the passed C#/DomHelper style format (e.g. "The variable {0} is equal to {1}") before calling {@link YAHOO#print}
224 * @param {String} format
225 * @param {Mixed} arg1
226 * @param {Mixed} arg2
227 * @param {Mixed} etc
228 * @static
229 */
230YAHOO.printf = function(format, arg1, arg2, etc){
231 var args = Array.prototype.slice.call(arguments, 1);
232 YAHOO.print(format.replace(
233 /\{\{[^{}]*\}\}|\{(\d+)(,\s*([\w.]+))?\}/g,
234 function(m, a1, a2, a3) {
235 if (m.chatAt == '{') {
236 return m.slice(1, -1);
237 }
238 var rpl = args[a1];
239 if (a3) {
240 var f = eval(a3);
241 rpl = f(rpl);
242 }
243 return rpl ? rpl : '';
244 }));
245}
246
247YAHOO._timers = {};
248/**
249 * If a timer with specified name doesn't exist it is started. If one exists and reset is not true
250 * it prints the ellapsed time since the start using YAHOO.printf.
251 * @param {String} name
252 * @param {Boolean} reset true to reset an existing timer
253 */
254YAHOO.timer = function(name, reset){
255 var t = new Date().getTime();
256 if(YAHOO._timers[name] && !reset){
257 YAHOO.printf("{0} : {1} ms", name, t-YAHOO._timers[name]);
258 }else{
259 YAHOO._timers[name] = t;
260 }
261}
262/**
263 * Extends one class with another class and optionally overrides members with the passed literal. This class
264 * also adds the function "override()" to the class that can be used to override
265 * members on an instance.
266 * @param {Object} subclass The class inheriting the functionality
267 * @param {Object} superclass The class being extended
268 * @param {Object} overrides (optional) A literal with members
269 * @static
270 */
271YAHOO.extendX = function(subclass, superclass, overrides){
272 YAHOO.extend(subclass, superclass);
273 subclass.override = function(o){
274 YAHOO.override(subclass, o);
275 };
276 if(!subclass.prototype.override){
277 subclass.prototype.override = function(o){
278 for(var method in o){
279 this[method] = o[method];
280 }
281 };
282 }
283 if(overrides){
284 subclass.override(overrides);
285 }
286};
287
288/**
289 * Creates namespaces but does not assume YAHOO is the root.
290 * @param {String} namespace1
291 * @param {String} namespace2
292 * @param {String} etc
293 * @static
294 */
295YAHOO.namespaceX = function(){
296 var a = arguments, len = a.length, i;
297 YAHOO.namespace.apply(YAHOO, a);
298 for(i = 0; i < len; i++){
299 var p = a[i].split('.')[0];
300 if(p != 'YAHOO' && YAHOO[p]){
301 eval(p + ' = YAHOO.' + p);
302 delete YAHOO[p];
303 }
304 }
305};
306
307YAHOO.override = function(origclass, overrides){
308 if(overrides){
309 var p = origclass.prototype;
310 for(var method in overrides){
311 p[method] = overrides[method];
312 }
313 }
314};
315
316/**
317 * @class YAHOO.ext.util.DelayedTask
318 * Provides a convenient method of performing setTimeout where a new
319 * timeout cancels the old timeout. An example would be performing validation on a keypress.
320 * You can use this class to buffer
321 * the keypress events for a certain number of milliseconds, and perform only if they stop
322 * for that amount of time.
323 * @constructor The parameters to this constructor serve as defaults and are not required.
324 * @param {<i>Function</i>} fn (optional) The default function to timeout
325 * @param {<i>Object</i>} scope (optional) The default scope of that timeout
326 * @param {<i>Array</i>} args (optional) The default Array of arguments
327 */
328YAHOO.ext.util.DelayedTask = function(fn, scope, args){
329 var timeoutId = null;
330
331 /**
332 * Cancels any pending timeout and queues a new one
333 * @param {Number} delay The milliseconds to delay
334 * @param {Function} newFn (optional) Overrides function passed to constructor
335 * @param {Object} newScope (optional) Overrides scope passed to constructor
336 * @param {Array} newArgs (optional) Overrides args passed to constructor
337 */
338 this.delay = function(delay, newFn, newScope, newArgs){
339 if(timeoutId){
340 clearTimeout(timeoutId);
341 }
342 fn = newFn || fn;
343 scope = newScope || scope;
344 args = newArgs || args;
345 timeoutId = setTimeout(fn.createDelegate(scope, args), delay);
346 };
347
348 /**
349 * Cancel the last queued timeout
350 */
351 this.cancel = function(){
352 if(timeoutId){
353 clearTimeout(timeoutId);
354 timeoutId = null;
355 }
356 };
357};
358
359/**
360 * @class YAHOO.ext.util.Observable
361 * Abstract base class that provides a common interface for publishing events. Subclasses are expected to
362 * to have a property "events" with all the events defined.<br>
363 * For example:
364 * <pre><code>
365 var Employee = function(name){
366 this.name = name;
367 this.events = {
368 'fired' : new YAHOO.util.CustomEvent('fired'),
369 'quit' : true // lazy initialize the CustomEvent
370 }
371 }
372 YAHOO.extend(Employee, YAHOO.ext.util.Observable);
373</code></pre>
374 */
375YAHOO.ext.util.Observable = function(){};
376YAHOO.ext.util.Observable.prototype = {
377 /**
378 * Fires the specified event with the passed parameters (minus the event name).
379 * @param {String} eventName
380 * @param {Object...} args Variable number of parameters are passed to handlers
381 * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
382 */
383 fireEvent : function(){
384 var ce = this.events[arguments[0].toLowerCase()];
385 if(typeof ce == 'object'){
386 return ce.fireDirect.apply(ce, Array.prototype.slice.call(arguments, 1));
387 }else{
388 return true;
389 }
390 },
391 /**
392 * Appends an event handler to this component
393 * @param {String} eventName The type of event to listen for
394 * @param {Function} handler The method the event invokes
395 * @param {<i>Object</i>} scope (optional) The scope (this object) for the handler
396 * @param {<i>boolean</i>} override (optional) If true, scope becomes the scope
397 */
398 addListener : function(eventName, fn, scope, override){
399 eventName = eventName.toLowerCase();
400 var ce = this.events[eventName];
401 if(!ce){
402 // added for a better message when subscribing to wrong event
403 throw 'You are trying to listen for an event that does not exist: "' + eventName + '".';
404 }
405 if(typeof ce == 'boolean'){
406 ce = new YAHOO.util.CustomEvent(eventName);
407 this.events[eventName] = ce;
408 }
409 ce.subscribe(fn, scope, override);
410 },
411
412 /**
413 * Appends an event handler to this component that is delayed the specified number of milliseconds. This
414 * is useful for events that modify the DOM and need to wait for the browser to catch up.
415 * @param {String} eventName The type of event to listen for
416 * @param {Function} handler The method the event invokes
417 * @param {<i>Object</i>} scope (optional) The scope (this object) for the handler
418 * @param {<i>Number</i>} delay (optional) The number of milliseconds to delay (defaults to 1 millisecond)
419 * @return {Function} The wrapped function that was created (can be used to remove the listener)
420 */
421 delayedListener : function(eventName, fn, scope, delay){
422 var newFn = function(){
423 setTimeout(fn.createDelegate(scope, arguments), delay || 1);
424 }
425 this.addListener(eventName, newFn);
426 return newFn;
427 },
428
429 /**
430 * Appends an event handler to this component that is buffered. If the event is triggered more than once
431 * in the specified time-frame, only the last one actually fires.
432 * @param {String} eventName The type of event to listen for
433 * @param {Function} handler The method the event invokes
434 * @param {<i>Object</i>} scope (optional) The scope (this object) for the handler
435 * @param {<i>Number</i>} millis (optional) The number of milliseconds to buffer (defaults to 250)
436 * @return {Function} The wrapped function that was created (can be used to remove the listener)
437 */
438 bufferedListener : function(eventName, fn, scope, millis){
439 var task = new YAHOO.ext.util.DelayedTask();
440 var newFn = function(){
441 task.delay(millis || 250, fn, scope, Array.prototype.slice.call(arguments, 0));
442 }
443 this.addListener(eventName, newFn);
444 return newFn;
445 },
446
447 /**
448 * Removes a listener
449 * @param {String} eventName The type of event to listen for
450 * @param {Function} handler The handler to remove
451 * @param {<i>Object</i>} scope (optional) The scope (this object) for the handler
452 */
453 removeListener : function(eventName, fn, scope){
454 var ce = this.events[eventName.toLowerCase()];
455 if(typeof ce == 'object'){
456 ce.unsubscribe(fn, scope);
457 }
458 },
459
460 /**
461 * Removes all listeners for this object
462 */
463 purgeListeners : function(){
464 for(var evt in this.events){
465 if(typeof this.events[evt] == 'object'){
466 this.events[evt].unsubscribeAll();
467 }
468 }
469 }
470};
471/**
472 * Appends an event handler to this element (shorthand for addListener)
473 * @param {String} eventName The type of event to listen for
474 * @param {Function} handler The method the event invokes
475 * @param {<i>Object</i>} scope (optional) The scope (this object) for the handler
476 * @param {<i>boolean</i>} override (optional) If true, scope becomes the scope
477 * @method
478 */
479YAHOO.ext.util.Observable.prototype.on = YAHOO.ext.util.Observable.prototype.addListener;
480
481/**
482 * Starts capture on the specified Observable. All events will be passed
483 * to the supplied function with the event name + standard signature of the event
484 * <b>before</b> the event is fired. If the supplied function returns false,
485 * the event will not fire.
486 * @param {Observable} o The Observable to capture
487 * @param {Function} fn The function to call
488 * @param {Object} scope (optional) The scope (this object) for the fn
489 * @static
490 */
491YAHOO.ext.util.Observable.capture = function(o, fn, scope){
492 o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
493};
494
495/**
496 * Removes <b>all</b> added captures from the Observable.
497 * @param {Observable} o The Observable to release
498 * @static
499 */
500YAHOO.ext.util.Observable.releaseCapture = function(o){
501 o.fireEvent = YAHOO.ext.util.Observable.prototype.fireEvent;
502};
503
504/**
505 * @class YAHOO.ext.util.Config
506 * Class with one useful method
507 * @singleton
508 */
509YAHOO.ext.util.Config = {
510 /**
511 * Copies all the properties of config to obj.
512 * @param {Object} obj The receiver of the properties
513 * @param {Object} config The source of the properties
514 * @param {Object} defaults A different object that will also be applied for default values
515 * @return {Object} returns obj
516 */
517 apply : function(obj, config, defaults){
518 if(defaults){
519 this.apply(obj, defaults);
520 }
521 if(config){
522 for(var prop in config){
523 obj[prop] = config[prop];
524 }
525 }
526 return obj;
527 }
528};
529
530if(!String.escape){
531 String.escape = function(string) {
532 return string.replace(/('|\\)/g, "\\$1");
533 };
534};
535
536String.leftPad = function (val, size, ch) {
537 var result = new String(val);
538 if (ch == null) {
539 ch = " ";
540 }
541 while (result.length < size) {
542 result = ch + result;
543 }
544 return result;
545};
546
547// workaround for Safari anim duration speed problems
548if(YAHOO.util.AnimMgr && YAHOO.ext.util.Browser.isSafari){
549 YAHOO.util.AnimMgr.fps = 500;
550}
551
552// add ability for callbacks instead of events for animations
553if(YAHOO.util.Anim){
554 YAHOO.util.Anim.prototype.animateX = function(callback, scope){
555 var f = function(){
556 this.onComplete.unsubscribe(f);
557 if(typeof callback == 'function'){
558 callback.call(scope || this, this);
559 }
560 };
561 this.onComplete.subscribe(f, this, true);
562 this.animate();
563 };
564}
565
566// workaround for Safari 1.3 not supporting hasOwnProperty
567if(YAHOO.util.Connect && YAHOO.ext.util.Browser.isSafari){
568 YAHOO.util.Connect.setHeader = function(o){
569 for(var prop in this._http_header){
570 // if(this._http_header.hasOwnProperty(prop)){
571 if(typeof this._http_header[prop] != 'function'){
572 o.conn.setRequestHeader(prop, this._http_header[prop]);
573 }
574 }
575 delete this._http_header;
576 this._http_header = {};
577 this._has_http_headers = false;
578 };
579}
580/**
581 * A simple enhancement to drag drop that allows you to constrain the movement of the
582 * DD or DDProxy object to a particular element.<br /><br />
583 *
584 * Usage:
585 <pre><code>
586 var dd = new YAHOO.util.DDProxy("dragDiv1", "proxytest",
587 { dragElId: "existingProxyDiv" });
588 dd.startDrag = function(){
589 this.constrainTo('parent-id');
590 };
591 </code></pre>
592 * Or you can initalize it using the {@link YAHOO.ext.Element} object:
593 <pre><code>
594 getEl('dragDiv1').initDDProxy('proxytest', {dragElId: "existingProxyDiv"}, {
595 startDrag : function(){
596 this.constrainTo('parent-id');
597 }
598 });
599 </code></pre>
600 */
601if(YAHOO.util.DragDrop){
602 /**
603 * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
604 * @type Object
605 */
606 YAHOO.util.DragDrop.prototype.defaultPadding = {left:0, right:0, top:0, bottom:0};
607
608 /**
609 * Initializes the drag drop object's constraints to restrict movement to a certain element.
610 * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
611 * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
612 * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
613 * an object containing the sides to pad. For example: {right:10, bottom:10}
614 * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
615 */
616 YAHOO.util.DragDrop.prototype.constrainTo = function(constrainTo, pad, inContent){
617 if(typeof pad == 'number'){
618 pad = {left: pad, right:pad, top:pad, bottom:pad};
619 }
620 pad = pad || this.defaultPadding;
621 var b = getEl(this.getEl()).getBox();
622 var ce = getEl(constrainTo);
623 var c = ce.dom == document.body ? { x: 0, y: 0,
624 width: YAHOO.util.Dom.getViewportWidth(),
625 height: YAHOO.util.Dom.getViewportHeight()} : ce.getBox(inContent || false);
626 var topSpace = b.y - c.y;
627 var leftSpace = b.x - c.x;
628
629 this.resetConstraints();
630 this.setXConstraint(leftSpace - (pad.left||0), // left
631 c.width - leftSpace - b.width - (pad.right||0) //right
632 );
633 this.setYConstraint(topSpace - (pad.top||0), //top
634 c.height - topSpace - b.height - (pad.bottom||0) //bottom
635 );
636 }
637}