bootswatch_rails 3.2.0.4 → 3.2.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,11 @@
1
+ /*!
2
+ CLEditor WYSIWYG HTML Editor v1.4.5
3
+ http://premiumsoftware.net/CLEditor
4
+ requires jQuery v1.4.2 or later
5
+ Copyright 2010, Chris Landowski, Premium Software, LLC
6
+ Dual licensed under the MIT or GPL Version 2 licenses.
7
+ */
8
+ (function(n){function vi(t){var i=this,e=t.target,y=n.data(e,a),p=s[y],w=p.popupName,k=f[w],v,b;if(!i.disabled&&n(e).attr(r)!==r){if(v={editor:i,button:e,buttonName:y,popup:k,popupName:w,command:p.command,useCSS:i.options.useCSS},p.buttonClick&&p.buttonClick(t,v)===!1)return!1;if(y==="source")l(i)?(delete i.range,i.$area.hide(),i.$frame.show(),e.title=p.title):(i.$frame.hide(),i.$area.show(),e.title="Show Rich Text");else if(!l(i)){if(w){if(b=n(k),w==="url"){if(y==="link"&&ri(i)==="")return ut(i,"A selection is required when inserting a link.",e),!1;b.children(":button").unbind(u).bind(u,function(){var t=b.find(":text"),r=n.trim(t.val());r!==""&&h(i,v.command,r,null,v.button);t.val("http://");o();c(i)})}else w==="pastetext"&&b.children(":button").unbind(u).bind(u,function(){var n=b.find("textarea"),t=n.val().replace(/\n/g,"<br />");t!==""&&h(i,v.command,t,null,v.button);n.val("");o();c(i)});return e!==n.data(k,d)?(ui(i,k,e),!1):void 0}if(y==="print")i.$frame[0].contentWindow.print();else if(!h(i,v.command,v.value,v.useCSS,e))return!1}c(i)}}function kt(t){var i=n(t.target).closest("div");i.css(et,i.data(a)?"#FFF":"#FFC")}function dt(t){n(t.target).closest("div").css(et,"transparent")}function yi(i){var v=this,y=i.data.popup,r=i.target,l;if(y!==f.msg&&!n(y).hasClass(tt)){var w=n.data(y,d),u=n.data(w,a),p=s[u],b=p.command,e,k=v.options.useCSS;if(u==="font"?e=r.style.fontFamily.replace(/"/g,""):u==="size"?(r.tagName.toUpperCase()==="DIV"&&(r=r.children[0]),e=r.innerHTML):u==="style"?e="<"+r.tagName+">":u==="color"?e=ti(r.style.backgroundColor):u==="highlight"&&(e=ti(r.style.backgroundColor),t?b="backcolor":k=!0),l={editor:v,button:w,buttonName:u,popup:y,popupName:p.popupName,command:b,value:e,useCSS:k},!p.popupClick||p.popupClick(i,l)!==!1){if(l.command&&!h(v,l.command,l.value,l.useCSS,w))return!1;o();c(v)}}}function it(n){for(var t=1,i=0,r=0;r<n.length;++r)t=(t+n.charCodeAt(r))%65521,i=(i+t)%65521;return i<<16|t}function pi(n){n.$area.val("");ft(n)}function gt(r,u,e,o,s){var h,c;return f[r]?f[r]:(h=n(i).hide().addClass(si).appendTo("body"),o?h.html(o):r==="color"?(c=u.colors.split(" "),c.length<10&&h.width("auto"),n.each(c,function(t,r){n(i).appendTo(h).css(et,"#"+r)}),e=hi):r==="font"?n.each(u.fonts.split(","),function(t,r){n(i).appendTo(h).css("fontFamily",r).html(r)}):r==="size"?n.each(u.sizes.split(","),function(t,r){n(i).appendTo(h).html('<font size="'+r+'">'+r+"<\/font>")}):r==="style"?n.each(u.styles,function(t,r){n(i).appendTo(h).html(r[1]+r[0]+r[1].replace("<","<\/"))}):r==="url"?(h.html('<label>Enter URL:<br /><input type="text" value="http://" style="width:200px" /><\/label><br /><input type="button" value="Submit" />'),e=tt):r==="pastetext"&&(h.html('<label>Paste your content here:<br /><textarea rows="3" style="width:200px"><\/textarea><\/label><br /><input type="button" value="Submit" />'),e=tt),e||o||(e=pt),h.addClass(e),t&&h.attr(ot,"on").find("div,font,p,h1,h2,h3,h4,h5,h6").attr(ot,"on"),(h.hasClass(pt)||s===!0)&&h.children().hover(kt,dt),f[r]=h[0],h[0])}function ni(n,i){i?(n.$area.attr(r,r),n.disabled=!0):(n.$area.removeAttr(r),delete n.disabled);try{t?n.doc.body.contentEditable=!i:n.doc.designMode=i?"off":"on"}catch(u){}b(n)}function h(n,i,r,u,f){var c,h,o,s,l;if(rt(n),t||((u===undefined||u===null)&&(u=n.options.useCSS),n.doc.execCommand("styleWithCSS",0,u.toString())),c=i.toLowerCase()==="inserthtml",t&&c)w(n).pasteHTML(r);else if(y&&c)h=e(n),o=h.getRangeAt(0),o.deleteContents(),o.insertNode(o.createContextualFragment(r)),h.removeAllRanges(),h.addRange(o);else{s=!0;try{s=n.doc.execCommand(i,0,r||null)}catch(a){l=a.message;s=!1}s||("cutcopypaste".indexOf(i)>-1?ut(n,"For security reasons, your browser does not support the "+i+" command. Try using the keyboard shortcut or context menu instead.",f):ut(n,l?l:"Error executing the "+i+" command.",f))}return b(n),ct(n,!0),s}function c(n){setTimeout(function(){l(n)?n.$area.focus():n.$frame[0].contentWindow.focus();b(n)},0)}function w(n){return t?e(n).createRange():e(n).getRangeAt(0)}function e(n){return t?n.doc.selection:n.$frame[0].contentWindow.getSelection()}function ti(n){var i=/rgba?\((\d+), (\d+), (\d+)/.exec(n),t;if(i){for(n=(i[1]<<16|i[2]<<8|i[3]).toString(16);n.length<6;)n="0"+n;return"#"+n}return(t=n.split(""),n.length===4)?"#"+t[1]+t[1]+t[2]+t[2]+t[3]+t[3]:n}function o(){n.each(f,function(t,i){n(i).hide().unbind(u).removeData(d)})}function ii(){var t=n("link[href*=cleditor]").attr("href");return t.replace(/^(.*\/)[^\/]+$/,"$1")+"images/"}function wi(n){return"url("+ii()+n+")"}function ht(i){var s=i.$main,r=i.options;i.$frame&&i.$frame.remove();var u=i.$frame=n('<iframe frameborder="0" src="javascript:true;" />').hide().appendTo(s),l=u[0].contentWindow,f=i.doc=l.document,h=n(f);f.open();f.write(r.docType+"<html>"+(r.docCSSFile===""?"":'<head><link rel="stylesheet" type="text/css" href="'+r.docCSSFile+'" /><\/head>')+'<body style="'+r.bodyStyle+'"><\/body><\/html>');f.close();(t||y)&&h.click(function(){c(i)});ft(i);t||y?(h.bind("beforedeactivate beforeactivate selectionchange keypress keyup",function(n){if(n.type==="beforedeactivate")i.inactive=!0;else if(n.type==="beforeactivate")!i.inactive&&i.range&&i.range.length>1&&i.range.shift(),delete i.inactive;else if(!i.inactive)for(i.range||(i.range=[]),i.range.unshift(w(i));i.range.length>2;)i.range.pop()}),u.focus(function(){rt(i);n(i).triggerHandler(nt)}),u.blur(function(){n(i).triggerHandler(k)})):n(u[0].contentWindow).focus(function(){n(i).triggerHandler(nt)}).blur(function(){n(i).triggerHandler(k)});h.click(o).keydown(function(n){t&&e(i).type=="Control"&&n.keyCode==8&&(e(i).clear(),n.preventDefault())}).bind("keyup mouseup",function(){b(i);ct(i,!0)});st?i.$area.show():u.show();n(function(){var t=i.$toolbar,f=t.children("div:last"),e=s.width(),n=f.offset().top+f.outerHeight()-t.offset().top+1;t.height(n);n=(/%/.test(""+r.height)?s.height():parseInt(r.height,10))-n;u.width(e).height(n);i.$area.width(e).height(li?n-2:n);ni(i,i.disabled);b(i)})}function b(i){var u,e;st||!ai||i.focused||(i.$frame[0].contentWindow.focus(),window.focus(),i.focused=!0);u=i.doc;t&&(u=w(i));e=l(i);n.each(i.$toolbar.find("."+vt),function(o,s){var v=n(s),h=n.cleditor.buttons[n.data(s,a)],c=h.command,l=!0,p;if(i.disabled)l=!1;else if(h.getEnabled)p={editor:i,button:s,buttonName:h.name,popup:f[h.popupName],popupName:h.popupName,command:h.command,useCSS:i.options.useCSS},l=h.getEnabled(p),l===undefined&&(l=!0);else if((e||st)&&h.name!=="source"||t&&(c==="undo"||c==="redo"))l=!1;else if(c&&c!=="print"&&(t&&c==="hilitecolor"&&(c="backcolor"),!t&&!y||c!=="inserthtml"))try{l=u.queryCommandEnabled(c)}catch(w){l=!1}l?(v.removeClass(yt),v.removeAttr(r)):(v.addClass(yt),v.attr(r,r))})}function rt(n){n.range&&(t?n.range[0].select():y&&e(n).addRange(n.range[0]))}function bi(n){setTimeout(function(){l(n)?n.$area.select():h(n,"selectall")},0)}function ki(i){var u,r,f;return(rt(i),u=w(i),t)?u.htmlText:(r=n("<layer>")[0],r.appendChild(u.cloneContents()),f=r.innerHTML,r=null,f)}function ri(n){return(rt(n),t)?w(n).text:e(n).toString()}function ut(n,t,i){var r=gt("msg",n.options,ci);r.innerHTML=t;ui(n,r,i)}function ui(t,i,r){var f,h,c,e=n(i),l,s;r?(l=n(r),f=l.offset(),h=--f.left,c=f.top+l.height()):(s=t.$toolbar,f=s.offset(),h=Math.floor((s.width()-e.width())/2)+f.left,c=f.top+s.height()-2);o();e.css({left:h,top:c}).show();r&&(n.data(i,d,r),e.bind(u,{popup:i},n.proxy(yi,t)));setTimeout(function(){e.find(":text,textarea").eq(0).focus().select()},100)}function l(n){return n.$area.is(":visible")}function ft(t,i){var u=t.$area.val(),o=t.options,f=o.updateFrame,s=n(t.doc.body),e,r;if(f){if(e=it(u),i&&t.areaChecksum===e)return;t.areaChecksum=e}r=f?f(u):u;r=r.replace(/<(?=\/?script)/ig,"&lt;");o.updateTextArea&&(t.frameChecksum=it(r));r!==s.html()&&(s.html(r),n(t).triggerHandler(g))}function ct(t,i){var u=n(t.doc.body).html(),o=t.options,f=o.updateTextArea,s=t.$area,e,r;if(f){if(e=it(u),i&&t.frameChecksum===e)return;t.frameChecksum=e}r=f?f(u):u;o.updateFrame&&(t.areaChecksum=it(r));r!==s.val()&&(s.val(r),n(t).triggerHandler(g))}var p,bt;n.cleditor={defaultOptions:{width:"auto",height:250,controls:"bold italic underline strikethrough subscript superscript | font size style | color highlight removeformat | bullets numbering | outdent indent | alignleft center alignright justify | undo redo | rule image link unlink | cut copy paste pastetext | print source",colors:"FFF FCC FC9 FF9 FFC 9F9 9FF CFF CCF FCF CCC F66 F96 FF6 FF3 6F9 3FF 6FF 99F F9F BBB F00 F90 FC6 FF0 3F3 6CC 3CF 66C C6C 999 C00 F60 FC3 FC0 3C0 0CC 36F 63F C3C 666 900 C60 C93 990 090 399 33F 60C 939 333 600 930 963 660 060 366 009 339 636 000 300 630 633 330 030 033 006 309 303",fonts:"Arial,Arial Black,Comic Sans MS,Courier New,Narrow,Garamond,Georgia,Impact,Sans Serif,Serif,Tahoma,Trebuchet MS,Verdana",sizes:"1,2,3,4,5,6,7",styles:[["Paragraph","<p>"],["Header 1","<h1>"],["Header 2","<h2>"],["Header 3","<h3>"],["Header 4","<h4>"],["Header 5","<h5>"],["Header 6","<h6>"]],useCSS:!0,docType:'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">',docCSSFile:"",bodyStyle:"margin:4px; font:10pt Arial,Verdana; cursor:text"},buttons:{init:"bold,,|italic,,|underline,,|strikethrough,,|subscript,,|superscript,,|font,,fontname,|size,Font Size,fontsize,|style,,formatblock,|color,Font Color,forecolor,|highlight,Text Highlight Color,hilitecolor,color|removeformat,Remove Formatting,|bullets,,insertunorderedlist|numbering,,insertorderedlist|outdent,,|indent,,|alignleft,Align Text Left,justifyleft|center,,justifycenter|alignright,Align Text Right,justifyright|justify,,justifyfull|undo,,|redo,,|rule,Insert Horizontal Rule,inserthorizontalrule|image,Insert Image,insertimage,url|link,Insert Hyperlink,createlink,url|unlink,Remove Hyperlink,|cut,,|copy,,|paste,,|pastetext,Paste as Text,inserthtml,|print,,|source,Show Source"},imagesPath:function(){return ii()}};n.fn.cleditor=function(t){var i=n([]);return this.each(function(r,u){if(u.tagName.toUpperCase()==="TEXTAREA"){var f=n.data(u,lt);f||(f=new cleditor(u,t));i=i.add(f)}}),i};var et="backgroundColor",k="blurred",d="button",a="buttonName",g="change",lt="cleditor",u="click",r="disabled",i="<div>",nt="focused",ot="unselectable",fi="cleditorMain",ei="cleditorToolbar",at="cleditorGroup",vt="cleditorButton",yt="cleditorDisabled",oi="cleditorDivider",si="cleditorPopup",pt="cleditorList",hi="cleditorColor",tt="cleditorPrompt",ci="cleditorMsg",v=navigator.userAgent.toLowerCase(),t=/msie/.test(v),li=/msie\s6/.test(v),y=/(trident)(?:.*rv:([\w.]+))?/.test(v),ai=/webkit/.test(v),st=/iPhone|iPad|iPod/i.test(v),f={},wt,s=n.cleditor.buttons;n.each(s.init.split("|"),function(n,t){var i=t.split(","),r=i[0];s[r]={stripIndex:n,name:r,title:i[1]===""?r.charAt(0).toUpperCase()+r.substr(1):i[1],command:i[2]===""?r:i[2],popupName:i[3]===""?r:i[3]}});delete s.init;cleditor=function(r,f){var e=this;e.options=f=n.extend({},n.cleditor.defaultOptions,f);var l=e.$area=n(r).css({border:"none",margin:0,padding:0}).hide().data(lt,e).blur(function(){ft(e,!0)}),v=e.$main=n(i).addClass(fi).width(f.width).height(f.height),y=e.$toolbar=n(i).addClass(ei).appendTo(v),h=n(i).addClass(at).appendTo(y),c=0;n.each(f.controls.split(" "),function(r,o){var w,l,p,v;if(o==="")return!0;o==="|"?(w=n(i).addClass(oi).appendTo(h),h.width(c+1),c=0,h=n(i).addClass(at).appendTo(y)):(l=s[o],p=n(i).data(a,l.name).addClass(vt).attr("title",l.title).bind(u,n.proxy(vi,e)).appendTo(h).hover(kt,dt),c+=24,h.width(c+1),v={},l.css?v=l.css:l.image&&(v.backgroundImage=wi(l.image)),l.stripIndex&&(v.backgroundPosition=l.stripIndex*-24),p.css(v),t&&p.attr(ot,"on"),l.popupName&&gt(l.popupName,f,l.popupClass,l.popupContent,l.popupHover))});v.insertBefore(l).append(l);wt||(n(document).click(function(t){var i=n(t.target);i.add(i.parents()).is("."+tt)||o()}),wt=!0);/auto|%/.test(""+f.width+f.height)&&n(window).bind("resize.cleditor",function(){ht(e)});ht(e)};p=cleditor.prototype;bt=[["clear",pi],["disable",ni],["execCommand",h],["focus",c],["hidePopups",o],["sourceMode",l,!0],["refresh",ht],["select",bi],["selectedHTML",ki,!0],["selectedText",ri,!0],["showMessage",ut],["updateFrame",ft],["updateTextArea",ct]];n.each(bt,function(n,t){p[t[0]]=function(){for(var u,n=this,r=[n],i=0;i<arguments.length;i++)r.push(arguments[i]);return(u=t[1].apply(n,r),t[2])?u:n}});p.blurred=function(t){var i=n(this);return t?i.bind(k,t):i.trigger(k)};p.change=function(t){var i=n(this);return t?i.bind(g,t):i.trigger(g)};p.focused=function(t){var i=n(this);return t?i.bind(nt,t):i.trigger(nt)}})(jQuery);
9
+ /*
10
+ //# sourceMappingURL=jquery.cleditor.min.js.map
11
+ */
data/generate.sh CHANGED
@@ -13,7 +13,9 @@ set -e
13
13
  git submodule foreach git pull
14
14
 
15
15
  _assets="vendor/assets"
16
- mkdir -p $_assets/stylesheets $_assets/fonts
16
+ for _dir in fonts images javascripts stylesheets ; do
17
+ mkdir -p $_assets/$_dir
18
+ done
17
19
 
18
20
  _themes_css=""
19
21
  _themes_raw=""
@@ -76,5 +78,36 @@ for _file in bootswatch/fonts/*.* ; do
76
78
  fi
77
79
  done
78
80
 
81
+ if [ -s cleditor/jquery.cleditor.js ] ; then
82
+ _src="cleditor/jquery.cleditor.js"
83
+ _dst="vendor/assets/javascripts/jquery.cleditor.js"
84
+ sed -e 's/\r//g' $_src >/tmp/cleditor.tmp
85
+ if cmp -s /tmp/cleditor.tmp $_dst ; then
86
+ rm -f /tmp/cleditor.tmp
87
+ else
88
+ echo "edit: $_dst"
89
+ mv /tmp/cleditor.tmp $_dst
90
+ fi
91
+
92
+ _src="cleditor/jquery.cleditor.css"
93
+ _dst="vendor/assets/stylesheets/jquery.cleditor.css"
94
+ sed -e 's/\r//g' -e 's%images/%/assets/%g' $_src >/tmp/cleditor.tmp
95
+ if cmp -s /tmp/cleditor.tmp $_dst ; then
96
+ rm -f /tmp/cleditor.tmp
97
+ else
98
+ echo "edit: $_dst"
99
+ mv /tmp/cleditor.tmp $_dst
100
+ fi
101
+
102
+ for _file in toolbar.gif buttons.gif ; do
103
+ _src="cleditor/images/$_file"
104
+ _dst="vendor/assets/images/$_file"
105
+ if ! cmp -s $_src $_dst ; then
106
+ echo "copy: $_src"
107
+ cp $_src $_dst
108
+ fi
109
+ done
110
+ fi
111
+
79
112
  exit 0
80
113
 
@@ -1,5 +1,5 @@
1
1
  module BootswatchRails
2
- VERSION = "3.2.0.4"
2
+ VERSION = "3.2.0.5"
3
3
  THEMES = [:amelia, :cerulean, :cosmo, :custom, :cyborg, :darkly, :flatly, :journal, :lumen, :paper, :readable, :sandstone, :simplex, :slate, :spacelab, :superhero, :united, :yeti]
4
4
  DEFAULT = 1
5
5
  end
Binary file
Binary file
@@ -0,0 +1,1190 @@
1
+ /*!
2
+ CLEditor WYSIWYG HTML Editor v1.4.5
3
+ http://premiumsoftware.net/CLEditor
4
+ requires jQuery v1.4.2 or later
5
+
6
+ Copyright 2010, Chris Landowski, Premium Software, LLC
7
+ Dual licensed under the MIT or GPL Version 2 licenses.
8
+ */
9
+
10
+ (function ($) {
11
+
12
+ //==============
13
+ // jQuery Plugin
14
+ //==============
15
+
16
+ $.cleditor = {
17
+
18
+ // Define the defaults used for all new cleditor instances
19
+ defaultOptions: {
20
+ width: 'auto', // width not including margins, borders or padding
21
+ height: 250, // height not including margins, borders or padding
22
+ controls: // controls to add to the toolbar
23
+ "bold italic underline strikethrough subscript superscript | font size " +
24
+ "style | color highlight removeformat | bullets numbering | outdent " +
25
+ "indent | alignleft center alignright justify | undo redo | " +
26
+ "rule image link unlink | cut copy paste pastetext | print source",
27
+ colors: // colors in the color popup
28
+ "FFF FCC FC9 FF9 FFC 9F9 9FF CFF CCF FCF " +
29
+ "CCC F66 F96 FF6 FF3 6F9 3FF 6FF 99F F9F " +
30
+ "BBB F00 F90 FC6 FF0 3F3 6CC 3CF 66C C6C " +
31
+ "999 C00 F60 FC3 FC0 3C0 0CC 36F 63F C3C " +
32
+ "666 900 C60 C93 990 090 399 33F 60C 939 " +
33
+ "333 600 930 963 660 060 366 009 339 636 " +
34
+ "000 300 630 633 330 030 033 006 309 303",
35
+ fonts: // font names in the font popup
36
+ "Arial,Arial Black,Comic Sans MS,Courier New,Narrow,Garamond," +
37
+ "Georgia,Impact,Sans Serif,Serif,Tahoma,Trebuchet MS,Verdana",
38
+ sizes: // sizes in the font size popup
39
+ "1,2,3,4,5,6,7",
40
+ styles: // styles in the style popup
41
+ [["Paragraph", "<p>"], ["Header 1", "<h1>"], ["Header 2", "<h2>"],
42
+ ["Header 3", "<h3>"], ["Header 4", "<h4>"], ["Header 5", "<h5>"],
43
+ ["Header 6", "<h6>"]],
44
+ useCSS: true, // use CSS to style HTML when possible (not supported in ie)
45
+ docType: // Document type contained within the editor
46
+ '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">',
47
+ docCSSFile: // CSS file used to style the document contained within the editor
48
+ "",
49
+ bodyStyle: // style to assign to document body contained within the editor
50
+ "margin:4px; font:10pt Arial,Verdana; cursor:text"
51
+ },
52
+
53
+ // Define all usable toolbar buttons - the init string property is
54
+ // expanded during initialization back into the buttons object and
55
+ // separate object properties are created for each button.
56
+ // e.g. buttons.size.title = "Font Size"
57
+ buttons: {
58
+ // name,title,command,popupName (""=use name)
59
+ init:
60
+ "bold,,|" +
61
+ "italic,,|" +
62
+ "underline,,|" +
63
+ "strikethrough,,|" +
64
+ "subscript,,|" +
65
+ "superscript,,|" +
66
+ "font,,fontname,|" +
67
+ "size,Font Size,fontsize,|" +
68
+ "style,,formatblock,|" +
69
+ "color,Font Color,forecolor,|" +
70
+ "highlight,Text Highlight Color,hilitecolor,color|" +
71
+ "removeformat,Remove Formatting,|" +
72
+ "bullets,,insertunorderedlist|" +
73
+ "numbering,,insertorderedlist|" +
74
+ "outdent,,|" +
75
+ "indent,,|" +
76
+ "alignleft,Align Text Left,justifyleft|" +
77
+ "center,,justifycenter|" +
78
+ "alignright,Align Text Right,justifyright|" +
79
+ "justify,,justifyfull|" +
80
+ "undo,,|" +
81
+ "redo,,|" +
82
+ "rule,Insert Horizontal Rule,inserthorizontalrule|" +
83
+ "image,Insert Image,insertimage,url|" +
84
+ "link,Insert Hyperlink,createlink,url|" +
85
+ "unlink,Remove Hyperlink,|" +
86
+ "cut,,|" +
87
+ "copy,,|" +
88
+ "paste,,|" +
89
+ "pastetext,Paste as Text,inserthtml,|" +
90
+ "print,,|" +
91
+ "source,Show Source"
92
+ },
93
+
94
+ // imagesPath - returns the path to the images folder
95
+ imagesPath: function () { return imagesPath(); }
96
+
97
+ };
98
+
99
+ // cleditor - creates a new editor for each of the matched textareas
100
+ $.fn.cleditor = function (options) {
101
+
102
+ // Create a new jQuery object to hold the results
103
+ var $result = $([]);
104
+
105
+ // Loop through all matching textareas and create the editors
106
+ this.each(function (idx, elem) {
107
+ if (elem.tagName.toUpperCase() === "TEXTAREA") {
108
+ var data = $.data(elem, CLEDITOR);
109
+ if (!data) data = new cleditor(elem, options);
110
+ $result = $result.add(data);
111
+ }
112
+ });
113
+
114
+ // return the new jQuery object
115
+ return $result;
116
+
117
+ };
118
+
119
+ //==================
120
+ // Private Variables
121
+ //==================
122
+
123
+ var
124
+
125
+ // Misc constants
126
+ BACKGROUND_COLOR = "backgroundColor",
127
+ BLURRED = "blurred",
128
+ BUTTON = "button",
129
+ BUTTON_NAME = "buttonName",
130
+ CHANGE = "change",
131
+ CLEDITOR = "cleditor",
132
+ CLICK = "click",
133
+ DISABLED = "disabled",
134
+ DIV_TAG = "<div>",
135
+ FOCUSED = "focused",
136
+ TRANSPARENT = "transparent",
137
+ UNSELECTABLE = "unselectable",
138
+
139
+ // Class name constants
140
+ MAIN_CLASS = "cleditorMain", // main containing div
141
+ TOOLBAR_CLASS = "cleditorToolbar", // toolbar div inside main div
142
+ GROUP_CLASS = "cleditorGroup", // group divs inside the toolbar div
143
+ BUTTON_CLASS = "cleditorButton", // button divs inside group div
144
+ DISABLED_CLASS = "cleditorDisabled",// disabled button divs
145
+ DIVIDER_CLASS = "cleditorDivider", // divider divs inside group div
146
+ POPUP_CLASS = "cleditorPopup", // popup divs inside body
147
+ LIST_CLASS = "cleditorList", // list popup divs inside body
148
+ COLOR_CLASS = "cleditorColor", // color popup div inside body
149
+ PROMPT_CLASS = "cleditorPrompt", // prompt popup divs inside body
150
+ MSG_CLASS = "cleditorMsg", // message popup div inside body
151
+
152
+ // Browser detection
153
+ ua = navigator.userAgent.toLowerCase(),
154
+ ie = /msie/.test(ua),
155
+ ie6 = /msie\s6/.test(ua),
156
+ iege11 = /(trident)(?:.*rv:([\w.]+))?/.test(ua),
157
+ webkit = /webkit/.test(ua),
158
+
159
+ // Test for iPhone/iTouch/iPad
160
+ iOS = /iPhone|iPad|iPod/i.test(ua),
161
+
162
+ // Popups are created once as needed and shared by all editor instances
163
+ popups = {},
164
+
165
+ // Used to prevent the document click event from being bound more than once
166
+ documentClickAssigned,
167
+
168
+ // Local copy of the buttons object
169
+ buttons = $.cleditor.buttons;
170
+
171
+ //===============
172
+ // Initialization
173
+ //===============
174
+
175
+ // Expand the buttons.init string back into the buttons object
176
+ // and create seperate object properties for each button.
177
+ // e.g. buttons.size.title = "Font Size"
178
+ $.each(buttons.init.split("|"), function (idx, button) {
179
+ var items = button.split(","), name = items[0];
180
+ buttons[name] = {
181
+ stripIndex: idx,
182
+ name: name,
183
+ title: items[1] === "" ? name.charAt(0).toUpperCase() + name.substr(1) : items[1],
184
+ command: items[2] === "" ? name : items[2],
185
+ popupName: items[3] === "" ? name : items[3]
186
+ };
187
+ });
188
+ delete buttons.init;
189
+
190
+ //============
191
+ // Constructor
192
+ //============
193
+
194
+ // cleditor - creates a new editor for the passed in textarea element
195
+ cleditor = function (area, options) {
196
+
197
+ var editor = this;
198
+
199
+ // Get the defaults and override with options
200
+ editor.options = options = $.extend({}, $.cleditor.defaultOptions, options);
201
+
202
+ // Hide the textarea and associate it with this editor
203
+ var $area = editor.$area = $(area)
204
+ .css({ border: "none", margin: 0, padding: 0 }) // Needed for IE6 & 7 (won't work in CSS file)
205
+ .hide()
206
+ .data(CLEDITOR, editor)
207
+ .blur(function () {
208
+ // Update the iframe when the textarea loses focus
209
+ updateFrame(editor, true);
210
+ });
211
+
212
+ // Create the main container
213
+ var $main = editor.$main = $(DIV_TAG)
214
+ .addClass(MAIN_CLASS)
215
+ .width(options.width)
216
+ .height(options.height);
217
+
218
+ // Create the toolbar
219
+ var $toolbar = editor.$toolbar = $(DIV_TAG)
220
+ .addClass(TOOLBAR_CLASS)
221
+ .appendTo($main);
222
+
223
+ // Add the first group to the toolbar
224
+ var $group = $(DIV_TAG)
225
+ .addClass(GROUP_CLASS)
226
+ .appendTo($toolbar);
227
+
228
+ // Initialize the group width
229
+ var groupWidth = 0;
230
+
231
+ // Add the buttons to the toolbar
232
+ $.each(options.controls.split(" "), function (idx, buttonName) {
233
+ if (buttonName === "") return true;
234
+
235
+ // Divider
236
+ if (buttonName === "|") {
237
+
238
+ // Add a new divider to the group
239
+ var $div = $(DIV_TAG)
240
+ .addClass(DIVIDER_CLASS)
241
+ .appendTo($group);
242
+
243
+ // Update the group width
244
+ $group.width(groupWidth + 1);
245
+ groupWidth = 0;
246
+
247
+ // Create a new group
248
+ $group = $(DIV_TAG)
249
+ .addClass(GROUP_CLASS)
250
+ .appendTo($toolbar);
251
+
252
+ }
253
+
254
+ // Button
255
+ else {
256
+
257
+ // Get the button definition
258
+ var button = buttons[buttonName];
259
+
260
+ // Add a new button to the group
261
+ var $buttonDiv = $(DIV_TAG)
262
+ .data(BUTTON_NAME, button.name)
263
+ .addClass(BUTTON_CLASS)
264
+ .attr("title", button.title)
265
+ .bind(CLICK, $.proxy(buttonClick, editor))
266
+ .appendTo($group)
267
+ .hover(hoverEnter, hoverLeave);
268
+
269
+ // Update the group width
270
+ groupWidth += 24;
271
+ $group.width(groupWidth + 1);
272
+
273
+ // Prepare the button image
274
+ var map = {};
275
+ if (button.css) map = button.css;
276
+ else if (button.image) map.backgroundImage = imageUrl(button.image);
277
+ if (button.stripIndex) map.backgroundPosition = button.stripIndex * -24;
278
+ $buttonDiv.css(map);
279
+
280
+ // Add the unselectable attribute for ie
281
+ if (ie)
282
+ $buttonDiv.attr(UNSELECTABLE, "on");
283
+
284
+ // Create the popup
285
+ if (button.popupName)
286
+ createPopup(button.popupName, options, button.popupClass,
287
+ button.popupContent, button.popupHover);
288
+
289
+ }
290
+
291
+ });
292
+
293
+ // Add the main div to the DOM and append the textarea
294
+ $main.insertBefore($area)
295
+ .append($area);
296
+
297
+ // Bind the document click event handler
298
+ if (!documentClickAssigned) {
299
+ $(document).click(function (e) {
300
+ // Dismiss all non-prompt popups
301
+ var $target = $(e.target);
302
+ if (!$target.add($target.parents()).is("." + PROMPT_CLASS))
303
+ hidePopups();
304
+ });
305
+ documentClickAssigned = true;
306
+ }
307
+
308
+ // Bind the window resize event when the width or height is auto or %
309
+ if (/auto|%/.test("" + options.width + options.height))
310
+ $(window).bind("resize.cleditor", function () { refresh(editor); });
311
+
312
+ // Create the iframe and resize the controls
313
+ refresh(editor);
314
+
315
+ };
316
+
317
+ //===============
318
+ // Public Methods
319
+ //===============
320
+
321
+ var fn = cleditor.prototype,
322
+
323
+ // Expose the following private functions as methods on the cleditor object.
324
+ // The closure compiler will rename the private functions. However, the
325
+ // exposed method names on the cleditor object will remain fixed.
326
+ methods = [
327
+ ["clear", clear],
328
+ ["disable", disable],
329
+ ["execCommand", execCommand],
330
+ ["focus", focus],
331
+ ["hidePopups", hidePopups],
332
+ ["sourceMode", sourceMode, true],
333
+ ["refresh", refresh],
334
+ ["select", select],
335
+ ["selectedHTML", selectedHTML, true],
336
+ ["selectedText", selectedText, true],
337
+ ["showMessage", showMessage],
338
+ ["updateFrame", updateFrame],
339
+ ["updateTextArea", updateTextArea]
340
+ ];
341
+
342
+ $.each(methods, function (idx, method) {
343
+ fn[method[0]] = function () {
344
+ var editor = this, args = [editor];
345
+ // using each here would cast booleans into objects!
346
+ for (var x = 0; x < arguments.length; x++) { args.push(arguments[x]); }
347
+ var result = method[1].apply(editor, args);
348
+ if (method[2]) return result;
349
+ return editor;
350
+ };
351
+ });
352
+
353
+ // blurred - shortcut for .bind("blurred", handler) or .trigger("blurred")
354
+ fn.blurred = function (handler) {
355
+ var $this = $(this);
356
+ return handler ? $this.bind(BLURRED, handler) : $this.trigger(BLURRED);
357
+ };
358
+
359
+ // change - shortcut for .bind("change", handler) or .trigger("change")
360
+ fn.change = function change(handler) {
361
+ var $this = $(this);
362
+ return handler ? $this.bind(CHANGE, handler) : $this.trigger(CHANGE);
363
+ };
364
+
365
+ // focused - shortcut for .bind("focused", handler) or .trigger("focused")
366
+ fn.focused = function (handler) {
367
+ var $this = $(this);
368
+ return handler ? $this.bind(FOCUSED, handler) : $this.trigger(FOCUSED);
369
+ };
370
+
371
+ //===============
372
+ // Event Handlers
373
+ //===============
374
+
375
+ // buttonClick - click event handler for toolbar buttons
376
+ function buttonClick(e) {
377
+
378
+ var editor = this,
379
+ buttonDiv = e.target,
380
+ buttonName = $.data(buttonDiv, BUTTON_NAME),
381
+ button = buttons[buttonName],
382
+ popupName = button.popupName,
383
+ popup = popups[popupName];
384
+
385
+ // Check if disabled
386
+ if (editor.disabled || $(buttonDiv).attr(DISABLED) === DISABLED)
387
+ return;
388
+
389
+ // Fire the buttonClick event
390
+ var data = {
391
+ editor: editor,
392
+ button: buttonDiv,
393
+ buttonName: buttonName,
394
+ popup: popup,
395
+ popupName: popupName,
396
+ command: button.command,
397
+ useCSS: editor.options.useCSS
398
+ };
399
+
400
+ if (button.buttonClick && button.buttonClick(e, data) === false)
401
+ return false;
402
+
403
+ // Toggle source
404
+ if (buttonName === "source") {
405
+
406
+ // Show the iframe
407
+ if (sourceMode(editor)) {
408
+ delete editor.range;
409
+ editor.$area.hide();
410
+ editor.$frame.show();
411
+ buttonDiv.title = button.title;
412
+ }
413
+
414
+ // Show the textarea
415
+ else {
416
+ editor.$frame.hide();
417
+ editor.$area.show();
418
+ buttonDiv.title = "Show Rich Text";
419
+ }
420
+
421
+ }
422
+
423
+ // Check for rich text mode
424
+ else if (!sourceMode(editor)) {
425
+
426
+ // Handle popups
427
+ if (popupName) {
428
+ var $popup = $(popup);
429
+
430
+ // URL
431
+ if (popupName === "url") {
432
+
433
+ // Check for selection before showing the link url popup
434
+ if (buttonName === "link" && selectedText(editor) === "") {
435
+ showMessage(editor, "A selection is required when inserting a link.", buttonDiv);
436
+ return false;
437
+ }
438
+
439
+ // Wire up the submit button click event handler
440
+ $popup.children(":button")
441
+ .unbind(CLICK)
442
+ .bind(CLICK, function () {
443
+
444
+ // Insert the image or link if a url was entered
445
+ var $text = $popup.find(":text"),
446
+ url = $.trim($text.val());
447
+ if (url !== "")
448
+ execCommand(editor, data.command, url, null, data.button);
449
+
450
+ // Reset the text, hide the popup and set focus
451
+ $text.val("http://");
452
+ hidePopups();
453
+ focus(editor);
454
+
455
+ });
456
+
457
+ }
458
+
459
+ // Paste as Text
460
+ else if (popupName === "pastetext") {
461
+
462
+ // Wire up the submit button click event handler
463
+ $popup.children(":button")
464
+ .unbind(CLICK)
465
+ .bind(CLICK, function () {
466
+
467
+ // Insert the unformatted text replacing new lines with break tags
468
+ var $textarea = $popup.find("textarea"),
469
+ text = $textarea.val().replace(/\n/g, "<br />");
470
+ if (text !== "")
471
+ execCommand(editor, data.command, text, null, data.button);
472
+
473
+ // Reset the text, hide the popup and set focus
474
+ $textarea.val("");
475
+ hidePopups();
476
+ focus(editor);
477
+
478
+ });
479
+
480
+ }
481
+
482
+ // Show the popup if not already showing for this button
483
+ if (buttonDiv !== $.data(popup, BUTTON)) {
484
+ showPopup(editor, popup, buttonDiv);
485
+ return false; // stop propagination to document click
486
+ }
487
+
488
+ // propaginate to document click
489
+ return;
490
+
491
+ }
492
+
493
+ // Print
494
+ else if (buttonName === "print")
495
+ editor.$frame[0].contentWindow.print();
496
+
497
+ // All other buttons
498
+ else if (!execCommand(editor, data.command, data.value, data.useCSS, buttonDiv))
499
+ return false;
500
+
501
+ }
502
+
503
+ // Focus the editor
504
+ focus(editor);
505
+
506
+ }
507
+
508
+ // hoverEnter - mouseenter event handler for buttons and popup items
509
+ function hoverEnter(e) {
510
+ var $div = $(e.target).closest("div");
511
+ $div.css(BACKGROUND_COLOR, $div.data(BUTTON_NAME) ? "#FFF" : "#FFC");
512
+ }
513
+
514
+ // hoverLeave - mouseleave event handler for buttons and popup items
515
+ function hoverLeave(e) {
516
+ $(e.target).closest("div").css(BACKGROUND_COLOR, "transparent");
517
+ }
518
+
519
+ // popupClick - click event handler for popup items
520
+ function popupClick(e) {
521
+
522
+ var editor = this,
523
+ popup = e.data.popup,
524
+ target = e.target;
525
+
526
+ // Check for message and prompt popups
527
+ if (popup === popups.msg || $(popup).hasClass(PROMPT_CLASS))
528
+ return;
529
+
530
+ // Get the button info
531
+ var buttonDiv = $.data(popup, BUTTON),
532
+ buttonName = $.data(buttonDiv, BUTTON_NAME),
533
+ button = buttons[buttonName],
534
+ command = button.command,
535
+ value,
536
+ useCSS = editor.options.useCSS;
537
+
538
+ // Get the command value
539
+ if (buttonName === "font")
540
+ // Opera returns the fontfamily wrapped in quotes
541
+ value = target.style.fontFamily.replace(/"/g, "");
542
+ else if (buttonName === "size") {
543
+ if (target.tagName.toUpperCase() === "DIV")
544
+ target = target.children[0];
545
+ value = target.innerHTML;
546
+ }
547
+ else if (buttonName === "style")
548
+ value = "<" + target.tagName + ">";
549
+ else if (buttonName === "color")
550
+ value = hex(target.style.backgroundColor);
551
+ else if (buttonName === "highlight") {
552
+ value = hex(target.style.backgroundColor);
553
+ if (ie) command = 'backcolor';
554
+ else useCSS = true;
555
+ }
556
+
557
+ // Fire the popupClick event
558
+ var data = {
559
+ editor: editor,
560
+ button: buttonDiv,
561
+ buttonName: buttonName,
562
+ popup: popup,
563
+ popupName: button.popupName,
564
+ command: command,
565
+ value: value,
566
+ useCSS: useCSS
567
+ };
568
+
569
+ if (button.popupClick && button.popupClick(e, data) === false)
570
+ return;
571
+
572
+ // Execute the command
573
+ if (data.command && !execCommand(editor, data.command, data.value, data.useCSS, buttonDiv))
574
+ return false;
575
+
576
+ // Hide the popup and focus the editor
577
+ hidePopups();
578
+ focus(editor);
579
+
580
+ }
581
+
582
+ //==================
583
+ // Private Functions
584
+ //==================
585
+
586
+ // checksum - returns a checksum using the Adler-32 method
587
+ function checksum(text) {
588
+ var a = 1, b = 0;
589
+ for (var index = 0; index < text.length; ++index) {
590
+ a = (a + text.charCodeAt(index)) % 65521;
591
+ b = (b + a) % 65521;
592
+ }
593
+ return (b << 16) | a;
594
+ }
595
+
596
+ // clear - clears the contents of the editor
597
+ function clear(editor) {
598
+ editor.$area.val("");
599
+ updateFrame(editor);
600
+ }
601
+
602
+ // createPopup - creates a popup and adds it to the body
603
+ function createPopup(popupName, options, popupTypeClass, popupContent, popupHover) {
604
+
605
+ // Check if popup already exists
606
+ if (popups[popupName])
607
+ return popups[popupName];
608
+
609
+ // Create the popup
610
+ var $popup = $(DIV_TAG)
611
+ .hide()
612
+ .addClass(POPUP_CLASS)
613
+ .appendTo("body");
614
+
615
+ // Add the content
616
+
617
+ // Custom popup
618
+ if (popupContent)
619
+ $popup.html(popupContent);
620
+
621
+ // Color
622
+ else if (popupName === "color") {
623
+ var colors = options.colors.split(" ");
624
+ if (colors.length < 10)
625
+ $popup.width("auto");
626
+ $.each(colors, function (idx, color) {
627
+ $(DIV_TAG).appendTo($popup)
628
+ .css(BACKGROUND_COLOR, "#" + color);
629
+ });
630
+ popupTypeClass = COLOR_CLASS;
631
+ }
632
+
633
+ // Font
634
+ else if (popupName === "font")
635
+ $.each(options.fonts.split(","), function (idx, font) {
636
+ $(DIV_TAG).appendTo($popup)
637
+ .css("fontFamily", font)
638
+ .html(font);
639
+ });
640
+
641
+ // Size
642
+ else if (popupName === "size")
643
+ $.each(options.sizes.split(","), function (idx, size) {
644
+ $(DIV_TAG).appendTo($popup)
645
+ .html('<font size="' + size + '">' + size + '</font>');
646
+ });
647
+
648
+ // Style
649
+ else if (popupName === "style")
650
+ $.each(options.styles, function (idx, style) {
651
+ $(DIV_TAG).appendTo($popup)
652
+ .html(style[1] + style[0] + style[1].replace("<", "</"));
653
+ });
654
+
655
+ // URL
656
+ else if (popupName === "url") {
657
+ $popup.html('<label>Enter URL:<br /><input type="text" value="http://" style="width:200px" /></label><br /><input type="button" value="Submit" />');
658
+ popupTypeClass = PROMPT_CLASS;
659
+ }
660
+
661
+ // Paste as Text
662
+ else if (popupName === "pastetext") {
663
+ $popup.html('<label>Paste your content here:<br /><textarea rows="3" style="width:200px"></textarea></label><br /><input type="button" value="Submit" />');
664
+ popupTypeClass = PROMPT_CLASS;
665
+ }
666
+
667
+ // Add the popup type class name
668
+ if (!popupTypeClass && !popupContent)
669
+ popupTypeClass = LIST_CLASS;
670
+ $popup.addClass(popupTypeClass);
671
+
672
+ // Add the unselectable attribute to all items
673
+ if (ie) {
674
+ $popup.attr(UNSELECTABLE, "on")
675
+ .find("div,font,p,h1,h2,h3,h4,h5,h6")
676
+ .attr(UNSELECTABLE, "on");
677
+ }
678
+
679
+ // Add the hover effect to all items
680
+ if ($popup.hasClass(LIST_CLASS) || popupHover === true)
681
+ $popup.children().hover(hoverEnter, hoverLeave);
682
+
683
+ // Add the popup to the array and return it
684
+ popups[popupName] = $popup[0];
685
+ return $popup[0];
686
+
687
+ }
688
+
689
+ // disable - enables or disables the editor
690
+ function disable(editor, disabled) {
691
+
692
+ // Update the textarea and save the state
693
+ if (disabled) {
694
+ editor.$area.attr(DISABLED, DISABLED);
695
+ editor.disabled = true;
696
+ }
697
+ else {
698
+ editor.$area.removeAttr(DISABLED);
699
+ delete editor.disabled;
700
+ }
701
+
702
+ // Switch the iframe into design mode.
703
+ // ie6 does not support designMode.
704
+ // ie7 & ie8 do not properly support designMode="off".
705
+ try {
706
+ if (ie) editor.doc.body.contentEditable = !disabled;
707
+ else editor.doc.designMode = !disabled ? "on" : "off";
708
+ }
709
+ // Firefox 1.5 throws an exception that can be ignored
710
+ // when toggling designMode from off to on.
711
+ catch (err) { }
712
+
713
+ // Enable or disable the toolbar buttons
714
+ refreshButtons(editor);
715
+
716
+ }
717
+
718
+ // execCommand - executes a designMode command
719
+ function execCommand(editor, command, value, useCSS, button) {
720
+
721
+ // Restore the current ie selection
722
+ restoreRange(editor);
723
+
724
+ // Set the styling method
725
+ if (!ie) {
726
+ if (useCSS === undefined || useCSS === null)
727
+ useCSS = editor.options.useCSS;
728
+ editor.doc.execCommand("styleWithCSS", 0, useCSS.toString());
729
+ }
730
+
731
+ // Execute the command and check for error
732
+ var inserthtml = command.toLowerCase() === "inserthtml";
733
+ if (ie && inserthtml)
734
+ getRange(editor).pasteHTML(value);
735
+
736
+ else if (iege11 && inserthtml) {
737
+ var selection = getSelection(editor),
738
+ range = selection.getRangeAt(0);
739
+ range.deleteContents();
740
+ range.insertNode(range.createContextualFragment(value));
741
+ selection.removeAllRanges();
742
+ selection.addRange(range);
743
+ }
744
+
745
+ else {
746
+ var success = true, message;
747
+ try { success = editor.doc.execCommand(command, 0, value || null); }
748
+ catch (err) { message = err.message; success = false; }
749
+ if (!success) {
750
+ if ("cutcopypaste".indexOf(command) > -1)
751
+ showMessage(editor, "For security reasons, your browser does not support the " +
752
+ command + " command. Try using the keyboard shortcut or context menu instead.",
753
+ button);
754
+ else
755
+ showMessage(editor,
756
+ (message ? message : "Error executing the " + command + " command."),
757
+ button);
758
+ }
759
+ }
760
+
761
+ // Enable the buttons and update the textarea
762
+ refreshButtons(editor);
763
+ updateTextArea(editor, true);
764
+ return success;
765
+
766
+ }
767
+
768
+ // focus - sets focus to either the textarea or iframe
769
+ function focus(editor) {
770
+ setTimeout(function () {
771
+ if (sourceMode(editor)) editor.$area.focus();
772
+ else editor.$frame[0].contentWindow.focus();
773
+ refreshButtons(editor);
774
+ }, 0);
775
+ }
776
+
777
+ // getRange - gets the current text range object
778
+ function getRange(editor) {
779
+ if (ie) return getSelection(editor).createRange();
780
+ return getSelection(editor).getRangeAt(0);
781
+ }
782
+
783
+ // getSelection - gets the current text range object
784
+ function getSelection(editor) {
785
+ if (ie) return editor.doc.selection;
786
+ return editor.$frame[0].contentWindow.getSelection();
787
+ }
788
+
789
+ // hex - returns the hex value for the passed in color string
790
+ function hex(s) {
791
+
792
+ // hex("rgb(255, 0, 0)") returns #FF0000
793
+ var m = /rgba?\((\d+), (\d+), (\d+)/.exec(s);
794
+ if (m) {
795
+ s = (m[1] << 16 | m[2] << 8 | m[3]).toString(16);
796
+ while (s.length < 6)
797
+ s = "0" + s;
798
+ return "#" + s;
799
+ }
800
+
801
+ // hex("#F00") returns #FF0000
802
+ var c = s.split("");
803
+ if (s.length === 4)
804
+ return "#" + c[1] + c[1] + c[2] + c[2] + c[3] + c[3];
805
+
806
+ // hex("#FF0000") returns #FF0000
807
+ return s;
808
+
809
+ }
810
+
811
+ // hidePopups - hides all popups
812
+ function hidePopups() {
813
+ $.each(popups, function (idx, popup) {
814
+ $(popup)
815
+ .hide()
816
+ .unbind(CLICK)
817
+ .removeData(BUTTON);
818
+ });
819
+ }
820
+
821
+ // imagesPath - returns the path to the images folder
822
+ function imagesPath() {
823
+ var href = $("link[href*=cleditor]").attr("href");
824
+ return href.replace(/^(.*\/)[^\/]+$/, '$1') + "images/";
825
+ }
826
+
827
+ // imageUrl - Returns the css url string for a filemane
828
+ function imageUrl(filename) {
829
+ return "url(" + imagesPath() + filename + ")";
830
+ }
831
+
832
+ // refresh - creates the iframe and resizes the controls
833
+ function refresh(editor) {
834
+
835
+ var $main = editor.$main,
836
+ options = editor.options;
837
+
838
+ // Remove the old iframe
839
+ if (editor.$frame)
840
+ editor.$frame.remove();
841
+
842
+ // Create a new iframe
843
+ var $frame = editor.$frame = $('<iframe frameborder="0" src="javascript:true;" />')
844
+ .hide()
845
+ .appendTo($main);
846
+
847
+ // Load the iframe document content
848
+ var contentWindow = $frame[0].contentWindow,
849
+ doc = editor.doc = contentWindow.document,
850
+ $doc = $(doc);
851
+
852
+ doc.open();
853
+ doc.write(
854
+ options.docType +
855
+ '<html>' +
856
+ ((options.docCSSFile === '') ? '' : '<head><link rel="stylesheet" type="text/css" href="' + options.docCSSFile + '" /></head>') +
857
+ '<body style="' + options.bodyStyle + '"></body></html>'
858
+ );
859
+ doc.close();
860
+
861
+ // Work around for bug in IE which causes the editor to lose
862
+ // focus when clicking below the end of the document.
863
+ if (ie || iege11)
864
+ $doc.click(function () { focus(editor); });
865
+
866
+ // Load the content
867
+ updateFrame(editor);
868
+
869
+ // Bind the ie specific iframe event handlers
870
+ if (ie || iege11) {
871
+
872
+ // Save the current user selection. This code is needed since IE will
873
+ // reset the selection just after the beforedeactivate event and just
874
+ // before the beforeactivate event.
875
+ $doc.bind("beforedeactivate beforeactivate selectionchange keypress keyup", function (e) {
876
+
877
+ // Flag the editor as inactive
878
+ if (e.type === "beforedeactivate")
879
+ editor.inactive = true;
880
+
881
+ // Get rid of the bogus selection and flag the editor as active
882
+ else if (e.type === "beforeactivate") {
883
+ if (!editor.inactive && editor.range && editor.range.length > 1)
884
+ editor.range.shift();
885
+ delete editor.inactive;
886
+ }
887
+
888
+ // Save the selection when the editor is active
889
+ else if (!editor.inactive) {
890
+ if (!editor.range)
891
+ editor.range = [];
892
+ editor.range.unshift(getRange(editor));
893
+
894
+ // We only need the last 2 selections
895
+ while (editor.range.length > 2)
896
+ editor.range.pop();
897
+ }
898
+
899
+ });
900
+
901
+ // Restore the text range and trigger focused event when the iframe gains focus
902
+ $frame.focus(function () {
903
+ restoreRange(editor);
904
+ $(editor).triggerHandler(FOCUSED);
905
+ });
906
+
907
+ // Trigger blurred event when the iframe looses focus
908
+ $frame.blur(function () {
909
+ $(editor).triggerHandler(BLURRED);
910
+ });
911
+
912
+ }
913
+
914
+ // Trigger focused and blurred events for all other browsers
915
+ else {
916
+ $($frame[0].contentWindow)
917
+ .focus(function () { $(editor).triggerHandler(FOCUSED); })
918
+ .blur(function () { $(editor).triggerHandler(BLURRED); });
919
+ }
920
+
921
+ // Enable the toolbar buttons and update the textarea as the user types or clicks
922
+ $doc.click(hidePopups)
923
+ .keydown(function (e) {
924
+ // Prevent Internet Explorer from going to prior page when an image
925
+ // is selected and the backspace key is pressed.
926
+ if (ie && getSelection(editor).type == "Control" && e.keyCode == 8) {
927
+ getSelection(editor).clear();
928
+ e.preventDefault();
929
+ }
930
+ })
931
+ .bind("keyup mouseup", function () {
932
+ refreshButtons(editor);
933
+ updateTextArea(editor, true);
934
+ });
935
+
936
+ // Show the textarea for iPhone/iTouch/iPad or
937
+ // the iframe when design mode is supported.
938
+ if (iOS) editor.$area.show();
939
+ else $frame.show();
940
+
941
+ // Wait for the layout to finish - shortcut for $(document).ready()
942
+ $(function () {
943
+
944
+ var $toolbar = editor.$toolbar,
945
+ $group = $toolbar.children("div:last"),
946
+ wid = $main.width();
947
+
948
+ // Resize the toolbar
949
+ var hgt = $group.offset().top + $group.outerHeight() - $toolbar.offset().top + 1;
950
+ $toolbar.height(hgt);
951
+
952
+ // Resize the iframe
953
+ hgt = (/%/.test("" + options.height) ? $main.height() : parseInt(options.height, 10)) - hgt;
954
+ $frame.width(wid).height(hgt);
955
+
956
+ // Resize the textarea. IE6 textareas have a 1px top
957
+ // & bottom margin that cannot be removed using css.
958
+ editor.$area.width(wid).height(ie6 ? hgt - 2 : hgt);
959
+
960
+ // Switch the iframe into design mode if enabled
961
+ disable(editor, editor.disabled);
962
+
963
+ // Enable or disable the toolbar buttons
964
+ refreshButtons(editor);
965
+
966
+ });
967
+
968
+ }
969
+
970
+ // refreshButtons - enables or disables buttons based on availability
971
+ function refreshButtons(editor) {
972
+
973
+ // Webkit requires focus before queryCommandEnabled will return anything but false
974
+ if (!iOS && webkit && !editor.focused) {
975
+ editor.$frame[0].contentWindow.focus();
976
+ window.focus();
977
+ editor.focused = true;
978
+ }
979
+
980
+ // Get the object used for checking queryCommandEnabled
981
+ var queryObj = editor.doc;
982
+ if (ie) queryObj = getRange(editor);
983
+
984
+ // Loop through each button
985
+ var inSourceMode = sourceMode(editor);
986
+ $.each(editor.$toolbar.find("." + BUTTON_CLASS), function (idx, elem) {
987
+
988
+ var $elem = $(elem),
989
+ button = $.cleditor.buttons[$.data(elem, BUTTON_NAME)],
990
+ command = button.command,
991
+ enabled = true;
992
+
993
+ // Determine the state
994
+ if (editor.disabled)
995
+ enabled = false;
996
+ else if (button.getEnabled) {
997
+ var data = {
998
+ editor: editor,
999
+ button: elem,
1000
+ buttonName: button.name,
1001
+ popup: popups[button.popupName],
1002
+ popupName: button.popupName,
1003
+ command: button.command,
1004
+ useCSS: editor.options.useCSS
1005
+ };
1006
+ enabled = button.getEnabled(data);
1007
+ if (enabled === undefined)
1008
+ enabled = true;
1009
+ }
1010
+ else if (((inSourceMode || iOS) && button.name !== "source") ||
1011
+ (ie && (command === "undo" || command === "redo")))
1012
+ enabled = false;
1013
+ else if (command && command !== "print") {
1014
+ if (ie && command === "hilitecolor")
1015
+ command = "backcolor";
1016
+ // IE does not support inserthtml, so it's always enabled
1017
+ if ((!ie && !iege11) || command !== "inserthtml") {
1018
+ try { enabled = queryObj.queryCommandEnabled(command); }
1019
+ catch (err) { enabled = false; }
1020
+ }
1021
+ }
1022
+
1023
+ // Enable or disable the button
1024
+ if (enabled) {
1025
+ $elem.removeClass(DISABLED_CLASS);
1026
+ $elem.removeAttr(DISABLED);
1027
+ }
1028
+ else {
1029
+ $elem.addClass(DISABLED_CLASS);
1030
+ $elem.attr(DISABLED, DISABLED);
1031
+ }
1032
+
1033
+ });
1034
+ }
1035
+
1036
+ // restoreRange - restores the current ie selection
1037
+ function restoreRange(editor) {
1038
+ if (editor.range) {
1039
+ if (ie)
1040
+ editor.range[0].select();
1041
+ else if (iege11)
1042
+ getSelection(editor).addRange(editor.range[0]);
1043
+ }
1044
+ }
1045
+
1046
+ // select - selects all the text in either the textarea or iframe
1047
+ function select(editor) {
1048
+ setTimeout(function () {
1049
+ if (sourceMode(editor)) editor.$area.select();
1050
+ else execCommand(editor, "selectall");
1051
+ }, 0);
1052
+ }
1053
+
1054
+ // selectedHTML - returns the current HTML selection or and empty string
1055
+ function selectedHTML(editor) {
1056
+ restoreRange(editor);
1057
+ var range = getRange(editor);
1058
+ if (ie)
1059
+ return range.htmlText;
1060
+ var layer = $("<layer>")[0];
1061
+ layer.appendChild(range.cloneContents());
1062
+ var html = layer.innerHTML;
1063
+ layer = null;
1064
+ return html;
1065
+ }
1066
+
1067
+ // selectedText - returns the current text selection or and empty string
1068
+ function selectedText(editor) {
1069
+ restoreRange(editor);
1070
+ if (ie) return getRange(editor).text;
1071
+ return getSelection(editor).toString();
1072
+ }
1073
+
1074
+ // showMessage - alert replacement
1075
+ function showMessage(editor, message, button) {
1076
+ var popup = createPopup("msg", editor.options, MSG_CLASS);
1077
+ popup.innerHTML = message;
1078
+ showPopup(editor, popup, button);
1079
+ }
1080
+
1081
+ // showPopup - shows a popup
1082
+ function showPopup(editor, popup, button) {
1083
+
1084
+ var offset, left, top, $popup = $(popup);
1085
+
1086
+ // Determine the popup location
1087
+ if (button) {
1088
+ var $button = $(button);
1089
+ offset = $button.offset();
1090
+ left = --offset.left;
1091
+ top = offset.top + $button.height();
1092
+ }
1093
+ else {
1094
+ var $toolbar = editor.$toolbar;
1095
+ offset = $toolbar.offset();
1096
+ left = Math.floor(($toolbar.width() - $popup.width()) / 2) + offset.left;
1097
+ top = offset.top + $toolbar.height() - 2;
1098
+ }
1099
+
1100
+ // Position and show the popup
1101
+ hidePopups();
1102
+ $popup.css({ left: left, top: top })
1103
+ .show();
1104
+
1105
+ // Assign the popup button and click event handler
1106
+ if (button) {
1107
+ $.data(popup, BUTTON, button);
1108
+ $popup.bind(CLICK, { popup: popup }, $.proxy(popupClick, editor));
1109
+ }
1110
+
1111
+ // Focus the first input element if any
1112
+ setTimeout(function () {
1113
+ $popup.find(":text,textarea").eq(0).focus().select();
1114
+ }, 100);
1115
+
1116
+ }
1117
+
1118
+ // sourceMode - returns true if the textarea is showing
1119
+ function sourceMode(editor) {
1120
+ return editor.$area.is(":visible");
1121
+ }
1122
+
1123
+ // updateFrame - updates the iframe with the textarea contents
1124
+ function updateFrame(editor, checkForChange) {
1125
+
1126
+ var code = editor.$area.val(),
1127
+ options = editor.options,
1128
+ updateFrameCallback = options.updateFrame,
1129
+ $body = $(editor.doc.body);
1130
+
1131
+ // Check for textarea change to avoid unnecessary firing
1132
+ // of potentially heavy updateFrame callbacks.
1133
+ if (updateFrameCallback) {
1134
+ var sum = checksum(code);
1135
+ if (checkForChange && editor.areaChecksum === sum)
1136
+ return;
1137
+ editor.areaChecksum = sum;
1138
+ }
1139
+
1140
+ // Convert the textarea source code into iframe html
1141
+ var html = updateFrameCallback ? updateFrameCallback(code) : code;
1142
+
1143
+ // Prevent script injection attacks by html encoding script tags
1144
+ html = html.replace(/<(?=\/?script)/ig, "&lt;");
1145
+
1146
+ // Update the iframe checksum
1147
+ if (options.updateTextArea)
1148
+ editor.frameChecksum = checksum(html);
1149
+
1150
+ // Update the iframe and trigger the change event
1151
+ if (html !== $body.html()) {
1152
+ $body.html(html);
1153
+ $(editor).triggerHandler(CHANGE);
1154
+ }
1155
+
1156
+ }
1157
+
1158
+ // updateTextArea - updates the textarea with the iframe contents
1159
+ function updateTextArea(editor, checkForChange) {
1160
+
1161
+ var html = $(editor.doc.body).html(),
1162
+ options = editor.options,
1163
+ updateTextAreaCallback = options.updateTextArea,
1164
+ $area = editor.$area;
1165
+
1166
+ // Check for iframe change to avoid unnecessary firing
1167
+ // of potentially heavy updateTextArea callbacks.
1168
+ if (updateTextAreaCallback) {
1169
+ var sum = checksum(html);
1170
+ if (checkForChange && editor.frameChecksum === sum)
1171
+ return;
1172
+ editor.frameChecksum = sum;
1173
+ }
1174
+
1175
+ // Convert the iframe html into textarea source code
1176
+ var code = updateTextAreaCallback ? updateTextAreaCallback(html) : html;
1177
+
1178
+ // Update the textarea checksum
1179
+ if (options.updateFrame)
1180
+ editor.areaChecksum = checksum(code);
1181
+
1182
+ // Update the textarea and trigger the change event
1183
+ if (code !== $area.val()) {
1184
+ $area.val(code);
1185
+ $(editor).triggerHandler(CHANGE);
1186
+ }
1187
+
1188
+ }
1189
+
1190
+ })(jQuery);