JAVASCRIPT 8
Editorengine.js Guest on 21st November 2020 10:02:09 AM
  1. /*
  2.         LicencovĂĄno pod MIT LicencĂ­, jejĂ­ celĂ˝ text je uveden v souboru licence.txt
  3.         Licenced under the MIT Licence, complete text is available in licence.txt file
  4. */
  5.  
  6. /**
  7.  * @overview WYSIWYG Editor
  8.  * @version 2.0
  9.  * @author zara
  10. */  
  11.  
  12. /**
  13.  * Pokud pouzivame i ColorPicker, bude tento vyuzit. Jeho optiony patri do vlastnosti 'colorPickerOptions' v definici
  14.  * ovladacich prvku na barvu textu a/nebo pozadi.
  15.  * @class Editor
  16.  * @group jak-widgets
  17.  */
  18. JAK.Editor = JAK.ClassMaker.makeClass({
  19.         NAME: "JAK.Editor",
  20.         VERSION: "2.0"
  21. });
  22.  
  23. /**
  24.  * @param {node || string} id textarea, ktera ma byt editorem nahrazena
  25.  * @param {object} [opts] asociativni pole parametru
  26.  * @param {string} [opts.imagePath="img/"] cesta k obrazkum s lomitkem na konci
  27.  * @param {object[]} [opts.controls] pole ovladacich prvku editoru
  28.  * @param {object} [opts.style] objekt vychozich stylu
  29.  * @param {HTMLElement} [opts.controlBox] element, kam se budou alternativne pripojovat tlacitka
  30.  */
  31. JAK.Editor.prototype.$constructor = function(id, opts) {
  32.         if (JAK.Browser.client == "konqueror") { return; }
  33.  
  34.         /* init */
  35.         this.options = {
  36.                 imagePath:"img/",
  37.                 controls:[],
  38.                 style:{}
  39.         }
  40.         for (var p in opts) { this.options[p] = opts[p]; }
  41.         this.dom = {
  42.                 container:JAK.mel("div", {className:"editor"}, {position:"relative"})
  43.         }
  44.         this.dom.controlBox = this.options.controlBox || this.dom.container;
  45.  
  46.         this.ec = [];
  47.         this.controls = [];
  48.         this.getContentHooks = []; //pole asociativnich odkazu {obj: obj, method: xxx}
  49.        
  50.         this.dom.ta = JAK.gel(id);
  51.         this.width = this.dom.ta.width || this.dom.ta.clientWidth;
  52.         this.height = this.dom.ta.height || this.dom.ta.clientHeight;
  53.         if (!this.width || !this.height) { return; }
  54.         this.dom.container.style.width = this.width+"px";
  55.        
  56.  
  57.         /* construct */
  58.         this.dom.ta.style.display = "none";
  59.         this.dom.ta.parentNode.insertBefore(this.dom.container,this.dom.ta.nextSibling);
  60.         this._buildInstance(this.width,this.height);
  61.         this._buildControls();
  62.         this._lock(this.dom.controls);
  63.         /* insert initial text */
  64.         this.setContent(this.dom.ta.value);
  65.         if (this.dom.ta.form) {
  66.                 JAK.Events.addListener(this.dom.ta.form,"submit",this,"submit");
  67.         }
  68.         this.refresh();  
  69. }
  70.  
  71. JAK.Editor.prototype.$destructor = function() {
  72.         for (var i=0;i<this.controls.length;i++) {
  73.                 this.controls[i].$destructor();
  74.         }
  75.         for (var i=0;i<this.ec.length;i++) {
  76.                 JAK.Events.removeListener(this.ec[i]);
  77.         }
  78.         for (var p in this) { this[p] = null; }
  79. }
  80.  
  81. JAK.Editor.prototype.setContent = function(data) {
  82.         this.instance.setContent(data);
  83. }
  84.  
  85. JAK.Editor.prototype.getContent = function() {
  86.         var txt = this.instance.getContent();
  87.         for(var i = 0; i < this.getContentHooks.length; i++) {
  88.                 var obj = this.getContentHooks[i].obj;
  89.                 var method = this.getContentHooks[i].method;
  90.                 if (typeof method == 'string' ) {
  91.                         txt = obj[method].call(obj, txt);
  92.                 } else {
  93.                         txt = method.call(obj, txt);
  94.                 }
  95.         }
  96.         return txt;
  97. }
  98.  
  99. JAK.Editor.prototype.getContainer = function() {
  100.         return this.instance.getContainer();
  101. }
  102.  
  103. JAK.Editor.prototype.getInstance = function() {
  104.         return this.instance;
  105. }
  106.  
  107. JAK.Editor.prototype.submit = function() {
  108.         for (var i=0;i<this.controls.length;i++) {
  109.                 this.controls[i].submit();
  110.         }
  111.         this.dom.ta.value = this.getContent();
  112. }
  113.  
  114. JAK.Editor.prototype.commandExec = function(command, args) {
  115.         if (JAK.Browser.client == "gecko" && command == "hilitecolor") {
  116.                 this.instance.commandExec("usecss",false);
  117.                 this.instance.commandExec(command, args);
  118.                 this.instance.commandExec("usecss",true);
  119.         } else {
  120.                 if (this.instance.commandQuerySupported("stylewithcss")) { this.instance.commandExec("stylewithcss",false); }
  121.                 if (this.instance.commandQuerySupported("usecss")) { this.instance.commandExec("usecss",true); }
  122.                 var isCaret = this._isCaret();
  123.                 //pokud neni kurzor v editoru, tak klikani na cudkliky se ma aplikovat na vsechno co je uvnitr
  124.                 if (!isCaret) {
  125.                         if (JAK.Browser.client == 'gecko') {
  126.                                 var r = this.instance._getRange();
  127.                                 r.selectNodeContents(this.instance.getContainer());
  128.                         } else if (JAK.Browser.client == 'ie' || JAK.Browser.client == 'opera') {
  129.                                 this.selectNode(this.instance.getContainer());
  130.                         //chrome a safari
  131.                         } else {
  132.                                 var s = this.instance._getSelection();
  133.                                 var e = this.instance.getContainer();
  134.                                 s.setBaseAndExtent(e, 0, e, e.innerText.length - 1);
  135.                         }
  136.  
  137.                 }
  138.  
  139.                 //vlastni command
  140.                 this.instance.commandExec(command, args);
  141.  
  142.                 //pokud jsme nahore vytvorily selekci, tak ji zase zrusime
  143.                 if (!isCaret) {
  144.                         var selection = this.instance._getSelection();
  145.                         if (selection.empty) {
  146.                                 selection.empty();
  147.                         } else if (JAK.Browser.client == 'gecko') {
  148.                                 selection.collapseToStart(); //FF potrebuje mit nejakou range pro dotaz document.queryCommandState, kterym tlacitko zjistuje, zda ma byt zamackle. takto odbarvime text, a udelame selekci na zacatek, nicmene tlacitka se pak nezasednou
  149.                         } else {
  150.                                 selection.removeAllRanges();
  151.                         }
  152.                 }
  153.         }
  154.         this.refresh();
  155. }
  156.  
  157. /**
  158.  * zjisteni zda je textovy kruzor v editoru, nebo ne.
  159.  * @return {bool}
  160.  */
  161. JAK.Editor.prototype._isCaret = function() {
  162.         var node = this.getSelectedNode();
  163.         var container = this.instance.getContainer();
  164.  
  165.         while (node.parentNode) {
  166.                 if (node == container) { return true; }
  167.                 node = node.parentNode;
  168.         }
  169.  
  170.         return false;
  171. }
  172.  
  173. JAK.Editor.prototype.commandQueryState = function(command) {
  174.         return this.instance.commandQueryState(command);
  175. }
  176.  
  177. JAK.Editor.prototype.commandQueryValue = function(command) {
  178.         return this.instance.commandQueryValue(command);
  179. }
  180.  
  181. JAK.Editor.prototype.commandQuerySupported = function(command) {
  182.         return this.instance.commandQuerySupported(command);
  183. }
  184.  
  185. JAK.Editor.prototype._buildControls = function() {
  186.         this.dom.controls = JAK.cel("div", "editor-controls");
  187.         var elm = this.dom.controlBox;
  188.        
  189.         elm.insertBefore(this.dom.controls, elm.firstChild);
  190.         if (JAK.Browser.client != "opera") {
  191.                 this.ec.push(JAK.Events.addListener(this.dom.controls,"mousedown",this,"_cancelDef",false,true));
  192.                 this.ec.push(JAK.Events.addListener(this.dom.controls,"click",this,"_cancelDef",false,true));
  193.         }
  194.         for (var i=0;i<this.options.controls.length;i++) {
  195.                 var c = this.options.controls[i];
  196.                 if (!(c.type in JAK.EditorControls)) { continue; }
  197.                 var obj = JAK.EditorControls[c.type];
  198.                
  199.                 var opts = {};
  200.                 for (var p in obj) { if (p != "object") { opts[p] = obj[p]; } }
  201.                 for (var p in c) { if (c != "type") { opts[p] = c[p]; } }
  202.                
  203.                 var inst = new obj.object(this,opts);
  204.                 this.controls.push(inst);
  205.                 this.dom.controls.appendChild(inst.dom.container);
  206.                 if (obj.label) { inst.dom.container.title = obj.label; }
  207.         }
  208. }
  209.  
  210. JAK.Editor.prototype._buildInstance = function(w,h) {
  211.         this.dom.content = JAK.mel("div", null, {overflow:"auto",position:"relative"});
  212.         this.setDimensions(w,h);
  213.  
  214.         this.dom.container.appendChild(this.dom.content);
  215.         if (this.dom.content.contentEditable && JAK.Browser.client !== 'gecko' /*|| JAK.Browser.client == "opera"*/) { //Firefox 3 sice umi contentEditable ale hazi to chyby, 3.6 nehaze chyby, ale pokud je vpravo scrollbar, tahnutim mysi za nej se oznacuje text v editoru
  216.                 this.instance = new JAK.Editor.Instance(this/*,w,height*/);
  217.         } else {
  218.                 this.instance = new JAK.Editor.Instance.Iframe(this/*,w,height*/);
  219.         }
  220.         if (typeof(this.options.style) == "string") {
  221.                 this.addStyle(this.options.style);
  222.         } else {
  223.                 for (var p in this.options.style) {
  224.                         this.instance.elm.style[p] = this.options.style[p];
  225.                 }
  226.         }
  227.         this.ec.push(JAK.Events.addListener(this.instance.elm,"click",this,"_click",false,true));
  228.         this.ec.push(JAK.Events.addListener(this.instance.elm,"mouseup",this,"refresh",false,true));
  229.         this.ec.push(JAK.Events.addListener(this.instance.key,"keyup",this,"refresh",false,true));
  230. }
  231.  
  232. JAK.Editor.prototype.setDimensions = function(w,h) {
  233.         this.width = w;
  234.         this.height = h;
  235.  
  236.         var p = 3;
  237.         var width = w-2*p;
  238.         var height = h-2*p;
  239.         JAK.DOM.setStyle(this.dom.content, {padding:p+"px",width:width+"px",height:height+"px",overflow:"auto",position:"relative"});
  240.  
  241.         if (this.instance){
  242.                 this.instance.refresh();
  243.         }
  244. }
  245.  
  246. JAK.Editor.prototype.refresh = function() {
  247.         this.instance.refresh();
  248.         for (var i=0;i<this.controls.length;i++) {
  249.                 this.controls[i].refresh();
  250.         }
  251. }
  252.  
  253. JAK.Editor.prototype.addStyle = function(str) {
  254.         var s = this.instance.doc.createElement('style');
  255.         s.type = "text/css";
  256.         if (JAK.Browser.client == "ie") {
  257.                 s.styleSheet.cssText = str;
  258.         } else {
  259.                 var t = JAK.ctext(str);
  260.                 s.appendChild(t);                      
  261.         }
  262.         this.instance.doc.getElementsByTagName('head')[0].appendChild(s);
  263. }
  264.  
  265. JAK.Editor.prototype.registerGetContentHook = function(obj, func) {
  266.         this.getContentHooks.push({obj: obj, method: func});
  267. }
  268.  
  269.  
  270. JAK.Editor.prototype._cancelDef = function(e, elm) {
  271.         JAK.Events.cancelDef(e);
  272. }
  273.  
  274. JAK.Editor.prototype._lock = function(node) {
  275.         if (node.setAttribute && node.contentEditable != true && node.nodeName != 'input' && node.nodeName != 'textarea') {
  276.                 node.setAttribute('unselectable','on');
  277.         }
  278.  
  279.         for (var i=0;i<node.childNodes.length;i++) {
  280.                 this._lock(node.childNodes[i]);
  281.         }
  282. }
  283.  
  284. /**
  285.  * ziskani focusu editoru, po zavolani teto metody jde do editoru primo psat, kurzor bude na zacatku
  286.  */
  287. JAK.Editor.prototype.focus = function() {
  288.         if (JAK.Browser.client == 'ie') {
  289.                 var that = this;
  290.                 setTimeout(function(){that.instance.elm.focus();},1); //ze zahadneho duvodu to v IE bez timeoutu neda kurzor do editoru
  291.         } else {
  292.                 if (this.instance.ifr) {
  293.                         this.instance.ifr.focus();
  294.                 } else {
  295.                         this.instance.elm.focus();
  296.                 }
  297.         }
  298. }
  299.  
  300. /**
  301.  * vybrani celeho obsahu editoru a zvyrazneni
  302.  */
  303. JAK.Editor.prototype.selectAll = function() {
  304.         if (JAK.Browser.client == 'gecko') {
  305.                 var r = this.instance._getRange();
  306.                 r.selectNodeContents(this.instance.getContainer());
  307.                 var s = this.instance._getSelection();
  308.                 s.addRange(r);
  309.         } else if (JAK.Browser.client == 'ie' || JAK.Browser.client == 'opera') {
  310.                 this.selectNode(this.instance.getContainer());
  311.         //chrome a safari
  312.         } else {
  313.                 var s = this.instance._getSelection();
  314.                 var e = this.instance.getContainer();
  315.                 s.setBaseAndExtent(e, 0, e, e.innerText.length - 1);
  316.         }
  317. }
  318.  
  319. /**
  320.  * metoda vybere vse a nastavi caret do editoru, takze clovek muze zacit psat a vse v nem prepsat
  321.  */
  322. JAK.Editor.prototype.selectAllWithFocus = function() {
  323.         this.focus();
  324.         if (JAK.Browser.client == 'ie') {
  325.                 var that = this;
  326.                 setTimeout(function(){that.selectAll();},10); //tohle musi mit telsi timeout nez je ve focus()
  327.         } else {
  328.                 this.selectAll();
  329.         }
  330. }
  331.  
  332. /* range metoda - zjisteni focusnuteho prvku */
  333. JAK.Editor.prototype.getSelectedNode = function() {
  334.         var elm = false;
  335.         var r = this.instance._getRange();
  336.         if (r.parentElement) {
  337.                 elm = (r.item ? r.item(0) : r.parentElement());
  338.         } else {
  339.                 elm = r.commonAncestorContainer;
  340.                 if (!r.collapsed && r.startContainer == r.endContainer && r.startOffset - r.endOffset < 2) {
  341.                         if (r.startContainer.hasChildNodes()) { elm = r.startContainer.childNodes[r.startOffset]; }
  342.                 }
  343.         }
  344.         return elm;
  345. }
  346.  
  347. /* range metoda - presun range na dany node */
  348. JAK.Editor.prototype.selectNode = function(node) {
  349.         if (JAK.Browser.client == "ie") {
  350.                 var r = this.instance.doc.body.createTextRange();
  351.                 r.moveToElementText(node);
  352.                 r.select();
  353.         } else {
  354.                 var s = this.instance._getSelection();
  355.                 var r = this.instance.doc.createRange();
  356.                 r.selectNode(node);
  357.                 s.removeAllRanges();
  358.                 s.addRange(r);
  359.         }
  360. }
  361.  
  362. JAK.Editor.prototype.createRangeFromNode = function(node) {
  363.         if (JAK.Browser.client == "ie") {
  364.                 var r = this.instance.doc.body.createTextRange();
  365.                 r.moveToElementText(node);
  366.         } else {
  367.                 var r = this.instance.doc.createRange();
  368.                 r.selectNode(node);
  369.         }
  370.         return r;
  371. }
  372.  
  373. /* range metoda - zjisteni vybraneho kodu */
  374. JAK.Editor.prototype.getSelectedHTML = function() {
  375.         var range = this.instance._getRange();
  376.         if (JAK.Browser.client == "ie") {
  377.                 return range.htmlText;
  378.         } else {
  379.                 var fragment = range.cloneContents();
  380.                 var div = this.instance.doc.createElement("div");
  381.                 while (fragment.firstChild) {
  382.                         div.appendChild(fragment.childNodes[0]);
  383.                 }
  384.                 return div.innerHTML;
  385.         }
  386. }
  387.  
  388. /* range metoda - vlozeni html */
  389. JAK.Editor.prototype.insertHTML = function(html) {
  390.         var range = this.instance._getRange();
  391.         if (JAK.Browser.client == "ie") {
  392.                 //test zda vybrany node kam apenduju je uvnitr editoru
  393.                 var selectedNode = this.getSelectedNode();
  394.                 while(true) {
  395.                         if (selectedNode == this.instance.elm) {
  396.                                 range.pasteHTML(html);
  397.                                 break;
  398.                         }
  399.                         if (selectedNode.parentNode) {
  400.                                 selectedNode = selectedNode.parentNode;
  401.                         } else {
  402.                                 this.instance.elm.innerHTML += html;
  403.                                 break;
  404.                         }
  405.                 }
  406.  
  407.  
  408.         } else {
  409.                 var fragment = this.instance.doc.createDocumentFragment();
  410.                 var div = this.instance.doc.createElement("div");
  411.                 div.innerHTML = html;
  412.                 while (div.firstChild) {
  413.                         // the following call also removes the node from div
  414.                         fragment.appendChild(div.firstChild);
  415.                 }
  416.                 range.deleteContents();
  417.                 range.insertNode(fragment);
  418.         }
  419.         this.refresh();
  420. }
  421.  
  422. /* range metoda - vlozeni node */
  423. JAK.Editor.prototype.insertNode = function(node) {
  424.         var range = this.instance._getRange();
  425.         if (JAK.Browser.client == "ie") {
  426.                 this.insertHTML(node.outerHTML);
  427.         } else {
  428.                 range.deleteContents();
  429.                 range.insertNode(node);
  430.         }
  431.         this.refresh();
  432. }
  433.  
  434. JAK.Editor.prototype._click = function(e, elm) {
  435.         if (JAK.Browser.client == "safari") {
  436.                 var tag = e.target.tagName;
  437.                 if (tag && tag.toLowerCase() == "img") { this.selectNode(e.target); }
  438.         }
  439.         this.refresh();
  440. }
  441.  
  442. /* --- */
  443.  
  444. /**
  445.  * @class
  446.  * @group jak-widgets
  447.  */
  448. JAK.Editor.Instance = JAK.ClassMaker.makeClass({
  449.         NAME: "JAK.Editor.Instance",
  450.         VERSION: "2.0"
  451. });
  452.  
  453. JAK.Editor.Instance.prototype.$constructor = function(owner) {
  454.         this.ec = [];
  455.         this.owner = owner;
  456.         this.elm = this.owner.dom.content;
  457.         JAK.DOM.addClass(this.elm, "editor-content");
  458.         this.doc = document;
  459.         this.win = window;
  460.         this.elm.setAttribute('contentEditable','true');
  461.         //opera pri pouziti zarovnavacich tlacitek prvni zarovnani nastavi contentEditable divu, takze pokd nastavime vychozi, bude se chovat jiz moralne
  462.         if (JAK.Browser.client == "opera") {
  463.                 this.elm.align = 'left';
  464.         }
  465.         this.key = this.elm;
  466. }
  467.  
  468. JAK.Editor.Instance.prototype.$destructor = function() {
  469.         for (var i=0;i<this.ec.length;i++) {
  470.                 JAK.Events.removeListener(this.ec[i]);
  471.         }
  472. }
  473.  
  474. JAK.Editor.Instance.prototype.getContent = function() {
  475.         return this.elm.innerHTML;
  476. }
  477.  
  478. JAK.Editor.Instance.prototype.getContainer = function() {
  479.         return this.elm;
  480. }
  481.  
  482. JAK.Editor.Instance.prototype.setContent = function(data) {
  483.         var d = data || "<br/>";
  484.         this.elm.innerHTML = d;
  485. }
  486.  
  487. JAK.Editor.Instance.prototype.commandExec = function(command, args) {
  488.         this.doc.execCommand(command,false,args);
  489. }
  490.  
  491. JAK.Editor.Instance.prototype.commandQueryState = function(command) {
  492.         return this.doc.queryCommandState(command);
  493. }
  494.  
  495. JAK.Editor.Instance.prototype.commandQueryValue = function(command) {
  496.         return this.doc.queryCommandValue(command);
  497. }
  498.  
  499. JAK.Editor.Instance.prototype.commandQuerySupported = function(command) {
  500.         return (JAK.Browser.client == "gecko" ? this.doc.queryCommandEnabled(command) : this.doc.queryCommandSupported(command));
  501. }
  502.  
  503. JAK.Editor.Instance.prototype._getSelection = function() {
  504.         return (this.win.getSelection ? this.win.getSelection() : this.doc.selection);
  505. }
  506.  
  507. JAK.Editor.Instance.prototype._getRange = function() {
  508.         var s = this._getSelection();
  509.         if (!s) { return false; }
  510.         if (s.rangeCount > 0) { return s.getRangeAt(0); }
  511.         if (s.createRange) {
  512.                 return s.createRange();
  513.         } else {
  514.                 var r = this.doc.createRange();
  515.                 return r;
  516.         }
  517. }
  518.  
  519. JAK.Editor.Instance.prototype.saveRange = function() {
  520.         this.selection = this._getSelection();
  521.         this.range = this._getRange();
  522. }
  523.  
  524. JAK.Editor.Instance.prototype.loadRange = function() {
  525.         if (this.range) {
  526.                 if (window.getSelection) {
  527.                         this.selection.removeAllRanges();
  528.                         this.selection.addRange(this.range);
  529.                 } else {
  530.                         this.range.select();
  531.                 }
  532.         }
  533. }
  534.  
  535. JAK.Editor.Instance.prototype.refresh = function() {}
  536.  
  537. /* --- */
  538.  
  539. /**
  540.  * @class instance
  541.  * @augments JAK.Editor.Instance
  542.  */
  543. JAK.Editor.Instance.Iframe = JAK.ClassMaker.makeClass({
  544.         NAME: "JAK.Editor.Instance.Iframe",
  545.         VERSION: "2.0",
  546.         EXTEND: JAK.Editor.Instance
  547. });
  548.  
  549. JAK.Editor.Instance.Iframe.prototype.$constructor = function(owner) {
  550.         this.ec = [];
  551.         this.owner = owner;
  552.         this.ifr = JAK.mel("iframe", null, {width:"100%", height:"100%"});
  553.         this.ifr.setAttribute("frameBorder","0");
  554.         this.ifr.setAttribute("allowTransparency","true");
  555.         this.ifr.setAttribute("scrolling","no");
  556.        
  557.         this.owner.dom.content.appendChild(this.ifr);
  558.        
  559.         this.win = this.ifr.contentWindow;
  560.         this.doc = this.ifr.contentWindow.document;
  561.         if (JAK.Browser.client == "ie") { this.doc.designMode = "on"; }
  562.     this.doc.open();
  563.     this.doc.write('<html><head></head><body class="editor-content" style="margin:0px !important; background-color:transparent !important; ""></body></html>');
  564.     this.doc.close();
  565.         if (JAK.Browser.client != "ie") {
  566.                 this.doc.designMode = "on";
  567.                 this.doc.designMode = "off";
  568.                 this.doc.designMode = "on";
  569.         }
  570.        
  571.     this.elm = this.doc.body;
  572.         this.key = this.elm.parentNode;
  573.  
  574.         JAK.Events.addListener(this.elm.parentNode, "click", window, JAK.EditorControl.Select.checkHide);
  575.  
  576. }
  577.  
  578. JAK.Editor.Instance.Iframe.prototype.refresh = function() {
  579.         var h = this.elm.offsetHeight;
  580.         var contentH =this.owner.dom.content.offsetHeight;
  581.         h = Math.max(h,contentH);
  582.         this.ifr.style.height = h + "px";
  583. }

Paste is for source code and general debugging text.

Login or Register to edit, delete and keep track of your pastes and more.

Raw Paste

Login or Register to edit or fork this paste. It's free.