bhf 0.4.2.2 → 0.4.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. data/app/controllers/bhf/application_controller.rb +2 -2
  2. data/app/controllers/bhf/embed_entries_controller.rb +8 -8
  3. data/app/controllers/bhf/entries_controller.rb +10 -10
  4. data/app/controllers/bhf/pages_controller.rb +2 -2
  5. data/app/helpers/bhf/application_helper.rb +3 -3
  6. data/app/helpers/bhf/entries_helper.rb +4 -4
  7. data/app/views/bhf/_user.haml +2 -2
  8. data/app/views/bhf/application/index.haml +1 -1
  9. data/app/views/bhf/entries/_form.haml +5 -5
  10. data/app/views/bhf/entries/_validation_errors.haml +1 -1
  11. data/app/views/bhf/entries/form/belongs_to/_account_scope.haml +1 -1
  12. data/app/views/bhf/entries/form/belongs_to/_radio.haml +1 -1
  13. data/app/views/bhf/entries/form/belongs_to/_select.haml +2 -2
  14. data/app/views/bhf/entries/form/belongs_to/_static.haml +1 -1
  15. data/app/views/bhf/entries/form/column/_array.haml +2 -2
  16. data/app/views/bhf/entries/form/column/_date.haml +1 -1
  17. data/app/views/bhf/entries/form/column/_file.haml +2 -2
  18. data/app/views/bhf/entries/form/column/_markdown.haml +2 -2
  19. data/app/views/bhf/entries/form/column/_multiple_fields.haml +1 -1
  20. data/app/views/bhf/entries/form/column/_number.haml +1 -1
  21. data/app/views/bhf/entries/form/column/_static.haml +1 -1
  22. data/app/views/bhf/entries/form/column/_wysiwyg.haml +1 -1
  23. data/app/views/bhf/entries/form/embeds_many/_static.haml +5 -5
  24. data/app/views/bhf/entries/form/embeds_one/_static.haml +5 -5
  25. data/app/views/bhf/entries/form/has_and_belongs_to_many/_account_scope.haml +1 -1
  26. data/app/views/bhf/entries/form/has_and_belongs_to_many/_static.haml +1 -1
  27. data/app/views/bhf/entries/form/has_many/_static.haml +4 -4
  28. data/app/views/bhf/entries/form/has_one/_account_scope.haml +1 -1
  29. data/app/views/bhf/entries/form/has_one/_static.haml +4 -4
  30. data/app/views/bhf/helper/_flash.haml +1 -1
  31. data/app/views/bhf/helper/_frontend_edit.haml +1 -1
  32. data/app/views/bhf/helper/_node.haml +3 -3
  33. data/app/views/bhf/helper/_reflection_node.haml +2 -2
  34. data/app/views/bhf/pages/_platform.haml +15 -15
  35. data/app/views/bhf/pages/_search.haml +7 -7
  36. data/app/views/bhf/pages/macro/belongs_to/_default.haml +1 -1
  37. data/app/views/bhf/pages/macro/column/_date.haml +1 -1
  38. data/app/views/bhf/pages/macro/column/_text.haml +1 -1
  39. data/app/views/bhf/pages/macro/embeds_many/_default.haml +1 -1
  40. data/app/views/bhf/pages/macro/embeds_one/_default.haml +1 -1
  41. data/app/views/bhf/pages/macro/has_and_belongs_to_many/_default.haml +1 -1
  42. data/app/views/bhf/pages/macro/has_many/_default.haml +1 -1
  43. data/app/views/bhf/pages/macro/has_one/_default.haml +1 -1
  44. data/app/views/bhf/pages/show.haml +1 -1
  45. data/app/views/kaminari/bhf/_next_page.html.haml +1 -1
  46. data/app/views/kaminari/bhf/_page.html.haml +1 -1
  47. data/app/views/kaminari/bhf/_prev_page.html.haml +1 -1
  48. data/app/views/layouts/bhf/application.haml +10 -11
  49. data/app/views/layouts/bhf/quick_edit.haml +2 -2
  50. data/config/routes.rb +6 -6
  51. data/lib/bhf/active_record/upload.rb +1 -1
  52. data/lib/bhf/form.rb +1 -1
  53. data/lib/bhf/i18n.rb +3 -3
  54. data/lib/bhf/mongoid/document.rb +2 -2
  55. data/lib/bhf/pagination.rb +14 -14
  56. data/lib/bhf/platform.rb +17 -17
  57. data/lib/bhf/view_helpers.rb +1 -1
  58. data/vendor/assets/images/bhf/ajax_loader.gif +0 -0
  59. data/vendor/assets/images/bhf/bg.png +0 -0
  60. data/vendor/assets/images/bhf/mooeditable-toolbarbuttons-tango.png +0 -0
  61. data/vendor/assets/images/bhf/pictos.png +0 -0
  62. data/vendor/assets/images/bhf/small_ajax_loader.gif +0 -0
  63. data/vendor/assets/images/bhf/small_ajax_loader_h.gif +0 -0
  64. data/vendor/assets/images/bhf/wmd-buttons.png +0 -0
  65. data/vendor/assets/images/logo_bhf.png +0 -0
  66. data/vendor/assets/javascripts/bhf/application.js +280 -0
  67. data/vendor/assets/javascripts/bhf/classes/AjaxEdit.js +104 -0
  68. data/vendor/assets/javascripts/bhf/classes/Ajaxify.js +63 -0
  69. data/vendor/assets/javascripts/bhf/classes/ArrayFields.js +25 -0
  70. data/vendor/assets/javascripts/bhf/classes/BrowserUpdate.js +185 -0
  71. data/vendor/assets/javascripts/bhf/classes/Datepicker.js +38 -0
  72. data/vendor/assets/javascripts/bhf/classes/MooEditable.js +1549 -0
  73. data/vendor/assets/javascripts/bhf/classes/MultipleFields.js +50 -0
  74. data/vendor/assets/javascripts/bhf_includes/wmd.js +1 -1
  75. data/vendor/assets/stylesheets/bhf/MooEditable.css.scss +176 -0
  76. data/vendor/assets/stylesheets/bhf/application.css.sass +1067 -0
  77. data/vendor/assets/stylesheets/bhf/functions.css.sass +137 -0
  78. data/vendor/assets/stylesheets/bhf/reset.css.sass +32 -0
  79. data/vendor/assets/stylesheets/bhf/typo.css.scss +62 -0
  80. metadata +24 -3
@@ -0,0 +1,25 @@
1
+ var ArrayFields = new Class({
2
+ version: 0.1,
3
+
4
+ // Implements: [Options, Events],
5
+
6
+ initialize: function(_object, _options) {
7
+ if ( ! _object) { return; }
8
+ // this.setOptions(_options);
9
+ var elem = _object;
10
+ var template = elem.getElement('input').clone().erase('value');
11
+ new Element('span.add_field', {text: '+'})
12
+ .inject(elem)
13
+ .addEvent('click', function(e){
14
+ template.clone().inject(e.target, 'before');
15
+ });
16
+
17
+ elem.getParent('form').addEvent('submit', function(){
18
+ elem.getElements('.array_fields').each(function(input){
19
+ if (input.value) { return; }
20
+ input.erase('name');
21
+ });
22
+ return true;
23
+ });
24
+ }
25
+ });
@@ -0,0 +1,185 @@
1
+ // browser-update.org notification script, <browser-update.org>
2
+ // Copyright (c) 2007-2009, MIT Style License <browser-update.org/LICENSE.txt>
3
+ var BrowserUpdate = new Class({
4
+ initialize: function(op, test) {
5
+ var b, jsv = 5, n = window.navigator;
6
+ this.op = op || {};
7
+ //options
8
+ this.op.l = op.l || n["language"] || n["userLanguage"] || document.documentElement.getAttribute("lang") || "en";
9
+ this.op.vsakt = {
10
+ i: 8,
11
+ f: 3.6,
12
+ o: 10.6,
13
+ s: 5,
14
+ n: 10
15
+ };
16
+ this.op.vsdefault = {
17
+ i: 6,
18
+ f: 2,
19
+ o: 9.64,
20
+ s: 3,
21
+ n: 10
22
+ };
23
+ this.op.vs = op.vs || this.op.vsdefault;
24
+ for (b in this.op.vsakt)
25
+ if (this.op.vs[b] >= this.op.vsakt[b])
26
+ this.op.vs[b] = this.op.vsdefault[b];
27
+
28
+ if (!op.reminder || op.reminder < 0.1)
29
+ this.op.reminder = 0;
30
+ else
31
+ this.op.reminder = op.reminder || 24;
32
+
33
+ this.op.onshow = op.onshow || function(o) {};
34
+ this.op.url = op.url || "http://browser-update.org/update.html";
35
+ this.op.pageurl = op.pageurl || window.location.hostname || "unknown";
36
+ this.op.newwindow = op.newwindow || false;
37
+
38
+ this.op.test = test || op.test || false;
39
+ if (window.location.hash == "#test-bu")
40
+ this.op.test = true;
41
+
42
+ function getBrowser() {
43
+ var n, v, t,
44
+ ua = navigator.userAgent,
45
+ names = {
46
+ i: 'Internet Explorer',
47
+ f: 'Firefox',
48
+ o: 'Opera',
49
+ s: 'Apple Safari',
50
+ n: 'Netscape Navigator',
51
+ c: 'Chrome',
52
+ x: 'Other'
53
+ };
54
+ if (/like firefox|chromeframe|seamonkey|opera mini|meego|netfront|moblin|maemo|arora|camino|flot|k-meleon|fennec|kazehakase|galeon|android|mobile|iphone|ipod|ipad|symbian|webos/i.test(ua)) n = "x";
55
+ else if (/MSIE (\d+\.\d+);/.test(ua)) n = "i";
56
+ else if (/Chrome.(\d+\.\d+)/i.test(ua)) n = "c";
57
+ else if (/Firefox.(\d+\.\d+)/i.test(ua)) n = "f";
58
+ else if (/Version.(\d+.\d+).{0,10}Safari/i.test(ua)) n = "s";
59
+ else if (/Safari.(\d+)/i.test(ua)) n = "so";
60
+ else if (/Opera.*Version.(\d+\.\d+)/i.test(ua)) n = "o";
61
+ else if (/Opera.(\d+\.\d+)/i.test(ua)) n = "o";
62
+ else if (/Netscape.(\d+)/i.test(ua)) n = "n";
63
+ else return {
64
+ n: "x",
65
+ v: 0,
66
+ t: names[n]
67
+ };
68
+ if (n == 'x') return {
69
+ n: 'x',
70
+ v: 0,
71
+ t: names[n]
72
+ };
73
+
74
+ v = new Number(RegExp.$1);
75
+ if (n == "so") {
76
+ v = ((v < 100) && 1.0) || ((v < 130) && 1.2) || ((v < 320) && 1.3) || ((v < 520) && 2.0) || ((v < 524) && 3.0) || ((v < 526) && 3.2) || 4.0;
77
+ n = "s";
78
+ }
79
+ if (n == "i" && v == 7 && window.XDomainRequest) {
80
+ v = 8;
81
+ }
82
+ return {
83
+ n: n,
84
+ v: v,
85
+ t: names[n] + " " + v
86
+ };
87
+ }
88
+
89
+ this.op.browser = getBrowser();
90
+ if (!this.op.test && (!this.op.browser || !this.op.browser.n || this.op.browser.n == "x" || this.op.browser.n == "c" || document.cookie.indexOf("browserupdateorg=pause") > -1 || this.op.browser.v > this.op.vs[this.op.browser.n])) {
91
+ return;
92
+ }
93
+ if (this.op.reminder > 0) {
94
+ var d = new Date(new Date().getTime() + 1000 * 3600 * this.op.reminder);
95
+ document.cookie = 'browserupdateorg=pause; expires=' + d.toGMTString() + '; path=/';
96
+ }
97
+ var ll = this.op.l.substr(0, 2);
98
+ var languages = "de,en";
99
+ if (languages.indexOf(ll) !== false)
100
+
101
+ this.op.url = "http://browser-update.org/" + ll + "/update.html#" + jsv;
102
+ var tar = "";
103
+ if (this.op.newwindow)
104
+ tar = ' target="_blank"';
105
+
106
+ function busprintf() {
107
+ var args = arguments;
108
+ var data = args[0];
109
+ for (var k = 1; k < args.length; ++k) {
110
+ data = data.replace(/%s/, args[k]);
111
+ }
112
+ return data;
113
+ }
114
+
115
+ ll = (window.Locale) ? Locale.getCurrent().name: 'en-US';
116
+
117
+ var t = 'Your browser (%s) is <b>out of date</b>. It has known <b>security flaws</b> and may <b>not display all features</b> of this and other websites. \
118
+ <a%s>Learn how to update your browser</a>';
119
+ if (ll == "de-DE")
120
+ t = 'Sie verwenden einen <b>veralteten Browser</b> (%s) mit <b>Sicherheitsschwachstellen</b> und <b>k&ouml;nnen nicht alle Funktionen dieser Webseite nutzen</b>. \
121
+ <a%s>Hier erfahren Sie, wie einfach Sie Ihren Browser aktualisieren k&ouml;nnen</a>.';
122
+ if (op.text)
123
+ t = op.text;
124
+
125
+ this.op.text = busprintf(t, this.op.browser.t, ' href="' + this.op.url + '"' + tar);
126
+
127
+ var div = document.createElement("div");
128
+ this.op.div = div;
129
+ div.id = "buorg";
130
+ div.className = "buorg";
131
+ div.innerHTML = '<div>' + this.op.text + '<div id="buorgclose">X</div></div>';
132
+
133
+ var sheet = document.createElement("style");
134
+ //sheet.setAttribute("type", "text/css");
135
+ var style = ".buorg {position:absolute;z-index:111111;\
136
+ width:100%; top:0px; left:0px; \
137
+ border-bottom:1px solid #A29330; \
138
+ background:#FDF2AB no-repeat 10px center url(http://browser-update.org/img/dialog-warning.gif);\
139
+ text-align:left; cursor:pointer; \
140
+ font-family: Arial,Helvetica,sans-serif; color:#000; font-size: 12px;}\
141
+ .buorg div { padding:5px 36px 5px 40px; } \
142
+ .buorg a,.buorg a:visited {color:#E25600; text-decoration: underline;}\
143
+ #buorgclose { position: absolute; right: .5em; top:.2em; height: 20px; width: 12px; font-weight: bold;font-size:14px; padding:0; }";
144
+ document.body.insertBefore(div, document.body.firstChild);
145
+ document.getElementsByTagName("head")[0].appendChild(sheet);
146
+ try {
147
+ sheet.innerText = style;
148
+ sheet.innerHTML = style;
149
+ }
150
+ catch(e) {
151
+ try {
152
+ sheet.styleSheet.cssText = style;
153
+ }
154
+ catch(e) {
155
+ return;
156
+ }
157
+ }
158
+ var me = this;
159
+ div.onclick = function() {
160
+ if (me.op.newwindow)
161
+ window.open(me.op.url, "_blank");
162
+ else
163
+ window.location.href = me.op.url;
164
+ return false;
165
+ };
166
+ div.getElementsByTagName("a")[0].onclick = function(e) {
167
+ var e = e || window.event;
168
+ if (e.stopPropagation) e.stopPropagation();
169
+ else e.cancelBubble = true;
170
+ return true;
171
+ };
172
+
173
+ this.op.bodymt = document.body.style.paddingTop;
174
+ document.body.style.paddingTop = (div.clientHeight) + "px";
175
+ document.getElementById("buorgclose").onclick = function(e) {
176
+ var e = e || window.event;
177
+ if (e.stopPropagation) e.stopPropagation();
178
+ else e.cancelBubble = true;
179
+ me.op.div.style.display = "none";
180
+ document.body.style.paddingTop = me.op.bodymt;
181
+ return true;
182
+ };
183
+ op.onshow(this.op);
184
+ }
185
+ });
@@ -0,0 +1,38 @@
1
+ // MonkeyPhysics: DatePicker
2
+ // this is a minified version, for production use
3
+ // source, updates and documentation available @ http://www.monkeyphysics.com/mootools
4
+
5
+ var DatePicker=new Class({Implements:Options,d:'',today:'',choice:{},bodysize:{},limit:{},attachTo:null,picker:null,slider:null,oldContents:null,newContents:null,input:null,visual:null,options:{pickerClass:'datepicker',days:['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'],months:['January','February','March','April','May','June','July','August','September','October','November','December'],dayShort:2,monthShort:3,startDay:1,timePicker:false,timePickerOnly:false,yearPicker:true,yearsPerPage:20,format:'d-m-Y',allowEmpty:false,inputOutputFormat:'U',animationDuration:400,useFadeInOut:!Browser.Engine.trident,startView:'month',positionOffset:{x:0,y:0},minDate:null,maxDate:null,debug:false,toggleElements:null,onShow:$empty,onClose:$empty,onSelect:$empty},initialize:function(attachTo,options){this.attachTo=attachTo;this.setOptions(options).attach();if(this.options.timePickerOnly){this.options.timePicker=true;this.options.startView='time';}
6
+ this.formatMinMaxDates();document.addEvent('mousedown',this.close.bind(this));},formatMinMaxDates:function(){if(this.options.minDate&&this.options.minDate.format){this.options.minDate=this.unformat(this.options.minDate.date,this.options.minDate.format);}
7
+ if(this.options.maxDate&&this.options.maxDate.format){this.options.maxDate=this.unformat(this.options.maxDate.date,this.options.maxDate.format);this.options.maxDate.setHours(23);this.options.maxDate.setMinutes(59);this.options.maxDate.setSeconds(59);}},attach:function(){if($chk(this.options.toggleElements)){var togglers=$$(this.options.toggleElements);document.addEvents({'keydown':function(e){if(e.key=="tab"){this.close(null,true);}}.bind(this)});};$$(this.attachTo).each(function(item,index){if(item.retrieve('datepicker'))return;if($chk(item.get('value'))){var init_clone_val=this.format(new Date(this.unformat(item.get('value'),this.options.inputOutputFormat)),this.options.format);}else if(!this.options.allowEmpty){var init_clone_val=this.format(new Date(),this.options.format);}else{var init_clone_val='';}
8
+ var display=item.getStyle('display');var clone=item.setStyle('display',this.options.debug?display:'none').store('datepicker',true).clone().store('datepicker',true).removeProperty('name').setStyle('display',display).set('value',init_clone_val).inject(item,'after');if($chk(this.options.toggleElements)){togglers[index].setStyle('cursor','pointer').addEvents({'click':function(e){this.onFocus(item,clone);}.bind(this)});clone.addEvents({'blur':function(){item.set('value',clone.get('value'));}});}else{clone.addEvents({'keydown':function(e){if(this.options.allowEmpty&&(e.key=="delete"||e.key=="backspace")){item.set('value','');e.target.set('value','');this.close(null,true);}else if(e.key=="tab"){this.close(null,true);}else{e.stop();}}.bind(this),'focus':function(e){this.onFocus(item,clone);}.bind(this)});}}.bind(this));},onFocus:function(original_input,visual_input){var init_visual_date,d=visual_input.getCoordinates();if($chk(original_input.get('value'))){init_visual_date=this.unformat(original_input.get('value'),this.options.inputOutputFormat).valueOf();}else{init_visual_date=new Date();if($chk(this.options.maxDate)&&init_visual_date.valueOf()>this.options.maxDate.valueOf()){init_visual_date=new Date(this.options.maxDate.valueOf());}
9
+ if($chk(this.options.minDate)&&init_visual_date.valueOf()<this.options.minDate.valueOf()){init_visual_date=new Date(this.options.minDate.valueOf());}}
10
+ this.show({left:d.left+this.options.positionOffset.x,top:d.top+d.height+this.options.positionOffset.y},init_visual_date);this.input=original_input;this.visual=visual_input;this.options.onShow();},dateToObject:function(d){return{year:d.getFullYear(),month:d.getMonth(),day:d.getDate(),hours:d.getHours(),minutes:d.getMinutes(),seconds:d.getSeconds()};},dateFromObject:function(values){var d=new Date();d.setDate(1);['year','month','day','hours','minutes','seconds'].each(function(type){var v=values[type];if(!$chk(v))return;switch(type){case'day':d.setDate(v);break;case'month':d.setMonth(v);break;case'year':d.setFullYear(v);break;case'hours':d.setHours(v);break;case'minutes':d.setMinutes(v);break;case'seconds':d.setSeconds(v);break;}});return d;},show:function(position,timestamp){this.formatMinMaxDates();if($chk(timestamp)){this.d=new Date(timestamp);}else{this.d=new Date();}
11
+ this.today=new Date();this.choice=this.dateToObject(this.d);this.mode=(this.options.startView=='time'&&!this.options.timePicker)?'month':this.options.startView;this.render();this.picker.setStyles(position);},render:function(fx){if(!$chk(this.picker)){this.constructPicker();}else{var o=this.oldContents;this.oldContents=this.newContents;this.newContents=o;this.newContents.empty();}
12
+ var startDate=new Date(this.d.getTime());this.limit={right:false,left:false};if(this.mode=='decades'){this.renderDecades();}else if(this.mode=='year'){this.renderYear();}else if(this.mode=='time'){this.renderTime();this.limit={right:true,left:true};}else{this.renderMonth();}
13
+ this.picker.getElement('.previous').setStyle('visibility',this.limit.left?'hidden':'visible');this.picker.getElement('.next').setStyle('visibility',this.limit.right?'hidden':'visible');this.picker.getElement('.titleText').setStyle('cursor',this.allowZoomOut()?'pointer':'default');this.d=startDate;if(this.picker.getStyle('opacity')==0){this.picker.tween('opacity',0,1);}
14
+ if($chk(fx))this.fx(fx);},fx:function(fx){if(fx=='right'){this.oldContents.setStyles({left:0,opacity:1});this.newContents.setStyles({left:this.bodysize.x,opacity:1});this.slider.setStyle('left',0).tween('left',0,-this.bodysize.x);}else if(fx=='left'){this.oldContents.setStyles({left:this.bodysize.x,opacity:1});this.newContents.setStyles({left:0,opacity:1});this.slider.setStyle('left',-this.bodysize.x).tween('left',-this.bodysize.x,0);}else if(fx=='fade'){this.slider.setStyle('left',0);this.oldContents.setStyle('left',0).set('tween',{duration:this.options.animationDuration/2}).tween('opacity',1,0);this.newContents.setStyles({opacity:0,left:0}).set('tween',{duration:this.options.animationDuration}).tween('opacity',0,1);}},constructPicker:function(){this.picker=new Element('div',{'class':this.options.pickerClass}).inject(document.body);if(this.options.useFadeInOut){this.picker.setStyle('opacity',0).set('tween',{duration:this.options.animationDuration});}
15
+ var h=new Element('div',{'class':'header'}).inject(this.picker);var titlecontainer=new Element('div',{'class':'title'}).inject(h);new Element('div',{'class':'previous'}).addEvent('click',this.previous.bind(this)).set('text','«').inject(h);new Element('div',{'class':'next'}).addEvent('click',this.next.bind(this)).set('text','»').inject(h);new Element('div',{'class':'closeButton'}).addEvent('click',this.close.bindWithEvent(this,true)).set('text','x').inject(h);new Element('span',{'class':'titleText'}).addEvent('click',this.zoomOut.bind(this)).inject(titlecontainer);var b=new Element('div',{'class':'body'}).inject(this.picker);this.bodysize=b.getSize();this.slider=new Element('div',{styles:{position:'absolute',top:0,left:0,width:2*this.bodysize.x,height:this.bodysize.y}}).set('tween',{duration:this.options.animationDuration,transition:Fx.Transitions.Quad.easeInOut}).inject(b);this.oldContents=new Element('div',{styles:{position:'absolute',top:0,left:this.bodysize.x,width:this.bodysize.x,height:this.bodysize.y}}).inject(this.slider);this.newContents=new Element('div',{styles:{position:'absolute',top:0,left:0,width:this.bodysize.x,height:this.bodysize.y}}).inject(this.slider);},renderTime:function(){var container=new Element('div',{'class':'time'}).inject(this.newContents);if(this.options.timePickerOnly){this.picker.getElement('.titleText').set('text','Select a time');}else{this.picker.getElement('.titleText').set('text',this.format(this.d,'j M, Y'));}
16
+ new Element('input',{type:'text','class':'hour'}).set('value',this.leadZero(this.d.getHours())).addEvents({mousewheel:function(e){var i=e.target,v=i.get('value').toInt();i.focus();if(e.wheel>0){v=(v<23)?v+1:0;}else{v=(v>0)?v-1:23;}
17
+ i.set('value',this.leadZero(v));e.stop();}.bind(this)}).set('maxlength',2).inject(container);new Element('input',{type:'text','class':'minutes'}).set('value',this.leadZero(this.d.getMinutes())).addEvents({mousewheel:function(e){var i=e.target,v=i.get('value').toInt();i.focus();if(e.wheel>0){v=(v<59)?v+1:0;}else{v=(v>0)?v-1:59;}
18
+ i.set('value',this.leadZero(v));e.stop();}.bind(this)}).set('maxlength',2).inject(container);new Element('div',{'class':'separator'}).set('text',':').inject(container);new Element('input',{type:'submit',value:'OK','class':'ok'}).addEvents({click:function(e){e.stop();this.select($merge(this.dateToObject(this.d),{hours:this.picker.getElement('.hour').get('value').toInt(),minutes:this.picker.getElement('.minutes').get('value').toInt()}));}.bind(this)}).set('maxlength',2).inject(container);},renderMonth:function(){var month=this.d.getMonth();this.picker.getElement('.titleText').set('text',this.options.months[month]+' '+this.d.getFullYear());this.d.setDate(1);while(this.d.getDay()!=this.options.startDay){this.d.setDate(this.d.getDate()-1);}
19
+ var container=new Element('div',{'class':'days'}).inject(this.newContents);var titles=new Element('div',{'class':'titles'}).inject(container);var d,i,classes,e,weekcontainer;for(d=this.options.startDay;d<(this.options.startDay+7);d++){new Element('div',{'class':'title day day'+(d%7)}).set('text',this.options.days[(d%7)].substring(0,this.options.dayShort)).inject(titles);}
20
+ var available=false;var t=this.today.toDateString();var currentChoice=this.dateFromObject(this.choice).toDateString();for(i=0;i<42;i++){classes=[];classes.push('day');classes.push('day'+this.d.getDay());if(this.d.toDateString()==t)classes.push('today');if(this.d.toDateString()==currentChoice)classes.push('selected');if(this.d.getMonth()!=month)classes.push('otherMonth');if(i%7==0){weekcontainer=new Element('div',{'class':'week week'+(Math.floor(i/7))}).inject(container);}
21
+ e=new Element('div',{'class':classes.join(' ')}).set('text',this.d.getDate()).inject(weekcontainer);if(this.limited('date')){e.addClass('unavailable');if(available){this.limit.right=true;}else if(this.d.getMonth()==month){this.limit.left=true;}}else{available=true;e.addEvent('click',function(e,d){if(this.options.timePicker){this.d.setDate(d.day);this.d.setMonth(d.month);this.mode='time';this.render('fade');}else{this.select(d);}}.bindWithEvent(this,{day:this.d.getDate(),month:this.d.getMonth(),year:this.d.getFullYear()}));}
22
+ this.d.setDate(this.d.getDate()+1);}
23
+ if(!available)this.limit.right=true;},renderYear:function(){var month=this.today.getMonth();var thisyear=this.d.getFullYear()==this.today.getFullYear();var selectedyear=this.d.getFullYear()==this.choice.year;this.picker.getElement('.titleText').set('text',this.d.getFullYear());this.d.setMonth(0);var i,e;var available=false;var container=new Element('div',{'class':'months'}).inject(this.newContents);for(i=0;i<=11;i++){e=new Element('div',{'class':'month month'+(i+1)+(i==month&&thisyear?' today':'')+(i==this.choice.month&&selectedyear?' selected':'')}).set('text',this.options.monthShort?this.options.months[i].substring(0,this.options.monthShort):this.options.months[i]).inject(container);if(this.limited('month')){e.addClass('unavailable');if(available){this.limit.right=true;}else{this.limit.left=true;}}else{available=true;e.addEvent('click',function(e,d){this.d.setDate(1);this.d.setMonth(d);this.mode='month';this.render('fade');}.bindWithEvent(this,i));}
24
+ this.d.setMonth(i);}
25
+ if(!available)this.limit.right=true;},renderDecades:function(){while(this.d.getFullYear()%this.options.yearsPerPage>0){this.d.setFullYear(this.d.getFullYear()-1);}
26
+ this.picker.getElement('.titleText').set('text',this.d.getFullYear()+'-'+(this.d.getFullYear()+this.options.yearsPerPage-1));var i,y,e;var available=false;var container=new Element('div',{'class':'years'}).inject(this.newContents);if($chk(this.options.minDate)&&this.d.getFullYear()<=this.options.minDate.getFullYear()){this.limit.left=true;}
27
+ for(i=0;i<this.options.yearsPerPage;i++){y=this.d.getFullYear();e=new Element('div',{'class':'year year'+i+(y==this.today.getFullYear()?' today':'')+(y==this.choice.year?' selected':'')}).set('text',y).inject(container);if(this.limited('year')){e.addClass('unavailable');if(available){this.limit.right=true;}else{this.limit.left=true;}}else{available=true;e.addEvent('click',function(e,d){this.d.setFullYear(d);this.mode='year';this.render('fade');}.bindWithEvent(this,y));}
28
+ this.d.setFullYear(this.d.getFullYear()+1);}
29
+ if(!available){this.limit.right=true;}
30
+ if($chk(this.options.maxDate)&&this.d.getFullYear()>=this.options.maxDate.getFullYear()){this.limit.right=true;}},limited:function(type){var cs=$chk(this.options.minDate);var ce=$chk(this.options.maxDate);if(!cs&&!ce)return false;switch(type){case'year':return(cs&&this.d.getFullYear()<this.options.minDate.getFullYear())||(ce&&this.d.getFullYear()>this.options.maxDate.getFullYear());case'month':var ms=(''+this.d.getFullYear()+this.leadZero(this.d.getMonth())).toInt();return cs&&ms<(''+this.options.minDate.getFullYear()+this.leadZero(this.options.minDate.getMonth())).toInt()||ce&&ms>(''+this.options.maxDate.getFullYear()+this.leadZero(this.options.maxDate.getMonth())).toInt()
31
+ case'date':return(cs&&this.d<this.options.minDate)||(ce&&this.d>this.options.maxDate);}},allowZoomOut:function(){if(this.mode=='time'&&this.options.timePickerOnly)return false;if(this.mode=='decades')return false;if(this.mode=='year'&&!this.options.yearPicker)return false;return true;},zoomOut:function(){if(!this.allowZoomOut())return;if(this.mode=='year'){this.mode='decades';}else if(this.mode=='time'){this.mode='month';}else{this.mode='year';}
32
+ this.render('fade');},previous:function(){if(this.mode=='decades'){this.d.setFullYear(this.d.getFullYear()-this.options.yearsPerPage);}else if(this.mode=='year'){this.d.setFullYear(this.d.getFullYear()-1);}else if(this.mode=='month'){this.d.setMonth(this.d.getMonth()-1);}
33
+ this.render('left');},next:function(){if(this.mode=='decades'){this.d.setFullYear(this.d.getFullYear()+this.options.yearsPerPage);}else if(this.mode=='year'){this.d.setFullYear(this.d.getFullYear()+1);}else if(this.mode=='month'){this.d.setMonth(this.d.getMonth()+1);}
34
+ this.render('right');},close:function(e,force){if(!$(this.picker))return;var clickOutside=($chk(e)&&e.target!=this.picker&&!this.picker.hasChild(e.target)&&e.target!=this.visual);if(force||clickOutside){if(this.options.useFadeInOut){this.picker.set('tween',{duration:this.options.animationDuration/2,onComplete:this.destroy.bind(this)}).tween('opacity',1,0);}else{this.destroy();}}},destroy:function(){this.picker.destroy();this.picker=null;this.options.onClose();},select:function(values){this.choice=$merge(this.choice,values);var d=this.dateFromObject(this.choice);this.input.set('value',this.format(d,this.options.inputOutputFormat));this.visual.set('value',this.format(d,this.options.format));this.options.onSelect(d);this.close(null,true);},leadZero:function(v){return v<10?'0'+v:v;},format:function(t,format){var f='';var h=t.getHours();var m=t.getMonth();for(var i=0;i<format.length;i++){switch(format.charAt(i)){case'\\':i++;f+=format.charAt(i);break;case'y':f+=(100+t.getYear()+'').substring(1);break
35
+ case'Y':f+=t.getFullYear();break;case'm':f+=this.leadZero(m+1);break;case'n':f+=(m+1);break;case'M':f+=this.options.months[m].substring(0,this.options.monthShort);break;case'F':f+=this.options.months[m];break;case'd':f+=this.leadZero(t.getDate());break;case'j':f+=t.getDate();break;case'D':f+=this.options.days[t.getDay()].substring(0,this.options.dayShort);break;case'l':f+=this.options.days[t.getDay()];break;case'G':f+=h;break;case'H':f+=this.leadZero(h);break;case'g':f+=(h%12?h%12:12);break;case'h':f+=this.leadZero(h%12?h%12:12);break;case'a':f+=(h>11?'pm':'am');break;case'A':f+=(h>11?'PM':'AM');break;case'i':f+=this.leadZero(t.getMinutes());break;case's':f+=this.leadZero(t.getSeconds());break;case'U':f+=Math.floor(t.valueOf()/1000);break;default:f+=format.charAt(i);}}
36
+ return f;},unformat:function(t,format){var d=new Date();var a={};var c,m;t=t.toString();for(var i=0;i<format.length;i++){c=format.charAt(i);switch(c){case'\\':r=null;i++;break;case'y':r='[0-9]{2}';break;case'Y':r='[0-9]{4}';break;case'm':r='0[1-9]|1[012]';break;case'n':r='[1-9]|1[012]';break;case'M':r='[A-Za-z]{'+this.options.monthShort+'}';break;case'F':r='[A-Za-z]+';break;case'd':r='0[1-9]|[12][0-9]|3[01]';break;case'j':r='[1-9]|[12][0-9]|3[01]';break;case'D':r='[A-Za-z]{'+this.options.dayShort+'}';break;case'l':r='[A-Za-z]+';break;case'G':case'H':case'g':case'h':r='[0-9]{1,2}';break;case'a':r='(am|pm)';break;case'A':r='(AM|PM)';break;case'i':case's':r='[012345][0-9]';break;case'U':r='-?[0-9]+$';break;default:r=null;}
37
+ if($chk(r)){m=t.match('^'+r);if($chk(m)){a[c]=m[0];t=t.substring(a[c].length);}else{if(this.options.debug)alert("Fatal Error in DatePicker\n\nUnexpected format at: '"+t+"' expected format character '"+c+"' (pattern '"+r+"')");return d;}}else{t=t.substring(1);}}
38
+ for(c in a){var v=a[c];switch(c){case'y':d.setFullYear(v<30?2000+v.toInt():1900+v.toInt());break;case'Y':d.setFullYear(v);break;case'm':case'n':d.setMonth(v-1);break;case'M':v=this.options.months.filter(function(item,index){return item.substring(0,this.options.monthShort)==v}.bind(this))[0];case'F':d.setMonth(this.options.months.indexOf(v));break;case'd':case'j':d.setDate(v);break;case'G':case'H':d.setHours(v);break;case'g':case'h':if(a['a']=='pm'||a['A']=='PM'){d.setHours(v==12?0:v.toInt()+12);}else{d.setHours(v);}break;case'i':d.setMinutes(v);break;case's':d.setSeconds(v);break;case'U':d=new Date(v.toInt()*1000);}};return d;}});
@@ -0,0 +1,1549 @@
1
+ /*
2
+ ---
3
+
4
+ name: MooEditable
5
+
6
+ description: Class for creating a WYSIWYG editor, for contentEditable-capable browsers.
7
+
8
+ license: MIT-style license
9
+
10
+ authors:
11
+ - Lim Chee Aun
12
+ - Radovan Lozej
13
+ - Ryan Mitchell
14
+ - Olivier Refalo
15
+ - T.J. Leahy
16
+
17
+ requires:
18
+ - Core/Class.Extras
19
+ - Core/Element.Event
20
+ - Core/Element.Dimensions
21
+
22
+ inspiration:
23
+ - Code inspired by Stefan's work [Safari Supports Content Editing!](http://www.xs4all.nl/~hhijdra/stefan/ContentEditable.html) from [safari gets contentEditable](http://walkah.net/blog/walkah/safari-gets-contenteditable)
24
+ - Main reference from Peter-Paul Koch's [execCommand compatibility](http://www.quirksmode.org/dom/execCommand.html)
25
+ - Some ideas and code inspired by [TinyMCE](http://tinymce.moxiecode.com/)
26
+ - Some functions inspired by Inviz's [Most tiny wysiwyg you ever seen](http://forum.mootools.net/viewtopic.php?id=746), [mooWyg (Most tiny WYSIWYG 2.0)](http://forum.mootools.net/viewtopic.php?id=5740)
27
+ - Some regex from Cameron Adams's [widgEditor](http://widgeditor.googlecode.com/)
28
+ - Some code from Juan M Martinez's [jwysiwyg](http://jwysiwyg.googlecode.com/)
29
+ - Some reference from MoxieForge's [PunyMCE](http://punymce.googlecode.com/)
30
+ - IE support referring Robert Bredlau's [Rich Text Editing](http://www.rbredlau.com/drupal/node/6)
31
+
32
+ provides: [MooEditable, MooEditable.Selection, MooEditable.UI, MooEditable.Actions]
33
+
34
+ ...
35
+ */
36
+
37
+ (function(){
38
+
39
+ var blockEls = /^(H[1-6]|HR|P|DIV|ADDRESS|PRE|FORM|TABLE|LI|OL|UL|TD|CAPTION|BLOCKQUOTE|CENTER|DL|DT|DD|SCRIPT|NOSCRIPT|STYLE)$/i;
40
+ var urlRegex = /^(https?|ftp|rmtp|mms):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(:(\d+))?\/?/i;
41
+ var protectRegex = /<(script|noscript|style)[\u0000-\uFFFF]*?<\/(script|noscript|style)>/g;
42
+
43
+ this.MooEditable = new Class({
44
+
45
+ Implements: [Events, Options],
46
+
47
+ options: {
48
+ toolbar: true,
49
+ cleanup: true,
50
+ paragraphise: true,
51
+ xhtml : true,
52
+ semantics : true,
53
+ actions: 'bold italic underline strikethrough | insertunorderedlist insertorderedlist indent outdent | undo redo | createlink unlink | urlimage | toggleview',
54
+ handleSubmit: true,
55
+ handleLabel: true,
56
+ disabled: false,
57
+ baseCSS: 'html{ height: 100%; cursor: text; } body{ font-family: sans-serif; }',
58
+ extraCSS: '',
59
+ externalCSS: '',
60
+ html: '<!DOCTYPE html><html><head><meta charset="UTF-8">{BASEHREF}<style>{BASECSS} {EXTRACSS}</style>{EXTERNALCSS}</head><body></body></html>',
61
+ rootElement: 'p',
62
+ baseURL: '',
63
+ dimensions: null
64
+ },
65
+
66
+ initialize: function(el, options){
67
+ this.setOptions(options);
68
+ this.textarea = document.id(el);
69
+ this.textarea.store('MooEditable', this);
70
+ this.actions = this.options.actions.clean().split(' ');
71
+ this.keys = {};
72
+ this.dialogs = {};
73
+ this.protectedElements = [];
74
+ this.actions.each(function(action){
75
+ var act = MooEditable.Actions[action];
76
+ if (!act) return;
77
+ if (act.options){
78
+ var key = act.options.shortcut;
79
+ if (key) this.keys[key] = action;
80
+ }
81
+ if (act.dialogs){
82
+ Object.each(act.dialogs, function(dialog, name){
83
+ dialog = dialog.attempt(this);
84
+ dialog.name = action + ':' + name;
85
+ if (typeOf(this.dialogs[action]) != 'object') this.dialogs[action] = {};
86
+ this.dialogs[action][name] = dialog;
87
+ }, this);
88
+ }
89
+ if (act.events){
90
+ Object.each(act.events, function(fn, event){
91
+ this.addEvent(event, fn);
92
+ }, this);
93
+ }
94
+ }.bind(this));
95
+ this.render();
96
+ },
97
+
98
+ toElement: function(){
99
+ return this.textarea;
100
+ },
101
+
102
+ render: function(){
103
+ var self = this;
104
+
105
+ // Dimensions
106
+ var dimensions = this.options.dimensions || this.textarea.getSize();
107
+
108
+ // Build the container
109
+ this.container = new Element('div', {
110
+ id: (this.textarea.id) ? this.textarea.id + '-mooeditable-container' : null,
111
+ 'class': 'mooeditable-container',
112
+ styles: {
113
+ width: dimensions.x
114
+ }
115
+ });
116
+
117
+ // Override all textarea styles
118
+ this.textarea.addClass('mooeditable-textarea').setStyle('height', dimensions.y);
119
+
120
+ // Build the iframe
121
+ this.iframe = new IFrame({
122
+ 'class': 'mooeditable-iframe',
123
+ frameBorder: 0,
124
+ src: 'javascript:""', // Workaround for HTTPs warning in IE6/7
125
+ styles: {
126
+ height: dimensions.y
127
+ }
128
+ });
129
+
130
+ this.toolbar = new MooEditable.UI.Toolbar({
131
+ onItemAction: function(){
132
+ var args = Array.from(arguments);
133
+ var item = args[0];
134
+ self.action(item.name, args);
135
+ }
136
+ });
137
+ this.attach.delay(1, this);
138
+
139
+ // Update the event for textarea's corresponding labels
140
+ if (this.options.handleLabel && this.textarea.id) $$('label[for="'+this.textarea.id+'"]').addEvent('click', function(e){
141
+ if (self.mode != 'iframe') return;
142
+ e.preventDefault();
143
+ self.focus();
144
+ });
145
+
146
+ // Update & cleanup content before submit
147
+ if (this.options.handleSubmit){
148
+ this.form = this.textarea.getParent('form');
149
+ if (!this.form) return;
150
+ this.form.addEvent('submit', function(){
151
+ if (self.mode == 'iframe') self.saveContent();
152
+ });
153
+ }
154
+
155
+ this.fireEvent('render', this);
156
+ },
157
+
158
+ attach: function(){
159
+ var self = this;
160
+
161
+ // Assign view mode
162
+ this.mode = 'iframe';
163
+
164
+ // Editor iframe state
165
+ this.editorDisabled = false;
166
+
167
+ // Put textarea inside container
168
+ this.container.wraps(this.textarea);
169
+
170
+ this.textarea.setStyle('display', 'none');
171
+
172
+ this.iframe.setStyle('display', '').inject(this.textarea, 'before');
173
+
174
+ Object.each(this.dialogs, function(action, name){
175
+ Object.each(action, function(dialog){
176
+ document.id(dialog).inject(self.iframe, 'before');
177
+ var range;
178
+ dialog.addEvents({
179
+ open: function(){
180
+ range = self.selection.getRange();
181
+ self.editorDisabled = true;
182
+ self.toolbar.disable(name);
183
+ self.fireEvent('dialogOpen', this);
184
+ },
185
+ close: function(){
186
+ self.toolbar.enable();
187
+ self.editorDisabled = false;
188
+ self.focus();
189
+ if (range) self.selection.setRange(range);
190
+ self.fireEvent('dialogClose', this);
191
+ }
192
+ });
193
+ });
194
+ });
195
+
196
+ // contentWindow and document references
197
+ this.win = this.iframe.contentWindow;
198
+ this.doc = this.win.document;
199
+
200
+ // Deal with weird quirks on Gecko
201
+ if (Browser.firefox) this.doc.designMode = 'On';
202
+
203
+ // Build the content of iframe
204
+ var docHTML = this.options.html.substitute({
205
+ BASECSS: this.options.baseCSS,
206
+ EXTRACSS: this.options.extraCSS,
207
+ EXTERNALCSS: (this.options.externalCSS) ? '<link rel="stylesheet" href="' + this.options.externalCSS + '">': '',
208
+ BASEHREF: (this.options.baseURL) ? '<base href="' + this.options.baseURL + '" />': ''
209
+ });
210
+ this.doc.open();
211
+ this.doc.write(docHTML);
212
+ this.doc.close();
213
+
214
+ // Turn on Design Mode
215
+ // IE fired load event twice if designMode is set
216
+ (Browser.ie) ? this.doc.body.contentEditable = true : this.doc.designMode = 'On';
217
+
218
+ // Mootoolize window, document and body
219
+ Object.append(this.win, new Window);
220
+ Object.append(this.doc, new Document);
221
+ if (Browser.Element){
222
+ var winElement = this.win.Element.prototype;
223
+ for (var method in Element){ // methods from Element generics
224
+ if (!method.test(/^[A-Z]|\$|prototype|mooEditable/)){
225
+ winElement[method] = Element.prototype[method];
226
+ }
227
+ }
228
+ } else {
229
+ document.id(this.doc.body);
230
+ }
231
+
232
+ this.setContent(this.textarea.get('value'));
233
+
234
+ // Bind all events
235
+ this.doc.addEvents({
236
+ mouseup: this.editorMouseUp.bind(this),
237
+ mousedown: this.editorMouseDown.bind(this),
238
+ mouseover: this.editorMouseOver.bind(this),
239
+ mouseout: this.editorMouseOut.bind(this),
240
+ mouseenter: this.editorMouseEnter.bind(this),
241
+ mouseleave: this.editorMouseLeave.bind(this),
242
+ contextmenu: this.editorContextMenu.bind(this),
243
+ click: this.editorClick.bind(this),
244
+ dblclick: this.editorDoubleClick.bind(this),
245
+ keypress: this.editorKeyPress.bind(this),
246
+ keyup: this.editorKeyUp.bind(this),
247
+ keydown: this.editorKeyDown.bind(this),
248
+ focus: this.editorFocus.bind(this),
249
+ blur: this.editorBlur.bind(this)
250
+ });
251
+ this.win.addEvents({
252
+ focus: this.editorFocus.bind(this),
253
+ blur: this.editorBlur.bind(this)
254
+ });
255
+ ['cut', 'copy', 'paste'].each(function(event){
256
+ self.doc.body.addListener(event, self['editor' + event.capitalize()].bind(self));
257
+ });
258
+ this.textarea.addEvent('keypress', this.textarea.retrieve('mooeditable:textareaKeyListener', this.keyListener.bind(this)));
259
+
260
+ // Fix window focus event not firing on Firefox 2
261
+ if (Browser.firefox2) this.doc.addEvent('focus', function(){
262
+ self.win.fireEvent('focus').focus();
263
+ });
264
+ // IE9 is also not firing focus event
265
+ if (this.doc.addEventListener) this.doc.addEventListener('focus', function(){
266
+ self.win.fireEvent('focus');
267
+ }, true);
268
+
269
+ // styleWithCSS, not supported in IE and Opera
270
+ if (!Browser.ie && !Browser.opera){
271
+ var styleCSS = function(){
272
+ self.execute('styleWithCSS', false, false);
273
+ self.doc.removeEvent('focus', styleCSS);
274
+ };
275
+ this.win.addEvent('focus', styleCSS);
276
+ }
277
+
278
+ if (this.options.toolbar){
279
+ document.id(this.toolbar).inject(this.container, 'top');
280
+ this.toolbar.render(this.actions);
281
+ }
282
+
283
+ if (this.options.disabled) this.disable();
284
+
285
+ this.selection = new MooEditable.Selection(this.win);
286
+
287
+ this.oldContent = this.getContent();
288
+
289
+ this.fireEvent('attach', this);
290
+
291
+ return this;
292
+ },
293
+
294
+ detach: function(){
295
+ this.saveContent();
296
+ this.textarea.setStyle('display', '').removeClass('mooeditable-textarea').inject(this.container, 'before');
297
+ this.textarea.removeEvent('keypress', this.textarea.retrieve('mooeditable:textareaKeyListener'));
298
+ this.container.dispose();
299
+ this.fireEvent('detach', this);
300
+ return this;
301
+ },
302
+
303
+ enable: function(){
304
+ this.editorDisabled = false;
305
+ this.toolbar.enable();
306
+ return this;
307
+ },
308
+
309
+ disable: function(){
310
+ this.editorDisabled = true;
311
+ this.toolbar.disable();
312
+ return this;
313
+ },
314
+
315
+ editorFocus: function(e){
316
+ this.oldContent = '';
317
+ this.fireEvent('editorFocus', [e, this]);
318
+ },
319
+
320
+ editorBlur: function(e){
321
+ this.oldContent = this.saveContent().getContent();
322
+ this.fireEvent('editorBlur', [e, this]);
323
+ },
324
+
325
+ editorMouseUp: function(e){
326
+ if (this.editorDisabled){
327
+ e.stop();
328
+ return;
329
+ }
330
+
331
+ if (this.options.toolbar) this.checkStates();
332
+
333
+ this.fireEvent('editorMouseUp', [e, this]);
334
+ },
335
+
336
+ editorMouseDown: function(e){
337
+ if (this.editorDisabled){
338
+ e.stop();
339
+ return;
340
+ }
341
+
342
+ this.fireEvent('editorMouseDown', [e, this]);
343
+ },
344
+
345
+ editorMouseOver: function(e){
346
+ if (this.editorDisabled){
347
+ e.stop();
348
+ return;
349
+ }
350
+
351
+ this.fireEvent('editorMouseOver', [e, this]);
352
+ },
353
+
354
+ editorMouseOut: function(e){
355
+ if (this.editorDisabled){
356
+ e.stop();
357
+ return;
358
+ }
359
+
360
+ this.fireEvent('editorMouseOut', [e, this]);
361
+ },
362
+
363
+ editorMouseEnter: function(e){
364
+ if (this.editorDisabled){
365
+ e.stop();
366
+ return;
367
+ }
368
+
369
+ if (this.oldContent && this.getContent() != this.oldContent){
370
+ this.focus();
371
+ this.fireEvent('editorPaste', [e, this]);
372
+ }
373
+
374
+ this.fireEvent('editorMouseEnter', [e, this]);
375
+ },
376
+
377
+ editorMouseLeave: function(e){
378
+ if (this.editorDisabled){
379
+ e.stop();
380
+ return;
381
+ }
382
+
383
+ this.fireEvent('editorMouseLeave', [e, this]);
384
+ },
385
+
386
+ editorContextMenu: function(e){
387
+ if (this.editorDisabled){
388
+ e.stop();
389
+ return;
390
+ }
391
+
392
+ this.fireEvent('editorContextMenu', [e, this]);
393
+ },
394
+
395
+ editorClick: function(e){
396
+ // make images selectable and draggable in Safari
397
+ if (Browser.safari || Browser.chrome){
398
+ var el = e.target;
399
+ if (Element.get(el, 'tag') == 'img'){
400
+
401
+ // safari doesnt like dragging locally linked images
402
+ if (this.options.baseURL){
403
+ if (el.getProperty('src').indexOf('http://') == -1){
404
+ el.setProperty('src', this.options.baseURL + el.getProperty('src'));
405
+ }
406
+ }
407
+
408
+ this.selection.selectNode(el);
409
+ this.checkStates();
410
+ }
411
+ }
412
+
413
+ this.fireEvent('editorClick', [e, this]);
414
+ },
415
+
416
+ editorDoubleClick: function(e){
417
+ this.fireEvent('editorDoubleClick', [e, this]);
418
+ },
419
+
420
+ editorKeyPress: function(e){
421
+ if (this.editorDisabled){
422
+ e.stop();
423
+ return;
424
+ }
425
+
426
+ this.keyListener(e);
427
+
428
+ this.fireEvent('editorKeyPress', [e, this]);
429
+ },
430
+
431
+ editorKeyUp: function(e){
432
+ if (this.editorDisabled){
433
+ e.stop();
434
+ return;
435
+ }
436
+
437
+ var c = e.code;
438
+ // 33-36 = pageup, pagedown, end, home; 45 = insert
439
+ if (this.options.toolbar && (/^enter|left|up|right|down|delete|backspace$/i.test(e.key) || (c >= 33 && c <= 36) || c == 45 || e.meta || e.control)){
440
+ if (Browser.ie6){ // Delay for less cpu usage when you are typing
441
+ clearTimeout(this.checkStatesDelay);
442
+ this.checkStatesDelay = this.checkStates.delay(500, this);
443
+ } else {
444
+ this.checkStates();
445
+ }
446
+ }
447
+
448
+ this.fireEvent('editorKeyUp', [e, this]);
449
+ },
450
+
451
+ editorKeyDown: function(e){
452
+ if (this.editorDisabled){
453
+ e.stop();
454
+ return;
455
+ }
456
+
457
+ if (e.key == 'enter'){
458
+ if (this.options.paragraphise){
459
+ if (e.shift && (Browser.safari || Browser.chrome)){
460
+ var s = this.selection;
461
+ var r = s.getRange();
462
+
463
+ // Insert BR element
464
+ var br = this.doc.createElement('br');
465
+ r.insertNode(br);
466
+
467
+ // Place caret after BR
468
+ r.setStartAfter(br);
469
+ r.setEndAfter(br);
470
+ s.setRange(r);
471
+
472
+ // Could not place caret after BR then insert an nbsp entity and move the caret
473
+ if (s.getSelection().focusNode == br.previousSibling){
474
+ var nbsp = this.doc.createTextNode('\u00a0');
475
+ var p = br.parentNode;
476
+ var ns = br.nextSibling;
477
+ (ns) ? p.insertBefore(nbsp, ns) : p.appendChild(nbsp);
478
+ s.selectNode(nbsp);
479
+ s.collapse(1);
480
+ }
481
+
482
+ // Scroll to new position, scrollIntoView can't be used due to bug: http://bugs.webkit.org/show_bug.cgi?id=16117
483
+ this.win.scrollTo(0, Element.getOffsets(s.getRange().startContainer).y);
484
+
485
+ e.preventDefault();
486
+ } else if (Browser.firefox || Browser.safari || Browser.chrome){
487
+ var node = this.selection.getNode();
488
+ var isBlock = Element.getParents(node).include(node).some(function(el){
489
+ return el.nodeName.test(blockEls);
490
+ });
491
+ if (!isBlock) this.execute('insertparagraph');
492
+ }
493
+ } else {
494
+ if (Browser.ie){
495
+ var r = this.selection.getRange();
496
+ var node = this.selection.getNode();
497
+ if (r && node.get('tag') != 'li'){
498
+ this.selection.insertContent('<br>');
499
+ this.selection.collapse(false);
500
+ }
501
+ e.preventDefault();
502
+ }
503
+ }
504
+ }
505
+
506
+ if (Browser.opera){
507
+ var ctrlmeta = e.control || e.meta;
508
+ if (ctrlmeta && e.key == 'x'){
509
+ this.fireEvent('editorCut', [e, this]);
510
+ } else if (ctrlmeta && e.key == 'c'){
511
+ this.fireEvent('editorCopy', [e, this]);
512
+ } else if ((ctrlmeta && e.key == 'v') || (e.shift && e.code == 45)){
513
+ this.fireEvent('editorPaste', [e, this]);
514
+ }
515
+ }
516
+
517
+ this.fireEvent('editorKeyDown', [e, this]);
518
+ },
519
+
520
+ editorCut: function(e){
521
+ if (this.editorDisabled){
522
+ e.stop();
523
+ return;
524
+ }
525
+
526
+ this.fireEvent('editorCut', [e, this]);
527
+ },
528
+
529
+ editorCopy: function(e){
530
+ if (this.editorDisabled){
531
+ e.stop();
532
+ return;
533
+ }
534
+
535
+ this.fireEvent('editorCopy', [e, this]);
536
+ },
537
+
538
+ editorPaste: function(e){
539
+ if (this.editorDisabled){
540
+ e.stop();
541
+ return;
542
+ }
543
+
544
+ this.fireEvent('editorPaste', [e, this]);
545
+ },
546
+
547
+ keyListener: function(e){
548
+ var key = (Browser.Platform.mac) ? e.meta : e.control;
549
+ if (!key || !this.keys[e.key]) return;
550
+ e.preventDefault();
551
+ var item = this.toolbar.getItem(this.keys[e.key]);
552
+ item.action(e);
553
+ },
554
+
555
+ focus: function(){
556
+ (this.mode == 'iframe' ? this.win : this.textarea).focus();
557
+ this.fireEvent('focus', this);
558
+ return this;
559
+ },
560
+
561
+ action: function(command, args){
562
+ var action = MooEditable.Actions[command];
563
+ if (action.command && typeOf(action.command) == 'function'){
564
+ action.command.apply(this, args);
565
+ } else {
566
+ this.focus();
567
+ this.execute(command, false, args);
568
+ if (this.mode == 'iframe') this.checkStates();
569
+ }
570
+ },
571
+
572
+ execute: function(command, param1, param2){
573
+ if (this.busy) return;
574
+ this.busy = true;
575
+ this.doc.execCommand(command, param1, param2);
576
+ this.saveContent();
577
+ this.busy = false;
578
+ return false;
579
+ },
580
+
581
+ toggleView: function(){
582
+ this.fireEvent('beforeToggleView', this);
583
+ if (this.mode == 'textarea'){
584
+ this.mode = 'iframe';
585
+ this.iframe.setStyle('display', '');
586
+ this.setContent(this.textarea.value);
587
+ this.textarea.setStyle('display', 'none');
588
+ } else {
589
+ this.saveContent();
590
+ this.mode = 'textarea';
591
+ this.textarea.setStyle('display', '');
592
+ this.iframe.setStyle('display', 'none');
593
+ }
594
+ this.fireEvent('toggleView', this);
595
+ this.focus.delay(10, this);
596
+ return this;
597
+ },
598
+
599
+ getContent: function(){
600
+ var protect = this.protectedElements;
601
+ var html = this.doc.body.get('html').replace(/<!-- mooeditable:protect:([0-9]+) -->/g, function(a, b){
602
+ return protect[b.toInt()];
603
+ });
604
+ return this.cleanup(this.ensureRootElement(html));
605
+ },
606
+
607
+ setContent: function(content){
608
+ var protect = this.protectedElements;
609
+ content = content.replace(protectRegex, function(a){
610
+ protect.push(a);
611
+ return '<!-- mooeditable:protect:' + (protect.length-1) + ' -->';
612
+ });
613
+ this.doc.body.set('html', this.ensureRootElement(content));
614
+ return this;
615
+ },
616
+
617
+ saveContent: function(){
618
+ if (this.mode == 'iframe'){
619
+ this.textarea.set('value', this.getContent());
620
+ }
621
+ return this;
622
+ },
623
+
624
+ ensureRootElement: function(val){
625
+ if (this.options.rootElement){
626
+ var el = new Element('div', {html: val.trim()});
627
+ var start = -1;
628
+ var create = false;
629
+ var html = '';
630
+ var length = el.childNodes.length;
631
+ for (var i=0; i<length; i++){
632
+ var childNode = el.childNodes[i];
633
+ var nodeName = childNode.nodeName;
634
+ if (!nodeName.test(blockEls) && nodeName !== '#comment'){
635
+ if (nodeName === '#text'){
636
+ if (childNode.nodeValue.trim()){
637
+ if (start < 0) start = i;
638
+ html += childNode.nodeValue;
639
+ }
640
+ } else {
641
+ if (start < 0) start = i;
642
+ html += new Element('div').adopt($(childNode).clone()).get('html');
643
+ }
644
+ } else {
645
+ create = true;
646
+ }
647
+ if (i == (length-1)) create = true;
648
+ if (start >= 0 && create){
649
+ var newel = new Element(this.options.rootElement, {html: html});
650
+ el.replaceChild(newel, el.childNodes[start]);
651
+ for (var k=start+1; k<i; k++){
652
+ el.removeChild(el.childNodes[k]);
653
+ length--;
654
+ i--;
655
+ k--;
656
+ }
657
+ start = -1;
658
+ create = false;
659
+ html = '';
660
+ }
661
+ }
662
+ val = el.get('html').replace(/\n\n/g, '');
663
+ }
664
+ return val;
665
+ },
666
+
667
+ checkStates: function(){
668
+ var element = this.selection.getNode();
669
+ if (!element) return;
670
+ if (typeOf(element) != 'element') return;
671
+
672
+ this.actions.each(function(action){
673
+ var item = this.toolbar.getItem(action);
674
+ if (!item) return;
675
+ item.deactivate();
676
+
677
+ var states = MooEditable.Actions[action]['states'];
678
+ if (!states) return;
679
+
680
+ // custom checkState
681
+ if (typeOf(states) == 'function'){
682
+ states.attempt([document.id(element), item], this);
683
+ return;
684
+ }
685
+
686
+ try{
687
+ if (this.doc.queryCommandState(action)){
688
+ item.activate();
689
+ return;
690
+ }
691
+ } catch(e){}
692
+
693
+ if (states.tags){
694
+ var el = element;
695
+ do {
696
+ var tag = el.tagName.toLowerCase();
697
+ if (states.tags.contains(tag)){
698
+ item.activate(tag);
699
+ break;
700
+ }
701
+ }
702
+ while ((el = Element.getParent(el)) != null);
703
+ }
704
+
705
+ if (states.css){
706
+ var el = element;
707
+ do {
708
+ var found = false;
709
+ for (var prop in states.css){
710
+ var css = states.css[prop];
711
+ if (el.style[prop.camelCase()].contains(css)){
712
+ item.activate(css);
713
+ found = true;
714
+ }
715
+ }
716
+ if (found || el.tagName.test(blockEls)) break;
717
+ }
718
+ while ((el = Element.getParent(el)) != null);
719
+ }
720
+ }.bind(this));
721
+ },
722
+
723
+ cleanup: function(source){
724
+ if (!this.options.cleanup) return source.trim();
725
+
726
+ do {
727
+ var oSource = source;
728
+
729
+ // replace base URL references: ie localize links
730
+ if (this.options.baseURL){
731
+ source = source.replace('="' + this.options.baseURL, '="');
732
+ }
733
+
734
+ // Webkit cleanup
735
+ source = source.replace(/<br class\="webkit-block-placeholder">/gi, "<br />");
736
+ source = source.replace(/<span class="Apple-style-span">(.*)<\/span>/gi, '$1');
737
+ source = source.replace(/ class="Apple-style-span"/gi, '');
738
+ source = source.replace(/<span style="">/gi, '');
739
+
740
+ // Remove padded paragraphs
741
+ source = source.replace(/<p>\s*<br ?\/?>\s*<\/p>/gi, '<p>\u00a0</p>');
742
+ source = source.replace(/<p>(&nbsp;|\s)*<\/p>/gi, '<p>\u00a0</p>');
743
+ if (!this.options.semantics){
744
+ source = source.replace(/\s*<br ?\/?>\s*<\/p>/gi, '</p>');
745
+ }
746
+
747
+ // Replace improper BRs (only if XHTML : true)
748
+ if (this.options.xhtml){
749
+ source = source.replace(/<br>/gi, "<br />");
750
+ }
751
+
752
+ if (this.options.semantics){
753
+ //remove divs from <li>
754
+ if (Browser.ie){
755
+ source = source.replace(/<li>\s*<div>(.+?)<\/div><\/li>/g, '<li>$1</li>');
756
+ }
757
+ //remove stupid apple divs
758
+ if (Browser.safari || Browser.chrome){
759
+ source = source.replace(/^([\w\s]+.*?)<div>/i, '<p>$1</p><div>');
760
+ source = source.replace(/<div>(.+?)<\/div>/ig, '<p>$1</p>');
761
+ }
762
+
763
+ //<p> tags around a list will get moved to after the list
764
+ if (!Browser.ie){
765
+ //not working properly in safari?
766
+ source = source.replace(/<p>[\s\n]*(<(?:ul|ol)>.*?<\/(?:ul|ol)>)(.*?)<\/p>/ig, '$1<p>$2</p>');
767
+ source = source.replace(/<\/(ol|ul)>\s*(?!<(?:p|ol|ul|img).*?>)((?:<[^>]*>)?\w.*)$/g, '</$1><p>$2</p>');
768
+ }
769
+
770
+ source = source.replace(/<br[^>]*><\/p>/g, '</p>'); // remove <br>'s that end a paragraph here.
771
+ source = source.replace(/<p>\s*(<img[^>]+>)\s*<\/p>/ig, '$1\n'); // if a <p> only contains <img>, remove the <p> tags
772
+
773
+ //format the source
774
+ source = source.replace(/<p([^>]*)>(.*?)<\/p>(?!\n)/g, '<p$1>$2</p>\n'); // break after paragraphs
775
+ source = source.replace(/<\/(ul|ol|p)>(?!\n)/g, '</$1>\n'); // break after </p></ol></ul> tags
776
+ source = source.replace(/><li>/g, '>\n\t<li>'); // break and indent <li>
777
+ source = source.replace(/([^\n])<\/(ol|ul)>/g, '$1\n</$2>'); //break before </ol></ul> tags
778
+ source = source.replace(/([^\n])<img/ig, '$1\n<img'); // move images to their own line
779
+ source = source.replace(/^\s*$/g, ''); // delete empty lines in the source code (not working in opera)
780
+ }
781
+
782
+ // Remove leading and trailing BRs
783
+ source = source.replace(/<br ?\/?>$/gi, '');
784
+ source = source.replace(/^<br ?\/?>/gi, '');
785
+
786
+ // Remove useless BRs
787
+ if (this.options.paragraphise) source = source.replace(/(h[1-6]|p|div|address|pre|li|ol|ul|blockquote|center|dl|dt|dd)><br ?\/?>/gi, '$1>');
788
+
789
+ // Remove BRs right before the end of blocks
790
+ source = source.replace(/<br ?\/?>\s*<\/(h1|h2|h3|h4|h5|h6|li|p)/gi, '</$1');
791
+
792
+ // Semantic conversion
793
+ source = source.replace(/<span style="font-weight: bold;">(.*)<\/span>/gi, '<strong>$1</strong>');
794
+ source = source.replace(/<span style="font-style: italic;">(.*)<\/span>/gi, '<em>$1</em>');
795
+ source = source.replace(/<b\b[^>]*>(.*?)<\/b[^>]*>/gi, '<strong>$1</strong>');
796
+ source = source.replace(/<i\b[^>]*>(.*?)<\/i[^>]*>/gi, '<em>$1</em>');
797
+ source = source.replace(/<u\b[^>]*>(.*?)<\/u[^>]*>/gi, '<span style="text-decoration: underline;">$1</span>');
798
+ source = source.replace(/<strong><span style="font-weight: normal;">(.*)<\/span><\/strong>/gi, '$1');
799
+ source = source.replace(/<em><span style="font-weight: normal;">(.*)<\/span><\/em>/gi, '$1');
800
+ source = source.replace(/<span style="text-decoration: underline;"><span style="font-weight: normal;">(.*)<\/span><\/span>/gi, '$1');
801
+ source = source.replace(/<strong style="font-weight: normal;">(.*)<\/strong>/gi, '$1');
802
+ source = source.replace(/<em style="font-weight: normal;">(.*)<\/em>/gi, '$1');
803
+
804
+ // Replace uppercase element names with lowercase
805
+ source = source.replace(/<[^> ]*/g, function(match){return match.toLowerCase();});
806
+
807
+ // Replace uppercase attribute names with lowercase
808
+ source = source.replace(/<[^>]*>/g, function(match){
809
+ match = match.replace(/ [^=]+=/g, function(match2){return match2.toLowerCase();});
810
+ return match;
811
+ });
812
+
813
+ // Put quotes around unquoted attributes
814
+ source = source.replace(/<[^!][^>]*>/g, function(match){
815
+ match = match.replace(/( [^=]+=)([^"][^ >]*)/g, "$1\"$2\"");
816
+ return match;
817
+ });
818
+
819
+ //make img tags xhtml compatible <img>,<img></img> -> <img/>
820
+ if (this.options.xhtml){
821
+ source = source.replace(/<img([^>]+)(\s*[^\/])>(<\/img>)*/gi, '<img$1$2 />');
822
+ }
823
+
824
+ //remove double <p> tags and empty <p> tags
825
+ source = source.replace(/<p>(?:\s*)<p>/g, '<p>');
826
+ source = source.replace(/<\/p>\s*<\/p>/g, '</p>');
827
+
828
+ // Replace <br>s inside <pre> automatically added by some browsers
829
+ source = source.replace(/<pre[^>]*>.*?<\/pre>/gi, function(match){
830
+ return match.replace(/<br ?\/?>/gi, '\n');
831
+ });
832
+
833
+ // Final trim
834
+ source = source.trim();
835
+ }
836
+ while (source != oSource);
837
+
838
+ return source;
839
+ }
840
+
841
+ });
842
+
843
+ MooEditable.Selection = new Class({
844
+
845
+ initialize: function(win){
846
+ this.win = win;
847
+ },
848
+
849
+ getSelection: function(){
850
+ this.win.focus();
851
+ return (this.win.getSelection) ? this.win.getSelection() : this.win.document.selection;
852
+ },
853
+
854
+ getRange: function(){
855
+ var s = this.getSelection();
856
+
857
+ if (!s) return null;
858
+
859
+ try {
860
+ return s.rangeCount > 0 ? s.getRangeAt(0) : (s.createRange ? s.createRange() : null);
861
+ } catch(e) {
862
+ // IE bug when used in frameset
863
+ return this.doc.body.createTextRange();
864
+ }
865
+ },
866
+
867
+ setRange: function(range){
868
+ if (range.select){
869
+ Function.attempt(function(){
870
+ range.select();
871
+ });
872
+ } else {
873
+ var s = this.getSelection();
874
+ if (s.addRange){
875
+ s.removeAllRanges();
876
+ s.addRange(range);
877
+ }
878
+ }
879
+ },
880
+
881
+ selectNode: function(node, collapse){
882
+ var r = this.getRange();
883
+ var s = this.getSelection();
884
+
885
+ if (r.moveToElementText){
886
+ Function.attempt(function(){
887
+ r.moveToElementText(node);
888
+ r.select();
889
+ });
890
+ } else if (s.addRange){
891
+ collapse ? r.selectNodeContents(node) : r.selectNode(node);
892
+ s.removeAllRanges();
893
+ s.addRange(r);
894
+ } else {
895
+ s.setBaseAndExtent(node, 0, node, 1);
896
+ }
897
+
898
+ return node;
899
+ },
900
+
901
+ isCollapsed: function(){
902
+ var r = this.getRange();
903
+ if (r.item) return false;
904
+ return r.boundingWidth == 0 || this.getSelection().isCollapsed;
905
+ },
906
+
907
+ collapse: function(toStart){
908
+ var r = this.getRange();
909
+ var s = this.getSelection();
910
+
911
+ if (r.select){
912
+ r.collapse(toStart);
913
+ r.select();
914
+ } else {
915
+ toStart ? s.collapseToStart() : s.collapseToEnd();
916
+ }
917
+ },
918
+
919
+ getContent: function(){
920
+ var r = this.getRange();
921
+ var body = new Element('body');
922
+
923
+ if (this.isCollapsed()) return '';
924
+
925
+ if (r.cloneContents){
926
+ body.appendChild(r.cloneContents());
927
+ } else if (r.item != undefined || r.htmlText != undefined){
928
+ body.set('html', r.item ? r.item(0).outerHTML : r.htmlText);
929
+ } else {
930
+ body.set('html', r.toString());
931
+ }
932
+
933
+ var content = body.get('html');
934
+ return content;
935
+ },
936
+
937
+ getText : function(){
938
+ var r = this.getRange();
939
+ var s = this.getSelection();
940
+ return this.isCollapsed() ? '' : r.text || (s.toString ? s.toString() : '');
941
+ },
942
+
943
+ getNode: function(){
944
+ var r = this.getRange();
945
+
946
+ if (!Browser.ie || Browser.version >= 9){
947
+ var el = null;
948
+
949
+ if (r){
950
+ el = r.commonAncestorContainer;
951
+
952
+ // Handle selection a image or other control like element such as anchors
953
+ if (!r.collapsed)
954
+ if (r.startContainer == r.endContainer)
955
+ if (r.startOffset - r.endOffset < 2)
956
+ if (r.startContainer.hasChildNodes())
957
+ el = r.startContainer.childNodes[r.startOffset];
958
+
959
+ while (typeOf(el) != 'element') el = el.parentNode;
960
+ }
961
+
962
+ return document.id(el);
963
+ }
964
+
965
+ return document.id(r.item ? r.item(0) : r.parentElement());
966
+ },
967
+
968
+ insertContent: function(content){
969
+ if (Browser.ie){
970
+ var r = this.getRange();
971
+ if (r.pasteHTML){
972
+ r.pasteHTML(content);
973
+ r.collapse(false);
974
+ r.select();
975
+ } else if (r.insertNode){
976
+ r.deleteContents();
977
+ if (r.createContextualFragment){
978
+ r.insertNode(r.createContextualFragment(content));
979
+ } else {
980
+ var doc = this.win.document;
981
+ var fragment = doc.createDocumentFragment();
982
+ var temp = doc.createElement('div');
983
+ fragment.appendChild(temp);
984
+ temp.outerHTML = content;
985
+ r.insertNode(fragment);
986
+ }
987
+ }
988
+ } else {
989
+ this.win.document.execCommand('insertHTML', false, content);
990
+ }
991
+ }
992
+
993
+ });
994
+
995
+ // Avoiding Locale dependency
996
+ // Wrapper functions to be used internally and for plugins, defaults to en-US
997
+ var phrases = {};
998
+ MooEditable.Locale = {
999
+
1000
+ define: function(key, value){
1001
+ if (typeOf(window.Locale) != 'null') return Locale.define('en-US', 'MooEditable', key, value);
1002
+ if (typeOf(key) == 'object') Object.merge(phrases, key);
1003
+ else phrases[key] = value;
1004
+ },
1005
+
1006
+ get: function(key){
1007
+ if (typeOf(window.Locale) != 'null') return Locale.get('MooEditable.' + key);
1008
+ return key ? phrases[key] : '';
1009
+ }
1010
+
1011
+ };
1012
+
1013
+ MooEditable.Locale.define({
1014
+ ok: 'OK',
1015
+ cancel: 'Cancel',
1016
+ bold: 'Bold',
1017
+ italic: 'Italic',
1018
+ underline: 'Underline',
1019
+ strikethrough: 'Strikethrough',
1020
+ unorderedList: 'Unordered List',
1021
+ orderedList: 'Ordered List',
1022
+ indent: 'Indent',
1023
+ outdent: 'Outdent',
1024
+ undo: 'Undo',
1025
+ redo: 'Redo',
1026
+ removeHyperlink: 'Remove Hyperlink',
1027
+ addHyperlink: 'Add Hyperlink',
1028
+ selectTextHyperlink: 'Please select the text you wish to hyperlink.',
1029
+ enterURL: 'Enter URL',
1030
+ enterImageURL: 'Enter image URL',
1031
+ addImage: 'Add Image',
1032
+ toggleView: 'Toggle View'
1033
+ });
1034
+
1035
+ MooEditable.UI = {};
1036
+
1037
+ MooEditable.UI.Toolbar= new Class({
1038
+
1039
+ Implements: [Events, Options],
1040
+
1041
+ options: {
1042
+ /*
1043
+ onItemAction: function(){},
1044
+ */
1045
+ 'class': ''
1046
+ },
1047
+
1048
+ initialize: function(options){
1049
+ this.setOptions(options);
1050
+ this.el = new Element('div', {'class': 'mooeditable-ui-toolbar ' + this.options['class']});
1051
+ this.items = {};
1052
+ this.content = null;
1053
+ },
1054
+
1055
+ toElement: function(){
1056
+ return this.el;
1057
+ },
1058
+
1059
+ render: function(actions){
1060
+ if (this.content){
1061
+ this.el.adopt(this.content);
1062
+ } else {
1063
+ this.content = actions.map(function(action){
1064
+ return (action == '|') ? this.addSeparator() : this.addItem(action);
1065
+ }.bind(this));
1066
+ }
1067
+ return this;
1068
+ },
1069
+
1070
+ addItem: function(action){
1071
+ var self = this;
1072
+ var act = MooEditable.Actions[action];
1073
+ if (!act) return;
1074
+ var type = act.type || 'button';
1075
+ var options = act.options || {};
1076
+ var item = new MooEditable.UI[type.camelCase().capitalize()](Object.append(options, {
1077
+ name: action,
1078
+ 'class': action + '-item toolbar-item',
1079
+ title: act.title,
1080
+ onAction: self.itemAction.bind(self)
1081
+ }));
1082
+ this.items[action] = item;
1083
+ document.id(item).inject(this.el);
1084
+ return item;
1085
+ },
1086
+
1087
+ getItem: function(action){
1088
+ return this.items[action];
1089
+ },
1090
+
1091
+ addSeparator: function(){
1092
+ return new Element('span', {'class': 'toolbar-separator'}).inject(this.el);
1093
+ },
1094
+
1095
+ itemAction: function(){
1096
+ this.fireEvent('itemAction', arguments);
1097
+ },
1098
+
1099
+ disable: function(except){
1100
+ Object.each(this.items, function(item){
1101
+ (item.name == except) ? item.activate() : item.deactivate().disable();
1102
+ });
1103
+ return this;
1104
+ },
1105
+
1106
+ enable: function(){
1107
+ Object.each(this.items, function(item){
1108
+ item.enable();
1109
+ });
1110
+ return this;
1111
+ },
1112
+
1113
+ show: function(){
1114
+ this.el.setStyle('display', '');
1115
+ return this;
1116
+ },
1117
+
1118
+ hide: function(){
1119
+ this.el.setStyle('display', 'none');
1120
+ return this;
1121
+ }
1122
+
1123
+ });
1124
+
1125
+ MooEditable.UI.Button = new Class({
1126
+
1127
+ Implements: [Events, Options],
1128
+
1129
+ options: {
1130
+ /*
1131
+ onAction: function(){},
1132
+ */
1133
+ title: '',
1134
+ name: '',
1135
+ text: 'Button',
1136
+ 'class': '',
1137
+ shortcut: '',
1138
+ mode: 'icon'
1139
+ },
1140
+
1141
+ initialize: function(options){
1142
+ this.setOptions(options);
1143
+ this.name = this.options.name;
1144
+ this.render();
1145
+ },
1146
+
1147
+ toElement: function(){
1148
+ return this.el;
1149
+ },
1150
+
1151
+ render: function(){
1152
+ var self = this;
1153
+ var key = (Browser.Platform.mac) ? 'Cmd' : 'Ctrl';
1154
+ var shortcut = (this.options.shortcut) ? ' ( ' + key + '+' + this.options.shortcut.toUpperCase() + ' )' : '';
1155
+ var text = this.options.title || name;
1156
+ var title = text + shortcut;
1157
+ this.el = new Element('button', {
1158
+ 'class': 'mooeditable-ui-button ' + self.options['class'],
1159
+ title: title,
1160
+ html: '<span class="button-icon"></span><span class="button-text">' + text + '</span>',
1161
+ events: {
1162
+ click: self.click.bind(self),
1163
+ mousedown: function(e){ e.preventDefault(); }
1164
+ }
1165
+ });
1166
+ if (this.options.mode != 'icon') this.el.addClass('mooeditable-ui-button-' + this.options.mode);
1167
+
1168
+ this.active = false;
1169
+ this.disabled = false;
1170
+
1171
+ // add hover effect for IE
1172
+ if (Browser.ie) this.el.addEvents({
1173
+ mouseenter: function(e){ this.addClass('hover'); },
1174
+ mouseleave: function(e){ this.removeClass('hover'); }
1175
+ });
1176
+
1177
+ return this;
1178
+ },
1179
+
1180
+ click: function(e){
1181
+ e.preventDefault();
1182
+ if (this.disabled) return;
1183
+ this.action(e);
1184
+ },
1185
+
1186
+ action: function(){
1187
+ this.fireEvent('action', [this].concat(Array.from(arguments)));
1188
+ },
1189
+
1190
+ enable: function(){
1191
+ if (this.active) this.el.removeClass('onActive');
1192
+ if (!this.disabled) return;
1193
+ this.disabled = false;
1194
+ this.el.removeClass('disabled').set({
1195
+ disabled: false,
1196
+ opacity: 1
1197
+ });
1198
+ return this;
1199
+ },
1200
+
1201
+ disable: function(){
1202
+ if (this.disabled) return;
1203
+ this.disabled = true;
1204
+ this.el.addClass('disabled').set({
1205
+ disabled: true,
1206
+ opacity: 0.4
1207
+ });
1208
+ return this;
1209
+ },
1210
+
1211
+ activate: function(){
1212
+ if (this.disabled) return;
1213
+ this.active = true;
1214
+ this.el.addClass('onActive');
1215
+ return this;
1216
+ },
1217
+
1218
+ deactivate: function(){
1219
+ this.active = false;
1220
+ this.el.removeClass('onActive');
1221
+ return this;
1222
+ }
1223
+
1224
+ });
1225
+
1226
+ MooEditable.UI.Dialog = new Class({
1227
+
1228
+ Implements: [Events, Options],
1229
+
1230
+ options:{
1231
+ /*
1232
+ onOpen: function(){},
1233
+ onClose: function(){},
1234
+ */
1235
+ 'class': '',
1236
+ contentClass: ''
1237
+ },
1238
+
1239
+ initialize: function(html, options){
1240
+ this.setOptions(options);
1241
+ this.html = html;
1242
+
1243
+ var self = this;
1244
+ this.el = new Element('div', {
1245
+ 'class': 'mooeditable-ui-dialog ' + self.options['class'],
1246
+ html: '<div class="dialog-content ' + self.options.contentClass + '">' + html + '</div>',
1247
+ styles: {
1248
+ 'display': 'none'
1249
+ },
1250
+ events: {
1251
+ click: self.click.bind(self)
1252
+ }
1253
+ });
1254
+ },
1255
+
1256
+ toElement: function(){
1257
+ return this.el;
1258
+ },
1259
+
1260
+ click: function(){
1261
+ this.fireEvent('click', arguments);
1262
+ return this;
1263
+ },
1264
+
1265
+ open: function(){
1266
+ this.el.setStyle('display', '');
1267
+ this.fireEvent('open', this);
1268
+ return this;
1269
+ },
1270
+
1271
+ close: function(){
1272
+ this.el.setStyle('display', 'none');
1273
+ this.fireEvent('close', this);
1274
+ return this;
1275
+ }
1276
+
1277
+ });
1278
+
1279
+ MooEditable.UI.AlertDialog = function(alertText){
1280
+ if (!alertText) return;
1281
+ var html = alertText + ' <button class="dialog-ok-button">' + MooEditable.Locale.get('ok') + '</button>';
1282
+ return new MooEditable.UI.Dialog(html, {
1283
+ 'class': 'mooeditable-alert-dialog',
1284
+ onOpen: function(){
1285
+ var button = this.el.getElement('.dialog-ok-button');
1286
+ (function(){
1287
+ button.focus();
1288
+ }).delay(10);
1289
+ },
1290
+ onClick: function(e){
1291
+ e.preventDefault();
1292
+ if (e.target.tagName.toLowerCase() != 'button') return;
1293
+ if (document.id(e.target).hasClass('dialog-ok-button')) this.close();
1294
+ }
1295
+ });
1296
+ };
1297
+
1298
+ MooEditable.UI.PromptDialog = function(questionText, answerText, fn){
1299
+ if (!questionText) return;
1300
+ var html = '<label class="dialog-label">' + questionText
1301
+ + ' <input type="text" class="text dialog-input" value="' + answerText + '">'
1302
+ + '</label> <button class="dialog-button dialog-ok-button">' + MooEditable.Locale.get('ok') + '</button>'
1303
+ + '<button class="dialog-button dialog-cancel-button">' + MooEditable.Locale.get('cancel') + '</button>';
1304
+ return new MooEditable.UI.Dialog(html, {
1305
+ 'class': 'mooeditable-prompt-dialog',
1306
+ onOpen: function(){
1307
+ var input = this.el.getElement('.dialog-input');
1308
+ (function(){
1309
+ input.focus();
1310
+ input.select();
1311
+ }).delay(10);
1312
+ },
1313
+ onClick: function(e){
1314
+ e.preventDefault();
1315
+ if (e.target.tagName.toLowerCase() != 'button') return;
1316
+ var button = document.id(e.target);
1317
+ var input = this.el.getElement('.dialog-input');
1318
+ if (button.hasClass('dialog-cancel-button')){
1319
+ input.set('value', answerText);
1320
+ this.close();
1321
+ } else if (button.hasClass('dialog-ok-button')){
1322
+ var answer = input.get('value');
1323
+ input.set('value', answerText);
1324
+ this.close();
1325
+ if (fn) fn.attempt(answer, this);
1326
+ }
1327
+ }
1328
+ });
1329
+ };
1330
+
1331
+ MooEditable.Actions = {
1332
+
1333
+ bold: {
1334
+ title: MooEditable.Locale.get('bold'),
1335
+ options: {
1336
+ shortcut: 'b'
1337
+ },
1338
+ states: {
1339
+ tags: ['b', 'strong'],
1340
+ css: {'font-weight': 'bold'}
1341
+ },
1342
+ events: {
1343
+ beforeToggleView: function(){
1344
+ if(Browser.firefox){
1345
+ var value = this.textarea.get('value');
1346
+ var newValue = value.replace(/<strong([^>]*)>/gi, '<b$1>').replace(/<\/strong>/gi, '</b>');
1347
+ if (value != newValue) this.textarea.set('value', newValue);
1348
+ }
1349
+ },
1350
+ attach: function(){
1351
+ if(Browser.firefox){
1352
+ var value = this.textarea.get('value');
1353
+ var newValue = value.replace(/<strong([^>]*)>/gi, '<b$1>').replace(/<\/strong>/gi, '</b>');
1354
+ if (value != newValue){
1355
+ this.textarea.set('value', newValue);
1356
+ this.setContent(newValue);
1357
+ }
1358
+ }
1359
+ }
1360
+ }
1361
+ },
1362
+
1363
+ italic: {
1364
+ title: MooEditable.Locale.get('italic'),
1365
+ options: {
1366
+ shortcut: 'i'
1367
+ },
1368
+ states: {
1369
+ tags: ['i', 'em'],
1370
+ css: {'font-style': 'italic'}
1371
+ },
1372
+ events: {
1373
+ beforeToggleView: function(){
1374
+ if (Browser.firefox){
1375
+ var value = this.textarea.get('value');
1376
+ var newValue = value.replace(/<embed([^>]*)>/gi, '<tmpembed$1>')
1377
+ .replace(/<em([^>]*)>/gi, '<i$1>')
1378
+ .replace(/<tmpembed([^>]*)>/gi, '<embed$1>')
1379
+ .replace(/<\/em>/gi, '</i>');
1380
+ if (value != newValue) this.textarea.set('value', newValue);
1381
+ }
1382
+ },
1383
+ attach: function(){
1384
+ if (Browser.firefox){
1385
+ var value = this.textarea.get('value');
1386
+ var newValue = value.replace(/<embed([^>]*)>/gi, '<tmpembed$1>')
1387
+ .replace(/<em([^>]*)>/gi, '<i$1>')
1388
+ .replace(/<tmpembed([^>]*)>/gi, '<embed$1>')
1389
+ .replace(/<\/em>/gi, '</i>');
1390
+ if (value != newValue){
1391
+ this.textarea.set('value', newValue);
1392
+ this.setContent(newValue);
1393
+ }
1394
+ }
1395
+ }
1396
+ }
1397
+ },
1398
+
1399
+ underline: {
1400
+ title: MooEditable.Locale.get('underline'),
1401
+ options: {
1402
+ shortcut: 'u'
1403
+ },
1404
+ states: {
1405
+ tags: ['u'],
1406
+ css: {'text-decoration': 'underline'}
1407
+ }
1408
+ },
1409
+
1410
+ strikethrough: {
1411
+ title: MooEditable.Locale.get('strikethrough'),
1412
+ options: {
1413
+ shortcut: 's'
1414
+ },
1415
+ states: {
1416
+ tags: ['s', 'strike'],
1417
+ css: {'text-decoration': 'line-through'}
1418
+ }
1419
+ },
1420
+
1421
+ insertunorderedlist: {
1422
+ title: MooEditable.Locale.get('unorderedList'),
1423
+ states: {
1424
+ tags: ['ul']
1425
+ }
1426
+ },
1427
+
1428
+ insertorderedlist: {
1429
+ title: MooEditable.Locale.get('orderedList'),
1430
+ states: {
1431
+ tags: ['ol']
1432
+ }
1433
+ },
1434
+
1435
+ indent: {
1436
+ title: MooEditable.Locale.get('indent'),
1437
+ states: {
1438
+ tags: ['blockquote']
1439
+ }
1440
+ },
1441
+
1442
+ outdent: {
1443
+ title: MooEditable.Locale.get('outdent')
1444
+ },
1445
+
1446
+ undo: {
1447
+ title: MooEditable.Locale.get('undo'),
1448
+ options: {
1449
+ shortcut: 'z'
1450
+ }
1451
+ },
1452
+
1453
+ redo: {
1454
+ title: MooEditable.Locale.get('redo'),
1455
+ options: {
1456
+ shortcut: 'y'
1457
+ }
1458
+ },
1459
+
1460
+ unlink: {
1461
+ title: MooEditable.Locale.get('removeHyperlink')
1462
+ },
1463
+
1464
+ createlink: {
1465
+ title: MooEditable.Locale.get('addHyperlink'),
1466
+ options: {
1467
+ shortcut: 'l'
1468
+ },
1469
+ states: {
1470
+ tags: ['a']
1471
+ },
1472
+ dialogs: {
1473
+ alert: MooEditable.UI.AlertDialog.pass(MooEditable.Locale.get('selectTextHyperlink')),
1474
+ prompt: function(editor){
1475
+ return MooEditable.UI.PromptDialog(MooEditable.Locale.get('enterURL'), 'http://', function(url){
1476
+ editor.execute('createlink', false, url.trim());
1477
+ });
1478
+ }
1479
+ },
1480
+ command: function(){
1481
+ var selection = this.selection;
1482
+ var dialogs = this.dialogs.createlink;
1483
+ if (selection.isCollapsed()){
1484
+ var node = selection.getNode();
1485
+ if (node.get('tag') == 'a' && node.get('href')){
1486
+ selection.selectNode(node);
1487
+ var prompt = dialogs.prompt;
1488
+ prompt.el.getElement('.dialog-input').set('value', node.get('href'));
1489
+ prompt.open();
1490
+ } else {
1491
+ dialogs.alert.open();
1492
+ }
1493
+ } else {
1494
+ var text = selection.getText();
1495
+ var prompt = dialogs.prompt;
1496
+ if (urlRegex.test(text)) prompt.el.getElement('.dialog-input').set('value', text);
1497
+ prompt.open();
1498
+ }
1499
+ }
1500
+ },
1501
+
1502
+ urlimage: {
1503
+ title: MooEditable.Locale.get('addImage'),
1504
+ options: {
1505
+ shortcut: 'm'
1506
+ },
1507
+ dialogs: {
1508
+ prompt: function(editor){
1509
+ return MooEditable.UI.PromptDialog(MooEditable.Locale.get('enterImageURL'), 'http://', function(url){
1510
+ editor.execute('insertimage', false, url.trim());
1511
+ });
1512
+ }
1513
+ },
1514
+ command: function(){
1515
+ this.dialogs.urlimage.prompt.open();
1516
+ }
1517
+ },
1518
+
1519
+ toggleview: {
1520
+ title: MooEditable.Locale.get('toggleView'),
1521
+ command: function(){
1522
+ (this.mode == 'textarea') ? this.toolbar.enable() : this.toolbar.disable('toggleview');
1523
+ this.toggleView();
1524
+ }
1525
+ }
1526
+
1527
+ };
1528
+
1529
+ MooEditable.Actions.Settings = {};
1530
+
1531
+ Element.Properties.mooeditable = {
1532
+
1533
+ get: function(){
1534
+ return this.retrieve('MooEditable');
1535
+ }
1536
+
1537
+ };
1538
+
1539
+ Element.implement({
1540
+
1541
+ mooEditable: function(options){
1542
+ var mooeditable = this.get('mooeditable');
1543
+ if (!mooeditable) mooeditable = new MooEditable(this, options);
1544
+ return mooeditable;
1545
+ }
1546
+
1547
+ });
1548
+
1549
+ })();