OSDN Git Service

setup spinelz environment
[cloudmanganw/git_repo.git] / war / WEB-INF / classes / jp / sourceforge / manganetwork / page / javascripts / spinelz_lib / spinelz_util.js
1 // Copyright (c) 2006 spinelz.org (http://script.spinelz.org/)
2 // 
3 // Permission is hereby granted, free of charge, to any person obtaining
4 // a copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to
8 // permit persons to whom the Software is furnished to do so, subject to
9 // the following conditions:
10 // 
11 // The above copyright notice and this permission notice shall be
12 // included in all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22 /**
23  * Element class
24  */
25 Object.extend(Element, {
26
27   getTagNodes: function(element, tree) {
28     return this.getElementsByNodeType(element, 1, tree);
29   },
30   
31   getTextNodes: function(element, tree) {
32     return this.getElementsByNodeType(element, 3, tree);
33   },
34   
35   getElementsByNodeType: function(element, nodeType, tree) {
36     
37     element = ($(element) || document.body);
38     var nodes = element.childNodes;
39     var result = [];
40     
41     for (var i = 0; i < nodes.length; i++) {
42       if (nodes[i].nodeType == nodeType)
43         result.push(nodes[i]);
44       if (tree && (nodes[i].nodeType == 1)) 
45         result = result.concat(this.getElementsByNodeType(nodes[i], nodeType, tree));
46     }
47     
48     return result;
49   },
50   
51   getParentByClassName: function(className, element) {
52     var parent = element.parentNode;
53     if (!parent || (parent.tagName == 'BODY'))
54       return null;
55     else if (!parent.className) 
56       return Element.getParentByClassName(className, parent);
57     else if (Element.hasClassName(parent, className))
58       return parent;
59     else
60       return Element.getParentByClassName(className, parent);
61   },
62   
63   getParentByTagName: function(tagNames, element) {
64     
65     var parent = element.parentNode;
66     if (parent.tagName == 'BODY')
67       return null;
68       
69     var index = tagNames.join('/').toUpperCase().indexOf(parent.tagName.toUpperCase(), 0);
70     if (index >= 0)
71       return parent;
72     else
73       return Element.getParentByTagName(tagNames, parent);
74   },
75   
76   getFirstElementByClassNames: function(element, classNames, tree) {
77     
78     if (!element || 
79         !((typeof(classNames) == 'object') && (classNames.constructor == Array))) {
80       return;  
81     }
82   
83     element = (element || document.body);
84     var nodes = element.childNodes;
85     
86     for (var i = 0; i < nodes.length; i++) {
87       for (var j = 0; j < classNames.length; j++) {
88         if (nodes[i].nodeType != 1) {
89           continue;
90         
91         } else if (Element.hasClassName(nodes[i], classNames[j])) {
92           return nodes[i];
93         
94         } else if (tree) {
95           var result = this.getFirstElementByClassNames(nodes[i], classNames, tree);
96           if (result) return result;
97         }
98       }
99     }
100     
101     return;
102   },
103   
104   getElementsByClassNames: function(element, classNames) {
105     
106     if (!element || 
107         !((typeof(classNames) == 'object') && (classNames.constructor == Array))) {
108       return;  
109     }
110   
111     var nodes = [];
112     classNames.each(function(c) {
113       nodes = nodes.concat(document.getElementsByClassName(c, element));
114     });
115     
116     return nodes;
117   },
118   
119   getWindowHeight: function() {
120       
121     if (window.innerHeight) {
122       return window.innerHeight; // Mozilla, Opera, NN4
123     } else if (document.documentElement && document.documentElement.offsetHeight){ // ?? IE
124       return document.documentElement.offsetHeight;
125     } else if (document.body && document.body.offsetHeight) {
126       return document.body.offsetHeight - 20;
127     }
128     return 0;
129   },
130   
131   getWindowWidth:function() {
132     
133     if(window.innerWidth) {
134       return window.innerWidth; // Mozilla, Opera, NN4
135     } else if (document.documentElement && document.documentElement.offsetWidth){ // ?? IE
136       return document.documentElement.offsetWidth - 20;
137     } else if (document.body && document.body.offsetWidth){
138       return document.body.offsetWidth - 20;
139     }
140     return 0;
141   },
142   
143   getMaxZindex: function(element) {
144     element = $(element);
145     if (!element) {
146       element = document.body;
147     }  
148     if (element.nodeType != 1) return 0;
149
150     var maxZindex = 0;
151     if (element.style) maxZindex = parseInt(Element.getStyle(element, "z-index"));  
152     if (isNaN(maxZindex)) maxZindex = 0;
153
154     var tmpZindex = 0;
155     var elements = element.childNodes;
156     for (var i = 0; i < elements.length; i++) {
157       if (elements[i] && elements[i].tagName) {
158         tmpZindex = Element.getMaxZindex(elements[i]);
159         if (maxZindex < tmpZindex) maxZindex = tmpZindex;
160       }
161     }
162
163     return maxZindex;
164   },
165
166   select: function(element, value) {
167     $A($(element).options).each(function(opt) {
168       if (opt.value == value) {
169         opt.selected = true;
170       } else {
171         opt.selected = false;
172       }
173     });
174   }
175 });
176
177
178 /**
179  * Array
180  */
181 Object.extend(Array.prototype, {
182   insert : function(index, element) {
183     this.splice(index, 0 , element);
184   },
185   
186   remove : function(index) {
187     this.splice(index, 1);
188   }
189 });
190
191
192 /**
193  * String
194  */
195 Object.extend(String.prototype, {
196   
197   getPrefix: function(delimiter) {
198   
199     if (!delimiter) delimiter = '_';
200     return this.split(delimiter)[0];
201   },
202   
203   getSuffix: function(delimiter) {
204     
205     if (!delimiter) delimiter = '_';
206     return this.split(delimiter).pop();
207   },
208
209   appendPrefix: function(prefix, delimiter) {
210   
211     if (!delimiter) delimiter = '_';
212     return this + delimiter + prefix;
213   },
214   
215   appendSuffix: function(suffix, delimiter) {
216   
217     if (!delimiter) delimiter = '_';
218     return this + delimiter + suffix;
219   },
220   
221   // for firefox
222   println: function() {
223     dump(this + '\n');
224   }
225 });
226
227
228 /**
229  * CssUtil
230  */
231 var CssUtil = Class.create();
232
233 CssUtil.appendPrefix = function(prefix, suffixes) {
234   var newHash = {};
235   $H(suffixes).each(function(pair) {
236     newHash[pair[0]] = prefix + suffixes[pair[0]];
237   });
238   return newHash;
239 }
240
241 CssUtil.getCssRules = function(sheet) {
242   return sheet.rules || sheet.cssRules;
243 }
244
245 CssUtil.getCssRuleBySelectorText = function(selector) {
246   var rule = null;
247   $A(document.styleSheets).each(function(s) {
248     var rules = CssUtil.getCssRules(s);
249     rule =  $A(rules).detect(function(r) {
250       if (!r.selectorText) return false;
251       return r.selectorText.toLowerCase() == selector.toLowerCase();
252     });
253     if (rule) throw $break;
254   });
255   return rule;
256 }
257
258 /*
259 CssUtil.require = function(file, attributes, parent) {
260   var links = document.getElementsByTagName('link');
261   var regex = /^.*\.css/; 
262   var match = file.match(regex)
263   alert(file)
264   regex.compile(match);
265
266   $A(links).each(function(ln) {
267     if (ln.href.match(regex)) {
268     }
269   });
270   
271 //  attributes = Object.extend({
272 //                  href: file, 
273 //                  media: 'screen', 
274 //                  rel: 'stylesheet', 
275 //                  type: 'text/css'}, attributes);
276 //  var node = Builder.node('link', attributes);
277 //  if (!parent) parent = document.body;
278 //  parent.appendChild(node);
279 //  alert(file);
280 }
281 */
282
283 CssUtil.prototype = {
284   
285   initialize: function(styles) {
286     if (!((typeof(styles) == 'object') && (styles.constructor == Array))) {
287       throw 'CssUtil#initialize: argument must be a Array object!';    
288     }
289     
290     this.styles = styles;
291   },
292
293   getClasses: function(key) {
294     return this.styles.collect(function(s) {
295       return s[key];
296     });
297   },
298   
299   joinClassNames: function(key) {
300     return this.getClasses(key).join(' ');
301   },
302   
303   addClassNames: function(element, key) {
304     this.styles.each(function(s) {
305       Element.addClassName(element, s[key]);
306     });
307   },
308   
309   removeClassNames: function(element, key) {
310     this.styles.each(function(s) {
311       Element.removeClassName(element, s[key]);
312     });
313   },
314   
315   refreshClassNames: function(element, key) {
316     element.className = '';
317     this.addClassNames(element, key);
318   },
319   
320   hasClassName: function(element, key) {
321     return this.styles.any(function(s) {
322       return Element.hasClassName(element, s[key]);
323     });
324   }
325 }
326
327
328 /** 
329  * Hover 
330  */
331 var Hover = Class.create();
332 Hover.prototype = {
333
334   initialize: function(element) {
335     this.options = Object.extend({
336       defaultClass: '',
337       hoverClass: '',
338       cssUtil: '',
339       list: false
340     }, arguments[1] || {});
341     
342     var element = $(element);
343     if (this.options.list) {
344       var nodes = element.childNodes;
345       for (var i = 0; i < nodes.length; i++) {
346         if (nodes[i].nodeType == 1) {
347           this.build(nodes[i]);
348         }
349       }
350     } else {
351       this.build(element);
352     }
353   },
354   
355   build: function(element) {
356     this.normal = this.getNormalClass(element);
357     this.hover = this.getHoverClass(this.normal);
358     
359     if (this.options.cssUtil) {
360       this.normal = this.options.cssUtil.joinClassNames(normal);
361       this.hover = this.options.cssUtil.joinClassNames(hover);    
362     }
363     this.setHoverEvent(element);
364   },
365
366   setHoverEvent: function(element) {
367     Event.observe(element, "mouseout", this.toggle.bindAsEventListener(this, element, this.normal));
368     Event.observe(element, "mouseover", this.toggle.bindAsEventListener(this, element, this.hover));
369   },
370
371   toggle: function(event, element, className) {
372     Event.stop(event);
373     element.className = className;
374   },
375
376   getNormalClass: function(element) {
377     var className = (this.options.defaultClass || element.className);
378     return (className || '');
379   },
380   
381   getHoverClass: function(defaultClass) {
382     var className = this.options.hoverClass;
383     if (!className) {
384       className = defaultClass.split(' ').collect(function(c) {
385         return c + 'Hover';
386       }).join(' ');
387     }  
388      return className;
389   }
390 }
391
392
393 /**
394  * Date
395  */
396 Object.extend(Date.prototype, {
397   msPerDay: function() {
398     return 24 * 60 * 60 * 1000;
399   },
400
401   advance: function(options) {
402     return new Date(this.getTime() + this.msPerDay() * options.days);
403   },
404
405   days: function() {
406     var date = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
407     return Math.round(date.getTime() / this.msPerDay());
408   },
409
410   toHash: function() {
411     return {
412       year:  this.getFullYear(),
413       month: this.getMonth(),
414       day:   this.getDate(),
415       hour:  this.getHours(),
416       min:   this.getMinutes(),
417       sec:   this.getSeconds()
418     }
419   },
420
421   sameYear: function(date) {
422     return this.getFullYear() == date.getFullYear();
423   },
424
425   sameMonth: function(date) {
426     return this.sameYear(date) && this.getMonth() == date.getMonth();
427   },
428
429   sameDate: function(date) {
430     return this.sameYear(date) && this.sameMonth(date) && this.getDate() == date.getDate();
431   },
432
433   betweenDate: function(start, finish) {
434     var myDays = this.days();
435     return (start.days() <= myDays && myDays <= finish.days());
436   },
437
438   betweenTime: function(start, finish) {
439     var myTime = this.getTime();
440     return (start.getTime() <= myTime && myTime <= finish.getTime());
441   }
442 });
443
444
445 /**
446  * DateUtil
447  */
448 var DateUtil = {
449
450   dayOfWeek: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
451
452   months: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
453
454   daysOfMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
455
456   numberOfDays: function(start, finish) {
457     return finish.days() - start.days();
458   },
459
460   isLeapYear: function(year) {
461     if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
462       return true;
463     return false;
464   }, 
465
466   nextDate: function(date) {
467     return new Date(date.getFullYear(), date.getMonth(), date.getDate() + 1);
468   },
469
470   previousDate: function(date) {
471     return new Date(date.getFullYear(), date.getMonth(), date.getDate() - 1);
472   },
473   
474   afterDays: function(date, after) {
475     return new Date(date.getFullYear(), date.getMonth(), date.getDate() + after);
476   },
477   
478   getLastDate: function(year, month) {
479     var last = this.daysOfMonth[month];
480     if ((month == 1) && this.isLeapYear(year)) {
481       return new Date(year, month, last + 1);
482     }
483     return new Date(year, month, last);
484   },
485   
486   getFirstDate: function(year, month) {
487     if (year.constructor == Date) {
488       return new Date(year.getFullYear(), year.getMonth(), 1);
489     }
490     return new Date(year, month, 1);
491   },
492
493   getWeekTurn: function(date, firstDWeek) {
494     var limit = 6 - firstDWeek + 1;
495     var turn = 0;
496     while (limit < date) {
497       date -= 7;
498       turn++;
499     }
500     return turn;
501   },
502
503   toDateString: function(date) {
504     return date.toDateString();
505   },
506   
507   toLocaleDateString: function(date) {
508     return date.toLocaleDateString();
509   },
510   
511   simpleFormat: function(formatStr) {
512     return function(date) {
513       var formated = formatStr.replace(/M+/g, DateUtil.zerofill((date.getMonth() + 1).toString(), 2));
514       formated = formated.replace(/d+/g, DateUtil.zerofill(date.getDate().toString(), 2));
515       formated = formated.replace(/y{4}/g, date.getFullYear());
516       formated = formated.replace(/y{1,3}/g, new String(date.getFullYear()).substr(2));
517       formated = formated.replace(/E+/g, DateUtil.dayOfWeek[date.getDay()]);
518       
519       return formated;
520     }
521   },
522
523   zerofill: function(date,digit){
524     var result = date;
525     if(date.length < digit){
526       var tmp = digit - date.length;
527       for(i=0; i < tmp; i++){
528         result = "0" + result;
529       }
530     }
531     return result;
532   },
533
534   toDate: function(hash) {
535     return new Date(hash.year, hash.month, hash.day, hash.hour, hash.min, hash.sec || 0);
536   }
537 }
538
539
540 /**
541  * ZindexManager
542  */
543 var ZindexManager = {
544   zIndex: 1000,
545
546   getIndex: function(zIndex) {
547     if (zIndex) {
548       if (isNaN(zIndex)) {
549         zIndex = Element.getMaxZindex() + 1;
550       } else if (ZindexManager.zIndex > zIndex) {
551         zIndex = ZindexManager.zIndex;
552       }
553     } else {
554       zIndex = ZindexManager.zIndex;
555     }
556     ZindexManager.zIndex = zIndex + 1;
557     return zIndex;
558   }
559 }
560
561
562 /**
563  * Modal
564  */
565 var Modal = {
566   maskId:         'modalMask',
567   maskClass:      'modal_mask',
568   maskClassIE:    'modal_mask_ie',
569   element:        null,
570   snaps:          null,
571   listener:       null,
572   resizeListener: null,
573   cover:          null,
574   excepteds:      null,
575
576   mask: function(excepted) {
577     var options = Object.extend({
578       cssPrefix: 'custom_',
579       zIndex: null
580     }, arguments[1] || {});
581
582     if (Modal.element) {
583       Modal._snap(excepted);
584       Modal._rebuildMask();
585     } else {
586       Modal.snaps = [];
587       Modal.excepteds = [];
588       Modal._buildMask(options.cssPrefix);
589       Modal.cover = new IECover(Modal.element, {transparent: true});
590     }
591     Modal._setZindex(excepted, options.zIndex);
592     Modal._setFullSize();
593     if (!Modal.hasExcepted(excepted)) Modal.excepteds.push(excepted);
594   },
595
596   unmask: function() {
597     if (Modal.element) {
598       if (Modal.snaps.length == 0) {
599         Element.hide(Modal.element);
600         Modal._removeEvent();
601         Modal.excepteds = [];
602         Element.remove(Modal.element);
603         Modal.element = null;
604       } else {
605         Element.setStyle(Modal.element, {zIndex: Modal.snaps.pop()});
606         Modal.excepteds.pop();
607       }
608     }
609   },
610
611   _addEvent: function() {
612     if (!Modal.listener) {
613       Modal.listener = Modal._handleEvent.bindAsEventListener();
614       Modal.resizeListener = Modal._onResize.bindAsEventListener();
615     }
616     Event.observe(document, "keypress", Modal.listener);
617     Event.observe(document, "keydown", Modal.listener);
618     Event.observe(document, "keyup", Modal.listener);
619     Event.observe(document, "focus", Modal.listener);
620     Event.observe(window, "resize", Modal.resizeListener);
621   },
622
623   _removeEvent: function() {
624     Event.stopObserving(document, "keypress", Modal.listener);
625     Event.stopObserving(document, "keydown", Modal.listener);
626     Event.stopObserving(document, "keyup", Modal.listener);
627     Event.stopObserving(document, "focus", Modal.listener);
628     Event.stopObserving(window, "resize", Modal.resizeListener);
629   },
630
631   _isMasked: function() {
632     return Modal.element && Element.visible(Modal.element);
633   },
634
635   _snap: function(excepted) {
636     var index = Element.getStyle(Modal.element, 'zIndex');
637     if (index && Modal._isMasked() && !Modal.hasExcepted(excepted)) Modal.snaps.push(index);
638   },
639
640   _setZindex: function(excepted, zIndex) {
641     zIndex = ZindexManager.getIndex(zIndex);
642     Element.setStyle(Modal.element, {zIndex:  zIndex});
643     excepted = Element.makePositioned($(excepted));
644     Element.setStyle(excepted, {zIndex: ++zIndex});
645   },
646
647   _setFullSize: function() {
648     Modal.element.setStyle({
649       width:  Element.getWindowWidth() + 'px',
650       height: Element.getWindowHeight() + 'px'
651     });
652     if (Modal.cover) Modal.cover.resetSize();
653   },
654
655   _buildMask: function(cssPrefix) {
656     var mask = Builder.node('div', {id: Modal.maskId});
657     Modal._setClassNames(mask, cssPrefix);
658     document.body.appendChild(mask);
659     Modal.element = mask;
660     Modal._addEvent();
661   },
662
663   _setClassNames: function(element, cssPrefix) {
664     var className = (UserAgent.isIE()) ? Modal.maskClassIE : Modal.maskClass;
665     Element.addClassName(element, className);
666     Element.addClassName(element, cssPrefix + className);
667   },
668
669   _rebuildMask: function() {
670     document.body.appendChild(Modal.element);
671     Element.show(Modal.element);
672   },
673
674   _isOutOfModal: function(src) {
675     var limit = Element.getStyle(Modal.element, 'zIndex');
676     var zIndex = null;
677     while ((src = src.parentNode) && src != document.body) {
678       if (src.style && (zIndex = Element.getStyle(src, 'zIndex'))) {
679         if (zIndex > limit) {
680           return true;
681         } else {
682           return false;
683         }
684       }
685     }
686     return false;
687   },
688
689   _handleEvent: function (event) {
690     var src = Event.element(event);
691     if (!Modal._isOutOfModal(src)) {
692       Event.stop(event);
693     }
694   },
695
696   _onResize: function(event) {
697     Modal._setFullSize();
698   },
699
700   hasExcepted: function(excepted) {
701     return Modal.excepteds && Modal.excepteds.include(excepted);
702   }
703 }
704
705
706 /**
707  * IECover
708  */
709 var IECover = Class.create();
710 IECover.src = '/blank.html';
711 IECover.prototype = {
712   idSuffix: 'iecover',
713
714   initialize: function(parent) {
715     this.options = Object.extend({
716       transparent : false,
717       padding     : 0
718     }, arguments[1] || {});
719
720     if (document.all) {
721       parent = $(parent);
722       this.id = parent.id.appendSuffix(this.idSuffix);
723       this._build(parent);
724       this.resetSize();
725     }
726   },
727
728   resetSize: function() {
729     if (this.element) {
730       var parent = this.element.parentNode;
731       var padding = this.options.padding;
732       this.element.width = parent.offsetWidth - padding + 'px';
733       this.element.height = Element.getHeight(parent) - padding + 'px';
734     }
735   },
736
737   _build: function(parent) {
738     var padding = this.options.padding / 2;
739     var styles = {
740       position : 'absolute',
741       top      : padding + 'px',
742       left     : padding + 'px'
743     };
744     if (this.options.transparent) styles.filter = 'alpha(opacity=0)';
745     this.element = Builder.node('iframe', {src: IECover.src, id: this.id, frameborder: 0});
746     Element.setStyle(this.element, styles);
747     var firstNode = Element.down(parent, 0);
748     if (firstNode) Element.makePositioned(firstNode);
749     parent.insertBefore(this.element, parent.firstChild);
750   }
751 }
752
753 /**
754  * UserAgent
755  */
756 var UserAgent = {
757   getUserAgent: function() {
758     return navigator.userAgent;
759   },
760   isIE: function() {
761     if(document.all && this.getUserAgent().toLowerCase().indexOf('msie') != -1) {
762       return true;
763     }
764   },
765   isIE7: function() {
766     if(document.all && this.getUserAgent().toLowerCase().indexOf('msie 7') != -1) {
767       return true;
768     }
769   }
770 }
771
772 /**
773  * ShortcutManager
774  */
775 var ShortcutManager = {
776   initialize: function() {
777     var defaultOptions  = {
778       initialStarted:   true,
779       preventDefault:   true
780     }
781     this.options            = Object.extend(defaultOptions, arguments[0] || {});
782     if(this.documentListener) {
783       Event.stopObserving(document, 'keydown', this.documentListener);
784     }
785     this.documentListener   = this.eventKeydown.bindAsEventListener(this);
786     this.functions          = new Object();
787     this.functions['a']     = new Object();
788     this.functions['ac']    = new Object();
789     this.functions['as']    = new Object();
790     this.functions['acs']   = new Object();
791     this.functions['c']     = new Object();
792     this.functions['cs']    = new Object();
793     this.functions['n']     = new Object();
794     this.functions['s']     = new Object();
795     this.keyCode            = {
796       'backspace':      8,
797       'tab':            9,
798       'return':        13,
799       'enter':         13,
800       'pause':         19,
801       'break':         19,
802       'caps':          20,
803       'capslock':      20,
804       'esc':           27,
805       'escape':        27,
806       'space':         32,
807       'pageup':        33,
808       'pgup':          33,
809       'pagedown':      34,
810       'pgdn':          34,
811       'end':           35,
812       'home':          36,
813       'left':          37,
814       'up':            38,
815       'right':         39,
816       'down':          40,
817       'insert':        45,
818       'delete':        46,
819       '0':             48,
820       '1':             49,
821       '2':             50,
822       '3':             51,
823       '4':             52,
824       '5':             53,
825       '6':             54,
826       '7':             55,
827       '8':             56,
828       '9':             57,
829       'a':             65,
830       'b':             66,
831       'c':             67,
832       'd':             68,
833       'e':             69,
834       'f':             70,
835       'g':             71,
836       'h':             72,
837       'i':             73,
838       'j':             74,
839       'k':             75,
840       'l':             76,
841       'm':             77,
842       'n':             78,
843       'o':             79,
844       'p':             80,
845       'q':             81,
846       'r':             82,
847       's':             83,
848       't':             84,
849       'u':             85,
850       'v':             86,
851       'w':             87,
852       'x':             88,
853       'y':             89,
854       'z':             90,
855       'f1':           112,
856       'f2':           113,
857       'f3':           114,
858       'f4':           115,
859       'f5':           116,
860       'f6':           117,
861       'f7':           118,
862       'f8':           119,
863       'f9':           120,
864       'f10':          121,
865       'f11':          122,
866       'f12':          123,
867       'numlock':      144,
868       'nmlk':         144,
869       'scrolllock':   145,
870       'scflk':        145
871     };
872     this.numKeys = {
873       96:   48,
874       97:   49,
875       98:   50,
876       99:   51,
877       100:  52,
878       101:  53,
879       102:  54,
880       103:  55,
881       104:  56,
882       105:  57
883     };
884     if(this.options.initialStarted) {
885       this.start();
886     } else {
887       this.stop();
888     }
889     Event.observe(document, 'keydown', this.documentListener);
890   },
891   add: function(shortcut, callback) {
892     this._add_or_remove_function(shortcut, callback);
893   },
894   destroy: function() {
895     Event.stopObserving(document, 'keydown', this.documentListener);
896   },
897   eventKeydown: function(event) {
898     if(this.executable) {
899       var code;
900       var key = '';
901       event = event || window.event;
902       if(event.keyCode) {
903         if(event.altKey) {
904           key += 'a';
905         }
906         if(event.ctrlKey) {
907           key += 'c';
908         }
909         if(event.shiftKey) {
910           key += 's';
911         }
912         if(key == '') {
913           key = 'n';
914         }
915         code = this._mergeNumKey(event.keyCode);
916         if(this.functions[key][code]) {
917           this.functions[key][code]();
918           if(this.options.preventDefault) {
919             Event.stop(event);
920           }
921         }
922       }
923     }
924   },
925   remove: function(shortcut) {
926     this._add_or_remove_function(shortcut);
927   },
928   start: function() {
929     this.executable = true;
930   },
931   stop: function() {
932     this.executable = false;
933   },
934   _add_or_remove_function: function(shortcut, callback) {
935     var pressed_key_code;
936     var additional_keys = new Array();
937     var self = this;
938     $A(shortcut.toLowerCase().split("+")).each(
939       function(key) {
940         if(key == 'alt') {
941           additional_keys.push('a');
942         } else if(key == 'ctrl') {
943           additional_keys.push('c');
944         } else if(key == 'shift') {
945           additional_keys.push('s');
946         } else {
947           pressed_key_code = self.keyCode[key];
948         }
949       }
950     );
951     var key = additional_keys.sortBy(function(value, index) {return value;}).join('');
952     if(key == '') {
953       key = 'n';
954     }
955     if(callback) {
956       this.functions[key][pressed_key_code] = callback;
957     } else {
958       this.functions[key][pressed_key_code] = null;
959     }
960   },
961   _mergeNumKey: function(code) {
962     return (this.numKeys[code]) ? this.numKeys[code] : code;
963   }
964 }
965
966
967 /**
968  * Function
969  */
970 Function.prototype.callAfterLoading = function(object) {
971   object = object || this;
972   if (UserAgent.isIE() && document.readyState != 'complete') {
973     Event.observe(window, 'load', this.bind(object));
974   } else {
975     this.call(object);
976   }
977 }