Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Stylist/Stylist.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Stylist/Stylist.js,v diff -u -r1.2 -r1.3 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Stylist/Stylist.js 27 Mar 2009 08:20:44 -0000 1.2 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Stylist/Stylist.js 23 May 2010 12:00:02 -0000 1.3 @@ -1,3 +1,709 @@ -/* This compressed file is part of Xinha. For uncompressed sources, forum, and bug reports, go to xinha.org */ -/* This file is part of version 0.96beta2 released Fri, 20 Mar 2009 11:01:14 +0100 */ -Xinha.Config.prototype.css_style={};Xinha.Config.prototype.stylistLoadStylesheet=function(a,d){if(!d){d={}}var c=Xinha.ripStylesFromCSSFile(a);for(var b in c){if(d[b]){this.css_style[b]=d[b]}else{this.css_style[b]=c[b]}}this.pageStyleSheets[this.pageStyleSheets.length]=a};Xinha.Config.prototype.stylistLoadStyles=function(b,d){if(!d){d={}}var c=Xinha.ripStylesFromCSSString(b);for(var a in c){if(d[a]){this.css_style[a]=d[a]}else{this.css_style[a]=c[a]}}this.pageStyle+=b};Xinha.prototype._fillStylist=function(){if(!this.plugins.Stylist.instance.dialog){return false}var c=this.plugins.Stylist.instance.dialog.main;c.innerHTML="";var f=true;var a=this._getSelection();var i=this._activeElement(a);for(var l in this.config.css_style){var m=null;var k=l.trim();var d=true;var b=i;if(d&&/[^a-zA-Z0-9_.-]/.test(k)){d=false}if(k.indexOf(".")<0){d=false}if(d&&(k.indexOf(".")>0)){m=k.substring(0,k.indexOf(".")).toLowerCase();k=k.substring(k.indexOf("."),k.length);if(i!=null&&i.tagName.toLowerCase()==m){d=true;b=i}else{if(this._getFirstAncestor(this._getSelection(),[m])!=null){d=true;b=this._getFirstAncestor(this._getSelection(),[m])}else{if((m=="div"||m=="span"||m=="p"||(m.substr(0,1)=="h"&&m.length==2&&m!="hr"))){if(!this._selectionEmpty(this._getSelection())){d=true;b="new"}else{b=this._getFirstAncestor(a,["p","h1","h2","h3","h4","h5","h6","h7"]);if(b!=null){d=true}else{d=false}}}else{d=false}}}}if(d){k=k.substring(k.indexOf("."),k.length);k=k.replace("."," ");if(b==null){if(this._selectionEmpty(this._getSelection())){b=this._getFirstAncestor(this._getSelection(),null)}else{b="new";m="span"}}}var e=(this._ancestorsWithClasses(a,m,k).length>0?true:false);var g=this._ancestorsWithClasses(a,m,k);if(d){var j=document.createElement("a");j.onfocus=function(){this.blur()};j._stylist_className=k.trim();j._stylist_applied=e;j._stylist_appliedTo=g;j._stylist_applyTo=b;j._stylist_applyTag=m;j.innerHTML=this.config.css_style[l];j.href="javascript:void(0)";var h=this;j.onclick=function(){if(this._stylist_applied==true){h._stylistRemoveClasses(this._stylist_className,this._stylist_appliedTo)}else{h._stylistAddClasses(this._stylist_applyTo,this._stylist_applyTag,this._stylist_className)}return false};j.style.display="block";j.style.paddingLeft="3px";j.style.paddingTop="1px";j.style.paddingBottom="1px";j.style.textDecoration="none";if(e){j.style.background="Highlight";j.style.color="HighlightText"}c.appendChild(j)}}};Xinha.prototype._stylistAddClasses=function(c,a,b){if(c=="new"){this.insertHTML("<"+a+' class="'+b+'">'+this.getSelectedHTML()+"")}else{if(a!=null&&c.tagName.toLowerCase()!=a){var d=this.switchElementTag(c,a);if(typeof c._stylist_usedToBe!="undefined"){d._stylist_usedToBe=c._stylist_usedToBe;d._stylist_usedToBe[d._stylist_usedToBe.length]={tagName:c.tagName,className:c.getAttribute("class")}}else{d._stylist_usedToBe=[{tagName:c.tagName,className:c.getAttribute("class")}]}Xinha.addClasses(d,b)}else{Xinha._addClasses(c,b)}}this.focusEditor();this.updateToolbar()};Xinha.prototype._stylistRemoveClasses=function(b,c){for(var a=0;a0&&a._stylist_usedToBe[a._stylist_usedToBe.length-1].className!=null){var h=a._stylist_usedToBe[a._stylist_usedToBe.length-1];var k=Xinha.arrayFilter(h.className.trim().split(" "),function(i){if(i==null||i.trim()==""){return false}return true});if((g.length==0)||(Xinha.arrayContainsArray(g,k)&&Xinha.arrayContainsArray(k,g))){a=this.switchElementTag(a,h.tagName);g=k}else{a._stylist_usedToBe=[]}}if(g.length>0||a.tagName.toLowerCase()!="span"||(a.id&&a.id!="")){a.className=g.join(" ").trim()}else{var b=a.parentNode;var d;while(a.hasChildNodes()){if(a.firstChild.nodeType==1){this._stylistRemoveClassesFull(a.firstChild,c)}d=a.removeChild(a.firstChild);b.insertBefore(d,a)}b.removeChild(a)}}};Xinha.prototype.switchElementTag=function(d,b){var c=d.parentNode;var f=this._doc.createElement(b);if(Xinha.is_ie||d.hasAttribute("id")){f.setAttribute("id",d.getAttribute("id"))}if(Xinha.is_ie||d.hasAttribute("style")){f.setAttribute("style",d.getAttribute("style"))}var e=d.childNodes;for(var a=0;a0)?a.offsetHeight-h.panel.offsetHeight:0)+"px";c.rootElem.style.height=b.offsetHeight+"px";e.sizeEditor();break;case"hide":f.resize();break}});e.notifyOn("before_resize",function(){if(!c.attached){return}c.rootElem.style.height=b.offsetHeight+"px"});e.notifyOn("resize",function(){if(!c.attached){return}f.resize()})};Stylist.prototype.resize=function(){var e=this.editor;var b=this.dialog.rootElem;if(b.style.display=="none"){return}var d=b.parentNode;var a=d.offsetHeight;for(var c=0;c 0)) + { + // requires specific html tag + tag = className.substring(0, className.indexOf('.')).toLowerCase(); + className = className.substring(className.indexOf('.'), className.length); + + // To apply we must have an ancestor tag that is the right type + if(active_elem != null && active_elem.tagName.toLowerCase() == tag) + { + applicable = true; + apply_to = active_elem; + } + else + { + if(this._getFirstAncestor(this._getSelection(), [tag]) != null) + { + applicable = true; + apply_to = this._getFirstAncestor(this._getSelection(), [tag]); + } + else + { + // alert (this._getFirstAncestor(this._getSelection(), tag)); + // If we don't have an ancestor, but it's a div/span/p/hx stle, we can make one + if(( tag == 'div' || tag == 'span' || tag == 'p' + || (tag.substr(0,1) == 'h' && tag.length == 2 && tag != 'hr'))) + { + if(!this._selectionEmpty(this._getSelection())) + { + applicable = true; + apply_to = 'new'; + } + else + { + // See if we can get a paragraph or header that can be converted + apply_to = this._getFirstAncestor(sel, ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'h7']); + if(apply_to != null) + { + applicable = true; + } + else + { + applicable = false; + } + } + } + else + { + applicable = false; + } + } + } + } + + if(applicable) + { + // Remove the first . + className = className.substring(className.indexOf('.'), className.length); + + // Replace any futher ones with spaces (for multiple class definitions) + className = className.replace('.', ' '); + + if(apply_to == null) + { + if(this._selectionEmpty(this._getSelection())) + { + // Get the previous element and apply to that + apply_to = this._getFirstAncestor(this._getSelection(), null); + } + else + { + apply_to = 'new'; + tag = 'span'; + } + } + } + + var applied = (this._ancestorsWithClasses(sel, tag, className).length > 0 ? true : false); + var applied_to = this._ancestorsWithClasses(sel, tag, className); + + if(applicable) + { + var anch = document.createElement('a'); + anch.onfocus = function () { this.blur() } // prevent dotted line around link that causes horizontal scrollbar + anch._stylist_className = className.trim(); + anch._stylist_applied = applied; + anch._stylist_appliedTo = applied_to; + anch._stylist_applyTo = apply_to; + anch._stylist_applyTag = tag; + + anch.innerHTML = this.config.css_style[x]; + anch.href = 'javascript:void(0)'; + var editor = this; + anch.onclick = function() + { + if(this._stylist_applied == true) + { + editor._stylistRemoveClasses(this._stylist_className, this._stylist_appliedTo); + } + else + { + editor._stylistAddClasses(this._stylist_applyTo, this._stylist_applyTag, this._stylist_className); + } + return false; + } + + anch.style.display = 'block'; + anch.style.paddingLeft = '3px'; + anch.style.paddingTop = '1px'; + anch.style.paddingBottom = '1px'; + anch.style.textDecoration = 'none'; + + if(applied) + { + anch.style.background = 'Highlight'; + anch.style.color = 'HighlightText'; + } + anch.style.position = 'relative'; + main.appendChild(anch); + } + } +}; + + +/** + * Add the given classes (space seperated list) to the currently selected element + * (will add a span if none selected) + */ +Xinha.prototype._stylistAddClasses = function(el, tag, classes) + { + if(el == 'new') + { + this.insertHTML('<' + tag + ' class="' + classes + '">' + this.getSelectedHTML() + ''); + } + else + { + if(tag != null && el.tagName.toLowerCase() != tag) + { + // Have to change the tag! + var new_el = this.switchElementTag(el, tag); + + if(typeof el._stylist_usedToBe != 'undefined') + { + new_el._stylist_usedToBe = el._stylist_usedToBe; + new_el._stylist_usedToBe[new_el._stylist_usedToBe.length] = {'tagName' : el.tagName, 'className' : el.getAttribute('class')}; + } + else + { + new_el._stylist_usedToBe = [{'tagName' : el.tagName, 'className' : el.getAttribute('class')}]; + } + + Xinha.addClasses(new_el, classes); + } + else + { + Xinha._addClasses(el, classes); + } + } + this.focusEditor(); + this.updateToolbar(); + }; + +/** + * Remove the given classes (space seperated list) from the given elements (array of elements) + */ +Xinha.prototype._stylistRemoveClasses = function(classes, from) + { + for(var x = 0; x < from.length; x++) + { + this._stylistRemoveClassesFull(from[x], classes); + } + this.focusEditor(); + this.updateToolbar(); + }; + +Xinha.prototype._stylistRemoveClassesFull = function(el, classes) +{ + if(el != null) + { + var thiers = el.className.trim().split(' '); + var new_thiers = [ ]; + var ours = classes.split(' '); + for(var x = 0; x < thiers.length; x++) + { + var exists = false; + for(var i = 0; exists == false && i < ours.length; i++) + { + if(ours[i] == thiers[x]) + { + exists = true; + } + } + if(exists == false) + { + new_thiers[new_thiers.length] = thiers[x]; + } + } + + if(new_thiers.length == 0 && el._stylist_usedToBe && el._stylist_usedToBe.length > 0 && el._stylist_usedToBe[el._stylist_usedToBe.length - 1].className != null) + { + // Revert back to what we were IF the classes are identical + var last_el = el._stylist_usedToBe[el._stylist_usedToBe.length - 1]; + var last_classes = Xinha.arrayFilter(last_el.className.trim().split(' '), function(c) { if (c == null || c.trim() == '') { return false;} return true; }); + + if( + (new_thiers.length == 0) + || + ( + Xinha.arrayContainsArray(new_thiers, last_classes) + && Xinha.arrayContainsArray(last_classes, new_thiers) + ) + ) + { + el = this.switchElementTag(el, last_el.tagName); + new_thiers = last_classes; + } + else + { + // We can't rely on the remembered tags any more + el._stylist_usedToBe = [ ]; + } + } + + if( new_thiers.length > 0 + || el.tagName.toLowerCase() != 'span' + || (el.id && el.id != '') + ) + { + el.className = new_thiers.join(' ').trim(); + } + else + { + // Must be a span with no classes and no id, so we can splice it out + var prnt = el.parentNode; + var tmp; + while (el.hasChildNodes()) + { + if (el.firstChild.nodeType == 1) + { + // if el.firstChild is an element, we've got to recurse to make sure classes are + // removed from it and and any of its children. + this._stylistRemoveClassesFull(el.firstChild, classes); + } + tmp = el.removeChild(el.firstChild); + prnt.insertBefore(tmp, el); + } + prnt.removeChild(el); + } + } +}; + +/** + * Change the tag of an element + */ +Xinha.prototype.switchElementTag = function(el, tag) +{ + var prnt = el.parentNode; + var new_el = this._doc.createElement(tag); + + if(Xinha.is_ie || el.hasAttribute('id')) new_el.setAttribute('id', el.getAttribute('id')); + if(Xinha.is_ie || el.hasAttribute('style')) new_el.setAttribute('style', el.getAttribute('style')); + + var childs = el.childNodes; + for(var x = 0; x < childs.length; x++) + { + new_el.appendChild(childs[x].cloneNode(true)); + } + + prnt.insertBefore(new_el, el); + new_el._stylist_usedToBe = [el.tagName]; + prnt.removeChild(el); + this.selectNodeContents(new_el); + return new_el; +}; + +Xinha.prototype._getAncestorsClassNames = function(sel) +{ + // Scan upwards to find a block level element that we can change or apply to + var prnt = this._activeElement(sel); + if(prnt == null) + { + prnt = (Xinha.is_ie ? this._createRange(sel).parentElement() : this._createRange(sel).commonAncestorContainer); + } + + var classNames = [ ]; + while(prnt) + { + if(prnt.nodeType == 1) + { + var classes = prnt.className.trim().split(' '); + for(var x = 0; x < classes.length; x++) + { + classNames[classNames.length] = classes[x]; + } + + if(prnt.tagName.toLowerCase() == 'body') break; + if(prnt.tagName.toLowerCase() == 'table' ) break; + } + prnt = prnt.parentNode; + } + + return classNames; +}; + +Xinha.prototype._ancestorsWithClasses = function(sel, tag, classes) +{ + var ancestors = [ ]; + var prnt = this._activeElement(sel); + if(prnt == null) + { + try + { + prnt = (Xinha.is_ie ? this._createRange(sel).parentElement() : this._createRange(sel).commonAncestorContainer); + } + catch(e) + { + return ancestors; + } + } + var search_classes = classes.trim().split(' '); + + while(prnt) + { + if(prnt.nodeType == 1 && prnt.className) + { + if(tag == null || prnt.tagName.toLowerCase() == tag) + { + var classes = prnt.className.trim().split(' '); + var found_all = true; + for(var i = 0; i < search_classes.length; i++) + { + var found_class = false; + for(var x = 0; x < classes.length; x++) + { + if(search_classes[i] == classes[x]) + { + found_class = true; + break; + } + } + + if(!found_class) + { + found_all = false; + break; + } + } + + if(found_all) ancestors[ancestors.length] = prnt; + } + if(prnt.tagName.toLowerCase() == 'body') break; + if(prnt.tagName.toLowerCase() == 'table' ) break; + } + prnt = prnt.parentNode; + } + + return ancestors; +}; + + +Xinha.ripStylesFromCSSFile = function(URL, skip_imports) +{ + var css = Xinha._geturlcontent(URL); + + return Xinha.ripStylesFromCSSString(css, skip_imports, URL); +}; + +Xinha.ripStylesFromCSSString = function(css, skip_imports, imports_relative_to) +{ + if(!skip_imports) + { + if(!imports_relative_to) + { + imports_relative_to = _editor_url + 'Xinha.css' + } + + var seen = { }; + + function resolve_imports(css, url) + { + seen[url] = true; // protects against infinite recursion + + var RE_atimport = '@import\\s*(url\\()?["\'](.*)["\'].*'; + var imports = css.match(new RegExp(RE_atimport,'ig')); + var m, file, re = new RegExp(RE_atimport,'i'); + + if (imports) + { + var path = url.replace(/\?.*$/,'').split("/"); + path.pop(); + path = path.join('/'); + for (var i=0;i 0) ? main.offsetHeight - args.panel.offsetHeight : 0) + 'px'; + dialog.rootElem.style.height = caption.offsetHeight + "px"; + editor.sizeEditor(); + break; + case 'hide': + stylist.resize(); + break; + } + } + ); + editor.notifyOn('before_resize', + function() + { + if (!dialog.attached) + { + return; + } + dialog.rootElem.style.height = caption.offsetHeight + "px"; + } + ); + editor.notifyOn('resize', + function() + { + if (!dialog.attached) + { + return; + } + stylist.resize(); + } + ); +} +Stylist.prototype.resize = function() +{ + var editor = this.editor; + var rootElem = this.dialog.rootElem; + + if (rootElem.style.display == 'none') return; + + var panelContainer = rootElem.parentNode; + + var newSize = panelContainer.offsetHeight; + for (var i=0; i < panelContainer.childNodes.length;++i) + { + if (panelContainer.childNodes[i] == rootElem || !panelContainer.childNodes[i].offsetHeight) + { + continue; + } + newSize -= panelContainer.childNodes[i].offsetHeight; + } + rootElem.style.height = newSize-5 + 'px'; + this.dialog.main.style.height = newSize - this.dialog.captionBar.offsetHeight -5 + 'px'; +} + +Stylist.prototype.onUpdateToolbar = function() +{ + if(this.dialog) + { + if(this._timeoutID) + { + window.clearTimeout(this._timeoutID); + } + + var e = this.editor; + this._timeoutID = window.setTimeout(function() { e._fillStylist(); }, 250); + } +};