Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/Xinha.css =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/Xinha.css,v diff -u -r1.6 -r1.7 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/Xinha.css 27 Mar 2009 08:20:42 -0000 1.6 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/Xinha.css 23 May 2010 11:58:33 -0000 1.7 @@ -1,3 +1,477 @@ -/* 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 */ -.htmlarea{background:#fff;}.htmlarea td{margin:0;padding:0;}.htmlarea .toolbarRow{width:1px;}.htmlarea .toolbar{cursor:default;background:ButtonFace;padding:3px;border:1px solid;border-color:ButtonHighlight ButtonShadow ButtonShadow ButtonHighlight;}.htmlarea .toolbar table{margin:0;font-family:Tahoma,Verdana,sans-serif;font-size:11px;}.htmlarea .toolbar img{border:none;vertical-align:top;}.htmlarea .toolbar .label{padding:0 3px;}.htmlarea .toolbar .button{background:ButtonFace;color:ButtonText;border:1px solid ButtonFace;padding:1px;margin:0;width:18px;height:18px;}.htmlarea .toolbar a.button:hover{border:1px solid;border-color:ButtonHighlight ButtonShadow ButtonShadow ButtonHighlight;}.htmlarea .toolbar a.buttonDisabled:hover{border-color:ButtonFace;}.htmlarea .toolbar .buttonActive,.htmlarea .toolbar .buttonPressed{padding:2px 0 0 2px;border:1px solid;border-color:ButtonShadow ButtonHighlight ButtonHighlight ButtonShadow;}.htmlarea .toolbar .buttonPressed{background:ButtonHighlight;}.htmlarea .toolbar .indicator{padding:0 3px;overflow:hidden;width:20px;text-align:center;cursor:default;border:1px solid ButtonShadow;}.htmlarea .toolbar .buttonDisabled img{filter:gray() alpha(opacity = 25);-moz-opacity:.25;opacity:.25;}.htmlarea .toolbar .separator{margin:0 3px;border-left:1px solid ButtonShadow;border-right:1px solid ButtonHighlight;width:0;height:18px;padding:0;}.htmlarea .toolbar .space{width:5px;}.htmlarea .toolbar select,.htmlarea .toolbar option{font:11px Tahoma,Verdana,sans-serif;}.htmlarea .toolbar select,.htmlarea .toolbar select:hover,.htmlarea .toolbar select:active{position:relative;top:-2px;margin-bottom:-2px;color:ButtonText;}.htmlarea iframe.xinha_iframe,.htmlarea textarea.xinha_textarea{border:none;}.htmlarea .statusBar{border:1px solid;border-color:ButtonShadow ButtonHighlight ButtonHighlight ButtonShadow;padding:2px 4px;background-color:ButtonFace;color:ButtonText;font:11px Tahoma,Verdana,sans-serif;height:16px;overflow:hidden;}.htmlarea .statusBar .statusBarTree a{padding:2px 5px;color:#00f;}.htmlarea .statusBar .statusBarTree a:visited{color:#00f;}.htmlarea .statusBar .statusBarTree a:hover{background-color:Highlight;color:HighlightText;padding:1px 4px;border:1px solid HighlightText;}.statusBarWidgetContainer{background-color:ButtonFace;}.dialog{color:ButtonText;background:ButtonFace;border:1px outset;border-color:WindowFrame;}div.dialog{padding-bottom:10px;border-radius:8px 8px 0 0;-moz-border-radius:8px 8px 0 0;-webkit-border-top-left-radius:8px;-webkit-border-top-right-radius:8px;box-shadow:9px 9px 10px #444;-moz-box-shadow:9px 9px 10px #444;-webkit-box-shadow:9px 9px 10px #444;}div.dialog.modeless{box-shadow:4px 4px 5px #888;-moz-box-shadow:4px 4px 5px #888;-webkit-box-shadow:4px 4px 5px #888;}div.dialog.chrome{-webkit-box-shadow:none!IMPORTANT;}.panels div.dialog.panel{border-radius:0;-moz-border-radius:0;-webkit-border-radius:0;box-shadow:none;-moz-box-shadow:none;-webkit-box-shadow:none;}.xinha_dialog_background{filter:alpha(opacity=0);-moz-opacity:0;opacity:0;border:none;}.xinha_dialog_background_modal_greyout{background-color:#666;filter:alpha(opacity=70)!IMPORTANT;-moz-opacity:.7;opacity:.7;}.xinha_dialog_background_modal{filter:alpha(opacity=0)!IMPORTANT;-moz-opacity:0;opacity:0;border:none;}body.xinha_dialog_background_modal_greyout{filter:alpha(opacity=100)!IMPORTANT;}body.xinha_dialog_background_modal{filter:alpha(opacity=0);}.dialog .content{padding:2px;}.dialog,.dialog button,.dialog input,.dialog select,.dialog textarea,.dialog table{font:11px Tahoma,Verdana,sans-serif;}.dialog table{border-collapse:collapse;}.dialog .title,.dialog h1{background:ActiveCaption;color:CaptionText;border-bottom:1px solid #000;padding:1px 0 2px 5px;font-size:12px;font-weight:bold;cursor:default;letter-spacing:.01em;}.dialog h1{margin:0;border-radius:8px 8px 0 0;-moz-border-radius:8px 8px 0 0;-webkit-border-top-left-radius:8px;-webkit-border-top-right-radius:8px;}.panels .dialog.panel h1{-moz-border-radius:0;-webkit-border-radius:0;}.dialog .title .button{float:right;border:1px solid #66a;padding:0 1px 0 2px;margin-right:1px;color:#fff;text-align:center;}.dialog .title .button-hilite{border-color:#88f;background:#44c;}.dialog button{width:5.5em;padding:0;}.dialog .closeButton{padding:0;cursor:default;border:1px solid;border-color:ButtonHighlight ButtonShadow ButtonShadow ButtonHighlight;height:11px;width:11px;vertical-align:top;position:absolute;top:3px;right:2px;background-color:ButtonFace;color:ButtonText;font-size:13px;font-family:Tahoma,Verdana,sans-serif;text-align:center;letter-spacing:0;overflow:hidden;}.dialog .buttonColor{width:1em;padding:1px;cursor:default;border:1px solid;border-color:ButtonHighlight ButtonShadow ButtonShadow ButtonHighlight;}.dialog .buttonColor .chooser,.dialog .buttonColor .nocolor{height:.6em;border:1px solid;padding:0 1em;border-color:ButtonShadow ButtonHighlight ButtonHighlight ButtonShadow;}.dialog .buttonClick{border-color:ButtonShadow ButtonHighlight ButtonHighlight ButtonShadow;}.dialog .buttonColor-hilite{border-color:ButtonShadow ButtonHighlight ButtonHighlight ButtonShadow;}.dialog .buttonColor .nocolor{padding:0;}.dialog .buttonColor .nocolor-hilite{background-color:#fff;color:#f00;}.dialog .label{text-align:right;width:6em;}.dialog .value input{width:100%;}.dialog legend{font-weight:bold;}.dialog fieldset table{margin:2px 0;}.dialog .buttons{padding:1em;text-align:center;}.dialog .resizeHandle{-moz-appearance:resizer;width:12px;height:12px;border-bottom:2px solid #000;border-right:2px solid #000;cursor:se-resize;}.popupwin{padding:0;margin:0;}.popupwin .title{background:#fff;color:#000;font-weight:bold;font-size:120%;padding:3px 10px;margin-bottom:10px;border-bottom:1px solid black;letter-spacing:2px;}form{margin:0;border:none;}.htmlarea .panels_top{border-bottom:1px solid;border-color:ButtonShadow;}.htmlarea .panels_right{border-left:1px solid;border-color:ButtonShadow;}.htmlarea .panels_left{border-right:1px solid;border-color:ButtonShadow;}.htmlarea .panels_bottom{border-top:1px solid;border-color:ButtonShadow;}.htmlarea .panel h1{clear:left;font-size:.9em;}.htmlarea .panel{overflow:hidden;background-color:white;padding-bottom:0!IMPORTANT;border:none!IMPORTANT;}.htmlarea .panels_left .panel{border-right:none;border-left:none;}.htmlarea .panels_left h1{border-right:none;}.htmlarea .panels_right .panel{border-right:none;border-left:none;}.htmlarea .panels_left h1{border-left:none;}.htmlarea{border:1px solid black;}.loading{font-family:sans-serif;position:absolute;z-index:998;text-align:center;width:212px;padding:55px 0 5px 0;border:2px solid #ccc;background:url(images/xinha_logo.gif) no-repeat #fff center 5px;}.loading_main{font-size:11px;color:#000;}.loading_sub{font-size:9px;color:#666;text-align:center;}.dialog a img{border:0 none transparent;}.dialog fieldset.collapsed{border:0 none transparent;}.dialog fieldset.collapsed form{display:none;}.hidden{display:none;}.placesmanager{width:95%;overflow:auto;}.filemanager{width:95%;height:200px;overflow:auto;background-color:#fff;}.filemanager div.file{min-width:80px;height:100px;position:relative;float:left;border:1px outset #666;margin:4px;}.placesmanager div.file{min-width:60px;height:70px;position:relative;float:left;border:1px outset #666;margin:4px;}.filemanager div.file:hover,.placesmanager div.file:hover{border:1px solid #333;background:#fffff3;}.filemanager div.selected,.filemanager div.selected:hover,.placesmanager div.selected,.placesmanager div.selected:hover{background:#ffffda;border:1px solid #000;}.filemanager .filename{margin:.5em;color:#222;}.filemanager div.selected .filename{color:#000;}.filemanager img.thumb{width:50px;height:50px;position:absolute;top:50%;left:50%;margin:-25px 0 0 -25px;border:1px solid black;}.filemanager img.icon{width:32px;height:32px;position:absolute;top:50%;left:50%;margin:-16px 0 0 -16px;}.filemanager img.action{width:15px;height:15px;position:absolute;}.filemanager img.delete{bottom:3px;left:20px;}.filemanager img.copy{bottom:3px;left:3px;} \ No newline at end of file +.htmlarea { background: #fff; } +.htmlarea td { margin:0;padding:0; } + +.htmlarea .toolbarRow { + width:1px; +} + +.htmlarea .toolbar { + cursor: default; + background: ButtonFace; + padding: 3px; + border: 1px solid; + border-color: ButtonHighlight ButtonShadow ButtonShadow ButtonHighlight; +} +.htmlarea .toolbar table { margin: 0; font-family: Tahoma, Verdana,sans-serif; font-size: 11px; } +.htmlarea .toolbar img { border: none; vertical-align: top; } +.htmlarea .toolbar .label { padding: 0 3px; } + +.htmlarea .toolbar .button { + background: ButtonFace; + color: ButtonText; + border: 1px solid ButtonFace; + padding: 1px; + margin: 0; + width: 18px; + height: 18px; +} +.htmlarea .toolbar a.button:hover { + border: 1px solid; + border-color: ButtonHighlight ButtonShadow ButtonShadow ButtonHighlight; +} +.htmlarea .toolbar a.buttonDisabled:hover { + border-color: ButtonFace; +} +.htmlarea .toolbar .buttonActive, +.htmlarea .toolbar .buttonPressed +{ + padding: 2px 0 0 2px; + border: 1px solid; + border-color: ButtonShadow ButtonHighlight ButtonHighlight ButtonShadow; +} +.htmlarea .toolbar .buttonPressed { + background: ButtonHighlight; +} +.htmlarea .toolbar .indicator { + padding: 0 3px; + overflow: hidden; + width: 20px; + text-align: center; + cursor: default; + border: 1px solid ButtonShadow; +} + +.htmlarea .toolbar .buttonDisabled img { + filter: gray() alpha(opacity = 25); + -moz-opacity: 0.25; + opacity: 0.25; +} + +.htmlarea .toolbar .separator { + /*position: relative;*/ + margin:0 3px; + border-left: 1px solid ButtonShadow; + border-right: 1px solid ButtonHighlight; + width: 0; + height: 18px; + padding: 0; +} + +.htmlarea .toolbar .space { width: 5px; } + +.htmlarea .toolbar select, .htmlarea .toolbar option { font: 11px Tahoma,Verdana,sans-serif;} + +.htmlarea .toolbar select, +.htmlarea .toolbar select:hover, +.htmlarea .toolbar select:active { + position:relative; + top:-2px; + margin-bottom:-2px; + color: ButtonText; +} + +.htmlarea iframe.xinha_iframe, .htmlarea textarea.xinha_textarea +{ + border: none; /*1px solid;*/ +} + +.htmlarea .statusBar { + border: 1px solid; + border-color: ButtonShadow ButtonHighlight ButtonHighlight ButtonShadow; + padding: 2px 4px; + background-color: ButtonFace; + color: ButtonText; + font: 11px Tahoma,Verdana,sans-serif; + height:16px; + overflow: hidden; +} + +.htmlarea .statusBar .statusBarTree a { + padding: 2px 5px; + color: #00f; +} + +.htmlarea .statusBar .statusBarTree a:visited { color: #00f; } +.htmlarea .statusBar .statusBarTree a:hover { + background-color: Highlight; + color: HighlightText; + padding: 1px 4px; + border: 1px solid HighlightText; +} + +.statusBarWidgetContainer { + background-color: ButtonFace; +} + +/* popup dialogs */ + +.dialog { + color: ButtonText; + background: ButtonFace; + border: 1px outset; + border-color: WindowFrame; +} +div.dialog { + padding-bottom:10px; + border-radius: 8px 8px 0 0; + -moz-border-radius: 8px 8px 0 0; + -webkit-border-top-left-radius: 8px; + -webkit-border-top-right-radius: 8px; + + box-shadow: 9px 9px 10px #444; + -moz-box-shadow: 9px 9px 10px #444; + -webkit-box-shadow: 9px 9px 10px #444; +} +div.dialog.modeless { + box-shadow: 4px 4px 5px #888; + -moz-box-shadow: 4px 4px 5px #888; + -webkit-box-shadow: 4px 4px 5px #888; +} +div.dialog.chrome { + -webkit-box-shadow: none !IMPORTANT; +} +.panels div.dialog.panel { + border-radius:0; + -moz-border-radius: 0; + -webkit-border-radius:0; + + box-shadow: none; + -moz-box-shadow: none; + -webkit-box-shadow: none; +} +.xinha_dialog_background { + filter: alpha(opacity=0); + -moz-opacity: 0; + opacity: 0; + border:none; +} +.xinha_dialog_background_modal_greyout { + background-color:#666; + filter: alpha(opacity=70) !IMPORTANT; + -moz-opacity: 0.7; + opacity: 0.7; +} +.xinha_dialog_background_modal { + filter: alpha(opacity=0) !IMPORTANT; + -moz-opacity: 0; + opacity: 0; + border:none; +} +body.xinha_dialog_background_modal_greyout { + filter: alpha(opacity=100) !IMPORTANT; +} +body.xinha_dialog_background_modal { + filter: alpha(opacity=0); +} +.dialog .content { padding: 2px; } + +.dialog, .dialog button, .dialog input, .dialog select, .dialog textarea, .dialog table { + font: 11px Tahoma,Verdana,sans-serif; +} + +.dialog table { border-collapse: collapse; } + +.dialog .title, .dialog h1 +{ + background: ActiveCaption; + color: CaptionText; + border-bottom: 1px solid #000; + padding: 1px 0 2px 5px; + font-size: 12px; + font-weight: bold; + cursor: default; + letter-spacing: 0.01em; +} +.dialog h1 { + padding-left:22px; + margin:0; + border-radius: 8px 8px 0 0; + -moz-border-radius: 8px 8px 0 0; + -webkit-border-top-left-radius: 8px; + -webkit-border-top-right-radius: 8px; +} +.panels .dialog.panel h1 { + -moz-border-radius: 0; + -webkit-border-radius:0; +} + +.dialog .title .button { + float: right; + border: 1px solid #66a; + padding: 0 1px 0 2px; + margin-right: 1px; + color: #fff; + text-align: center; +} + +.dialog .title .button-hilite { border-color: #88f; background: #44c; } + +.dialog button { + width: 5.5em; + padding: 0; +} +.dialog .closeButton { + padding: 0; + cursor: default; + border: 1px solid; + border-color: ButtonHighlight ButtonShadow ButtonShadow ButtonHighlight; + height : 11px; + width : 11px; + vertical-align : top; + position : absolute; + top : 3px; + right : 2px; + background-color: ButtonFace; + color: ButtonText; + font-size: 13px; + font-family: Tahoma,Verdana,sans-serif; + text-align:center; + letter-spacing:0; + overflow:hidden; +} +.dialog .buttonColor { + width :1em; + padding: 1px; + cursor: default; + border: 1px solid; + border-color: ButtonHighlight ButtonShadow ButtonShadow ButtonHighlight; +} + +.dialog .buttonColor .chooser, .dialog .buttonColor .nocolor { + height: 0.6em; + border: 1px solid; + padding: 0 1em; + border-color: ButtonShadow ButtonHighlight ButtonHighlight ButtonShadow; +} + +.dialog .buttonClick { + border-color: ButtonShadow ButtonHighlight ButtonHighlight ButtonShadow; +} +.dialog .buttonColor-hilite { + border-color: ButtonShadow ButtonHighlight ButtonHighlight ButtonShadow; +} + +.dialog .buttonColor .nocolor { padding: 0; } +.dialog .buttonColor .nocolor-hilite { background-color: #fff; color: #f00; } + +.dialog .label { text-align: right; width: 6em; } +.dialog .value input { width: 100%; } + +.dialog legend { font-weight: bold; } +.dialog fieldset table { margin: 2px 0; } + +.dialog .buttons { + + padding: 1em; + text-align: center; +} +.dialog .resizeHandle { + -moz-appearance : resizer; + width: 12px; + height: 12px; + border-bottom: 2px solid #000; + border-right: 2px solid #000; + cursor : se-resize; +} +.popupwin { + padding: 0; + margin: 0; +} + +.popupwin .title { + background: #fff; + color: #000; + font-weight: bold; + font-size: 120%; + padding: 3px 10px; + margin-bottom: 10px; + border-bottom: 1px solid black; + letter-spacing: 2px; +} + +form { margin: 0; border: none; } + + +/** Panels **/ +.htmlarea .panels_top +{ + border-bottom : 1px solid; + border-color: ButtonShadow; +} + +.htmlarea .panels_right +{ + border-left : 1px solid; + border-color: ButtonShadow; +} + +.htmlarea .panels_left +{ + border-right : 1px solid; + border-color: ButtonShadow; +} + +.htmlarea .panels_bottom +{ + border-top : 1px solid; + border-color: ButtonShadow; +} + +.htmlarea .panel h1 { + clear:left; + font-size:0.9em; +} + +.htmlarea .panel { + overflow:hidden; + background-color:white; + padding-bottom:0 !IMPORTANT; + border: none !IMPORTANT; +} +.htmlarea .panels_left .panel { border-right:none; border-left:none; } +.htmlarea .panels_left h1 { border-right:none; } +.htmlarea .panels_right .panel { border-right:none; border-left:none; } +.htmlarea .panels_left h1 { border-left:none; } +.htmlarea { border: 1px solid black; } + +.loading +{ + font-family:sans-serif; + position:absolute; + z-index:998; + text-align:center; + width:212px; + padding: 55px 0 5px 0; + border:2px solid #ccc; + /* border-color: ButtonHighlight ButtonShadow ButtonShadow ButtonHighlight;*/ + background: url(images/xinha_logo.gif) no-repeat #fff center 5px; +} +.loading_main +{ + font-size:11px; + color:#000; + +} +.loading_sub +{ + font-size:9px; + color:#666; + text-align:center; +} +/* Classes for filemanager styles in a dialog. */ +.dialog a img +{ +border: 0 none transparent; +} + +.dialog fieldset.collapsed { +border: 0 none transparent; +} + +.dialog fieldset.collapsed form { +display: none; +} + +.hidden +{ +display: none; +} + +.placesmanager +{ + width: 95%; + overflow: auto; +} + +.filemanager +{ + width: 95%; + height: 200px; + overflow: auto; + background-color: #fff; +} +.filemanager div.file +{ + min-width: 80px; + height: 100px; + position: relative; + float: left; + border: 1px outset #666; + margin: 4px; +} +.placesmanager div.file +{ + min-width: 60px; + height: 70px; + position: relative; + float: left; + border: 1px outset #666; + margin: 4px; +} +.filemanager div.file:hover, +.placesmanager div.file:hover +{ + border: 1px solid #333; + background: #fffff3; +} + +.filemanager div.selected, +.filemanager div.selected:hover, +.placesmanager div.selected, +.placesmanager div.selected:hover +{ + background: #ffffda; + border: 1px solid #000; +} +.filemanager .filename { + margin: 0.5em; + color: #222; +} +.filemanager div.selected .filename { + color: #000; +} +.filemanager img.thumb +{ + width: 50px; + height: 50px; + position: absolute; + top: 50%; + left: 50%; + margin: -25px 0 0 -25px; + border: 1px solid black; +} +.filemanager img.icon +{ + width: 32px; + height: 32px; + position: absolute; + top: 50%; + left: 50%; + margin: -16px 0 0 -16px; +} +.filemanager img.action +{ + width: 15px; + height: 15px; + position: absolute; +} +.filemanager img.delete +{ + bottom: 3px; + left: 20px; +} +.filemanager img.copy +{ + bottom: 3px; + left: 3px; +} Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/XinhaCore.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/XinhaCore.js,v diff -u -r1.7 -r1.8 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/XinhaCore.js 27 Mar 2009 08:20:42 -0000 1.7 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/XinhaCore.js 23 May 2010 11:58:33 -0000 1.8 @@ -1,18 +1,8308 @@ -/* 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 */ -/* The URL of the most recent uncompressed version of this file is http://svn.xinha.org/trunk/XinhaCore.js */ - /*-------------------------------------------------------------------------- + + /*--------------------------------------:noTabs=true:tabSize=2:indentSize=2:-- -- Xinha (is not htmlArea) - http://xinha.org -- -- Use of Xinha is granted by the terms of the htmlArea License (based on -- BSD license) please read license.txt in this package for details. -- - -- Copyright (c) 2005-2009 Xinha Developer Team and contributors + -- Copyright (c) 2005-2008 Xinha Developer Team and contributors -- -- Xinha was originally based on work by Mihai Bazon which is: -- Copyright (c) 2003-2004 dynarch.com. -- Copyright (c) 2002-2003 interactivetools.com, inc. -- This copyright notice MUST stay intact for use. - -------------------------------------------------------------------------*/ + -- + -- Developers - Coding Style: + -- Before you are going to work on Xinha code, please see http://trac.xinha.org/wiki/Documentation/StyleGuide + -- + -- $HeadURL: http://svn.xinha.org/trunk/XinhaCore.js $ + -- $LastChangedDate: 2010-05-12 09:40:06 +1200 (Wed, 12 May 2010) $ + -- $LastChangedRevision: 1263 $ + -- $LastChangedBy: gogo $ + --------------------------------------------------------------------------*/ +/*jslint regexp: false, rhino: false, browser: true, bitwise: false, forin: true, adsafe: false, evil: true, nomen: false, +glovar: false, debug: false, eqeqeq: false, passfail: false, sidebar: false, laxbreak: false, on: false, cap: true, +white: false, widget: false, undef: true, plusplus: false*/ +/*global Dialog , _editor_css , _editor_icons, _editor_lang , _editor_skin , _editor_url, dumpValues, ActiveXObject, HTMLArea, _editor_lcbackend*/ -Xinha.version={Release:"0.96beta2",Head:"$HeadURL: http://svn.xinha.org/trunk/XinhaCore.js $".replace(/^[^:]*:\s*(.*)\s*\$$/,"$1"),Date:"$LastChangedDate: 2009-03-19 22:40:09 +0100 (Do, 19. Mär 2009) $".replace(/^[^:]*:\s*([0-9\-]*) ([0-9:]*) ([+0-9]*) \((.*)\)\s*\$/,"$4 $2 $3"),Revision:"$LastChangedRevision: 1170 $".replace(/^[^:]*:\s*(.*)\s*\$$/,"$1"),RevisionBy:"$LastChangedBy: ray $".replace(/^[^:]*:\s*(.*)\s*\$$/,"$1")};Xinha._resolveRelativeUrl=function(d,c){if(c.match(/^([^:]+\:)?\/\//)){return c}else{var a=d.split("/");if(a[a.length-1]===""){a.pop()}var e=c.split("/");if(e[0]=="."){e.shift()}while(e[0]==".."){a.pop();e.shift()}return a.join("/")+"/"+e.join("/")}};if(typeof _editor_url=="string"){_editor_url=_editor_url.replace(/\x2f*$/,"/");if(!_editor_url.match(/^([^:]+\:)?\//)){(function(){var a=window.location.toString().replace(/\?.*$/,"").split("/");a.pop();_editor_url=Xinha._resolveRelativeUrl(a.join("/"),_editor_url)})()}}else{alert("WARNING: _editor_url is not set! You should set this variable to the editor files path; it should preferably be an absolute path, like in '/xinha/', but it can be relative if you prefer. Further we will try to load the editor files correctly but we'll probably fail.");_editor_url=""}if(typeof _editor_lang=="string"){_editor_lang=_editor_lang.toLowerCase()}else{_editor_lang="en"}if(typeof _editor_skin!=="string"){_editor_skin=""}if(typeof _editor_icons!=="string"){_editor_icons=""}var __xinhas=[];Xinha.agt=navigator.userAgent.toLowerCase();Xinha.is_ie=((Xinha.agt.indexOf("msie")!=-1)&&(Xinha.agt.indexOf("opera")==-1));Xinha.ie_version=parseFloat(Xinha.agt.substring(Xinha.agt.indexOf("msie")+5));Xinha.is_opera=(Xinha.agt.indexOf("opera")!=-1);if(Xinha.is_opera&&Xinha.agt.match(/opera[\/ ]([0-9.]+)/)){Xinha.opera_version=parseFloat(RegExp.$1)}else{Xinha.opera_version=0}Xinha.is_khtml=(Xinha.agt.indexOf("khtml")!=-1);Xinha.is_webkit=(Xinha.agt.indexOf("applewebkit")!=-1);Xinha.webkit_version=parseInt(navigator.appVersion.replace(/.*?AppleWebKit\/([\d]).*?/,"$1"),10);Xinha.is_safari=(Xinha.agt.indexOf("safari")!=-1);Xinha.is_chrome=(Xinha.agt.indexOf("chrome")!=-1);Xinha.is_mac=(Xinha.agt.indexOf("mac")!=-1);Xinha.is_mac_ie=(Xinha.is_ie&&Xinha.is_mac);Xinha.is_win_ie=(Xinha.is_ie&&!Xinha.is_mac);Xinha.is_gecko=(navigator.product=="Gecko")||Xinha.is_opera;Xinha.is_real_gecko=(navigator.product=="Gecko"&&!Xinha.is_webkit);Xinha.is_ff3=Xinha.is_real_gecko&&parseInt(navigator.productSub,10)>=2007121016;Xinha.is_ff2=Xinha.is_real_gecko&&parseInt(navigator.productSub,10)<2007121016;Xinha.isRunLocally=document.URL.toLowerCase().search(/^file:/)!=-1;Xinha.is_designMode=(typeof document.designMode!="undefined"&&!Xinha.is_ie);Xinha.checkSupportedBrowser=function(){return Xinha.is_real_gecko||(Xinha.is_opera&&Xinha.opera_version>=9.2)||Xinha.ie_version>=5.5||Xinha.webkit_version>=522};Xinha.isSupportedBrowser=Xinha.checkSupportedBrowser();if(Xinha.isRunLocally&&Xinha.isSupportedBrowser){alert('Xinha *must* be installed on a web server. Locally opened files (those that use the "file://" protocol) cannot properly function. Xinha will try to initialize but may not be correctly loaded.')}function Xinha(a,c){if(!Xinha.isSupportedBrowser){return}if(!a){throw new Error("Tried to create Xinha without textarea specified.")}if(typeof c=="undefined"){this.config=new Xinha.Config()}else{this.config=c}if(typeof a!="object"){a=Xinha.getElementById("textarea",a)}this._textArea=a;this._textArea.spellcheck=false;Xinha.freeLater(this,"_textArea");this._initial_ta_size={w:a.style.width?a.style.width:(a.offsetWidth?(a.offsetWidth+"px"):(a.cols+"em")),h:a.style.height?a.style.height:(a.offsetHeight?(a.offsetHeight+"px"):(a.rows+"em"))};if(document.getElementById("loading_"+a.id)||this.config.showLoading){if(!document.getElementById("loading_"+a.id)){Xinha.createLoadingMessage(a)}this.setLoadingMessage(Xinha._lc("Constructing object"))}this._editMode="wysiwyg";this.plugins={};this._timerToolbar=null;this._timerUndo=null;this._undoQueue=[this.config.undoSteps];this._undoPos=-1;this._customUndo=true;this._mdoc=document;this.doctype="";this.__htmlarea_id_num=__xinhas.length;__xinhas[this.__htmlarea_id_num]=this;this._notifyListeners={};var b={right:{on:true,container:document.createElement("td"),panels:[]},left:{on:true,container:document.createElement("td"),panels:[]},top:{on:true,container:document.createElement("td"),panels:[]},bottom:{on:true,container:document.createElement("td"),panels:[]}};for(var d in b){if(!b[d].container){continue}b[d].div=b[d].container;b[d].container.className="panels panels_"+d;Xinha.freeLater(b[d],"container");Xinha.freeLater(b[d],"div")}this._panels=b;this._statusBar=null;this._statusBarTree=null;this._statusBarTextMode=null;this._statusBarItems=[];this._framework={};this._htmlArea=null;this._iframe=null;this._doc=null;this._toolBar=this._toolbar=null;this._toolbarObjects={};this.plugins.Events={name:"Events",developer:"The Xinha Core Developer Team",instance:c.Events}}Xinha.onload=function(){};Xinha.init=function(){Xinha.onload()};Xinha.RE_tagName=/(<\/|<)\s*([^ \t\n>]+)/ig;Xinha.RE_doctype=/()\n?/i;Xinha.RE_head=/((.|\n)*?)<\/head>/i;Xinha.RE_body=/]*>((.|\n|\r|\t)*?)<\/body>/i;Xinha.RE_Specials=/([\/\^$*+?.()|{}\[\]])/g;Xinha.escapeStringForRegExp=function(a){return a.replace(Xinha.RE_Specials,"\\$1")};Xinha.RE_email=/^[_a-z\d\-\.]{3,}@[_a-z\d\-]{2,}(\.[_a-z\d\-]{2,})+$/i;Xinha.RE_url=/(https?:\/\/)?(([a-z0-9_]+:[a-z0-9_]+@)?[a-z0-9_\-]{2,}(\.[a-z0-9_\-]{2,}){2,}(:[0-9]+)?(\/\S+)*)/i;Xinha.Config=function(){this.version=Xinha.version.Revision;this.width="auto";this.height="auto";this.sizeIncludesBars=true;this.sizeIncludesPanels=true;this.panel_dimensions={left:"200px",right:"200px",top:"100px",bottom:"100px"};this.iframeWidth=null;this.statusBar=true;this.htmlareaPaste=false;this.mozParaHandler="best";this.getHtmlMethod="DOMwalk";this.undoSteps=20;this.undoTimeout=500;this.changeJustifyWithDirection=false;this.fullPage=false;this.pageStyle="";this.pageStyleSheets=[];this.baseHref=null;this.expandRelativeUrl=true;this.stripBaseHref=true;this.stripSelfNamedAnchors=true;this.only7BitPrintablesInURLs=true;this.sevenBitClean=false;this.specialReplacements={};this.inwardHtml=function(c){return c};this.outwardHtml=function(c){return c};this.autofocus=false;this.killWordOnPaste=true;this.makeLinkShowsTarget=true;this.charSet=(typeof document.characterSet!="undefined")?document.characterSet:document.charset;this.browserQuirksMode=null;this.imgURL="images/";this.popupURL="popups/";this.htmlRemoveTags=null;this.flowToolbars=true;this.toolbarAlign="left";this.showLoading=false;this.stripScripts=true;this.convertUrlsToLinks=true;this.colorPickerCellSize="6px";this.colorPickerGranularity=18;this.colorPickerPosition="bottom,right";this.colorPickerWebSafe=false;this.colorPickerSaveColors=20;this.fullScreen=false;this.fullScreenMargins=[0,0,0,0];this.toolbar=[["popupeditor"],["separator","formatblock","fontname","fontsize","bold","italic","underline","strikethrough"],["separator","forecolor","hilitecolor","textindicator"],["separator","subscript","superscript"],["linebreak","separator","justifyleft","justifycenter","justifyright","justifyfull"],["separator","insertorderedlist","insertunorderedlist","outdent","indent"],["separator","inserthorizontalrule","createlink","insertimage","inserttable"],["linebreak","separator","undo","redo","selectall","print"],(Xinha.is_gecko?[]:["cut","copy","paste","overwrite","saveas"]),["separator","killword","clearfonts","removeformat","toggleborders","splitblock","lefttoright","righttoleft"],["separator","htmlmode","showhelp","about"]];this.fontname={"— font —":"",Arial:"arial,helvetica,sans-serif","Courier New":"courier new,courier,monospace",Georgia:"georgia,times new roman,times,serif",Tahoma:"tahoma,arial,helvetica,sans-serif","Times New Roman":"times new roman,times,serif",Verdana:"verdana,arial,helvetica,sans-serif",impact:"impact",WingDings:"wingdings"};this.fontsize={"— size —":"","1 (8 pt)":"1","2 (10 pt)":"2","3 (12 pt)":"3","4 (14 pt)":"4","5 (18 pt)":"5","6 (24 pt)":"6","7 (36 pt)":"7"};this.formatblock={"— format —":"","Heading 1":"h1","Heading 2":"h2","Heading 3":"h3","Heading 4":"h4","Heading 5":"h5","Heading 6":"h6",Normal:"p",Address:"address",Formatted:"pre"};this.dialogOptions={centered:true,greyout:true,closeOnEscape:true};this.Events={};this.customSelects={};this.debug=false;this.URIs={blank:_editor_url+"popups/blank.html",link:_editor_url+"modules/CreateLink/link.html",insert_image:_editor_url+"modules/InsertImage/insert_image.html",insert_table:_editor_url+"modules/InsertTable/insert_table.html",select_color:_editor_url+"popups/select_color.html",help:_editor_url+"popups/editor_help.html"};this.btnList={bold:["Bold",Xinha._lc({key:"button_bold",string:["ed_buttons_main.png",3,2]},"Xinha"),false,function(c){c.execCommand("bold")}],italic:["Italic",Xinha._lc({key:"button_italic",string:["ed_buttons_main.png",2,2]},"Xinha"),false,function(c){c.execCommand("italic")}],underline:["Underline",Xinha._lc({key:"button_underline",string:["ed_buttons_main.png",2,0]},"Xinha"),false,function(c){c.execCommand("underline")}],strikethrough:["Strikethrough",Xinha._lc({key:"button_strikethrough",string:["ed_buttons_main.png",3,0]},"Xinha"),false,function(c){c.execCommand("strikethrough")}],subscript:["Subscript",Xinha._lc({key:"button_subscript",string:["ed_buttons_main.png",3,1]},"Xinha"),false,function(c){c.execCommand("subscript")}],superscript:["Superscript",Xinha._lc({key:"button_superscript",string:["ed_buttons_main.png",2,1]},"Xinha"),false,function(c){c.execCommand("superscript")}],justifyleft:["Justify Left",["ed_buttons_main.png",0,0],false,function(c){c.execCommand("justifyleft")}],justifycenter:["Justify Center",["ed_buttons_main.png",1,1],false,function(c){c.execCommand("justifycenter")}],justifyright:["Justify Right",["ed_buttons_main.png",1,0],false,function(c){c.execCommand("justifyright")}],justifyfull:["Justify Full",["ed_buttons_main.png",0,1],false,function(c){c.execCommand("justifyfull")}],orderedlist:["Ordered List",["ed_buttons_main.png",0,3],false,function(c){c.execCommand("insertorderedlist")}],unorderedlist:["Bulleted List",["ed_buttons_main.png",1,3],false,function(c){c.execCommand("insertunorderedlist")}],insertorderedlist:["Ordered List",["ed_buttons_main.png",0,3],false,function(c){c.execCommand("insertorderedlist")}],insertunorderedlist:["Bulleted List",["ed_buttons_main.png",1,3],false,function(c){c.execCommand("insertunorderedlist")}],outdent:["Decrease Indent",["ed_buttons_main.png",1,2],false,function(c){c.execCommand("outdent")}],indent:["Increase Indent",["ed_buttons_main.png",0,2],false,function(c){c.execCommand("indent")}],forecolor:["Font Color",["ed_buttons_main.png",3,3],false,function(c){c.execCommand("forecolor")}],hilitecolor:["Background Color",["ed_buttons_main.png",2,3],false,function(c){c.execCommand("hilitecolor")}],undo:["Undoes your last action",["ed_buttons_main.png",4,2],false,function(c){c.execCommand("undo")}],redo:["Redoes your last action",["ed_buttons_main.png",5,2],false,function(c){c.execCommand("redo")}],cut:["Cut selection",["ed_buttons_main.png",5,0],false,function(d,c){d.execCommand(c)}],copy:["Copy selection",["ed_buttons_main.png",4,0],false,function(d,c){d.execCommand(c)}],paste:["Paste from clipboard",["ed_buttons_main.png",4,1],false,function(d,c){d.execCommand(c)}],selectall:["Select all",["ed_buttons_main.png",3,5],false,function(c){c.execCommand("selectall")}],inserthorizontalrule:["Horizontal Rule",["ed_buttons_main.png",6,0],false,function(c){c.execCommand("inserthorizontalrule")}],createlink:["Insert Web Link",["ed_buttons_main.png",6,1],false,function(c){c._createLink()}],insertimage:["Insert/Modify Image",["ed_buttons_main.png",6,3],false,function(c){c.execCommand("insertimage")}],inserttable:["Insert Table",["ed_buttons_main.png",6,2],false,function(c){c.execCommand("inserttable")}],htmlmode:["Toggle HTML Source",["ed_buttons_main.png",7,0],true,function(c){c.execCommand("htmlmode")}],toggleborders:["Toggle Borders",["ed_buttons_main.png",7,2],false,function(c){c._toggleBorders()}],print:["Print document",["ed_buttons_main.png",8,1],false,function(c){if(Xinha.is_gecko){c._iframe.contentWindow.print()}else{c.focusEditor();print()}}],saveas:["Save as","ed_saveas.png",false,function(c){c.execCommand("saveas",false,"noname.htm")}],about:["About this editor",["ed_buttons_main.png",8,2],true,function(c){c.getPluginInstance("AboutBox").show()}],showhelp:["Help using editor",["ed_buttons_main.png",9,2],true,function(c){c.execCommand("showhelp")}],splitblock:["Split Block","ed_splitblock.gif",false,function(c){c._splitBlock()}],lefttoright:["Direction left to right",["ed_buttons_main.png",0,2],false,function(c){c.execCommand("lefttoright")}],righttoleft:["Direction right to left",["ed_buttons_main.png",1,2],false,function(c){c.execCommand("righttoleft")}],overwrite:["Insert/Overwrite","ed_overwrite.gif",false,function(c){c.execCommand("overwrite")}],wordclean:["MS Word Cleaner",["ed_buttons_main.png",5,3],false,function(c){c._wordClean()}],clearfonts:["Clear Inline Font Specifications",["ed_buttons_main.png",5,4],true,function(c){c._clearFonts()}],removeformat:["Remove formatting",["ed_buttons_main.png",4,4],false,function(c){c.execCommand("removeformat")}],killword:["Clear MSOffice tags",["ed_buttons_main.png",4,3],false,function(c){c.execCommand("killword")}]};this.iconList={dialogCaption:_editor_url+"images/xinha-small-icon.gif",wysiwygmode:[_editor_url+"images/ed_buttons_main.png",7,1]};for(var b in this.btnList){var a=this.btnList[b];if(typeof a!="object"){continue}if(typeof a[1]!="string"){a[1][0]=_editor_url+this.imgURL+a[1][0]}else{a[1]=_editor_url+this.imgURL+a[1]}a[0]=Xinha._lc(a[0])}};Xinha.Config.prototype.registerIcon=function(b,a){this.iconList[b]=a};Xinha.Config.prototype.registerButton=function(f,d,e,a,c,b){if(typeof f=="string"){this.btnList[f]=[d,e,a,c,b]}else{if(typeof f=="object"){this.btnList[f.id]=[f.tooltip,f.image,f.textMode,f.action,f.context]}else{alert("ERROR [Xinha.Config::registerButton]:\ninvalid arguments");return false}}};Xinha.prototype.registerPanel=function(c,b){if(!c){c="right"}this.setLoadingMessage("Register "+c+" panel ");var a=this.addPanel(c);if(b){b.drawPanelIn(a)}};Xinha.Config.prototype.registerDropdown=function(a){this.customSelects[a.id]=a};Xinha.Config.prototype.hideSomeButtons=function(b){var f=this.toolbar;for(var e=f.length;--e>=0;){var c=f[e];for(var d=c.length;--d>=0;){if(b.indexOf(" "+c[d]+" ")>=0){var a=1;if(/separator|space/.test(c[d+1])){a=2}c.splice(d,a)}}}};Xinha.Config.prototype.addToolbarElement=function(d,h,k){var p=this.toolbar;var q,g,f,e,c;var n=false;var b=false;var t=0;var l=0;var m=0;var r=false;var s=false;if((d&&typeof d=="object")&&(d.constructor==Array)){n=true}if((h&&typeof h=="object")&&(h.constructor==Array)){b=true;t=h.length}if(n){for(g=0;g=0;){q.splice(f,0,d[g])}}else{q[f]=d}}else{if(k<0){f=f+k+1}else{if(k>0){f=f+k}}if(n){for(g=d.length;--g>=0;){q.splice(f,0,d[g])}}else{q.splice(f,0,d)}}}else{p[0].splice(0,0,"separator");if(n){for(g=d.length;--g>=0;){p[0].splice(0,0,d[g])}}else{p[0].splice(0,0,d)}}};Xinha.Config.prototype.removeToolbarElement=Xinha.Config.prototype.hideSomeButtons;Xinha.replaceAll=function(a){var c=document.getElementsByTagName("textarea");for(var b=c.length;b>0;new Xinha(c[--b],a).generate()){}};Xinha.replace=function(c,b){var a=Xinha.getElementById("textarea",c);return a?new Xinha(a,b).generate():null};Xinha.prototype._createToolbar=function(){this.setLoadingMessage(Xinha._lc("Create Toolbar"));var b=this;var c=document.createElement("div");this._toolBar=this._toolbar=c;c.className="toolbar";c.unselectable="1";c.align=this.config.toolbarAlign;Xinha.freeLater(this,"_toolBar");Xinha.freeLater(this,"_toolbar");var a=null;var d={};this._toolbarObjects=d;this._createToolbar1(b,c,d);this._htmlArea.appendChild(c);return c};Xinha.prototype._setConfig=function(a){this.config=a};Xinha.prototype._rebuildToolbar=function(){this._createToolbar1(this,this._toolbar,this._toolbarObjects);if(Xinha._currentlyActiveEditor){if(Xinha._currentlyActiveEditor==this){this.activateEditor()}}else{this.disableToolbar()}};Xinha._createToolbarBreakingElement=function(){var a=document.createElement("div");a.style.height="1px";a.style.width="1px";a.style.lineHeight="1px";a.style.fontSize="1px";a.style.clear="both";return a};Xinha.prototype._createToolbar1=function(m,p,s){while(p.lastChild){p.removeChild(p.lastChild)}var d;if(m.config.flowToolbars){p.appendChild(Xinha._createToolbarBreakingElement())}function l(){if(typeof d!="undefined"&&d.childNodes.length===0){return}var i=document.createElement("table");i.border="0px";i.cellSpacing="0px";i.cellPadding="0px";if(m.config.flowToolbars){if(Xinha.is_ie){i.style.styleFloat="left"}else{i.style.cssFloat="left"}}p.appendChild(i);var j=document.createElement("tbody");i.appendChild(j);d=document.createElement("tr");j.appendChild(d);i.className="toolbarRow"}l();function c(u,t){var j=this[u];var i=this.element;if(j!=t){switch(u){case"enabled":if(t){Xinha._removeClass(i,"buttonDisabled");i.disabled=false}else{Xinha._addClass(i,"buttonDisabled");i.disabled=true}break;case"active":if(t){Xinha._addClass(i,"buttonPressed")}else{Xinha._removeClass(i,"buttonPressed")}break}this[u]=t}}function b(x){var C=null;var t=null;var u=null;var z=m.config.customSelects;var j=null;var B="";switch(x){case"fontsize":case"fontname":case"formatblock":C=m.config[x];u=x;break;default:u=x;var A=z[u];if(typeof A!="undefined"){C=A.options;j=A.context;if(typeof A.tooltip!="undefined"){B=A.tooltip}}else{alert("ERROR [createSelect]:\nCan't find the requested dropdown definition")}break}if(C){t=document.createElement("select");t.title=B;t.style.width="auto";t.name=x;var w={name:x,element:t,enabled:true,text:false,cmd:u,state:c,context:j};Xinha.freeLater(w);s[x]=w;for(var v in C){if(typeof C[v]!="string"){continue}var y=document.createElement("option");y.innerHTML=Xinha._lc(v);y.value=C[v];t.appendChild(y)}Xinha._addEvent(t,"change",function(){m._comboSelected(t,x)})}return t}function q(i){var u,t,v=null;switch(i){case"separator":if(m.config.flowToolbars){l()}u=document.createElement("div");u.className="separator";break;case"space":u=document.createElement("div");u.className="space";break;case"linebreak":l();return false;case"textindicator":u=document.createElement("div");u.appendChild(document.createTextNode("A"));u.className="indicator";u.title=Xinha._lc("Current style");v={name:i,element:u,enabled:true,active:false,text:false,cmd:"textindicator",state:c};Xinha.freeLater(v);s[i]=v;break;default:t=m.config.btnList[i]}if(!u&&t){u=document.createElement("a");u.style.display="block";u.href="javascript:void(0)";u.style.textDecoration="none";u.title=t[0];u.className="button";u.style.direction="ltr";v={name:i,element:u,enabled:true,active:false,text:t[2],cmd:t[3],state:c,context:t[4]||null};Xinha.freeLater(u);Xinha.freeLater(v);s[i]=v;u.ondrag=function(){return false};Xinha._addEvent(u,"mouseout",function(x){if(v.enabled){Xinha._removeClass(u,"buttonActive");if(v.active){Xinha._addClass(u,"buttonPressed")}}});Xinha._addEvent(u,"mousedown",function(x){if(v.enabled){Xinha._addClass(u,"buttonActive");Xinha._removeClass(u,"buttonPressed");Xinha._stopEvent(Xinha.is_ie?window.event:x)}});Xinha._addEvent(u,"click",function(x){x=x||window.event;m.btnClickEvent={clientX:x.clientX,clientY:x.clientY};if(v.enabled){Xinha._removeClass(u,"buttonActive");if(Xinha.is_gecko){m.activateEditor()}v.cmd(m,v.name,v,x);Xinha._stopEvent(x)}});var w=Xinha.makeBtnImg(t[1]);var j=w.firstChild;Xinha.freeLater(w);Xinha.freeLater(j);u.appendChild(w);v.imgel=j;v.swapImage=function(x){if(typeof x!="string"){j.src=x[0];j.style.position="relative";j.style.top=x[2]?("-"+(18*(x[2]+1))+"px"):"-18px";j.style.left=x[1]?("-"+(18*(x[1]+1))+"px"):"-18px"}else{v.imgel.src=x;j.style.top="0px";j.style.left="0px"}}}else{if(!u){u=b(i)}}return u}var k=true;for(var h=0;h] button to switch back to WYSIWYG.");c.style.display="none";this._statusBarTextMode=c;Xinha.freeLater(this,"_statusBarTextMode");b.appendChild(c);b.style.whiteSpace="nowrap";var a=this;this.notifyOn("before_resize",function(f,g){a._statusBar.style.width=null});this.notifyOn("resize",function(f,g){if(Xinha.is_ie&&Xinha.ie_version==6){a._statusBar.style.width="100%"}else{var h=g.width;a._statusBar.style.width=h+"px"}});this.notifyOn("modechange",function(f,l){for(var h in a._statusWidgets){var k=a._statusWidgets[h];for(var g=0;g=0;){for(var v=o[w].length;--v>=0;){switch(o[w][v]){case"popupeditor":if(!this.plugins.FullScreen){d.registerPlugin("FullScreen")}break;case"insertimage":e=_editor_url+"modules/InsertImage/insert_image.js";if(typeof Xinha.prototype._insertImage=="undefined"&&!Xinha.loadPlugins([{plugin:"InsertImage",url:e}],f)){return false}else{if(typeof Xinha.getPluginConstructor("InsertImage")!="undefined"&&!this.plugins.InsertImage){d.registerPlugin("InsertImage")}}break;case"createlink":e=_editor_url+"modules/CreateLink/link.js";if(typeof Xinha.getPluginConstructor("Linker")=="undefined"&&!Xinha.loadPlugins([{plugin:"CreateLink",url:e}],f)){return false}else{if(typeof Xinha.getPluginConstructor("CreateLink")!="undefined"&&!this.plugins.CreateLink){d.registerPlugin("CreateLink")}}break;case"inserttable":e=_editor_url+"modules/InsertTable/insert_table.js";if(!Xinha.loadPlugins([{plugin:"InsertTable",url:e}],f)){return false}else{if(typeof Xinha.getPluginConstructor("InsertTable")!="undefined"&&!this.plugins.InsertTable){d.registerPlugin("InsertTable")}}break;case"about":e=_editor_url+"modules/AboutBox/AboutBox.js";if(!Xinha.loadPlugins([{plugin:"AboutBox",url:e}],f)){return false}else{if(typeof Xinha.getPluginConstructor("AboutBox")!="undefined"&&!this.plugins.AboutBox){d.registerPlugin("AboutBox")}}break}}}if(Xinha.is_gecko&&d.config.mozParaHandler!="built-in"){if(!Xinha.loadPlugins([{plugin:"EnterParagraphs",url:_editor_url+"modules/Gecko/paraHandlerBest.js"}],f)){return false}if(!this.plugins.EnterParagraphs){d.registerPlugin("EnterParagraphs")}}var E=this.config.getHtmlMethod=="TransformInnerHTML"?_editor_url+"modules/GetHtml/TransformInnerHTML.js":_editor_url+"modules/GetHtml/DOMwalk.js";if(!Xinha.loadPlugins([{plugin:"GetHtmlImplementation",url:E}],f)){return false}else{if(!this.plugins.GetHtmlImplementation){d.registerPlugin("GetHtmlImplementation")}}function s(i){return i.textContent||i.text}if(_editor_skin){this.skinInfo={};var u=Xinha._geturlcontent(_editor_url+"skins/"+_editor_skin+"/skin.xml",true);if(u){var p=u.getElementsByTagName("meta");for(w=0;w'}else{if(f.config.browserQuirksMode===true){a=""}else{a=Xinha.getDoctype(document)}}if(!f.config.fullPage){d+=a+"\n";d+="\n";d+="\n";d+='\n';if(typeof f.config.baseHref!="undefined"&&f.config.baseHref!==null){d+='\n'}d+=Xinha.addCoreCSS();if(typeof f.config.pageStyleSheets!=="undefined"){for(var c=0;c0){d+=''}}}if(f.config.pageStyle){d+='"}d+="\n";d+="\n";d+=f.inwardHtml(f._textArea.value);d+="\n";d+=""}else{d=f.inwardHtml(f._textArea.value);if(d.match(Xinha.RE_doctype)){f.setDoctype(RegExp.$1)}var b=d.match(//gi);d=d.replace(/\s*/gi,"");if(b){d=d.replace(/<\/head>/i,b.join("\n")+"\n")}}g.write(d);g.close();if(this.config.fullScreen){this._fullScreen()}this.setEditorEvents();if((typeof f.config.autofocus!="undefined")&&((f.config.autofocus==f._textArea.id)||f.config.autofocus==true)){f.activateEditor();f.focusEditor()}};Xinha.prototype.whenDocReady=function(a){var b=this;if(this._doc&&this._doc.body){a()}else{setTimeout(function(){b.whenDocReady(a)},50)}};Xinha.prototype.setMode=function(b){var a;if(typeof b=="undefined"){b=this._editMode=="textmode"?"wysiwyg":"textmode"}switch(b){case"textmode":this.firePluginEvent("onBeforeMode","textmode");this._toolbarObjects.htmlmode.swapImage(this.config.iconList.wysiwygmode);this.setCC("iframe");a=this.outwardHtml(this.getHTML());this.setHTML(a);this.deactivateEditor();this._iframe.style.display="none";this._textArea.style.display="";if(this.config.statusBar){this._statusBarTree.style.display="none";this._statusBarTextMode.style.display=""}this.findCC("textarea");this.notifyOf("modechange",{mode:"text"});this.firePluginEvent("onMode","textmode");break;case"wysiwyg":this.firePluginEvent("onBeforeMode","wysiwyg");this._toolbarObjects.htmlmode.swapImage([this.imgURL("images/ed_buttons_main.png"),7,0]);this.setCC("textarea");a=this.inwardHtml(this.getHTML());this.deactivateEditor();this.setHTML(a);this._iframe.style.display="";this._textArea.style.display="none";this.activateEditor();if(this.config.statusBar){this._statusBarTree.style.display="";this._statusBarTextMode.style.display="none"}this.findCC("iframe");this.notifyOf("modechange",{mode:"wysiwyg"});this.firePluginEvent("onMode","wysiwyg");break;default:alert("Mode <"+b+"> not defined!");return false}this._editMode=b};Xinha.prototype.setFullHTML=function(c){var a=RegExp.multiline;RegExp.multiline=true;if(c.match(Xinha.RE_doctype)){this.setDoctype(RegExp.$1)}RegExp.multiline=a;if(0){if(c.match(Xinha.RE_head)){this._doc.getElementsByTagName("head")[0].innerHTML=RegExp.$1}if(c.match(Xinha.RE_body)){this._doc.getElementsByTagName("body")[0].innerHTML=RegExp.$1}}else{var d=this.editorIsActivated();if(d){this.deactivateEditor()}var b=/((.|\n)*?)<\/html>/i;c=c.replace(b,"$1");this._doc.open("text/html","replace");this._doc.write(c);this._doc.close();if(d){this.activateEditor()}this.setEditorEvents();return true}};Xinha.prototype.setEditorEvents=function(){var a=this;var b=this._doc;a.whenDocReady(function(){Xinha._addEvents(b,["mousedown"],function(){a.activateEditor();return true});if(Xinha.is_ie){Xinha._addEvent(a._doc.getElementsByTagName("html")[0],"click",function(){if(a._iframe.contentWindow.event.srcElement.tagName.toLowerCase()=="html"){var e=a._doc.body.createTextRange();e.collapse();e.select()}return true})}Xinha._addEvents(b,["keydown","keypress","mousedown","mouseup","drag"],function(e){return a._editorEvent(Xinha.is_ie?a._iframe.contentWindow.event:e)});for(var c in a.plugins){var d=a.plugins[c].instance;Xinha.refreshPlugin(d)}if(typeof a._onGenerate=="function"){a._onGenerate()}Xinha.addDom0Event(window,"resize",function(f){if(Xinha.ie_version>7&&!window.parent){if(a.execResize){a.sizeEditor();a.execResize=false}else{a.execResize=true}}else{a.sizeEditor()}});a.removeLoadingMessage()})};Xinha.getPluginConstructor=function(a){return Xinha.plugins[a]||window[a]};Xinha.prototype.registerPlugin=function(){if(!Xinha.isSupportedBrowser){return}var c=arguments[0];if(c===null||typeof c=="undefined"||(typeof c=="string"&&Xinha.getPluginConstructor(c)=="undefined")){return false}var a=[];for(var b=1;b=0;){c.value+=" "}c.value+=e+"\n"}function a(e,h){var d=e.tagName.toLowerCase(),f;var g=Xinha.is_ie?e.scopeName:e.prefix;b(h,"- "+d+" ["+g+"]");for(f=e.firstChild;f;f=f.nextSibling){if(f.nodeType==1){a(f,h+2)}}}a(this._doc.body,0);document.body.appendChild(c)};Xinha.getInnerText=function(c){var a="",b;for(b=c.firstChild;b;b=b.nextSibling){if(b.nodeType==3){a+=b.data}else{if(b.nodeType==1){a+=Xinha.getInnerText(b)}}}return a};Xinha.prototype._wordClean=function(){var e=this;var c={empty_tags:0,cond_comm:0,mso_elmts:0,mso_class:0,mso_style:0,mso_xmlel:0,orig_len:this._doc.body.innerHTML.length,T:new Date().getTime()};var h={empty_tags:"Empty tags removed: ",cond_comm:"Conditional comments removed",mso_elmts:"MSO invalid elements removed",mso_class:"MSO class names removed: ",mso_style:"MSO inline style removed: ",mso_xmlel:"MSO XML elements stripped: "};function a(){var j="Xinha word cleaner stats: \n\n";for(var k in c){if(h[k]){j+=h[k]+c[k]+"\n"}}j+="\nInitial document length: "+c.orig_len+"\n";j+="Final document length: "+e._doc.body.innerHTML.length+"\n";j+="Clean-up took "+((new Date().getTime()-c.T)/1000)+" seconds";alert(j)}function i(j){var k=j.className.replace(/(^|\s)mso.*?(\s|$)/ig," ");if(k!=j.className){j.className=k;if(!/\S/.test(j.className)){j.removeAttribute("className");++c.mso_class}}}function f(k){var l=k.style.cssText.split(/\s*;\s*/);for(var j=l.length;--j>=0;){if(/^mso|^tab-stops/i.test(l[j])||/^margin\s*:\s*0..\s+0..\s+0../i.test(l[j])){++c.mso_style;l.splice(j,1)}}k.style.cssText=l.join("; ")}function d(j){if(("link"==j.tagName.toLowerCase()&&(j.attributes&&/File-List|Edit-Time-Data|themeData|colorSchemeMapping/.test(j.attributes.rel.nodeValue)))||/^(style|meta)$/i.test(j.tagName)){Xinha.removeFromParent(j);++c.mso_elmts;return true}return false}function b(j){if(/^(a|span|b|strong|i|em|font|div|p)$/i.test(j.tagName)&&!j.firstChild){Xinha.removeFromParent(j);++c.empty_tags;return true}return false}function g(j){i(j);f(j);var m;for(var l=j.firstChild;l;l=m){m=l.nextSibling;if(l.nodeType==1&&g(l)){if((Xinha.is_ie&&j.scopeName!="HTML")||(!Xinha.is_ie&&/:/.test(l.tagName))){for(var k=l.childNodes&&l.childNodes.length-1;l.childNodes&&l.childNodes.length&&l.childNodes[k];--k){if(l.nextSibling){l.parentNode.insertBefore(l.childNodes[k],l.nextSibling)}else{l.parentNode.appendChild(l.childNodes[k])}}Xinha.removeFromParent(l);continue}if(b(l)){continue}if(d(l)){continue}}else{if(l.nodeType==8){if(/(\s*\[\s*if\s*(([gl]te?|!)\s*)?(IE|mso)\s*(\d+(\.\d+)?\s*)?\]>)/.test(l.nodeValue)){Xinha.removeFromParent(l);++c.cond_comm}}}}return true}g(this._doc.body);this.updateToolbar()};Xinha.prototype._clearFonts=function(){var a=this.getInnerHTML();if(confirm(Xinha._lc("Would you like to clear font typefaces?"))){a=a.replace(/face="[^"]*"/gi,"");a=a.replace(/font-family:[^;}"']+;?/gi,"")}if(confirm(Xinha._lc("Would you like to clear font sizes?"))){a=a.replace(/size="[^"]*"/gi,"");a=a.replace(/font-size:[^;}"']+;?/gi,"")}if(confirm(Xinha._lc("Would you like to clear font colours?"))){a=a.replace(/color="[^"]*"/gi,"");a=a.replace(/([^\-])color:[^;}"']+;?/gi,"$1")}a=a.replace(/(style|class)="\s*"/gi,"");a=a.replace(/<(font|span)\s*>/gi,"");this.setHTML(a);this.updateToolbar()};Xinha.prototype._splitBlock=function(){this._doc.execCommand("formatblock",false,"div")};Xinha.prototype.forceRedraw=function(){this._doc.body.style.visibility="hidden";this._doc.body.style.visibility=""};Xinha.prototype.focusEditor=function(){switch(this._editMode){case"wysiwyg":try{if(Xinha._someEditorHasBeenActivated){this.activateEditor();this._iframe.contentWindow.focus()}}catch(a){}break;case"textmode":try{this._textArea.focus()}catch(b){}break;default:alert("ERROR: mode "+this._editMode+" is not defined")}return this._doc};Xinha.prototype._undoTakeSnapshot=function(){++this._undoPos;if(this._undoPos>=this.config.undoSteps){this._undoQueue.shift();--this._undoPos}var b=true;var a=this.getInnerHTML();if(this._undoPos>0){b=(this._undoQueue[this._undoPos-1]!=a)}if(b){this._undoQueue[this._undoPos]=a}else{this._undoPos--}};Xinha.prototype.undo=function(){if(this._undoPos>0){var a=this._undoQueue[--this._undoPos];if(a){this.setHTML(a)}else{++this._undoPos}}};Xinha.prototype.redo=function(){if(this._undoPos=0;){var el=ancestors[i];if(!el){continue}var a=document.createElement("a");a.href="javascript:void(0);";a.el=el;a.editor=this;this._statusBarItems.push(a);Xinha.addDom0Event(a,"click",function(){this.blur();this.editor.selectNodeContents(this.el);this.editor.updateToolbar(true);return false});Xinha.addDom0Event(a,"contextmenu",function(){this.blur();var info="Inline style:\n\n";info+=this.el.style.cssText.split(/;\s*/).join(";\n");alert(info);return false});var txt=el.tagName.toLowerCase();switch(txt){case"b":txt="strong";break;case"i":txt="em";break;case"strike":txt="del";break}if(typeof el.style!="undefined"){a.title=el.style.cssText}if(el.id){txt+="#"+el.id}if(el.className){txt+="."+el.className}a.appendChild(document.createTextNode(txt));this._statusBarTree.appendChild(a);if(i!==0){this._statusBarTree.appendChild(document.createTextNode(String.fromCharCode(187)))}Xinha.freeLater(a)}}}for(var cmd in this._toolbarObjects){var btn=this._toolbarObjects[cmd];var inContext=true;if(typeof btn.state!="function"){continue}if(btn.context&&!text){inContext=false;var context=btn.context;var attrs=[];if(/(.*)\[(.*?)\]/.test(context)){context=RegExp.$1;attrs=RegExp.$2.split(",")}context=context.toLowerCase();var match=(context=="*");for(var k=0;k|>=|<|<=)(.*)/);att=contextSplit[1];comp=contextSplit[2];attVal=contextSplit[3];if(!eval(ancestors[k][att]+comp+attVal)){inContext=false;break}}if(inContext){break}}}}btn.state("enabled",(!text||btn.text)&&inContext);if(typeof cmd=="function"){continue}var dropdown=this.config.customSelects[cmd];if((!text||btn.text)&&(typeof dropdown!="undefined")){dropdown.refresh(this);continue}switch(cmd){case"fontname":case"fontsize":if(!text){try{var value=(""+doc.queryCommandValue(cmd)).toLowerCase();if(!value){btn.element.selectedIndex=0;break}var options=this.config[cmd];var sIndex=0;for(var j in options){if((j.toLowerCase()==value)||(options[j].substr(0,value.length).toLowerCase()==value)){btn.element.selectedIndex=sIndex;throw"ok"}++sIndex}btn.element.selectedIndex=0}catch(ex){}}break;case"formatblock":var blocks=[];for(var indexBlock in this.config.formatblock){if(typeof this.config.formatblock[indexBlock]=="string"){blocks[blocks.length]=this.config.formatblock[indexBlock]}}var deepestAncestor=this._getFirstAncestor(this.getSelection(),blocks);if(deepestAncestor){for(var x=0;x"}this.execCommand(a,false,c);break;default:var d=this.config.customSelects[a];if(typeof d!="undefined"){d.action(this)}else{alert("FIXME: combo box "+a+" not implemented")}break}};Xinha.prototype._colorSelector=function(h){var g=this;if(Xinha.is_gecko){try{g._doc.execCommand("useCSS",false,false);g._doc.execCommand("styleWithCSS",false,true)}catch(e){}}var d=g._toolbarObjects[h].element;var f;if(h=="hilitecolor"){if(Xinha.is_ie){h="backcolor";f=Xinha._colorToRgb(g._doc.queryCommandValue("backcolor"))}else{f=Xinha._colorToRgb(g._doc.queryCommandValue("hilitecolor"))}}else{f=Xinha._colorToRgb(g._doc.queryCommandValue("forecolor"))}var b=function(i){g._doc.execCommand(h,false,i)};if(Xinha.is_ie){var a=g.createRange(g.getSelection());b=function(i){a.select();g._doc.execCommand(h,false,i)}}var c=new Xinha.colorPicker({cellsize:g.config.colorPickerCellSize,callback:b,granularity:g.config.colorPickerGranularity,websafe:g.config.colorPickerWebSafe,savecolors:g.config.colorPickerSaveColors});c.open(g.config.colorPickerPosition,d,f)};Xinha.prototype.execCommand=function(h,f,g){var e=this;this.focusEditor();h=h.toLowerCase();if(this.firePluginEvent("onExecCommand",h,f,g)){this.updateToolbar();return false}switch(h){case"htmlmode":this.setMode();break;case"hilitecolor":case"forecolor":this._colorSelector(h);break;case"createlink":this._createLink();break;case"undo":case"redo":if(this._customUndo){this[h]()}else{this._doc.execCommand(h,f,g)}break;case"inserttable":this._insertTable();break;case"insertimage":this._insertImage();break;case"showhelp":this._popupDialog(e.config.URIs.help,null,this);break;case"killword":this._wordClean();break;case"cut":case"copy":case"paste":this._doc.execCommand(h,f,g);if(this.config.killWordOnPaste){this._wordClean()}break;case"lefttoright":case"righttoleft":if(this.config.changeJustifyWithDirection){this._doc.execCommand((h=="righttoleft")?"justifyright":"justifyleft",f,g)}var a=(h=="righttoleft")?"rtl":"ltr";var d=this.getParentElement();while(d&&!Xinha.isBlockElement(d)){d=d.parentNode}if(d){if(d.style.direction==a){d.style.direction=""}else{d.style.direction=a}}break;case"justifyleft":case"justifyright":h.match(/^justify(.*)$/);var c=this.activeElement(this.getSelection());if(c&&c.tagName.toLowerCase()=="img"){c.align=c.align==RegExp.$1?"":RegExp.$1}else{this._doc.execCommand(h,f,g)}break;default:try{this._doc.execCommand(h,f,g)}catch(b){if(this.config.debug){alert(b+"\n\nby execCommand("+h+");")}}break}this.updateToolbar();return false};Xinha.prototype._editorEvent=function(b){var a=this;if(typeof a._textArea["on"+b.type]=="function"){a._textArea["on"+b.type](b)}if(this.isKeyEvent(b)){if(a.firePluginEvent("onKeyPress",b)){return false}if(this.isShortCut(b)){this._shortCuts(b)}}if(b.type=="mousedown"){if(a.firePluginEvent("onMouseDown",b)){return false}}if(a._timerToolbar){clearTimeout(a._timerToolbar)}if(!this.suspendUpdateToolbar){a._timerToolbar=setTimeout(function(){a.updateToolbar();a._timerToolbar=null},250)}};Xinha.prototype._shortCuts=function(b){var a=this.getKey(b).toLowerCase();var d=null;var c=null;switch(a){case"b":d="bold";break;case"i":d="italic";break;case"u":d="underline";break;case"s":d="strikethrough";break;case"l":d="justifyleft";break;case"e":d="justifycenter";break;case"r":d="justifyright";break;case"j":d="justifyfull";break;case"z":d="undo";break;case"y":d="redo";break;case"v":d="paste";break;case"n":d="formatblock";c="p";break;case"0":d="killword";break;case"1":case"2":case"3":case"4":case"5":case"6":d="formatblock";c="h"+a;break}if(d){this.execCommand(d,false,c);Xinha._stopEvent(b)}};Xinha.prototype.convertNode=function(a,c){var b=this._doc.createElement(c);while(a.firstChild){b.appendChild(a.firstChild)}return b};Xinha.prototype.scrollToElement=function(b){if(!b){b=this.getParentElement();if(!b){return}}var a=Xinha.getElementTopLeft(b);this._iframe.contentWindow.scrollTo(a.left,a.top)};Xinha.prototype.getEditorContent=function(){return this.outwardHtml(this.getHTML())};Xinha.prototype.setEditorContent=function(a){this.setHTML(this.inwardHtml(a))};Xinha.updateTextareas=function(){var b;for(var a=0;a<__xinhas.length;a++){b=__xinhas[a];b._textArea.value=b.getEditorContent()}};Xinha.prototype.getHTML=function(){var a="";switch(this._editMode){case"wysiwyg":if(!this.config.fullPage){a=Xinha.getHTML(this._doc.body,false,this).trim()}else{a=this.doctype+"\n"+Xinha.getHTML(this._doc.documentElement,true,this)}break;case"textmode":a=this._textArea.value;break;default:alert("Mode <"+this._editMode+"> not defined!");return false}return a};Xinha.prototype.outwardHtml=function(c){for(var b in this.plugins){var d=this.plugins[b].instance;if(d&&typeof d.outwardHtml=="function"){c=d.outwardHtml(c)}}c=c.replace(/<(\/?)b(\s|>|\/)/ig,"<$1strong$2");c=c.replace(/<(\/?)i(\s|>|\/)/ig,"<$1em$2");c=c.replace(/<(\/?)strike(\s|>|\/)/ig,"<$1del$2");c=c.replace(/(<[^>]*on(click|mouse(over|out|up|down))=['"])if\(window\.parent && window\.parent\.Xinha\)\{return false\}/gi,"$1");var a=location.href.replace(/(https?:\/\/[^\/]*)\/.*/,"$1")+"/";c=c.replace(/https?:\/\/null\//g,a);c=c.replace(/((href|src|background)=[\'\"])\/+/ig,"$1"+a);c=this.outwardSpecialReplacements(c);c=this.fixRelativeLinks(c);if(this.config.sevenBitClean){c=c.replace(/[^ -~\r\n\t]/g,function(e){return(e!=Xinha.cc)?"&#"+e.charCodeAt(0)+";":e})}c=c.replace(/(]*((type=[\"\']text\/)|(language=[\"\'])))(freezescript)/gi,"$1javascript");if(this.config.fullPage){c=Xinha.stripCoreCSS(c)}if(typeof this.config.outwardHtml=="function"){c=this.config.outwardHtml(c)}return c};Xinha.prototype.inwardHtml=function(b){for(var a in this.plugins){var c=this.plugins[a].instance;if(c&&typeof c.inwardHtml=="function"){b=c.inwardHtml(b)}}b=b.replace(/<(\/?)del(\s|>|\/)/ig,"<$1strike$2");b=b.replace(/(<[^>]*on(click|mouse(over|out|up|down))=["'])/gi,"$1if(window.parent && window.parent.Xinha){return false}");b=this.inwardSpecialReplacements(b);b=b.replace(/(]*((type=[\"\']text\/)|(language=[\"\'])))(javascript)/gi,"$1freezescript");var d=new RegExp("((href|src|background)=['\"])/+","gi");b=b.replace(d,"$1"+location.href.replace(/(https?:\/\/[^\/]*)\/.*/,"$1")+"/");b=this.fixRelativeLinks(b);if(this.config.fullPage){b=Xinha.addCoreCSS(b)}if(typeof this.config.inwardHtml=="function"){b=this.config.inwardHtml(b)}return b};Xinha.prototype.outwardSpecialReplacements=function(b){for(var a in this.config.specialReplacements){var e=this.config.specialReplacements[a];var d=a;if(typeof e.replace!="function"||typeof d.replace!="function"){continue}var c=new RegExp(Xinha.escapeStringForRegExp(e),"g");b=b.replace(c,d.replace(/\$/g,"$$$$"))}return b};Xinha.prototype.inwardSpecialReplacements=function(b){for(var a in this.config.specialReplacements){var e=a;var d=this.config.specialReplacements[a];if(typeof e.replace!="function"||typeof d.replace!="function"){continue}var c=new RegExp(Xinha.escapeStringForRegExp(e),"g");b=b.replace(c,d.replace(/\$/g,"$$$$"))}return b};Xinha.prototype.fixRelativeLinks=function(g){if(typeof this.config.expandRelativeUrl!="undefined"&&this.config.expandRelativeUrl){if(g==null){return""}var a=g.match(/(src|href)="([^"]*)"/gi);var j=document.location.href;if(a){var d,k,l,m,c;for(var f=0;f not defined!");return false}return a};Xinha.prototype.setHTML=function(a){if(!this.config.fullPage){this._doc.body.innerHTML=a}else{this.setFullHTML(a)}this._textArea.value=a};Xinha.prototype.setDoctype=function(a){this.doctype=a};Xinha._object=null;Array.prototype.isArray=true;RegExp.prototype.isRegExp=true;Xinha.cloneObject=function(obj){if(!obj){return null}var newObj=obj.isArray?[]:{};if(obj.constructor.toString().match(/\s*function Function\(/)||typeof obj=="function"){newObj=obj}else{if(obj.isRegExp){newObj=eval(obj.toString())}else{for(var n in obj){var node=obj[n];if(typeof node=="object"){newObj[n]=Xinha.cloneObject(node)}else{newObj[n]=node}}}}return newObj};Xinha.flushEvents=function(){var a=0;var c=Xinha._eventFlushers.pop();while(c){try{if(c.length==3){Xinha._removeEvent(c[0],c[1],c[2]);a++}else{if(c.length==2){c[0]["on"+c[1]]=null;c[0]._xinha_dom0Events[c[1]]=null;a++}}}catch(b){}c=Xinha._eventFlushers.pop()}};Xinha._eventFlushers=[];if(document.addEventListener){Xinha._addEvent=function(a,c,b){a.addEventListener(c,b,false);Xinha._eventFlushers.push([a,c,b])};Xinha._removeEvent=function(a,c,b){a.removeEventListener(c,b,false)};Xinha._stopEvent=function(a){a.preventDefault();a.stopPropagation()}}else{if(document.attachEvent){Xinha._addEvent=function(a,c,b){a.attachEvent("on"+c,b);Xinha._eventFlushers.push([a,c,b])};Xinha._removeEvent=function(a,c,b){a.detachEvent("on"+c,b)};Xinha._stopEvent=function(b){try{b.cancelBubble=true;b.returnValue=false}catch(a){}}}else{Xinha._addEvent=function(a,c,b){alert("_addEvent is not supported")};Xinha._removeEvent=function(a,c,b){alert("_removeEvent is not supported")};Xinha._stopEvent=function(a){alert("_stopEvent is not supported")}}}Xinha._addEvents=function(c,a,d){for(var b=a.length;--b>=0;){Xinha._addEvent(c,a[b],d)}};Xinha._removeEvents=function(c,a,d){for(var b=a.length;--b>=0;){Xinha._removeEvent(c,a[b],d)}};Xinha.addOnloadHandler=function(b,a){a=a?a:window;var c=function(){if(arguments.callee.done){return}arguments.callee.done=true;if(Xinha.onloadTimer){clearInterval(Xinha.onloadTimer)}b()};if(Xinha.is_ie){document.attachEvent("onreadystatechange",function(){if(document.readyState==="complete"){document.detachEvent("onreadystatechange",arguments.callee);c()}});if(document.documentElement.doScroll&&typeof window.frameElement==="undefined"){(function(){if(arguments.callee.done){return}try{document.documentElement.doScroll("left")}catch(d){setTimeout(arguments.callee,0);return}c()})()}}else{if(/applewebkit|KHTML/i.test(navigator.userAgent)){Xinha.onloadTimer=a.setInterval(function(){if(/loaded|complete/.test(a.document.readyState)){c()}},10)}else{a.document.addEventListener("DOMContentLoaded",c,false)}}Xinha._addEvent(a,"load",c)};Xinha.addDom0Event=function(b,c,a){Xinha._prepareForDom0Events(b,c);b._xinha_dom0Events[c].unshift(a)};Xinha.prependDom0Event=function(b,c,a){Xinha._prepareForDom0Events(b,c);b._xinha_dom0Events[c].push(a)};Xinha.getEvent=function(a){return a||window.event};Xinha._prepareForDom0Events=function(a,b){if(typeof a._xinha_dom0Events=="undefined"){a._xinha_dom0Events={};Xinha.freeLater(a,"_xinha_dom0Events")}if(typeof a._xinha_dom0Events[b]=="undefined"){a._xinha_dom0Events[b]=[];if(typeof a["on"+b]=="function"){a._xinha_dom0Events[b].push(a["on"+b])}a["on"+b]=function(e){var c=a._xinha_dom0Events[b];var f=true;for(var d=c.length;--d>=0;){a._xinha_tempEventHandler=c[d];if(a._xinha_tempEventHandler(e)===false){a._xinha_tempEventHandler=null;f=false;break}a._xinha_tempEventHandler=null}return f};Xinha._eventFlushers.push([a,b])}};Xinha.prototype.notifyOn=function(b,a){if(typeof this._notifyListeners[b]=="undefined"){this._notifyListeners[b]=[];Xinha.freeLater(this,"_notifyListeners")}this._notifyListeners[b].push(a)};Xinha.prototype.notifyOf=function(c,a){if(this._notifyListeners[c]){for(var b=0;b/ig,">");a=a.replace(/\xA0/g," ");a=a.replace(/\x22/g,""");return a};Xinha.prototype.stripBaseURL=function(a){if(this.config.baseHref===null||!this.config.stripBaseHref){return a}var c=this.config.baseHref.replace(/^(https?:\/\/[^\/]+)(.*)$/,"$1");var b=new RegExp(c);return a.replace(b,"")};if(typeof String.prototype.trim!="function"){String.prototype.trim=function(){return this.replace(/^\s+/,"").replace(/\s+$/,"")}}Xinha._makeColor=function(c){if(typeof c!="number"){return c}var e=c&255;var d=(c>>8)&255;var a=(c>>16)&255;return"rgb("+e+","+d+","+a+")"};Xinha._colorToRgb=function(c){if(!c){return""}var h,f,a;function e(b){return(b<16)?("0"+b.toString(16)):b.toString(16)}if(typeof c=="number"){h=c&255;f=(c>>8)&255;a=(c>>16)&255;return"#"+e(h)+e(f)+e(a)}if(c.substr(0,3)=="rgb"){var d=/rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/;if(c.match(d)){h=parseInt(RegExp.$1,10);f=parseInt(RegExp.$2,10);a=parseInt(RegExp.$3,10);return"#"+e(h)+e(f)+e(a)}return null}if(c.substr(0,1)=="#"){return c}return null};Xinha.prototype._popupDialog=function(a,b,c){Dialog(this.popupURL(a),b,c)};Xinha.prototype.imgURL=function(a,b){if(typeof b=="undefined"){return _editor_url+a}else{return Xinha.getPluginDir(b)+"/img/"+a}};Xinha.prototype.popupURL=function(c){var b="";if(c.match(/^plugin:\/\/(.*?)\/(.*)/)){var d=RegExp.$1;var a=RegExp.$2;if(!/\.(html?|php)$/.test(a)){a+=".html"}b=Xinha.getPluginDir(d)+"/popups/"+a}else{if(c.match(/^\/.*?/)||c.match(/^https?:\/\//)){b=c}else{b=_editor_url+this.config.popupURL+c}}return b};Xinha.getElementById=function(a,e){var c,b,d=document.getElementsByTagName(a);for(b=d.length;--b>=0&&(c=d[b]);){if(c.id==e){return c}}return null};Xinha.prototype._toggleBorders=function(){var b=this._doc.getElementsByTagName("TABLE");if(b.length!==0){if(!this.borders){this.borders=true}else{this.borders=false}for(var a=0;a/i.test(b)){return b.replace(//i,""+a)}else{if(b){return a+b}else{return a}}};Xinha.prototype.addEditorStylesheet=function(a){var b=this._doc.createElement("link");b.rel="stylesheet";b.type="text/css";b.title="XinhaInternalCSS";b.href=a;this._doc.getElementsByTagName("HEAD")[0].appendChild(b)};Xinha.stripCoreCSS=function(a){return a.replace(/]+title="XinhaInternalCSS"(.|\n)*?<\/style>/ig,"").replace(/]+title="XinhaInternalCSS"(.|\n)*?>/ig,"")};Xinha._removeClass=function(e,d){if(!(e&&e.className)){return}var a=e.className.split(" ");var b=[];for(var c=a.length;c>0;){if(a[--c]!=d){b[b.length]=a[c]}}e.className=b.join(" ")};Xinha._addClass=function(b,a){Xinha._removeClass(b,a);b.className+=" "+a};Xinha.addClasses=function(f,e){if(f!==null){var b=f.className.trim().split(" ");var d=e.split(" ");for(var a=0;a0;){if(a[--b]==c){return true}}return false};Xinha._postback_send_charset=true;Xinha._postback=function(b,g,h,a){var e=null;e=Xinha.getXMLHTTPRequestObject();var d="";if(typeof g=="string"){d=g}else{if(typeof g=="object"){for(var c in g){d+=(d.length?"&":"")+c+"="+encodeURIComponent(g[c])}}}function f(){if(e.readyState==4){if(((e.status/100)==2)||Xinha.isRunLocally&&e.status===0){if(typeof h=="function"){h(e.responseText,e)}}else{if(Xinha._postback_send_charset){Xinha._postback_send_charset=false;Xinha._postback(b,g,h,a)}else{if(typeof a=="function"){a(e)}else{alert("An error has occurred: "+e.statusText+"\nURL: "+b)}}}}}e.onreadystatechange=f;e.open("POST",b,true);e.setRequestHeader("Content-Type","application/x-www-form-urlencoded"+(Xinha._postback_send_charset?"; charset=UTF-8":""));e.send(d)};Xinha._getback=function(b,e,a){var c=null;c=Xinha.getXMLHTTPRequestObject();function d(){if(c.readyState==4){if(((c.status/100)==2)||Xinha.isRunLocally&&c.status===0){e(c.responseText,c)}else{if(typeof a=="function"){a(c)}else{alert("An error has occurred: "+c.statusText+"\nURL: "+b)}}}}c.onreadystatechange=d;c.open("GET",b,true);c.send(null)};Xinha.ping=function(c,b,a){var d=null;d=Xinha.getXMLHTTPRequestObject();function e(){if(d.readyState==4){if(((d.status/100)==2)||Xinha.isRunLocally&&d.status===0){if(b){b(d)}}else{if(a){a(d)}}}}var f=Xinha.is_opera?"GET":"HEAD";d.onreadystatechange=e;d.open(f,c,true);d.send(null)};Xinha._geturlcontent=function(b,a){var c=null;c=Xinha.getXMLHTTPRequestObject();c.open("GET",b,false);c.send(null);if(((c.status/100)==2)||Xinha.isRunLocally&&c.status===0){return(a)?c.responseXML:c.responseText}else{return""}};if(typeof dumpValues=="undefined"){dumpValues=function(c){var b="";for(var d in c){if(window.console&&typeof window.console.log=="function"){if(typeof console.firebug!="undefined"){console.log(c)}else{console.log(d+" = "+c[d]+"\n")}}else{b+=d+" = "+c[d]+"\n"}}if(b){if(document.getElementById("errors")){document.getElementById("errors").value+=b}else{var a=window.open("","debugger");a.document.write("
"+b+"
")}}}}if(!Array.prototype.contains){Array.prototype.contains=function(c){var b=this;for(var a=0;a>2;h=((l&3)<<4)|(j>>4);f=((j&15)<<2)|(g>>6);e=g&63;if(isNaN(j)){f=e=64}else{if(isNaN(g)){e=64}}a=a+b.charAt(k)+b.charAt(h)+b.charAt(f)+b.charAt(e)}while(c>4);j=((h&15)<<4)|(f>>2);g=((f&3)<<6)|e;a=a+String.fromCharCode(l);if(f!=64){a=a+String.fromCharCode(j)}if(e!=64){a=a+String.fromCharCode(g)}}while(cb){a=d.document.body.scrollWidth;e=d.document.body.scrollHeight}else{a=d.document.documentElement.scrollWidth;e=d.document.documentElement.scrollHeight}return{x:a,y:e}};Xinha.prototype.scrollPos=function(b){b=(b)?b:window;var a,c;if(typeof b.pageYOffset!="undefined"){a=b.pageXOffset;c=b.pageYOffset}else{if(b.document.documentElement&&typeof document.documentElement.scrollTop!="undefined"){a=b.document.documentElement.scrollLeft;c=b.document.documentElement.scrollTop}else{if(b.document.body){a=b.document.body.scrollLeft;c=b.document.body.scrollTop}}}return{x:a,y:c}};Xinha.getElementTopLeft=function(a){var c=0;var b=0;if(a.offsetParent){c=a.offsetLeft;b=a.offsetTop;while(a=a.offsetParent){c+=a.offsetLeft;b+=a.offsetTop}}return{top:b,left:c}};Xinha.findPosX=function(a){var b=0;if(a.offsetParent){return Xinha.getElementTopLeft(a).left}else{if(a.x){b+=a.x}}return b};Xinha.findPosY=function(b){var a=0;if(b.offsetParent){return Xinha.getElementTopLeft(b).top}else{if(b.y){a+=b.y}}return a};Xinha.createLoadingMessages=function(b){if(Xinha.loadingMessages||!Xinha.isSupportedBrowser){return}Xinha.loadingMessages=[];for(var a=0;a +* +* var html = __xinhas[0].getEditorContent(); // gives you the HTML of the first editor in the page +* +*/ +var __xinhas = []; + +// browser identification +/** Cache the user agent for the following checks + * @type String + * @private + */ +Xinha.agt = navigator.userAgent.toLowerCase(); +/** Browser is Microsoft Internet Explorer + * @type Boolean + */ +Xinha.is_ie = ((Xinha.agt.indexOf("msie") != -1) && (Xinha.agt.indexOf("opera") == -1)); +/** Version Number, if browser is Microsoft Internet Explorer + * @type Float + */ +Xinha.ie_version= parseFloat(Xinha.agt.substring(Xinha.agt.indexOf("msie")+5)); +/** Browser is Opera + * @type Boolean + */ +Xinha.is_opera = (Xinha.agt.indexOf("opera") != -1); +/** Version Number, if browser is Opera + * @type Float + */ +if(Xinha.is_opera && Xinha.agt.match(/opera[\/ ]([0-9.]+)/)) +{ + Xinha.opera_version = parseFloat(RegExp.$1); +} +else +{ + Xinha.opera_version = 0; +} +/** Browserengine is KHTML (Konqueror, Safari) + * @type Boolean + */ +Xinha.is_khtml = (Xinha.agt.indexOf("khtml") != -1); +/** Browser is WebKit + * @type Boolean + */ +Xinha.is_webkit = (Xinha.agt.indexOf("applewebkit") != -1); +/** Webkit build number + * @type Integer + */ +Xinha.webkit_version = parseInt(navigator.appVersion.replace(/.*?AppleWebKit\/([\d]).*?/,'$1'), 10); + +/** Browser is Safari + * @type Boolean + */ +Xinha.is_safari = (Xinha.agt.indexOf("safari") != -1); + +/** Browser is Google Chrome + * @type Boolean + */ +Xinha.is_chrome = (Xinha.agt.indexOf("chrome") != -1); + +/** OS is MacOS + * @type Boolean + */ +Xinha.is_mac = (Xinha.agt.indexOf("mac") != -1); +/** Browser is Microsoft Internet Explorer Mac + * @type Boolean + */ +Xinha.is_mac_ie = (Xinha.is_ie && Xinha.is_mac); +/** Browser is Microsoft Internet Explorer Windows + * @type Boolean + */ +Xinha.is_win_ie = (Xinha.is_ie && !Xinha.is_mac); +/** Browser engine is Gecko (Mozilla), applies also to Safari and Opera which work + * largely similar. + *@type Boolean + */ +Xinha.is_gecko = (navigator.product == "Gecko") || Xinha.is_opera; +/** Browser engine is really Gecko, i.e. Browser is Firefox (or Netscape, SeaMonkey, Flock, Songbird, Beonex, K-Meleon, Camino, Galeon, Kazehakase, Skipstone, or whatever derivate might exist out there...) + * @type Boolean + */ +Xinha.is_real_gecko = (navigator.product == "Gecko" && !Xinha.is_webkit); + +/** Gecko version lower than 1.9 + * @type Boolean + */ +Xinha.is_ff2 = Xinha.is_real_gecko && parseInt(navigator.productSub.substr(0,10), 10) < 20071210; + +/** File is opened locally opened ("file://" protocol) + * @type Boolean + * @private + */ +Xinha.isRunLocally = document.URL.toLowerCase().search(/^file:/) != -1; +/** Editing is enabled by document.designMode (Gecko, Opera), as opposed to contenteditable (IE) + * @type Boolean + * @private + */ +Xinha.is_designMode = (typeof document.designMode != 'undefined' && !Xinha.is_ie); // IE has designMode, but we're not using it + +/** Check if Xinha can run in the used browser, otherwise the textarea will be remain unchanged + * @type Boolean + * @private + */ +Xinha.checkSupportedBrowser = function() +{ + return Xinha.is_real_gecko || (Xinha.is_opera && Xinha.opera_version >= 9.2) || Xinha.ie_version >= 5.5 || Xinha.webkit_version >= 522; +}; +/** Cache result of checking for browser support + * @type Boolean + * @private + */ +Xinha.isSupportedBrowser = Xinha.checkSupportedBrowser(); + +if ( Xinha.isRunLocally && Xinha.isSupportedBrowser) +{ + alert('Xinha *must* be installed on a web server. Locally opened files (those that use the "file://" protocol) cannot properly function. Xinha will try to initialize but may not be correctly loaded.'); +} + +/** Creates a new Xinha object + * @version $Rev: 1263 $ $LastChangedDate: 2010-05-12 09:40:06 +1200 (Wed, 12 May 2010) $ + * @constructor + * @param {String|DomNode} textarea the textarea to replace; can be either only the id or the DOM object as returned by document.getElementById() + * @param {Xinha.Config} config optional if no Xinha.Config object is passed, the default config is used + */ +function Xinha(textarea, config) +{ + if ( !Xinha.isSupportedBrowser ) + { + return; + } + + if ( !textarea ) + { + throw new Error ("Tried to create Xinha without textarea specified."); + } + + if ( typeof config == "undefined" ) + { + /** The configuration used in the editor + * @type Xinha.Config + */ + this.config = new Xinha.Config(); + } + else + { + this.config = config; + } + + if ( typeof textarea != 'object' ) + { + textarea = Xinha.getElementById('textarea', textarea); + } + /** This property references the original textarea, which is at the same time the editor in text mode + * @type DomNode textarea + */ + this._textArea = textarea; + this._textArea.spellcheck = false; + Xinha.freeLater(this, '_textArea'); + + // + /** Before we modify anything, get the initial textarea size + * @private + * @type Object w,h + */ + this._initial_ta_size = + { + w: textarea.style.width ? textarea.style.width : ( textarea.offsetWidth ? ( textarea.offsetWidth + 'px' ) : ( textarea.cols + 'em') ), + h: textarea.style.height ? textarea.style.height : ( textarea.offsetHeight ? ( textarea.offsetHeight + 'px' ) : ( textarea.rows + 'em') ) + }; + + if ( document.getElementById("loading_" + textarea.id) || this.config.showLoading ) + { + if (!document.getElementById("loading_" + textarea.id)) + { + Xinha.createLoadingMessage(textarea); + } + this.setLoadingMessage(Xinha._lc("Constructing object")); + } + + /** the current editing mode + * @private + * @type string "wysiwyg"|"text" + */ + this._editMode = "wysiwyg"; + /** this object holds the plugins used in the editor + * @private + * @type Object + */ + this.plugins = {}; + /** periodically updates the toolbar + * @private + * @type timeout + */ + this._timerToolbar = null; + /** periodically takes a snapshot of the current editor content + * @private + * @type timeout + */ + this._timerUndo = null; + /** holds the undo snapshots + * @private + * @type Array + */ + this._undoQueue = [this.config.undoSteps]; + /** the current position in the undo queue + * @private + * @type integer + */ + this._undoPos = -1; + /** use our own undo implementation (true) or the browser's (false) + * @private + * @type Boolean + */ + this._customUndo = true; + /** the document object of the page Xinha is embedded in + * @private + * @type document + */ + this._mdoc = document; // cache the document, we need it in plugins + /** doctype of the edited document (fullpage mode) + * @private + * @type string + */ + this.doctype = ''; + /** running number that identifies the current editor + * @public + * @type integer + */ + this.__htmlarea_id_num = __xinhas.length; + __xinhas[this.__htmlarea_id_num] = this; + + /** holds the events for use with the notifyOn/notifyOf system + * @private + * @type Object + */ + this._notifyListeners = {}; + + // Panels + var panels = + { + right: + { + on: true, + container: document.createElement('td'), + panels: [] + }, + left: + { + on: true, + container: document.createElement('td'), + panels: [] + }, + top: + { + on: true, + container: document.createElement('td'), + panels: [] + }, + bottom: + { + on: true, + container: document.createElement('td'), + panels: [] + } + }; + + for ( var i in panels ) + { + if(!panels[i].container) { continue; } // prevent iterating over wrong type + panels[i].div = panels[i].container; // legacy + panels[i].container.className = 'panels panels_' + i; + Xinha.freeLater(panels[i], 'container'); + Xinha.freeLater(panels[i], 'div'); + } + /** holds the panels + * @private + * @type Array + */ + // finally store the variable + this._panels = panels; + + // Init some properties that are defined later + /** The statusbar container + * @type DomNode statusbar div + */ + this._statusBar = null; + /** The DOM path that is shown in the statusbar in wysiwyg mode + * @private + * @type DomNode + */ + this._statusBarTree = null; + /** The message that is shown in the statusbar in text mode + * @private + * @type DomNode + */ + this._statusBarTextMode = null; + /** Holds the items of the DOM path that is shown in the statusbar in wysiwyg mode + * @private + * @type Array tag names + */ + this._statusBarItems = []; + /** Holds the parts (table cells) of the UI (toolbar, panels, statusbar) + + * @type Object framework parts + */ + this._framework = {}; + /** Them whole thing (table) + * @private + * @type DomNode + */ + this._htmlArea = null; + /** This is the actual editable area.
+ * Technically it's an iframe that's made editable using window.designMode = 'on', respectively document.body.contentEditable = true (IE).
+ * Use this property to get a grip on the iframe's window features
+ * + * @type window + */ + this._iframe = null; + /** The document object of the iframe.
+ * Use this property to perform DOM operations on the edited document + * @type document + */ + this._doc = null; + /** The toolbar + * @private + * @type DomNode + */ + this._toolBar = this._toolbar = null; //._toolbar is for legacy, ._toolBar is better thanks. + /** Holds the botton objects + * @private + * @type Object + */ + this._toolbarObjects = {}; + + //hook in config.Events as as a "plugin" + this.plugins.Events = + { + name: 'Events', + developer : 'The Xinha Core Developer Team', + instance: config.Events + }; +}; +// ray: What is this for? Do we need it? +Xinha.onload = function() { }; +Xinha.init = function() { Xinha.onload(); }; + +// cache some regexps +/** Identifies HTML tag names +* @type RegExp +*/ +Xinha.RE_tagName = /(<\/|<)\s*([^ \t\n>]+)/ig; +/** Exracts DOCTYPE string from HTML +* @type RegExp +*/ +Xinha.RE_doctype = /()\n?/i; +/** Finds head section in HTML +* @type RegExp +*/ +Xinha.RE_head = /((.|\n)*?)<\/head>/i; +/** Finds body section in HTML +* @type RegExp +*/ +Xinha.RE_body = /]*>((.|\n|\r|\t)*?)<\/body>/i; +/** Special characters that need to be escaped when dynamically creating a RegExp from an arbtrary string +* @private +* @type RegExp +*/ +Xinha.RE_Specials = /([\/\^$*+?.()|{}\[\]])/g; +/** When dynamically creating a RegExp from an arbtrary string, some charactes that have special meanings in regular expressions have to be escaped. +* Run any string through this function to escape reserved characters. +* @param {string} string the string to be escaped +* @returns string +*/ +Xinha.escapeStringForRegExp = function (string) +{ + return string.replace(Xinha.RE_Specials, '\\$1'); +}; +/** Identifies email addresses +* @type RegExp +*/ +Xinha.RE_email = /^[_a-z\d\-\.]{3,}@[_a-z\d\-]{2,}(\.[_a-z\d\-]{2,})+$/i; +/** Identifies URLs +* @type RegExp +*/ +Xinha.RE_url = /(https?:\/\/)?(([a-z0-9_]+:[a-z0-9_]+@)?[a-z0-9_\-]{2,}(\.[a-z0-9_\-]{2,}){2,}(:[0-9]+)?(\/\S+)*)/i; + + + +/** + * This class creates an object that can be passed to the Xinha constructor as a parameter. + * Set the object's properties as you need to configure the editor (toolbar etc.) + * @version $Rev: 1263 $ $LastChangedDate: 2010-05-12 09:40:06 +1200 (Wed, 12 May 2010) $ + * @constructor + */ +Xinha.Config = function() +{ + /** The svn revision number + * @type Number + */ + this.version = Xinha.version.Revision; + + /** This property controls the width of the editor.
+ * Allowed values are 'auto', 'toolbar' or a numeric value followed by "px".
+ * auto: let Xinha choose the width to use.
+ * toolbar: compute the width size from the toolbar width.
+ * numeric value: forced width in pixels ('600px').
+ * + * Default: "auto" + * @type String + */ + this.width = "auto"; + /** This property controls the height of the editor.
+ * Allowed values are 'auto' or a numeric value followed by px.
+ * "auto": let Xinha choose the height to use.
+ * numeric value: forced height in pixels ('200px').
+ * Default: "auto" + * @type String + */ + this.height = "auto"; + + /** Specifies whether the toolbar should be included + * in the size, or are extra to it. If false then it's recommended + * to have the size set as explicit pixel sizes (either in Xinha.Config or on your textarea)
+ * + * Default: true + * + * @type Boolean + */ + this.sizeIncludesBars = true; + /** + * Specifies whether the panels should be included + * in the size, or are extra to it. If false then it's recommended + * to have the size set as explicit pixel sizes (either in Xinha.Config or on your textarea)
+ * + * Default: true + * + * @type Boolean + */ + this.sizeIncludesPanels = true; + + /** + * each of the panels has a dimension, for the left/right it's the width + * for the top/bottom it's the height. + * + * WARNING: PANEL DIMENSIONS MUST BE SPECIFIED AS PIXEL WIDTHS
+ *Default values: + *
+  *	  xinha_config.panel_dimensions =
+  *   {
+  *	    left:   '200px', // Width
+  *	    right:  '200px',
+  *	    top:    '100px', // Height
+  *	    bottom: '100px'
+  *	  }
+  *
+ * @type Object + */ + this.panel_dimensions = + { + left: '200px', // Width + right: '200px', + top: '100px', // Height + bottom: '100px' + }; + + /** To make the iframe width narrower than the toolbar width, e.g. to maintain + * the layout when editing a narrow column of text, set the next parameter (in pixels).
+ * + * Default: true + * + * @type Integer|null + */ + this.iframeWidth = null; + + /** Enable creation of the status bar?
+ * + * Default: true + * + * @type Boolean + */ + this.statusBar = true; + + /** Intercept ^V and use the Xinha paste command + * If false, then passes ^V through to browser editor widget, which is the only way it works without problems in Mozilla
+ * + * Default: false + * + * @type Boolean + */ + this.htmlareaPaste = false; + + /** Gecko only: Let the built-in routine for handling the return key decide if to enter br or p tags, + * or use a custom implementation.
+ * For information about the rules applied by Gecko, see Mozilla website
+ * Possible values are built-in or best
+ * + * Default: "best" + * + * @type String + */ + this.mozParaHandler = 'best'; + + /** This determines the method how the HTML output is generated. + * There are two choices: + * + * + * + * + * + * + * + * + * + * + *
DOMwalkThis is the classic and proven method. It recusively traverses the DOM tree + * and builds the HTML string "from scratch". Tends to be a bit slow, especially in IE.
TransformInnerHTMLThis method uses the JavaScript innerHTML property and relies on Regular Expressions to produce + * clean XHTML output. This method is much faster than the other one.
+ * + * Default: "DOMwalk" + * + * @type String + */ + this.getHtmlMethod = 'DOMwalk'; + + /** Maximum size of the undo queue
+ * Default: 20 + * @type Integer + */ + this.undoSteps = 20; + + /** The time interval at which undo samples are taken
+ * Default: 500 (1/2 sec) + * @type Integer milliseconds + */ + this.undoTimeout = 500; + + /** Set this to true if you want to explicitly right-justify when setting the text direction to right-to-left
+ * Default: false + * @type Boolean + */ + this.changeJustifyWithDirection = false; + + /** If true then Xinha will retrieve the full HTML, starting with the <HTML> tag.
+ * Default: false + * @type Boolean + */ + this.fullPage = false; + + /** Raw style definitions included in the edited document
+ * When a lot of inline style is used, perhaps it is wiser to use one or more external stylesheets.
+ * To set tags P in red, H1 in blue andn A not underlined, we may do the following + *
+   * xinha_config.pageStyle =
+   *  'p { color:red; }\n' +
+   *  'h1 { color:bleu; }\n' +
+   *  'a {text-decoration:none; }';
+   *
+ * Default: "" (empty) + * @type String + */ + this.pageStyle = ""; + + /** Array of external stylesheets to load. (Reference these absolutely)
+ * Example
+ *
xinha_config.pageStyleSheets = ["/css/myPagesStyleSheet.css","/css/anotherOne.css"];
+ * Default: [] (empty) + * @type Array + */ + this.pageStyleSheets = []; + + // specify a base href for relative links + /** Specify a base href for relative links
+ * ATTENTION: this does not work as expected and needs t be changed, see Ticket #961
+ * Default: null + * @type String|null + */ + this.baseHref = null; + + /** If true, relative URLs (../) will be made absolute. + * When the editor is in different directory depth + * as the edited page relative image sources will break the display of your images. + * this fixes an issue where Mozilla converts the urls of images and links that are on the same server + * to relative ones (../) when dragging them around in the editor (Ticket #448)
+ * Default: true + * @type Boolean + */ + this.expandRelativeUrl = true; + + /** We can strip the server part out of URL to make/leave them semi-absolute, reason for this + * is that the browsers will prefix the server to any relative links to make them absolute, + * which isn't what you want most the time.
+ * Default: true + * @type Boolean + */ + this.stripBaseHref = true; + + /** We can strip the url of the editor page from named links (eg <a href="#top">...</a>) and links + * that consist only of URL parameters (eg <a href="?parameter=value">...</a>) + * reason for this is that browsers tend to prefixe location.href to any href that + * that don't have a full url
+ * Default: true + * @type Boolean + */ + this.stripSelfNamedAnchors = true; + + /** In URLs all characters above ASCII value 127 have to be encoded using % codes
+ * Default: true + * @type Boolean + */ + this.only7BitPrintablesInURLs = true; + + + /** If you are putting the HTML written in Xinha into an email you might want it to be 7-bit + * characters only. This config option will convert all characters consuming + * more than 7bits into UNICODE decimal entity references (actually it will convert anything + * below (chr 20) except cr, lf and tab and above (~, chr 7E))
+ * Default: false + * @type Boolean + */ + this.sevenBitClean = false; + + + /** Sometimes we want to be able to replace some string in the html coming in and going out + * so that in the editor we use the "internal" string, and outside and in the source view + * we use the "external" string this is useful for say making special codes for + * your absolute links, your external string might be some special code, say "{server_url}" + * an you say that the internal represenattion of that should be http://your.server/
+ * Example: { 'html_string' : 'wysiwyg_string' }
+ * Default: {} (empty) + * @type Object + */ + this.specialReplacements = {}; //{ 'html_string' : 'wysiwyg_string' } + + /** A filter function for the HTML used inside the editor
+ * Default: function (html) { return html } + * + * @param {String} html The whole document's HTML content + * @return {String} The processed HTML + */ + this.inwardHtml = function (html) { return html; }; + + /** A filter function for the generated HTML
+ * Default: function (html) { return html } + * + * @param {String} html The whole document's HTML content + * @return {String} The processed HTML + */ + this.outwardHtml = function (html) { return html; }; + + /** This setting determines whether or not the editor will be automatically activated and focused when the page loads. + * If the page contains only a single editor, autofocus can be set to true to focus it. + * Alternatively, if the page contains multiple editors, autofocus may be set to the ID of the text area of the editor to be focused. + * For example, the following setting would focus the editor attached to the text area whose ID is "myTextArea": + * xinha_config.autofocus = "myTextArea"; + * Default: false + * @type Boolean|String + */ + this.autofocus = false; + + /** Set to true if you want Word code to be cleaned upon Paste. This only works if + * you use the toolbr button to paste, not ^V. This means that due to the restrictions + * regarding pasting, this actually has no real effect in Mozilla
+ * Default: true + * @type Boolean + */ + this.killWordOnPaste = true; + + /** Enable the 'Target' field in the Make Link dialog. Note that the target attribute is invalid in (X)HTML strict
+ * Default: true + * @type Boolean + */ + this.makeLinkShowsTarget = true; + + /** CharSet of the iframe, default is the charset of the document + * @type String + */ + this.charSet = (typeof document.characterSet != 'undefined') ? document.characterSet : document.charset; + + /** Whether the edited document should be rendered in Quirksmode or Standard Compliant (Strict) Mode.
+ * This is commonly known as the "doctype switch"
+ * for details read here http://www.quirksmode.org/css/quirksmode.html + * + * Possible values:
+ * true : Quirksmode is used
+ * false : Strict mode is used
+ * null (default): the mode of the document Xinha is in is used + * @type Boolean|null + */ + this.browserQuirksMode = null; + + // URL-s + this.imgURL = "images/"; + this.popupURL = "popups/"; + + /** RegExp allowing to remove certain HTML tags when rendering the HTML.
+ * Example: remove span and font tags + * + * xinha_config.htmlRemoveTags = /span|font/; + * + * Default: null + * @type RegExp|null + */ + this.htmlRemoveTags = null; + + /** Turning this on will turn all "linebreak" and "separator" items in your toolbar into soft-breaks, + * this means that if the items between that item and the next linebreak/separator can + * fit on the same line as that which came before then they will, otherwise they will + * float down to the next line. + + * If you put a linebreak and separator next to each other, only the separator will + * take effect, this allows you to have one toolbar that works for both flowToolbars = true and false + * infact the toolbar below has been designed in this way, if flowToolbars is false then it will + * create explictly two lines (plus any others made by plugins) breaking at justifyleft, however if + * flowToolbars is false and your window is narrow enough then it will create more than one line + * even neater, if you resize the window the toolbars will reflow.
+ * Default: true + * @type Boolean + */ + this.flowToolbars = true; + + /** Set to center or right to change button alignment in toolbar + * @type String + */ + this.toolbarAlign = "left"; + + /** Set to true to display the font names in the toolbar font select list in their actual font. + * Note that this doesn't work in IE, but doesn't hurt anything either. + * Default: false + * @type Boolean + */ + this.showFontStylesInToolbar = false; + + /** Set to true if you want the loading panel to show at startup
+ * Default: false + * @type Boolean + */ + this.showLoading = false; + + /** Set to false if you want to allow JavaScript in the content, otherwise <script> tags are stripped out.
+ * This currently only affects the "DOMwalk" getHtmlMethod.
+ * Default: true + * @type Boolean + */ + this.stripScripts = true; + + /** See if the text just typed looks like a URL, or email address + * and link it appropriatly + * Note: Setting this option to false only affects Mozilla based browsers. + * In InternetExplorer this is native behaviour and cannot be turned off.
+ * Default: true + * @type Boolean + */ + this.convertUrlsToLinks = true; + + + /** Size of color picker cells
+ * Use number + "px"
+ * Default: "6px" + * @type String + */ + this.colorPickerCellSize = '6px'; + /** Granularity of color picker cells (number per column/row)
+ * Default: 18 + * @type Integer + */ + this.colorPickerGranularity = 18; + /** Position of color picker from toolbar button
+ * Default: "bottom,right" + * @type String + */ + this.colorPickerPosition = 'bottom,right'; + /** Set to true to show only websafe checkbox in picker
+ * Default: false + * @type Boolean + */ + this.colorPickerWebSafe = false; + /** Number of recent colors to remember
+ * Default: 20 + * @type Integer + */ + this.colorPickerSaveColors = 20; + + /** Start up the editor in fullscreen mode
+ * Default: false + * @type Boolean + */ + this.fullScreen = false; + + /** You can tell the fullscreen mode to leave certain margins on each side.
+ * The value is an array with the values for [top,right,bottom,left] in that order
+ * Default: [0,0,0,0] + * @type Array + */ + this.fullScreenMargins = [0,0,0,0]; + + + /** Specify the method that is being used to calculate the editor's size
+ * when we return from fullscreen mode. + * There are two choices: + * + * + * + * + * + * + * + * + * + * + *
initSizeUse the internal Xinha.initSize() method to calculate the editor's + * dimensions. This is suitable for most usecases.
restoreThe editor's dimensions will be stored before going into fullscreen + * mode and restored when we return to normal mode, taking a possible + * window resize during fullscreen in account.
+ * + * Default: "initSize" + * @type String + */ + this.fullScreenSizeDownMethod = 'initSize'; + + /** This array orders all buttons except plugin buttons in the toolbar. Plugin buttons typically look for one + * a certain button in the toolbar and place themselves next to it. + * Default value: + *
+   *xinha_config.toolbar =
+   * [
+   *   ["popupeditor"],
+   *   ["separator","formatblock","fontname","fontsize","bold","italic","underline","strikethrough"],
+   *   ["separator","forecolor","hilitecolor","textindicator"],
+   *   ["separator","subscript","superscript"],
+   *   ["linebreak","separator","justifyleft","justifycenter","justifyright","justifyfull"],
+   *   ["separator","insertorderedlist","insertunorderedlist","outdent","indent"],
+   *   ["separator","inserthorizontalrule","createlink","insertimage","inserttable"],
+   *   ["linebreak","separator","undo","redo","selectall","print"], (Xinha.is_gecko ? [] : ["cut","copy","paste","overwrite","saveas"]),
+   *   ["separator","killword","clearfonts","removeformat","toggleborders","splitblock","lefttoright", "righttoleft"],
+   *   ["separator","htmlmode","showhelp","about"]
+   * ];
+   *
+ * @type Array + */ + this.toolbar = + [ + ["popupeditor"], + ["separator","formatblock","fontname","fontsize","bold","italic","underline","strikethrough"], + ["separator","forecolor","hilitecolor","textindicator"], + ["separator","subscript","superscript"], + ["linebreak","separator","justifyleft","justifycenter","justifyright","justifyfull"], + ["separator","insertorderedlist","insertunorderedlist","outdent","indent"], + ["separator","inserthorizontalrule","createlink","insertimage","inserttable"], + ["linebreak","separator","undo","redo","selectall","print"], (Xinha.is_gecko ? [] : ["cut","copy","paste","overwrite","saveas"]), + ["separator","killword","clearfonts","removeformat","toggleborders","splitblock","lefttoright", "righttoleft"], + ["separator","htmlmode","showhelp","about"] + ]; + + /** The fontnames listed in the fontname dropdown + * Default value: + *
+   *xinha_config.fontname =
+   *{
+   *  "— font —" : '',
+   *  "Arial"                : 'arial,helvetica,sans-serif',
+   *  "Courier New"          : 'courier new,courier,monospace',
+   *  "Georgia"              : 'georgia,times new roman,times,serif',
+   *  "Tahoma"               : 'tahoma,arial,helvetica,sans-serif',
+   *  "Times New Roman"      : 'times new roman,times,serif',
+   *  "Verdana"              : 'verdana,arial,helvetica,sans-serif',
+   *  "impact"               : 'impact',
+   *  "WingDings"            : 'wingdings'
+   *};
+   *
+ * @type Object + */ + this.fontname = + { + "— font —": "", // — is mdash + "Arial" : 'arial,helvetica,sans-serif', + "Courier New" : 'courier new,courier,monospace', + "Georgia" : 'georgia,times new roman,times,serif', + "Tahoma" : 'tahoma,arial,helvetica,sans-serif', + "Times New Roman" : 'times new roman,times,serif', + "Verdana" : 'verdana,arial,helvetica,sans-serif', + "impact" : 'impact', + "WingDings" : 'wingdings' + }; + + /** The fontsizes listed in the fontsize dropdown + * Default value: + *
+   *xinha_config.fontsize =
+   *{
+   *  "— size —": "",
+   *  "1 (8 pt)" : "1",
+   *  "2 (10 pt)": "2",
+   *  "3 (12 pt)": "3",
+   *  "4 (14 pt)": "4",
+   *  "5 (18 pt)": "5",
+   *  "6 (24 pt)": "6",
+   *  "7 (36 pt)": "7"
+   *};
+   *
+ * @type Object + */ + this.fontsize = + { + "— size —": "", // — is mdash + "1 (8 pt)" : "1", + "2 (10 pt)": "2", + "3 (12 pt)": "3", + "4 (14 pt)": "4", + "5 (18 pt)": "5", + "6 (24 pt)": "6", + "7 (36 pt)": "7" + }; + /** The tags listed in the formatblock dropdown + * Default value: + *
+   *xinha_config.formatblock =
+   *{
+   *  "— size —": "",
+   *  "1 (8 pt)" : "1",
+   *  "2 (10 pt)": "2",
+   *  "3 (12 pt)": "3",
+   *  "4 (14 pt)": "4",
+   *  "5 (18 pt)": "5",
+   *  "6 (24 pt)": "6",
+   *  "7 (36 pt)": "7"
+   *};
+   *
+ * @type Object + */ + this.formatblock = + { + "— format —": "", // — is mdash + "Heading 1": "h1", + "Heading 2": "h2", + "Heading 3": "h3", + "Heading 4": "h4", + "Heading 5": "h5", + "Heading 6": "h6", + "Normal" : "p", + "Address" : "address", + "Formatted": "pre" + }; + + this.dialogOptions = + { + 'centered' : true, //true: dialog is shown in the center the screen, false dialog is shown near the clicked toolbar button + 'greyout':true, //true: when showing modal dialogs, the page behind the dialoge is greyed-out + 'closeOnEscape':true + }; + /** You can add functions to this object to be executed on specific events + * Example: + *
+   * xinha_config.Events.onKeyPress = function (event)
+   * {
+   *    //do something 
+   *    return false;
+   * }
+   * 
+ * Note that this inside the function refers to the respective Xinha object + * The possible function names are documented at http://trac.xinha.org/wiki/Documentation/EventHooks + */ + this.Events = {}; + + /** ?? + * Default: {} + * @type Object + */ + this.customSelects = {}; + + /** Switches on some debugging (only in execCommand() as far as I see at the moment)
+ * + * Default: false + * @type Boolean + */ + this.debug = false; + + this.URIs = + { + "blank": _editor_url + "popups/blank.html", + "link": _editor_url + "modules/CreateLink/link.html", + "insert_image": _editor_url + "modules/InsertImage/insert_image.html", + "insert_table": _editor_url + "modules/InsertTable/insert_table.html", + "select_color": _editor_url + "popups/select_color.html", + "help": _editor_url + "popups/editor_help.html" + }; + + /** The button list conains the definitions of the toolbar button. Normally, there's nothing to change here :) + *
ADDING CUSTOM BUTTONS: please read below! + * format of the btnList elements is "ID: [ ToolTip, Icon, Enabled in text mode?, ACTION ]" + * - ID: unique ID for the button. If the button calls document.execCommand + * it's wise to give it the same name as the called command. + * - ACTION: function that gets called when the button is clicked. + * it has the following prototype: + * function(editor, buttonName) + * - editor is the Xinha object that triggered the call + * - buttonName is the ID of the clicked button + * These 2 parameters makes it possible for you to use the same + * handler for more Xinha objects or for more different buttons. + * - ToolTip: tooltip, will be translated below + * - Icon: path to an icon image file for the button + * OR; you can use an 18x18 block of a larger image by supllying an array + * that has three elemtents, the first is the larger image, the second is the column + * the third is the row. The ros and columns numbering starts at 0 but there is + * a header row and header column which have numbering to make life easier. + * See images/buttons_main.gif to see how it's done. + * - Enabled in text mode: if false the button gets disabled for text-only mode; otherwise enabled all the time.
+ * @type Object + */ + this.btnList = + { + bold: [ "Bold", Xinha._lc({key: 'button_bold', string: ["ed_buttons_main.png",3,2]}, 'Xinha'), false, function(e) { e.execCommand("bold"); } ], + italic: [ "Italic", Xinha._lc({key: 'button_italic', string: ["ed_buttons_main.png",2,2]}, 'Xinha'), false, function(e) { e.execCommand("italic"); } ], + underline: [ "Underline", Xinha._lc({key: 'button_underline', string: ["ed_buttons_main.png",2,0]}, 'Xinha'), false, function(e) { e.execCommand("underline"); } ], + strikethrough: [ "Strikethrough", Xinha._lc({key: 'button_strikethrough', string: ["ed_buttons_main.png",3,0]}, 'Xinha'), false, function(e) { e.execCommand("strikethrough"); } ], + subscript: [ "Subscript", Xinha._lc({key: 'button_subscript', string: ["ed_buttons_main.png",3,1]}, 'Xinha'), false, function(e) { e.execCommand("subscript"); } ], + superscript: [ "Superscript", Xinha._lc({key: 'button_superscript', string: ["ed_buttons_main.png",2,1]}, 'Xinha'), false, function(e) { e.execCommand("superscript"); } ], + + justifyleft: [ "Justify Left", ["ed_buttons_main.png",0,0], false, function(e) { e.execCommand("justifyleft"); } ], + justifycenter: [ "Justify Center", ["ed_buttons_main.png",1,1], false, function(e){ e.execCommand("justifycenter"); } ], + justifyright: [ "Justify Right", ["ed_buttons_main.png",1,0], false, function(e) { e.execCommand("justifyright"); } ], + justifyfull: [ "Justify Full", ["ed_buttons_main.png",0,1], false, function(e) { e.execCommand("justifyfull"); } ], + + orderedlist: [ "Ordered List", ["ed_buttons_main.png",0,3], false, function(e) { e.execCommand("insertorderedlist"); } ], + unorderedlist: [ "Bulleted List", ["ed_buttons_main.png",1,3], false, function(e) { e.execCommand("insertunorderedlist"); } ], + insertorderedlist: [ "Ordered List", ["ed_buttons_main.png",0,3], false, function(e) { e.execCommand("insertorderedlist"); } ], + insertunorderedlist: [ "Bulleted List", ["ed_buttons_main.png",1,3], false, function(e) { e.execCommand("insertunorderedlist"); } ], + + outdent: [ "Decrease Indent", ["ed_buttons_main.png",1,2], false, function(e) { e.execCommand("outdent"); } ], + indent: [ "Increase Indent",["ed_buttons_main.png",0,2], false, function(e) { e.execCommand("indent"); } ], + forecolor: [ "Font Color", ["ed_buttons_main.png",3,3], false, function(e) { e.execCommand("forecolor"); } ], + hilitecolor: [ "Background Color", ["ed_buttons_main.png",2,3], false, function(e) { e.execCommand("hilitecolor"); } ], + + undo: [ "Undoes your last action", ["ed_buttons_main.png",4,2], false, function(e) { e.execCommand("undo"); } ], + redo: [ "Redoes your last action", ["ed_buttons_main.png",5,2], false, function(e) { e.execCommand("redo"); } ], + cut: [ "Cut selection", ["ed_buttons_main.png",5,0], false, function (e, cmd) { e.execCommand(cmd); } ], + copy: [ "Copy selection", ["ed_buttons_main.png",4,0], false, function (e, cmd) { e.execCommand(cmd); } ], + paste: [ "Paste from clipboard", ["ed_buttons_main.png",4,1], false, function (e, cmd) { e.execCommand(cmd); } ], + selectall: [ "Select all", ["ed_buttons_main.png",3,5], false, function(e) {e.execCommand("selectall");} ], + + inserthorizontalrule: [ "Horizontal Rule", ["ed_buttons_main.png",6,0], false, function(e) { e.execCommand("inserthorizontalrule"); } ], + createlink: [ "Insert Web Link", ["ed_buttons_main.png",6,1], false, function(e) { e._createLink(); } ], + insertimage: [ "Insert/Modify Image", ["ed_buttons_main.png",6,3], false, function(e) { e.execCommand("insertimage"); } ], + inserttable: [ "Insert Table", ["ed_buttons_main.png",6,2], false, function(e) { e.execCommand("inserttable"); } ], + + htmlmode: [ "Toggle HTML Source", ["ed_buttons_main.png",7,0], true, function(e) { e.execCommand("htmlmode"); } ], + toggleborders: [ "Toggle Borders", ["ed_buttons_main.png",7,2], false, function(e) { e._toggleBorders(); } ], + print: [ "Print document", ["ed_buttons_main.png",8,1], false, function(e) { if(Xinha.is_gecko) {e._iframe.contentWindow.print(); } else { e.focusEditor(); print(); } } ], + saveas: [ "Save as", ["ed_buttons_main.png",9,1], false, function(e) { e.execCommand("saveas",false,"noname.htm"); } ], + about: [ "About this editor", ["ed_buttons_main.png",8,2], true, function(e) { e.getPluginInstance("AboutBox").show(); } ], + showhelp: [ "Help using editor", ["ed_buttons_main.png",9,2], true, function(e) { e.execCommand("showhelp"); } ], + + splitblock: [ "Split Block", "ed_splitblock.gif", false, function(e) { e._splitBlock(); } ], + lefttoright: [ "Direction left to right", ["ed_buttons_main.png",0,2], false, function(e) { e.execCommand("lefttoright"); } ], + righttoleft: [ "Direction right to left", ["ed_buttons_main.png",1,2], false, function(e) { e.execCommand("righttoleft"); } ], + overwrite: [ "Insert/Overwrite", "ed_overwrite.gif", false, function(e) { e.execCommand("overwrite"); } ], + + wordclean: [ "MS Word Cleaner", ["ed_buttons_main.png",5,3], false, function(e) { e._wordClean(); } ], + clearfonts: [ "Clear Inline Font Specifications", ["ed_buttons_main.png",5,4], true, function(e) { e._clearFonts(); } ], + removeformat: [ "Remove formatting", ["ed_buttons_main.png",4,4], false, function(e) { e.execCommand("removeformat"); } ], + killword: [ "Clear MSOffice tags", ["ed_buttons_main.png",4,3], false, function(e) { e.execCommand("killword"); } ] + }; + + /** A hash of double click handlers for the given elements, each element may have one or more double click handlers + * called in sequence. The element may contain a class selector ( a.somethingSpecial ) + * + */ + + this.dblclickList = + { + "a": [function(e, target) {e._createLink(target);}], + "img": [function(e, target) {e._insertImage(target);}] + }; + + /** A container for additional icons that may be swapped within one button (like fullscreen) + * @private + */ + this.iconList = + { + dialogCaption : _editor_url + 'images/xinha-small-icon.gif', + wysiwygmode : [_editor_url + 'images/ed_buttons_main.png',7,1] + }; + // initialize tooltips from the I18N module and generate correct image path + for ( var i in this.btnList ) + { + var btn = this.btnList[i]; + // prevent iterating over wrong type + if ( typeof btn != 'object' ) + { + continue; + } + if ( typeof btn[1] != 'string' ) + { + btn[1][0] = _editor_url + this.imgURL + btn[1][0]; + } + else + { + btn[1] = _editor_url + this.imgURL + btn[1]; + } + btn[0] = Xinha._lc(btn[0]); //initialize tooltip + } +}; +/** A plugin may require more than one icon for one button, this has to be registered in order to work with the iconsets (see FullScreen) + * + * @param {String} id + * @param {String|Array} icon definition like in registerButton + */ +Xinha.Config.prototype.registerIcon = function (id, icon) +{ + this.iconList[id] = icon; +}; +/** ADDING CUSTOM BUTTONS +* --------------------- +* +* +* Example on how to add a custom button when you construct the Xinha: +* +* var editor = new Xinha("your_text_area_id"); +* var cfg = editor.config; // this is the default configuration +* cfg.btnList["my-hilite"] = +* [ "Highlight selection", // tooltip +* "my_hilite.gif", // image +* false // disabled in text mode +* function(editor) { editor.surroundHTML('', ''); }, // action +* ]; +* cfg.toolbar.push(["linebreak", "my-hilite"]); // add the new button to the toolbar +* +* An alternate (also more convenient and recommended) way to +* accomplish this is to use the registerButton function below. +*/ +/** Helper function: register a new button with the configuration. It can be + * called with all 5 arguments, or with only one (first one). When called with + * only one argument it must be an object with the following properties: id, + * tooltip, image, textMode, action.
+ * + * Examples:
+ *
+ * config.registerButton("my-hilite", "Hilite text", "my-hilite.gif", false, function(editor) {...});
+ * config.registerButton({
+ *      id       : "my-hilite",      // the ID of your button
+ *      tooltip  : "Hilite text",    // the tooltip
+ *      image    : "my-hilite.gif",  // image to be displayed in the toolbar
+ *      textMode : false,            // disabled in text mode
+ *      action   : function(editor) { // called when the button is clicked
+ *                   editor.surroundHTML('', '');
+ *                 },
+ *      context  : "p"               // will be disabled if outside a 

element + * });

+ */ +Xinha.Config.prototype.registerButton = function(id, tooltip, image, textMode, action, context) +{ + if ( typeof id == "string" ) + { + this.btnList[id] = [ tooltip, image, textMode, action, context ]; + } + else if ( typeof id == "object" ) + { + this.btnList[id.id] = [ id.tooltip, id.image, id.textMode, id.action, id.context ]; + } + else + { + alert("ERROR [Xinha.Config::registerButton]:\ninvalid arguments"); + return false; + } +}; + +Xinha.prototype.registerPanel = function(side, object) +{ + if ( !side ) + { + side = 'right'; + } + this.setLoadingMessage('Register ' + side + ' panel '); + var panel = this.addPanel(side); + if ( object ) + { + object.drawPanelIn(panel); + } +}; + +/** The following helper function registers a dropdown box with the editor + * configuration. You still have to add it to the toolbar, same as with the + * buttons. Call it like this: + * + * FIXME: add example + */ +Xinha.Config.prototype.registerDropdown = function(object) +{ + // check for existing id +// if ( typeof this.customSelects[object.id] != "undefined" ) +// { + // alert("WARNING [Xinha.Config::registerDropdown]:\nA dropdown with the same ID already exists."); +// } +// if ( typeof this.btnList[object.id] != "undefined" ) +// { + // alert("WARNING [Xinha.Config::registerDropdown]:\nA button with the same ID already exists."); +// } + this.customSelects[object.id] = object; +}; + +/** Call this function to remove some buttons/drop-down boxes from the toolbar. + * Pass as the only parameter a string containing button/drop-down names + * delimited by spaces. Note that the string should also begin with a space + * and end with a space. Example: + * + * config.hideSomeButtons(" fontname fontsize textindicator "); + * + * It's useful because it's easier to remove stuff from the defaul toolbar than + * create a brand new toolbar ;-) + */ +Xinha.Config.prototype.hideSomeButtons = function(remove) +{ + var toolbar = this.toolbar; + for ( var i = toolbar.length; --i >= 0; ) + { + var line = toolbar[i]; + for ( var j = line.length; --j >= 0; ) + { + if ( remove.indexOf(" " + line[j] + " ") >= 0 ) + { + var len = 1; + if ( /separator|space/.test(line[j + 1]) ) + { + len = 2; + } + line.splice(j, len); + } + } + } +}; + +/** Helper Function: add buttons/drop-downs boxes with title or separator to the toolbar + * if the buttons/drop-downs boxes doesn't allready exists. + * id: button or selectbox (as array with separator or title) + * where: button or selectbox (as array if the first is not found take the second and so on) + * position: + * -1 = insert button (id) one position before the button (where) + * 0 = replace button (where) by button (id) + * +1 = insert button (id) one position after button (where) + * + * cfg.addToolbarElement(["T[title]", "button_id", "separator"] , ["first_id","second_id"], -1); +*/ + +Xinha.Config.prototype.addToolbarElement = function(id, where, position) +{ + var toolbar = this.toolbar; + var a, i, j, o, sid; + var idIsArray = false; + var whereIsArray = false; + var whereLength = 0; + var whereJ = 0; + var whereI = 0; + var exists = false; + var found = false; + // check if id and where are arrys + if ( ( id && typeof id == "object" ) && ( id.constructor == Array ) ) + { + idIsArray = true; + } + if ( ( where && typeof where == "object" ) && ( where.constructor == Array ) ) + { + whereIsArray = true; + whereLength = where.length; + } + + if ( idIsArray ) //find the button/select box in input array + { + for ( i = 0; i < id.length; ++i ) + { + if ( ( id[i] != "separator" ) && ( id[i].indexOf("T[") !== 0) ) + { + sid = id[i]; + } + } + } + else + { + sid = id; + } + + for ( i = 0; i < toolbar.length; ++i ) { + a = toolbar[i]; + for ( j = 0; j < a.length; ++j ) { + // check if button/select box exists + if ( a[j] == sid ) { + return; // cancel to add elements if same button already exists + } + } + } + + + for ( i = 0; !found && i < toolbar.length; ++i ) + { + a = toolbar[i]; + for ( j = 0; !found && j < a.length; ++j ) + { + if ( whereIsArray ) + { + for ( o = 0; o < whereLength; ++o ) + { + if ( a[j] == where[o] ) + { + if ( o === 0 ) + { + found = true; + j--; + break; + } + else + { + whereI = i; + whereJ = j; + whereLength = o; + } + } + } + } + else + { + // find the position to insert + if ( a[j] == where ) + { + found = true; + break; + } + } + } + } + + //if check found any other as the first button + if ( !found && whereIsArray ) + { + if ( where.length != whereLength ) + { + j = whereJ; + a = toolbar[whereI]; + found = true; + } + } + if ( found ) + { + // replace the found button + if ( position === 0 ) + { + if ( idIsArray) + { + a[j] = id[id.length-1]; + for ( i = id.length-1; --i >= 0; ) + { + a.splice(j, 0, id[i]); + } + } + else + { + a[j] = id; + } + } + else + { + // insert before/after the found button + if ( position < 0 ) + { + j = j + position + 1; //correct position before + } + else if ( position > 0 ) + { + j = j + position; //correct posion after + } + if ( idIsArray ) + { + for ( i = id.length; --i >= 0; ) + { + a.splice(j, 0, id[i]); + } + } + else + { + a.splice(j, 0, id); + } + } + } + else + { + // no button found + toolbar[0].splice(0, 0, "separator"); + if ( idIsArray) + { + for ( i = id.length; --i >= 0; ) + { + toolbar[0].splice(0, 0, id[i]); + } + } + else + { + toolbar[0].splice(0, 0, id); + } + } +}; +/** Alias of Xinha.Config.prototype.hideSomeButtons() +* @type Function +*/ +Xinha.Config.prototype.removeToolbarElement = Xinha.Config.prototype.hideSomeButtons; + +/** Helper function: replace all TEXTAREA-s in the document with Xinha-s. +* @param {Xinha.Config} optional config +*/ +Xinha.replaceAll = function(config) +{ + var tas = document.getElementsByTagName("textarea"); + // @todo: weird syntax, doesnt help to read the code, doesnt obfuscate it and doesnt make it quicker, better rewrite this part + for ( var i = tas.length; i > 0; new Xinha(tas[--i], config).generate() ) + { + // NOP + } +}; + +/** Helper function: replaces the TEXTAREA with the given ID with Xinha. +* @param {string} id id of the textarea to replace +* @param {Xinha.Config} optional config +*/ +Xinha.replace = function(id, config) +{ + var ta = Xinha.getElementById("textarea", id); + return ta ? new Xinha(ta, config).generate() : null; +}; + +/** Creates the toolbar and appends it to the _htmlarea +* @private +* @returns {DomNode} toolbar +*/ +Xinha.prototype._createToolbar = function () +{ + this.setLoadingMessage(Xinha._lc('Create Toolbar')); + var editor = this; // to access this in nested functions + + var toolbar = document.createElement("div"); + // ._toolbar is for legacy, ._toolBar is better thanks. + this._toolBar = this._toolbar = toolbar; + toolbar.className = "toolbar"; + toolbar.align = this.config.toolbarAlign; + + Xinha.freeLater(this, '_toolBar'); + Xinha.freeLater(this, '_toolbar'); + + var tb_row = null; + var tb_objects = {}; + this._toolbarObjects = tb_objects; + + this._createToolbar1(editor, toolbar, tb_objects); + + // IE8 is totally retarded, if you click on a toolbar element (eg button) + // and it doesn't have unselectable="on", then it defocuses the editor losing the selection + // so nothing works. Particularly prevalent with TableOperations + function noselect(e) + { + if(e.tagName) e.unselectable = "on"; + if(e.childNodes) + { + for(var i = 0; i < e.childNodes.length; i++) if(e.tagName) noselect(e.childNodes(i)); + } + } + if(Xinha.is_ie) noselect(toolbar); + + + this._htmlArea.appendChild(toolbar); + + return toolbar; +}; + +/** FIXME : function never used, can probably be removed from source +* @private +* @deprecated +*/ +Xinha.prototype._setConfig = function(config) +{ + this.config = config; +}; +/** FIXME: How can this be used?? +* @private +*/ +Xinha.prototype._rebuildToolbar = function() +{ + this._createToolbar1(this, this._toolbar, this._toolbarObjects); + + // We only want ONE editor at a time to be active + if ( Xinha._currentlyActiveEditor ) + { + if ( Xinha._currentlyActiveEditor == this ) + { + this.activateEditor(); + } + } + else + { + this.disableToolbar(); + } +}; + +/** + * Create a break element to add in the toolbar + * + * @return {DomNode} HTML element to add + * @private + */ +Xinha._createToolbarBreakingElement = function() +{ + var brk = document.createElement('div'); + brk.style.height = '1px'; + brk.style.width = '1px'; + brk.style.lineHeight = '1px'; + brk.style.fontSize = '1px'; + brk.style.clear = 'both'; + return brk; +}; + + +/** separate from previous createToolBar to allow dynamic change of toolbar + * @private + * @return {DomNode} toolbar + */ +Xinha.prototype._createToolbar1 = function (editor, toolbar, tb_objects) +{ + // We will clean out any existing toolbar elements. + while (toolbar.lastChild) + { + toolbar.removeChild(toolbar.lastChild); + } + + var tb_row; + // This shouldn't be necessary, but IE seems to float outside of the container + // when we float toolbar sections, so we have to clear:both here as well + // as at the end (which we do have to do). + if ( editor.config.flowToolbars ) + { + toolbar.appendChild(Xinha._createToolbarBreakingElement()); + } + + // creates a new line in the toolbar + function newLine() + { + if ( typeof tb_row != 'undefined' && tb_row.childNodes.length === 0) + { + return; + } + + var table = document.createElement("table"); + table.border = "0px"; + table.cellSpacing = "0px"; + table.cellPadding = "0px"; + if ( editor.config.flowToolbars ) + { + if ( Xinha.is_ie ) + { + table.style.styleFloat = "left"; + } + else + { + table.style.cssFloat = "left"; + } + } + + toolbar.appendChild(table); + // TBODY is required for IE, otherwise you don't see anything + // in the TABLE. + var tb_body = document.createElement("tbody"); + table.appendChild(tb_body); + tb_row = document.createElement("tr"); + tb_body.appendChild(tb_row); + + table.className = 'toolbarRow'; // meh, kinda. + } // END of function: newLine + + // init first line + newLine(); + + // updates the state of a toolbar element. This function is member of + // a toolbar element object (unnamed objects created by createButton or + // createSelect functions below). + function setButtonStatus(id, newval) + { + var oldval = this[id]; + var el = this.element; + if ( oldval != newval ) + { + switch (id) + { + case "enabled": + if ( newval ) + { + Xinha._removeClass(el, "buttonDisabled"); + el.disabled = false; + } + else + { + Xinha._addClass(el, "buttonDisabled"); + el.disabled = true; + } + break; + case "active": + if ( newval ) + { + Xinha._addClass(el, "buttonPressed"); + } + else + { + Xinha._removeClass(el, "buttonPressed"); + } + break; + } + this[id] = newval; + } + } // END of function: setButtonStatus + + // this function will handle creation of combo boxes. Receives as + // parameter the name of a button as defined in the toolBar config. + // This function is called from createButton, above, if the given "txt" + // doesn't match a button. + function createSelect(txt) + { + var options = null; + var el = null; + var cmd = null; + var customSelects = editor.config.customSelects; + var context = null; + var tooltip = ""; + switch (txt) + { + case "fontsize": + case "fontname": + case "formatblock": + // the following line retrieves the correct + // configuration option because the variable name + // inside the Config object is named the same as the + // button/select in the toolbar. For instance, if txt + // == "formatblock" we retrieve config.formatblock (or + // a different way to write it in JS is + // config["formatblock"]. + options = editor.config[txt]; + cmd = txt; + break; + default: + // try to fetch it from the list of registered selects + cmd = txt; + var dropdown = customSelects[cmd]; + if ( typeof dropdown != "undefined" ) + { + options = dropdown.options; + context = dropdown.context; + if ( typeof dropdown.tooltip != "undefined" ) + { + tooltip = dropdown.tooltip; + } + } + else + { + alert("ERROR [createSelect]:\nCan't find the requested dropdown definition"); + } + break; + } + if ( options ) + { + el = document.createElement("select"); + el.title = tooltip; + el.style.width = 'auto'; + el.name = txt; + var obj = + { + name : txt, // field name + element : el, // the UI element (SELECT) + enabled : true, // is it enabled? + text : false, // enabled in text mode? + cmd : cmd, // command ID + state : setButtonStatus, // for changing state + context : context + }; + + Xinha.freeLater(obj); + + tb_objects[txt] = obj; + + for ( var i in options ) + { + // prevent iterating over wrong type + if ( typeof options[i] != 'string' ) + { + continue; + } + var op = document.createElement("option"); + op.innerHTML = Xinha._lc(i); + op.value = options[i]; + if (txt =='fontname' && editor.config.showFontStylesInToolbar) + { + op.style.fontFamily = options[i]; + } + el.appendChild(op); + } + Xinha._addEvent(el, "change", function () { editor._comboSelected(el, txt); } ); + } + return el; + } // END of function: createSelect + + // appends a new button to toolbar + function createButton(txt) + { + // the element that will be created + var el, btn, obj = null; + switch (txt) + { + case "separator": + if ( editor.config.flowToolbars ) + { + newLine(); + } + el = document.createElement("div"); + el.className = "separator"; + break; + case "space": + el = document.createElement("div"); + el.className = "space"; + break; + case "linebreak": + newLine(); + return false; + case "textindicator": + el = document.createElement("div"); + el.appendChild(document.createTextNode("A")); + el.className = "indicator"; + el.title = Xinha._lc("Current style"); + obj = + { + name : txt, // the button name (i.e. 'bold') + element : el, // the UI element (DIV) + enabled : true, // is it enabled? + active : false, // is it pressed? + text : false, // enabled in text mode? + cmd : "textindicator", // the command ID + state : setButtonStatus // for changing state + }; + + Xinha.freeLater(obj); + + tb_objects[txt] = obj; + break; + default: + btn = editor.config.btnList[txt]; + } + if ( !el && btn ) + { + el = document.createElement("a"); + el.style.display = 'block'; + el.href = 'javascript:void(0)'; + el.style.textDecoration = 'none'; + el.title = btn[0]; + el.className = "button"; + el.style.direction = "ltr"; + // let's just pretend we have a button object, and + // assign all the needed information to it. + obj = + { + name : txt, // the button name (i.e. 'bold') + element : el, // the UI element (DIV) + enabled : true, // is it enabled? + active : false, // is it pressed? + text : btn[2], // enabled in text mode? + cmd : btn[3], // the command ID + state : setButtonStatus, // for changing state + context : btn[4] || null // enabled in a certain context? + }; + Xinha.freeLater(el); + Xinha.freeLater(obj); + + tb_objects[txt] = obj; + + // prevent drag&drop of the icon to content area + el.ondrag = function() { return false; }; + + // handlers to emulate nice flat toolbar buttons + Xinha._addEvent( + el, + "mouseout", + function(ev) + { + if ( obj.enabled ) + { + //Xinha._removeClass(el, "buttonHover"); + Xinha._removeClass(el, "buttonActive"); + if ( obj.active ) + { + Xinha._addClass(el, "buttonPressed"); + } + } + } + ); + + Xinha._addEvent( + el, + "mousedown", + function(ev) + { + if ( obj.enabled ) + { + Xinha._addClass(el, "buttonActive"); + Xinha._removeClass(el, "buttonPressed"); + Xinha._stopEvent(Xinha.is_ie ? window.event : ev); + } + } + ); + + // when clicked, do the following: + Xinha._addEvent( + el, + "click", + function(ev) + { + ev = ev || window.event; + editor.btnClickEvent = {clientX : ev.clientX, clientY : ev.clientY}; + if ( obj.enabled ) + { + Xinha._removeClass(el, "buttonActive"); + //Xinha._removeClass(el, "buttonHover"); + if ( Xinha.is_gecko ) + { + editor.activateEditor(); + } + // We pass the event to the action so they can can use it to + // enhance the UI (e.g. respond to shift or ctrl-click) + obj.cmd(editor, obj.name, obj, ev); + Xinha._stopEvent(ev); + } + } + ); + + var i_contain = Xinha.makeBtnImg(btn[1]); + var img = i_contain.firstChild; + Xinha.freeLater(i_contain); + Xinha.freeLater(img); + + el.appendChild(i_contain); + + obj.imgel = img; + obj.swapImage = function(newimg) + { + if ( typeof newimg != 'string' ) + { + img.src = newimg[0]; + img.style.position = 'relative'; + img.style.top = newimg[2] ? ('-' + (18 * (newimg[2] + 1)) + 'px') : '-18px'; + img.style.left = newimg[1] ? ('-' + (18 * (newimg[1] + 1)) + 'px') : '-18px'; + } + else + { + obj.imgel.src = newimg; + img.style.top = '0px'; + img.style.left = '0px'; + } + }; + + } + else if( !el ) + { + el = createSelect(txt); + } + + return el; + } + + var first = true; + for ( var i = 0; i < this.config.toolbar.length; ++i ) + { + if ( !first ) + { + // createButton("linebreak"); + } + else + { + first = false; + } + if ( this.config.toolbar[i] === null ) + { + this.config.toolbar[i] = ['separator']; + } + var group = this.config.toolbar[i]; + + for ( var j = 0; j < group.length; ++j ) + { + var code = group[j]; + var tb_cell; + if ( /^([IT])\[(.*?)\]/.test(code) ) + { + // special case, create text label + var l7ed = RegExp.$1 == "I"; // localized? + var label = RegExp.$2; + if ( l7ed ) + { + label = Xinha._lc(label); + } + tb_cell = document.createElement("td"); + tb_row.appendChild(tb_cell); + tb_cell.className = "label"; + tb_cell.innerHTML = label; + } + else if ( typeof code != 'function' ) + { + var tb_element = createButton(code); + if ( tb_element ) + { + tb_cell = document.createElement("td"); + tb_cell.className = 'toolbarElement'; + tb_row.appendChild(tb_cell); + tb_cell.appendChild(tb_element); + } + else if ( tb_element === null ) + { + alert("FIXME: Unknown toolbar item: " + code); + } + } + } + } + + if ( editor.config.flowToolbars ) + { + toolbar.appendChild(Xinha._createToolbarBreakingElement()); + } + + return toolbar; +}; + +/** creates a button (i.e. container element + image) + * @private + * @return {DomNode} conteainer element + */ +Xinha.makeBtnImg = function(imgDef, doc) +{ + if ( !doc ) + { + doc = document; + } + + if ( !doc._xinhaImgCache ) + { + doc._xinhaImgCache = {}; + Xinha.freeLater(doc._xinhaImgCache); + } + + var i_contain = null; + if ( Xinha.is_ie && ( ( !doc.compatMode ) || ( doc.compatMode && doc.compatMode == "BackCompat" ) ) ) + { + i_contain = doc.createElement('span'); + } + else + { + i_contain = doc.createElement('div'); + i_contain.style.position = 'relative'; + } + + i_contain.style.overflow = 'hidden'; + i_contain.style.width = "18px"; + i_contain.style.height = "18px"; + i_contain.className = 'buttonImageContainer'; + + var img = null; + if ( typeof imgDef == 'string' ) + { + if ( doc._xinhaImgCache[imgDef] ) + { + img = doc._xinhaImgCache[imgDef].cloneNode(); + } + else + { + if (Xinha.ie_version < 7 && /\.png$/.test(imgDef[0])) + { + img = doc.createElement("span"); + + img.style.display = 'block'; + img.style.width = '18px'; + img.style.height = '18px'; + img.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="'+imgDef+'")'; + img.unselectable = 'on'; + } + else + { + img = doc.createElement("img"); + img.src = imgDef; + } + } + } + else + { + if ( doc._xinhaImgCache[imgDef[0]] ) + { + img = doc._xinhaImgCache[imgDef[0]].cloneNode(); + } + else + { + if (Xinha.ie_version < 7 && /\.png$/.test(imgDef[0])) + { + img = doc.createElement("span"); + img.style.display = 'block'; + img.style.width = '18px'; + img.style.height = '18px'; + img.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="'+imgDef[0]+'")'; + img.unselectable = 'on'; + } + else + { + img = doc.createElement("img"); + img.src = imgDef[0]; + } + img.style.position = 'relative'; + } + // @todo: Using 18 dont let us use a theme with its own icon toolbar height + // and width. Probably better to calculate this value 18 + // var sizeIcon = img.width / nb_elements_per_image; + img.style.top = imgDef[2] ? ('-' + (18 * (imgDef[2] + 1)) + 'px') : '-18px'; + img.style.left = imgDef[1] ? ('-' + (18 * (imgDef[1] + 1)) + 'px') : '-18px'; + } + i_contain.appendChild(img); + return i_contain; +}; +/** creates the status bar + * @private + * @return {DomNode} status bar + */ +Xinha.prototype._createStatusBar = function() +{ + // TODO: Move styling into separate stylesheet + this.setLoadingMessage(Xinha._lc('Create Statusbar')); + var statusBar = document.createElement("div"); + statusBar.style.position = "relative"; + statusBar.className = "statusBar"; + statusBar.style.width = "100%"; + Xinha.freeLater(this, '_statusBar'); + + var widgetContainer = document.createElement("div"); + widgetContainer.className = "statusBarWidgetContainer"; + widgetContainer.style.position = "absolute"; + widgetContainer.style.right = "0"; + widgetContainer.style.top = "0"; + widgetContainer.style.padding = "3px 3px 3px 10px"; + statusBar.appendChild(widgetContainer); + + // statusbar.appendChild(document.createTextNode(Xinha._lc("Path") + ": ")); + // creates a holder for the path view + var statusBarTree = document.createElement("span"); + statusBarTree.className = "statusBarTree"; + statusBarTree.innerHTML = Xinha._lc("Path") + ": "; + + this._statusBarTree = statusBarTree; + Xinha.freeLater(this, '_statusBarTree'); + statusBar.appendChild(statusBarTree); + var statusBarTextMode = document.createElement("span"); + statusBarTextMode.innerHTML = Xinha.htmlEncode(Xinha._lc("You are in TEXT MODE. Use the [<>] button to switch back to WYSIWYG.")); + statusBarTextMode.style.display = "none"; + + this._statusBarTextMode = statusBarTextMode; + Xinha.freeLater(this, '_statusBarTextMode'); + statusBar.appendChild(statusBarTextMode); + + statusBar.style.whiteSpace = "nowrap"; + + var self = this; + this.notifyOn("before_resize", function(evt, size) { + self._statusBar.style.width = null; + }); + this.notifyOn("resize", function(evt, size) { + // HACK! IE6 doesn't update the width properly when resizing if it's + // given in pixels, but does hide the overflow content correctly when + // using 100% as the width. (FF, Safari and IE7 all require fixed + // pixel widths to do the overflow hiding correctly.) + if (Xinha.is_ie && Xinha.ie_version == 6) + { + self._statusBar.style.width = "100%"; + } + else + { + var width = size['width']; + self._statusBar.style.width = width + "px"; + } + }); + + this.notifyOn("modechange", function(evt, mode) { + // Loop through all registered status bar items + // and show them only if they're turned on for + // the new mode. + for (var i in self._statusWidgets) + { + var widget = self._statusWidgets[i]; + for (var index=0; index= 0; ) + { + for ( var j = toolbar[i].length; --j >= 0; ) + { + switch (toolbar[i][j]) + { + case "popupeditor": + if (!this.plugins.FullScreen) + { + editor.registerPlugin('FullScreen'); + } + break; + case "insertimage": + url = _editor_url + 'modules/InsertImage/insert_image.js'; + if ( typeof Xinha.prototype._insertImage == 'undefined' && !Xinha.loadPlugins([{plugin:"InsertImage",url:url}], callback ) ) + { + return false; + } + else if ( typeof Xinha.getPluginConstructor('InsertImage') != 'undefined' && !this.plugins.InsertImage) + { + editor.registerPlugin('InsertImage'); + } + break; + case "createlink": + url = _editor_url + 'modules/CreateLink/link.js'; + if ( typeof Xinha.getPluginConstructor('Linker') == 'undefined' && !Xinha.loadPlugins([{plugin:"CreateLink",url:url}], callback )) + { + return false; + } + else if ( typeof Xinha.getPluginConstructor('CreateLink') != 'undefined' && !this.plugins.CreateLink) + { + editor.registerPlugin('CreateLink'); + } + break; + case "inserttable": + url = _editor_url + 'modules/InsertTable/insert_table.js'; + if ( !Xinha.loadPlugins([{plugin:"InsertTable",url:url}], callback ) ) + { + return false; + } + else if ( typeof Xinha.getPluginConstructor('InsertTable') != 'undefined' && !this.plugins.InsertTable) + { + editor.registerPlugin('InsertTable'); + } + break; + case "about": + url = _editor_url + 'modules/AboutBox/AboutBox.js'; + if ( !Xinha.loadPlugins([{plugin:"AboutBox",url:url}], callback ) ) + { + return false; + } + else if ( typeof Xinha.getPluginConstructor('AboutBox') != 'undefined' && !this.plugins.AboutBox) + { + editor.registerPlugin('AboutBox'); + } + break; + } + } + } + + // If this is gecko, set up the paragraph handling now + if ( Xinha.is_gecko && editor.config.mozParaHandler != 'built-in' ) + { + if ( !Xinha.loadPlugins([{plugin:"EnterParagraphs",url: _editor_url + 'modules/Gecko/paraHandlerBest.js'}], callback ) ) + { + return false; + } + if (!this.plugins.EnterParagraphs) + { + editor.registerPlugin('EnterParagraphs'); + } + } + var getHtmlMethodPlugin = this.config.getHtmlMethod == 'TransformInnerHTML' ? _editor_url + 'modules/GetHtml/TransformInnerHTML.js' : _editor_url + 'modules/GetHtml/DOMwalk.js'; + + if ( !Xinha.loadPlugins([{plugin:"GetHtmlImplementation",url:getHtmlMethodPlugin}], callback)) + { + return false; + } + else if (!this.plugins.GetHtmlImplementation) + { + editor.registerPlugin('GetHtmlImplementation'); + } + function getTextContent(node) + { + return node.textContent || node.text; + } + if (_editor_skin) + { + this.skinInfo = {}; + var skinXML = Xinha._geturlcontent(_editor_url + 'skins/' + _editor_skin + '/skin.xml', true); + if (skinXML) + { + var meta = skinXML.getElementsByTagName('meta'); + for (i=0;i = the width is an explicit size (any CSS measurement, eg 100em should be fine) + * + * config.height + * auto = the height is inherited from the original textarea + * = an explicit size measurement (again, CSS measurements) + * + * config.sizeIncludesBars + * true = the tool & status bars will appear inside the width & height confines + * false = the tool & status bars will appear outside the width & height confines + * + * @private + */ + +Xinha.prototype.initSize = function() +{ + this.setLoadingMessage(Xinha._lc('Init editor size')); + var editor = this; + var width = null; + var height = null; + + switch ( this.config.width ) + { + case 'auto': + width = this._initial_ta_size.w; + break; + + case 'toolbar': + width = this._toolBar.offsetWidth + 'px'; + break; + + default : + // @todo: check if this is better : + // width = (parseInt(this.config.width, 10) == this.config.width)? this.config.width + 'px' : this.config.width; + width = /[^0-9]/.test(this.config.width) ? this.config.width : this.config.width + 'px'; + break; + } + // @todo: check if this is better : + // height = (parseInt(this.config.height, 10) == this.config.height)? this.config.height + 'px' : this.config.height; + height = this.config.height == 'auto' ? this._initial_ta_size.h : /[^0-9]/.test(this.config.height) ? this.config.height : this.config.height + 'px'; + + this.sizeEditor(width, height, this.config.sizeIncludesBars, this.config.sizeIncludesPanels); + + // why can't we use the following line instead ? +// this.notifyOn('panel_change',this.sizeEditor); + this.notifyOn('panel_change',function() { editor.sizeEditor(); }); +}; + +/** + * Size the editor to a specific size, or just refresh the size (when window resizes for example) + * @param {string} width optional width (CSS specification) + * @param {string} height optional height (CSS specification) + * @param {Boolean} includingBars optional to indicate if the size should include or exclude tool & status bars + * @param {Boolean} includingPanels optional to indicate if the size should include or exclude panels + */ +Xinha.prototype.sizeEditor = function(width, height, includingBars, includingPanels) +{ + if (this._risizing) + { + return; + } + this._risizing = true; + + var framework = this._framework; + + this.notifyOf('before_resize', {width:width, height:height}); + this.firePluginEvent('onBeforeResize', width, height); + // We need to set the iframe & textarea to 100% height so that the htmlarea + // isn't "pushed out" when we get it's height, so we can change them later. + this._iframe.style.height = '100%'; + //here 100% can lead to an effect that the editor is considerably higher in text mode + this._textArea.style.height = '1px'; + + this._iframe.style.width = '0px'; + this._textArea.style.width = '0px'; + + if ( includingBars !== null ) + { + this._htmlArea.sizeIncludesToolbars = includingBars; + } + if ( includingPanels !== null ) + { + this._htmlArea.sizeIncludesPanels = includingPanels; + } + + if ( width ) + { + this._htmlArea.style.width = width; + if ( !this._htmlArea.sizeIncludesPanels ) + { + // Need to add some for l & r panels + var rPanel = this._panels.right; + if ( rPanel.on && rPanel.panels.length && Xinha.hasDisplayedChildren(rPanel.div) ) + { + this._htmlArea.style.width = (this._htmlArea.offsetWidth + parseInt(this.config.panel_dimensions.right, 10)) + 'px'; + } + + var lPanel = this._panels.left; + if ( lPanel.on && lPanel.panels.length && Xinha.hasDisplayedChildren(lPanel.div) ) + { + this._htmlArea.style.width = (this._htmlArea.offsetWidth + parseInt(this.config.panel_dimensions.left, 10)) + 'px'; + } + } + } + + if ( height ) + { + this._htmlArea.style.height = height; + if ( !this._htmlArea.sizeIncludesToolbars ) + { + // Need to add some for toolbars + this._htmlArea.style.height = (this._htmlArea.offsetHeight + this._toolbar.offsetHeight + this._statusBar.offsetHeight) + 'px'; + } + + if ( !this._htmlArea.sizeIncludesPanels ) + { + // Need to add some for t & b panels + var tPanel = this._panels.top; + if ( tPanel.on && tPanel.panels.length && Xinha.hasDisplayedChildren(tPanel.div) ) + { + this._htmlArea.style.height = (this._htmlArea.offsetHeight + parseInt(this.config.panel_dimensions.top, 10)) + 'px'; + } + + var bPanel = this._panels.bottom; + if ( bPanel.on && bPanel.panels.length && Xinha.hasDisplayedChildren(bPanel.div) ) + { + this._htmlArea.style.height = (this._htmlArea.offsetHeight + parseInt(this.config.panel_dimensions.bottom, 10)) + 'px'; + } + } + } + + // At this point we have this._htmlArea.style.width & this._htmlArea.style.height + // which are the size for the OUTER editor area, including toolbars and panels + // now we size the INNER area and position stuff in the right places. + width = this._htmlArea.offsetWidth; + height = this._htmlArea.offsetHeight; + + // Set colspan for toolbar, and statusbar, rowspan for left & right panels, and insert panels to be displayed + // into thier rows + var panels = this._panels; + var editor = this; + var col_span = 1; + + function panel_is_alive(pan) + { + if ( panels[pan].on && panels[pan].panels.length && Xinha.hasDisplayedChildren(panels[pan].container) ) + { + panels[pan].container.style.display = ''; + return true; + } + // Otherwise make sure it's been removed from the framework + else + { + panels[pan].container.style.display='none'; + return false; + } + } + + if ( panel_is_alive('left') ) + { + col_span += 1; + } + +// if ( panel_is_alive('top') ) +// { + // NOP +// } + + if ( panel_is_alive('right') ) + { + col_span += 1; + } + +// if ( panel_is_alive('bottom') ) +// { + // NOP +// } + + framework.tb_cell.colSpan = col_span; + framework.tp_cell.colSpan = col_span; + framework.bp_cell.colSpan = col_span; + framework.sb_cell.colSpan = col_span; + + // Put in the panel rows, top panel goes above editor row + if ( !framework.tp_row.childNodes.length ) + { + Xinha.removeFromParent(framework.tp_row); + } + else + { + if ( !Xinha.hasParentNode(framework.tp_row) ) + { + framework.tbody.insertBefore(framework.tp_row, framework.ler_row); + } + } + + // bp goes after the editor + if ( !framework.bp_row.childNodes.length ) + { + Xinha.removeFromParent(framework.bp_row); + } + else + { + if ( !Xinha.hasParentNode(framework.bp_row) ) + { + framework.tbody.insertBefore(framework.bp_row, framework.ler_row.nextSibling); + } + } + + // finally if the statusbar is on, insert it + if ( !this.config.statusBar ) + { + Xinha.removeFromParent(framework.sb_row); + } + else + { + if ( !Xinha.hasParentNode(framework.sb_row) ) + { + framework.table.appendChild(framework.sb_row); + } + } + + // Size and set colspans, link up the framework + framework.lp_cell.style.width = this.config.panel_dimensions.left; + framework.rp_cell.style.width = this.config.panel_dimensions.right; + framework.tp_cell.style.height = this.config.panel_dimensions.top; + framework.bp_cell.style.height = this.config.panel_dimensions.bottom; + framework.tb_cell.style.height = this._toolBar.offsetHeight + 'px'; + framework.sb_cell.style.height = this._statusBar.offsetHeight + 'px'; + + var edcellheight = height - this._toolBar.offsetHeight - this._statusBar.offsetHeight; + if ( panel_is_alive('top') ) + { + edcellheight -= parseInt(this.config.panel_dimensions.top, 10); + } + if ( panel_is_alive('bottom') ) + { + edcellheight -= parseInt(this.config.panel_dimensions.bottom, 10); + } + this._iframe.style.height = edcellheight + 'px'; + + var edcellwidth = width; + if ( panel_is_alive('left') ) + { + edcellwidth -= parseInt(this.config.panel_dimensions.left, 10); + } + if ( panel_is_alive('right') ) + { + edcellwidth -= parseInt(this.config.panel_dimensions.right, 10); + } + var iframeWidth = this.config.iframeWidth ? parseInt(this.config.iframeWidth,10) : null; + this._iframe.style.width = (iframeWidth && iframeWidth < edcellwidth) ? iframeWidth + "px": edcellwidth + "px"; + + this._textArea.style.height = this._iframe.style.height; + this._textArea.style.width = this._iframe.style.width; + + this.notifyOf('resize', {width:this._htmlArea.offsetWidth, height:this._htmlArea.offsetHeight}); + this.firePluginEvent('onResize',this._htmlArea.offsetWidth, this._htmlArea.offsetWidth); + this._risizing = false; +}; +/** FIXME: Never used, what is this for? +* @param {string} side +* @param {Object} +*/ +Xinha.prototype.registerPanel = function(side, object) +{ + if ( !side ) + { + side = 'right'; + } + this.setLoadingMessage('Register ' + side + ' panel '); + var panel = this.addPanel(side); + if ( object ) + { + object.drawPanelIn(panel); + } +}; +/** Creates a panel in the panel container on the specified side +* @param {String} side the panel container to which the new panel will be added
+* Possible values are: "right","left","top","bottom" +* @returns {DomNode} Panel div +*/ +Xinha.prototype.addPanel = function(side) +{ + var div = document.createElement('div'); + div.side = side; + if ( side == 'left' || side == 'right' ) + { + div.style.width = this.config.panel_dimensions[side]; + if (this._iframe) + { + div.style.height = this._iframe.style.height; + } + } + Xinha.addClasses(div, 'panel'); + this._panels[side].panels.push(div); + this._panels[side].div.appendChild(div); + + this.notifyOf('panel_change', {'action':'add','panel':div}); + this.firePluginEvent('onPanelChange','add',div); + return div; +}; +/** Removes a panel +* @param {DomNode} panel object as returned by Xinha.prototype.addPanel() +*/ +Xinha.prototype.removePanel = function(panel) +{ + this._panels[panel.side].div.removeChild(panel); + var clean = []; + for ( var i = 0; i < this._panels[panel.side].panels.length; i++ ) + { + if ( this._panels[panel.side].panels[i] != panel ) + { + clean.push(this._panels[panel.side].panels[i]); + } + } + this._panels[panel.side].panels = clean; + this.notifyOf('panel_change', {'action':'remove','panel':panel}); + this.firePluginEvent('onPanelChange','remove',panel); +}; +/** Hides a panel +* @param {DomNode} panel object as returned by Xinha.prototype.addPanel() +*/ +Xinha.prototype.hidePanel = function(panel) +{ + if ( panel && panel.style.display != 'none' ) + { + try { var pos = this.scrollPos(this._iframe.contentWindow); } catch(e) { } + panel.style.display = 'none'; + this.notifyOf('panel_change', {'action':'hide','panel':panel}); + this.firePluginEvent('onPanelChange','hide',panel); + try { this._iframe.contentWindow.scrollTo(pos.x,pos.y); } catch(e) { } + } +}; +/** Shows a panel +* @param {DomNode} panel object as returned by Xinha.prototype.addPanel() +*/ +Xinha.prototype.showPanel = function(panel) +{ + if ( panel && panel.style.display == 'none' ) + { + try { var pos = this.scrollPos(this._iframe.contentWindow); } catch(e) {} + panel.style.display = ''; + this.notifyOf('panel_change', {'action':'show','panel':panel}); + this.firePluginEvent('onPanelChange','show',panel); + try { this._iframe.contentWindow.scrollTo(pos.x,pos.y); } catch(e) { } + } +}; +/** Hides the panel(s) on one or more sides +* @param {Array} sides the sides on which the panels shall be hidden +*/ +Xinha.prototype.hidePanels = function(sides) +{ + if ( typeof sides == 'undefined' ) + { + sides = ['left','right','top','bottom']; + } + + var reShow = []; + for ( var i = 0; i < sides.length;i++ ) + { + if ( this._panels[sides[i]].on ) + { + reShow.push(sides[i]); + this._panels[sides[i]].on = false; + } + } + this.notifyOf('panel_change', {'action':'multi_hide','sides':sides}); + this.firePluginEvent('onPanelChange','multi_hide',sides); +}; +/** Shows the panel(s) on one or more sides +* @param {Array} sides the sides on which the panels shall be hidden +*/ +Xinha.prototype.showPanels = function(sides) +{ + if ( typeof sides == 'undefined' ) + { + sides = ['left','right','top','bottom']; + } + + var reHide = []; + for ( var i = 0; i < sides.length; i++ ) + { + if ( !this._panels[sides[i]].on ) + { + reHide.push(sides[i]); + this._panels[sides[i]].on = true; + } + } + this.notifyOf('panel_change', {'action':'multi_show','sides':sides}); + this.firePluginEvent('onPanelChange','multi_show',sides); +}; +/** Returns an array containig all properties that are set in an object +* @param {Object} obj +* @returns {Array} +*/ +Xinha.objectProperties = function(obj) +{ + var props = []; + for ( var x in obj ) + { + props[props.length] = x; + } + return props; +}; + +/** Checks if editor is active + *
+ * EDITOR ACTIVATION NOTES:
+ * when a page has multiple Xinha editors, ONLY ONE should be activated at any time (this is mostly to + * work around a bug in Mozilla, but also makes some sense). No editor should be activated or focused + * automatically until at least one editor has been activated through user action (by mouse-clicking in + * the editor). + * @private + * @returns {Boolean} + */ +Xinha.prototype.editorIsActivated = function() +{ + try + { + return Xinha.is_designMode ? this._doc.designMode == 'on' : this._doc.body.contentEditable; + } + catch (ex) + { + return false; + } +}; +/** We need to know that at least one editor on the page has been activated +* this is because we will not focus any editor until an editor has been activated +* @private +* @type {Boolean} +*/ +Xinha._someEditorHasBeenActivated = false; +/** Stores a reference to the currently active editor +* @private +* @type {Xinha} +*/ +Xinha._currentlyActiveEditor = null; +/** Enables one editor for editing, e.g. by a click in the editing area or after it has been + * deactivated programmatically before + * @private + * @returns {Boolean} + */ +Xinha.prototype.activateEditor = function() +{ + if (this.currentModal) + { + return; + } + // We only want ONE editor at a time to be active + if ( Xinha._currentlyActiveEditor ) + { + if ( Xinha._currentlyActiveEditor == this ) + { + return true; + } + Xinha._currentlyActiveEditor.deactivateEditor(); + } + + if ( Xinha.is_designMode && this._doc.designMode != 'on' ) + { + try + { + // cannot set design mode if no display + if ( this._iframe.style.display == 'none' ) + { + this._iframe.style.display = ''; + this._doc.designMode = 'on'; + this._iframe.style.display = 'none'; + } + else + { + this._doc.designMode = 'on'; + } + + // Opera loses some of it's event listeners when the designMode is set to on. + // the true just shortcuts the method to only set some listeners. + if(Xinha.is_opera) this.setEditorEvents(true); + + } catch (ex) {} + } + else if ( Xinha.is_ie&& this._doc.body.contentEditable !== true ) + { + this._doc.body.contentEditable = true; + } + + Xinha._someEditorHasBeenActivated = true; + Xinha._currentlyActiveEditor = this; + + var editor = this; + this.enableToolbar(); +}; +/** Disables the editor + * @private + */ +Xinha.prototype.deactivateEditor = function() +{ + // If the editor isn't active then the user shouldn't use the toolbar + this.disableToolbar(); + + if ( Xinha.is_designMode && this._doc.designMode != 'off' ) + { + try + { + this._doc.designMode = 'off'; + } catch (ex) {} + } + else if ( !Xinha.is_designMode && this._doc.body.contentEditable !== false ) + { + this._doc.body.contentEditable = false; + } + + if ( Xinha._currentlyActiveEditor != this ) + { + // We just deactivated an editor that wasn't marked as the currentlyActiveEditor + + return; // I think this should really be an error, there shouldn't be a situation where + // an editor is deactivated without first being activated. but it probably won't + // hurt anything. + } + + Xinha._currentlyActiveEditor = false; +}; +/** Creates the iframe (editable area) + * @private + */ +Xinha.prototype.initIframe = function() +{ + this.disableToolbar(); + var doc = null; + var editor = this; + try + { + if ( editor._iframe.contentDocument ) + { + this._doc = editor._iframe.contentDocument; + } + else + { + this._doc = editor._iframe.contentWindow.document; + } + doc = this._doc; + // try later + if ( !doc ) + { + if ( Xinha.is_gecko ) + { + setTimeout(function() { editor.initIframe(); }, 50); + return false; + } + else + { + alert("ERROR: IFRAME can't be initialized."); + } + } + } + catch(ex) + { // try later + setTimeout(function() { editor.initIframe(); }, 50); + return false; + } + + Xinha.freeLater(this, '_doc'); + + doc.open("text/html","replace"); + var html = '', doctype; + if ( editor.config.browserQuirksMode === false ) + { + doctype = ''; + } + else if ( editor.config.browserQuirksMode === true ) + { + doctype = ''; + } + else + { + doctype = Xinha.getDoctype(document); + } + + if ( !editor.config.fullPage ) + { + html += doctype + "\n"; + html += "\n"; + html += "\n"; + html += "\n"; + if ( typeof editor.config.baseHref != 'undefined' && editor.config.baseHref !== null ) + { + html += "\n"; + } + + html += Xinha.addCoreCSS(); + + if ( typeof editor.config.pageStyleSheets !== 'undefined' ) + { + for ( var i = 0; i < editor.config.pageStyleSheets.length; i++ ) + { + if ( editor.config.pageStyleSheets[i].length > 0 ) + { + html += ""; + //html += "\n"; + } + } + } + + if ( editor.config.pageStyle ) + { + html += ""; + } + + html += "\n"; + html += "\n"; + html += editor.inwardHtml(editor._textArea.value); + html += "\n"; + html += ""; + } + else + { + html = editor.inwardHtml(editor._textArea.value); + if ( html.match(Xinha.RE_doctype) ) + { + editor.setDoctype(RegExp.$1); + //html = html.replace(Xinha.RE_doctype, ""); + } + + //Fix Firefox problem with link elements not in right place (just before head) + var match = html.match(//gi); + html = html.replace(/\s*/gi, ''); + if (match) + { + html = html.replace(/<\/head>/i, match.join('\n') + "\n"); + } + } + doc.write(html); + doc.close(); + if ( this.config.fullScreen ) + { + this._fullScreen(); + } + this.setEditorEvents(); + + + // If this IFRAME had been configured for autofocus, we'll focus it now, + // since everything needed to do so is now fully loaded. + if ((typeof editor.config.autofocus != "undefined") && editor.config.autofocus !== false && + ((editor.config.autofocus == editor._textArea.id) || editor.config.autofocus == true)) + { + editor.activateEditor(); + editor.focusEditor(); + } +}; + +/** + * Delay a function until the document is ready for operations. + * See ticket:547 + * @public + * @param {Function} f The function to call once the document is ready + */ +Xinha.prototype.whenDocReady = function(f) +{ + var e = this; + if ( this._doc && this._doc.body ) + { + f(); + } + else + { + setTimeout(function() { e.whenDocReady(f); }, 50); + } +}; + + +/** Switches editor mode between wysiwyg and text (HTML) + * @param {String} mode optional "textmode" or "wysiwyg", if omitted, toggles between modes. + */ +Xinha.prototype.setMode = function(mode) +{ + var html; + if ( typeof mode == "undefined" ) + { + mode = this._editMode == "textmode" ? "wysiwyg" : "textmode"; + } + switch ( mode ) + { + case "textmode": + this.firePluginEvent('onBeforeMode', 'textmode'); + this._toolbarObjects.htmlmode.swapImage(this.config.iconList.wysiwygmode); + this.setCC("iframe"); + html = this.outwardHtml(this.getHTML()); + this.setHTML(html); + + // Hide the iframe + this.deactivateEditor(); + this._iframe.style.display = 'none'; + this._textArea.style.display = ''; + + if ( this.config.statusBar ) + { + this._statusBarTree.style.display = "none"; + this._statusBarTextMode.style.display = ""; + } + this.findCC("textarea"); + this.notifyOf('modechange', {'mode':'text'}); + this.firePluginEvent('onMode', 'textmode'); + break; + + case "wysiwyg": + this.firePluginEvent('onBeforeMode', 'wysiwyg'); + this._toolbarObjects.htmlmode.swapImage([this.imgURL('images/ed_buttons_main.png'),7,0]); + this.setCC("textarea"); + html = this.inwardHtml(this.getHTML()); + this.deactivateEditor(); + this.setHTML(html); + this._iframe.style.display = ''; + this._textArea.style.display = "none"; + this.activateEditor(); + if ( this.config.statusBar ) + { + this._statusBarTree.style.display = ""; + this._statusBarTextMode.style.display = "none"; + } + this.findCC("iframe"); + this.notifyOf('modechange', {'mode':'wysiwyg'}); + this.firePluginEvent('onMode', 'wysiwyg'); + + break; + + default: + alert("Mode <" + mode + "> not defined!"); + return false; + } + this._editMode = mode; +}; +/** Sets the HTML in fullpage mode. Actually the whole iframe document is rewritten. + * @private + * @param {String} html + */ +Xinha.prototype.setFullHTML = function(html) +{ + var save_multiline = RegExp.multiline; + RegExp.multiline = true; + if ( html.match(Xinha.RE_doctype) ) + { + this.setDoctype(RegExp.$1); + // html = html.replace(Xinha.RE_doctype, ""); + } + RegExp.multiline = save_multiline; + // disabled to save body attributes see #459 + if ( 0 ) + { + if ( html.match(Xinha.RE_head) ) + { + this._doc.getElementsByTagName("head")[0].innerHTML = RegExp.$1; + } + if ( html.match(Xinha.RE_body) ) + { + this._doc.getElementsByTagName("body")[0].innerHTML = RegExp.$1; + } + } + else + { + // FIXME - can we do this without rewriting the entire document + // does the above not work for IE? + var reac = this.editorIsActivated(); + if ( reac ) + { + this.deactivateEditor(); + } + var html_re = /((.|\n)*?)<\/html>/i; + html = html.replace(html_re, "$1"); + this._doc.open("text/html","replace"); + this._doc.write(html); + this._doc.close(); + if ( reac ) + { + this.activateEditor(); + } + this.setEditorEvents(); + return true; + } +}; +/** Initialize some event handlers + * @private + */ +Xinha.prototype.setEditorEvents = function(resetting_events_for_opera) +{ + var editor=this; + var doc = this._doc; + + editor.whenDocReady( + function() + { + if(!resetting_events_for_opera) { + // if we have multiple editors some bug in Mozilla makes some lose editing ability + Xinha._addEvents( + doc, + ["mousedown"], + function() + { + editor.activateEditor(); + return true; + } + ); + if (Xinha.is_ie) + { // #1019 Cusor not jumping to editable part of window when clicked in IE, see also #1039 + Xinha._addEvent( + editor._doc.getElementsByTagName("html")[0], + "click", + function() + { + if (editor._iframe.contentWindow.event.srcElement.tagName.toLowerCase() == 'html') // if clicked below the text (=body), the text cursor does not appear, see #1019 + { + var r = editor._doc.body.createTextRange(); + r.collapse(); + r.select(); + //setTimeout (function () { r.collapse(); r.select();},100); // won't do without timeout, dunno why + } + return true; + } + ); + } + } + + // intercept some events; for updating the toolbar & keyboard handlers + Xinha._addEvents( + doc, + ["keydown", "keypress", "mousedown", "mouseup", "drag"], + function (event) + { + return editor._editorEvent(Xinha.is_ie ? editor._iframe.contentWindow.event : event); + } + ); + + Xinha._addEvents( + doc, + ["dblclick"], + function (event) + { + return editor._onDoubleClick(Xinha.is_ie ? editor._iframe.contentWindow.event : event); + } + ); + + if(resetting_events_for_opera) return; + + // FIXME - this needs to be cleaned up and use editor.firePluginEvent + // I don't like both onGenerate and onGenerateOnce, we should only + // have onGenerate and it should only be called when the editor is + // generated (once and only once) + // check if any plugins have registered refresh handlers + for ( var i in editor.plugins ) + { + var plugin = editor.plugins[i].instance; + Xinha.refreshPlugin(plugin); + } + + // specific editor initialization + if ( typeof editor._onGenerate == "function" ) + { + editor._onGenerate(); + } + //ticket #1407 IE8 fires two resize events on one actual resize, seemingly causing an infinite loop (but not when Xinha is in an frame/iframe) + Xinha.addDom0Event(window, 'resize', function(e) + { + if (Xinha.ie_version > 7 && !window.parent) + { + if (editor.execResize) + { + editor.sizeEditor(); + editor.execResize = false; + } + else + { + editor.execResize = true; + } + } + else + { + editor.sizeEditor(); + } + }); + editor.removeLoadingMessage(); + } + ); +}; + +/*************************************************** + * Category: PLUGINS + ***************************************************/ +/** Plugins may either reside in the golbal scope (not recommended) or in Xinha.plugins. + * This function looks in both locations and is used to check the loading status and finally retrieve the plugin's constructor + * @private + * @type {Function|undefined} + * @param {String} pluginName + */ +Xinha.getPluginConstructor = function(pluginName) +{ + return Xinha.plugins[pluginName] || window[pluginName]; +}; + +/** Create the specified plugin and register it with this Xinha + * return the plugin created to allow refresh when necessary.
+ * This is only useful if Xinha is generated without using Xinha.makeEditors() + */ +Xinha.prototype.registerPlugin = function() +{ + if (!Xinha.isSupportedBrowser) + { + return; + } + var plugin = arguments[0]; + + // We can only register plugins that have been succesfully loaded + if ( plugin === null || typeof plugin == 'undefined' || (typeof plugin == 'string' && Xinha.getPluginConstructor(plugin) == 'undefined') ) + { + return false; + } + var args = []; + for ( var i = 1; i < arguments.length; ++i ) + { + args.push(arguments[i]); + } + return this.registerPlugin2(plugin, args); +}; +/** This is the variant of the function above where the plugin arguments are + * already packed in an array. Externally, it should be only used in the + * full-screen editor code, in order to initialize plugins with the same + * parameters as in the opener window. + * @private + */ +Xinha.prototype.registerPlugin2 = function(plugin, args) +{ + if ( typeof plugin == "string" && typeof Xinha.getPluginConstructor(plugin) == 'function' ) + { + var pluginName = plugin; + plugin = Xinha.getPluginConstructor(plugin); + } + if ( typeof plugin == "undefined" ) + { + /* FIXME: This should never happen. But why does it do? */ + return false; + } + if (!plugin._pluginInfo) + { + plugin._pluginInfo = + { + name: pluginName + }; + } + var obj = new plugin(this, args); + if ( obj ) + { + var clone = {}; + var info = plugin._pluginInfo; + for ( var i in info ) + { + clone[i] = info[i]; + } + clone.instance = obj; + clone.args = args; + this.plugins[plugin._pluginInfo.name] = clone; + return obj; + } + else + { + Xinha.debugMsg("Can't register plugin " + plugin.toString() + ".", 'warn'); + } +}; + + +/** Dynamically returns the directory from which the plugins are loaded
+ * This could be overridden to change the dir
+ * @TODO: Wouldn't this be better as a config option? + * @private + * @param {String} pluginName + * @param {Boolean} return the directory for an unsupported plugin + * @returns {String} path to plugin + */ +Xinha.getPluginDir = function(plugin, forceUnsupported) +{ + if (Xinha.externalPlugins[plugin]) + { + return Xinha.externalPlugins[plugin][0]; + } + if (forceUnsupported || + // If the plugin is fully loaded, it's supported status is already set. + (Xinha.getPluginConstructor(plugin) && (typeof Xinha.getPluginConstructor(plugin).supported != 'undefined') && !Xinha.getPluginConstructor(plugin).supported)) + { + return _editor_url + "unsupported_plugins/" + plugin ; + } + return _editor_url + "plugins/" + plugin ; +}; +/** Static function that loads the given plugin + * @param {String} pluginName + * @param {Function} callback function to be called when file is loaded + * @param {String} plugin_file URL of the file to load + * @returns {Boolean} true if plugin loaded, false otherwise + */ +Xinha.loadPlugin = function(pluginName, callback, url) +{ + if (!Xinha.isSupportedBrowser) + { + return; + } + Xinha.setLoadingMessage (Xinha._lc("Loading plugin $plugin="+pluginName+"$")); + + // Might already be loaded + if ( typeof Xinha.getPluginConstructor(pluginName) != 'undefined' ) + { + if ( callback ) + { + callback(pluginName); + } + return true; + } + Xinha._pluginLoadStatus[pluginName] = 'loading'; + + // This function will try to load a plugin in multiple passes. It tries to + // load the plugin from either the plugin or unsupported directory, using + // both naming schemes in this order: + // 1. /plugins -> CurrentNamingScheme + // 2. /plugins -> old-naming-scheme + // 3. /unsupported -> CurrentNamingScheme + // 4. /unsupported -> old-naming-scheme + + function multiStageLoader(stage,pluginName) + { + var nextstage, dir, file, success_message; + switch (stage) + { + case 'start': + nextstage = 'old_naming'; + dir = Xinha.getPluginDir(pluginName); + file = pluginName + ".js"; + break; + case 'old_naming': + nextstage = 'unsupported'; + dir = Xinha.getPluginDir(pluginName); + file = pluginName.replace(/([a-z])([A-Z])([a-z])/g, function (str, l1, l2, l3) { return l1 + "-" + l2.toLowerCase() + l3; }).toLowerCase() + ".js"; + success_message = 'You are using an obsolete naming scheme for the Xinha plugin '+pluginName+'. Please rename '+file+' to '+pluginName+'.js'; + break; + case 'unsupported': + nextstage = 'unsupported_old_name'; + dir = Xinha.getPluginDir(pluginName, true); + file = pluginName + ".js"; + success_message = 'You are using the unsupported Xinha plugin '+pluginName+'. If you wish continued support, please see http://trac.xinha.org/ticket/1297'; + break; + case 'unsupported_old_name': + nextstage = ''; + dir = Xinha.getPluginDir(pluginName, true); + file = pluginName.replace(/([a-z])([A-Z])([a-z])/g, function (str, l1, l2, l3) { return l1 + "-" + l2.toLowerCase() + l3; }).toLowerCase() + ".js"; + success_message = 'You are using the unsupported Xinha plugin '+pluginName+'. If you wish continued support, please see http://trac.xinha.org/ticket/1297'; + break; + default: + Xinha._pluginLoadStatus[pluginName] = 'failed'; + Xinha.debugMsg('Xinha was not able to find the plugin '+pluginName+'. Please make sure the plugin exists.', 'warn'); + return; + } + var url = dir + "/" + file; + + // This is a callback wrapper that allows us to set the plugin's status + // once it loads. + function statusCallback(pluginName) + { + Xinha.getPluginConstructor(pluginName).supported = stage.indexOf('unsupported') !== 0; + callback(pluginName); + } + + // To speed things up, we start loading the script file before pinging it. + // If the load fails, we'll just clean up afterwards. + Xinha._loadback(url, statusCallback, this, pluginName); + + Xinha.ping(url, + // On success, we'll display a success message if there is one. + function() + { + if (success_message) + { + Xinha.debugMsg(success_message); + } + }, + // On failure, we'll clean up the failed load and try the next stage + function() + { + Xinha.removeFromParent(document.getElementById(url)); + multiStageLoader(nextstage, pluginName); + }); + } + + if(!url) + { + if (Xinha.externalPlugins[pluginName]) + { + Xinha._loadback(Xinha.externalPlugins[pluginName][0]+Xinha.externalPlugins[pluginName][1], callback, this, pluginName); + } + else + { + var editor = this; + multiStageLoader('start',pluginName); + } + } + else + { + Xinha._loadback(url, callback, this, pluginName); + } + + return false; +}; +/** Stores a status for each loading plugin that may be one of "loading","ready", or "failed" + * @private + * @type {Object} + */ +Xinha._pluginLoadStatus = {}; +/** Stores the paths to plugins that are not in the default location + * @private + * @type {Object} + */ +Xinha.externalPlugins = {}; +/** The namespace for plugins + * @private + * @type {Object} + */ +Xinha.plugins = {}; + +/** Static function that loads the plugins (see xinha_plugins in NewbieGuide) + * @param {Array} plugins + * @param {Function} callbackIfNotReady function that is called repeatedly until all files are + * @param {String} optional url URL of the plugin file; obviously plugins should contain only one item if url is given + * @returns {Boolean} true if all plugins are loaded, false otherwise + */ +Xinha.loadPlugins = function(plugins, callbackIfNotReady,url) +{ + if (!Xinha.isSupportedBrowser) + { + return; + } + //Xinha.setLoadingMessage (Xinha._lc("Loading plugins")); + var m,i; + for (i=0;i
+ * + * Example: editor.firePluginEvent('onExecCommand', 'paste')
+ * The plugin would then define a method
+ * PluginName.prototype.onExecCommand = function (cmdID, UI, param) {do something...}

+ * The following methodNames are currently available:
+ * + * + * + * + * + * + * + * + * + * + * + * + * + *
methodNameParameters
onExecCommand cmdID, UI, param
onKeyPressev
onMouseDownev


+ * + * The browser specific plugin (if any) is called last. The result of each call is + * treated as boolean. A true return means that the event will stop, no further plugins + * will get the event, a false return means the event will continue to fire. + * + * @param {String} methodName + * @param {mixed} arguments to pass to the method, optional [2..n] + * @returns {Boolean} + */ + +Xinha.prototype.firePluginEvent = function(methodName) +{ + // arguments is not a real array so we can't just .shift() it unfortunatly. + var argsArray = [ ]; + for(var i = 1; i < arguments.length; i++) + { + argsArray[i-1] = arguments[i]; + } + + for ( i in this.plugins ) + { + var plugin = this.plugins[i].instance; + + // Skip the browser specific plugin + if (plugin == this._browserSpecificPlugin) + { + continue; + } + if ( plugin && typeof plugin[methodName] == "function" ) + { + var thisArg = (i == 'Events') ? this : plugin; + if ( plugin[methodName].apply(thisArg, argsArray) ) + { + return true; + } + } + } + + // Now the browser speific + plugin = this._browserSpecificPlugin; + if ( plugin && typeof plugin[methodName] == "function" ) + { + if ( plugin[methodName].apply(plugin, argsArray) ) + { + return true; + } + } + return false; +}; +/** Adds a stylesheet to the document + * @param {String} style name of the stylesheet file + * @param {String} plugin optional name of a plugin; if passed this function looks for the stylesheet file in the plugin directory + * @param {String} id optional a unique id for identifiing the created link element, e.g. for avoiding double loading + * or later removing it again + */ +Xinha.loadStyle = function(style, plugin, id,prepend) +{ + var url = _editor_url || ''; + if ( plugin ) + { + url = Xinha.getPluginDir( plugin ) + "/"; + } + url += style; + // @todo: would not it be better to check the first character instead of a regex ? + // if ( typeof style == 'string' && style.charAt(0) == '/' ) + // { + // url = style; + // } + if ( /^\//.test(style) ) + { + url = style; + } + var head = document.getElementsByTagName("head")[0]; + var link = document.createElement("link"); + link.rel = "stylesheet"; + link.href = url; + link.type = "text/css"; + if (id) + { + link.id = id; + } + if (prepend && head.getElementsByTagName('link')[0]) + { + head.insertBefore(link,head.getElementsByTagName('link')[0]); + } + else + { + head.appendChild(link); + } + +}; + +/** Adds a script to the document + * + * Warning: Browsers may cause the script to load asynchronously. + * + * @param {String} style name of the javascript file + * @param {String} plugin optional name of a plugin; if passed this function looks for the stylesheet file in the plugin directory + * + */ +Xinha.loadScript = function(script, plugin, callback) +{ + var url = _editor_url || ''; + if ( plugin ) + { + url = Xinha.getPluginDir( plugin ) + "/"; + } + url += script; + // @todo: would not it be better to check the first character instead of a regex ? + // if ( typeof style == 'string' && style.charAt(0) == '/' ) + // { + // url = style; + // } + if ( /^\//.test(script) ) + { + url = script; + } + + Xinha._loadback(url, callback); + +}; + +/** Load one or more assets, sequentially, where an asset is a CSS file, or a javascript file. + * + * Example Usage: + * + * Xinha.includeAssets( 'foo.css', 'bar.js', [ 'foo.css', 'MyPlugin' ], { type: 'text/css', url: 'foo.php', plugin: 'MyPlugin } ); + * + * Alternative usage, use Xinha.includeAssets() to make a loader, then use loadScript, loadStyle and whenReady methods + * on your loader object as and when you wish, you can chain the calls if you like. + * + * You may add any number of callbacks using .whenReady() multiple times. + * + * var myAssetLoader = Xinha.includeAssets(); + * myAssetLoader.loadScript('foo.js', 'MyPlugin') + * .loadStyle('foo.css', 'MyPlugin'); + * + */ + +Xinha.includeAssets = function() +{ + var assetLoader = { pendingAssets: [ ], loaderRunning: false, loadedScripts: [ ] }; + + assetLoader.callbacks = [ ]; + + assetLoader.loadNext = function() + { + var self = this; + this.loaderRunning = true; + + if(this.pendingAssets.length) + { + var nxt = this.pendingAssets[0]; + this.pendingAssets.splice(0,1); // Remove 1 element + switch(nxt.type) + { + case 'text/css': + Xinha.loadStyle(nxt.url, nxt.plugin); + return this.loadNext(); + + case 'text/javascript': + Xinha.loadScript(nxt.url, nxt.plugin, function() { self.loadNext(); }); + } + } + else + { + this.loaderRunning = false; + this.runCallback(); + } + }; + + assetLoader.loadScript = function(url, plugin) + { + var self = this; + + this.pendingAssets.push({ 'type': 'text/javascript', 'url': url, 'plugin': plugin }); + if(!this.loaderRunning) this.loadNext(); + + return this; + }; + + assetLoader.loadScriptOnce = function(url, plugin) + { + for(var i = 0; i < this.loadedScripts.length; i++) + { + if(this.loadedScripts[i].url == url && this.loadedScripts[i].plugin == plugin) + return this; // Already done (or in process) + } + + return this.loadScript(url, plugin); + } + + assetLoader.loadStyle = function(url, plugin) + { + var self = this; + + this.pendingAssets.push({ 'type': 'text/css', 'url': url, 'plugin': plugin }); + if(!this.loaderRunning) this.loadNext(); + + return this; + }; + + assetLoader.whenReady = function(callback) + { + this.callbacks.push(callback); + if(!this.loaderRunning) this.loadNext(); + + return this; + }; + + assetLoader.runCallback = function() + { + while(this.callbacks.length) + { + var _callback = this.callbacks.splice(0,1); + _callback[0](); + _callback = null; + } + return this; + } + + for(var i = 0 ; i < arguments.length; i++) + { + if(typeof arguments[i] == 'string') + { + if(arguments[i].match(/\.css$/i)) + { + assetLoader.loadStyle(arguments[i]); + } + else + { + assetLoader.loadScript(arguments[i]); + } + } + else if(arguments[i].type) + { + if(arguments[i].type.match(/text\/css/i)) + { + assetLoader.loadStyle(arguments[i].url, arguments[i].plugin); + } + else if(arguments[i].type.match(/text\/javascript/i)) + { + assetLoader.loadScript(arguments[i].url, arguments[i].plugin); + } + } + else if(arguments[i].length >= 1) + { + if(arguments[i][0].match(/\.css$/i)) + { + assetLoader.loadStyle(arguments[i][0], arguments[i][1]); + } + else + { + assetLoader.loadScript(arguments[i][0], arguments[i][1]); + } + } + } + + return assetLoader; +} + +/*************************************************** + * Category: EDITOR UTILITIES + ***************************************************/ +/** Utility function: Outputs the structure of the edited document */ +Xinha.prototype.debugTree = function() +{ + var ta = document.createElement("textarea"); + ta.style.width = "100%"; + ta.style.height = "20em"; + ta.value = ""; + function debug(indent, str) + { + for ( ; --indent >= 0; ) + { + ta.value += " "; + } + ta.value += str + "\n"; + } + function _dt(root, level) + { + var tag = root.tagName.toLowerCase(), i; + var ns = Xinha.is_ie ? root.scopeName : root.prefix; + debug(level, "- " + tag + " [" + ns + "]"); + for ( i = root.firstChild; i; i = i.nextSibling ) + { + if ( i.nodeType == 1 ) + { + _dt(i, level + 2); + } + } + } + _dt(this._doc.body, 0); + document.body.appendChild(ta); +}; +/** Extracts the textual content of a given node + * @param {DomNode} el + */ + +Xinha.getInnerText = function(el) +{ + var txt = '', i; + for ( i = el.firstChild; i; i = i.nextSibling ) + { + if ( i.nodeType == 3 ) + { + txt += i.data; + } + else if ( i.nodeType == 1 ) + { + txt += Xinha.getInnerText(i); + } + } + return txt; +}; +/** Cleans dirty HTML from MS word; always cleans the whole editor content + * @TODO: move this in a separate file + * @TODO: turn this into a static function that cleans a given string + */ +Xinha.prototype._wordClean = function() +{ + var editor = this; + var stats = + { + empty_tags : 0, + cond_comm : 0, + mso_elmts : 0, + mso_class : 0, + mso_style : 0, + mso_xmlel : 0, + orig_len : this._doc.body.innerHTML.length, + T : new Date().getTime() + }; + var stats_txt = + { + empty_tags : "Empty tags removed: ", + cond_comm : "Conditional comments removed", + mso_elmts : "MSO invalid elements removed", + mso_class : "MSO class names removed: ", + mso_style : "MSO inline style removed: ", + mso_xmlel : "MSO XML elements stripped: " + }; + + function showStats() + { + var txt = "Xinha word cleaner stats: \n\n"; + for ( var i in stats ) + { + if ( stats_txt[i] ) + { + txt += stats_txt[i] + stats[i] + "\n"; + } + } + txt += "\nInitial document length: " + stats.orig_len + "\n"; + txt += "Final document length: " + editor._doc.body.innerHTML.length + "\n"; + txt += "Clean-up took " + ((new Date().getTime() - stats.T) / 1000) + " seconds"; + alert(txt); + } + + function clearClass(node) + { + var newc = node.className.replace(/(^|\s)mso.*?(\s|$)/ig, ' '); + if ( newc != node.className ) + { + node.className = newc; + if ( !/\S/.test(node.className)) + { + node.removeAttribute("className"); + ++stats.mso_class; + } + } + } + + function clearStyle(node) + { + var declarations = node.style.cssText.split(/\s*;\s*/); + for ( var i = declarations.length; --i >= 0; ) + { + if ( /^mso|^tab-stops/i.test(declarations[i]) || /^margin\s*:\s*0..\s+0..\s+0../i.test(declarations[i]) ) + { + ++stats.mso_style; + declarations.splice(i, 1); + } + } + node.style.cssText = declarations.join("; "); + } + + function removeElements(el) + { + if (('link' == el.tagName.toLowerCase() && + (el.attributes && /File-List|Edit-Time-Data|themeData|colorSchemeMapping/.test(el.attributes.rel.nodeValue))) || + /^(style|meta)$/i.test(el.tagName)) + { + Xinha.removeFromParent(el); + ++stats.mso_elmts; + return true; + } + return false; + } + + function checkEmpty(el) + { + // @todo : check if this is quicker + // if (!['A','SPAN','B','STRONG','I','EM','FONT'].contains(el.tagName) && !el.firstChild) + if ( /^(a|span|b|strong|i|em|font|div|p)$/i.test(el.tagName) && !el.firstChild) + { + Xinha.removeFromParent(el); + ++stats.empty_tags; + return true; + } + return false; + } + + function parseTree(root) + { + clearClass(root); + clearStyle(root); + var next; + for (var i = root.firstChild; i; i = next ) + { + next = i.nextSibling; + if ( i.nodeType == 1 && parseTree(i) ) + { + if ((Xinha.is_ie && root.scopeName != 'HTML') || (!Xinha.is_ie && /:/.test(i.tagName))) + { + // Nowadays, Word spits out tags like ''. Since the + // document being cleaned might be HTML4 and not XHTML, this tag is + // interpreted as ''. For HTML tags without + // closing elements (e.g. IMG) these two forms are equivalent. Since + // HTML does not recognize these tags, however, they end up as + // parents of elements that should be their siblings. We reparent + // the children and remove them from the document. + for (var index=i.childNodes && i.childNodes.length-1; i.childNodes && i.childNodes.length && i.childNodes[index]; --index) + { + if (i.nextSibling) + { + i.parentNode.insertBefore(i.childNodes[index],i.nextSibling); + } + else + { + i.parentNode.appendChild(i.childNodes[index]); + } + } + Xinha.removeFromParent(i); + continue; + } + if (checkEmpty(i)) + { + continue; + } + if (removeElements(i)) + { + continue; + } + } + else if (i.nodeType == 8) + { + // 8 is a comment node, and can contain conditional comments, which + // will be interpreted by IE as if they were not comments. + if (/(\s*\[\s*if\s*(([gl]te?|!)\s*)?(IE|mso)\s*(\d+(\.\d+)?\s*)?\]>)/.test(i.nodeValue)) + { + // We strip all conditional comments directly from the tree. + Xinha.removeFromParent(i); + ++stats.cond_comm; + } + } + } + return true; + } + parseTree(this._doc.body); + // showStats(); + // this.debugTree(); + // this.setHTML(this.getHTML()); + // this.setHTML(this.getInnerHTML()); + // this.forceRedraw(); + this.updateToolbar(); +}; + +/** Removes <font> tags; always cleans the whole editor content + * @TODO: move this in a separate file + * @TODO: turn this into a static function that cleans a given string + */ +Xinha.prototype._clearFonts = function() +{ + var D = this.getInnerHTML(); + + if ( confirm(Xinha._lc("Would you like to clear font typefaces?")) ) + { + D = D.replace(/face="[^"]*"/gi, ''); + D = D.replace(/font-family:[^;}"']+;?/gi, ''); + } + + if ( confirm(Xinha._lc("Would you like to clear font sizes?")) ) + { + D = D.replace(/size="[^"]*"/gi, ''); + D = D.replace(/font-size:[^;}"']+;?/gi, ''); + } + + if ( confirm(Xinha._lc("Would you like to clear font colours?")) ) + { + D = D.replace(/color="[^"]*"/gi, ''); + D = D.replace(/([^\-])color:[^;}"']+;?/gi, '$1'); + } + + D = D.replace(/(style|class)="\s*"/gi, ''); + D = D.replace(/<(font|span)\s*>/gi, ''); + this.setHTML(D); + this.updateToolbar(); +}; + +Xinha.prototype._splitBlock = function() +{ + this._doc.execCommand('formatblock', false, 'div'); +}; + +/** Sometimes the display has to be refreshed to make DOM changes visible (?) (Gecko bug?) */ +Xinha.prototype.forceRedraw = function() +{ + this._doc.body.style.visibility = "hidden"; + this._doc.body.style.visibility = ""; + // this._doc.body.innerHTML = this.getInnerHTML(); +}; + +/** Focuses the iframe window. + * @returns {document} a reference to the editor document + */ +Xinha.prototype.focusEditor = function() +{ + switch (this._editMode) + { + // notice the try { ... } catch block to avoid some rare exceptions in FireFox + // (perhaps also in other Gecko browsers). Manual focus by user is required in + // case of an error. Somebody has an idea? + case "wysiwyg" : + try + { + // We don't want to focus the field unless at least one field has been activated. + if ( Xinha._someEditorHasBeenActivated ) + { + this.activateEditor(); // Ensure *this* editor is activated + this._iframe.contentWindow.focus(); // and focus it + } + } catch (ex) {} + break; + case "textmode": + try + { + this._textArea.focus(); + } catch (e) {} + break; + default: + alert("ERROR: mode " + this._editMode + " is not defined"); + } + return this._doc; +}; + +/** Takes a snapshot of the current text (for undo) + * @private + */ +Xinha.prototype._undoTakeSnapshot = function() +{ + ++this._undoPos; + if ( this._undoPos >= this.config.undoSteps ) + { + // remove the first element + this._undoQueue.shift(); + --this._undoPos; + } + // use the fasted method (getInnerHTML); + var take = true; + var txt = this.getInnerHTML(); + if ( this._undoPos > 0 ) + { + take = (this._undoQueue[this._undoPos - 1] != txt); + } + if ( take ) + { + this._undoQueue[this._undoPos] = txt; + } + else + { + this._undoPos--; + } +}; +/** Custom implementation of undo functionality + * @private + */ +Xinha.prototype.undo = function() +{ + if ( this._undoPos > 0 ) + { + var txt = this._undoQueue[--this._undoPos]; + if ( txt ) + { + this.setHTML(txt); + } + else + { + ++this._undoPos; + } + } +}; +/** Custom implementation of redo functionality + * @private + */ +Xinha.prototype.redo = function() +{ + if ( this._undoPos < this._undoQueue.length - 1 ) + { + var txt = this._undoQueue[++this._undoPos]; + if ( txt ) + { + this.setHTML(txt); + } + else + { + --this._undoPos; + } + } +}; +/** Disables (greys out) the buttons of the toolbar + * @param {Array} except this array contains ids of toolbar objects that will not be disabled + */ +Xinha.prototype.disableToolbar = function(except) +{ + if ( this._timerToolbar ) + { + clearTimeout(this._timerToolbar); + } + if ( typeof except == 'undefined' ) + { + except = [ ]; + } + else if ( typeof except != 'object' ) + { + except = [except]; + } + + for ( var i in this._toolbarObjects ) + { + var btn = this._toolbarObjects[i]; + if ( except.contains(i) ) + { + continue; + } + // prevent iterating over wrong type + if ( typeof btn.state != 'function' ) + { + continue; + } + btn.state("enabled", false); + } +}; +/** Enables the toolbar again when disabled by disableToolbar() */ +Xinha.prototype.enableToolbar = function() +{ + this.updateToolbar(); +}; + +/** Updates enabled/disable/active state of the toolbar elements, the statusbar and other things + * This function is called on every key stroke as well as by a timer on a regular basis.
+ * Plugins have the opportunity to implement a prototype.onUpdateToolbar() method, which will also + * be called by this function. + * @param {Boolean} noStatus private use Exempt updating of statusbar + */ +// FIXME : this function needs to be splitted in more functions. +// It is actually to heavy to be understable and very scary to manipulate +Xinha.prototype.updateToolbar = function(noStatus) +{ + if (this.suspendUpdateToolbar) + { + return; + } + var doc = this._doc; + var text = (this._editMode == "textmode"); + var ancestors = null; + if ( !text ) + { + ancestors = this.getAllAncestors(); + if ( this.config.statusBar && !noStatus ) + { + while ( this._statusBarItems.length ) + { + var item = this._statusBarItems.pop(); + item.el = null; + item.editor = null; + item.onclick = null; + item.oncontextmenu = null; + item._xinha_dom0Events.click = null; + item._xinha_dom0Events.contextmenu = null; + item = null; + } + + this._statusBarTree.innerHTML = ' '; + this._statusBarTree.appendChild(document.createTextNode(Xinha._lc("Path") + ": ")); + for ( var i = ancestors.length; --i >= 0; ) + { + var el = ancestors[i]; + if ( !el ) + { + // hell knows why we get here; this + // could be a classic example of why + // it's good to check for conditions + // that are impossible to happen ;-) + continue; + } + var a = document.createElement("a"); + a.href = "javascript:void(0);"; + a.el = el; + a.editor = this; + this._statusBarItems.push(a); + Xinha.addDom0Event( + a, + 'click', + function() { + this.blur(); + this.editor.selectNodeContents(this.el); + this.editor.updateToolbar(true); + return false; + } + ); + Xinha.addDom0Event( + a, + 'contextmenu', + function() + { + // TODO: add context menu here + this.blur(); + var info = "Inline style:\n\n"; + info += this.el.style.cssText.split(/;\s*/).join(";\n"); + alert(info); + return false; + } + ); + var txt = el.tagName.toLowerCase(); + switch (txt) + { + case 'b': + txt = 'strong'; + break; + case 'i': + txt = 'em'; + break; + case 'strike': + txt = 'del'; + break; + } + if (typeof el.style != 'undefined') + { + a.title = el.style.cssText; + } + if ( el.id ) + { + txt += "#" + el.id; + } + if ( el.className ) + { + txt += "." + el.className; + } + a.appendChild(document.createTextNode(txt)); + this._statusBarTree.appendChild(a); + if ( i !== 0 ) + { + this._statusBarTree.appendChild(document.createTextNode(String.fromCharCode(0xbb))); + } + Xinha.freeLater(a); + } + } + } + + for ( var cmd in this._toolbarObjects ) + { + var btn = this._toolbarObjects[cmd]; + var inContext = true; + // prevent iterating over wrong type + if ( typeof btn.state != 'function' ) + { + continue; + } + if ( btn.context && !text ) + { + inContext = false; + var context = btn.context; + var attrs = []; + if ( /(.*)\[(.*?)\]/.test(context) ) + { + context = RegExp.$1; + attrs = RegExp.$2.split(","); + } + context = context.toLowerCase(); + var match = (context == "*"); + for ( var k = 0; k < ancestors.length; ++k ) + { + if ( !ancestors[k] ) + { + // the impossible really happens. + continue; + } + if ( match || ( ancestors[k].tagName.toLowerCase() == context ) ) + { + inContext = true; + var contextSplit = null; + var att = null; + var comp = null; + var attVal = null; + for ( var ka = 0; ka < attrs.length; ++ka ) + { + contextSplit = attrs[ka].match(/(.*)(==|!=|===|!==|>|>=|<|<=)(.*)/); + att = contextSplit[1]; + comp = contextSplit[2]; + attVal = contextSplit[3]; + + if (!eval(ancestors[k][att] + comp + attVal)) + { + inContext = false; + break; + } + } + if ( inContext ) + { + break; + } + } + } + } + btn.state("enabled", (!text || btn.text) && inContext); + if ( typeof cmd == "function" ) + { + continue; + } + // look-it-up in the custom dropdown boxes + var dropdown = this.config.customSelects[cmd]; + if ( ( !text || btn.text ) && ( typeof dropdown != "undefined" ) ) + { + dropdown.refresh(this); + continue; + } + switch (cmd) + { + case "fontname": + case "fontsize": + if ( !text ) + { + try + { + var value = ("" + doc.queryCommandValue(cmd)).toLowerCase(); + if ( !value ) + { + btn.element.selectedIndex = 0; + break; + } + + // HACK -- retrieve the config option for this + // combo box. We rely on the fact that the + // variable in config has the same name as + // button name in the toolbar. + var options = this.config[cmd]; + var sIndex = 0; + for ( var j in options ) + { + // FIXME: the following line is scary. + if ( ( j.toLowerCase() == value ) || ( options[j].substr(0, value.length).toLowerCase() == value ) ) + { + btn.element.selectedIndex = sIndex; + throw "ok"; + } + ++sIndex; + } + btn.element.selectedIndex = 0; + } catch(ex) {} + } + break; + + // It's better to search for the format block by tag name from the + // current selection upwards, because IE has a tendancy to return + // things like 'heading 1' for 'h1', which breaks things if you want + // to call your heading blocks 'header 1'. Stupid MS. + case "formatblock": + var blocks = []; + for ( var indexBlock in this.config.formatblock ) + { + // prevent iterating over wrong type + if ( typeof this.config.formatblock[indexBlock] == 'string' ) + { + blocks[blocks.length] = this.config.formatblock[indexBlock]; + } + } + + var deepestAncestor = this._getFirstAncestor(this.getSelection(), blocks); + if ( deepestAncestor ) + { + for ( var x = 0; x < blocks.length; x++ ) + { + if ( blocks[x].toLowerCase() == deepestAncestor.tagName.toLowerCase() ) + { + btn.element.selectedIndex = x; + } + } + } + else + { + btn.element.selectedIndex = 0; + } + break; + + case "textindicator": + if ( !text ) + { + try + { + var style = btn.element.style; + style.backgroundColor = Xinha._makeColor(doc.queryCommandValue(Xinha.is_ie ? "backcolor" : "hilitecolor")); + if ( /transparent/i.test(style.backgroundColor) ) + { + // Mozilla + style.backgroundColor = Xinha._makeColor(doc.queryCommandValue("backcolor")); + } + style.color = Xinha._makeColor(doc.queryCommandValue("forecolor")); + style.fontFamily = doc.queryCommandValue("fontname"); + style.fontWeight = doc.queryCommandState("bold") ? "bold" : "normal"; + style.fontStyle = doc.queryCommandState("italic") ? "italic" : "normal"; + } catch (ex) { + // alert(e + "\n\n" + cmd); + } + } + break; + + case "htmlmode": + btn.state("active", text); + break; + + case "lefttoright": + case "righttoleft": + var eltBlock = this.getParentElement(); + while ( eltBlock && !Xinha.isBlockElement(eltBlock) ) + { + eltBlock = eltBlock.parentNode; + } + if ( eltBlock ) + { + btn.state("active", (eltBlock.style.direction == ((cmd == "righttoleft") ? "rtl" : "ltr"))); + } + break; + + default: + cmd = cmd.replace(/(un)?orderedlist/i, "insert$1orderedlist"); + try + { + btn.state("active", (!text && doc.queryCommandState(cmd))); + } catch (ex) {} + break; + } + } + // take undo snapshots + if ( this._customUndo && !this._timerUndo ) + { + this._undoTakeSnapshot(); + var editor = this; + this._timerUndo = setTimeout(function() { editor._timerUndo = null; }, this.config.undoTimeout); + } + this.firePluginEvent('onUpdateToolbar'); +}; + +/** Returns a editor object referenced by the id or name of the textarea or the textarea node itself + * For example to retrieve the HTML of an editor made out of the textarea with the id "myTextArea" you would do
+ * + * var editor = Xinha.getEditor("myTextArea"); + * var html = editor.getEditorContent(); + * + * @returns {Xinha|null} + * @param {String|DomNode} ref id or name of the textarea or the textarea node itself + */ +Xinha.getEditor = function(ref) +{ + for ( var i = __xinhas.length; i--; ) + { + var editor = __xinhas[i]; + if ( editor && ( editor._textArea.id == ref || editor._textArea.name == ref || editor._textArea == ref ) ) + { + return editor; + } + } + return null; +}; +/** Sometimes one wants to call a plugin method directly, e.g. from outside the editor. + * This function returns the respective editor's instance of a plugin. + * For example you might want to have a button to trigger SaveSubmit's save() method:
+ * + * <button type="button" onclick="Xinha.getEditor('myTextArea').getPluginInstance('SaveSubmit').save();return false;">Save</button> + * + * @returns {PluginObject|null} + * @param {String} plugin name of the plugin + */ +Xinha.prototype.getPluginInstance = function (plugin) +{ + if (this.plugins[plugin]) + { + return this.plugins[plugin].instance; + } + else + { + return null; + } +}; +/** Returns an array with all the ancestor nodes of the selection or current cursor position. +* @returns {Array} +*/ +Xinha.prototype.getAllAncestors = function() +{ + var p = this.getParentElement(); + var a = []; + while ( p && (p.nodeType == 1) && ( p.tagName.toLowerCase() != 'body' ) ) + { + a.push(p); + p = p.parentNode; + } + a.push(this._doc.body); + return a; +}; + +/** Traverses the DOM upwards and returns the first element that is of one of the specified types + * @param {Selection} sel Selection object as returned by getSelection + * @param {Array} types Array of matching criteria. Each criteria is either a string containing the tag name, or a callback used to select the element. + * @returns {DomNode|null} + */ +Xinha.prototype._getFirstAncestor = function(sel, types) +{ + var prnt = this.activeElement(sel); + if ( prnt === null ) + { + // Hmm, I think Xinha.getParentElement() would do the job better?? - James + try + { + prnt = (Xinha.is_ie ? this.createRange(sel).parentElement() : this.createRange(sel).commonAncestorContainer); + } + catch(ex) + { + return null; + } + } + + if ( typeof types == 'string' ) + { + types = [types]; + } + + while ( prnt ) + { + if ( prnt.nodeType == 1 ) + { + if ( types === null ) + { + return prnt; + } + for (var index=0; index) if no parameter is passed + if ( !value ) + { + this.updateToolbar(); + break; + } + if( !Xinha.is_gecko || value !== 'blockquote' ) + { + value = "<" + value + ">"; + } + this.execCommand(txt, false, value); + break; + default: + // try to look it up in the registered dropdowns + var dropdown = this.config.customSelects[txt]; + if ( typeof dropdown != "undefined" ) + { + dropdown.action(this, value, el, txt); + } + else + { + alert("FIXME: combo box " + txt + " not implemented"); + } + break; + } +}; + +/** Open a popup to select the hilitecolor or forecolor + * @private + * @param {String} cmdID The commande ID (hilitecolor or forecolor) + */ +Xinha.prototype._colorSelector = function(cmdID) +{ + var editor = this; // for nested functions + + // backcolor only works with useCSS/styleWithCSS (see mozilla bug #279330 & Midas doc) + // and its also nicer as + if ( Xinha.is_gecko ) + { + try + { + editor._doc.execCommand('useCSS', false, false); // useCSS deprecated & replaced by styleWithCSS + editor._doc.execCommand('styleWithCSS', false, true); + + } catch (ex) {} + } + + var btn = editor._toolbarObjects[cmdID].element; + var initcolor; + if ( cmdID == 'hilitecolor' ) + { + if ( Xinha.is_ie ) + { + cmdID = 'backcolor'; + initcolor = Xinha._colorToRgb(editor._doc.queryCommandValue("backcolor")); + } + else + { + initcolor = Xinha._colorToRgb(editor._doc.queryCommandValue("hilitecolor")); + } + } + else + { + initcolor = Xinha._colorToRgb(editor._doc.queryCommandValue("forecolor")); + } + var cback = function(color) { editor._doc.execCommand(cmdID, false, color); }; + if ( Xinha.is_ie ) + { + var range = editor.createRange(editor.getSelection()); + cback = function(color) + { + range.select(); + editor._doc.execCommand(cmdID, false, color); + }; + } + var picker = new Xinha.colorPicker( + { + cellsize:editor.config.colorPickerCellSize, + callback:cback, + granularity:editor.config.colorPickerGranularity, + websafe:editor.config.colorPickerWebSafe, + savecolors:editor.config.colorPickerSaveColors + }); + picker.open(editor.config.colorPickerPosition, btn, initcolor); +}; + +/** This is a wrapper for the browser's execCommand function that handles things like + * formatting, inserting elements, etc.
+ * It intercepts some commands and replaces them with our own implementation.
+ * It provides a hook for the "firePluginEvent" system ("onExecCommand").

+ * For reference see:
+ * Mozilla implementation
+ * MS implementation + * + * @see Xinha#firePluginEvent + * @param {String} cmdID command to be executed as defined in the browsers implemantations or Xinha custom + * @param {Boolean} UI for compatibility with the execCommand syntax; false in most (all) cases + * @param {Mixed} param Some commands require parameters + * @returns {Boolean} always false + */ +Xinha.prototype.execCommand = function(cmdID, UI, param) +{ + var editor = this; // for nested functions + this.focusEditor(); + cmdID = cmdID.toLowerCase(); + + // See if any plugins want to do something special + if(this.firePluginEvent('onExecCommand', cmdID, UI, param)) + { + this.updateToolbar(); + return false; + } + + switch (cmdID) + { + case "htmlmode": + this.setMode(); + break; + + case "hilitecolor": + case "forecolor": + this._colorSelector(cmdID); + break; + + case "createlink": + this._createLink(); + break; + + case "undo": + case "redo": + if (this._customUndo) + { + this[cmdID](); + } + else + { + this._doc.execCommand(cmdID, UI, param); + } + break; + + case "inserttable": + this._insertTable(); + break; + + case "insertimage": + this._insertImage(); + break; + + case "showhelp": + this._popupDialog(editor.config.URIs.help, null, this); + break; + + case "killword": + this._wordClean(); + break; + + case "cut": + case "copy": + case "paste": + this._doc.execCommand(cmdID, UI, param); + if ( this.config.killWordOnPaste ) + { + this._wordClean(); + } + break; + case "lefttoright": + case "righttoleft": + if (this.config.changeJustifyWithDirection) + { + this._doc.execCommand((cmdID == "righttoleft") ? "justifyright" : "justifyleft", UI, param); + } + var dir = (cmdID == "righttoleft") ? "rtl" : "ltr"; + var el = this.getParentElement(); + while ( el && !Xinha.isBlockElement(el) ) + { + el = el.parentNode; + } + if ( el ) + { + if ( el.style.direction == dir ) + { + el.style.direction = ""; + } + else + { + el.style.direction = dir; + } + } + break; + + case 'justifyleft' : + case 'justifyright' : + cmdID.match(/^justify(.*)$/); + var ae = this.activeElement(this.getSelection()); + if(ae && ae.tagName.toLowerCase() == 'img') + { + ae.align = ae.align == RegExp.$1 ? '' : RegExp.$1; + } + else + { + this._doc.execCommand(cmdID, UI, param); + } + break; + + default: + try + { + this._doc.execCommand(cmdID, UI, param); + } + catch(ex) + { + if ( this.config.debug ) + { + alert(ex + "\n\nby execCommand(" + cmdID + ");"); + } + } + break; + } + + this.updateToolbar(); + return false; +}; + +/** A generic event handler for things that happen in the IFRAME's document.
+ * It provides two hooks for the "firePluginEvent" system:
+ * "onKeyPress"
+ * "onMouseDown" + * @see Xinha#firePluginEvent + * @param {Event} ev + */ +Xinha.prototype._editorEvent = function(ev) +{ + var editor = this; + + //call events of textarea + if ( typeof editor._textArea['on'+ev.type] == "function" ) + { + editor._textArea['on'+ev.type](ev); + } + + if ( this.isKeyEvent(ev) ) + { + // Run the ordinary plugins first + if(editor.firePluginEvent('onKeyPress', ev)) + { + return false; + } + + // Handle the core shortcuts + if ( this.isShortCut( ev ) ) + { + this._shortCuts(ev); + } + } + + if ( ev.type == 'mousedown' ) + { + if(editor.firePluginEvent('onMouseDown', ev)) + { + return false; + } + } + + // update the toolbar state after some time + if ( editor._timerToolbar ) + { + clearTimeout(editor._timerToolbar); + } + if (!this.suspendUpdateToolbar) + { + editor._timerToolbar = setTimeout( + function() + { + editor.updateToolbar(); + editor._timerToolbar = null; + }, + 250); + } +}; + +/** Handle double click events. + * See dblclickList in the config. + */ + +Xinha.prototype._onDoubleClick = function(ev) +{ + var editor=this; + var target = Xinha.is_ie ? ev.srcElement : ev.target; + var tag = target.tagName; + var className = target.className; + if (tag) { + tag = tag.toLowerCase(); + if (className && (this.config.dblclickList[tag+"."+className] != undefined)) + this.config.dblclickList[tag+"."+className][0](editor, target); + else if (this.config.dblclickList[tag] != undefined) + this.config.dblclickList[tag][0](editor, target); + }; +}; + +/** Handles ctrl + key shortcuts + * @TODO: make this mor flexible + * @private + * @param {Event} ev + */ +Xinha.prototype._shortCuts = function (ev) +{ + var key = this.getKey(ev).toLowerCase(); + var cmd = null; + var value = null; + switch (key) + { + // simple key commands follow + + case 'b': cmd = "bold"; break; + case 'i': cmd = "italic"; break; + case 'u': cmd = "underline"; break; + case 's': cmd = "strikethrough"; break; + case 'l': cmd = "justifyleft"; break; + case 'e': cmd = "justifycenter"; break; + case 'r': cmd = "justifyright"; break; + case 'j': cmd = "justifyfull"; break; + case 'z': cmd = "undo"; break; + case 'y': cmd = "redo"; break; + case 'v': cmd = "paste"; break; + case 'n': + cmd = "formatblock"; + value = "p"; + break; + + case '0': cmd = "killword"; break; + + // headings + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + cmd = "formatblock"; + value = "h" + key; + break; + } + if ( cmd ) + { + // execute simple command + this.execCommand(cmd, false, value); + Xinha._stopEvent(ev); + } +}; +/** Changes the type of a given node + * @param {DomNode} el The element to convert + * @param {String} newTagName The type the element will be converted to + * @returns {DomNode} A reference to the new element + */ +Xinha.prototype.convertNode = function(el, newTagName) +{ + var newel = this._doc.createElement(newTagName); + while ( el.firstChild ) + { + newel.appendChild(el.firstChild); + } + return newel; +}; + +/** Scrolls the editor iframe to a given element or to the cursor + * @param {DomNode} e optional The element to scroll to; if ommitted, element the element the cursor is in + */ +Xinha.prototype.scrollToElement = function(e) +{ + if(!e) + { + e = this.getParentElement(); + if(!e) + { + return; + } + } + + // This was at one time limited to Gecko only, but I see no reason for it to be. - James + var position = Xinha.getElementTopLeft(e); + this._iframe.contentWindow.scrollTo(position.left, position.top); +}; + +/** Get the edited HTML + * + * @public + * @returns {String} HTML content + */ +Xinha.prototype.getEditorContent = function() +{ + return this.outwardHtml(this.getHTML()); +}; + +/** Completely change the HTML inside the editor + * + * @public + * @param {String} html new content + */ +Xinha.prototype.setEditorContent = function(html) +{ + this.setHTML(this.inwardHtml(html)); +}; +/** Saves the contents of all Xinhas to their respective textareas + * @public + */ +Xinha.updateTextareas = function() +{ + var e; + for (var i=0;i<__xinhas.length;i++) + { + e = __xinhas[i]; + e._textArea.value = e.getEditorContent(); + } +} +/** Get the raw edited HTML, should not be used without Xinha.prototype.outwardHtml() + * + * @private + * @returns {String} HTML content + */ +Xinha.prototype.getHTML = function() +{ + var html = ''; + switch ( this._editMode ) + { + case "wysiwyg": + if ( !this.config.fullPage ) + { + html = Xinha.getHTML(this._doc.body, false, this).trim(); + } + else + { + html = this.doctype + "\n" + Xinha.getHTML(this._doc.documentElement, true, this); + } + break; + case "textmode": + html = this._textArea.value; + break; + default: + alert("Mode <" + this._editMode + "> not defined!"); + return false; + } + return html; +}; + +/** Performs various transformations of the HTML used internally, complement to Xinha.prototype.inwardHtml() + * Plugins can provide their own, additional transformations by defining a plugin.prototype.outwardHtml() implematation, + * which is called by this function + * + * @private + * @see Xinha#inwardHtml + * @param {String} html + * @returns {String} HTML content + */ +Xinha.prototype.outwardHtml = function(html) +{ + for ( var i in this.plugins ) + { + var plugin = this.plugins[i].instance; + if ( plugin && typeof plugin.outwardHtml == "function" ) + { + html = plugin.outwardHtml(html); + } + } + + html = html.replace(/<(\/?)b(\s|>|\/)/ig, "<$1strong$2"); + html = html.replace(/<(\/?)i(\s|>|\/)/ig, "<$1em$2"); + html = html.replace(/<(\/?)strike(\s|>|\/)/ig, "<$1del$2"); + + // remove disabling of inline event handle inside Xinha iframe + html = html.replace(/(<[^>]*on(click|mouse(over|out|up|down))=['"])if\(window\.parent && window\.parent\.Xinha\)\{return false\}/gi,'$1'); + + // Figure out what our server name is, and how it's referenced + var serverBase = location.href.replace(/(https?:\/\/[^\/]*)\/.*/, '$1') + '/'; + + // IE puts this in can't figure out why + // leaving this in the core instead of InternetExplorer + // because it might be something we are doing so could present itself + // in other browsers - James + html = html.replace(/https?:\/\/null\//g, serverBase); + + // Make semi-absolute links to be truely absolute + // we do this just to standardize so that special replacements knows what + // to expect + html = html.replace(/((href|src|background)=[\'\"])\/+/ig, '$1' + serverBase); + + html = this.outwardSpecialReplacements(html); + + html = this.fixRelativeLinks(html); + + if ( this.config.sevenBitClean ) + { + html = html.replace(/[^ -~\r\n\t]/g, function(c) { return (c != Xinha.cc) ? '&#'+c.charCodeAt(0)+';' : c; }); + } + + //prevent execution of JavaScript (Ticket #685) + html = html.replace(/(]*((type=[\"\']text\/)|(language=[\"\'])))(freezescript)/gi,"$1javascript"); + + // If in fullPage mode, strip the coreCSS + if(this.config.fullPage) + { + html = Xinha.stripCoreCSS(html); + } + + if (typeof this.config.outwardHtml == 'function' ) + { + html = this.config.outwardHtml(html); + } + + return html; +}; + +/** Performs various transformations of the HTML to be edited + * Plugins can provide their own, additional transformations by defining a plugin.prototype.inwardHtml() implematation, + * which is called by this function + * + * @private + * @see Xinha#outwardHtml + * @param {String} html + * @returns {String} transformed HTML + */ +Xinha.prototype.inwardHtml = function(html) +{ + for ( var i in this.plugins ) + { + var plugin = this.plugins[i].instance; + if ( plugin && typeof plugin.inwardHtml == "function" ) + { + html = plugin.inwardHtml(html); + } + } + + // Both IE and Gecko use strike instead of del (#523) + html = html.replace(/<(\/?)del(\s|>|\/)/ig, "<$1strike$2"); + + // disable inline event handle inside Xinha iframe + html = html.replace(/(<[^>]*on(click|mouse(over|out|up|down))=["'])/gi,'$1if(window.parent && window.parent.Xinha){return false}'); + + html = this.inwardSpecialReplacements(html); + + html = html.replace(/(]*((type=[\"\']text\/)|(language=[\"\'])))(javascript)/gi,"$1freezescript"); + + // For IE's sake, make any URLs that are semi-absolute (="/....") to be + // truely absolute + var nullRE = new RegExp('((href|src|background)=[\'"])/+', 'gi'); + html = html.replace(nullRE, '$1' + location.href.replace(/(https?:\/\/[^\/]*)\/.*/, '$1') + '/'); + + html = this.fixRelativeLinks(html); + + // If in fullPage mode, add the coreCSS + if(this.config.fullPage) + { + html = Xinha.addCoreCSS(html); + } + + if (typeof this.config.inwardHtml == 'function' ) + { + html = this.config.inwardHtml(html); + } + + return html; +}; +/** Apply the replacements defined in Xinha.Config.specialReplacements + * + * @private + * @see Xinha#inwardSpecialReplacements + * @param {String} html + * @returns {String} transformed HTML + */ +Xinha.prototype.outwardSpecialReplacements = function(html) +{ + for ( var i in this.config.specialReplacements ) + { + var from = this.config.specialReplacements[i]; + var to = i; // why are declaring a new variable here ? Seems to be better to just do : for (var to in config) + // prevent iterating over wrong type + if ( typeof from.replace != 'function' || typeof to.replace != 'function' ) + { + continue; + } + // alert('out : ' + from + '=>' + to); + var reg = new RegExp(Xinha.escapeStringForRegExp(from), 'g'); + html = html.replace(reg, to.replace(/\$/g, '$$$$')); + //html = html.replace(from, to); + } + return html; +}; +/** Apply the replacements defined in Xinha.Config.specialReplacements + * + * @private + * @see Xinha#outwardSpecialReplacements + * @param {String} html + * @returns {String} transformed HTML + */ +Xinha.prototype.inwardSpecialReplacements = function(html) +{ + // alert("inward"); + for ( var i in this.config.specialReplacements ) + { + var from = i; // why are declaring a new variable here ? Seems to be better to just do : for (var from in config) + var to = this.config.specialReplacements[i]; + // prevent iterating over wrong type + if ( typeof from.replace != 'function' || typeof to.replace != 'function' ) + { + continue; + } + // alert('in : ' + from + '=>' + to); + // + // html = html.replace(reg, to); + // html = html.replace(from, to); + var reg = new RegExp(Xinha.escapeStringForRegExp(from), 'g'); + html = html.replace(reg, to.replace(/\$/g, '$$$$')); // IE uses doubled dollar signs to escape backrefs, also beware that IE also implements $& $_ and $' like perl. + } + return html; +}; +/** Transforms the paths in src & href attributes + * + * @private + * @see Xinha.Config#expandRelativeUrl + * @see Xinha.Config#stripSelfNamedAnchors + * @see Xinha.Config#stripBaseHref + * @see Xinha.Config#baseHref + * @param {String} html + * @returns {String} transformed HTML + */ +Xinha.prototype.fixRelativeLinks = function(html) +{ + if ( typeof this.config.expandRelativeUrl != 'undefined' && this.config.expandRelativeUrl ) + { + if (html == null) + { + return ""; + } + var src = html.match(/(src|href)="([^"]*)"/gi); + var b = document.location.href; + if ( src ) + { + var url,url_m,relPath,base_m,absPath; + for ( var i=0;i not defined!"); + return false; + } + + return html; +}; + +/** Completely change the HTML inside + * + * @private + * @param {String} html new content, should have been run through inwardHtml() first + */ +Xinha.prototype.setHTML = function(html) +{ + if ( !this.config.fullPage ) + { + this._doc.body.innerHTML = html; + } + else + { + this.setFullHTML(html); + } + this._textArea.value = html; +}; + +/** sets the given doctype (useful only when config.fullPage is true) + * + * @private + * @param {String} doctype + */ +Xinha.prototype.setDoctype = function(doctype) +{ + this.doctype = doctype; +}; + +/*************************************************** + * Category: UTILITY FUNCTIONS + ***************************************************/ + +/** Variable used to pass the object to the popup editor window. + * @FIXME: Is this in use? + * @deprecated + * @private + * @type {Object} + */ +Xinha._object = null; + +/** Arrays are identified as "object" in typeof calls. Adding this tag to the Array prototype allows to distinguish between the two + */ +Array.prototype.isArray = true; +/** RegExps are identified as "object" in typeof calls. Adding this tag to the RegExp prototype allows to distinguish between the two + */ +RegExp.prototype.isRegExp = true; +/** function that returns a clone of the given object + * + * @private + * @param {Object} obj + * @returns {Object} cloned object + */ +Xinha.cloneObject = function(obj) +{ + if ( !obj ) + { + return null; + } + var newObj = obj.isArray ? [] : {}; + + // check for function and RegExp objects (as usual, IE is fucked up) + if ( obj.constructor.toString().match( /\s*function Function\(/ ) || typeof obj == 'function' ) + { + newObj = obj; // just copy reference to it + } + else if ( obj.isRegExp ) + { + newObj = eval( obj.toString() ); //see no way without eval + } + else + { + for ( var n in obj ) + { + var node = obj[n]; + if ( typeof node == 'object' ) + { + newObj[n] = Xinha.cloneObject(node); + } + else + { + newObj[n] = node; + } + } + } + + return newObj; +}; + + +/** Extend one class from another, that is, make a sub class. + * This manner of doing it was probably first devised by Kevin Lindsey + * + * http://kevlindev.com/tutorials/javascript/inheritance/index.htm + * + * It has subsequently been used in one form or another by various toolkits + * such as the YUI. + * + * I make no claim as to understanding it really, but it works. + * + * Example Usage: + * {{{ + * ------------------------------------------------------------------------- + + // ========= MAKING THE INITIAL SUPER CLASS =========== + + document.write("

Superclass Creation And Test

"); + + function Vehicle(name, sound) + { + this.name = name; + this.sound = sound + } + + Vehicle.prototype.pressHorn = function() + { + document.write(this.name + ': ' + this.sound + '
'); + } + + var Bedford = new Vehicle('Bedford Van', 'Honk Honk'); + Bedford.pressHorn(); // Vehicle::pressHorn() is defined + + + // ========= MAKING A SUBCLASS OF A SUPER CLASS ========= + + document.write("

Subclass Creation And Test

"); + + // Make the sub class constructor first + Car = function(name) + { + // This is how we call the parent's constructor, note that + // we are using Car.parent.... not "this", we can't use this. + Car.parentConstructor.call(this, name, 'Toot Toot'); + } + + // Remember the subclass comes first, then the base class, you are extending + // Car with the methods and properties of Vehicle. + Xinha.extend(Car, Vehicle); + + var MazdaMx5 = new Car('Mazda MX5'); + MazdaMx5.pressHorn(); // Car::pressHorn() is inherited from Vehicle::pressHorn() + + // ========= ADDING METHODS TO THE SUB CLASS =========== + + document.write("

Add Method to Sub Class And Test

"); + + Car.prototype.isACar = function() + { + document.write(this.name + ": Car::isACar() is implemented, this is a car!
"); + this.pressHorn(); + } + + MazdaMx5.isACar(); // Car::isACar() is defined as above + try { Bedford.isACar(); } // Vehicle::isACar() is not defined, will throw this exception + catch(e) { document.write("Bedford: Vehicle::onGettingCutOff() not implemented, this is not a car!
"); } + + // ========= EXTENDING A METHOD (CALLING MASKED PARENT METHODS) =========== + + document.write("

Extend/Override Inherited Method in Sub Class And Test

"); + + Car.prototype.pressHorn = function() + { + document.write(this.name + ': I am going to press the horn...
'); + Car.superClass.pressHorn.call(this); + } + MazdaMx5.pressHorn(); // Car::pressHorn() + Bedford.pressHorn(); // Vehicle::pressHorn() + + // ========= MODIFYING THE SUPERCLASS AFTER SUBCLASSING =========== + + document.write("

Add New Method to Superclass And Test In Subclass

"); + + Vehicle.prototype.startUp = function() { document.write(this.name + ": Vroooom
"); } + MazdaMx5.startUp(); // Cars get the prototype'd startUp() also. + + * ------------------------------------------------------------------------- + * }}} + * + * @param subclass_constructor (optional) Constructor function for the subclass + * @param superclass Constructor function for the superclass + */ + +Xinha.extend = function(subClass, baseClass) { + function inheritance() {} + inheritance.prototype = baseClass.prototype; + + subClass.prototype = new inheritance(); + subClass.prototype.constructor = subClass; + subClass.parentConstructor = baseClass; + subClass.superClass = baseClass.prototype; +} + +/** Event Flushing + * To try and work around memory leaks in the rather broken + * garbage collector in IE, Xinha.flushEvents can be called + * onunload, it will remove any event listeners (that were added + * through _addEvent(s)) and clear any DOM-0 events. + * @private + * + */ +Xinha.flushEvents = function() +{ + var x = 0; + // @todo : check if Array.prototype.pop exists for every supported browsers + var e = Xinha._eventFlushers.pop(); + while ( e ) + { + try + { + if ( e.length == 3 ) + { + Xinha._removeEvent(e[0], e[1], e[2]); + x++; + } + else if ( e.length == 2 ) + { + e[0]['on' + e[1]] = null; + e[0]._xinha_dom0Events[e[1]] = null; + x++; + } + } + catch(ex) + { + // Do Nothing + } + e = Xinha._eventFlushers.pop(); + } + + /* + // This code is very agressive, and incredibly slow in IE, so I've disabled it. + + if(document.all) + { + for(var i = 0; i < document.all.length; i++) + { + for(var j in document.all[i]) + { + if(/^on/.test(j) && typeof document.all[i][j] == 'function') + { + document.all[i][j] = null; + x++; + } + } + } + } + */ + + // alert('Flushed ' + x + ' events.'); +}; + /** Holds the events to be flushed + * @type Array + */ +Xinha._eventFlushers = []; + +if ( document.addEventListener ) +{ + /** adds an event listener for the specified element and event type + * + * @public + * @see Xinha#_addEvents + * @see Xinha#addDom0Event + * @see Xinha#prependDom0Event + * @param {DomNode} el the DOM element the event should be attached to + * @param {String} evname the name of the event to listen for (without leading "on") + * @param {function} func the function to be called when the event is fired + */ + Xinha._addEvent = function(el, evname, func) + { + el.addEventListener(evname, func, false); + Xinha._eventFlushers.push([el, evname, func]); + }; + + /** removes an event listener previously added + * + * @public + * @see Xinha#_removeEvents + * @param {DomNode} el the DOM element the event should be removed from + * @param {String} evname the name of the event the listener should be removed from (without leading "on") + * @param {function} func the function to be removed + */ + Xinha._removeEvent = function(el, evname, func) + { + el.removeEventListener(evname, func, false); + }; + + /** stops bubbling of the event, if no further listeners should be triggered + * + * @public + * @param {event} ev the event to be stopped + */ + Xinha._stopEvent = function(ev) + { + ev.preventDefault(); + ev.stopPropagation(); + }; +} + /** same as above, for IE + * + */ +else if ( document.attachEvent ) +{ + Xinha._addEvent = function(el, evname, func) + { + el.attachEvent("on" + evname, func); + Xinha._eventFlushers.push([el, evname, func]); + }; + Xinha._removeEvent = function(el, evname, func) + { + el.detachEvent("on" + evname, func); + }; + Xinha._stopEvent = function(ev) + { + try + { + ev.cancelBubble = true; + ev.returnValue = false; + } + catch (ex) + { + // Perhaps we could try here to stop the window.event + // window.event.cancelBubble = true; + // window.event.returnValue = false; + } + }; +} +else +{ + Xinha._addEvent = function(el, evname, func) + { + alert('_addEvent is not supported'); + }; + Xinha._removeEvent = function(el, evname, func) + { + alert('_removeEvent is not supported'); + }; + Xinha._stopEvent = function(ev) + { + alert('_stopEvent is not supported'); + }; +} + /** add several events at once to one element + * + * @public + * @see Xinha#_addEvent + * @param {DomNode} el the DOM element the event should be attached to + * @param {Array} evs the names of the event to listen for (without leading "on") + * @param {function} func the function to be called when the event is fired + */ +Xinha._addEvents = function(el, evs, func) +{ + for ( var i = evs.length; --i >= 0; ) + { + Xinha._addEvent(el, evs[i], func); + } +}; + /** remove several events at once to from element + * + * @public + * @see Xinha#_removeEvent + * @param {DomNode} el the DOM element the events should be remove from + * @param {Array} evs the names of the events the listener should be removed from (without leading "on") + * @param {function} func the function to be removed + */ +Xinha._removeEvents = function(el, evs, func) +{ + for ( var i = evs.length; --i >= 0; ) + { + Xinha._removeEvent(el, evs[i], func); + } +}; + +/** Adds a function that is executed in the moment the DOM is ready, but as opposed to window.onload before images etc. have been loaded +* http://dean.edwards.name/weblog/2006/06/again/ +* IE part from jQuery +* @public +* @author Dean Edwards/Matthias Miller/ John Resig / Diego Perini +* @param {Function} func the function to be executed +* @param {Window} scope the window that is listened to +*/ +Xinha.addOnloadHandler = function (func, scope) +{ + scope = scope ? scope : window; + + var init = function () + { + // quit if this function has already been called + if (arguments.callee.done) + { + return; + } + // flag this function so we don't do the same thing twice + arguments.callee.done = true; + // kill the timer + if (Xinha.onloadTimer) + { + clearInterval(Xinha.onloadTimer); + } + + func(); + }; + if (Xinha.is_ie) + { + // ensure firing before onload, + // maybe late but safe also for iframes + document.attachEvent("onreadystatechange", function(){ + if ( document.readyState === "complete" ) { + document.detachEvent( "onreadystatechange", arguments.callee ); + init(); + } + }); + if ( document.documentElement.doScroll && typeof window.frameElement === "undefined" ) (function(){ + if (arguments.callee.done) return; + try { + // If IE is used, use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + document.documentElement.doScroll("left"); + } catch( error ) { + setTimeout( arguments.callee, 0 ); + return; + } + // and execute any waiting functions + init(); + })(); + } + else if (/applewebkit|KHTML/i.test(navigator.userAgent) ) /* Safari/WebKit/KHTML */ + { + Xinha.onloadTimer = scope.setInterval(function() + { + if (/loaded|complete/.test(scope.document.readyState)) + { + init(); // call the onload handler + } + }, 10); + } + else /* for Mozilla/Opera9 */ + { + scope.document.addEventListener("DOMContentLoaded", init, false); + + } + Xinha._addEvent(scope, 'load', init); // incase anything went wrong +}; + +/** + * Adds a standard "DOM-0" event listener to an element. + * The DOM-0 events are those applied directly as attributes to + * an element - eg element.onclick = stuff; + * + * By using this function instead of simply overwriting any existing + * DOM-0 event by the same name on the element it will trigger as well + * as the existing ones. Handlers are triggered one after the other + * in the order they are added. + * + * Remember to return true/false from your handler, this will determine + * whether subsequent handlers will be triggered (ie that the event will + * continue or be canceled). + * + * @public + * @see Xinha#_addEvent + * @see Xinha#prependDom0Event + * @param {DomNode} el the DOM element the event should be attached to + * @param {String} ev the name of the event to listen for (without leading "on") + * @param {function} fn the function to be called when the event is fired + */ + +Xinha.addDom0Event = function(el, ev, fn) +{ + Xinha._prepareForDom0Events(el, ev); + el._xinha_dom0Events[ev].unshift(fn); +}; + + +/** See addDom0Event, the difference is that handlers registered using + * prependDom0Event will be triggered before existing DOM-0 events of the + * same name on the same element. + * + * @public + * @see Xinha#_addEvent + * @see Xinha#addDom0Event + * @param {DomNode} the DOM element the event should be attached to + * @param {String} the name of the event to listen for (without leading "on") + * @param {function} the function to be called when the event is fired + */ + +Xinha.prependDom0Event = function(el, ev, fn) +{ + Xinha._prepareForDom0Events(el, ev); + el._xinha_dom0Events[ev].push(fn); +}; + +Xinha.getEvent = function(ev) +{ + return ev || window.event; +}; +/** + * Prepares an element to receive more than one DOM-0 event handler + * when handlers are added via addDom0Event and prependDom0Event. + * + * @private + */ +Xinha._prepareForDom0Events = function(el, ev) +{ + // Create a structure to hold our lists of event handlers + if ( typeof el._xinha_dom0Events == 'undefined' ) + { + el._xinha_dom0Events = {}; + Xinha.freeLater(el, '_xinha_dom0Events'); + } + + // Create a list of handlers for this event type + if ( typeof el._xinha_dom0Events[ev] == 'undefined' ) + { + el._xinha_dom0Events[ev] = [ ]; + if ( typeof el['on'+ev] == 'function' ) + { + el._xinha_dom0Events[ev].push(el['on'+ev]); + } + + // Make the actual event handler, which runs through + // each of the handlers in the list and executes them + // in the correct context. + el['on'+ev] = function(event) + { + var a = el._xinha_dom0Events[ev]; + // call previous submit methods if they were there. + var allOK = true; + for ( var i = a.length; --i >= 0; ) + { + // We want the handler to be a member of the form, not the array, so that "this" will work correctly + el._xinha_tempEventHandler = a[i]; + if ( el._xinha_tempEventHandler(event) === false ) + { + el._xinha_tempEventHandler = null; + allOK = false; + break; + } + el._xinha_tempEventHandler = null; + } + return allOK; + }; + + Xinha._eventFlushers.push([el, ev]); + } +}; + +Xinha.prototype.notifyOn = function(ev, fn) +{ + if ( typeof this._notifyListeners[ev] == 'undefined' ) + { + this._notifyListeners[ev] = []; + Xinha.freeLater(this, '_notifyListeners'); + } + this._notifyListeners[ev].push(fn); +}; + +Xinha.prototype.notifyOf = function(ev, args) +{ + if ( this._notifyListeners[ev] ) + { + for ( var i = 0; i < this._notifyListeners[ev].length; i++ ) + { + this._notifyListeners[ev][i](ev, args); + } + } +}; + +/** List of tag names that are defined as block level elements in HTML + * + * @private + * @see Xinha#isBlockElement + * @type {String} + */ +Xinha._blockTags = " body form textarea fieldset ul ol dl li div " + +"p h1 h2 h3 h4 h5 h6 quote pre table thead " + +"tbody tfoot tr td th iframe address blockquote title meta link style head "; + +/** Checks if one element is in the list of elements that are defined as block level elements in HTML + * + * @param {DomNode} el The DOM element to check + * @returns {Boolean} + */ +Xinha.isBlockElement = function(el) +{ + return el && el.nodeType == 1 && (Xinha._blockTags.indexOf(" " + el.tagName.toLowerCase() + " ") != -1); +}; +/** List of tag names that are allowed to contain a paragraph + * + * @private + * @see Xinha#isParaContainer + * @type {String} + */ +Xinha._paraContainerTags = " body td th caption fieldset div "; +/** Checks if one element is in the list of elements that are allowed to contain a paragraph in HTML + * + * @param {DomNode} el The DOM element to check + * @returns {Boolean} + */ +Xinha.isParaContainer = function(el) +{ + return el && el.nodeType == 1 && (Xinha._paraContainerTags.indexOf(" " + el.tagName.toLowerCase() + " ") != -1); +}; + + +/** These are all the tags for which the end tag is not optional or forbidden, taken from the list at: + * http: www.w3.org/TR/REC-html40/index/elements.html + * + * @private + * @see Xinha#needsClosingTag + * @type String + */ +Xinha._closingTags = " a abbr acronym address applet b bdo big blockquote button caption center cite code del dfn dir div dl em fieldset font form frameset h1 h2 h3 h4 h5 h6 i iframe ins kbd label legend map menu noframes noscript object ol optgroup pre q s samp script select small span strike strong style sub sup table textarea title tt u ul var "; + +/** Checks if one element is in the list of elements for which the end tag is not optional or forbidden in HTML + * + * @param {DomNode} el The DOM element to check + * @returns {Boolean} + */ +Xinha.needsClosingTag = function(el) +{ + return el && el.nodeType == 1 && (Xinha._closingTags.indexOf(" " + el.tagName.toLowerCase() + " ") != -1); +}; + +/** Performs HTML encoding of some given string (converts HTML special characters to entities) + * + * @param {String} str The unencoded input + * @returns {String} The encoded output + */ +Xinha.htmlEncode = function(str) +{ + if (!str) + { + return ''; + } if ( typeof str.replace == 'undefined' ) + { + str = str.toString(); + } + // we don't need regexp for that, but.. so be it for now. + str = str.replace(/&/ig, "&"); + str = str.replace(//ig, ">"); + str = str.replace(/\xA0/g, " "); // Decimal 160, non-breaking-space + str = str.replace(/\x22/g, """); + // \x22 means '"' -- we use hex reprezentation so that we don't disturb + // JS compressors (well, at least mine fails.. ;) + return str; +}; + +/** Strips host-part of URL which is added by browsers to links relative to server root + * + * @param {String} string + * @returns {String} + */ +Xinha.prototype.stripBaseURL = function(string) +{ + if ( this.config.baseHref === null || !this.config.stripBaseHref ) + { + return string; + } + var baseurl = this.config.baseHref.replace(/^(https?:\/\/[^\/]+)(.*)$/, '$1'); + var basere = new RegExp(baseurl); + return string.replace(basere, ""); +}; + +if (typeof String.prototype.trim != 'function') +{ + /** Removes whitespace from beginning and end of a string. Custom implementation for JS engines that don't support it natively + * + * @returns {String} + */ + String.prototype.trim = function() + { + return this.replace(/^\s+/, '').replace(/\s+$/, ''); + }; +} + +/** Creates a rgb-style rgb(r,g,b) color from a (24bit) number + * + * @param {Integer} + * @returns {String} rgb(r,g,b) color definition + */ +Xinha._makeColor = function(v) +{ + if ( typeof v != "number" ) + { + // already in rgb (hopefully); IE doesn't get here. + return v; + } + // IE sends number; convert to rgb. + var r = v & 0xFF; + var g = (v >> 8) & 0xFF; + var b = (v >> 16) & 0xFF; + return "rgb(" + r + "," + g + "," + b + ")"; +}; + +/** Returns hexadecimal color representation from a number or a rgb-style color. + * + * @param {String|Integer} v rgb(r,g,b) or 24bit color definition + * @returns {String} #RRGGBB color definition + */ +Xinha._colorToRgb = function(v) +{ + if ( !v ) + { + return ''; + } + var r,g,b; + // @todo: why declaring this function here ? This needs to be a public methode of the object Xinha._colorToRgb + // returns the hex representation of one byte (2 digits) + function hex(d) + { + return (d < 16) ? ("0" + d.toString(16)) : d.toString(16); + } + + if ( typeof v == "number" ) + { + // we're talking to IE here + r = v & 0xFF; + g = (v >> 8) & 0xFF; + b = (v >> 16) & 0xFF; + return "#" + hex(r) + hex(g) + hex(b); + } + + if ( v.substr(0, 3) == "rgb" ) + { + // in rgb(...) form -- Mozilla + var re = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/; + if ( v.match(re) ) + { + r = parseInt(RegExp.$1, 10); + g = parseInt(RegExp.$2, 10); + b = parseInt(RegExp.$3, 10); + return "#" + hex(r) + hex(g) + hex(b); + } + // doesn't match RE?! maybe uses percentages or float numbers + // -- FIXME: not yet implemented. + return null; + } + + if ( v.substr(0, 1) == "#" ) + { + // already hex rgb (hopefully :D ) + return v; + } + + // if everything else fails ;) + return null; +}; + +/** Modal popup dialogs + * + * @param {String} url URL to the popup dialog + * @param {Function} action A function that receives one value; this function will get called + * after the dialog is closed, with the return value of the dialog. + * @param {Mixed} init A variable that is passed to the popup window to pass arbitrary data + */ +Xinha.prototype._popupDialog = function(url, action, init) +{ + Dialog(this.popupURL(url), action, init); +}; + +/** Creates a path in the form _editor_url + "plugins/" + plugin + "/img/" + file + * + * @deprecated + * @param {String} file Name of the image + * @param {String} plugin optional If omitted, simply _editor_url + file is returned + * @returns {String} + */ +Xinha.prototype.imgURL = function(file, plugin) +{ + if ( typeof plugin == "undefined" ) + { + return _editor_url + file; + } + else + { + return Xinha.getPluginDir(plugin) + "/img/" + file; + } +}; +/** Creates a path + * + * @deprecated + * @param {String} file Name of the popup + * @returns {String} + */ +Xinha.prototype.popupURL = function(file) +{ + var url = ""; + if ( file.match(/^plugin:\/\/(.*?)\/(.*)/) ) + { + var plugin = RegExp.$1; + var popup = RegExp.$2; + if ( !/\.(html?|php)$/.test(popup) ) + { + popup += ".html"; + } + url = Xinha.getPluginDir(plugin) + "/popups/" + popup; + } + else if ( file.match(/^\/.*?/) || file.match(/^https?:\/\//)) + { + url = file; + } + else + { + url = _editor_url + this.config.popupURL + file; + } + return url; +}; + + + +/** FIX: Internet Explorer returns an item having the _name_ equal to the given + * id, even if it's not having any id. This way it can return a different form + * field, even if it's not a textarea. This workarounds the problem by + * specifically looking to search only elements having a certain tag name. + * @param {String} tag The tag name to limit the return to + * @param {String} id + * @returns {DomNode} + */ +Xinha.getElementById = function(tag, id) +{ + var el, i, objs = document.getElementsByTagName(tag); + for ( i = objs.length; --i >= 0 && (el = objs[i]); ) + { + if ( el.id == id ) + { + return el; + } + } + return null; +}; + + +/** Use some CSS trickery to toggle borders on tables + * @returns {Boolean} always true + */ + +Xinha.prototype._toggleBorders = function() +{ + var tables = this._doc.getElementsByTagName('TABLE'); + if ( tables.length !== 0 ) + { + if ( !this.borders ) + { + this.borders = true; + } + else + { + this.borders = false; + } + + for ( var i=0; i < tables.length; i++ ) + { + if ( this.borders ) + { + Xinha._addClass(tables[i], 'htmtableborders'); + } + else + { + Xinha._removeClass(tables[i], 'htmtableborders'); + } + } + } + return true; +}; +/** Adds the styles for table borders to the iframe during generation + * + * @private + * @see Xinha#stripCoreCSS + * @param {String} html optional + * @returns {String} html HTML with added styles or only styles if html omitted + */ +Xinha.addCoreCSS = function(html) +{ + var coreCSS = "\n"; + + if( html && //i.test(html)) + { + return html.replace(//i, '' + coreCSS); + } + else if ( html) + { + return coreCSS + html; + } + else + { + return coreCSS; + } +}; +/** Allows plugins to add a stylesheet for internal use to the edited document that won't appear in the HTML output + * + * @see Xinha#stripCoreCSS + * @param {String} stylesheet URL of the styleshett to be added + */ +Xinha.prototype.addEditorStylesheet = function (stylesheet) +{ + var style = this._doc.createElement("link"); + style.rel = 'stylesheet'; + style.type = 'text/css'; + style.title = 'XinhaInternalCSS'; + style.href = stylesheet; + this._doc.getElementsByTagName("HEAD")[0].appendChild(style); +}; +/** Remove internal styles + * + * @private + * @see Xinha#addCoreCSS + * @param {String} html + * @returns {String} + */ +Xinha.stripCoreCSS = function(html) +{ + return html.replace(/]+title="XinhaInternalCSS"(.|\n)*?<\/style>/ig, '').replace(/]+title="XinhaInternalCSS"(.|\n)*?>/ig, ''); +}; +/** Removes one CSS class (that is one of possible more parts + * separated by spaces) from a given element + * + * @see Xinha#_removeClasses + * @param {DomNode} el The DOM element the class will be removed from + * @param {String} className The class to be removed + */ +Xinha._removeClass = function(el, className) +{ + if ( ! ( el && el.className ) ) + { + return; + } + var cls = el.className.split(" "); + var ar = []; + for ( var i = cls.length; i > 0; ) + { + if ( cls[--i] != className ) + { + ar[ar.length] = cls[i]; + } + } + el.className = ar.join(" "); +}; +/** Adds one CSS class to a given element (that is, it expands its className property by the given string, + * separated by a space) + * + * @see Xinha#addClasses + * @param {DomNode} el The DOM element the class will be added to + * @param {String} className The class to be added + */ +Xinha._addClass = function(el, className) +{ + // remove the class first, if already there + Xinha._removeClass(el, className); + el.className += " " + className; +}; + +/** Adds CSS classes to a given element (that is, it expands its className property by the given string, + * separated by a space, thereby checking that no class is doubly added) + * + * @see Xinha#addClass + * @param {DomNode} el The DOM element the classes will be added to + * @param {String} classes The classes to be added + */ +Xinha.addClasses = function(el, classes) +{ + if ( el !== null ) + { + var thiers = el.className.trim().split(' '); + var ours = classes.split(' '); + for ( var x = 0; x < ours.length; x++ ) + { + var exists = false; + for ( var i = 0; exists === false && i < thiers.length; i++ ) + { + if ( thiers[i] == ours[x] ) + { + exists = true; + } + } + if ( exists === false ) + { + thiers[thiers.length] = ours[x]; + } + } + el.className = thiers.join(' ').trim(); + } +}; + +/** Removes CSS classes (that is one or more of possibly several parts + * separated by spaces) from a given element + * + * @see Xinha#_removeClasses + * @param {DomNode} el The DOM element the class will be removed from + * @param {String} className The class to be removed + */ +Xinha.removeClasses = function(el, classes) +{ + var existing = el.className.trim().split(); + var new_classes = []; + var remove = classes.trim().split(); + + for ( var i = 0; i < existing.length; i++ ) + { + var found = false; + for ( var x = 0; x < remove.length && !found; x++ ) + { + if ( existing[i] == remove[x] ) + { + found = true; + } + } + if ( !found ) + { + new_classes[new_classes.length] = existing[i]; + } + } + return new_classes.join(' '); +}; + +/** Alias of Xinha._addClass() + * @see Xinha#_addClass + */ +Xinha.addClass = Xinha._addClass; +/** Alias of Xinha.Xinha._removeClass() + * @see Xinha#_removeClass + */ +Xinha.removeClass = Xinha._removeClass; +/** Alias of Xinha.addClasses() + * @see Xinha#addClasses + */ +Xinha._addClasses = Xinha.addClasses; +/** Alias of Xinha.removeClasses() + * @see Xinha#removeClasses + */ +Xinha._removeClasses = Xinha.removeClasses; + +/** Checks if one element has set the given className + * + * @param {DomNode} el The DOM element to check + * @param {String} className The class to be looked for + * @returns {Boolean} + */ +Xinha._hasClass = function(el, className) +{ + if ( ! ( el && el.className ) ) + { + return false; + } + var cls = el.className.split(" "); + for ( var i = cls.length; i > 0; ) + { + if ( cls[--i] == className ) + { + return true; + } + } + return false; +}; + +/** + * Use XMLHTTPRequest to post some data back to the server and do something + * with the response (asyncronously!), this is used by such things as the tidy + * functions + * @param {String} url The address for the HTTPRequest + * @param {Object} data The data to be passed to the server like {name:"value"} + * @param {Function} success A function that is called when an answer is + * received from the server with the responseText as argument. + * @param {Function} failure A function that is called when we fail to receive + * an answer from the server. We pass it the request object. + */ + +/** mod_security (an apache module which scans incoming requests for potential hack attempts) + * has a rule which triggers when it gets an incoming Content-Type with a charset + * see ticket:1028 to try and work around this, if we get a failure in a postback + * then Xinha._postback_send_charset will be set to false and the request tried again (once) + * @type Boolean + * @private + */ +// +// +// +Xinha._postback_send_charset = true; +/** Use XMLHTTPRequest to send some some data to the server and do something + * with the getback (asyncronously!) + * @param {String} url The address for the HTTPRequest + * @param {Function} success A function that is called when an answer is + * received from the server with the responseText as argument. + * @param {Function} failure A function that is called when we fail to receive + * an answer from the server. We pass it the request object. + */ +Xinha._postback = function(url, data, success, failure) +{ + var req = null; + req = Xinha.getXMLHTTPRequestObject(); + + var content = ''; + if (typeof data == 'string') + { + content = data; + } + else if(typeof data == "object") + { + for ( var i in data ) + { + content += (content.length ? '&' : '') + i + '=' + encodeURIComponent(data[i]); + } + } + + function callBack() + { + if ( req.readyState == 4 ) + { + if ( ((req.status / 100) == 2) || Xinha.isRunLocally && req.status === 0 ) + { + if ( typeof success == 'function' ) + { + success(req.responseText, req); + } + } + else if(Xinha._postback_send_charset) + { + Xinha._postback_send_charset = false; + Xinha._postback(url,data,success, failure); + } + else if (typeof failure == 'function') + { + failure(req); + } + else + { + alert('An error has occurred: ' + req.statusText + '\nURL: ' + url); + } + } + } + + req.onreadystatechange = callBack; + + req.open('POST', url, true); + req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'+(Xinha._postback_send_charset ? '; charset=UTF-8' : '')); + + req.send(content); +}; + +/** Use XMLHTTPRequest to receive some data from the server and do something + * with the it (asyncronously!) + * @param {String} url The address for the HTTPRequest + * @param {Function} success A function that is called when an answer is + * received from the server with the responseText as argument. + * @param {Function} failure A function that is called when we fail to receive + * an answer from the server. We pass it the request object. + */ +Xinha._getback = function(url, success, failure) +{ + var req = null; + req = Xinha.getXMLHTTPRequestObject(); + + function callBack() + { + if ( req.readyState == 4 ) + { + if ( ((req.status / 100) == 2) || Xinha.isRunLocally && req.status === 0 ) + { + success(req.responseText, req); + } + else if (typeof failure == 'function') + { + failure(req); + } + else + { + alert('An error has occurred: ' + req.statusText + '\nURL: ' + url); + } + } + } + + req.onreadystatechange = callBack; + req.open('GET', url, true); + req.send(null); +}; + +Xinha.ping = function(url, successHandler, failHandler) +{ + var req = null; + req = Xinha.getXMLHTTPRequestObject(); + + function callBack() + { + if ( req.readyState == 4 ) + { + if ( ((req.status / 100) == 2) || Xinha.isRunLocally && req.status === 0 ) + { + if (successHandler) + { + successHandler(req); + } + } + else + { + if (failHandler) + { + failHandler(req); + } + } + } + } + + // Opera seems to have some problems mixing HEAD requests with GET requests. + // The GET is slower, so it's a net slowdown for Opera, but it keeps things + // from breaking. + var method = 'GET'; + req.onreadystatechange = callBack; + req.open(method, url, true); + req.send(null); +}; + +/** Use XMLHTTPRequest to receive some data from the server syncronously + * @param {String} url The address for the HTTPRequest + */ +Xinha._geturlcontent = function(url, returnXML) +{ + var req = null; + req = Xinha.getXMLHTTPRequestObject(); + + // Synchronous! + req.open('GET', url, false); + req.send(null); + if ( ((req.status / 100) == 2) || Xinha.isRunLocally && req.status === 0 ) + { + return (returnXML) ? req.responseXML : req.responseText; + } + else + { + return ''; + } +}; + +// Unless somebody already has, make a little function to debug things + +if (typeof dumpValues == 'undefined') +{ + dumpValues = function(o) + { + var s = ''; + for (var prop in o) + { + if (window.console && typeof window.console.log == 'function') + { + if (typeof console.firebug != 'undefined') + { + console.log(o); + } + else + { + console.log(prop + ' = ' + o[prop] + '\n'); + } + } + else + { + s += prop + ' = ' + o[prop] + '\n'; + } + } + if (s) + { + if (document.getElementById('errors')) + { + document.getElementById('errors').value += s; + } + else + { + var x = window.open("", "debugger"); + x.document.write('
' + s + '
'); + } + + } + }; +} +if ( !Array.prototype.contains ) +{ + /** Walks through an array and checks if the specified item exists in it + * @param {String} needle The string to search for + * @returns {Boolean} True if item found, false otherwise + */ + Array.prototype.contains = function(needle) + { + var haystack = this; + for ( var i = 0; i < haystack.length; i++ ) + { + if ( needle == haystack[i] ) + { + return true; + } + } + return false; + }; +} + +if ( !Array.prototype.indexOf ) +{ + /** Walks through an array and, if the specified item exists in it, returns the position + * @param {String} needle The string to search for + * @returns {Integer|null} Index position if item found, null otherwise + */ + Array.prototype.indexOf = function(needle) + { + var haystack = this; + for ( var i = 0; i < haystack.length; i++ ) + { + if ( needle == haystack[i] ) + { + return i; + } + } + return null; + }; +} +if ( !Array.prototype.append ) +{ + /** Adds an item to an array + * @param {Mixed} a Item to add + * @returns {Array} The array including the newly added item + */ + Array.prototype.append = function(a) + { + for ( var i = 0; i < a.length; i++ ) + { + this.push(a[i]); + } + return this; + }; +} +/** Executes a provided function once per array element. + * Custom implementation for JS engines that don't support it natively + * @source http://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Global_Objects/Array/ForEach + * @param {Function} fn Function to execute for each element + * @param {Object} thisObject Object to use as this when executing callback. + */ +if (!Array.prototype.forEach) +{ + Array.prototype.forEach = function(fn /*, thisObject*/) + { + var len = this.length; + if (typeof fn != "function") + { + throw new TypeError(); + } + + var thisObject = arguments[1]; + for (var i = 0; i < len; i++) + { + if (i in this) + { + fn.call(thisObject, this[i], i, this); + } + } + }; +} +/** Returns all elements within a given class name inside an element + * @type Array + * @param {DomNode|document} el wherein to search + * @param {Object} className + */ +Xinha.getElementsByClassName = function(el,className) +{ + if (el.getElementsByClassName) + { + return Array.prototype.slice.call(el.getElementsByClassName(className)); + } + else + { + var els = el.getElementsByTagName('*'); + var result = []; + var classNames; + for (var i=0;ia2 are also contained in a1 (at least I think this is what it does) +* @param {Array} a1 +* @param {Array} a2 +* @returns {Boolean} +*/ +Xinha.arrayContainsArray = function(a1, a2) +{ + var all_found = true; + for ( var x = 0; x < a2.length; x++ ) + { + var found = false; + for ( var i = 0; i < a1.length; i++ ) + { + if ( a1[i] == a2[x] ) + { + found = true; + break; + } + } + if ( !found ) + { + all_found = false; + break; + } + } + return all_found; +}; +/** Walks through an array and applies a filter function to each item +* @param {Array} a1 The array to filter +* @param {Function} filterfn If this function returns true, the item is added to the new array +* @returns {Array} Filtered array +*/ +Xinha.arrayFilter = function(a1, filterfn) +{ + var new_a = [ ]; + for ( var x = 0; x < a1.length; x++ ) + { + if ( filterfn(a1[x]) ) + { + new_a[new_a.length] = a1[x]; + } + } + return new_a; +}; +/** Converts a Collection object to an array +* @param {Collection} collection The array to filter +* @returns {Array} Array containing the item of collection +*/ +Xinha.collectionToArray = function(collection) +{ + try + { + return collection.length ? Array.prototype.slice.call(collection) : []; //Collection to Array + } + catch(e) + { + // In certain implementations (*cough* IE), you can't call slice on a + // collection. We'll fallback to using the simple, non-native iterative + // approach. + } + + var array = [ ]; + for ( var i = 0; i < collection.length; i++ ) + { + array.push(collection.item(i)); + } + return array; +}; + +/** Index for Xinha.uniq function +* @private +*/ +Xinha.uniq_count = 0; +/** Returns a string that is unique on the page +* @param {String} prefix This string is prefixed to a running number +* @returns {String} +*/ +Xinha.uniq = function(prefix) +{ + return prefix + Xinha.uniq_count++; +}; + +// New language handling functions + +/** Load a language file. + * This function should not be used directly, Xinha._lc will use it when necessary. + * @private + * @param {String} context Case sensitive context name, eg 'Xinha', 'TableOperations', ... + * @returns {Object} + */ +Xinha._loadlang = function(context,url) +{ + var lang; + + if ( typeof _editor_lcbackend == "string" ) + { + //use backend + url = _editor_lcbackend; + url = url.replace(/%lang%/, _editor_lang); + url = url.replace(/%context%/, context); + } + else if (!url) + { + //use internal files + if ( context != 'Xinha') + { + url = Xinha.getPluginDir(context)+"/lang/"+_editor_lang+".js"; + } + else + { + Xinha.setLoadingMessage("Loading language"); + url = _editor_url+"lang/"+_editor_lang+".js"; + } + } + + var langData = Xinha._geturlcontent(url); + if ( langData !== "" ) + { + try + { + eval('lang = ' + langData); + } + catch(ex) + { + alert('Error reading Language-File ('+url+'):\n'+Error.toString()); + lang = {}; + } + } + else + { + lang = {}; + } + + return lang; +}; + +/** Return a localised string. + * @param {String} string English language string. It can also contain variables in the form "Some text with $variable=replaced text$". + * This replaces $variable in "Some text with $variable" with "replaced text" + * @param {String} context Case sensitive context name, eg 'Xinha' (default), 'TableOperations'... + * @param {Object} replace Replace $variables in String, eg {foo: 'replaceText'} ($foo in string will be replaced by replaceText) + */ +Xinha._lc = function(string, context, replace) +{ + var url,ret; + if (typeof context == 'object' && context.url && context.context) + { + url = context.url + _editor_lang + ".js"; + context = context.context; + } + + var m = null; + if (typeof string == 'string') + { + m = string.match(/\$(.*?)=(.*?)\$/g); + } + if (m) + { + if (!replace) + { + replace = {}; + } + for (var i = 0;i> 2; + enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); + enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); + enc4 = chr3 & 63; + + if ( isNaN(chr2) ) + { + enc3 = enc4 = 64; + } + else if ( isNaN(chr3) ) + { + enc4 = 64; + } + + output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2) + keyStr.charAt(enc3) + keyStr.charAt(enc4); + } while ( i < input.length ); + + return output; +}; + +/** Utility function to base64_decode some arbitrary data, uses the builtin atob() if it exists (Moz) + * @param {String} input + * @returns {String} + */ +Xinha.base64_decode = function(input) +{ + var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + var output = ""; + var chr1, chr2, chr3; + var enc1, enc2, enc3, enc4; + var i = 0; + + // remove all characters that are not A-Z, a-z, 0-9, +, /, or = + input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); + + do + { + enc1 = keyStr.indexOf(input.charAt(i++)); + enc2 = keyStr.indexOf(input.charAt(i++)); + enc3 = keyStr.indexOf(input.charAt(i++)); + enc4 = keyStr.indexOf(input.charAt(i++)); + + chr1 = (enc1 << 2) | (enc2 >> 4); + chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); + chr3 = ((enc3 & 3) << 6) | enc4; + + output = output + String.fromCharCode(chr1); + + if ( enc3 != 64 ) + { + output = output + String.fromCharCode(chr2); + } + if ( enc4 != 64 ) + { + output = output + String.fromCharCode(chr3); + } + } while ( i < input.length ); + + return output; +}; +/** Removes a node from the DOM + * @param {DomNode} el The element to be removed + * @returns {DomNode} The removed element + */ +Xinha.removeFromParent = function(el) +{ + if ( !el.parentNode ) + { + return; + } + var pN = el.parentNode; + return pN.removeChild(el); +}; +/** Checks if some element has a parent node + * @param {DomNode} el + * @returns {Boolean} + */ +Xinha.hasParentNode = function(el) +{ + if ( el.parentNode ) + { + // When you remove an element from the parent in IE it makes the parent + // of the element a document fragment. Moz doesn't. + if ( el.parentNode.nodeType == 11 ) + { + return false; + } + return true; + } + + return false; +}; + +/** Detect the size of visible area + * @param {Window} scope optional When calling from a popup window, pass its window object to get the values of the popup + * @returns {Object} Object with Integer properties x and y + */ +Xinha.viewportSize = function(scope) +{ + scope = (scope) ? scope : window; + var x,y; + if (scope.innerHeight) // all except Explorer + { + x = scope.innerWidth; + y = scope.innerHeight; + } + else if (scope.document.documentElement && scope.document.documentElement.clientHeight) + // Explorer 6 Strict Mode + { + x = scope.document.documentElement.clientWidth; + y = scope.document.documentElement.clientHeight; + } + else if (scope.document.body) // other Explorers + { + x = scope.document.body.clientWidth; + y = scope.document.body.clientHeight; + } + return {'x':x,'y':y}; +}; +/** Detect the size of the whole document + * @param {Window} scope optional When calling from a popup window, pass its window object to get the values of the popup + * @returns {Object} Object with Integer properties x and y + */ +Xinha.pageSize = function(scope) +{ + scope = (scope) ? scope : window; + var x,y; + + var test1 = scope.document.body.scrollHeight; //IE Quirks + var test2 = scope.document.documentElement.scrollHeight; // IE Standard + Moz Here quirksmode.org errs! + + if (test1 > test2) + { + x = scope.document.body.scrollWidth; + y = scope.document.body.scrollHeight; + } + else + { + x = scope.document.documentElement.scrollWidth; + y = scope.document.documentElement.scrollHeight; + } + return {'x':x,'y':y}; +}; +/** Detect the current scroll position + * @param {Window} scope optional When calling from a popup window, pass its window object to get the values of the popup + * @returns {Object} Object with Integer properties x and y + */ +Xinha.prototype.scrollPos = function(scope) +{ + scope = (scope) ? scope : window; + var x,y; + if (typeof scope.pageYOffset != 'undefined') // all except Explorer + { + x = scope.pageXOffset; + y = scope.pageYOffset; + } + else if (scope.document.documentElement && typeof document.documentElement.scrollTop != 'undefined') + // Explorer 6 Strict + { + x = scope.document.documentElement.scrollLeft; + y = scope.document.documentElement.scrollTop; + } + else if (scope.document.body) // all other Explorers + { + x = scope.document.body.scrollLeft; + y = scope.document.body.scrollTop; + } + return {'x':x,'y':y}; +}; + +/** Calculate the top and left pixel position of an element in the DOM. + * @param {DomNode} element HTML Element + * @returns {Object} Object with Integer properties top and left + */ + +Xinha.getElementTopLeft = function(element) +{ + var curleft = 0; + var curtop = 0; + if (element.offsetParent) + { + curleft = element.offsetLeft; + curtop = element.offsetTop; + while (element = element.offsetParent) + { + curleft += element.offsetLeft; + curtop += element.offsetTop; + } + } + return { top:curtop, left:curleft }; +}; +/** Find left pixel position of an element in the DOM. + * @param {DomNode} element HTML Element + * @returns {Integer} + */ +Xinha.findPosX = function(obj) +{ + var curleft = 0; + if ( obj.offsetParent ) + { + return Xinha.getElementTopLeft(obj).left; + } + else if ( obj.x ) + { + curleft += obj.x; + } + return curleft; +}; +/** Find top pixel position of an element in the DOM. + * @param {DomNode} element HTML Element + * @returns {Integer} + */ +Xinha.findPosY = function(obj) +{ + var curtop = 0; + if ( obj.offsetParent ) + { + return Xinha.getElementTopLeft(obj).top; + } + else if ( obj.y ) + { + curtop += obj.y; + } + return curtop; +}; + +Xinha.createLoadingMessages = function(xinha_editors) +{ + if ( Xinha.loadingMessages || !Xinha.isSupportedBrowser ) + { + return; + } + Xinha.loadingMessages = []; + + for (var i=0;i, + * which for Xinha is a shortcut. Note that CTRL-ALT- is not a shortcut. + * + * @param {Event} keyEvent + * @returns {Boolean} + */ + +Xinha.prototype.isShortCut = function(keyEvent) +{ + if(keyEvent.ctrlKey && !keyEvent.altKey) + { + return true; + } + + return false; +}; + +/** Return the character (as a string) of a keyEvent - ie, press the 'a' key and + * this method will return 'a', press SHIFT-a and it will return 'A'. + * + * @param {Event} keyEvent + * @returns {String} + */ + +Xinha.prototype.getKey = function(keyEvent) { Xinha.notImplemented("getKey"); }; + +/** Return the HTML string of the given Element, including the Element. + * + * @param {DomNode} element HTML Element + * @returns {String} + */ + +Xinha.getOuterHTML = function(element) { Xinha.notImplemented("getOuterHTML"); }; + +/** Get a new XMLHTTPRequest Object ready to be used. + * + * @returns {XMLHTTPRequest} + */ + +Xinha.getXMLHTTPRequestObject = function() +{ + try + { + if (typeof XMLHttpRequest != "undefined" && typeof XMLHttpRequest.constructor == 'function' ) // Safari's XMLHttpRequest is typeof object + { + return new XMLHttpRequest(); + } + else if (typeof ActiveXObject == "function") + { + return new ActiveXObject("Microsoft.XMLHTTP"); + } + } + catch(e) + { + Xinha.notImplemented('getXMLHTTPRequestObject'); + } +}; + +// Compatability - all these names are deprecated and will be removed in a future version +/** Alias of activeElement() + * @see Xinha#activeElement + * @deprecated + * @returns {DomNode|null} + */ +Xinha.prototype._activeElement = function(sel) { return this.activeElement(sel); }; +/** Alias of selectionEmpty() + * @see Xinha#selectionEmpty + * @deprecated + * @param {Selection} sel Selection object as returned by getSelection + * @returns {Boolean} + */ +Xinha.prototype._selectionEmpty = function(sel) { return this.selectionEmpty(sel); }; +/** Alias of getSelection() + * @see Xinha#getSelection + * @deprecated + * @returns {Selection} + */ +Xinha.prototype._getSelection = function() { return this.getSelection(); }; +/** Alias of createRange() + * @see Xinha#createRange + * @deprecated + * @param {Selection} sel Selection object + * @returns {Range} + */ +Xinha.prototype._createRange = function(sel) { return this.createRange(sel); }; +HTMLArea = Xinha; + +//what is this for? Do we need it? +Xinha.init(); + +if ( Xinha.ie_version < 8 ) +{ + Xinha.addDom0Event(window,'unload',Xinha.collectGarbageForIE); +} +/** Print some message to Firebug, Webkit, Opera, or IE8 console + * + * @param {String} text + * @param {String} level one of 'warn', 'info', or empty + */ +Xinha.debugMsg = function(text, level) +{ + if (typeof console != 'undefined' && typeof console.log == 'function') + { + if (level && level == 'warn' && typeof console.warn == 'function') + { + console.warn(text); + } + else + if (level && level == 'info' && typeof console.info == 'function') + { + console.info(text); + } + else + { + console.log(text); + } + } + else if (typeof opera != 'undefined' && typeof opera.postError == 'function') + { + opera.postError(text); + } +}; +Xinha.notImplemented = function(methodName) +{ + throw new Error("Method Not Implemented", "Part of Xinha has tried to call the " + methodName + " method which has not been implemented."); +}; Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/XinhaLoader.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/XinhaLoader.js,v diff -u -r1.3 -r1.4 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/XinhaLoader.js 27 Mar 2009 08:20:42 -0000 1.3 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/XinhaLoader.js 23 May 2010 11:58:33 -0000 1.4 @@ -1,3 +1,283 @@ -/* 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 */ -var Xinha={};if(!window._editor_url){var scripts=document.getElementsByTagName("script");var this_script=scripts[scripts.length-1];var args=this_script.src.split("?");args=args.length==2?args[1].split("&"):"";for(var index=0;index=9.1)||Xinha.ie_version>=5.5||Xinha.is_safari;Xinha.loadPlugins=function(a,b){if(!Xinha.isSupportedBrowser){return}Xinha.loadStyle(typeof _editor_css=="string"?_editor_css:"Xinha.css","XinhaCoreDesign");Xinha.createLoadingMessages(xinha_editors);var c=Xinha.loadingMessages;Xinha._loadback(_editor_url+"XinhaCore.js",function(){Xinha.removeLoadingMessages(xinha_editors);Xinha.createLoadingMessages(xinha_editors);b()});return false};Xinha._loadback=function(f,b,a,e){var c=!Xinha.is_ie?"onload":"onreadystatechange";var d=document.createElement("script");d.type="text/javascript";d.src=f;if(b){d[c]=function(){if(Xinha.is_ie&&(!(/loaded|complete/.test(window.event.srcElement.readyState)))){return}b.call(a?a:this,e);d[c]=null}}document.getElementsByTagName("head")[0].appendChild(d)};Xinha.getElementTopLeft=function(a){var c=0;var b=0;if(a.offsetParent){c=a.offsetLeft;b=a.offsetTop;while(a=a.offsetParent){c+=a.offsetLeft;b+=a.offsetTop}}return{top:b,left:c}};Xinha.findPosX=function(a){var b=0;if(a.offsetParent){return Xinha.getElementTopLeft(a).left}else{if(a.x){b+=a.x}}return b};Xinha.findPosY=function(b){var a=0;if(b.offsetParent){return Xinha.getElementTopLeft(b).top}else{if(b.y){a+=b.y}}return a};Xinha.createLoadingMessages=function(b){if(Xinha.loadingMessages||!Xinha.isSupportedBrowser){return}Xinha.loadingMessages=[];for(var a=0;a= 9.1) || Xinha.ie_version >= 5.5 || Xinha.is_safari; + +Xinha.loadPlugins = function(plugins, callbackIfNotReady) +{ + if ( !Xinha.isSupportedBrowser ) return; + + Xinha.loadStyle(typeof _editor_css == "string" ? _editor_css : "Xinha.css","XinhaCoreDesign"); + Xinha.createLoadingMessages(xinha_editors); + var loadingMessages = Xinha.loadingMessages; + Xinha._loadback(_editor_url + "XinhaCore.js",function () { + Xinha.removeLoadingMessages(xinha_editors); + Xinha.createLoadingMessages(xinha_editors); + callbackIfNotReady() + }); + return false; +} + +Xinha._loadback = function(Url, Callback, Scope, Bonus) +{ + var T = !Xinha.is_ie ? "onload" : 'onreadystatechange'; + var S = document.createElement("script"); + S.type = "text/javascript"; + S.src = Url; + if ( Callback ) + { + S[T] = function() + { + if ( Xinha.is_ie && ( ! ( /loaded|complete/.test(window.event.srcElement.readyState) ) ) ) + { + return; + } + + Callback.call(Scope ? Scope : this, Bonus); + S[T] = null; + }; + } + document.getElementsByTagName("head")[0].appendChild(S); +}; + +Xinha.getElementTopLeft = function(element) +{ + var curleft = 0; + var curtop = 0; + if (element.offsetParent) + { + curleft = element.offsetLeft + curtop = element.offsetTop + while (element = element.offsetParent) + { + curleft += element.offsetLeft + curtop += element.offsetTop + } + } + return { top:curtop, left:curleft }; +} + +// find X position of an element +Xinha.findPosX = function(obj) +{ + var curleft = 0; + if ( obj.offsetParent ) + { + return Xinha.getElementTopLeft(obj).left; + } + else if ( obj.x ) + { + curleft += obj.x; + } + return curleft; +}; + +// find Y position of an element +Xinha.findPosY = function(obj) +{ + var curtop = 0; + if ( obj.offsetParent ) + { + return Xinha.getElementTopLeft(obj).top; + } + else if ( obj.y ) + { + curtop += obj.y; + } + return curtop; +}; + +Xinha.createLoadingMessages = function(xinha_editors) +{ + if ( Xinha.loadingMessages || !Xinha.isSupportedBrowser ) + { + return; + } + Xinha.loadingMessages = []; + + for (var i=0;i"; + +$prefix = "/* This compressed file is part of Xinha. For uncompressed sources, forum, and bug reports, go to xinha.org */"; +if ($version) $prefix .= "\n/* This file is part of version $version released $date */"; + +$core_prefix = ' + /*-------------------------------------------------------------------------- + -- Xinha (is not htmlArea) - http://xinha.org + -- + -- Use of Xinha is granted by the terms of the htmlArea License (based on + -- BSD license) please read license.txt in this package for details. + -- + -- Copyright (c) 2005-2008 Xinha Developer Team and contributors + -- + -- Xinha was originally based on work by Mihai Bazon which is: + -- Copyright (c) 2003-2004 dynarch.com. + -- Copyright (c) 2002-2003 interactivetools.com, inc. + -- This copyright notice MUST stay intact for use. + -------------------------------------------------------------------------*/ +'; +foreach ($return as $file) +{ + set_time_limit ( 60 ); + print "Processed $file
"; + flush(); + $file_url = $repository_url.str_replace($root_dir,'',$file); + + copy($file,$file."_uncompr.js"); + + $file_prefix = $prefix."\n/* The URL of the most recent version of this file is $file_url */"; + + exec("echo \"".(preg_match('/XinhaCore.js$/',$file) ? $file_prefix.$core_prefix : $file_prefix)."\" > $file && java -jar ${cwd}/dojo_js_compressor.jar -c ${file}_uncompr.js >> $file 2>&1"); + if (preg_match('/js: ".*?", line \d+:/',file_get_contents($file)) || preg_match('/sh: java: command not found/', file_get_contents($file))) + { + unlink($file); + rename($file."_uncompr.js",$file); + } + else + { + unlink($file."_uncompr.js"); + } + +} +print "Operation complete." +?> Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/contrib/compress_yui.php =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/contrib/compress_yui.php,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/contrib/compress_yui.php 23 May 2010 11:58:33 -0000 1.1 @@ -0,0 +1,107 @@ +"; + +$prefix = "/* This compressed file is part of Xinha. For uncompressed sources, forum, and bug reports, go to xinha.org */"; +if ($version) $prefix .= "\n/* This file is part of version $version released $date */"; +$core_prefix = ' + /*-------------------------------------------------------------------------- + -- Xinha (is not htmlArea) - http://xinha.org + -- + -- Use of Xinha is granted by the terms of the htmlArea License (based on + -- BSD license) please read license.txt in this package for details. + -- + -- Copyright (c) 2005-'.date('Y').' Xinha Developer Team and contributors + -- + -- Xinha was originally based on work by Mihai Bazon which is: + -- Copyright (c) 2003-2004 dynarch.com. + -- Copyright (c) 2002-2003 interactivetools.com, inc. + -- This copyright notice MUST stay intact for use. + -------------------------------------------------------------------------*/ +'; + +foreach ($return as $file) +{ + set_time_limit ( 60 ); + print "Processing $file\n"; + flush(); + + $file_url = $repository_url.str_replace($xinha_root,'',$file); + + copy($file,$file."_uncompr.js"); + + $file_prefix = $prefix."\n/* The URL of the most recent uncompressed version of this file is $file_url */"; + $ext = preg_replace('/.*?(\.js|\.css)$/','$1',$file); + + file_put_contents($file."_uncompr${ext}", preg_replace('/(\/\/[^\n]*)?(?![*])\\\[\n]/','',file_get_contents($file))); + + passthru("echo \"".(preg_match('/XinhaCore.js$/',$file) ? $file_prefix.$core_prefix : $prefix)."\" > $file && java -jar {$xinha_root}/contrib/yuicompressor-2.4.2.jar --charset UTF-8 ${file}_uncompr${ext} >> $file 2>&1"); + if (preg_match('/\d+:\d+:syntax error/',file_get_contents($file)) || preg_match('/sh: java: command not found/', file_get_contents($file))) + { + unlink($file); + rename($file."_uncompr${ext}",$file); + } + else + { + unlink($file."_uncompr${ext}"); + } + +} +print "Operation complete." +?> Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/contrib/lc_merge_strings.php =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/contrib/lc_merge_strings.php,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/contrib/lc_merge_strings.php 23 May 2010 11:58:33 -0000 1.1 @@ -0,0 +1,178 @@ +, 2009-08-16 + +// Distributed under the same terms as Xinha itself. +// This notice MUST stay intact for use (see license.txt). + +// merge-strings.php - merge translation strings from master into +// individual language files. + + +function usage() +{ + print "merge-strings.php - merge translation strings\n"; + print "Options:\n"; + print " -l xx Process language xx. Required option\n"; + print " -m master Master file. Defaults to 'de.js'\n"; + print " -m base Base directory. Defaults to '..'\n"; + print " -v Verbose\n"; + print " -c Tell about files that must be created\n"; + print " -d Debug. Very noisy\n"; + exit(1); +} + +// This function taken from the php.net manual page for glob + +function rglob($pattern='*', $flags = 0, $path='') +{ + $paths=glob($path.'*', GLOB_MARK|GLOB_ONLYDIR|GLOB_NOSORT); + $files=glob($path.$pattern, $flags); + foreach ($paths as $path) + { + $files=array_merge($files,rglob($pattern, $flags, $path)); + } + return $files; +} + +error_reporting(E_ALL); + +$opts = getopt('l:b:m:cvd'); + +if ($opts === false) usage; +// here we should check extra options, but php lacks that functionality + +$lang = 'xx'; // The language we process +$create = 0; // Tell about missing files? +$verbose = 0; // Log the details to stdout? +$debug = 0; // ? +$basedir = '..'; // Base directory to process +$mastername = 'de.js'; // The best bet on a complete translation file + +if (isset($opts['l'])) $lang = $opts['l']; +else die("Missing -l option\n"); +if (isset($opts['c'])) $create = 1; +if (isset($opts['v'])) $verbose = 1; +if (isset($opts['d'])) $debug = 1; +if (isset($opts['b'])) $basedir = $opts['b']; +if (isset($opts['m'])) $mastername = $opts['m']; + +if (!preg_match('#/$#', $basedir)) $basedir .= '/'; + +// So, find all the master files + +$files = rglob($mastername, 0, $basedir); +if (count($files) == 0) +{ + print "No master files found. Check your -b and -m options!\n"; + exit(1); +} + +// and process them + +$filenum = 0; +foreach ($files as $masterjs) +{ + $langjs = preg_replace("/$mastername/", "$lang.js", $masterjs); + $langnew = $langjs.'.new'; + + if (!file_exists($langjs)) + { + if ($create) print "Missing file: $langjs\n"; + continue; + } + + // Populate $trans with the strings that must be translated + + $filenum++; + $min = fopen($masterjs, "r"); + $trans = array(); + $strings = 0; + while ($str = fgets($min)) + { + $str = trim($str); + if (preg_match('#^ *"([^"]*)" *: *"([^"]*)"(,)? *(//.*)?$#', $str, $m)) + { + if (isset($trans[$m[1]])) + { + print "Duplicate string in $masterjs: $m[1]\n"; + continue; + } + if ($debug) print "Translate: $m[1]\n"; + $trans[$m[1]] = 1; + $strings++; + } + } + fclose($min); + + // Now copy from $lin to $lout while verifying that the strings + // are still current. + // Break out when we hit the last string in the master (no ',' + // after the translation. + + $lin = fopen($langjs, "r"); + $lout = fopen($langnew, "w"); + $obsolete = 0; + $new = 0; + $kept = 0; + while ($fstr = fgets($lin)) + { + $str = trim($fstr); + if (preg_match('#^ *"([^"]*)" *: *"([^"]*)"(,)? *(//.*)?$#', $str, $m)) + { + if (!isset($trans[$m[1]])) + { + if ($verbose) print "Obsolete: $m[1]\n"; + $obsolete++; + fprintf($lout, " // \"%s\": \"%s\" // OBSOLETE\n", $m[1], $m[2]); + } + else + { + if ($debug) print "Keep: $m[1]\n"; + unset($trans[$m[1]]); + $strings--; + $kept++; + fprintf($lout, " \"%s\": \"%s\"%s\n", $m[1], $m[2], $strings ? ',' : ''); + } + if (!isset($m[3]) || $m[3] != ',') + break; + } + else + fprintf($lout, "%s", $fstr); + } + + // Add the strings that are missing + + foreach ($trans as $tr => $v) + { + if ($verbose) print("New: $tr\n"); + $new++; + $strings--; + fprintf($lout, " \"%s\": \"%s\"%s // NEW\n", $tr, $tr, $strings ? ',' : ''); + } + + // And then the final part of $lin + + while ($str = fgets($lin)) + fprintf($lout, "%s", $str); + + // Clean up, and tell user what happened + + fclose($lin); + fclose($lout); + + if ($obsolete == 0 && $new == 0) + { + if ($verbose) print "$langjs: Unchanged\n"; + unlink($langnew); + } + else + { + print "$langnew: $new new, $obsolete obsoleted, $kept unchanged entries.\n"; + // rename($langnew, $langjs); + } +} + +print "$filenum files processed.\n"; + +?> Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/contrib/php-xinha.php =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/contrib/php-xinha.php,v diff -u -r1.2 -r1.3 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/contrib/php-xinha.php 13 May 2008 18:53:45 -0000 1.2 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/contrib/php-xinha.php 23 May 2010 11:58:33 -0000 1.3 @@ -81,6 +81,11 @@ return "'" . xinha_js_encode($var) . "'"; } + if(is_bool($var)) + { + return $var ? 'true': 'false'; + } + if(is_array($var)) { $useObject = false; @@ -138,7 +143,7 @@ * if none was passed or a security error was encountered. */ - function xinha_read_passed_data() + function xinha_read_passed_data($KeyLocation = 'Xinha:BackendKey') { if(isset($_REQUEST['backend_data']) && is_array($_REQUEST['backend_data'])) { @@ -147,6 +152,12 @@ @session_start(); if(!isset($_SESSION[$bk['key_location']])) return NULL; + if($KeyLocation !== $bk['key_location']) + { + trigger_error('Programming Error - please contact the website administrator/programmer to alert them to this problem. A non-default backend key location is being used to pass backend data to Xinha, but the same key location is not being used to receive data. The special backend configuration has been ignored. To resolve this, find where you are using xinha_pass_to_php_backend and remove the non default key, or find the locations where xinha_read_passed_data is used (in Xinha) and add a parameter with the non default key location, or edit contrib/php-xinha.php and change the default key location in both these functions. See: http://trac.xinha.org/ticket/1518', E_USER_ERROR); + return NULL; + } + if($bk['hash'] === function_exists('sha1') ? sha1($_SESSION[$bk['key_location']] . $bk['data']) Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/contrib/test_server.py =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/contrib/test_server.py,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/contrib/test_server.py 23 May 2010 11:58:33 -0000 1.1 @@ -0,0 +1,61 @@ +#!/usr/bin/python +"""Runs a very basic file server so that we can test Xinha. By default, the +server runs on port 8080, but you can pass the -p or --port option to change +the port used.""" + +import os +import SimpleHTTPServer +import SocketServer + +# File server for testing Xinha + +def __main(): + """Use the embed_url.py program from the command-line + + The embed_url.py program downloads files and processes links in the case of + HTML files. See embed_url.py -h for more info. This procedure has the + sole purpose of reading in and verifying the command-line arguments before + passing them to the embed_url funtion.""" + + from getopt import getopt, GetoptError + from sys import argv, exit, stderr + + try: + options, arguments = getopt(argv[1:], "p:", ["port="]) + except GetoptError: + print "Invalid option" + __usage() + exit(2) + + PORT = 8080 + for option, value in options: + if option in ("-p", "--port"): + try: + PORT = int(value) + except ValueError: + print "'%s' is not a valid port number" % value + __usage() + exit(2) + + # SimpleHTTPRequestHandler serves data from the current directory, so if we + # are running from inside contrib, we have to change our current working + # directory + if os.path.split(os.getcwd())[1] == 'contrib': + os.chdir('..') + + Handler = SimpleHTTPServer.SimpleHTTPRequestHandler + + httpd = SocketServer.TCPServer(("", PORT), Handler) + + print "Serving at port %s" % PORT + print "Try viewing the example at http://localhost:%s/examples/Newbie.html" % PORT + httpd.serve_forever() + +def __usage(): + """ + Print the usage information contained in the module docstring + """ + print __doc__ + +if __name__ == '__main__': + __main() Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/contrib/yuicompressor-2.4.2.jar =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/contrib/Attic/yuicompressor-2.4.2.jar,v diff -u Binary files differ Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/examples/XinhaConfig.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/examples/XinhaConfig.js,v diff -u -r1.5 -r1.6 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/examples/XinhaConfig.js 27 Mar 2009 08:20:42 -0000 1.5 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/examples/XinhaConfig.js 23 May 2010 11:58:33 -0000 1.6 @@ -1,3 +1,134 @@ -/* 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_editors=null;xinha_init=null;xinha_config=null;xinha_plugins=null;xinha_init=xinha_init?xinha_init:function(){xinha_editors=xinha_editors?xinha_editors:["myTextArea","anotherOne"];xinha_plugins=xinha_plugins?xinha_plugins:["CharacterMap","ContextMenu","SmartReplace","Stylist","PersistentStorage","PSLocal","Linker","SuperClean","TableOperations"];if(!Xinha.loadPlugins(xinha_plugins,xinha_init)){return}xinha_config=xinha_config?xinha_config():new Xinha.Config();xinha_config.pageStyleSheets=[_editor_url+"examples/files/full_example.css"];xinha_config.stylistLoadStylesheet(_editor_url+"examples/files/stylist.css");xinha_editors=Xinha.makeEditors(xinha_editors,xinha_config,xinha_plugins);Xinha.startEditors(xinha_editors)};Xinha.addOnloadHandler(xinha_init); \ No newline at end of file +xinha_editors = null; +xinha_init = null; +xinha_config = null; +xinha_plugins = null; + +// This contains the names of textareas we will make into Xinha editors +xinha_init = xinha_init ? xinha_init : function() +{ + /** STEP 1 *************************************************************** + * First, specify the textareas that shall be turned into Xinhas. + * For each one add the respective id to the xinha_editors array. + * I you want add more than on textarea, keep in mind that these + * values are comma seperated BUT there is no comma after the last value. + * If you are going to use this configuration on several pages with different + * textarea ids, you can add them all. The ones that are not found on the + * current page will just be skipped. + ************************************************************************/ + + xinha_editors = xinha_editors ? xinha_editors : + [ + 'myTextArea', 'anotherOne' + ]; + + /** STEP 2 *************************************************************** + * Now, what are the plugins you will be using in the editors on this + * page. List all the plugins you will need, even if not all the editors + * will use all the plugins. + * + * The list of plugins below is a good starting point, but if you prefer + * a simpler editor to start with then you can use the following + * + * xinha_plugins = xinha_plugins ? xinha_plugins : [ ]; + * + * which will load no extra plugins at all. + ************************************************************************/ + + xinha_plugins = xinha_plugins ? xinha_plugins : + [ + 'CharacterMap', + 'ContextMenu', + 'SmartReplace', + 'Stylist', + 'ExtendedFileManager', + 'Linker', + 'SuperClean', + 'TableOperations' + ]; + + // THIS BIT OF JAVASCRIPT LOADS THE PLUGINS, NO TOUCHING :) + if(!Xinha.loadPlugins(xinha_plugins, xinha_init)) return; + + + /** STEP 3 *************************************************************** + * We create a default configuration to be used by all the editors. + * If you wish to configure some of the editors differently this will be + * done in step 5. + * + * If you want to modify the default config you might do something like this. + * + * xinha_config = new Xinha.Config(); + * xinha_config.width = '640px'; + * xinha_config.height = '420px'; + * + * + * For a list of the available configuration options, see: + * http://trac.xinha.org/wiki/Documentation/ConfigVariablesList + * + *************************************************************************/ + + xinha_config = xinha_config ? xinha_config() : new Xinha.Config(); + + //this is the standard toolbar, feel free to remove buttons as you like + xinha_config.toolbar = + [ + ["popupeditor"], + ["separator","formatblock","fontname","fontsize","bold","italic","underline","strikethrough"], + ["separator","forecolor","hilitecolor","textindicator"], + ["separator","subscript","superscript"], + ["linebreak","separator","justifyleft","justifycenter","justifyright","justifyfull"], + ["separator","insertorderedlist","insertunorderedlist","outdent","indent"], + ["separator","inserthorizontalrule","createlink","insertimage","inserttable"], + ["linebreak","separator","undo","redo","selectall","print"], (Xinha.is_gecko ? [] : ["cut","copy","paste","overwrite","saveas"]), + ["separator","killword","clearfonts","removeformat","toggleborders","splitblock","lefttoright", "righttoleft"], + ["separator","htmlmode","showhelp","about"] + ]; + + // To adjust the styling inside the editor, we can load an external stylesheet like this + // NOTE : YOU MUST GIVE AN ABSOLUTE URL + xinha_config.pageStyleSheets = [ _editor_url + "examples/files/full_example.css" ]; + //if you're using Stylist, import a stylesheet like this + xinha_config.stylistLoadStylesheet(_editor_url + "examples/files/stylist.css"); + + + /** STEP 4 *************************************************************** + * We first create editors for the textareas. + * + * You can do this in two ways, either + * + * xinha_editors = Xinha.makeEditors(xinha_editors, xinha_config, xinha_plugins); + * + * if you want all the editor objects to use the same set of plugins, OR; + * + * xinha_editors = Xinha.makeEditors(xinha_editors, xinha_config); + * xinha_editors.myTextArea.registerPlugins(['Stylist']); + * xinha_editors.anotherOne.registerPlugins(['CSS','SuperClean']); + * + * if you want to use a different set of plugins for one or more of the + * editors. + ************************************************************************/ + + xinha_editors = Xinha.makeEditors(xinha_editors, xinha_config, xinha_plugins); + + /** STEP 5 *************************************************************** + * If you want to change the configuration variables of any of the + * editors, this is the place to do that, for example you might want to + * change the width and height of one of the editors, like this... + * + * xinha_editors.myTextArea.config.width = '640px'; + * xinha_editors.myTextArea.config.height = '480px'; + * + ************************************************************************/ + + + /** STEP 6 *************************************************************** + * Finally we "start" the editors, this turns the textareas into + * Xinha editors. + ************************************************************************/ + + Xinha.startEditors(xinha_editors); +} + +Xinha.addOnloadHandler(xinha_init); // this executes the xinha_init function on page load + // and does not interfere with window.onload properties set by other scripts + Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/examples/testbed.html =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/examples/testbed.html,v diff -u -r1.8 -r1.9 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/examples/testbed.html 16 Jan 2009 08:36:13 -0000 1.8 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/examples/testbed.html 23 May 2010 11:58:33 -0000 1.9 @@ -14,9 +14,9 @@ -- use of the file as a boilerplate. -- -- $HeadURL: http://svn.xinha.org/trunk/examples/testbed.html $ - -- $LastChangedDate: 2008-10-12 19:42:42 +0200 (So, 12. Okt 2008) $ - -- $LastChangedRevision: 1084 $ - -- $LastChangedBy: ray $ + -- $LastChangedDate: 2009-11-08 17:36:46 +1300 (Sun, 08 Nov 2009) $ + -- $LastChangedRevision: 1197 $ + -- $LastChangedBy: gogo $ ---------------------------------------------------------------------------> @@ -54,7 +54,7 @@ xinha_plugins = xinha_plugins ? xinha_plugins : [ - 'CharacterMap', 'SpellChecker', 'Linker' + 'CharacterMap', 'SpellChecker', 'Linker', 'ContextMenu' ]; // THIS BIT OF JAVASCRIPT LOADS THE PLUGINS, NO TOUCHING :) if(!Xinha.loadPlugins(xinha_plugins, xinha_init)) return; @@ -85,6 +85,7 @@ xinha_config = xinha_config ? xinha_config : new Xinha.Config(); xinha_config.fullPage = true; xinha_config.CharacterMap.mode = 'panel'; + xinha_config.ContextMenu.customHooks = { 'a': [ ['Label', function() { alert('Action'); }, 'Tooltip', '/xinha/images/ed_copy.gif' ] ] } /* // We can load an external stylesheet like this - NOTE : YOU MUST GIVE AN ABSOLUTE URL // otherwise it won't work! Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/iconsets/Tango/README =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/iconsets/Tango/README,v diff -u -r1.1 -r1.2 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/iconsets/Tango/README 16 Jan 2009 09:05:46 -0000 1.1 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/iconsets/Tango/README 23 May 2010 11:58:33 -0000 1.2 @@ -1,3 +1,3 @@ -These icons are from the Tango Desktop Project. The icons are realease under the Creative Commons Share-Alike license. From http://tango.freedesktop.org: +These icons are from the Tango Desktop Project. The icons are released to the Public Domain. From http://tango.freedesktop.org: The Tango Desktop Project exists to help create a consistent graphical user interface experience for free and Open Source software. Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/da.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/da.js,v diff -u -r1.4 -r1.5 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/da.js 24 Nov 2007 15:35:28 -0000 1.4 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/da.js 23 May 2010 11:58:33 -0000 1.5 @@ -1,30 +1,172 @@ +// I18N constants // LANG: "da", ENCODING: UTF-8 // Author: rene, - +// Niels Baggesen, , 0.95, 2009-08-15 { "Bold": "Fed", "Italic": "Kursiv", "Underline": "Understregning", - "Strikethrough": "Overstregning ", + "Strikethrough": "Gennemstregning", "Subscript": "Sænket skrift", "Superscript": "Hævet skrift", "Justify Left": "Venstrejuster", "Justify Center": "Centrer", "Justify Right": "Højrejuster", "Justify Full": "Lige margener", - "Ordered List": "Opstilling med tal", - "Bulleted List": "Opstilling med punkttegn", + "Ordered List": "Ordnet liste", + "Bulleted List": "Punktliste", "Decrease Indent": "Formindsk indrykning", "Increase Indent": "Forøg indrykning", "Font Color": "Skriftfarve", "Background Color": "Baggrundsfarve", - "Horizontal Rule": "Horisontal linie", + "Horizontal Rule": "Vandret streg", "Insert Web Link": "Indsæt hyperlink", - "Insert/Modify Image": "Indsæt billede", + "Insert/Modify Image": "Indsæt/udskift billede", "Insert Table": "Indsæt tabel", "Toggle HTML Source": "HTML visning", "Enlarge Editor": "Vis editor i popup", - "About this editor": "Om htmlarea", + "About this editor": "Om Xinha", "Help using editor": "Hjælp", - "Current style": "Anvendt stil" + "Current style": "Anvendt stil", + "Undoes your last action": "Fortryd sidste ændring", + "Redoes your last action": "Gentag sidste ændring", + "Cut selection": "Klip", + "Copy selection": "Kopier", + "Paste from clipboard": "Indsæt", + "Direction left to right": "Tekst venstre mod højre", + "Direction right to left": "Tekst højre mod venstre", + "Remove formatting": "Fjern formatering", + "Select all": "Vælg alt", + "Print document": "Udskriv dokument", + "Clear MSOffice tags": "MSOffice filter", + "Clear Inline Font Specifications": "Fjern skrift valg", + "Would you like to clear font typefaces?": "Vil du fjern skriftsnit valg", + "Would you like to clear font sizes?": "Vil du fjerne skriftstørrelse valg", + "Would you like to clear font colours?": "Vil du fjerne skriftfarve valg", + "Split Block": "Del blok", + "Toggle Borders": "Tabelkanter til/fra", + "Save as": "Gem som", + "Insert/Overwrite": "Indsæt/Overskriv", + "— format —": "— Format —", + "Heading 1": "Overskrift 1", + "Heading 2": "Overskrift 2", + "Heading 3": "Overskrift 3", + "Heading 4": "Overskrift 4", + "Heading 5": "Overskrift 5", + "Heading 6": "Overskrift 6", + "Normal": "Normal", + "Address": "Adresse", + "Formatted": "Formateret", + + //dialogs + "OK": "OK", + "Cancel": "Fortryd", + "Path": "STi", + "You are in TEXT MODE. Use the [<>] button to switch back to WYSIWYG.": "Du er i TEXT mode. Brug [<>] knappen til at skifte til visuel editering.", + "The Paste button does not work in Mozilla based web browsers (technical security reasons). Press CTRL-V on your keyboard to paste directly.": "Indsæt-knappen virker ikke i Mozilla-baserede browsere. Brug Ctrl-V på tastaturet for at indsætte.", + + "You need to select some text before create a link": "Du skal markere noget tekst for at indsætte et hyperlink", + "Your Document is not well formed. Check JavaScript console for details.": "Dit dokument er ikke syntaktisk korrekt. Åbn Javascript konsollen for at få flere detaljer.", + + "Alignment:": "Justering:", + "Not set": "Ubestemt", + "Left": "Venstre", + "Right": "Højre", + "Texttop": "Teksttop", + "Absmiddle": "Centreret", + "Baseline": "Grundlinje", + "Absbottom": "Bund", + "Bottom": "Tekstbund", + "Middle": "Midt", + "Top": "Top", + + "Layout": "Layout", + "Spacing": "Afstand", + "Horizontal:": "vandret:", + "Horizontal padding": "Vandret fyld", + "Vertical:": "lodret:", + "Vertical padding": "Lodret fyld", + "Border thickness:": "Kantbredde:", + "Leave empty for no border": "Tom hvis ingen kant", + + //Insert Link + "Insert/Modify Link": "Indsæt/rediger hyperlink", + "None (use implicit)": "ingen (implicit)", + "New window (_blank)": "Nyt vindue (_blank)", + "Same frame (_self)": "Samme ramme (_self)", + "Top frame (_top)": "Topramme (_top)", + "Other": "Andet", + "Target:": "Placering:", + "Title (tooltip):": "Titel (Tooltip):", + "URL:": "URL:", + "You must enter the URL where this link points to": "Du skal angive en mål-URL for linket", + + // Insert Table + "Insert Table": "Indsæt tabel", + "Rows:": "Rækker:", + "Number of rows": "Antal rækker", + "Cols:": "Søjler:", + "Number of columns": "Antal søjler", + "Width:": "Bredde:", + "Width of the table": "Tabelbredde", + "Percent": "Procent", + "Pixels": "Pixel", + "Em": "Geviert (Em)", + "Width unit": "Breddeenhed", + "Fixed width columns": "Fast-bredde søjler", + "Positioning of this table": "Placering af tabel", + "Cell spacing:": "Celleafstand:", + "Space between adjacent cells": "Afstand mellem celler", + "Cell padding:": "Cellefyld:", + "Space between content and border in cell": "Luft mellem indhold og kanter", + "You must enter a number of rows": "Du skal skrive antallet af rækker", + "You must enter a number of columns": "Du skal skrive antallet af søjler", + + // Insert Image + "Insert Image": "Indsæt billede", + "Image URL:": "Billede URL:", + "Enter the image URL here": "Angiv billedets URL", + "Preview": "Smugkig", + "Preview the image in a new window": "Smugkig af billedet i et nyt vindue", + "Alternate text:": "Alternativ text:", + "For browsers that don't support images": "for browsere der ikke understøtter billeder", + "Positioning of this image": "Placering af billedet", + "Image Preview:": "Billede smugkig:", + "You must enter the URL": "Du skal angive en URL", + + // de-buttons have letters matching danish :-) + "button_bold": "de/bold.gif", + "button_italic": "de/italic.gif", + "button_underline": "de/underline.gif", + + // Editor Help + "Keyboard shortcuts": "Tastaturgenveje", + "The editor provides the following key combinations:": "Editoren kender følgende kombinationer:", + "new paragraph": "Nyt afsnit", + "insert linebreak": "Indsæt linjeskift", + "Set format to paragraph": "Formater afsnit", + "Clean content pasted from Word": "Rens indhold kopieret fra Word", + "Headings": "Overskrift 1 til 6", + "Close": "Luk", + + // Loading messages + "Loading in progress. Please wait!": "Editoren hentes ind. Vent venligst.", + "Loading plugin $plugin" : "Plugin $plugin hentes", + "Register plugin $plugin" : "Plugin $plugin registreres", + "Constructing object": "Objekt registreres", + "Generate Xinha framework": "Xinha Framework genereres", + "Init editor size":"Størrelsen beregnes", + "Create Toolbar": "Opretter værktøjslinje", + "Create Statusbar" : "Opretter statuslinje", + "Register right panel" : "Registrerer højre panel", + "Register left panel" : "Registrerer venstre panel", + "Register bottom panel" : "Registrerer nederste panel", + "Register top panel" : "Registrerer øverste panel", + "Finishing" : "Afslutter", + + // ColorPicker + "Click a color..." : "Vælg farve", + "Sample" : "Eksempel", + "Web Safe: " : "Web Safe: ", + "Color: " : "Farve: " } Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/de.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/de.js,v diff -u -r1.7 -r1.8 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/de.js 16 Jan 2009 09:05:46 -0000 1.7 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/de.js 23 May 2010 11:58:33 -0000 1.8 @@ -45,7 +45,7 @@ "Toggle Borders": "Tabellenränder ein/ausblenden", "Save as": "speichern unter", "Insert/Overwrite": "Einfügen/Überschreiben", - "— format —": "— Format —", + "— format —": "— Format —", "Heading 1": "Überschrift 1", "Heading 2": "Überschrift 2", "Heading 3": "Überschrift 3", Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/es.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/es.js,v diff -u -r1.5 -r1.6 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/es.js 13 May 2008 18:53:45 -0000 1.5 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/es.js 23 May 2010 11:58:33 -0000 1.6 @@ -45,9 +45,9 @@ "Toggle Borders": "Añadir/Quitar bordes", "Save as": "Guardar como", "Insert/Overwrite": "Insertar/Sobreescribir", - "— format —": "— formato —", - "— font —": "— fuente —", - "— size —": "— tamaño —", + "— format —": "— formato —", + "— font —": "— fuente —", + "— size —": "— tamaño —", "Heading 1": "Cabecera 1", "Heading 2": "Cabecera 2", "Heading 3": "Cabecera 3", Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/eu.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/eu.js,v diff -u -r1.1 -r1.2 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/eu.js 14 Jun 2007 01:36:54 -0000 1.1 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/eu.js 23 May 2010 11:58:33 -0000 1.2 @@ -45,7 +45,7 @@ "Toggle Borders": "Ertzak trukatu", "Save as": "Gorde honela:", "Insert/Overwrite": "Txertatu/Gainidatzi", - "— format —": "— Formatua —", + "— format —": "— Formatua —", "Heading 1": "Goiburua 1", "Heading 2": "Goiburua 2", "Heading 3": "Goiburua 3", Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/fa.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/fa.js,v diff -u -r1.2 -r1.3 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/fa.js 13 May 2008 18:53:45 -0000 1.2 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/fa.js 23 May 2010 11:58:33 -0000 1.3 @@ -45,7 +45,7 @@ "Toggle Borders": "فعال/غیر فعال کردن لبه ها", "Save as": "ذخیره مانند...", "Insert/Overwrite": "افزودن/جانویسی", - "— format —": "— قالب —", + "— format —": "— قالب —", "Heading 1": "تیتر 1", "Heading 2": "تیتر 2", "Heading 3": "تیتر 3", Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/fr.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/fr.js,v diff -u -r1.8 -r1.9 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/fr.js 16 Jan 2009 09:05:46 -0000 1.8 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/fr.js 23 May 2010 11:58:33 -0000 1.9 @@ -45,7 +45,7 @@ "Toggle Borders": "Afficher / Masquer les bordures", "Save as": "Enregistrer sous", "Insert/Overwrite": "Insertion / Remplacement", - "— format —": "— Format —", + "— format —": "— Format —", "Heading 1": "Titre 1", "Heading 2": "Titre 2", "Heading 3": "Titre 3", Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/ja.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/ja.js,v diff -u -r1.4 -r1.5 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/ja.js 14 Jun 2007 01:36:54 -0000 1.4 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/ja.js 23 May 2010 11:58:33 -0000 1.5 @@ -46,7 +46,7 @@ "Toggle Borders": "境界線の切替", "Save as": "名前をつけて保存", "Insert/Overwrite": "挿入/上書き", - "— format —": "— 書式 —", + "— format —": "— 書式 —", "Heading 1": "見出し1", "Heading 2": "見出し2", "Heading 3": "見出し3", @@ -56,8 +56,8 @@ "Normal": "標準", "Address": "アドレス", "Formatted": "整形済み", - "— font —": "— フォント —", - "— size —": "— サイズ —", + "— font —": "— フォント —", + "— size —": "— サイズ —", //dialogs "OK": "OK", Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/pl.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/pl.js,v diff -u -r1.3 -r1.4 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/pl.js 2 Feb 2007 21:04:45 -0000 1.3 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/pl.js 23 May 2010 11:58:33 -0000 1.4 @@ -42,7 +42,7 @@ "Split Block": "Podziel blok", "Toggle Borders": "Włącz / wyłącz ramki", - "— format —": "— Format —", + "— format —": "— Format —", "Heading 1": "Nagłówek 1", "Heading 2": "Nagłówek 2", "Heading 3": "Nagłówek 3", Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/pt_br.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/pt_br.js,v diff -u -r1.3 -r1.4 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/pt_br.js 24 Nov 2007 15:35:28 -0000 1.3 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/pt_br.js 23 May 2010 11:58:33 -0000 1.4 @@ -155,7 +155,7 @@ "Toggle Borders": "Mudar Bordas", "Save as": "Salvar como", "Insert/Overwrite": "Inserir/Sobrescrever", - "— format —": "— formato —", + "— format —": "— formato —", "Heading 1": "Título 1", "Heading 2": "Título 2", "Heading 3": "Título 3", @@ -165,8 +165,8 @@ "Normal": "Normal", "Address": "Endereço", "Formatted": "Formatado", - "— font —": "— fonte —", - "— size —": "— tamanho —", + "— font —": "— fonte —", + "— size —": "— tamanho —", "Ordered List": "Lista Numerada", "Bulleted List": "Lista Marcadores", "Decrease Indent": "Diminuir Indentação", Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/ru.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/ru.js,v diff -u -r1.5 -r1.6 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/ru.js 24 Nov 2007 15:35:28 -0000 1.5 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/ru.js 23 May 2010 11:58:33 -0000 1.6 @@ -61,7 +61,7 @@ "Toggle Borders": "Включить/выключить отображение границ", "Save as": "Сохранить как", "Insert/Overwrite": "Вставка/замена", - "— format —": "— форматирование —", + "— format —": "— форматирование —", "Heading 1": "Заголовок 1", "Heading 2": "Заголовок 2", "Heading 3": "Заголовок 3", @@ -72,8 +72,8 @@ "Address": "Адрес", "Formatted": "Отформатированный текст", - "— font —": "— шрифт —", - "— size —": "— размер —", + "— font —": "— шрифт —", + "— size —": "— размер —", // Диалоги Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/sh.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/sh.js,v diff -u -r1.2 -r1.3 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/sh.js 4 Jun 2006 00:45:49 -0000 1.2 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/sh.js 23 May 2010 11:58:33 -0000 1.3 @@ -53,7 +53,7 @@ "Split Block": "Podeli blok", "Toggle Borders": "Izmeni okvire", - "— format —": "— Format —", + "— format —": "— Format —", "Heading 1": "Zaglavlje 1", "Heading 2": "Zaglavlje 2", "Heading 3": "Zaglavlje 3", Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/sr.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/sr.js,v diff -u -r1.2 -r1.3 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/sr.js 4 Jun 2006 00:45:49 -0000 1.2 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/lang/sr.js 23 May 2010 11:58:33 -0000 1.3 @@ -53,7 +53,7 @@ "Split Block": "Подели блок", "Toggle Borders": "Пребаци оквирне линије", - "— format —": "— Format —", + "— format —": "— Format —", "Heading 1": "Заглавље 1", "Heading 2": "Заглавље 2", "Heading 3": "Заглавље 3", Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/modules/AboutBox/AboutBox.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/modules/AboutBox/AboutBox.js,v diff -u -r1.2 -r1.3 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/modules/AboutBox/AboutBox.js 27 Mar 2009 08:20:43 -0000 1.2 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/modules/AboutBox/AboutBox.js 23 May 2010 11:58:33 -0000 1.3 @@ -1,3 +1,156 @@ -/* 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 */ -(function(){Xinha.plugins.AboutBox=a;function a(b){this.editor=b}a._pluginInfo={name:"AboutBox",developer:"The Xinha Core Developer Team"};a.prototype._lc=function(b){return Xinha._lc(b,"AboutBox")};a.prototype._prepareDialog=function(){var b=this;var d=this.editor;Xinha.loadStyle("about.css","AboutBox","aboutCSS");this.dialog=new Xinha.Dialog(d,a.html,"Xinha",{width:600});this.dialog.getElementById("close").onclick=function(){b.dialog.hide()};this.dialog.getElementById("xinha_logo").src=_editor_url+"images/xinha_logo.gif";var c=this.dialog.getElementsByClassName("tab");this.currentTab=c[0];c.forEach(function(e){e.onclick=function(){if(b.currentTab){Xinha._removeClass(b.currentTab,"tab-current");b.dialog.getElementById(b.currentTab.rel).style.display="none"}Xinha._addClass(e,"tab-current");e.blur();b.currentTab=e;b.dialog.getElementById(e.rel).style.display="block"}});this.fillPlugins();this.fillVersion();this.dialog.onresize=function(){this.getElementById("content").style.height=parseInt(this.height,10)-this.getElementById("h1").offsetHeight-this.getElementById("buttons").offsetHeight-100+"px"}};a.prototype.fillPlugins=function(){var k=this.editor;var d=this.dialog.getElementById("plugins_table");var g,l,b;var c=0;for(var f in k.plugins){var h=k.plugins[f];g=document.createElement("tr");if(c%2){g.style.backgroundColor="#e5e5e5"}d.appendChild(g);l=document.createElement("td");l.innerHTML=h.name;if(h.version){l.innerHTML+=" v"+h.version}g.appendChild(l);l=document.createElement("td");if(h.developer){if(h.developer_url){l.innerHTML=''+h.developer+""}else{l.innerHTML=h.developer}}g.appendChild(l);l=document.createElement("td");if(h.sponsor){if(h.sponsor_url){l.innerHTML=''+h.sponsor+""}else{l.innerHTML=h.sponsor}}g.appendChild(l);l=document.createElement("td");if(h.license){l.innerHTML=h.license}else{l.innerHTML="htmlArea"}g.appendChild(l);c++}};a.prototype.fillVersion=function(){var b=Xinha.version;this.dialog.getElementById("version").innerHTML="
\nRelease:         "+b.Release+" ("+b.Date+")\nHead:            "+b.Head+"\nRevision:        "+b.Revision+"\nLast Changed By: "+b.RevisionBy+"\n
"};a.prototype.show=function(){var c=this;if(!a.html){if(a.loading){return}a.loading=true;Xinha._getback(Xinha.getPluginDir("AboutBox")+"/dialog.html",function(d){a.html=d;c.show()});return}if(!this.dialog){this._prepareDialog()}var b={inputArea:""};this.dialog.show(b)}})(); \ No newline at end of file +// Paste Plain Text plugin for Xinha + +// Distributed under the same terms as Xinha itself. +// This notice MUST stay intact for use (see license.txt). +(function(){ +Xinha.plugins.AboutBox = AboutBox; +function AboutBox(editor) { + this.editor = editor; +} + +AboutBox._pluginInfo = { + name : "AboutBox", + developer : "The Xinha Core Developer Team" +}; + +AboutBox.prototype._lc = function(string) { + return Xinha._lc(string, 'AboutBox'); +}; + + +AboutBox.prototype._prepareDialog = function() +{ + var self = this; + var editor = this.editor; + + Xinha.loadStyle ('about.css', 'AboutBox', 'aboutCSS'); +/// Now we have everything we need, so we can build the dialog. + this.dialog = new Xinha.Dialog(editor, AboutBox.html, 'Xinha',{width:600}) + + this.dialog.getElementById('close').onclick = function() { self.dialog.hide()}; + this.dialog.getElementById('xinha_logo').src = _editor_url + 'images/xinha_logo.gif'; + + var tabs = this.dialog.getElementsByClassName('tab'); + this.currentTab = tabs[0]; + tabs.forEach(function(tab){ + //alert (tab); + tab.onclick = function() { + if (self.currentTab) + { + Xinha._removeClass(self.currentTab,'tab-current'); + self.dialog.getElementById(self.currentTab.rel).style.display = 'none'; + } + Xinha._addClass(tab, 'tab-current'); + tab.blur(); + self.currentTab = tab; + self.dialog.getElementById(tab.rel).style.display = 'block'; + } + }) + this.fillPlugins(); + this.fillVersion(); + this.dialog.onresize = function () + { + this.getElementById("content").style.height = + parseInt(this.height,10) // the actual height of the dialog + - this.getElementById('h1').offsetHeight // the title bar + - this.getElementById('buttons').offsetHeight // the buttons + - 100 // we have a padding at the bottom, gotta take this into acount + + 'px'; // don't forget this ;) + + //this.getElementById("content").style.width =(this.width - 2) + 'px'; // and the width + } +}; +AboutBox.prototype.fillPlugins = function() +{ + var e = this.editor; + var tbody = this.dialog.getElementById('plugins_table'); + var tr,td,a; + var j = 0; + for (var i in e.plugins) + { + var info = e.plugins[i]; + tr = document.createElement('tr'); + if (j%2) tr.style.backgroundColor = '#e5e5e5'; + tbody.appendChild(tr); + td = document.createElement('td'); + td.innerHTML = info.name; + if (info.version) td.innerHTML += ' v'+info.version; + tr.appendChild(td); + + td = document.createElement('td'); + if (info.developer) + { + if (info.developer_url) + { + td.innerHTML = ''+info.developer+''; + } + else + { + td.innerHTML = info.developer + } + } + tr.appendChild(td); + + td = document.createElement('td'); + if (info.sponsor) + { + if (info.sponsor_url) + { + td.innerHTML = ''+info.sponsor+''; + } + else + { + td.innerHTML = info.sponsor + } + } + tr.appendChild(td); + + td = document.createElement('td'); + if (info.license) + { + td.innerHTML = info.license; + } + else + { + td.innerHTML = 'htmlArea'; + } + tr.appendChild(td); + j++; + } +} +AboutBox.prototype.fillVersion = function() +{ + var ver = Xinha.version; + this.dialog.getElementById('version').innerHTML = '
'
+                      + '\nRelease:         ' + ver.Release + ' (' + ver.Date + ')'
+                      + '\nHead:            ' + ver.Head
+                      + '\nRevision:        ' + ver.Revision
+                      + '\nLast Changed By: ' + ver.RevisionBy
+                      + '\n' +
+                      '
'; +} +AboutBox.prototype.show = function() +{ + var self = this; + if (!AboutBox.html) + { + if (AboutBox.loading) return; + AboutBox.loading = true; + Xinha._getback(Xinha.getPluginDir("AboutBox") + '/dialog.html', function(getback) { AboutBox.html = getback; self.show()}); + return; + } + if (!this.dialog) this._prepareDialog(); + + // here we can pass values to the dialog + // each property pair consists of the "name" of the input we want to populate, and the value to be set + var inputs = + { + inputArea : '' // we want the textarea always to be empty on showing + } + // now calling the show method of the Xinha.Dialog object to set the values and show the actual dialog + this.dialog.show(inputs); + + // Init the sizes (only if we have set up the custom resize function) + //this.dialog.onresize(); +}; +})() Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/modules/ColorPicker/ColorPicker.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/modules/ColorPicker/ColorPicker.js,v diff -u -r1.7 -r1.8 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/modules/ColorPicker/ColorPicker.js 27 Mar 2009 08:20:43 -0000 1.7 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/modules/ColorPicker/ColorPicker.js 23 May 2010 11:58:33 -0000 1.8 @@ -1,3 +1,906 @@ -/* 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 */ -ColorPicker._pluginInfo={name:"colorPicker",version:"$LastChangedRevision: 1084 $".replace(/^[^:]*:\s*(.*)\s*\$$/,"$1"),developer:"James Sleeman",developer_url:"http://www.gogo.co.nz/",c_owner:"Gogo Internet Services",license:"htmlArea",sponsor:"Gogo Internet Services",sponsor_url:"http://www.gogo.co.nz/"};function ColorPicker(){}try{if(window.opener&&window.opener.Xinha){var openerColorPicker=window.opener.Xinha.colorPicker;Xinha._addEvent(window,"unload",function(){Xinha.colorPicker=openerColorPicker})}}catch(e){}Xinha.colorPicker=function(t){if(Xinha.colorPicker.savedColors.length===0){Xinha.colorPicker.loadColors()}this.is_ie_6=(Xinha.is_ie&&Xinha.ie_version<7);var i=this;var c=false;var d=false;var w=0;var u=0;this.callback=t.callback?t.callback:function(x){alert("You picked "+x)};this.websafe=t.websafe?t.websafe:false;this.savecolors=t.savecolors?t.savecolors:20;this.cellsize=parseInt(t.cellsize?t.cellsize:"10px",10);this.side=t.granularity?t.granularity:18;var h=this.side+1;var k=this.side-1;this.value=1;this.saved_cells=null;this.table=document.createElement("table");this.table.className="dialog";this.table.cellSpacing=this.table.cellPadding=0;this.table.onmouseup=function(){c=false;d=false};this.tbody=document.createElement("tbody");this.table.appendChild(this.tbody);this.table.style.border="1px solid WindowFrame";this.table.style.zIndex="1050";var b=document.createElement("tr");var g=document.createElement("td");g.colSpan=this.side;g.className="title";g.style.fontFamily="small-caption,caption,sans-serif";g.style.fontSize="x-small";g.unselectable="on";g.style.MozUserSelect="none";g.style.cursor="default";g.appendChild(document.createTextNode(Xinha._lc("Click a color...")));g.style.borderBottom="1px solid WindowFrame";b.appendChild(g);g=null;var g=document.createElement("td");g.className="title";g.colSpan=2;g.style.fontFamily="Tahoma,Verdana,sans-serif";g.style.borderBottom="1px solid WindowFrame";g.style.paddingRight="0";b.appendChild(g);var m=document.createElement("div");m.title=Xinha._lc("Close");m.className="buttonColor";m.style.height="11px";m.style.width="11px";m.style.cursor="pointer";m.onclick=function(){i.close()};m.appendChild(document.createTextNode("\u00D7"));m.align="center";m.style.verticalAlign="top";m.style.position="relative";m.style.cssFloat="right";m.style.styleFloat="right";m.style.padding="0";m.style.margin="2px";m.style.backgroundColor="transparent";m.style.fontSize="11px";if(!Xinha.is_ie){m.style.lineHeight="9px"}m.style.letterSpacing="0";g.appendChild(m);this.tbody.appendChild(b);m=b=g=null;this.constrain_cb=document.createElement("input");this.constrain_cb.type="checkbox";this.chosenColor=document.createElement("input");this.chosenColor.type="text";this.chosenColor.maxLength=7;this.chosenColor.style.width="50px";this.chosenColor.style.fontSize="11px";this.chosenColor.onchange=function(){if(/#[0-9a-f]{6,6}/i.test(this.value)){i.backSample.style.backgroundColor=this.value;i.foreSample.style.color=this.value}};this.backSample=document.createElement("div");this.backSample.appendChild(document.createTextNode("\u00A0"));this.backSample.style.fontWeight="bold";this.backSample.style.fontFamily="small-caption,caption,sans-serif";this.backSample.fontSize="x-small";this.foreSample=document.createElement("div");this.foreSample.appendChild(document.createTextNode(Xinha._lc("Sample")));this.foreSample.style.fontWeight="bold";this.foreSample.style.fontFamily="small-caption,caption,sans-serif";this.foreSample.fontSize="x-small";function q(y){var x=y.toString(16);if(x.length<2){x="0"+x}return x}function p(x){return"#"+q(x.red)+q(x.green)+q(x.blue)}function v(x,y){return Math.round(Math.round(x/y)*y)}function f(x){return parseInt(x.toString(16)+x.toString(16),16)}function s(x){x.red=f(v(parseInt(q(x.red).charAt(0),16),3));x.blue=f(v(parseInt(q(x.blue).charAt(0),16),3));x.green=f(v(parseInt(q(x.green).charAt(0),16),3));return x}function l(B,F,D){var x;if(F===0){x={red:D,green:D,blue:D}}else{B/=60;var A=Math.floor(B);var C=B-A;var z=D*(1-F);var y=D*(1-F*C);var E=D*(1-F*(1-C));switch(A){case 0:x={red:D,green:E,blue:z};break;case 1:x={red:y,green:D,blue:z};break;case 2:x={red:z,green:D,blue:E};break;case 3:x={red:z,green:y,blue:D};break;case 4:x={red:E,green:z,blue:D};break;default:x={red:D,green:z,blue:y};break}}x.red=Math.ceil(x.red*255);x.green=Math.ceil(x.green*255);x.blue=Math.ceil(x.blue*255);return x}var o=this;function a(x){x=x?x:window.event;el=x.target?x.target:x.srcElement;do{if(el==o.table){return}}while(el=el.parentNode);o.close()}this.open=function(G,A,B){this.table.style.display="";this.pick_color();if(B&&/#[0-9a-f]{6,6}/i.test(B)){this.chosenColor.value=B;this.backSample.style.backgroundColor=B;this.foreSample.style.color=B}Xinha._addEvent(document.body,"mousedown",a);this.table.style.position="absolute";var E=A;var D=0;var C=0;do{if(E.style.position=="fixed"){this.table.style.position="fixed"}D+=E.offsetTop-E.scrollTop;C+=E.offsetLeft-E.scrollLeft;E=E.offsetParent}while(E);var z,F;if(/top/.test(G)||(D+this.table.offsetHeight>document.body.offsetHeight)){if(D-this.table.offsetHeight>0){this.table.style.top=(D-this.table.offsetHeight)+"px"}else{this.table.style.top=0}}else{this.table.style.top=(D+A.offsetHeight)+"px"}if(/left/.test(G)||(C+this.table.offsetWidth>document.body.offsetWidth)){if(C-(this.table.offsetWidth-A.offsetWidth)>0){this.table.style.left=(C-(this.table.offsetWidth-A.offsetWidth))+"px"}else{this.table.style.left=0}}else{this.table.style.left=C+"px"}if(this.is_ie_6){this.iframe.style.top=this.table.style.top;this.iframe.style.left=this.table.style.left}};function n(x){i.chosenColor.value=x.colorCode;i.backSample.style.backgroundColor=x.colorCode;i.foreSample.style.color=x.colorCode;if((x.hue>=195&&x.saturation>0.5)||(x.hue===0&&x.saturation===0&&x.value<0.5)||(x.hue!==0&&i.value<0.75)){x.style.borderColor="#fff"}else{x.style.borderColor="#000"}w=x.thisrow;u=x.thiscol}function j(x){if(i.value<0.5){x.style.borderColor="#fff"}else{x.style.borderColor="#000"}k=x.thisrow;h=x.thiscol;i.chosenColor.value=i.saved_cells[w][u].colorCode;i.backSample.style.backgroundColor=i.saved_cells[w][u].colorCode;i.foreSample.style.color=i.saved_cells[w][u].colorCode}function r(y,x){i.saved_cells[y][x].style.borderColor=i.saved_cells[y][x].colorCode}this.pick_color=function(){var K,L;var J=this;var R=359/(this.side);var B=1/(this.side-1);var I=1/(this.side-1);var Q=this.constrain_cb.checked;if(this.saved_cells===null){this.saved_cells=[];for(var C=0;C=195&&D.saturation>0.5)||(D.hue===0&&D.saturation===0&&D.value<0.5)||(D.hue!==0&&J.value<0.75)){D.style.borderColor="#fff"}else{D.style.borderColor="#000"}}};this.close=function(){Xinha._removeEvent(document.body,"mousedown",a);this.table.style.display="none";if(this.is_ie_6){if(this.iframe){this.iframe.style.display="none"}}}};Xinha.colorPicker.savedColors=[];Xinha.colorPicker.remember=function(a,c){for(var b=Xinha.colorPicker.savedColors.length;b--;){if(Xinha.colorPicker.savedColors[b]==a){return false}}Xinha.colorPicker.savedColors.splice(0,0,a);Xinha.colorPicker.savedColors=Xinha.colorPicker.savedColors.slice(0,c);var d=new Date();d.setMonth(d.getMonth()+1);document.cookie="XinhaColorPicker="+escape(Xinha.colorPicker.savedColors.join("-"))+";expires="+d.toGMTString();return true};Xinha.colorPicker.loadColors=function(){var b=document.cookie.indexOf("XinhaColorPicker");if(b!=-1){var c=(document.cookie.indexOf("=",b)+1);var a=document.cookie.indexOf(";",b);if(a==-1){a=document.cookie.length}Xinha.colorPicker.savedColors=unescape(document.cookie.substring(c,a)).split("-")}};Xinha.colorPicker.InputBinding=function(b,c){var g=b.ownerDocument;var a=g.createElement("span");a.className="buttonColor";var d=this.chooser=g.createElement("span");d.className="chooser";if(b.value){d.style.backgroundColor=b.value}d.onmouseover=function(){d.className="chooser buttonColor-hilite"};d.onmouseout=function(){d.className="chooser"};d.appendChild(g.createTextNode("\u00a0"));a.appendChild(d);var f=g.createElement("span");f.className="nocolor";f.onmouseover=function(){f.className="nocolor buttonColor-hilite";f.style.color="#f00"};f.onmouseout=function(){f.className="nocolor";f.style.color="#000"};f.onclick=function(){b.value="";d.style.backgroundColor=""};f.appendChild(g.createTextNode("\u00d7"));a.appendChild(f);b.parentNode.insertBefore(a,b.nextSibling);Xinha._addEvent(b,"change",function(){d.style.backgroundColor=this.value});c=(c)?Xinha.cloneObject(c):{cellsize:"5px"};c.callback=(c.callback)?c.callback:function(h){d.style.backgroundColor=h;b.value=h};d.onclick=function(){var h=new Xinha.colorPicker(c);h.open("",d,b.value)};Xinha.freeLater(this,"chooser")};Xinha.colorPicker.InputBinding.prototype.setColor=function(a){this.chooser.style.backgroundColor=a}; \ No newline at end of file + + /** + * Gogo Internet Services Color Picker Javascript Widget + * colorPicker for short. + * + * @author James Sleeman + * @date June, 2005 + * + * The colorPicker class provides access to a color map for selecting + * colors which will be passed back to a callback (usually such a callback would + * write the RGB hex value returned into a field, but that's up to you). + * + * The color map presented is a standard rectangular pallate with 0->360 degrees of + * hue on the Y axis and 0->100% saturation on the X axis, the value (brightness) is + * selectable as a vertical column of grey values. Also present is a one row of + * white->grey->black for easy selection of these colors. + * + * A checkbox is presented, which when checked will force the palatte into "web safe" + * mode, only colours in the "web safe palatte" of 216 colors will be shown, the palatte + * is adjusted so that the normal blend of colours are "rounded" to the nearest web safe + * one. It should be noted that "web safe" colours really are a thing of the past, + * not only can pretty much every body display several million colours, but it's actually + * been found that of those 216 web safe colours only 20 to 30 are actually going to be + * displayed equally on the majority of monitors, and those are mostly yellows! + * + * =Usage Example= + * {{{ + * ";break}return h}; \ No newline at end of file + + /*--------------------------------------:noTabs=true:tabSize=2:indentSize=2:-- + -- Xinha (is not htmlArea) - http://xinha.gogo.co.nz/ + -- + -- Use of Xinha is granted by the terms of the htmlArea License (based on + -- BSD license) please read license.txt in this package for details. + -- + -- Xinha was originally based on work by Mihai Bazon which is: + -- Copyright (c) 2003-2004 dynarch.com. + -- Copyright (c) 2002-2003 interactivetools.com, inc. + -- This copyright notice MUST stay intact for use. + -- + -- This is the standard implementation of the method for rendering HTML code from the DOM + -- + -- The file is loaded by the Xinha Core when no alternative method (plugin) is loaded. + -- + -- + -- $HeadURL: http://svn.xinha.org/trunk/modules/GetHtml/DOMwalk.js $ + -- $LastChangedDate: 2009-04-14 21:07:43 +1200 (Tue, 14 Apr 2009) $ + -- $LastChangedRevision: 1185 $ + -- $LastChangedBy: ray $ + --------------------------------------------------------------------------*/ +function GetHtmlImplementation(editor) { + this.editor = editor; +} + +GetHtmlImplementation._pluginInfo = { + name : "GetHtmlImplementation DOMwalk", + origin : "Xinha Core", + version : "$LastChangedRevision: 1185 $".replace(/^[^:]*:\s*(.*)\s*\$$/, '$1'), + developer : "The Xinha Core Developer Team", + developer_url : "$HeadURL: http://svn.xinha.org/trunk/modules/GetHtml/DOMwalk.js $".replace(/^[^:]*:\s*(.*)\s*\$$/, '$1'), + sponsor : "", + sponsor_url : "", + license : "htmlArea" +}; + +// Retrieves the HTML code from the given node. This is a replacement for +// getting innerHTML, using standard DOM calls. +// Wrapper legacy see #442 +Xinha.getHTML = function(root, outputRoot, editor) +{ + return Xinha.getHTMLWrapper(root,outputRoot,editor); +}; + +Xinha.emptyAttributes = " checked disabled ismap readonly nowrap compact declare selected defer multiple noresize noshade " + +Xinha.getHTMLWrapper = function(root, outputRoot, editor, indent) +{ + var html = ""; + if ( !indent ) + { + indent = ''; + } + + switch ( root.nodeType ) + { + case 10:// Node.DOCUMENT_TYPE_NODE + case 6: // Node.ENTITY_NODE + case 12:// Node.NOTATION_NODE + // this all are for the document type, probably not necessary + break; + + case 2: // Node.ATTRIBUTE_NODE + // Never get here, this has to be handled in the ELEMENT case because + // of IE crapness requring that some attributes are grabbed directly from + // the attribute (nodeValue doesn't return correct values), see + //http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=UTF-8&safe=off&selm=3porgu4mc4ofcoa1uqkf7u8kvv064kjjb4%404ax.com + // for information + break; + + case 4: // Node.CDATA_SECTION_NODE + // Mozilla seems to convert CDATA into a comment when going into wysiwyg mode, + // don't know about IE + html += (Xinha.is_ie ? ('\n' + indent) : '') + '' ; + break; + + case 5: // Node.ENTITY_REFERENCE_NODE + html += '&' + root.nodeValue + ';'; + break; + + case 7: // Node.PROCESSING_INSTRUCTION_NODE + // PI's don't seem to survive going into the wysiwyg mode, (at least in moz) + // so this is purely academic + html += (Xinha.is_ie ? ('\n' + indent) : '') + '<'+'?' + root.target + ' ' + root.data + ' ?>'; + break; + + case 1: // Node.ELEMENT_NODE + case 11: // Node.DOCUMENT_FRAGMENT_NODE + case 9: // Node.DOCUMENT_NODE + var closed; + var i; + var root_tag = (root.nodeType == 1) ? root.tagName.toLowerCase() : ''; + if ( ( root_tag == "script" || root_tag == "noscript" ) && editor.config.stripScripts ) + { + break; + } + if ( outputRoot ) + { + outputRoot = !(editor.config.htmlRemoveTags && editor.config.htmlRemoveTags.test(root_tag)); + } + if ( Xinha.is_ie && root_tag == "head" ) + { + if ( outputRoot ) + { + html += (Xinha.is_ie ? ('\n' + indent) : '') + ""; + } + + var save_multiline = RegExp.multiline; + RegExp.multiline = true; + var txt = + root.innerHTML + .replace(Xinha.RE_tagName, function(str, p1, p2) { return p1 + p2.toLowerCase(); }) // lowercasize + .replace(/\s*=\s*(([^'"][^>\s]*)([>\s])|"([^"]+)"|'([^']+)')/g, '="$2$4$5"$3') //add attribute quotes + .replace(/<(link|meta)((\s*\S*="[^"]*")*)>([\n\r]*)/g, '<$1$2 />\n'); //terminate singlet tags + RegExp.multiline = save_multiline; + html += txt + '\n'; + if ( outputRoot ) + { + html += (Xinha.is_ie ? ('\n' + indent) : '') + ""; + } + break; + } + else if ( outputRoot ) + { + closed = (!(root.hasChildNodes() || Xinha.needsClosingTag(root))); + html += ((Xinha.isBlockElement(root)) ? ('\n' + indent) : '') + "<" + root.tagName.toLowerCase(); + var attrs = root.attributes; + + for ( i = 0; i < attrs.length; ++i ) + { + var a = attrs.item(i); + // In certain browsers (*cough* firefox) the dom node loses + // information if the image is currently broken. In order to prevent + // corrupting the height and width of image tags, we strip height and + // width from the image rather than reporting bad information. + if (Xinha.is_real_gecko && (root.tagName.toLowerCase() == 'img') && + ((a.nodeName.toLowerCase() == 'height') || (a.nodeName.toLowerCase() == 'width'))) + { + if (!root.complete || root.naturalWidth === 0) + { + // This means that firefox has been unable to read the dimensions from the actual image + continue; + } + } + if (typeof a.nodeValue == 'object' ) continue; // see #684 + if (root.tagName.toLowerCase() == "input" + && root.type.toLowerCase() == "checkbox" + && a.nodeName.toLowerCase() == "value" + && a.nodeValue.toLowerCase() == "on") + { + continue; + } + if ( !a.specified + // IE claims these are !a.specified even though they are. Perhaps others too? + && !(root.tagName.toLowerCase().match(/input|option/) && a.nodeName == 'value') + && !(root.tagName.toLowerCase().match(/area/) && a.nodeName.match(/shape|coords/i)) + ) + { + continue; + } + var name = a.nodeName.toLowerCase(); + if ( /_moz_editor_bogus_node/.test(name) || ( name == 'class' && a.nodeValue == 'webkit-block-placeholder') ) + { + html = ""; + break; + } + if ( /(_moz)|(contenteditable)|(_msh)/.test(name) ) + { + // avoid certain attributes + continue; + } + var value; + if ( Xinha.emptyAttributes.indexOf(" "+name+" ") != -1) + { + value = name; + } + else if ( name != "style" ) + { + // IE5.5 reports 25 when cellSpacing is + // 1; other values might be doomed too. + // For this reason we extract the + // values directly from the root node. + // I'm starting to HATE JavaScript + // development. Browser differences + // suck. + // + // Using Gecko the values of href and src are converted to absolute links + // unless we get them using nodeValue() + if ( typeof root[a.nodeName] != "undefined" && name != "href" && name != "src" && !(/^on/.test(name)) ) + { + value = root[a.nodeName]; + } + else + { + value = a.nodeValue; + if (name == 'class') + { + value = value.replace(/Apple-style-span/,''); + if (!value) continue; + } + // IE seems not willing to return the original values - it converts to absolute + // links using a.nodeValue, a.value, a.stringValue, root.getAttribute("href") + // So we have to strip the baseurl manually :-/ + if ( Xinha.is_ie && (name == "href" || name == "src") ) + { + value = editor.stripBaseURL(value); + } + + // High-ascii (8bit) characters in links seem to cause problems for some sites, + // while this seems to be consistent with RFC 3986 Section 2.4 + // because these are not "reserved" characters, it does seem to + // cause links to international resources not to work. See ticket:167 + + // IE always returns high-ascii characters un-encoded in links even if they + // were supplied as % codes (it unescapes them when we pul the value from the link). + + // Hmmm, very strange if we use encodeURI here, or encodeURIComponent in place + // of escape below, then the encoding is wrong. I mean, completely. + // Nothing like it should be at all. Using escape seems to work though. + // It's in both browsers too, so either I'm doing something wrong, or + // something else is going on? + + if ( editor.config.only7BitPrintablesInURLs && ( name == "href" || name == "src" ) ) + { + value = value.replace(/([^!-~]+)/g, function(match) { return escape(match); }); + } + } + } + else if ( !Xinha.is_ie ) + { + value = root.style.cssText.replace(/rgb\(.*?\)/ig,function(rgb){ return Xinha._colorToRgb(rgb) }); + } + else if (!value) // IE8 has style in attributes (see below), but it's empty! + { + continue; + } + if ( /^(_moz)?$/.test(value) ) + { + // Mozilla reports some special tags + // here; we don't need them. + continue; + } + html += " " + name + '="' + Xinha.htmlEncode(value) + '"'; + } + //IE fails to put style in attributes list & cssText is UPPERCASE + if ( Xinha.is_ie && root.style.cssText ) + { + html += ' style="' + root.style.cssText.replace(/(^)?([^:]*):(.*?)(;|$)/g, function(m0, m1,m2,m3, m4){return m2.toLowerCase() + ':' + m3 + m4;}) + '"'; + } + if ( Xinha.is_ie && root.tagName.toLowerCase() == "option" && root.selected ) + { + html += ' selected="selected"'; + } + if ( html !== "" ) + { + if ( closed && root_tag=="p" ) + { + //never use

as empty paragraphs won't be visible + html += "> 

"; + } + else if ( closed ) + { + html += " />"; + } + else + { + html += ">"; + } + } + } + var containsBlock = false; + if ( root_tag == "script" || root_tag == "noscript" ) + { + if ( !editor.config.stripScripts ) + { + if (Xinha.is_ie) + { + var innerText = "\n" + root.innerHTML.replace(/^[\n\r]*/,'').replace(/\s+$/,'') + '\n' + indent; + } + else + { + var innerText = (root.hasChildNodes()) ? root.firstChild.nodeValue : ''; + } + html += innerText + '' + ((Xinha.is_ie) ? '\n' : ''); + } + } + else if (root_tag == "pre") + { + html += ((Xinha.is_ie) ? '\n' : '') + root.innerHTML.replace(/
/g,'\n') + ''; + } + else + { + for ( i = root.firstChild; i; i = i.nextSibling ) + { + if ( !containsBlock && i.nodeType == 1 && Xinha.isBlockElement(i) ) + { + containsBlock = true; + } + html += Xinha.getHTMLWrapper(i, true, editor, indent + ' '); + } + if ( outputRoot && !closed ) + { + html += (((Xinha.isBlockElement(root) && containsBlock) || root_tag == 'head' || root_tag == 'html') ? ('\n' + indent) : '') + ""; + } + } + break; + + case 3: // Node.TEXT_NODE + if ( /^script|noscript|style$/i.test(root.parentNode.tagName) ) + { + html = root.data; + } + else if(root.data.trim() == '') + { + if(root.data) + { + html = ' '; + } + else + { + html = ''; + } + } + else + { + html = Xinha.htmlEncode(root.data); + } + break; + + case 8: // Node.COMMENT_NODE + html = ""; + break; + } + return html; +}; + + + Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/modules/GetHtml/TransformInnerHTML.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/modules/GetHtml/TransformInnerHTML.js,v diff -u -r1.6 -r1.7 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/modules/GetHtml/TransformInnerHTML.js 27 Mar 2009 08:20:43 -0000 1.6 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/modules/GetHtml/TransformInnerHTML.js 23 May 2010 11:58:33 -0000 1.7 @@ -1,3 +1,231 @@ -/* 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 */ -function GetHtmlImplementation(a){this.editor=a}GetHtmlImplementation._pluginInfo={name:"GetHtmlImplementation TransformInnerHTML",version:"1.0",developer:"Nelson Bright",developer_url:"http://www.brightworkweb.com/",sponsor:"",sponsor_url:"",license:"htmlArea"};Xinha.RegExpCache=[/<\s*\/?([^\s\/>]+)[\s*\/>]/gi,/(\s+)_moz[^=>]*=[^\s>]*/gi,/\s*=\s*(([^'"][^>\s]*)([>\s])|"([^"]+)"|'([^']+)')/g,/\/>/g,/<(br|hr|img|input|link|meta|param|embed|area)((\s*\S*="[^"]*")*)>/g,/(<\w+\s+(\w*="[^"]*"\s+)*)(checked|compact|declare|defer|disabled|ismap|multiple|no(href|resize|shade|wrap)|readonly|selected)([\s>])/gi,/(="[^']*)'([^'"]*")/,/&(?=(?!(#[0-9]{2,5};|[a-zA-Z0-9]{2,6};|#x[0-9a-fA-F]{2,4};))[^<]*>)/g,/<\s+/g,/\s+(\/)?>/g,/\s{2,}/g,/\s+([^=\s]+)((="[^"]+")|([\s>]))/g,/\s+contenteditable(=[^>\s\/]*)?/gi,/((href|src)=")([^\s]*)"/g,/<\/?(div|p|h[1-6]|table|tr|td|th|ul|ol|li|dl|dt|dd|blockquote|object|br|hr|img|embed|param|pre|script|html|head|body|meta|link|title|area|input|form|textarea|select|option)[^>]*>/g,/<\/(div|p|h[1-6]|table|tr|ul|ol|dl|blockquote|object|html|head|body|script|form|select)( [^>]*)?>/g,/<(div|p|h[1-6]|table|tr|ul|ol|dl|blockquote|object|html|head|body|script|form|select)( [^>]*)?>/g,/<(td|th|li|dt|dd|option|br|hr|embed|param|pre|meta|link|title|area|input|textarea)[^>]*>/g,/(^|<\/(pre|script)>)(\s|[^\s])*?(<(pre|script)[^>]*>|$)/g,/(]*>)([\s\S])*?(<\/pre>)/g,/(^|)([\s\S]*?)(?=|$)/g,/\S*=""/g,/|<\?[\s\S]*?\?>|<\/?\w[^>]*>/g,/(^|<\/script>)[\s\S]*?(]*>|$)/g];if(typeof RegExp.prototype.compile=="function"){for(var i=0;i").replace(d[9],"$1>").replace(d[5],'$1$3="$3"$5').replace(d[4],"<$1$2 />").replace(d[6],"$1$2").replace(d[7],"&").replace(d[8],"<").replace(d[10]," ");if(Xinha.is_ie&&d[13].test(b)){b=b.replace(d[13],"$1"+Xinha._escapeDollars(stripBaseURL(RegExp.$3))+'"')}if(this.config.only7BitPrintablesInURLs){if(Xinha.is_ie){d[13].test(b)}if(d[13].test(b)){try{b=b.replace(d[13],"$1"+Xinha._escapeDollars(decodeURIComponent(RegExp.$3).replace(/([^!-~]+)/g,function(c){return escape(c)}))+'"')}catch(a){b=b.replace(d[13],Xinha._escapeDollars("$1"+RegExp.$3.replace(/([^!-~]+)/g,function(c){return escape(c)})+'"'))}}}return b};Xinha.indent=function(a,b){Xinha.__nindent=0;Xinha.__sindent="";Xinha.__sindentChar=(typeof b=="undefined")?" ":b;var d=Xinha.RegExpCache;if(Xinha.is_gecko){a=a.replace(d[19],function(c){return c.replace(/
/g,"\n")})}a=a.replace(d[18],function(c){c=c.replace(d[20],function(f,e,g){string=g.replace(/[\n\r]/gi," ").replace(/\s+/gi," ").replace(d[14],function(l){if(l.match(d[16])){var k="\n"+Xinha.__sindent+l;Xinha.__sindent+=Xinha.__sindentChar;++Xinha.__nindent;return k}else{if(l.match(d[15])){--Xinha.__nindent;Xinha.__sindent="";for(var h=Xinha.__nindent;h>0;--h){Xinha.__sindent+=Xinha.__sindentChar}return"\n"+Xinha.__sindent+l}else{if(l.match(d[17])){return"\n"+Xinha.__sindent+l}}}return l});return e+string});return c});a=a.replace(/^\s*/,"").replace(/ +\n/g,"\n").replace(/[\r\n]+(\s+)<\/script>/g,"\n$1<\/script>");return a};Xinha.getHTML=function(k,e,g){var f="";var h=Xinha.RegExpCache;if(k.nodeType==11){var d=document.createElement("div");var p=k.insertBefore(d,k.firstChild);for(j=p.nextSibling;j;j=j.nextSibling){p.appendChild(j.cloneNode(true))}f+=p.innerHTML.replace(h[23],function(a){a=a.replace(h[22],function(c){if(/^<[!\?]/.test(c)){return c}else{return g.cleanHTML(c)}});return a})}else{var n=(k.nodeType==1)?k.tagName.toLowerCase():"";if(e){f+="<"+n;var o=k.attributes;for(i=0;i\/]+)/,"$1")))){return g.cleanHTML(c)}else{return""}}});return a});if(Xinha.is_ie){f=f.replace(/<(li|dd|dt)( [^>]*)?>/g,"<$1$2>").replace(/(<[uod]l[^>]*>[\s\S]*?)<\/(li|dd|dt)>/g,"$1").replace(/\s*<\/(li|dd|dt)>(\s*<\/(li|dd|dt)>)+/g,"").replace(/(][\s\S]*?)(<\/d[dt]>)+/g,"$1")}if(Xinha.is_gecko){f=f.replace(/
\n$/,"")}f=f.replace(/\s*(<\/(li|dd|dt)>)/g,"$1");if(e){f+=""}f=Xinha.indent(f)}return f};Xinha._escapeDollars=function(a){return a.replace(/\$/g,"$$$$")}; \ No newline at end of file +/** + * Based on XML_Utility functions submitted by troels_kn. + * credit also to adios, who helped with reg exps: + * http://www.sitepoint.com/forums/showthread.php?t=201052 + * + * A replacement for Xinha.getHTML + * + * Features: + * - Generates XHTML code + * - Much faster than Xinha.getHTML + * - Eliminates the hacks to accomodate browser quirks + * - Returns correct code for Flash objects and scripts + * - Formats html in an indented, readable format in html mode + * - Preserves script and pre formatting + * - Preserves formatting in comments + * - Removes contenteditable from body tag in full-page mode + * - Supports only7BitPrintablesInURLs config option + * - Supports htmlRemoveTags config option + */ + +function GetHtmlImplementation(editor) { + this.editor = editor; +} + +GetHtmlImplementation._pluginInfo = { + name : "GetHtmlImplementation TransformInnerHTML", + version : "1.0", + developer : "Nelson Bright", + developer_url : "http://www.brightworkweb.com/", + sponsor : "", + sponsor_url : "", + license : "htmlArea" +}; + +Xinha.RegExpCache = [ +/*00*/ /<\s*\/?([^\s\/>]+)[\s*\/>]/gi,//lowercase tags +/*01*/ /(\s+)_moz[^=>]*=[^\s>]*/gi,//strip _moz attributes +/*02*/ /\s*=\s*(([^'"][^>\s]*)([>\s])|"([^"]+)"|'([^']+)')/g,// find attributes +/*03*/ /\/>/g,//strip singlet terminators +/*04*/ /<(br|hr|img|input|link|meta|param|embed|area)((\s*\S*="[^"]*")*)>/g,//terminate singlet tags +/*05*/ /(<\w+\s+(\w*="[^"]*"\s+)*)(checked|compact|declare|defer|disabled|ismap|multiple|no(href|resize|shade|wrap)|readonly|selected)([\s>])/gi,//expand singlet attributes +/*06*/ /(="[^']*)'([^'"]*")/,//check quote nesting +/*07*/ /&(?=(?!(#[0-9]{2,5};|[a-zA-Z0-9]{2,6};|#x[0-9a-fA-F]{2,4};))[^<]*>)/g,//expand query ampersands not in html entities +/*08*/ /<\s+/g,//strip tagstart whitespace +/*09*/ /\s+(\/)?>/g,//trim whitespace +/*10*/ /\s{2,}/g,//trim extra whitespace +/*11*/ /\s+([^=\s]+)((="[^"]+")|([\s>]))/g,// lowercase attribute names +/*12*/ /\s+contenteditable(=[^>\s\/]*)?/gi,//strip contenteditable +/*13*/ /((href|src)=")([^\s]*)"/g, //find href and src for stripBaseHref() +/*14*/ /<\/?(div|p|h[1-6]|table|tr|td|th|ul|ol|li|dl|dt|dd|blockquote|object|br|hr|img|embed|param|pre|script|html|head|body|meta|link|title|area|input|form|textarea|select|option)[^>]*>/g, +/*15*/ /<\/(div|p|h[1-6]|table|tr|ul|ol|dl|blockquote|html|head|body|script|form|select)( [^>]*)?>/g,//blocklevel closing tag +/*16*/ /<(div|p|h[1-6]|table|tr|ul|ol|dl|blockquote|object|html|head|body|script|form|select)( [^>]*)?>/g,//blocklevel opening tag +/*17*/ /<(td|th|li|dt|dd|option|br|hr|embed|param|pre|meta|link|title|area|input|textarea)[^>]*>/g,//singlet tag or output on 1 line +/*18*/ /(^|<\/(pre|script)>)(\s|[^\s])*?(<(pre|script)[^>]*>|$)/g,//find content NOT inside pre and script tags +/*19*/ /(]*>)([\s\S])*?(<\/pre>)/g,//find content inside pre tags +/*20*/ /(^|)([\s\S]*?)(?=|$)/g,//find content NOT inside comments +/*21*/ /\S*=""/g, //find empty attributes +/*22*/ /|<\?[\s\S]*?\?>|<\/?\w[^>]*>/g, //find all tags, including comments and php +/*23*/ /(^|<\/script>)[\s\S]*?(]*>|$)/g //find content NOT inside script tags +]; +// compile for performance; WebKit doesn't support this +var testRE = new RegExp().compile(Xinha.RegExpCache[3]); +if (typeof testRE != 'undefined') { + for (var i=0; i').//strip singlet terminators + replace(c[9], '$1>').//trim whitespace + replace(c[5], '$1$3="$3"$5').//expand singlet attributes + replace(c[4], '<$1$2 />').//terminate singlet tags + replace(c[6], '$1$2').//check quote nesting + replace(c[7], '&').//expand query ampersands + replace(c[8], '<').//strip tagstart whitespace + replace(c[10], ' ');//trim extra whitespace + if(Xinha.is_ie && c[13].test(sHtml)) { + sHtml = sHtml.replace(c[13],'$1'+Xinha._escapeDollars(this.stripBaseURL(RegExp.$3))+'"'); + } + + if(this.config.only7BitPrintablesInURLs) { + if (Xinha.is_ie) c[13].test(sHtml); // oddly the test below only triggers when we call this once before (IE6), in Moz it fails if tested twice + if ( c[13].test(sHtml)) { + try { //Mozilla returns an incorrectly encoded value with innerHTML + sHtml = sHtml.replace(c[13], '$1'+Xinha._escapeDollars(decodeURIComponent(RegExp.$3).replace(/([^!-~]+)/g, function(chr) + {return escape(chr);}))+'"'); + } catch (e) { // once the URL is escape()ed, you can't decodeURIComponent() it anymore + sHtml = sHtml.replace(c[13], Xinha._escapeDollars('$1'+RegExp.$3.replace(/([^!-~]+)/g,function(chr){return escape(chr);})+'"')); + } + } + } + return sHtml; +}; + +/** + * Prettyfies html by inserting linebreaks before tags, and indenting blocklevel tags + */ +Xinha.indent = function(s, sindentChar) { + Xinha.__nindent = 0; + Xinha.__sindent = ""; + Xinha.__sindentChar = (typeof sindentChar == "undefined") ? " " : sindentChar; + var c = Xinha.RegExpCache; + if(Xinha.is_gecko) { //moz changes returns into
inside
 tags
+		s = s.replace(c[19], function(str){return str.replace(/
/g,"\n")}); + } + s = s.replace(c[18], function(strn) { //skip pre and script tags + strn = strn.replace(c[20], function(st,$1,$2) { //exclude comments + string = $2.replace(/[\n\r]/gi, " ").replace(/\s+/gi," ").replace(c[14], function(str) { + if (str.match(c[16])) { + var s = "\n" + Xinha.__sindent + str; + // blocklevel openingtag - increase indent + Xinha.__sindent += Xinha.__sindentChar; + ++Xinha.__nindent; + return s; + } else if (str.match(c[15])) { + // blocklevel closingtag - decrease indent + --Xinha.__nindent; + Xinha.__sindent = ""; + for (var i=Xinha.__nindent;i>0;--i) { + Xinha.__sindent += Xinha.__sindentChar; + } + return "\n" + Xinha.__sindent + str; + } else if (str.match(c[17])) { + // singlet tag + return "\n" + Xinha.__sindent + str; + } + return str; // this won't actually happen + }); + return $1 + string; + });return strn; + }); + //final cleanup + s = s.replace(/^\s*/,'').//strip leading whitespace + replace(/ +\n/g,'\n').//strip spaces at end of lines + replace(/[\r\n]+(\s+)<\/script>/g,'\n$1');//strip returns added into scripts + return s; +}; + +Xinha.getHTML = function(root, outputRoot, editor) { + var html = ""; + var c = Xinha.RegExpCache; + + if(root.nodeType == 11) {//document fragment + //we can't get innerHTML from the root (type 11) node, so we + //copy all the child nodes into a new div and get innerHTML from the div + var div = document.createElement("div"); + var temp = root.insertBefore(div,root.firstChild); + for (j = temp.nextSibling; j; j = j.nextSibling) { + temp.appendChild(j.cloneNode(true)); + } + html += temp.innerHTML.replace(c[23], function(strn) { //skip content inside script tags + strn = strn.replace(c[22], function(tag){ + if(/^<[!\?]/.test(tag)) return tag; //skip comments and php tags + else return editor.cleanHTML(tag)}); + return strn; + }); + + } else { + + var root_tag = (root.nodeType == 1) ? root.tagName.toLowerCase() : ''; + if (outputRoot) { //only happens with tag in fullpage mode + html += "<" + root_tag; + var attrs = root.attributes; // strangely, this doesn't work in moz + for (i = 0; i < attrs.length; ++i) { + var a = attrs.item(i); + if (!a.specified) { + continue; + } + var name = a.nodeName.toLowerCase(); + var value = a.nodeValue; + html += " " + name + '="' + value + '"'; + } + html += ">"; + } + if(root_tag == "html") { + innerhtml = editor._doc.documentElement.innerHTML; + } else { + innerhtml = root.innerHTML; + } + //pass tags to cleanHTML() one at a time + //includes support for htmlRemoveTags config option + html += innerhtml.replace(c[23], function(strn) { //skip content inside script tags + strn = strn.replace(c[22], function(tag){ + if(/^<[!\?]/.test(tag)) return tag; //skip comments and php tags + else if(!(editor.config.htmlRemoveTags && editor.config.htmlRemoveTags.test(tag.replace(/<([^\s>\/]+)/,'$1')))) + return editor.cleanHTML(tag); + else return ''}); + return strn; + }); + //IE drops all ,, tags in a list except the last one + if(Xinha.is_ie) { + html = html.replace(/<(li|dd|dt)( [^>]*)?>/g,'<$1$2>'). + replace(/(<[uod]l[^>]*>[\s\S]*?)<\/(li|dd|dt)>/g, '$1'). + replace(/\s*<\/(li|dd|dt)>(\s*<\/(li|dd|dt)>)+/g, ''). + replace(/(][\s\S]*?)(<\/d[dt]>)+/g, '$1'); + } + if(Xinha.is_gecko) + html = html.replace(/
\n$/, ''); //strip trailing
added by moz + //Cleanup redundant whitespace before in IE and Mozilla + html = html.replace(/\s*(<\/(li|dd|dt)>)/g, '$1'); + if (outputRoot) { + html += ""; + } + html = Xinha.indent(html); + }; +// html = Xinha.htmlEncode(html); + + return html; +}; + +/** + * Escapes dollar signs ($) to make them safe to use in regex replacement functions by replacing each $ in the input with $$. + * + * This is advisable any time the replacement string for a call to replace() is a variable and could contain dollar signs that should not be interpreted as references to captured groups (e.g., when you want the text "$10" and not the first captured group followed by a 0). + * See http://trac.xinha.org/ticket/1337 + */ +Xinha._escapeDollars = function(str) { + return str.replace(/\$/g, "$$$$"); +}; Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/modules/InlineStyler/InlineStyler.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/modules/InlineStyler/InlineStyler.js,v diff -u -r1.2 -r1.3 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/modules/InlineStyler/InlineStyler.js 27 Mar 2009 08:20:43 -0000 1.2 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/modules/InlineStyler/InlineStyler.js 23 May 2010 11:58:33 -0000 1.3 @@ -1,3 +1,505 @@ -/* 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.InlineStyler=function(b,c,a,d){this.element=b;this.editor=c;this.dialog=a;this.doc=d?d:document;this.inputs={styles:{},aux:{}};this.styles={};this.auxData={}};Xinha.InlineStyler.getLength=function(b){var a=parseInt(b);if(isNaN(a)){a=""}return a};Xinha.InlineStyler.prototype.applyStyle=function(f){var b=this.element;var d=b.style;for(var a in f){if(typeof f[a]=="function"){continue}if(f[a]!=null){var e=f[a].value||f[a]}switch(a){case"backgroundImage":if(/\S/.test(e)){d.backgroundImage="url("+e+")"}else{d.backgroundImage="none"}break;case"borderCollapse":d.borderCollapse=f[a]=="on"?"collapse":"separate";break;case"width":if(/\S/.test(e)){d.width=e+this.inputs.aux.widthUnit.value}else{d.width=""}break;case"height":if(/\S/.test(e)){d.height=e+this.inputs.aux.heightUnit.value}else{d.height=""}break;case"textAlign":if(e=="char"){var c=this.inputs.aux.textAlignChar.value;if(c=='"'){c='\\"'}d.textAlign='"'+c+'"'}else{if(e=="-"){d.textAlign=""}else{d.textAlign=e}}break;case"verticalAlign":b.vAlign="";if(e=="-"){d.verticalAlign=""}else{d.verticalAlign=e}break;case"float":if(Xinha.is_ie){d.styleFloat=e}else{d.cssFloat=e}break;case"borderWidth":d[a]=e+"px";break;default:d[a]=e;break}}};Xinha.InlineStyler.prototype.createStyleLayoutFieldset=function(){var m=this;var f=this.editor;var u=this.doc;var d=this.element;var l=u.createElement("fieldset");var c=u.createElement("legend");l.appendChild(c);c.innerHTML=Xinha._lc("Layout","TableOperations");var q=u.createElement("table");l.appendChild(q);q.style.width="100%";var a=u.createElement("tbody");q.appendChild(a);var r=d.tagName.toLowerCase();var b,h,j,n,k,e,o;if(r!="td"&&r!="tr"&&r!="th"){b=u.createElement("tr");a.appendChild(b);h=u.createElement("td");h.className="label";b.appendChild(h);h.innerHTML=Xinha._lc("Float","TableOperations")+":";h=u.createElement("td");b.appendChild(h);n=u.createElement("select");n.name=this.dialog.createId("float");h.appendChild(n);this.inputs.styles["float"]=n;e=["None","Left","Right"];for(var o=0;o0)?d:0)+"px";this.getElementById("ipreview").style.width=this.width-2+"px"};this.dialogReady=true}; \ No newline at end of file + /*--------------------------------------:noTabs=true:tabSize=2:indentSize=2:-- + -- Xinha (is not htmlArea) - http://xinha.org + -- + -- Use of Xinha is granted by the terms of the htmlArea License (based on + -- BSD license) please read license.txt in this package for details. + -- + -- Copyright (c) 2005-2008 Xinha Developer Team and contributors + -- + -- This is the Xinha standard implementation of an image insertion plugin + -- + -- he file is loaded as a special plugin by the Xinha Core when no alternative method (plugin) is loaded. + -- + -- + -- $HeadURL: http://svn.xinha.org/trunk/modules/InsertImage/insert_image.js $ + -- $LastChangedDate: 2010-02-18 15:14:45 +1300 (Thu, 18 Feb 2010) $ + -- $LastChangedRevision: 1239 $ + -- $LastChangedBy: gogo $ + --------------------------------------------------------------------------*/ + +InsertImage._pluginInfo = { + name : "InsertImage", + origin : "Xinha Core", + version : "$LastChangedRevision: 1239 $".replace(/^[^:]*:\s*(.*)\s*\$$/, '$1'), + developer : "The Xinha Core Developer Team", + developer_url : "$HeadURL: http://svn.xinha.org/trunk/modules/InsertImage/insert_image.js $".replace(/^[^:]*:\s*(.*)\s*\$$/, '$1'), + sponsor : "", + sponsor_url : "", + license : "htmlArea" +}; + +function InsertImage(editor) { + this.editor = editor; + var cfg = editor.config; + var self = this; + + + if(typeof editor._insertImage == 'undefined') + { + editor._insertImage = function() { self.show(); }; + // editor.config.btnList.insertimage[3] = function() { self.show(); } + } + } + +InsertImage.prototype._lc = function(string) { + return Xinha._lc(string, 'Xinha'); +}; + +InsertImage.prototype.onGenerateOnce = function() +{ + InsertImage.loadAssets(); +}; + +InsertImage.loadAssets = function() +{ + var self = InsertImage; + if (self.loading) return; + self.loading = true; + Xinha._getback(_editor_url + 'modules/InsertImage/dialog.html', function(getback) { self.html = getback; self.dialogReady = true; }); + Xinha._getback(_editor_url + 'modules/InsertImage/pluginMethods.js', function(getback) { eval(getback); self.methodsReady = true; }); +}; +InsertImage.prototype.onUpdateToolbar = function() +{ + if (!(InsertImage.dialogReady && InsertImage.methodsReady)) + { + this.editor._toolbarObjects.insertimage.state("enabled", false); + } + else this.onUpdateToolbar = null; +}; + +InsertImage.prototype.prepareDialog = function() +{ + var self = this; + var editor = this.editor; + + var dialog = this.dialog = new Xinha.Dialog(editor, InsertImage.html, 'Xinha',{width:410}) + // Connect the OK and Cancel buttons + dialog.getElementById('ok').onclick = function() {self.apply();} + + dialog.getElementById('cancel').onclick = function() { self.dialog.hide()}; + + dialog.getElementById('preview').onclick = function() { + var f_url = dialog.getElementById("f_url"); + var url = f_url.value; + + if (!url) { + alert(dialog._lc("You must enter the URL")); + f_url.focus(); + return false; + } + dialog.getElementById('ipreview').src = url; + return false; + } + this.dialog.onresize = function () + { + var newHeightForPreview = + parseInt(this.height,10) + - this.getElementById('h1').offsetHeight + - this.getElementById('buttons').offsetHeight + - this.getElementById('inputs').offsetHeight + - parseInt(this.rootElem.style.paddingBottom,10); // we have a padding at the bottom, gotta take this into acount + + + this.getElementById("ipreview").style.height = ((newHeightForPreview > 0) ? newHeightForPreview : 0) + "px"; // no-go beyond 0 + + this.getElementById("ipreview").style.width = this.width - 2 + 'px'; // and the width + + } + this.dialogReady = true; +}; Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/modules/InsertImage/pluginMethods.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/modules/InsertImage/pluginMethods.js,v diff -u -r1.2 -r1.3 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/modules/InsertImage/pluginMethods.js 27 Mar 2009 08:20:43 -0000 1.2 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/modules/InsertImage/pluginMethods.js 23 May 2010 11:58:33 -0000 1.3 @@ -1,3 +1,152 @@ -/* 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 */ -InsertImage.prototype.show=function(c){if(!this.dialog){this.prepareDialog()}var b=this.editor;if(typeof c=="undefined"){c=b.getParentElement();if(c&&c.tagName.toLowerCase()!="img"){c=null}}if(c){function a(f,g){var d=f.attributes;for(var e=0;e");Xinha._stopEvent(a);return true;break;case"1":case"2":case"3":case"4":case"5":case"6":this.editor.execCommand("formatblock",false,"");Xinha._stopEvent(a);return true;break}}switch(a.keyCode){case 8:case 46:if(this.handleBackspace()){Xinha._stopEvent(a);return true}break}return false};InternetExplorer.prototype.handleBackspace=function(){var e=this.editor;var f=e.getSelection();if(f.type=="Control"){var g=e.activeElement(f);Xinha.removeFromParent(g);return true}var d=e.createRange(f);var c=d.duplicate();c.moveStart("character",-1);var b=c.parentElement();if(b!=d.parentElement()&&(/^a$/i.test(b.tagName))){c.collapse(true);c.moveEnd("character",1);c.pasteHTML("");c.select();return true}};InternetExplorer.prototype.inwardHtml=function(a){a=a.replace(/<(\/?)del(\s|>|\/)/ig,"<$1strike$2");a=a.replace(/(]+id="__InsertSpan_Workaround_[a-z]+".*?>([\s\S]*?)<\/span>/i,"$1");return a};InternetExplorer.prototype.outwardHtml=function(a){a=a.replace(/ (\s*)(]+id="__InsertSpan_Workaround_[a-z]+".*?>([\s\S]*?)<\/span>/i,"$1");return a};InternetExplorer.prototype.onExecCommand=function(f,d,c){switch(f){case"saveas":var o=null;var l=this.editor;var h=document.createElement("iframe");h.src="about:blank";h.style.display="none";document.body.appendChild(h);try{if(h.contentDocument){o=h.contentDocument}else{o=h.contentWindow.document}}catch(n){}o.open("text/html","replace");var k="";if(l.config.browserQuirksMode===false){var e=''}else{if(l.config.browserQuirksMode===true){var e=""}else{var e=Xinha.getDoctype(document)}}if(!l.config.fullPage){k+=e+"\n";k+="\n";k+="\n";k+='\n';if(typeof l.config.baseHref!="undefined"&&l.config.baseHref!==null){k+='\n'}if(typeof l.config.pageStyleSheets!=="undefined"){for(var j=0;j0){k+=''}}}if(l.config.pageStyle){k+='"}k+="\n";k+="\n";k+=l.getEditorContent();k+="\n";k+=""}else{k=l.getEditorContent();if(k.match(Xinha.RE_doctype)){l.setDoctype(RegExp.$1)}}o.write(k);o.close();o.execCommand(f,d,c);document.body.removeChild(h);return true;break;case"removeformat":var l=this.editor;var b=l.getSelection();var p=l.saveSelection(b);var j,a,g;function m(q){if(q.nodeType!=1){return}q.removeAttribute("style");for(var i=0;i');var h=g.getElementById(j);k.moveToElementText(h);k.select();break;case"JustificationHack":var a=String.fromCharCode(1);k.pasteHTML(a);k.findText(a,-1);k.select();k.execCommand("JustifyNone");k.pasteHTML("");break;case"VisibleCue":default:var a=String.fromCharCode(1);k.pasteHTML(a);k.findText(a,-1);k.select()}}};Xinha.prototype.selectNodeContents=function(d,e){this.focusEditor();this.forceRedraw();var a;var f=typeof e=="undefined"?true:false;if(f&&d.tagName&&d.tagName.toLowerCase().match(/table|img|input|select|textarea/)){a=this._doc.body.createControlRange();a.add(d)}else{a=this._doc.body.createTextRange();if(3==d.nodeType){if(d.parentNode){a.moveToElementText(d.parentNode)}else{a.moveToElementText(this._doc.body)}var g=this._doc.body.createTextRange();var b=0;var c=d.previousSibling;for(;c&&(1!=c.nodeType);c=c.previousSibling){if(3==c.nodeType){b+=c.nodeValue.length-c.nodeValue.split("\r").length-1}}if(c&&(1==c.nodeType)){g.moveToElementText(c);a.setEndPoint("StartToEnd",g)}if(b){a.moveStart("character",b)}b=0;c=d.nextSibling;for(;c&&(1!=c.nodeType);c=c.nextSibling){if(3==c.nodeType){b+=c.nodeValue.length-c.nodeValue.split("\r").length-1;if(!c.nextSibling){b+=1}}}if(c&&(1==c.nodeType)){g.moveToElementText(c);a.setEndPoint("EndToStart",g)}if(b){a.moveEnd("character",-b)}if(!d.nextSibling){a.moveEnd("character",-1)}}else{a.moveToElementText(d)}}if(typeof e!="undefined"){a.collapse(e);if(!e){a.moveStart("character",-1);a.moveEnd("character",-1)}}a.select()};Xinha.prototype.insertHTML=function(b){this.focusEditor();var c=this.getSelection();var a=this.createRange(c);a.pasteHTML(b)};Xinha.prototype.getSelectedHTML=function(){var b=this.getSelection();if(this.selectionEmpty(b)){return""}var a=this.createRange(b);if(a.htmlText){return a.htmlText}else{if(a.length>=1){return a.item(0).outerHTML}}return""};Xinha.prototype.getSelection=function(){return this._doc.selection};Xinha.prototype.createRange=function(a){if(!a){a=this.getSelection()}return a.createRange()};Xinha.prototype.isKeyEvent=function(a){return a.type=="keydown"};Xinha.prototype.getKey=function(a){return String.fromCharCode(a.keyCode)};Xinha.getOuterHTML=function(a){return a.outerHTML};Xinha.cc=String.fromCharCode(8201);Xinha.prototype.setCC=function(h){var d=Xinha.cc;if(h=="textarea"){var f=this._textArea;var j=document.selection.createRange();j.collapse();j.text=d;var g=f.value.indexOf(d);var k=f.value.substring(0,g);var b=f.value.substring(g+d.length,f.value.length);if(b.match(/^[^<]*>/)){var i=b.indexOf(">")+1;f.value=k+b.substring(0,i)+d+b.substring(i,b.length)}else{f.value=k+d+b}f.value=f.value.replace(new RegExp("(&[^"+d+"]*?)("+d+")([^"+d+"]*?;)"),"$1$3$2");f.value=f.value.replace(new RegExp("(]*>[^"+d+"]*?)("+d+")([^"+d+"]*?<\/script>)"),"$1$3$2");f.value=f.value.replace(new RegExp("^([^"+d+"]*)("+d+")([^"+d+"]*]*>)(.*?)"),"$1$3$2$4")}else{var c=this.getSelection();var a=c.createRange();if(c.type=="Control"){var e=a.item(0);e.outerHTML+=d}else{a.collapse();a.text=d}}};Xinha.prototype.findCC=function(b){var a=(b=="textarea")?this._textArea:this._doc.body;range=a.createTextRange();if(range.findText(escape(Xinha.cc))){range.select();range.text="";range.select()}if(range.findText(Xinha.cc)){range.select();range.text="";range.select()}if(b=="textarea"){this._textArea.focus()}};Xinha.getDoctype=function(a){return(a.compatMode=="CSS1Compat"&&Xinha.ie_version<8)?'':""}; \ No newline at end of file + + /*--------------------------------------:noTabs=true:tabSize=2:indentSize=2:-- + -- Xinha (is not htmlArea) - http://xinha.gogo.co.nz/ + -- + -- Use of Xinha is granted by the terms of the htmlArea License (based on + -- BSD license) please read license.txt in this package for details. + -- + -- Xinha was originally based on work by Mihai Bazon which is: + -- Copyright (c) 2003-2004 dynarch.com. + -- Copyright (c) 2002-2003 interactivetools.com, inc. + -- This copyright notice MUST stay intact for use. + -- + -- This is the Internet Explorer compatability plugin, part of the + -- Xinha core. + -- + -- The file is loaded as a special plugin by the Xinha Core when + -- Xinha is being run under an Internet Explorer based browser. + -- + -- It provides implementation and specialisation for various methods + -- in the core where different approaches per browser are required. + -- + -- Design Notes:: + -- Most methods here will simply be overriding Xinha.prototype. + -- and should be called that, but methods specific to IE should + -- be a part of the InternetExplorer.prototype, we won't trample on + -- namespace that way. + -- + -- $HeadURL: http://svn.xinha.org/trunk/modules/InternetExplorer/InternetExplorer.js $ + -- $LastChangedDate: 2010-05-12 00:31:04 +1200 (Wed, 12 May 2010) $ + -- $LastChangedRevision: 1260 $ + -- $LastChangedBy: gogo $ + --------------------------------------------------------------------------*/ + +InternetExplorer._pluginInfo = { + name : "Internet Explorer", + origin : "Xinha Core", + version : "$LastChangedRevision: 1260 $".replace(/^[^:]*:\s*(.*)\s*\$$/, '$1'), + developer : "The Xinha Core Developer Team", + developer_url : "$HeadURL: http://svn.xinha.org/trunk/modules/InternetExplorer/InternetExplorer.js $".replace(/^[^:]*:\s*(.*)\s*\$$/, '$1'), + sponsor : "", + sponsor_url : "", + license : "htmlArea" +}; + +function InternetExplorer(editor) { + this.editor = editor; + editor.InternetExplorer = this; // So we can do my_editor.InternetExplorer.doSomethingIESpecific(); +} + +/** Allow Internet Explorer to handle some key events in a special way. + */ + +InternetExplorer.prototype.onKeyPress = function(ev) +{ + // Shortcuts + if(this.editor.isShortCut(ev)) + { + switch(this.editor.getKey(ev).toLowerCase()) + { + case 'n': + { + this.editor.execCommand('formatblock', false, '

'); + Xinha._stopEvent(ev); + return true; + } + break; + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + { + this.editor.execCommand('formatblock', false, ''); + Xinha._stopEvent(ev); + return true; + } + break; + } + } + + switch(ev.keyCode) + { + case 8: // KEY backspace + case 46: // KEY delete + { + if(this.handleBackspace()) + { + Xinha._stopEvent(ev); + return true; + } + } + break; + + case 9: // KEY tab, see ticket #1121 + { + Xinha._stopEvent(ev); + return true; + } + + } + + return false; +} + +/** When backspace is hit, the IE onKeyPress will execute this method. + * It preserves links when you backspace over them and apparently + * deletes control elements (tables, images, form fields) in a better + * way. + * + * @returns true|false True when backspace has been handled specially + * false otherwise (should pass through). + */ + +InternetExplorer.prototype.handleBackspace = function() +{ + var editor = this.editor; + var sel = editor.getSelection(); + if ( sel.type == 'Control' ) + { + var elm = editor.activeElement(sel); + Xinha.removeFromParent(elm); + return true; + } + + // This bit of code preseves links when you backspace over the + // endpoint of the link in IE. Without it, if you have something like + // link_here | + // where | is the cursor, and backspace over the last e, then the link + // will de-link, which is a bit tedious + var range = editor.createRange(sel); + var r2 = range.duplicate(); + r2.moveStart("character", -1); + var a = r2.parentElement(); + // @fixme: why using again a regex to test a single string ??? + if ( a != range.parentElement() && ( /^a$/i.test(a.tagName) ) ) + { + r2.collapse(true); + r2.moveEnd("character", 1); + r2.pasteHTML(''); + r2.select(); + return true; + } +}; + +InternetExplorer.prototype.inwardHtml = function(html) +{ + // Both IE and Gecko use strike internally instead of del (#523) + // Xinha will present del externally (see Xinha.prototype.outwardHtml + html = html.replace(/<(\/?)del(\s|>|\/)/ig, "<$1strike$2"); + // ie eats scripts and comments at beginning of page, so + // make sure there is something before the first script on the page + html = html.replace(/(]+id="__InsertSpan_Workaround_[a-z]+".*?>([\s\S]*?)<\/span>/i,"$1"); + + return html; +} + +InternetExplorer.prototype.outwardHtml = function(html) +{ + // remove space added before first script on the page + html = html.replace(/ (\s*)(]+id="__InsertSpan_Workaround_[a-z]+".*?>([\s\S]*?)<\/span>/i,"$1"); + + return html; +} + +InternetExplorer.prototype.onExecCommand = function(cmdID, UI, param) +{ + switch(cmdID) + { + // #645 IE only saves the initial content of the iframe, so we create a temporary iframe with the current editor contents + case 'saveas': + var doc = null; + var editor = this.editor; + var iframe = document.createElement("iframe"); + iframe.src = "about:blank"; + iframe.style.display = 'none'; + document.body.appendChild(iframe); + try + { + if ( iframe.contentDocument ) + { + doc = iframe.contentDocument; + } + else + { + doc = iframe.contentWindow.document; + } + } + catch(ex) + { + //hope there's no exception + } + + doc.open("text/html","replace"); + var html = ''; + if ( editor.config.browserQuirksMode === false ) + { + var doctype = ''; + } + else if ( editor.config.browserQuirksMode === true ) + { + var doctype = ''; + } + else + { + var doctype = Xinha.getDoctype(document); + } + if ( !editor.config.fullPage ) + { + html += doctype + "\n"; + html += "\n"; + html += "\n"; + html += "\n"; + if ( typeof editor.config.baseHref != 'undefined' && editor.config.baseHref !== null ) + { + html += "\n"; + } + + if ( typeof editor.config.pageStyleSheets !== 'undefined' ) + { + for ( var i = 0; i < editor.config.pageStyleSheets.length; i++ ) + { + if ( editor.config.pageStyleSheets[i].length > 0 ) + { + html += ""; + //html += "\n"; + } + } + } + + if ( editor.config.pageStyle ) + { + html += ""; + } + + html += "\n"; + html += "\n"; + html += editor.getEditorContent(); + html += "\n"; + html += ""; + } + else + { + html = editor.getEditorContent(); + if ( html.match(Xinha.RE_doctype) ) + { + editor.setDoctype(RegExp.$1); + } + } + doc.write(html); + doc.close(); + doc.execCommand(cmdID, UI, param); + document.body.removeChild(iframe); + return true; + break; + case 'removeformat': + var editor = this.editor; + var sel = editor.getSelection(); + var selSave = editor.saveSelection(sel); + + var i, el, els; + + function clean (el) + { + if (el.nodeType != 1) return; + el.removeAttribute('style'); + for (var j=0; jText Node

Text in Block

+ // ^ + // The problem occurs when the cursor is after the 'e' in Node. + + var solution = this.config.selectWorkaround || 'VisibleCue'; + switch (solution) + { + case 'SimulateClick': + // Try to get the bounding box of the selection and then simulate a + // mouse click in the upper right corner to return the cursor to the + // correct location. + + // No code yet, fall through to InsertSpan + case 'InsertSpan': + // This workaround inserts an empty span element so that we are no + // longer trying to select a text node, + var parentDoc = findDoc(savedParentElement); + + // A function used to generate a unique ID for our temporary span. + var randLetters = function(count) + { + // Build a list of 26 letters. + var Letters = ''; + for (var index = 0; index<26; ++index) + { + Letters += String.fromCharCode('a'.charCodeAt(0) + index); + } + + var result = ''; + for (var index=0; index'); + var tempSpan = parentDoc.getElementById(tempId); + savedSelection.moveToElementText(tempSpan); + savedSelection.select(); + break; + case 'JustificationHack': + // Setting the justification on an element causes IE to alter the + // markup so that the selection we want to make is possible. + // Unfortunately, this can force block elements to be kicked out of + // their containing element, so it is not recommended. + + // Set a non-valid character and use it to anchor our selection. + var magicString = String.fromCharCode(1); + savedSelection.pasteHTML(magicString); + savedSelection.findText(magicString,-1); + savedSelection.select(); + + // I don't know how to find out if there's an existing justification on + // this element. Hopefully, you're doing all of your styling outside, + // so I'll just clear. I already told you this was a hack. + savedSelection.execCommand('JustifyNone'); + savedSelection.pasteHTML(''); + break; + case 'VisibleCue': + default: + // This method will insert a little box character to hold our selection + // in the desired spot. We're depending on the user to see this ugly + // box and delete it themselves. + var magicString = String.fromCharCode(1); + savedSelection.pasteHTML(magicString); + savedSelection.findText(magicString,-1); + savedSelection.select(); + } + } +} + +/** + * Selects the contents of the given node. If the node is a "control" type element, (image, form input, table) + * the node itself is selected for manipulation. + * + * @param node DomNode + * @param collapseToStart A boolean that, when supplied, says to collapse the selection. True collapses to the start, and false to the end. + */ + +Xinha.prototype.selectNodeContents = function(node, collapseToStart) +{ + this.focusEditor(); + this.forceRedraw(); + var range; + var collapsed = typeof collapseToStart == "undefined" ? true : false; + // Tables and Images get selected as "objects" rather than the text contents + if ( collapsed && node.tagName && node.tagName.toLowerCase().match(/table|img|input|select|textarea/) ) + { + range = this._doc.body.createControlRange(); + range.add(node); + } + else + { + range = this._doc.body.createTextRange(); + if (3 == node.nodeType) + { + // Special handling for text nodes, since moveToElementText fails when + // attempting to select a text node + + // Since the TextRange has a quite limited API, our strategy here is to + // select (where possible) neighboring nodes, and then move our ranges + // endpoints to be just inside of neighboring selections. + if (node.parentNode) + { + range.moveToElementText(node.parentNode); + } else + { + range.moveToElementText(this._doc.body); + } + var trimmingRange = this._doc.body.createTextRange(); + + // In rare situations (mostly html that's been monkeyed about with by + // javascript, but that's what we're doing) there can be two adjacent + // text nodes. Since we won't be able to handle these, we'll have to + // hack an offset by 'move'ing the number of characters they contain. + var texthackOffset = 0; + var borderElement=node.previousSibling; + for (; borderElement && (1 != borderElement.nodeType); borderElement = borderElement.previousSibling) + { + if (3 == borderElement.nodeType) + { + // IE doesn't count '\r' as a character, so we have to adjust the offset. + texthackOffset += borderElement.nodeValue.length-borderElement.nodeValue.split('\r').length-1; + } + } + if (borderElement && (1 == borderElement.nodeType)) + { + trimmingRange.moveToElementText(borderElement); + range.setEndPoint('StartToEnd', trimmingRange); + } + if (texthackOffset) + { + // We now need to move the selection forward the number of characters + // in all text nodes in between our text node and our ranges starting + // border. + range.moveStart('character',texthackOffset); + } + + // Youpi! Now we get to repeat this trimming on the right side. + texthackOffset = 0; + borderElement=node.nextSibling; + for (; borderElement && (1 != borderElement.nodeType); borderElement = borderElement.nextSibling) + { + if (3 == borderElement.nodeType) + { + // IE doesn't count '\r' as a character, so we have to adjust the offset. + texthackOffset += borderElement.nodeValue.length-borderElement.nodeValue.split('\r').length-1; + if (!borderElement.nextSibling) + { + // When a text node is the last child, IE adds an extra selection + // "placeholder" for the newline character. We need to adjust for + // this character as well. + texthackOffset += 1; + } + } + } + if (borderElement && (1 == borderElement.nodeType)) + { + trimmingRange.moveToElementText(borderElement); + range.setEndPoint('EndToStart', trimmingRange); + } + if (texthackOffset) + { + // We now need to move the selection backward the number of characters + // in all text nodes in between our text node and our ranges ending + // border. + range.moveEnd('character',-texthackOffset); + } + if (!node.nextSibling) + { + // Above we performed a slight adjustment to the offset if the text + // node contains a selectable "newline". We need to do the same if the + // node we are trying to select contains a newline. + range.moveEnd('character',-1); + } + } + else + { + range.moveToElementText(node); + } + } + if (typeof collapseToStart != "undefined") + { + range.collapse(collapseToStart); + if (!collapseToStart) + { + range.moveStart('character',-1); + range.moveEnd('character',-1); + } + } + range.select(); +}; + +/** Insert HTML at the current position, deleting the selection if any. + * + * @param html string + */ + +Xinha.prototype.insertHTML = function(html) +{ + this.focusEditor(); + var sel = this.getSelection(); + var range = this.createRange(sel); + range.pasteHTML(html); +}; + + +/** Get the HTML of the current selection. HTML returned has not been passed through outwardHTML. + * + * @returns string + */ + +Xinha.prototype.getSelectedHTML = function() +{ + var sel = this.getSelection(); + if (this.selectionEmpty(sel)) return ''; + var range = this.createRange(sel); + + // Need to be careful of control ranges which won't have htmlText + if( range.htmlText ) + { + return range.htmlText; + } + else if(range.length >= 1) + { + return range.item(0).outerHTML; + } + + return ''; +}; + +/** Get a Selection object of the current selection. Note that selection objects are browser specific. + * + * @returns Selection + */ + +Xinha.prototype.getSelection = function() +{ + return this._doc.selection; +}; + +/** Create a Range object from the given selection. Note that range objects are browser specific. + * + * @param sel Selection object (see getSelection) + * @returns Range + */ + +Xinha.prototype.createRange = function(sel) +{ + if (!sel) sel = this.getSelection(); + + // ticket:1508 - when you do a key event within a + // absolute position div, in IE, the toolbar update + // for formatblock etc causes a getParentElement() (above) + // which produces a "None" select, then if we focusEditor() it + // defocuses the absolute div and focuses into the iframe outside of the + // div somewhere. + // + // Removing this is probably a workaround and maybe it breaks something else + // focusEditor is used in a number of spots, I woudl have thought it should + // do nothing if the editor is already focused. + // + // if(sel.type == 'None') this.focusEditor(); + + return sel.createRange(); +}; + +/** Determine if the given event object is a keydown/press event. + * + * @param event Event + * @returns true|false + */ + +Xinha.prototype.isKeyEvent = function(event) +{ + return event.type == "keydown"; +} + +/** Return the character (as a string) of a keyEvent - ie, press the 'a' key and + * this method will return 'a', press SHIFT-a and it will return 'A'. + * + * @param keyEvent + * @returns string + */ + +Xinha.prototype.getKey = function(keyEvent) +{ + return String.fromCharCode(keyEvent.keyCode); +} + + +/** Return the HTML string of the given Element, including the Element. + * + * @param element HTML Element DomNode + * @returns string + */ + +Xinha.getOuterHTML = function(element) +{ + return element.outerHTML; +}; + +// Control character for retaining edit location when switching modes +Xinha.cc = String.fromCharCode(0x2009); + +Xinha.prototype.setCC = function ( target ) +{ + var cc = Xinha.cc; + if ( target == "textarea" ) + { + var ta = this._textArea; + var pos = document.selection.createRange(); + pos.collapse(); + pos.text = cc; + var index = ta.value.indexOf( cc ); + var before = ta.value.substring( 0, index ); + var after = ta.value.substring( index + cc.length , ta.value.length ); + + if ( after.match(/^[^<]*>/) ) // make sure cursor is in an editable area (outside tags, script blocks, entities, and inside the body) + { + var tagEnd = after.indexOf(">") + 1; + ta.value = before + after.substring( 0, tagEnd ) + cc + after.substring( tagEnd, after.length ); + } + else ta.value = before + cc + after; + ta.value = ta.value.replace(new RegExp ('(&[^'+cc+']*?)('+cc+')([^'+cc+']*?;)'), "$1$3$2"); + ta.value = ta.value.replace(new RegExp ('(]*>[^'+cc+']*?)('+cc+')([^'+cc+']*?<\/script>)'), "$1$3$2"); + ta.value = ta.value.replace(new RegExp ('^([^'+cc+']*)('+cc+')([^'+cc+']*]*>)(.*?)'), "$1$3$2$4"); + } + else + { + var sel = this.getSelection(); + var r = sel.createRange(); + if ( sel.type == 'Control' ) + { + var control = r.item(0); + control.outerHTML += cc; + } + else + { + r.collapse(); + r.text = cc; + } + } +}; + +Xinha.prototype.findCC = function ( target ) +{ + var findIn = ( target == 'textarea' ) ? this._textArea : this._doc.body; + range = findIn.createTextRange(); + // in case the cursor is inside a link automatically created from a url + // the cc also appears in the url and we have to strip it out additionally + if( range.findText( escape(Xinha.cc) ) ) + { + range.select(); + range.text = ''; + range.select(); + } + if( range.findText( Xinha.cc ) ) + { + range.select(); + range.text = ''; + range.select(); + } + if ( target == 'textarea' ) this._textArea.focus(); +}; + +/** Return a doctype or empty string depending on whether the document is in Qirksmode or Standards Compliant Mode + * It's hardly possible to detect the actual doctype without unreasonable effort, so we set HTML 4.01 just to trigger the rendering mode + * + * @param doc DOM element document + * @returns string doctype || empty + */ +Xinha.getDoctype = function (doc) +{ + return (doc.compatMode == "CSS1Compat" && Xinha.ie_version < 8 ) ? '' : ''; +}; Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/modules/Opera/Opera.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/modules/Opera/Opera.js,v diff -u -r1.3 -r1.4 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/modules/Opera/Opera.js 27 Mar 2009 08:20:43 -0000 1.3 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/modules/Opera/Opera.js 23 May 2010 11:58:33 -0000 1.4 @@ -1,3 +1,902 @@ -/* 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 */ -Opera._pluginInfo={name:"Opera",origin:"Xinha Core",version:"$LastChangedRevision: 1084 $".replace(/^[^:]*:\s*(.*)\s*\$$/,"$1"),developer:"The Xinha Core Developer Team",developer_url:"$HeadURL: http://svn.xinha.org/trunk/modules/Opera/Opera.js $".replace(/^[^:]*:\s*(.*)\s*\$$/,"$1"),sponsor:"Gogo Internet Services Limited",sponsor_url:"http://www.gogo.co.nz/",license:"htmlArea"};function Opera(a){this.editor=a;a.Opera=this}Opera.prototype.onKeyPress=function(u){var d=this.editor;var j=d.getSelection();if(d.isShortCut(u)){switch(d.getKey(u).toLowerCase()){case"z":if(d._unLink&&d._unlinkOnUndo){Xinha._stopEvent(u);d._unLink();d.updateToolbar();return true}break;case"a":sel=d.getSelection();sel.removeAllRanges();range=d.createRange();range.selectNodeContents(d._doc.body);sel.addRange(range);Xinha._stopEvent(u);return true;break;case"v":if(!d.config.htmlareaPaste){return true}break}}switch(d.getKey(u)){case" ":var g=function(y,m){var x=y.nextSibling;if(typeof m=="string"){m=d._doc.createElement(m)}var s=y.parentNode.insertBefore(m,x);Xinha.removeFromParent(y);s.appendChild(y);x.data=" "+x.data;j.collapse(x,1);d._unLink=function(){var a=s.firstChild;s.removeChild(a);s.parentNode.insertBefore(a,s);Xinha.removeFromParent(s);d._unLink=null;d._unlinkOnUndo=false};d._unlinkOnUndo=true;return s};if(d.config.convertUrlsToLinks&&j&&j.isCollapsed&&j.anchorNode.nodeType==3&&j.anchorNode.data.length>3&&j.anchorNode.data.indexOf(".")>=0){var t=j.anchorNode.data.substring(0,j.anchorOffset).search(/\S{4,}$/);if(t==-1){break}if(d._getFirstAncestor(j,"a")){break}var h=j.anchorNode.data.substring(0,j.anchorOffset).replace(/^.*?(\S*)$/,"$1");var e=h.match(Xinha.RE_email);if(e){var v=j.anchorNode;var f=v.splitText(j.anchorOffset);var k=v.splitText(t);g(k,"a").href="mailto:"+e[0];break}RE_date=/([0-9]+\.)+/;RE_ip=/(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/;var p=h.match(Xinha.RE_url);if(p){if(RE_date.test(h)){break}var i=j.anchorNode;var b=i.splitText(j.anchorOffset);var q=i.splitText(t);g(q,"a").href=(p[1]?p[1]:"http://")+p[2];break}}break}switch(u.keyCode){case 27:if(d._unLink){d._unLink();Xinha._stopEvent(u)}break;break;case 8:case 46:if(!u.shiftKey&&this.handleBackspace()){Xinha._stopEvent(u)}default:d._unlinkOnUndo=false;if(j.anchorNode&&j.anchorNode.nodeType==3){var w=d._getFirstAncestor(j,"a");if(!w){break}if(!w._updateAnchTimeout){if(j.anchorNode.data.match(Xinha.RE_email)&&w.href.match("mailto:"+j.anchorNode.data.trim())){var l=j.anchorNode;var c=function(){w.href="mailto:"+l.data.trim();w._updateAnchTimeout=setTimeout(c,250)};w._updateAnchTimeout=setTimeout(c,1000);break}var n=j.anchorNode.data.match(Xinha.RE_url);if(n&&w.href.match(new RegExp("http(s)?://"+Xinha.escapeStringForRegExp(j.anchorNode.data.trim())))){var o=j.anchorNode;var r=function(){n=o.data.match(Xinha.RE_url);if(n){w.href=(n[1]?n[1]:"http://")+n[2]}w._updateAnchTimeout=setTimeout(r,250)};w._updateAnchTimeout=setTimeout(r,1000)}}}break}return false};Opera.prototype.handleBackspace=function(){var a=this.editor;setTimeout(function(){var e=a.getSelection();var g=a.createRange(e);var f=g.startContainer;var i=g.startOffset;var c=g.endContainer;var h=g.endOffset;var j=f.nextSibling;if(f.nodeType==3){f=f.parentNode}if(!(/\S/.test(f.tagName))){var d=document.createElement("p");while(f.firstChild){d.appendChild(f.firstChild)}f.parentNode.insertBefore(d,f);Xinha.removeFromParent(f);var b=g.cloneRange();b.setStartBefore(j);b.setEndAfter(j);b.extractContents();e.removeAllRanges();e.addRange(b)}},10)};Opera.prototype.inwardHtml=function(a){a=a.replace(/<(\/?)del(\s|>|\/)/ig,"<$1strike$2");return a};Opera.prototype.outwardHtml=function(a){return a};Opera.prototype.onExecCommand=function(f,e,d){switch(f){case"removeformat":var k=this.editor;var c=k.getSelection();var l=k.saveSelection(c);var j=k.createRange(c);var g=k._doc.body.getElementsByTagName("*");var a=(j.startContainer.nodeType==1)?j.startContainer:j.startContainer.parentNode;var h,b;if(c.isCollapsed){j.selectNodeContents(k._doc.body)}for(h=0;ha.anchorOffset&&a.anchorNode.childNodes[a.anchorOffset].nodeType==1){return a.anchorNode.childNodes[a.anchorOffset]}else{if(a.anchorNode.nodeType==1){return a.anchorNode}else{return null}}}return null};Xinha.prototype.selectionEmpty=function(a){if(!a){return true}if(typeof a.isCollapsed!="undefined"){return a.isCollapsed}return true};Xinha.prototype.saveSelection=function(){return this.createRange(this.getSelection()).cloneRange()};Xinha.prototype.restoreSelection=function(a){var b=this.getSelection();b.removeAllRanges();b.addRange(a)};Xinha.prototype.selectNodeContents=function(b,d){this.focusEditor();this.forceRedraw();var a;var e=typeof d=="undefined"?true:false;var c=this.getSelection();a=this._doc.createRange();if(e&&b.tagName&&b.tagName.toLowerCase().match(/table|img|input|textarea|select/)){a.selectNode(b)}else{a.selectNodeContents(b)}c.removeAllRanges();c.addRange(a);if(typeof d!="undefined"){if(d){c.collapse(a.startContainer,a.startOffset)}else{c.collapse(a.endContainer,a.endOffset)}}};Xinha.prototype.insertHTML=function(c){var e=this.getSelection();var a=this.createRange(e);this.focusEditor();var b=this._doc.createDocumentFragment();var f=this._doc.createElement("div");f.innerHTML=c;while(f.firstChild){b.appendChild(f.firstChild)}var d=this.insertNodeAtSelection(b)};Xinha.prototype.getSelectedHTML=function(){var b=this.getSelection();if(b.isCollapsed){return""}var a=this.createRange(b);return Xinha.getHTML(a.cloneContents(),false,this)};Xinha.prototype.getSelection=function(){var c=this._iframe.contentWindow.getSelection();if(c&&c.focusNode&&c.focusNode.tagName&&c.focusNode.tagName=="HTML"){var b=this._doc.getElementsByTagName("body")[0];var a=this.createRange();a.selectNodeContents(b);c.removeAllRanges();c.addRange(a);c.collapseToEnd()}return c};Xinha.prototype.createRange=function(b){this.activateEditor();if(typeof b!="undefined"){try{return b.getRangeAt(0)}catch(a){return this._doc.createRange()}}else{return this._doc.createRange()}};Xinha.prototype.isKeyEvent=function(a){return a.type=="keypress"};Xinha.prototype.getKey=function(a){return String.fromCharCode(a.charCode)};Xinha.getOuterHTML=function(a){return(new XMLSerializer()).serializeToString(a)};Xinha.cc=String.fromCharCode(8286);Xinha.prototype.setCC=function(i){var c=Xinha.cc;try{if(i=="textarea"){var f=this._textArea;var g=f.selectionStart;var k=f.value.substring(0,g);var a=f.value.substring(g,f.value.length);if(a.match(/^[^<]*>/)){var j=a.indexOf(">")+1;f.value=k+a.substring(0,j)+c+a.substring(j,a.length)}else{f.value=k+c+a}f.value=f.value.replace(new RegExp("(&[^"+c+"]*?)("+c+")([^"+c+"]*?;)"),"$1$3$2");f.value=f.value.replace(new RegExp("(]*>[^"+c+"]*?)("+c+")([^"+c+"]*?<\/script>)"),"$1$3$2");f.value=f.value.replace(new RegExp("^([^"+c+"]*)("+c+")([^"+c+"]*]*>)(.*?)"),"$1$3$2$4");f.value=f.value.replace(c,'MARK')}else{var b=this.getSelection();var d=this._doc.createElement("span");d.id="XinhaOperaCaretMarker";b.getRangeAt(0).insertNode(d)}}catch(h){}};Xinha.prototype.findCC=function(i){if(i=="textarea"){var h=this._textArea;var j=h.value.search(/(((\s|(MARK))*<\/span>)?)/);if(j==-1){return}var e=RegExp.$1;var f=j+e.length;var k=h.value.substring(0,j);var b=h.value.substring(f,h.value.length);h.value=k;h.scrollTop=h.scrollHeight;var d=h.scrollTop;h.value+=b;h.setSelectionRange(j,j);h.focus();h.scrollTop=d}else{var g=this._doc.getElementById("XinhaOperaCaretMarker");if(g){this.focusEditor();var a=this.createRange();a.selectNode(g);var c=this.getSelection();c.addRange(a);c.collapseToStart();this.scrollToElement(g);g.parentNode.removeChild(g);return}}};Xinha.getDoctype=function(a){var b="";if(a.doctype){b+=""}return b};Xinha.prototype._standardInitIframe=Xinha.prototype.initIframe;Xinha.prototype.initIframe=function(){if(!this._iframeLoadDone){if(this._iframe.contentWindow&&this._iframe.contentWindow.xinhaReadyToRoll){this._iframeLoadDone=true;this._standardInitIframe()}else{var a=this;setTimeout(function(){a.initIframe()},5)}}};Xinha._addEventOperaOrig=Xinha._addEvent;Xinha._addEvent=function(a,c,b){if(a.tagName&&a.tagName.toLowerCase()=="select"&&c=="change"){return Xinha.addDom0Event(a,c,b)}return Xinha._addEventOperaOrig(a,c,b)}; \ No newline at end of file + + /*--------------------------------------:noTabs=true:tabSize=2:indentSize=2:-- + -- Xinha (is not htmlArea) - http://xinha.gogo.co.nz/ + -- + -- Use of Xinha is granted by the terms of the htmlArea License (based on + -- BSD license) please read license.txt in this package for details. + -- + -- Xinha was originally based on work by Mihai Bazon which is: + -- Copyright (c) 2003-2004 dynarch.com. + -- Copyright (c) 2002-2003 interactivetools.com, inc. + -- This copyright notice MUST stay intact for use. + -- + -- This is the Opera compatability plugin, part of the Xinha core. + -- + -- The file is loaded as a special plugin by the Xinha Core when + -- Xinha is being run under an Opera based browser with the Midas + -- editing API. + -- + -- It provides implementation and specialisation for various methods + -- in the core where different approaches per browser are required. + -- + -- Design Notes:: + -- Most methods here will simply be overriding Xinha.prototype. + -- and should be called that, but methods specific to Opera should + -- be a part of the Opera.prototype, we won't trample on namespace + -- that way. + -- + -- $HeadURL: http://svn.xinha.org/trunk/modules/Opera/Opera.js $ + -- $LastChangedDate: 2008-10-13 06:42:42 +1300 (Mon, 13 Oct 2008) $ + -- $LastChangedRevision: 1084 $ + -- $LastChangedBy: ray $ + --------------------------------------------------------------------------*/ + +Opera._pluginInfo = { + name : "Opera", + origin : "Xinha Core", + version : "$LastChangedRevision: 1084 $".replace(/^[^:]*:\s*(.*)\s*\$$/, '$1'), + developer : "The Xinha Core Developer Team", + developer_url : "$HeadURL: http://svn.xinha.org/trunk/modules/Opera/Opera.js $".replace(/^[^:]*:\s*(.*)\s*\$$/, '$1'), + sponsor : "Gogo Internet Services Limited", + sponsor_url : "http://www.gogo.co.nz/", + license : "htmlArea" +}; + +function Opera(editor) { + this.editor = editor; + editor.Opera = this; +} + +/** Allow Opera to handle some key events in a special way. + */ + +Opera.prototype.onKeyPress = function(ev) +{ + var editor = this.editor; + var s = editor.getSelection(); + + // Handle shortcuts + if(editor.isShortCut(ev)) + { + switch(editor.getKey(ev).toLowerCase()) + { + case 'z': + { + if(editor._unLink && editor._unlinkOnUndo) + { + Xinha._stopEvent(ev); + editor._unLink(); + editor.updateToolbar(); + return true; + } + } + break; + + case 'a': + { + // KEY select all + sel = editor.getSelection(); + sel.removeAllRanges(); + range = editor.createRange(); + range.selectNodeContents(editor._doc.body); + sel.addRange(range); + Xinha._stopEvent(ev); + return true; + } + break; + + case 'v': + { + // If we are not using htmlareaPaste, don't let Xinha try and be fancy but let the + // event be handled normally by the browser (don't stopEvent it) + if(!editor.config.htmlareaPaste) + { + return true; + } + } + break; + } + } + + // Handle normal characters + switch(editor.getKey(ev)) + { + // Space, see if the text just typed looks like a URL, or email address + // and link it appropriatly + case ' ': + { + var autoWrap = function (textNode, tag) + { + var rightText = textNode.nextSibling; + if ( typeof tag == 'string') + { + tag = editor._doc.createElement(tag); + } + var a = textNode.parentNode.insertBefore(tag, rightText); + Xinha.removeFromParent(textNode); + a.appendChild(textNode); + rightText.data = ' ' + rightText.data; + + s.collapse(rightText, 1); + + editor._unLink = function() + { + var t = a.firstChild; + a.removeChild(t); + a.parentNode.insertBefore(t, a); + Xinha.removeFromParent(a); + editor._unLink = null; + editor._unlinkOnUndo = false; + }; + editor._unlinkOnUndo = true; + + return a; + }; + + if ( editor.config.convertUrlsToLinks && s && s.isCollapsed && s.anchorNode.nodeType == 3 && s.anchorNode.data.length > 3 && s.anchorNode.data.indexOf('.') >= 0 ) + { + var midStart = s.anchorNode.data.substring(0,s.anchorOffset).search(/\S{4,}$/); + if ( midStart == -1 ) + { + break; + } + + if ( editor._getFirstAncestor(s, 'a') ) + { + break; // already in an anchor + } + + var matchData = s.anchorNode.data.substring(0,s.anchorOffset).replace(/^.*?(\S*)$/, '$1'); + + var mEmail = matchData.match(Xinha.RE_email); + if ( mEmail ) + { + var leftTextEmail = s.anchorNode; + var rightTextEmail = leftTextEmail.splitText(s.anchorOffset); + var midTextEmail = leftTextEmail.splitText(midStart); + + autoWrap(midTextEmail, 'a').href = 'mailto:' + mEmail[0]; + break; + } + + RE_date = /([0-9]+\.)+/; //could be date or ip or something else ... + RE_ip = /(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/; + var mUrl = matchData.match(Xinha.RE_url); + if ( mUrl ) + { + if (RE_date.test(matchData)) + { + break; //ray: disabling linking of IP numbers because of general bugginess (see Ticket #1085) + /*if (!RE_ip.test(matchData)) + { + break; + }*/ + } + var leftTextUrl = s.anchorNode; + var rightTextUrl = leftTextUrl.splitText(s.anchorOffset); + var midTextUrl = leftTextUrl.splitText(midStart); + autoWrap(midTextUrl, 'a').href = (mUrl[1] ? mUrl[1] : 'http://') + mUrl[2]; + break; + } + } + } + break; + } + + // Handle special keys + switch ( ev.keyCode ) + { +/* This is now handled by a plugin + case 13: // ENTER + + break;*/ + + case 27: // ESCAPE + { + if ( editor._unLink ) + { + editor._unLink(); + Xinha._stopEvent(ev); + } + break; + } + break; + + case 8: // KEY backspace + case 46: // KEY delete + { + // We handle the mozilla backspace directly?? + if ( !ev.shiftKey && this.handleBackspace() ) + { + Xinha._stopEvent(ev); + } + } + + default: + { + editor._unlinkOnUndo = false; + + // Handle the "auto-linking", specifically this bit of code sets up a handler on + // an self-titled anchor (eg www.gogo.co.nz) + // when the text content is edited, such that it will update the href on the anchor + + if ( s.anchorNode && s.anchorNode.nodeType == 3 ) + { + // See if we might be changing a link + var a = editor._getFirstAncestor(s, 'a'); + // @todo: we probably need here to inform the setTimeout below that we not changing a link and not start another setTimeout + if ( !a ) + { + break; // not an anchor + } + + if ( !a._updateAnchTimeout ) + { + if ( s.anchorNode.data.match(Xinha.RE_email) && a.href.match('mailto:' + s.anchorNode.data.trim()) ) + { + var textNode = s.anchorNode; + var fnAnchor = function() + { + a.href = 'mailto:' + textNode.data.trim(); + // @fixme: why the hell do another timeout is started ? + // This lead to never ending timer if we dont remove this line + // But when removed, the email is not correctly updated + // + // - to fix this we should make fnAnchor check to see if textNode.data has + // stopped changing for say 5 seconds and if so we do not make this setTimeout + a._updateAnchTimeout = setTimeout(fnAnchor, 250); + }; + a._updateAnchTimeout = setTimeout(fnAnchor, 1000); + break; + } + + var m = s.anchorNode.data.match(Xinha.RE_url); + + if ( m && a.href.match(new RegExp( 'http(s)?://' + Xinha.escapeStringForRegExp( s.anchorNode.data.trim() ) ) ) ) + { + var txtNode = s.anchorNode; + var fnUrl = function() + { + // Sometimes m is undefined becase the url is not an url anymore (was www.url.com and become for example www.url) + // ray: shouldn't the link be un-linked then? + m = txtNode.data.match(Xinha.RE_url); + if(m) + { + a.href = (m[1] ? m[1] : 'http://') + m[2]; + } + + // @fixme: why the hell do another timeout is started ? + // This lead to never ending timer if we dont remove this line + // But when removed, the url is not correctly updated + // + // - to fix this we should make fnUrl check to see if textNode.data has + // stopped changing for say 5 seconds and if so we do not make this setTimeout + a._updateAnchTimeout = setTimeout(fnUrl, 250); + }; + a._updateAnchTimeout = setTimeout(fnUrl, 1000); + } + } + } + } + break; + } + + return false; // Let other plugins etc continue from here. +} + +/** When backspace is hit, the Opera onKeyPress will execute this method. + * I don't remember what the exact purpose of this is though :-( + */ + +Opera.prototype.handleBackspace = function() +{ + var editor = this.editor; + setTimeout( + function() + { + var sel = editor.getSelection(); + var range = editor.createRange(sel); + var SC = range.startContainer; + var SO = range.startOffset; + var EC = range.endContainer; + var EO = range.endOffset; + var newr = SC.nextSibling; + if ( SC.nodeType == 3 ) + { + SC = SC.parentNode; + } + if ( ! ( /\S/.test(SC.tagName) ) ) + { + var p = document.createElement("p"); + while ( SC.firstChild ) + { + p.appendChild(SC.firstChild); + } + SC.parentNode.insertBefore(p, SC); + Xinha.removeFromParent(SC); + var r = range.cloneRange(); + r.setStartBefore(newr); + r.setEndAfter(newr); + r.extractContents(); + sel.removeAllRanges(); + sel.addRange(r); + } + }, + 10); +}; + +Opera.prototype.inwardHtml = function(html) +{ + // Both IE and Opera use strike internally instead of del (#523) + // Xinha will present del externally (see Xinha.prototype.outwardHtml + html = html.replace(/<(\/?)del(\s|>|\/)/ig, "<$1strike$2"); + + return html; +} + +Opera.prototype.outwardHtml = function(html) +{ + + return html; +} + +Opera.prototype.onExecCommand = function(cmdID, UI, param) +{ + + switch(cmdID) + { + + /*case 'paste': + { + alert(Xinha._lc("The Paste button does not work in Mozilla based web browsers (technical security reasons). Press CTRL-V on your keyboard to paste directly.")); + return true; // Indicate paste is done, stop command being issued to browser by Xinha.prototype.execCommand + } + break;*/ + + case 'removeformat': + var editor = this.editor; + var sel = editor.getSelection(); + var selSave = editor.saveSelection(sel); + var range = editor.createRange(sel); + + var els = editor._doc.body.getElementsByTagName('*'); + + var start = ( range.startContainer.nodeType == 1 ) ? range.startContainer : range.startContainer.parentNode; + var i, el; + if (sel.isCollapsed) range.selectNodeContents(editor._doc.body); + + for (i=0; i sel.anchorOffset && sel.anchorNode.childNodes[sel.anchorOffset].nodeType == 1 ) + { + return sel.anchorNode.childNodes[sel.anchorOffset]; + } + else if ( sel.anchorNode.nodeType == 1 ) + { + return sel.anchorNode; + } + else + { + return null; // return sel.anchorNode.parentNode; + } + } + return null; +}; + +/** + * Determines if the given selection is empty (collapsed). + * @param selection Selection object as returned by getSelection + * @returns true|false + */ + +Xinha.prototype.selectionEmpty = function(sel) +{ + if ( !sel ) + { + return true; + } + + if ( typeof sel.isCollapsed != 'undefined' ) + { + return sel.isCollapsed; + } + + return true; +}; + +/** + * Returns a range object to be stored + * and later restored with Xinha.prototype.restoreSelection() + * + * @returns Range + */ +Xinha.prototype.saveSelection = function() +{ + return this.createRange(this.getSelection()).cloneRange(); +} +/** + * Restores a selection previously stored + * @param savedSelection Range object as returned by Xinha.prototype.restoreSelection() + */ +Xinha.prototype.restoreSelection = function(savedSelection) +{ + var sel = this.getSelection(); + sel.removeAllRanges(); + sel.addRange(savedSelection); +} +/** + * Selects the contents of the given node. If the node is a "control" type element, (image, form input, table) + * the node itself is selected for manipulation. + * + * @param node DomNode + * @param collapseToStart A boolean that, when supplied, says to collapse the selection. True collapses to the start, and false to the end. + */ + +Xinha.prototype.selectNodeContents = function(node, collapseToStart) +{ + this.focusEditor(); + this.forceRedraw(); + var range; + var collapsed = typeof collapseToStart == "undefined" ? true : false; + var sel = this.getSelection(); + range = this._doc.createRange(); + // Tables and Images get selected as "objects" rather than the text contents + if ( collapsed && node.tagName && node.tagName.toLowerCase().match(/table|img|input|textarea|select/) ) + { + range.selectNode(node); + } + else + { + range.selectNodeContents(node); + } + sel.removeAllRanges(); + sel.addRange(range); + if (typeof collapseToStart != "undefined") + { + if (collapseToStart) + { + sel.collapse(range.startContainer, range.startOffset); + } else + { + sel.collapse(range.endContainer, range.endOffset); + } + } +}; + +/** Insert HTML at the current position, deleting the selection if any. + * + * @param html string + */ + +Xinha.prototype.insertHTML = function(html) +{ + var sel = this.getSelection(); + var range = this.createRange(sel); + this.focusEditor(); + // construct a new document fragment with the given HTML + var fragment = this._doc.createDocumentFragment(); + var div = this._doc.createElement("div"); + div.innerHTML = html; + while ( div.firstChild ) + { + // the following call also removes the node from div + fragment.appendChild(div.firstChild); + } + // this also removes the selection + var node = this.insertNodeAtSelection(fragment); +}; + +/** Get the HTML of the current selection. HTML returned has not been passed through outwardHTML. + * + * @returns string + */ + +Xinha.prototype.getSelectedHTML = function() +{ + var sel = this.getSelection(); + if (sel.isCollapsed) return ''; + var range = this.createRange(sel); + return Xinha.getHTML(range.cloneContents(), false, this); +}; + + +/** Get a Selection object of the current selection. Note that selection objects are browser specific. + * + * @returns Selection + */ + +Xinha.prototype.getSelection = function() +{ + var sel = this._iframe.contentWindow.getSelection(); + if(sel && sel.focusNode && sel.focusNode.tagName && sel.focusNode.tagName == 'HTML') + { // Bad news batman, this selection is wonky + var bod = this._doc.getElementsByTagName('body')[0]; + var rng = this.createRange(); + rng.selectNodeContents(bod); + sel.removeAllRanges(); + sel.addRange(rng); + sel.collapseToEnd(); + } + return sel; +}; + +/** Create a Range object from the given selection. Note that range objects are browser specific. + * + * @param sel Selection object (see getSelection) + * @returns Range + */ + +Xinha.prototype.createRange = function(sel) +{ + this.activateEditor(); + if ( typeof sel != "undefined" ) + { + try + { + return sel.getRangeAt(0); + } + catch(ex) + { + return this._doc.createRange(); + } + } + else + { + return this._doc.createRange(); + } +}; + +/** Determine if the given event object is a keydown/press event. + * + * @param event Event + * @returns true|false + */ + +Xinha.prototype.isKeyEvent = function(event) +{ + return event.type == "keypress"; +} + +/** Return the character (as a string) of a keyEvent - ie, press the 'a' key and + * this method will return 'a', press SHIFT-a and it will return 'A'. + * + * @param keyEvent + * @returns string + */ + +Xinha.prototype.getKey = function(keyEvent) +{ + return String.fromCharCode(keyEvent.charCode); +} + +/** Return the HTML string of the given Element, including the Element. + * + * @param element HTML Element DomNode + * @returns string + */ + +Xinha.getOuterHTML = function(element) +{ + return (new XMLSerializer()).serializeToString(element); +}; + + +/* Caret position remembering when switch between text and html mode. + * Largely this is the same as for Gecko except: + * + * Opera does not have window.find() so we use instead an element with known + * id (MARK) so that we can + * do _doc.getElementById() on it. + * + * Opera for some reason will not set the insert point (flashing caret) + * anyway though, in theory, the iframe is focused (in findCC below) and then + * the selection (containing the span above) is collapsed, it should show + * caret. I don't know why not. Seems to require user to actually + * click to get the caret to show (or type anything without it acting wierd)? + * Very annoying. + * + */ +Xinha.cc = String.fromCharCode(8286); +Xinha.prototype.setCC = function ( target ) +{ + // Do a two step caret insertion, first a single char, then we'll replace that with the + // id'd span. + var cc = Xinha.cc; + + try + { + if ( target == "textarea" ) + { + var ta = this._textArea; + var index = ta.selectionStart; + var before = ta.value.substring( 0, index ) + var after = ta.value.substring( index, ta.value.length ); + + if ( after.match(/^[^<]*>/) ) // make sure cursor is in an editable area (outside tags, script blocks, entities, and inside the body) + { + var tagEnd = after.indexOf(">") + 1; + ta.value = before + after.substring( 0, tagEnd ) + cc + after.substring( tagEnd, after.length ); + } + else ta.value = before + cc + after; + ta.value = ta.value.replace(new RegExp ('(&[^'+cc+']*?)('+cc+')([^'+cc+']*?;)'), "$1$3$2"); + ta.value = ta.value.replace(new RegExp ('(]*>[^'+cc+']*?)('+cc+')([^'+cc+']*?<\/script>)'), "$1$3$2"); + ta.value = ta.value.replace(new RegExp ('^([^'+cc+']*)('+cc+')([^'+cc+']*]*>)(.*?)'), "$1$3$2$4"); + + ta.value = ta.value.replace(cc, 'MARK'); + } + else + { + var sel = this.getSelection(); + + var mark = this._doc.createElement('span'); + mark.id = 'XinhaOperaCaretMarker'; + sel.getRangeAt(0).insertNode( mark ); + + } + } catch (e) {} +}; + +Xinha.prototype.findCC = function ( target ) +{ + if ( target == 'textarea' ) + { + var ta = this._textArea; + var pos = ta.value.search( /(((\s|(MARK))*<\/span>)?)/ ); + if ( pos == -1 ) return; + var cc = RegExp.$1; + var end = pos + cc.length; + var before = ta.value.substring( 0, pos ); + var after = ta.value.substring( end, ta.value.length ); + ta.value = before ; + + ta.scrollTop = ta.scrollHeight; + var scrollPos = ta.scrollTop; + + ta.value += after; + ta.setSelectionRange(pos,pos); + + ta.focus(); + + ta.scrollTop = scrollPos; + + } + else + { + var marker = this._doc.getElementById('XinhaOperaCaretMarker'); + if(marker) + { + this.focusEditor();// this._iframe.contentWindow.focus(); + + var rng = this.createRange(); + rng.selectNode(marker); + + var sel = this.getSelection(); + sel.addRange(rng); + sel.collapseToStart(); + + this.scrollToElement(marker); + + marker.parentNode.removeChild(marker); + return; + } + } +}; + +/*--------------------------------------------------------------------------*/ +/*------------ EXTEND SOME STANDARD "Xinha.prototype" METHODS --------------*/ +/*--------------------------------------------------------------------------*/ + +/* +Xinha.prototype._standardToggleBorders = Xinha.prototype._toggleBorders; +Xinha.prototype._toggleBorders = function() +{ + var result = this._standardToggleBorders(); + + // flashing the display forces moz to listen (JB:18-04-2005) - #102 + var tables = this._doc.getElementsByTagName('TABLE'); + for(var i = 0; i < tables.length; i++) + { + tables[i].style.display="none"; + tables[i].style.display="table"; + } + + return result; +}; +*/ + +/** Return the doctype of a document, if set + * + * @param doc DOM element document + * @returns string the actual doctype + */ +Xinha.getDoctype = function (doc) +{ + var d = ''; + if (doc.doctype) + { + d += '"; + } + return d; +}; + +Xinha.prototype._standardInitIframe = Xinha.prototype.initIframe; +Xinha.prototype.initIframe = function() +{ + if(!this._iframeLoadDone) + { + if(this._iframe.contentWindow && this._iframe.contentWindow.xinhaReadyToRoll) + { + this._iframeLoadDone = true; + this._standardInitIframe(); + } + else + { + // Timeout a little (Opera is a bit dodgy about using an event) + var editor = this; + setTimeout( function() { editor.initIframe() }, 5); + } + } +} + +// For some reason, Opera doesn't listen to addEventListener for at least select elements with event = change +// so we will have to intercept those and use a Dom0 event, these are used eg for the fontname/size/format +// dropdowns in the toolbar +Xinha._addEventOperaOrig = Xinha._addEvent; +Xinha._addEvent = function(el, evname, func) +{ + if(el.tagName && el.tagName.toLowerCase() == 'select' && evname == 'change') + { + return Xinha.addDom0Event(el,evname,func); + } + + return Xinha._addEventOperaOrig(el,evname,func); +} Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/modules/WebKit/WebKit.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/modules/WebKit/WebKit.js,v diff -u -r1.3 -r1.4 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/modules/WebKit/WebKit.js 27 Mar 2009 08:20:43 -0000 1.3 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/modules/WebKit/WebKit.js 23 May 2010 11:58:33 -0000 1.4 @@ -1,3 +1,834 @@ -/* 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 */ -WebKit._pluginInfo={name:"WebKit",origin:"Xinha Core",version:"$LastChangedRevision: 1146 $".replace(/^[^:]*:\s*(.*)\s*\$$/,"$1"),developer:"The Xinha Core Developer Team",developer_url:"$HeadURL: http://svn.xinha.org/trunk/modules/WebKit/WebKit.js $".replace(/^[^:]*:\s*(.*)\s*\$$/,"$1"),sponsor:"",sponsor_url:"",license:"htmlArea"};function WebKit(a){this.editor=a;a.WebKit=this}WebKit.prototype.onKeyPress=function(u){var d=this.editor;var j=d.getSelection();if(d.isShortCut(u)){switch(d.getKey(u).toLowerCase()){case"z":if(d._unLink&&d._unlinkOnUndo){Xinha._stopEvent(u);d._unLink();d.updateToolbar();return true}break;case"a":break;case"v":if(!d.config.htmlareaPaste){return true}break}}switch(d.getKey(u)){case" ":var g=function(y,m){var x=y.nextSibling;if(typeof m=="string"){m=d._doc.createElement(m)}var s=y.parentNode.insertBefore(m,x);Xinha.removeFromParent(y);s.appendChild(y);x.data=" "+x.data;j.collapse(x,1);d._unLink=function(){var a=s.firstChild;s.removeChild(a);s.parentNode.insertBefore(a,s);Xinha.removeFromParent(s);d._unLink=null;d._unlinkOnUndo=false};d._unlinkOnUndo=true;return s};if(d.config.convertUrlsToLinks&&j&&j.isCollapsed&&j.anchorNode.nodeType==3&&j.anchorNode.data.length>3&&j.anchorNode.data.indexOf(".")>=0){var t=j.anchorNode.data.substring(0,j.anchorOffset).search(/\S{4,}$/);if(t==-1){break}if(d._getFirstAncestor(j,"a")){break}var h=j.anchorNode.data.substring(0,j.anchorOffset).replace(/^.*?(\S*)$/,"$1");var e=h.match(Xinha.RE_email);if(e){var v=j.anchorNode;var f=v.splitText(j.anchorOffset);var k=v.splitText(t);g(k,"a").href="mailto:"+e[0];break}RE_date=/([0-9]+\.)+/;RE_ip=/(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/;var p=h.match(Xinha.RE_url);if(p){if(RE_date.test(h)){break}var i=j.anchorNode;var b=i.splitText(j.anchorOffset);var q=i.splitText(t);g(q,"a").href=(p[1]?p[1]:"http://")+p[2];break}}break}switch(u.keyCode){case 13:if(u.shiftKey){}break;case 27:if(d._unLink){d._unLink();Xinha._stopEvent(u)}break;case 8:case 46:if(!u.shiftKey&&this.handleBackspace()){Xinha._stopEvent(u)}break;default:d._unlinkOnUndo=false;if(j.anchorNode&&j.anchorNode.nodeType==3){var w=d._getFirstAncestor(j,"a");if(!w){break}if(!w._updateAnchTimeout){if(j.anchorNode.data.match(Xinha.RE_email)&&w.href.match("mailto:"+j.anchorNode.data.trim())){var l=j.anchorNode;var c=function(){w.href="mailto:"+l.data.trim();w._updateAnchTimeout=setTimeout(c,250)};w._updateAnchTimeout=setTimeout(c,1000);break}var n=j.anchorNode.data.match(Xinha.RE_url);if(n&&w.href.match(new RegExp("http(s)?://"+Xinha.escapeStringForRegExp(j.anchorNode.data.trim())))){var o=j.anchorNode;var r=function(){n=o.data.match(Xinha.RE_url);if(n){w.href=(n[1]?n[1]:"http://")+n[2]}w._updateAnchTimeout=setTimeout(r,250)};w._updateAnchTimeout=setTimeout(r,1000)}}}break}return false};WebKit.prototype.handleBackspace=function(){var a=this.editor;setTimeout(function(){var e=a.getSelection();var g=a.createRange(e);var f=g.startContainer;var i=g.startOffset;var c=g.endContainer;var h=g.endOffset;var j=f.nextSibling;if(f.nodeType==3){f=f.parentNode}if(!(/\S/.test(f.tagName))){var d=document.createElement("p");while(f.firstChild){d.appendChild(f.firstChild)}f.parentNode.insertBefore(d,f);Xinha.removeFromParent(f);var b=g.cloneRange();b.setStartBefore(j);b.setEndAfter(j);b.extractContents();e.removeAllRanges();e.addRange(b)}},10)};WebKit.prototype.inwardHtml=function(a){return a};WebKit.prototype.outwardHtml=function(a){return a};WebKit.prototype.onExecCommand=function(h,g,f){this.editor._doc.execCommand("styleWithCSS",false,false);switch(h){case"paste":alert(Xinha._lc("The Paste button does not work in the Safari browser for security reasons. Press CTRL-V on your keyboard to paste directly."));return true;break;case"removeformat":var m=this.editor;var e=m.getSelection();var q=m.saveSelection(e);var l=m.createRange(e);var j=m._doc.getElementsByTagName("*");j=Xinha.collectionToArray(j);var a=(l.startContainer.nodeType==1)?l.startContainer:l.startContainer.parentNode;var k,d,p,n,c,b=m._doc.createRange();function o(r){if(r.nodeType!=1){return}r.removeAttribute("style");for(var i=0;ia.anchorOffset&&a.anchorNode.childNodes[a.anchorOffset].nodeType==1){return a.anchorNode.childNodes[a.anchorOffset]}else{if(a.anchorNode.nodeType==1){return a.anchorNode}else{return null}}}return null};Xinha.prototype.selectionEmpty=function(a){if(!a){return true}if(typeof a.isCollapsed!="undefined"){return a.isCollapsed}return true};Xinha.prototype.saveSelection=function(){return this.createRange(this.getSelection()).cloneRange()};Xinha.prototype.restoreSelection=function(a){var b=this.getSelection();b.removeAllRanges();b.addRange(a)};Xinha.prototype.selectNodeContents=function(b,d){this.focusEditor();this.forceRedraw();var a;var e=typeof d=="undefined"?true:false;var c=this.getSelection();a=this._doc.createRange();if(e&&b.tagName&&b.tagName.toLowerCase().match(/table|img|input|textarea|select/)){a.selectNode(b)}else{a.selectNodeContents(b)}c.removeAllRanges();c.addRange(a);if(typeof d!="undefined"){if(d){c.collapse(a.startContainer,a.startOffset)}else{c.collapse(a.endContainer,a.endOffset)}}};Xinha.prototype.insertHTML=function(c){var e=this.getSelection();var a=this.createRange(e);this.focusEditor();var b=this._doc.createDocumentFragment();var f=this._doc.createElement("div");f.innerHTML=c;while(f.firstChild){b.appendChild(f.firstChild)}var d=this.insertNodeAtSelection(b)};Xinha.prototype.getSelectedHTML=function(){var b=this.getSelection();if(b.isCollapsed){return""}var a=this.createRange(b);if(a){return Xinha.getHTML(a.cloneContents(),false,this)}else{return""}};Xinha.prototype.getSelection=function(){return this._iframe.contentWindow.getSelection()};Xinha.prototype.createRange=function(b){this.activateEditor();if(typeof b!="undefined"){try{return b.getRangeAt(0)}catch(a){return this._doc.createRange()}}else{return this._doc.createRange()}};Xinha.prototype.isKeyEvent=function(a){return a.type=="keydown"};Xinha.prototype.getKey=function(b){var a=String.fromCharCode(parseInt(b.keyIdentifier.replace(/^U\+/,""),16));if(b.shiftKey){return a}else{return a.toLowerCase()}};Xinha.getOuterHTML=function(a){return(new XMLSerializer()).serializeToString(a)};Xinha.cc=String.fromCharCode(8286);Xinha.prototype.setCC=function(h){var c=Xinha.cc;try{if(h=="textarea"){var d=this._textArea;var f=d.selectionStart;var j=d.value.substring(0,f);var a=d.value.substring(f,d.value.length);if(a.match(/^[^<]*>/)){var i=a.indexOf(">")+1;d.value=j+a.substring(0,i)+c+a.substring(i,a.length)}else{d.value=j+c+a}d.value=d.value.replace(new RegExp("(&[^"+c+";]*?)("+c+")([^"+c+"]*?;)"),"$1$3$2");d.value=d.value.replace(new RegExp("(]*>[^"+c+"]*?)("+c+")([^"+c+"]*?<\/script>)"),"$1$3$2");d.value=d.value.replace(new RegExp("^([^"+c+"]*)("+c+")([^"+c+"]*]*>)(.*?)"),"$1$3$2$4")}else{var b=this.getSelection();b.getRangeAt(0).insertNode(this._doc.createTextNode(c))}}catch(g){}};Xinha.prototype.findCC=function(i){var c=Xinha.cc;if(i=="textarea"){var f=this._textArea;var j=f.value.indexOf(c);if(j==-1){return}var d=j+c.length;var k=f.value.substring(0,j);var a=f.value.substring(d,f.value.length);f.value=k;f.scrollTop=f.scrollHeight;var b=f.scrollTop;f.value+=a;f.setSelectionRange(j,j);f.focus();f.scrollTop=b}else{var m=this;try{var l=this._doc;l.body.innerHTML=l.body.innerHTML.replace(new RegExp(c),'');var h=l.getElementById("XinhaEditingPostion");this.selectNodeContents(h);this.scrollToElement(h);h.parentNode.removeChild(h);this._iframe.contentWindow.focus()}catch(g){}}};Xinha.prototype._standardToggleBorders=Xinha.prototype._toggleBorders;Xinha.prototype._toggleBorders=function(){var a=this._standardToggleBorders();var c=this._doc.getElementsByTagName("TABLE");for(var b=0;b + -- and should be called that, but methods specific to Webkit should + -- be a part of the WebKit.prototype, we won't trample on namespace + -- that way. + -- + -- $HeadURL: http://svn.xinha.org/trunk/modules/WebKit/WebKit.js $ + -- $LastChangedDate: 2008-12-31 08:18:54 +1300 (Wed, 31 Dec 2008) $ + -- $LastChangedRevision: 1146 $ + -- $LastChangedBy: nicholasbs $ + --------------------------------------------------------------------------*/ + +WebKit._pluginInfo = { + name : "WebKit", + origin : "Xinha Core", + version : "$LastChangedRevision: 1146 $".replace(/^[^:]*:\s*(.*)\s*\$$/, '$1'), + developer : "The Xinha Core Developer Team", + developer_url : "$HeadURL: http://svn.xinha.org/trunk/modules/WebKit/WebKit.js $".replace(/^[^:]*:\s*(.*)\s*\$$/, '$1'), + sponsor : "", + sponsor_url : "", + license : "htmlArea" +}; + +function WebKit(editor) { + this.editor = editor; + editor.WebKit = this; +} + +/** Allow Webkit to handle some key events in a special way. + */ + +WebKit.prototype.onKeyPress = function(ev) +{ + var editor = this.editor; + var s = editor.getSelection(); + + // Handle shortcuts + if(editor.isShortCut(ev)) + { + switch(editor.getKey(ev).toLowerCase()) + { + case 'z': + if(editor._unLink && editor._unlinkOnUndo) + { + Xinha._stopEvent(ev); + editor._unLink(); + editor.updateToolbar(); + return true; + } + break; + + case 'a': + // ctrl-a selects all, but + break; + + case 'v': + // If we are not using htmlareaPaste, don't let Xinha try and be fancy but let the + // event be handled normally by the browser (don't stopEvent it) + if(!editor.config.htmlareaPaste) + { + return true; + } + break; + } + } + + // Handle normal characters + switch(editor.getKey(ev)) + { + // Space, see if the text just typed looks like a URL, or email address + // and link it appropriatly + case ' ': + var autoWrap = function (textNode, tag) + { + var rightText = textNode.nextSibling; + if ( typeof tag == 'string') + { + tag = editor._doc.createElement(tag); + } + var a = textNode.parentNode.insertBefore(tag, rightText); + Xinha.removeFromParent(textNode); + a.appendChild(textNode); + rightText.data = ' ' + rightText.data; + + s.collapse(rightText, 1); + + editor._unLink = function() + { + var t = a.firstChild; + a.removeChild(t); + a.parentNode.insertBefore(t, a); + Xinha.removeFromParent(a); + editor._unLink = null; + editor._unlinkOnUndo = false; + }; + editor._unlinkOnUndo = true; + + return a; + }; + + if ( editor.config.convertUrlsToLinks && s && s.isCollapsed && s.anchorNode.nodeType == 3 && s.anchorNode.data.length > 3 && s.anchorNode.data.indexOf('.') >= 0 ) + { + var midStart = s.anchorNode.data.substring(0,s.anchorOffset).search(/\S{4,}$/); + if ( midStart == -1 ) + { + break; + } + + if ( editor._getFirstAncestor(s, 'a') ) + { + break; // already in an anchor + } + + var matchData = s.anchorNode.data.substring(0,s.anchorOffset).replace(/^.*?(\S*)$/, '$1'); + + var mEmail = matchData.match(Xinha.RE_email); + if ( mEmail ) + { + var leftTextEmail = s.anchorNode; + var rightTextEmail = leftTextEmail.splitText(s.anchorOffset); + var midTextEmail = leftTextEmail.splitText(midStart); + + autoWrap(midTextEmail, 'a').href = 'mailto:' + mEmail[0]; + break; + } + + RE_date = /([0-9]+\.)+/; //could be date or ip or something else ... + RE_ip = /(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/; + var mUrl = matchData.match(Xinha.RE_url); + if ( mUrl ) + { + if (RE_date.test(matchData)) + { + break; //ray: disabling linking of IP numbers because of general bugginess (see Ticket #1085) + /*if (!RE_ip.test(matchData)) + { + break; + }*/ + } + var leftTextUrl = s.anchorNode; + var rightTextUrl = leftTextUrl.splitText(s.anchorOffset); + var midTextUrl = leftTextUrl.splitText(midStart); + autoWrap(midTextUrl, 'a').href = (mUrl[1] ? mUrl[1] : 'http://') + mUrl[2]; + break; + } + } + break; + } + + // Handle special keys + switch ( ev.keyCode ) + { + case 13: // ENTER + if( ev.shiftKey ) + { + //TODO: here we need to add insert new line + } + break; + + case 27: // ESCAPE + if ( editor._unLink ) + { + editor._unLink(); + Xinha._stopEvent(ev); + } + + break; + + case 8: // KEY backspace + case 46: // KEY delete + // We handle the mozilla backspace directly?? + if ( !ev.shiftKey && this.handleBackspace() ) + { + Xinha._stopEvent(ev); + } + break; + default: + editor._unlinkOnUndo = false; + + // Handle the "auto-linking", specifically this bit of code sets up a handler on + // an self-titled anchor (eg www.gogo.co.nz) + // when the text content is edited, such that it will update the href on the anchor + + if ( s.anchorNode && s.anchorNode.nodeType == 3 ) + { + // See if we might be changing a link + var a = editor._getFirstAncestor(s, 'a'); + // @todo: we probably need here to inform the setTimeout below that we not changing a link and not start another setTimeout + if ( !a ) + { + break; // not an anchor + } + + if ( !a._updateAnchTimeout ) + { + if ( s.anchorNode.data.match(Xinha.RE_email) && a.href.match('mailto:' + s.anchorNode.data.trim()) ) + { + var textNode = s.anchorNode; + var fnAnchor = function() + { + a.href = 'mailto:' + textNode.data.trim(); + // @fixme: why the hell do another timeout is started ? + // This lead to never ending timer if we dont remove this line + // But when removed, the email is not correctly updated + // + // - to fix this we should make fnAnchor check to see if textNode.data has + // stopped changing for say 5 seconds and if so we do not make this setTimeout + a._updateAnchTimeout = setTimeout(fnAnchor, 250); + }; + a._updateAnchTimeout = setTimeout(fnAnchor, 1000); + break; + } + + var m = s.anchorNode.data.match(Xinha.RE_url); + + if ( m && a.href.match(new RegExp( 'http(s)?://' + Xinha.escapeStringForRegExp( s.anchorNode.data.trim() ) ) ) ) + { + var txtNode = s.anchorNode; + var fnUrl = function() + { + // Sometimes m is undefined becase the url is not an url anymore (was www.url.com and become for example www.url) + // ray: shouldn't the link be un-linked then? + m = txtNode.data.match(Xinha.RE_url); + if(m) + { + a.href = (m[1] ? m[1] : 'http://') + m[2]; + } + + // @fixme: why the hell do another timeout is started ? + // This lead to never ending timer if we dont remove this line + // But when removed, the url is not correctly updated + // + // - to fix this we should make fnUrl check to see if textNode.data has + // stopped changing for say 5 seconds and if so we do not make this setTimeout + a._updateAnchTimeout = setTimeout(fnUrl, 250); + }; + a._updateAnchTimeout = setTimeout(fnUrl, 1000); + } + } + } + break; + } + + return false; // Let other plugins etc continue from here. +} + +/** When backspace is hit, the Gecko onKeyPress will execute this method. + * I don't remember what the exact purpose of this is though :-( + * + */ + +WebKit.prototype.handleBackspace = function() +{ + var editor = this.editor; + setTimeout( + function() + { + var sel = editor.getSelection(); + var range = editor.createRange(sel); + var SC = range.startContainer; + var SO = range.startOffset; + var EC = range.endContainer; + var EO = range.endOffset; + var newr = SC.nextSibling; + if ( SC.nodeType == 3 ) + { + SC = SC.parentNode; + } + if ( ! ( /\S/.test(SC.tagName) ) ) + { + var p = document.createElement("p"); + while ( SC.firstChild ) + { + p.appendChild(SC.firstChild); + } + SC.parentNode.insertBefore(p, SC); + Xinha.removeFromParent(SC); + var r = range.cloneRange(); + r.setStartBefore(newr); + r.setEndAfter(newr); + r.extractContents(); + sel.removeAllRanges(); + sel.addRange(r); + } + }, + 10); +}; + +WebKit.prototype.inwardHtml = function(html) +{ + return html; +} + +WebKit.prototype.outwardHtml = function(html) +{ + return html; +} + +WebKit.prototype.onExecCommand = function(cmdID, UI, param) +{ + this.editor._doc.execCommand('styleWithCSS', false, false); //switch styleWithCSS off; seems to make no difference though + + switch(cmdID) + { + case 'paste': + alert(Xinha._lc("The Paste button does not work in the Safari browser for security reasons. Press CTRL-V on your keyboard to paste directly.")); + return true; // Indicate paste is done, stop command being issued to browser by Xinha.prototype.execCommand + break; + case 'removeformat': + var editor = this.editor; + var sel = editor.getSelection(); + var selSave = editor.saveSelection(sel); + var range = editor.createRange(sel); + + var els = editor._doc.getElementsByTagName('*'); + els = Xinha.collectionToArray(els); + var start = ( range.startContainer.nodeType == 1 ) ? range.startContainer : range.startContainer.parentNode; + var i,el,newNode, fragment, child,r2 = editor._doc.createRange(); + + function clean (el) + { + if (el.nodeType != 1) return; + el.removeAttribute('style'); + for (var j=0; j sel.anchorOffset && sel.anchorNode.childNodes[sel.anchorOffset].nodeType == 1 ) + { + return sel.anchorNode.childNodes[sel.anchorOffset]; + } + else if ( sel.anchorNode.nodeType == 1 ) + { + return sel.anchorNode; + } + else + { + return null; // return sel.anchorNode.parentNode; + } + } + return null; +}; + +/** + * Determines if the given selection is empty (collapsed). + * @param selection Selection object as returned by getSelection + * @returns true|false + */ + +Xinha.prototype.selectionEmpty = function(sel) +{ + if ( !sel ) + { + return true; + } + + if ( typeof sel.isCollapsed != 'undefined' ) + { + return sel.isCollapsed; + } + + return true; +}; + +/** + * Returns a range object to be stored + * and later restored with Xinha.prototype.restoreSelection() + * + * @returns Range + */ +Xinha.prototype.saveSelection = function() +{ + return this.createRange(this.getSelection()).cloneRange(); +} +/** + * Restores a selection previously stored + * @param savedSelection Range object as returned by Xinha.prototype.restoreSelection() + */ +Xinha.prototype.restoreSelection = function(savedSelection) +{ + var sel = this.getSelection(); + sel.removeAllRanges(); + sel.addRange(savedSelection); +} +/** + * Selects the contents of the given node. If the node is a "control" type element, (image, form input, table) + * the node itself is selected for manipulation. + * + * @param node DomNode + * @param collapseToStart A boolean that, when supplied, says to collapse the selection. True collapses to the start, and false to the end. + */ + +Xinha.prototype.selectNodeContents = function(node, collapseToStart) +{ + this.focusEditor(); + this.forceRedraw(); + var range; + var collapsed = typeof collapseToStart == "undefined" ? true : false; + var sel = this.getSelection(); + range = this._doc.createRange(); + // Tables and Images get selected as "objects" rather than the text contents + if ( collapsed && node.tagName && node.tagName.toLowerCase().match(/table|img|input|textarea|select/) ) + { + range.selectNode(node); + } + else + { + range.selectNodeContents(node); + } + sel.removeAllRanges(); + sel.addRange(range); + if (typeof collapseToStart != "undefined") + { + if (collapseToStart) + { + sel.collapse(range.startContainer, range.startOffset); + } else + { + sel.collapse(range.endContainer, range.endOffset); + } + } +}; + +/** Insert HTML at the current position, deleting the selection if any. + * + * @param html string + */ + +Xinha.prototype.insertHTML = function(html) +{ + var sel = this.getSelection(); + var range = this.createRange(sel); + this.focusEditor(); + // construct a new document fragment with the given HTML + var fragment = this._doc.createDocumentFragment(); + var div = this._doc.createElement("div"); + div.innerHTML = html; + while ( div.firstChild ) + { + // the following call also removes the node from div + fragment.appendChild(div.firstChild); + } + // this also removes the selection + var node = this.insertNodeAtSelection(fragment); +}; + +/** Get the HTML of the current selection. HTML returned has not been passed through outwardHTML. + * + * @returns string + */ + +Xinha.prototype.getSelectedHTML = function() +{ + var sel = this.getSelection(); + if (sel.isCollapsed) return ''; + var range = this.createRange(sel); + + if ( range ) + { + return Xinha.getHTML(range.cloneContents(), false, this); + } + else return ''; +}; + + +/** Get a Selection object of the current selection. Note that selection objects are browser specific. + * + * @returns Selection + */ + +Xinha.prototype.getSelection = function() +{ + return this._iframe.contentWindow.getSelection(); +}; + +/** Create a Range object from the given selection. Note that range objects are browser specific. + * + * @param sel Selection object (see getSelection) + * @returns Range + */ + +Xinha.prototype.createRange = function(sel) +{ + this.activateEditor(); + if ( typeof sel != "undefined" ) + { + try + { + return sel.getRangeAt(0); + } + catch(ex) + { + return this._doc.createRange(); + } + } + else + { + return this._doc.createRange(); + } +}; + +/** Determine if the given event object is a keydown/press event. + * + * @param event Event + * @returns true|false + */ + +Xinha.prototype.isKeyEvent = function(event) +{ + return event.type == "keydown"; +} + +/** Return the character (as a string) of a keyEvent - ie, press the 'a' key and + * this method will return 'a', press SHIFT-a and it will return 'A'. + * + * @param keyEvent + * @returns string + */ + +Xinha.prototype.getKey = function(keyEvent) +{ + // with ctrl pressed Safari does not give the charCode, unfortunately this (shortcuts) is about the only thing this function is for + var key = String.fromCharCode(parseInt(keyEvent.keyIdentifier.replace(/^U\+/,''),16)); + if (keyEvent.shiftKey) return key; + else return key.toLowerCase(); +} + +/** Return the HTML string of the given Element, including the Element. + * + * @param element HTML Element DomNode + * @returns string + */ + +Xinha.getOuterHTML = function(element) +{ + return (new XMLSerializer()).serializeToString(element); +}; + +Xinha.cc = String.fromCharCode(8286); + +Xinha.prototype.setCC = function ( target ) +{ + var cc = Xinha.cc; + try + { + if ( target == "textarea" ) + { + var ta = this._textArea; + var index = ta.selectionStart; + var before = ta.value.substring( 0, index ) + var after = ta.value.substring( index, ta.value.length ); + + if ( after.match(/^[^<]*>/) ) // make sure cursor is in an editable area (outside tags, script blocks, enities and inside the body) + { + var tagEnd = after.indexOf(">") + 1; + ta.value = before + after.substring( 0, tagEnd ) + cc + after.substring( tagEnd, after.length ); + } + else ta.value = before + cc + after; + ta.value = ta.value.replace(new RegExp ('(&[^'+cc+';]*?)('+cc+')([^'+cc+']*?;)'), "$1$3$2"); + ta.value = ta.value.replace(new RegExp ('(]*>[^'+cc+']*?)('+cc+')([^'+cc+']*?<\/script>)'), "$1$3$2"); + ta.value = ta.value.replace(new RegExp ('^([^'+cc+']*)('+cc+')([^'+cc+']*]*>)(.*?)'), "$1$3$2$4"); + } + else + { + var sel = this.getSelection(); + sel.getRangeAt(0).insertNode( this._doc.createTextNode( cc ) ); + } + } catch (e) {} +}; + +Xinha.prototype.findCC = function ( target ) +{ + var cc = Xinha.cc; + + if ( target == 'textarea' ) + { + var ta = this._textArea; + var pos = ta.value.indexOf( cc ); + if ( pos == -1 ) return; + var end = pos + cc.length; + var before = ta.value.substring( 0, pos ); + var after = ta.value.substring( end, ta.value.length ); + ta.value = before ; + + ta.scrollTop = ta.scrollHeight; + var scrollPos = ta.scrollTop; + + ta.value += after; + ta.setSelectionRange(pos,pos); + + ta.focus(); + + ta.scrollTop = scrollPos; + + } + else + { + var self = this; + try + { + var doc = this._doc; + doc.body.innerHTML = doc.body.innerHTML.replace(new RegExp(cc),''); + var posEl = doc.getElementById('XinhaEditingPostion'); + this.selectNodeContents(posEl); + this.scrollToElement(posEl); + posEl.parentNode.removeChild(posEl); + + this._iframe.contentWindow.focus(); + } catch (e) {} + } +}; +/*--------------------------------------------------------------------------*/ +/*------------ EXTEND SOME STANDARD "Xinha.prototype" METHODS --------------*/ +/*--------------------------------------------------------------------------*/ + +Xinha.prototype._standardToggleBorders = Xinha.prototype._toggleBorders; +Xinha.prototype._toggleBorders = function() +{ + var result = this._standardToggleBorders(); + + // flashing the display forces moz to listen (JB:18-04-2005) - #102 + var tables = this._doc.getElementsByTagName('TABLE'); + for(var i = 0; i < tables.length; i++) + { + tables[i].style.display="none"; + tables[i].style.display="table"; + } + + return result; +}; + +/** Return the doctype of a document, if set + * + * @param doc DOM element document + * @returns string the actual doctype + */ +Xinha.getDoctype = function (doc) +{ + var d = ''; + if (doc.doctype) + { + d += '"; + } + return d; +}; Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Abbreviation/Abbreviation.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Abbreviation/Abbreviation.js,v diff -u -r1.2 -r1.3 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Abbreviation/Abbreviation.js 27 Mar 2009 08:20:43 -0000 1.2 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Abbreviation/Abbreviation.js 23 May 2010 11:58:33 -0000 1.3 @@ -1,3 +1,153 @@ -/* 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 */ -function Abbreviation(c){this.editor=c;var a=c.config;var b=this;a.registerButton({id:"abbreviation",tooltip:Xinha._lc("Abbreviation","Abbreviation"),image:c.imgURL("ed_abbreviation.gif","Abbreviation"),textMode:false,action:function(d){b.show()}});a.addToolbarElement("abbreviation","inserthorizontalrule",1)}Abbreviation._pluginInfo={name:"Abbreviation",version:"1.0",developer:"Udo Schmal",developer_url:"",sponsor:"L.N.Schaffrath NeueMedien",sponsor_url:"http://www.schaffrath-neuemedien.de/",c_owner:"Udo Schmal & Schaffrath-NeueMedien",license:"htmlArea"};Abbreviation.prototype.fillText=function(){var editor=this.editor;var text=this.html.toUpperCase();var abbr=Xinha.getPluginDir(this.constructor.name)+"/abbr/"+_editor_lang+".js";var abbrData=Xinha._geturlcontent(abbr);if(abbrData){eval("abbrObj = "+abbrData);if(abbrObj!=""){var dest=this.dialog.getElementById("title");dest.value=this.title||"";for(var i in abbrObj){same=(i.toUpperCase()==text);if(same){dest.value=abbrObj[i]}}}}};Abbreviation.prototype.onGenerateOnce=function(b){this.editor.addEditorStylesheet(Xinha.getPluginDir("Abbreviation")+"/abbreviation.css");this.methodsReady=true;var a=Abbreviation;Xinha._getback(Xinha.getPluginDir("Abbreviation")+"/dialog.html",function(c){a.html=c;a.dialogReady=true})};Abbreviation.prototype.OnUpdateToolbar=function(a){if(!(Abbreviation.dialogReady&&Abbreviation.methodsReady)){this.editor._toolbarObjects.Abbreviation.state("enabled",false)}else{this.onUpdateToolbar=null}};Abbreviation.prototype.prepareDialog=function(c){var a=this;var d=this.editor;var b=this.dialog=new Xinha.Dialog(d,Abbreviation.html,"Xinha",{width:260,height:140});b.getElementById("ok").onclick=function(){a.apply()};b.getElementById("delete").onclick=function(){a.ondelete()};b.getElementById("cancel").onclick=function(){a.dialog.hide()};this.dialogReady=true};Abbreviation.prototype.show=function(d){var d=this.editor;this.html=d.getSelectedHTML();if(!this.dialog){this.prepareDialog()}var b=this;var f=d._doc;var e=d._getSelection();var a=d._createRange(e);var c=d._activeElement(e);if(!(c!=null&&c.tagName.toLowerCase()=="abbr")){c=d._getFirstAncestor(e,"abbr")}this.abbr=c;if(c){this.title=c.title}this.fillText();this.dialog.getElementById("inputs").onsubmit=function(){b.apply();return false};this.dialog.show();this.dialog.getElementById("title").select()};Abbreviation.prototype.apply=function(){var c=this.editor;var f=c._doc;var a=this.abbr;var b=this.html;var h=this.dialog.hide();if(h){var g=h.title;if(g==""||g==null){if(a){var i=a.innerHTML;a.parentNode.removeChild(a);c.insertHTML(i)}return}try{if(!a){a=f.createElement("abbr");a.title=g;a.innerHTML=b;if(Xinha.is_ie){range.pasteHTML(a.outerHTML)}else{c.insertNodeAtSelection(a)}}else{a.title=g}}catch(d){}}};Abbreviation.prototype.ondelete=function(){this.dialog.getElementById("title").value="";this.apply()}; \ No newline at end of file +// Abbreviation plugin for Xinha +// Implementation by Udo Schmal & Schaffrath NeueMedien +// Original Author - Udo Schmal +// +// (c) Udo Schmal & Schaffrath NeueMedien 2004 +// Distributed under the same terms as HTMLArea itself. +// This notice MUST stay intact for use (see license.txt). + +function Abbreviation(editor) { + this.editor = editor; + var cfg = editor.config; + var self = this; + + // register the toolbar buttons provided by this plugin + cfg.registerButton({ + id : "abbreviation", + tooltip : Xinha._lc("Abbreviation", "Abbreviation"), + image : editor.imgURL("ed_abbreviation.gif", "Abbreviation"), + textMode : false, + action : function(editor) { + self.show(); + } + }); + cfg.addToolbarElement("abbreviation", "inserthorizontalrule", 1); +} + +Abbreviation._pluginInfo = { + name : "Abbreviation", + version : "1.0", + developer : "Udo Schmal", + developer_url : "", + sponsor : "L.N.Schaffrath NeueMedien", + sponsor_url : "http://www.schaffrath-neuemedien.de/", + c_owner : "Udo Schmal & Schaffrath-NeueMedien", + license : "htmlArea" +}; + +// Fills in the text field if the acronym is either known (i.e., in the [lang].js file) +// or if we're editing an existing abbreviation. +Abbreviation.prototype.fillText = function() { + var editor = this.editor; + var text = this.html.toUpperCase(); + var abbr = Xinha.getPluginDir(this.constructor.name) + "/abbr/" + _editor_lang + ".js"; + var abbrData = Xinha._geturlcontent(abbr); + + if (abbrData) { + eval('abbrObj = ' + abbrData); + if (abbrObj != "") { + var dest = this.dialog.getElementById("title"); + dest.value = this.title || ""; + for (var i in abbrObj) { + same = (i.toUpperCase()==text); + if (same) + dest.value = abbrObj[i]; + } + } + } +} + +Abbreviation.prototype.onGenerateOnce = function(editor) { + this.editor.addEditorStylesheet(Xinha.getPluginDir('Abbreviation') + '/abbreviation.css'); + this.methodsReady = true; //remove this? + var self = Abbreviation; + Xinha._getback(Xinha.getPluginDir('Abbreviation') + '/dialog.html', function(getback) { self.html = getback; self.dialogReady = true; }); +}; + +Abbreviation.prototype.OnUpdateToolbar = function(editor) { + if (!(Abbreviation.dialogReady && Abbreviation.methodsReady)) + { + this.editor._toolbarObjects.Abbreviation.state("enabled", false); + } + else this.onUpdateToolbar = null; +} + +Abbreviation.prototype.prepareDialog = function(html) { + var self = this; + var editor = this.editor; + var dialog = this.dialog = new Xinha.Dialog(editor, Abbreviation.html, 'Xinha', {width: 260, height:140}); + + dialog.getElementById('ok').onclick = function() { self.apply(); }; + dialog.getElementById('delete').onclick = function() { self.ondelete(); }; + dialog.getElementById('cancel').onclick = function() { self.dialog.hide(); }; + + this.dialogReady = true; +} + +Abbreviation.prototype.show = function(editor) { + var editor = this.editor; + this.html = editor.getSelectedHTML(); + if (!this.dialog) this.prepareDialog(); + var self = this; + var doc = editor._doc; + var sel = editor._getSelection(); + var range = editor._createRange(sel); + var abbr = editor._activeElement(sel); + + if(!(abbr != null && abbr.tagName.toLowerCase() == "abbr")) { + abbr = editor._getFirstAncestor(sel, 'abbr'); + } + this.abbr = abbr; + + if (abbr) this.title = abbr.title; + this.fillText(); + + this.dialog.getElementById("inputs").onsubmit = function() { + self.apply(); + return false; + } + + this.dialog.show(); + this.dialog.getElementById("title").select(); +} + +Abbreviation.prototype.apply = function() { + var editor = this.editor; + var doc = editor._doc; + var abbr = this.abbr; + var html = this.html; + var param = this.dialog.hide(); + + if ( param ) { + var title = param["title"]; + if (title == "" || title == null) { + if (abbr) { + var child = abbr.innerHTML; + abbr.parentNode.removeChild(abbr); + editor.insertHTML(child); // FIX: This doesn't work in Safari 3 + } + return; + } + try { + if (!abbr) { + abbr = doc.createElement("abbr"); + abbr.title = title; + abbr.innerHTML = html; + if (Xinha.is_ie) { + range.pasteHTML(abbr.outerHTML); + } else { + editor.insertNodeAtSelection(abbr); + } + } else { + abbr.title = title; + } + } + catch (e) { } + } +} + + +Abbreviation.prototype.ondelete = function() { + this.dialog.getElementById('title').value = ""; + this.apply(); +} \ No newline at end of file Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Abbreviation/abbreviation.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Abbreviation/Attic/abbreviation.js,v diff -u -r1.6 -r1.7 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Abbreviation/abbreviation.js 27 Mar 2009 08:20:43 -0000 1.6 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Abbreviation/abbreviation.js 23 May 2010 11:58:33 -0000 1.7 @@ -1,3 +1,153 @@ -/* 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 */ -function Abbreviation(c){this.editor=c;var a=c.config;var b=this;a.registerButton({id:"abbreviation",tooltip:Xinha._lc("Abbreviation","Abbreviation"),image:c.imgURL("ed_abbreviation.gif","Abbreviation"),textMode:false,action:function(d){b.show()}});a.addToolbarElement("abbreviation","inserthorizontalrule",1)}Abbreviation._pluginInfo={name:"Abbreviation",version:"1.0",developer:"Udo Schmal",developer_url:"",sponsor:"L.N.Schaffrath NeueMedien",sponsor_url:"http://www.schaffrath-neuemedien.de/",c_owner:"Udo Schmal & Schaffrath-NeueMedien",license:"htmlArea"};Abbreviation.prototype.fillText=function(){var editor=this.editor;var text=this.html.toUpperCase();var abbr=Xinha.getPluginDir(this.constructor.name)+"/abbr/"+_editor_lang+".js";var abbrData=Xinha._geturlcontent(abbr);if(abbrData){eval("abbrObj = "+abbrData);if(abbrObj!=""){var dest=this.dialog.getElementById("title");dest.value=this.title||"";for(var i in abbrObj){same=(i.toUpperCase()==text);if(same){dest.value=abbrObj[i]}}}}};Abbreviation.prototype.onGenerateOnce=function(b){this.editor.addEditorStylesheet(Xinha.getPluginDir("Abbreviation")+"/abbreviation.css");this.methodsReady=true;var a=Abbreviation;Xinha._getback(Xinha.getPluginDir("Abbreviation")+"/dialog.html",function(c){a.html=c;a.dialogReady=true})};Abbreviation.prototype.OnUpdateToolbar=function(a){if(!(Abbreviation.dialogReady&&Abbreviation.methodsReady)){this.editor._toolbarObjects.Abbreviation.state("enabled",false)}else{this.onUpdateToolbar=null}};Abbreviation.prototype.prepareDialog=function(c){var a=this;var d=this.editor;var b=this.dialog=new Xinha.Dialog(d,Abbreviation.html,"Xinha",{width:260,height:140});b.getElementById("ok").onclick=function(){a.apply()};b.getElementById("delete").onclick=function(){a.ondelete()};b.getElementById("cancel").onclick=function(){a.dialog.hide()};this.dialogReady=true};Abbreviation.prototype.show=function(d){var d=this.editor;this.html=d.getSelectedHTML();if(!this.dialog){this.prepareDialog()}var b=this;var f=d._doc;var e=d._getSelection();var a=d._createRange(e);var c=d._activeElement(e);if(!(c!=null&&c.tagName.toLowerCase()=="abbr")){c=d._getFirstAncestor(e,"abbr")}this.abbr=c;if(c){this.title=c.title}this.fillText();this.dialog.getElementById("inputs").onsubmit=function(){b.apply();return false};this.dialog.show();this.dialog.getElementById("title").select()};Abbreviation.prototype.apply=function(){var c=this.editor;var f=c._doc;var a=this.abbr;var b=this.html;var h=this.dialog.hide();if(h){var g=h.title;if(g==""||g==null){if(a){var i=a.innerHTML;a.parentNode.removeChild(a);c.insertHTML(i)}return}try{if(!a){a=f.createElement("abbr");a.title=g;a.innerHTML=b;if(Xinha.is_ie){range.pasteHTML(a.outerHTML)}else{c.insertNodeAtSelection(a)}}else{a.title=g}}catch(d){}}};Abbreviation.prototype.ondelete=function(){this.dialog.getElementById("title").value="";this.apply()}; \ No newline at end of file +// Abbreviation plugin for Xinha +// Implementation by Udo Schmal & Schaffrath NeueMedien +// Original Author - Udo Schmal +// +// (c) Udo Schmal & Schaffrath NeueMedien 2004 +// Distributed under the same terms as HTMLArea itself. +// This notice MUST stay intact for use (see license.txt). + +function Abbreviation(editor) { + this.editor = editor; + var cfg = editor.config; + var self = this; + + // register the toolbar buttons provided by this plugin + cfg.registerButton({ + id : "abbreviation", + tooltip : Xinha._lc("Abbreviation", "Abbreviation"), + image : editor.imgURL("ed_abbreviation.gif", "Abbreviation"), + textMode : false, + action : function(editor) { + self.show(); + } + }); + cfg.addToolbarElement("abbreviation", "inserthorizontalrule", 1); +} + +Abbreviation._pluginInfo = { + name : "Abbreviation", + version : "1.0", + developer : "Udo Schmal", + developer_url : "", + sponsor : "L.N.Schaffrath NeueMedien", + sponsor_url : "http://www.schaffrath-neuemedien.de/", + c_owner : "Udo Schmal & Schaffrath-NeueMedien", + license : "htmlArea" +}; + +// Fills in the text field if the acronym is either known (i.e., in the [lang].js file) +// or if we're editing an existing abbreviation. +Abbreviation.prototype.fillText = function() { + var editor = this.editor; + var text = this.html.toUpperCase(); + var abbr = Xinha.getPluginDir(this.constructor.name) + "/abbr/" + _editor_lang + ".js"; + var abbrData = Xinha._geturlcontent(abbr); + + if (abbrData) { + eval('abbrObj = ' + abbrData); + if (abbrObj != "") { + var dest = this.dialog.getElementById("title"); + dest.value = this.title || ""; + for (var i in abbrObj) { + same = (i.toUpperCase()==text); + if (same) + dest.value = abbrObj[i]; + } + } + } +} + +Abbreviation.prototype.onGenerateOnce = function(editor) { + this.editor.addEditorStylesheet(Xinha.getPluginDir('Abbreviation') + '/abbreviation.css'); + this.methodsReady = true; //remove this? + var self = Abbreviation; + Xinha._getback(Xinha.getPluginDir('Abbreviation') + '/dialog.html', function(getback) { self.html = getback; self.dialogReady = true; }); +}; + +Abbreviation.prototype.OnUpdateToolbar = function(editor) { + if (!(Abbreviation.dialogReady && Abbreviation.methodsReady)) + { + this.editor._toolbarObjects.Abbreviation.state("enabled", false); + } + else this.onUpdateToolbar = null; +} + +Abbreviation.prototype.prepareDialog = function(html) { + var self = this; + var editor = this.editor; + var dialog = this.dialog = new Xinha.Dialog(editor, Abbreviation.html, 'Xinha', {width: 260, height:140}); + + dialog.getElementById('ok').onclick = function() { self.apply(); }; + dialog.getElementById('delete').onclick = function() { self.ondelete(); }; + dialog.getElementById('cancel').onclick = function() { self.dialog.hide(); }; + + this.dialogReady = true; +} + +Abbreviation.prototype.show = function(editor) { + var editor = this.editor; + this.html = editor.getSelectedHTML(); + if (!this.dialog) this.prepareDialog(); + var self = this; + var doc = editor._doc; + var sel = editor._getSelection(); + var range = editor._createRange(sel); + var abbr = editor._activeElement(sel); + + if(!(abbr != null && abbr.tagName.toLowerCase() == "abbr")) { + abbr = editor._getFirstAncestor(sel, 'abbr'); + } + this.abbr = abbr; + + if (abbr) this.title = abbr.title; + this.fillText(); + + this.dialog.getElementById("inputs").onsubmit = function() { + self.apply(); + return false; + } + + this.dialog.show(); + this.dialog.getElementById("title").select(); +} + +Abbreviation.prototype.apply = function() { + var editor = this.editor; + var doc = editor._doc; + var abbr = this.abbr; + var html = this.html; + var param = this.dialog.hide(); + + if ( param ) { + var title = param["title"]; + if (title == "" || title == null) { + if (abbr) { + var child = abbr.innerHTML; + abbr.parentNode.removeChild(abbr); + editor.insertHTML(child); // FIX: This doesn't work in Safari 3 + } + return; + } + try { + if (!abbr) { + abbr = doc.createElement("abbr"); + abbr.title = title; + abbr.innerHTML = html; + if (Xinha.is_ie) { + range.pasteHTML(abbr.outerHTML); + } else { + editor.insertNodeAtSelection(abbr); + } + } else { + abbr.title = title; + } + } + catch (e) { } + } +} + + +Abbreviation.prototype.ondelete = function() { + this.dialog.getElementById('title').value = ""; + this.apply(); +} \ No newline at end of file Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Abbreviation/lang/es.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Abbreviation/lang/es.js,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Abbreviation/lang/es.js 23 May 2010 11:58:33 -0000 1.1 @@ -0,0 +1,8 @@ +// I18N constants +// LANG: "en", ENCODING: UTF-8 +// translated: Derick Leony +{ + "Abbreviation": "Abreviatura", + "Expansion:": "Explicación", + "Delete": "Suprimir" +}; Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/CSS/CSS.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/CSS/CSS.js,v diff -u -r1.2 -r1.3 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/CSS/CSS.js 27 Mar 2009 08:20:43 -0000 1.2 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/CSS/CSS.js 23 May 2010 11:58:33 -0000 1.3 @@ -1,3 +1,149 @@ -/* 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.cssPluginConfig={combos:[{label:"Syntax",options:{None:"",Code:"code",String:"string",Comment:"comment","Variable name":"variable-name",Type:"type",Reference:"reference",Preprocessor:"preprocessor",Keyword:"keyword","Function name":"function-name","Html tag":"html-tag","Html italic":"html-helper-italic",Warning:"warning","Html bold":"html-helper-bold"},context:"pre"},{label:"Info",options:{None:"",Quote:"quote",Highlight:"highlight",Deprecated:"deprecated"}}]};function CSS(f,d){this.editor=f;var g=f.config;var j=this;var c;if(d&&d.length){c=d[0]}else{c=f.config.cssPluginConfig}var k=c.combos;for(var e=0;e","")}};CSS.prototype.updateValue=function(f,h){var a=f._toolbarObjects[h.id].element;var e=f.getParentElement();if(typeof e.className!="undefined"&&/\S/.test(e.className)){var b=a.options;var g=e.className;for(var c=b.length;--c>=0;){var d=b[c];if(g==d.value){a.selectedIndex=c;return}}}a.selectedIndex=0}; \ No newline at end of file +// Simple CSS (className) plugin for the editor +// Sponsored by http://www.miro.com.au +// Implementation by Mihai Bazon, http://dynarch.com/mishoo. +// +// (c) dynarch.com 2003 +// Distributed under the same terms as HTMLArea itself. +// This notice MUST stay intact for use (see license.txt). +// +// $Id$ +// @TODO This is the default and won't be very useful to others. +// We should make this better. +Xinha.Config.prototype.cssPluginConfig = + { + combos : [ + { label: "Syntax", + // menu text // CSS class + options: { "None" : "", + "Code" : "code", + "String" : "string", + "Comment" : "comment", + "Variable name" : "variable-name", + "Type" : "type", + "Reference" : "reference", + "Preprocessor" : "preprocessor", + "Keyword" : "keyword", + "Function name" : "function-name", + "Html tag" : "html-tag", + "Html italic" : "html-helper-italic", + "Warning" : "warning", + "Html bold" : "html-helper-bold" + }, + context: "pre" + }, + { label: "Info", + options: { "None" : "", + "Quote" : "quote", + "Highlight" : "highlight", + "Deprecated" : "deprecated" + } + } + ] + }; + +function CSS(editor, params) { + this.editor = editor; + var cfg = editor.config; + var self = this; + var plugin_config; + if(params && params.length) + { + plugin_config = params[0]; + } + else + { + plugin_config = editor.config.cssPluginConfig; + } + + var combos = plugin_config.combos; + + for (var i = 0; i < combos.length; i++) { + var combo = combos[i]; + var id = "CSS-class" + i; + var css_class = { + id : id, + options : combo.options, + action : function(editor) { self.onSelect(editor, this, combo.context, combo.updatecontextclass); }, + refresh : function(editor) { self.updateValue(editor, this); }, + context : combo.context + }; + cfg.registerDropdown(css_class); + cfg.addToolbarElement(["T[" + combo.label + "]", id, "separator"] , "formatblock", -1); + } +} + +CSS._pluginInfo = { + name : "CSS", + version : "1.0", + developer : "Mihai Bazon", + developer_url : "http://dynarch.com/mishoo/", + c_owner : "Mihai Bazon", + sponsor : "Miro International", + sponsor_url : "http://www.miro.com.au", + license : "htmlArea" +}; + +CSS.prototype.onSelect = function(editor, obj, context, updatecontextclass) { + var tbobj = editor._toolbarObjects[obj.id]; + var index = tbobj.element.selectedIndex; + var className = tbobj.element.value; + + // retrieve parent element of the selection + var parent = editor.getParentElement(); + var surround = true; + + var is_span = (parent && parent.tagName.toLowerCase() == "span"); + var update_parent = (context && updatecontextclass && parent && parent.tagName.toLowerCase() == context); + + if (update_parent) { + parent.className = className; + editor.updateToolbar(); + return; + } + + if (is_span && index == 0 && !/\S/.test(parent.style.cssText)) { + while (parent.firstChild) { + parent.parentNode.insertBefore(parent.firstChild, parent); + } + parent.parentNode.removeChild(parent); + editor.updateToolbar(); + return; + } + + if (is_span) { + // maybe we could simply change the class of the parent node? + if (parent.childNodes.length == 1) { + parent.className = className; + surround = false; + // in this case we should handle the toolbar updation + // ourselves. + editor.updateToolbar(); + } + } + + // Other possibilities could be checked but require a lot of code. We + // can't afford to do that now. + if (surround) { + // shit happens ;-) most of the time. this method works, but + // it's dangerous when selection spans multiple block-level + // elements. + editor.surroundHTML("", ""); + } +}; + +CSS.prototype.updateValue = function(editor, obj) { + var select = editor._toolbarObjects[obj.id].element; + var parent = editor.getParentElement(); + if (typeof parent.className != "undefined" && /\S/.test(parent.className)) { + var options = select.options; + var value = parent.className; + for (var i = options.length; --i >= 0;) { + var option = options[i]; + if (value == option.value) { + select.selectedIndex = i; + return; + } + } + } + select.selectedIndex = 0; +}; \ No newline at end of file Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/CSS/css.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/CSS/Attic/css.js,v diff -u -r1.9 -r1.10 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/CSS/css.js 27 Mar 2009 08:20:43 -0000 1.9 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/CSS/css.js 23 May 2010 11:58:33 -0000 1.10 @@ -1,3 +1,149 @@ -/* 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.cssPluginConfig={combos:[{label:"Syntax",options:{None:"",Code:"code",String:"string",Comment:"comment","Variable name":"variable-name",Type:"type",Reference:"reference",Preprocessor:"preprocessor",Keyword:"keyword","Function name":"function-name","Html tag":"html-tag","Html italic":"html-helper-italic",Warning:"warning","Html bold":"html-helper-bold"},context:"pre"},{label:"Info",options:{None:"",Quote:"quote",Highlight:"highlight",Deprecated:"deprecated"}}]};function CSS(f,d){this.editor=f;var g=f.config;var j=this;var c;if(d&&d.length){c=d[0]}else{c=f.config.cssPluginConfig}var k=c.combos;for(var e=0;e","")}};CSS.prototype.updateValue=function(f,h){var a=f._toolbarObjects[h.id].element;var e=f.getParentElement();if(typeof e.className!="undefined"&&/\S/.test(e.className)){var b=a.options;var g=e.className;for(var c=b.length;--c>=0;){var d=b[c];if(g==d.value){a.selectedIndex=c;return}}}a.selectedIndex=0}; \ No newline at end of file +// Simple CSS (className) plugin for the editor +// Sponsored by http://www.miro.com.au +// Implementation by Mihai Bazon, http://dynarch.com/mishoo. +// +// (c) dynarch.com 2003 +// Distributed under the same terms as HTMLArea itself. +// This notice MUST stay intact for use (see license.txt). +// +// $Id$ +// @TODO This is the default and won't be very useful to others. +// We should make this better. +Xinha.Config.prototype.cssPluginConfig = + { + combos : [ + { label: "Syntax", + // menu text // CSS class + options: { "None" : "", + "Code" : "code", + "String" : "string", + "Comment" : "comment", + "Variable name" : "variable-name", + "Type" : "type", + "Reference" : "reference", + "Preprocessor" : "preprocessor", + "Keyword" : "keyword", + "Function name" : "function-name", + "Html tag" : "html-tag", + "Html italic" : "html-helper-italic", + "Warning" : "warning", + "Html bold" : "html-helper-bold" + }, + context: "pre" + }, + { label: "Info", + options: { "None" : "", + "Quote" : "quote", + "Highlight" : "highlight", + "Deprecated" : "deprecated" + } + } + ] + }; + +function CSS(editor, params) { + this.editor = editor; + var cfg = editor.config; + var self = this; + var plugin_config; + if(params && params.length) + { + plugin_config = params[0]; + } + else + { + plugin_config = editor.config.cssPluginConfig; + } + + var combos = plugin_config.combos; + + for (var i = 0; i < combos.length; i++) { + var combo = combos[i]; + var id = "CSS-class" + i; + var css_class = { + id : id, + options : combo.options, + action : function(editor) { self.onSelect(editor, this, combo.context, combo.updatecontextclass); }, + refresh : function(editor) { self.updateValue(editor, this); }, + context : combo.context + }; + cfg.registerDropdown(css_class); + cfg.addToolbarElement(["T[" + combo.label + "]", id, "separator"] , "formatblock", -1); + } +} + +CSS._pluginInfo = { + name : "CSS", + version : "1.0", + developer : "Mihai Bazon", + developer_url : "http://dynarch.com/mishoo/", + c_owner : "Mihai Bazon", + sponsor : "Miro International", + sponsor_url : "http://www.miro.com.au", + license : "htmlArea" +}; + +CSS.prototype.onSelect = function(editor, obj, context, updatecontextclass) { + var tbobj = editor._toolbarObjects[obj.id]; + var index = tbobj.element.selectedIndex; + var className = tbobj.element.value; + + // retrieve parent element of the selection + var parent = editor.getParentElement(); + var surround = true; + + var is_span = (parent && parent.tagName.toLowerCase() == "span"); + var update_parent = (context && updatecontextclass && parent && parent.tagName.toLowerCase() == context); + + if (update_parent) { + parent.className = className; + editor.updateToolbar(); + return; + } + + if (is_span && index == 0 && !/\S/.test(parent.style.cssText)) { + while (parent.firstChild) { + parent.parentNode.insertBefore(parent.firstChild, parent); + } + parent.parentNode.removeChild(parent); + editor.updateToolbar(); + return; + } + + if (is_span) { + // maybe we could simply change the class of the parent node? + if (parent.childNodes.length == 1) { + parent.className = className; + surround = false; + // in this case we should handle the toolbar updation + // ourselves. + editor.updateToolbar(); + } + } + + // Other possibilities could be checked but require a lot of code. We + // can't afford to do that now. + if (surround) { + // shit happens ;-) most of the time. this method works, but + // it's dangerous when selection spans multiple block-level + // elements. + editor.surroundHTML("", ""); + } +}; + +CSS.prototype.updateValue = function(editor, obj) { + var select = editor._toolbarObjects[obj.id].element; + var parent = editor.getParentElement(); + if (typeof parent.className != "undefined" && /\S/.test(parent.className)) { + var options = select.options; + var value = parent.className; + for (var i = options.length; --i >= 0;) { + var option = options[i]; + if (value == option.value) { + select.selectedIndex = i; + return; + } + } + } + select.selectedIndex = 0; +}; \ No newline at end of file Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/CSSPicker/CSSPicker.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/CSSPicker/CSSPicker.js,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/CSSPicker/CSSPicker.js 23 May 2010 11:58:33 -0000 1.1 @@ -0,0 +1,167 @@ +/** CSS Picker Plugin by Justin Rovang + * + * For full documentation, please see + * http://bitbucket.org/rovangju/xinha_csspicker/wiki/Home + * + * SAMPLE CONFIG: + * + * You can configure the appearance of the item/style list using the following CSS classes: + * .CSSPickerOption (Normal state) + * .CSSPickerOptionOver (Mouse-over state, typically border change) + * .CSSPickerOptionActive (Indicator for active classes under the selection/carat) + * + * Keys are CSS Class names + * wrapper: tag to wrap selected text with + * name: friendly name to display in panel with that class style applied to it. + * + * Sample config: + * CSSPicker.cssList = { + * 'xinhaDashedBox' : { 'wrapper':'div', 'name':'Breakout box' } + * 'xinhaMiniHeadline' : { 'wrapper':'div', 'name':'Sub-headline' } + * } + * + */ + +function CSSPicker(editor, args) { + this.editor = editor; + var CSSPicker = this; +} + +CSSPicker._pluginInfo = { + name : "CSSPicker", + version : "2008-12-01", + author : "Justin Rovang" +} + +CSSPicker.prototype.onGenerateOnce = function() { + var editor = this.editor; + var CSSPicker = this; + editor._cssPicker = editor.addPanel("right"); + + this.main = document.createElement("div"); + editor._cssPicker.style.backgroundColor='#dee5f8'; + editor._cssPicker.appendChild(this.main); + + Xinha.freeLater(this,"main"); + editor.showPanel(editor._cssPicker); +} + + +CSSPicker.prototype.onUpdateToolbar = function() { + if(this.editor._cssPicker) { + if(this._timeoutID) window.clearTimeout(this._timeoutID); + var e = this.editor; + this._timeoutID = window.setTimeout(function() { e._gen(); }, 250); //1000 = 1sec / 500=.5sec / 250=.25sec + } +} + +Xinha.prototype.listStyles = function(s) { + var editor = this; + var mySel = this.getSelection(); + var myRange; + if(Xinha.is_ie) { + myRange = this.saveSelection();//mySel; + mySel = this.createRange(mySel).text; + } + + var d = document.createElement("div"); + + d.className='CSSPickerOption'; + + /* If our carat is within an active class, highlight it */ + var toggleState = editor.getStyleInfo(s); + if(toggleState) Xinha._addClass(d, 'CSSPickerOptionActive'); + + d.align='center'; + d.innerHTML='
'+CSSPicker.cssList[s].name+'
'; + d.onclick = function() { + editor.wrapStyle(s, mySel, myRange, CSSPicker.cssList[s].wrapper); + return false; + }; + + Xinha._addEvent(d, 'mouseover', function(ev) { + Xinha._addClass(d, 'CSSPickerOptionOver'); + }); + + Xinha._addEvent(d, 'mouseout', function(ev) { + Xinha._removeClass(d, 'CSSPickerOptionOver'); + }); + + return d; +} + +Xinha.prototype._gen = function() { + this.plugins.CSSPicker.instance.main.innerHTML=''; + for(var s in CSSPicker.cssList) { + this.plugins.CSSPicker.instance.main.appendChild(this.listStyles(s)); + } + return true; +} + +/* + (string) s: style name + (string) sel: selection text + (object) myRange: selection object + (string) sWrapper: wrapper tag (e.g.: div, span) +*/ +Xinha.prototype.wrapStyle = function(s, sel, myRange, sWrapper) { + if(!sWrapper) sWrapper="div"; + sWrapper=sWrapper.toLowerCase(); + + /* The reason for these next lines is that we want the user to be able to place + * their cursor below the new div element. Otherwise they can't which makes + * placing anything after a div wrapper difficult/almost impossible. */ + var divBreak=''; + if(sWrapper=="div") divBreak='
'; + + var editor=this; + this.focusEditor(); + if(Xinha.is_ie) this.restoreSelection(myRange); + + /* + * First - Get parent elements and see if the style is already applied. + */ + var toggleState = editor.getStyleInfo(s); + if(!toggleState) { + /* Create a new wrapper when: + * 1. Selected text has no 'snug' wrapper around it already. + * 2. If it does have a 'snug' wrapper, only append to the className if it's of the same type (span or div) + */ + if(sel == '') sel = ' '; //We insert this if the selection is empty, making it easier for carat placement via click + + this.insertHTML("<"+sWrapper+" class='"+s+"'>"+sel+""+divBreak); + /* Modify the 'snug' wrapper if the above conditions are not met for a new element: */ + } + else { + /* 1. If the current ancestor has -just- this classname. It should be removed. + * 2. If it has more than one class, it should be removed from the list of the parents + */ + Xinha._removeClass(toggleState, s); + } + + return true; +} + +Xinha.prototype.getStyleInfo = function(sClassToProbe) { + var editor = this; + var aList = this.getAllAncestors(); + var a,s; + + if(aList) aList.pop(); //We don't want the body element to show up in this list. + if(aList.length > 0) { + for(var o in aList){ + a = aList[o]; + /* Instead of break down and rebuild the array for this search, we're going + * to do some string trickery... + * // NOTE: THIS MAY BE PRONE TO PARTIAL MATCHES. SOLUTION IS TO ADD A SPACE PREPEND + */ + if(a.className) { + s = a.className.trim()+' '; + if(s.toLowerCase().match(sClassToProbe.toLowerCase()+' ')) { + return a; + } + } + } + } + return false; +} Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/CharCounter/CharCounter.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/CharCounter/CharCounter.js,v diff -u -r1.2 -r1.3 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/CharCounter/CharCounter.js 27 Mar 2009 08:20:43 -0000 1.2 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/CharCounter/CharCounter.js 23 May 2010 11:58:33 -0000 1.3 @@ -1,3 +1,101 @@ -/* 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 */ -function CharCounter(a){this.editor=a;this._Chars=0;this._Words=0;this._HTML=0;this.onKeyPress=this.__onKeyPress}Xinha.Config.prototype.CharCounter={showChar:true,showWord:true,showHtml:true,separator:" | ",maxHTML:-1};CharCounter._pluginInfo={name:"CharCounter",version:"1.31",developer:"Udo Schmal",developer_url:"http://www.schaffrath-neuemedien.de",sponsor:"L.N.Schaffrath NeueMedien",sponsor_url:"http://www.schaffrath-neuemedien.de",c_owner:"Udo Schmal & L.N.Schaffrath NeueMedien",license:"htmlArea"};CharCounter.prototype._lc=function(a){return Xinha._lc(a,"CharCounter")};CharCounter.prototype.onGenerateOnce=function(){var b=this;if(this.charCount==null){var a=b.editor.registerStatusWidget("CharCounter",["wysiwyg"]);this.charCount=a}};CharCounter.prototype.__onKeyPress=function(b){if((b.keyCode!=8)&&(b.keyCode!=46)){if(this.editor.config.CharCounter.maxHTML!=-1){var a=this.editor.getHTML();if(a.length>=this.editor.config.CharCounter.maxHTML){Xinha._stopEvent(b);return true}}}};CharCounter.prototype._updateCharCount=function(){var d=this.editor;var b=d.config;var e=d.getHTML();var c=new Array();if(b.CharCounter.showHtml){c[c.length]=this._lc("HTML")+": "+e.length}this._HTML=e.length;if(b.CharCounter.showWord||b.CharCounter.showChar){e=e.replace(/<\/?\s*!--[^-->]*-->/gi,"");e=e.replace(/<(.+?)>/g,"");e=e.replace(/ /gi," ");e=e.replace(/([\n\r\t])/g," ");e=e.replace(/( +)/g," ");e=e.replace(/&(.*);/g," ");e=e.replace(/^\s*|\s*$/g,"")}if(b.CharCounter.showWord){this._Words=0;for(var a=0;a=1){this._Words++}c[c.length]=this._lc("Words")+": "+this._Words}if(b.CharCounter.showChar){c[c.length]=this._lc("Chars")+": "+e.length;this._Chars=e.length}this.charCount.innerHTML=c.join(b.CharCounter.separator)};CharCounter.prototype.onUpdateToolbar=function(){this.charCount.innerHTML=this._lc("... in progress");if(this._timeoutID){window.clearTimeout(this._timeoutID)}var a=this;this._timeoutID=window.setTimeout(function(){a._updateCharCount()},1000)}; \ No newline at end of file +// Charcounter for Xinha +// (c) Udo Schmal & L.N.Schaffrath NeueMedien +// Distributed under the same terms as HTMLArea itself. +// This notice MUST stay intact for use (see license.txt). + +function CharCounter(editor) { + this.editor = editor; + this._Chars = 0; + this._Words = 0; + this._HTML = 0; + this.onKeyPress = this.__onKeyPress; +} + +Xinha.Config.prototype.CharCounter = +{ + 'showChar': true, // show the characters count, + 'showWord': true, // show the words count, + 'showHtml': true, // show the exact html count + 'separator': ' | ', // separator used to join informations + 'maxHTML' : -1 // -1 for unlimited length, other number for limiting the length of the edited HTML +}; + +CharCounter._pluginInfo = { + name : "CharCounter", + version : "1.31", + developer : "Udo Schmal", + developer_url : "http://www.schaffrath-neuemedien.de", + sponsor : "L.N.Schaffrath NeueMedien", + sponsor_url : "http://www.schaffrath-neuemedien.de", + c_owner : "Udo Schmal & L.N.Schaffrath NeueMedien", + license : "htmlArea" +}; + +CharCounter.prototype._lc = function(string) { + return Xinha._lc(string, "CharCounter"); +}; + + +CharCounter.prototype.onGenerateOnce = function() { + var self = this; + if (this.charCount==null) { + var charCount = self.editor.registerStatusWidget('CharCounter', ['wysiwyg']); + this.charCount = charCount; + } +}; + +CharCounter.prototype.__onKeyPress= function(ev) { + if ((ev.keyCode != 8) && (ev.keyCode !=46)) { // not backspace & delete + if (this.editor.config.CharCounter.maxHTML!=-1) { + var contents = this.editor.getHTML(); + if (contents.length >= this.editor.config.CharCounter.maxHTML) { + Xinha._stopEvent(ev); + return true; + } + } + } +} + +CharCounter.prototype._updateCharCount= function() { + var editor = this.editor; + var cfg = editor.config; + var contents = editor.getHTML(); + var string = new Array(); + if (cfg.CharCounter.showHtml) { + string[string.length] = this._lc("HTML") + ": " + contents.length; + } + this._HTML = contents.length; + if (cfg.CharCounter.showWord || cfg.CharCounter.showChar) { + contents = contents.replace(/<\/?\s*!--[^-->]*-->/gi, "" ); + contents = contents.replace(/<(.+?)>/g, '');//Don't count HTML tags + contents = contents.replace(/ /gi, ' '); + contents = contents.replace(/([\n\r\t])/g, ' ');//convert newlines and tabs into space + contents = contents.replace(/( +)/g, ' ');//count spaces only once + contents = contents.replace(/&(.*);/g, ' ');//Count htmlentities as one keystroke + contents = contents.replace(/^\s*|\s*$/g, '');//trim + } + if (cfg.CharCounter.showWord) { + this._Words = 0; + for (var x=0;x=1) { this._Words++; } + string[string.length] = this._lc("Words") + ": " + this._Words ; + } + if (cfg.CharCounter.showChar) { + string[string.length] = this._lc("Chars") + ": " + contents.length; + this._Chars = contents.length; + } + this.charCount.innerHTML = string.join(cfg.CharCounter.separator); +}; + +CharCounter.prototype.onUpdateToolbar = function() { + this.charCount.innerHTML = this._lc("... in progress"); + if(this._timeoutID) { + window.clearTimeout(this._timeoutID); + } + var e = this; + this._timeoutID = window.setTimeout(function() {e._updateCharCount();}, 1000); +}; + Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/CharacterMap/CharacterMap.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/CharacterMap/CharacterMap.js,v diff -u -r1.2 -r1.3 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/CharacterMap/CharacterMap.js 27 Mar 2009 08:20:43 -0000 1.2 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/CharacterMap/CharacterMap.js 23 May 2010 11:58:33 -0000 1.3 @@ -1,3 +1,124 @@ -/* 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.loadStyle("CharacterMap.css","CharacterMap");function CharacterMap(c){this.editor=c;var a=c.config;var b=this;a.registerButton({id:"insertcharacter",tooltip:Xinha._lc("Insert special character","CharacterMap"),image:c.imgURL("images/tango/16x16/apps/accessories-character-map.png"),textMode:false,action:function(){b.show()}});a.addToolbarElement("insertcharacter","createlink",-1)}Xinha.Config.prototype.CharacterMap={mode:"popup"};CharacterMap._pluginInfo={name:"CharacterMap",version:"2.0",developer:"Laurent Vilday",developer_url:"http://www.mokhet.com/",c_owner:"Xinha community",sponsor:"",sponsor_url:"",license:"Creative Commons Attribution-ShareAlike License"};CharacterMap._isActive=false;CharacterMap.prototype.addEntity=function(b,f){var e=this.editor;var d=this;var c=document.createElement("a");Xinha._addClass(c,"entity");c.innerHTML=b;c.href="javascript:void(0)";Xinha._addClass(c,(f%2)?"light":"dark");c.onclick=function(){if(Xinha.is_ie){e.focusEditor()}e.insertHTML(b);return false};this.dialog.main.appendChild(c);c=null};CharacterMap.prototype.onGenerateOnce=function(){this._prepareDialog()};CharacterMap.prototype._prepareDialog=function(){var a=this;var e=this.editor;var c="

Insert special character

";this.dialog=new Xinha.Dialog(e,c,"CharacterMap",{width:300},{modal:false});Xinha._addClass(this.dialog.rootElem,"CharacterMap");if(e.config.CharacterMap&&e.config.CharacterMap.mode=="panel"){this.dialog.attachToPanel("right")}var d=["Ÿ","š","@",""","¡","¢","£","¤","¥","¦","§","¨","©","ª","«","¬","¯","°","±","²","³","´","µ","¶","·","¸","¹","º","»","¼","½","¾","¿","×","Ø","÷","ø","ƒ","ˆ","˜","–","—","‘","’","‚","“","”","„","†","‡","•","…","‰","‹","›","€","™","À","Á","Â","Ã","Ä","Å","Æ","Ç","È","É","Ê","Ë","Ì","Í","Î","Ï","Ð","Ñ","Ò","Ó","Ô","Õ","Ö","®","×","Ù","Ú","Û","Ü","Ý","Þ","ß","à","á","â","ã","ä","å","æ","ç","è","é","ê","ë","ì","í","î","ï","ð","ñ","ò","ó","ô","õ","ö","÷","ø","ù","ú","û","ü","ý","þ","ÿ","Œ","œ","Š"];for(var b=0;b +{ + "Edit HTML for selected text": "Editar código HTML del texto seleccionado", + "Tag Editor": "Editor de Etiquetas" +}; Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/ContextMenu/ContextMenu.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/ContextMenu/ContextMenu.js,v diff -u -r1.2 -r1.3 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/ContextMenu/ContextMenu.js 27 Mar 2009 08:20:43 -0000 1.2 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/ContextMenu/ContextMenu.js 23 May 2010 11:58:33 -0000 1.3 @@ -1,3 +1,501 @@ -/* 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.loadStyle("menu.css","ContextMenu");function ContextMenu(a){this.editor=a}ContextMenu._pluginInfo={name:"ContextMenu",version:"1.0",developer:"Mihai Bazon",developer_url:"http://dynarch.com/mishoo/",c_owner:"dynarch.com",sponsor:"American Bible Society",sponsor_url:"http://www.americanbible.org",license:"htmlArea"};Xinha.Config.prototype.ContextMenu={disableMozillaSpellCheck:false};ContextMenu.prototype.onGenerate=function(){var a=this;var b=this.editordoc=this.editor._iframe.contentWindow.document;Xinha._addEvents(b,["contextmenu"],function(c){return a.popupMenu(Xinha.is_ie?a.editor._iframe.contentWindow.event:c)});this.currentMenu=null;if(this.editor.config.ContextMenu.disableMozillaSpellCheck){this.editordoc.body.spellcheck=false}};ContextMenu.prototype.getContextMenu=function(o){var j=this;var d=this.editor;var n=d.config;var b=[];var c=this.editor.plugins.TableOperations;if(c){c=c.instance}var q=d.hasSelectedText();if(!Xinha.is_gecko){if(q){b.push([Xinha._lc("Cut","ContextMenu"),function(){d.execCommand("cut")},null,n.btnList.cut[1]],[Xinha._lc("Copy","ContextMenu"),function(){d.execCommand("copy")},null,n.btnList.copy[1]]);b.push([Xinha._lc("Paste","ContextMenu"),function(){d.execCommand("paste")},null,n.btnList.paste[1]])}}var s=o;var e=[];var f=null;var m=null;var a=null;var g=null;var r=null;function h(i){c.buttonPress(d,i)}function k(x){var u=s;var t=u.parentNode;var w=d._doc.createElement("p");w.appendChild(d._doc.createElement("br"));t.insertBefore(w,x?u.nextSibling:u);var v=d._getSelection();var i=d._createRange(v);if(!Xinha.is_ie){v.removeAllRanges();i.selectNodeContents(w);i.collapse(true);v.addRange(i)}else{i.moveToElementText(w);i.collapse(true);i.select()}}for(;o;o=o.parentNode){var p=o.tagName;if(!p){continue}p=p.toLowerCase();switch(p){case"img":r=o;e.push(null,[Xinha._lc("_Image Properties...","ContextMenu"),function(){d._insertImage(r)},Xinha._lc("Show the image properties dialog","ContextMenu"),n.btnList.insertimage[1]]);break;case"a":f=o;e.push(null,[Xinha._lc("_Modify Link...","ContextMenu"),function(){d.config.btnList.createlink[3](d)},Xinha._lc("Current URL is","ContextMenu")+": "+f.href,n.btnList.createlink[1]],[Xinha._lc("Chec_k Link...","ContextMenu"),function(){window.open(f.href)},Xinha._lc("Opens this link in a new window","ContextMenu")],[Xinha._lc("_Remove Link...","ContextMenu"),function(){if(confirm(Xinha._lc("Please confirm that you want to unlink this element.","ContextMenu")+"\n"+Xinha._lc("Link points to:","ContextMenu")+" "+f.href)){while(f.firstChild){f.parentNode.insertBefore(f.firstChild,f)}f.parentNode.removeChild(f)}},Xinha._lc("Unlink the current element","ContextMenu")]);break;case"td":g=o;if(!c){break}e.push(null,[Xinha._lc("C_ell Properties...","ContextMenu"),function(){h("TO-cell-prop")},Xinha._lc("Show the Table Cell Properties dialog","ContextMenu"),n.btnList["TO-cell-prop"][1]],[Xinha._lc("Insert Cell After","ContextMenu"),function(){h("TO-cell-insert-after")},Xinha._lc("Insert Cell After","ContextMenu"),n.btnList["TO-cell-insert-after"][1]],[Xinha._lc("Insert Cell Before","ContextMenu"),function(){h("TO-cell-insert-before")},Xinha._lc("Insert Cell After","ContextMenu"),n.btnList["TO-cell-insert-before"][1]],[Xinha._lc("Delete Cell","ContextMenu"),function(){h("TO-cell-delete")},Xinha._lc("Delete Cell","ContextMenu"),n.btnList["TO-cell-delete"][1]],[Xinha._lc("Merge Cells","ContextMenu"),function(){h("TO-cell-merge")},Xinha._lc("Merge Cells","ContextMenu"),n.btnList["TO-cell-merge"][1]]);break;case"tr":a=o;if(!c){break}e.push(null,[Xinha._lc("Ro_w Properties...","ContextMenu"),function(){h("TO-row-prop")},Xinha._lc("Show the Table Row Properties dialog","ContextMenu"),n.btnList["TO-row-prop"][1]],[Xinha._lc("I_nsert Row Before","ContextMenu"),function(){h("TO-row-insert-above")},Xinha._lc("Insert a new row before the current one","ContextMenu"),n.btnList["TO-row-insert-above"][1]],[Xinha._lc("In_sert Row After","ContextMenu"),function(){h("TO-row-insert-under")},Xinha._lc("Insert a new row after the current one","ContextMenu"),n.btnList["TO-row-insert-under"][1]],[Xinha._lc("_Delete Row","ContextMenu"),function(){h("TO-row-delete")},Xinha._lc("Delete the current row","ContextMenu"),n.btnList["TO-row-delete"][1]]);break;case"table":m=o;if(!c){break}e.push(null,[Xinha._lc("_Table Properties...","ContextMenu"),function(){h("TO-table-prop")},Xinha._lc("Show the Table Properties dialog","ContextMenu"),n.btnList["TO-table-prop"][1]],[Xinha._lc("Insert _Column Before","ContextMenu"),function(){h("TO-col-insert-before")},Xinha._lc("Insert a new column before the current one","ContextMenu"),n.btnList["TO-col-insert-before"][1]],[Xinha._lc("Insert C_olumn After","ContextMenu"),function(){h("TO-col-insert-after")},Xinha._lc("Insert a new column after the current one","ContextMenu"),n.btnList["TO-col-insert-after"][1]],[Xinha._lc("De_lete Column","ContextMenu"),function(){h("TO-col-delete")},Xinha._lc("Delete the current column","ContextMenu"),n.btnList["TO-col-delete"][1]]);break;case"body":e.push(null,[Xinha._lc("Justify Left","ContextMenu"),function(){d.execCommand("justifyleft")},null,n.btnList.justifyleft[1]],[Xinha._lc("Justify Center","ContextMenu"),function(){d.execCommand("justifycenter")},null,n.btnList.justifycenter[1]],[Xinha._lc("Justify Right","ContextMenu"),function(){d.execCommand("justifyright")},null,n.btnList.justifyright[1]],[Xinha._lc("Justify Full","ContextMenu"),function(){d.execCommand("justifyfull")},null,n.btnList.justifyfull[1]]);break}}if(q&&!f){b.push(null,[Xinha._lc("Make lin_k...","ContextMenu"),function(){d.config.btnList.createlink[3](d)},Xinha._lc("Create a link","ContextMenu"),n.btnList.createlink[1]])}for(var l=0;l=0;){var h=r[t];if(h[0].toLowerCase()==w){h[1].__msh.activate()}}}v.closeMenu=function(){v.currentMenu.parentNode.removeChild(v.currentMenu);v.currentMenu=null;Xinha._removeEvent(document,"mousedown",e);Xinha._removeEvent(v.editordoc,"mousedown",e);if(r.length>0){Xinha._removeEvent(v.editordoc,"keypress",z)}if(Xinha.is_ie){v.iePopup.hide()}};var G=Xinha.is_ie?F.srcElement:F.target;var f=C(v.editor._htmlArea);var m=F.clientX+f.x;var l=F.clientY+f.y;var s;var H;if(!Xinha.is_ie){H=document}else{var a=this.iePopup=window.createPopup();H=a.document;H.open();H.write("");H.close()}s=H.createElement("div");if(Xinha.is_ie){s.unselectable="on"}s.oncontextmenu=function(){return false};s.className="htmlarea-context-menu";if(!Xinha.is_ie){s.style.visibility="hidden";s.style.left=s.style.top="-200px"}H.body.appendChild(s);var D=H.createElement("table");s.appendChild(D);D.cellSpacing=0;D.cellPadding=0;var k=H.createElement("tbody");D.appendChild(k);var d=this.getContextMenu(G);for(var A=0;A";var c=g.cloneNode(true);c.className="label";E.appendChild(g);E.appendChild(c)}else{var j=q[0];E.className="item";E.__msh={item:E,label:j,action:q[1],tooltip:q[2]||null,icon:q[3]||null,activate:function(){v.closeMenu();v.editor.focusEditor();this.action()}};j=j.replace(/_([a-zA-Z0-9])/,"$1");if(j!=q[0]){r.push([RegExp.$1,E])}j=j.replace(/__/,"_");var c=H.createElement("td");if(Xinha.is_ie){c.unselectable="on"}E.appendChild(c);c.className="icon";if(E.__msh.icon){var p=Xinha.makeBtnImg(E.__msh.icon,H);c.appendChild(p)}var b=H.createElement("td");if(Xinha.is_ie){b.unselectable="on"}E.appendChild(b);b.className="label";b.innerHTML=j;E.onmouseover=function(){this.className+=" hover";v.editor._statusBarTree.innerHTML=this.__msh.tooltip||" "};E.onmouseout=function(){this.className="item"};E.oncontextmenu=function(h){this.__msh.activate();if(!Xinha.is_ie){Xinha._stopEvent(h)}return false};E.onmouseup=function(i){var h=(new Date()).getTime();if(h-v.timeStamp>500){this.__msh.activate()}if(!Xinha.is_ie){Xinha._stopEvent(i)}return false}}}if(!Xinha.is_ie){var u=10;if(l+s.offsetHeight+u>window.innerHeight){l=window.innerHeight-s.offsetHeight-u}if(m+s.offsetWidth+u>window.innerWidth){m=window.innerWidth-s.offsetWidth-u}s.style.left=m+"px";s.style.top=l+"px";s.style.visibility="visible"}else{this.iePopup.show(F.screenX,F.screenY,300,50);var n=s.offsetWidth;var B=s.offsetHeight;this.iePopup.show(F.screenX,F.screenY,n,B)}this.currentMenu=s;this.timeStamp=(new Date()).getTime();Xinha._addEvent(document,"mousedown",e);Xinha._addEvent(this.editordoc,"mousedown",e);if(r.length>0){Xinha._addEvent(this.editordoc,"keypress",z)}Xinha._stopEvent(F);return false}; \ No newline at end of file +// Context Menu Plugin for HTMLArea-3.0 +// Sponsored by www.americanbible.org +// Implementation by Mihai Bazon, http://dynarch.com/mishoo/ +// +// (c) dynarch.com 2003. +// Distributed under the same terms as HTMLArea itself. +// This notice MUST stay intact for use (see license.txt). +// +// $Id$ + +Xinha.loadStyle("menu.css", "ContextMenu"); + +function ContextMenu(editor) { + this.editor = editor; +} + +ContextMenu._pluginInfo = { + name : "ContextMenu", + version : "1.0", + developer : "Mihai Bazon", + developer_url : "http://dynarch.com/mishoo/", + c_owner : "dynarch.com", + sponsor : "American Bible Society", + sponsor_url : "http://www.americanbible.org", + license : "htmlArea" +}; + +Xinha.Config.prototype.ContextMenu = { + disableMozillaSpellCheck : false, + customHooks : { } // 'a': [ ['Label', function() { alert('Action'); }, 'Tooltip', '/icon.jpg' ] ] +} + +ContextMenu.prototype.onGenerate = function() { + var self = this; + var doc = this.editordoc = this.editor._iframe.contentWindow.document; + Xinha._addEvents(doc, ["contextmenu"], + function (event) { + return self.popupMenu(Xinha.is_ie ? self.editor._iframe.contentWindow.event : event); + }); + this.currentMenu = null; + + if (this.editor.config.ContextMenu.disableMozillaSpellCheck) { + this.editordoc.body.spellcheck = false; // Firefox spellchecking is quite confusing for the user when they don't get the browser context menu + } +}; + +ContextMenu.prototype.getContextMenu = function(target) { + var self = this; + var editor = this.editor; + var config = editor.config; + var menu = []; + var tbo = this.editor.plugins.TableOperations; + if (tbo) tbo = tbo.instance; + + var selection = editor.hasSelectedText(); + if (!Xinha.is_gecko) { + if (selection) { + menu.push([ Xinha._lc("Cut", "ContextMenu"), function() { editor.execCommand("cut"); }, null, config.btnList["cut"][1] ], + [ Xinha._lc("Copy", "ContextMenu"), function() { editor.execCommand("copy"); }, null, config.btnList["copy"][1] ]); + menu.push([ Xinha._lc("Paste", "ContextMenu"), function() { editor.execCommand("paste"); }, null, config.btnList["paste"][1] ]); + } + } + var currentTarget = target; + var elmenus = []; + + var link = null; + var table = null; + var tr = null; + var td = null; + var img = null; + + function tableOperation(opcode) { + tbo.buttonPress(editor, opcode); + } + + function insertPara(after) { + var el = currentTarget; + var par = el.parentNode; + var p = editor._doc.createElement("p"); + p.appendChild(editor._doc.createElement("br")); + par.insertBefore(p, after ? el.nextSibling : el); + var sel = editor._getSelection(); + var range = editor._createRange(sel); + if (!Xinha.is_ie) { + sel.removeAllRanges(); + range.selectNodeContents(p); + range.collapse(true); + sel.addRange(range); + } else { + range.moveToElementText(p); + range.collapse(true); + range.select(); + } + } + + for (; target; target = target.parentNode) { + var tag = target.tagName; + if (!tag) + continue; + tag = tag.toLowerCase(); + switch (tag) { + case "img": + img = target; + elmenus.push(null, + [ Xinha._lc("_Image Properties...", "ContextMenu"), + function() { + editor._insertImage(img); + }, + Xinha._lc("Show the image properties dialog", "ContextMenu"), + config.btnList["insertimage"][1] ] + ); + break; + case "a": + link = target; + elmenus.push(null, + [ Xinha._lc("_Modify Link...", "ContextMenu"), + function() { editor.config.btnList['createlink'][3](editor); }, + Xinha._lc("Current URL is", "ContextMenu") + ': ' + link.href, + config.btnList["createlink"][1] ], + + [ Xinha._lc("Chec_k Link...", "ContextMenu"), + function() { window.open(link.href); }, + Xinha._lc("Opens this link in a new window", "ContextMenu") ], + + [ Xinha._lc("_Remove Link...", "ContextMenu"), + function() { + if (confirm(Xinha._lc("Please confirm that you want to unlink this element.", "ContextMenu") + "\n" + + Xinha._lc("Link points to:", "ContextMenu") + " " + link.href)) { + while (link.firstChild) + link.parentNode.insertBefore(link.firstChild, link); + link.parentNode.removeChild(link); + } + }, + Xinha._lc("Unlink the current element", "ContextMenu") ] + ); + break; + case "td": + td = target; + if (!tbo) break; + elmenus.push(null, + [ Xinha._lc("C_ell Properties...", "ContextMenu"), + function() { tableOperation("TO-cell-prop"); }, + Xinha._lc("Show the Table Cell Properties dialog", "ContextMenu"), + config.btnList["TO-cell-prop"][1] ], + + [ Xinha._lc("Insert Cell After", "ContextMenu"), + function() { tableOperation("TO-cell-insert-after"); }, + Xinha._lc("Insert Cell After", "ContextMenu"), + config.btnList["TO-cell-insert-after"][1] ], + + [ Xinha._lc("Insert Cell Before", "ContextMenu"), + function() { tableOperation("TO-cell-insert-before"); }, + Xinha._lc("Insert Cell After", "ContextMenu"), + config.btnList["TO-cell-insert-before"][1] ], + + [ Xinha._lc("Delete Cell", "ContextMenu"), + function() { tableOperation("TO-cell-delete"); }, + Xinha._lc("Delete Cell", "ContextMenu"), + config.btnList["TO-cell-delete"][1] ], + + [ Xinha._lc("Merge Cells", "ContextMenu"), + function() { tableOperation("TO-cell-merge"); }, + Xinha._lc("Merge Cells", "ContextMenu"), + config.btnList["TO-cell-merge"][1] ] + ); + break; + case "tr": + tr = target; + if (!tbo) break; + elmenus.push(null, + [ Xinha._lc("Ro_w Properties...", "ContextMenu"), + function() { tableOperation("TO-row-prop"); }, + Xinha._lc("Show the Table Row Properties dialog", "ContextMenu"), + config.btnList["TO-row-prop"][1] ], + + [ Xinha._lc("I_nsert Row Before", "ContextMenu"), + function() { tableOperation("TO-row-insert-above"); }, + Xinha._lc("Insert a new row before the current one", "ContextMenu"), + config.btnList["TO-row-insert-above"][1] ], + + [ Xinha._lc("In_sert Row After", "ContextMenu"), + function() { tableOperation("TO-row-insert-under"); }, + Xinha._lc("Insert a new row after the current one", "ContextMenu"), + config.btnList["TO-row-insert-under"][1] ], + + [ Xinha._lc("_Delete Row", "ContextMenu"), + function() { tableOperation("TO-row-delete"); }, + Xinha._lc("Delete the current row", "ContextMenu"), + config.btnList["TO-row-delete"][1] ] + ); + break; + case "table": + table = target; + if (!tbo) break; + elmenus.push(null, + [ Xinha._lc("_Table Properties...", "ContextMenu"), + function() { tableOperation("TO-table-prop"); }, + Xinha._lc("Show the Table Properties dialog", "ContextMenu"), + config.btnList["TO-table-prop"][1] ], + + [ Xinha._lc("Insert _Column Before", "ContextMenu"), + function() { tableOperation("TO-col-insert-before"); }, + Xinha._lc("Insert a new column before the current one", "ContextMenu"), + config.btnList["TO-col-insert-before"][1] ], + + [ Xinha._lc("Insert C_olumn After", "ContextMenu"), + function() { tableOperation("TO-col-insert-after"); }, + Xinha._lc("Insert a new column after the current one", "ContextMenu"), + config.btnList["TO-col-insert-after"][1] ], + + [ Xinha._lc("De_lete Column", "ContextMenu"), + function() { tableOperation("TO-col-delete"); }, + Xinha._lc("Delete the current column", "ContextMenu"), + config.btnList["TO-col-delete"][1] ] + ); + break; + case "body": + elmenus.push(null, + [ Xinha._lc("Justify Left", "ContextMenu"), + function() { editor.execCommand("justifyleft"); }, null, + config.btnList["justifyleft"][1] ], + [ Xinha._lc("Justify Center", "ContextMenu"), + function() { editor.execCommand("justifycenter"); }, null, + config.btnList["justifycenter"][1] ], + [ Xinha._lc("Justify Right", "ContextMenu"), + function() { editor.execCommand("justifyright"); }, null, + config.btnList["justifyright"][1] ], + [ Xinha._lc("Justify Full", "ContextMenu"), + function() { editor.execCommand("justifyfull"); }, null, + config.btnList["justifyfull"][1] ] + ); + break; + } + } + + if (selection && !link) + menu.push(null, [ Xinha._lc("Make lin_k...", "ContextMenu"), + function() { editor.config.btnList['createlink'][3](editor); }, + Xinha._lc("Create a link", "ContextMenu"), + config.btnList["createlink"][1] ]); + + if(editor.config.ContextMenu.customHooks[currentTarget.tagName.toLowerCase()]) + { + var items = editor.config.ContextMenu.customHooks[currentTarget.tagName.toLowerCase()]; + + for (var i = 0; i < items.length; ++i) + { + menu.push(items[i]); + } + } + + for (var i = 0; i < elmenus.length; ++i) + menu.push(elmenus[i]); + + if (!/html|body/i.test(currentTarget.tagName)) + menu.push(null, + [ Xinha._lc({string: "Remove the $elem Element...", replace: {elem: "<" + currentTarget.tagName + ">"}}, "ContextMenu"), + function() { + if (confirm(Xinha._lc("Please confirm that you want to remove this element:", "ContextMenu") + " " + + currentTarget.tagName)) { + var el = currentTarget; + var p = el.parentNode; + p.removeChild(el); + if (Xinha.is_gecko) { + if (p.tagName.toLowerCase() == "td" && !p.hasChildNodes()) + p.appendChild(editor._doc.createElement("br")); + editor.forceRedraw(); + editor.focusEditor(); + editor.updateToolbar(); + if (table) { + var save_collapse = table.style.borderCollapse; + table.style.borderCollapse = "collapse"; + table.style.borderCollapse = "separate"; + table.style.borderCollapse = save_collapse; + } + } + } + }, + Xinha._lc("Remove this node from the document", "ContextMenu") ], + [ Xinha._lc("Insert paragraph before", "ContextMenu"), + function() { insertPara(false); }, + Xinha._lc("Insert a paragraph before the current node", "ContextMenu") ], + [ Xinha._lc("Insert paragraph after", "ContextMenu"), + function() { insertPara(true); }, + Xinha._lc("Insert a paragraph after the current node", "ContextMenu") ] + ); + if (!menu[0]) menu.shift(); //If the menu begins with a separator, remove it for cosmetical reasons + return menu; +}; + +ContextMenu.prototype.popupMenu = function(ev) { + var self = this; + if (this.currentMenu) + { + this.closeMenu(); + } + function getPos(el) { + var r = { x: el.offsetLeft, y: el.offsetTop }; + if (el.offsetParent) { + var tmp = getPos(el.offsetParent); + r.x += tmp.x; + r.y += tmp.y; + } + return r; + } + function documentClick(ev) { + ev || (ev = window.event); + if (!self.currentMenu) { + alert(Xinha._lc("How did you get here? (Please report!)", "ContextMenu")); + return false; + } + var el = Xinha.is_ie ? ev.srcElement : ev.target; + for (; el != null && el != self.currentMenu; el = el.parentNode); + if (el == null) + self.closeMenu(); + //Xinha._stopEvent(ev); + //return false; + } + var keys = []; + function keyPress(ev) { + ev || (ev = window.event); + Xinha._stopEvent(ev); + if (ev.keyCode == 27) { + self.closeMenu(); + return false; + } + var key = String.fromCharCode(Xinha.is_ie ? ev.keyCode : ev.charCode).toLowerCase(); + for (var i = keys.length; --i >= 0;) { + var k = keys[i]; + if (k[0].toLowerCase() == key) + k[1].__msh.activate(); + } + } + self.closeMenu = function() { + self.currentMenu.parentNode.removeChild(self.currentMenu); + self.currentMenu = null; + Xinha._removeEvent(document, "mousedown", documentClick); + Xinha._removeEvent(self.editordoc, "mousedown", documentClick); + if (keys.length > 0) + Xinha._removeEvent(self.editordoc, "keypress", keyPress); + if (Xinha.is_ie) + self.iePopup.hide(); + } + var target = Xinha.is_ie ? ev.srcElement : ev.target; + var ifpos = getPos(self.editor._htmlArea);//_iframe); + var x = ev.clientX + ifpos.x; + var y = ev.clientY + ifpos.y; + + var div; + var doc; + if (!Xinha.is_ie) { + doc = document; + } else { + // IE stinks + var popup = this.iePopup = window.createPopup(); + doc = popup.document; + doc.open(); + doc.write(""); + doc.close(); + } + div = doc.createElement("div"); + if (Xinha.is_ie) + div.unselectable = "on"; + div.oncontextmenu = function() { return false; }; + div.className = "htmlarea-context-menu"; + if (!Xinha.is_ie) { + div.style.visibility = "hidden"; + div.style.left = div.style.top = "-200px"; + } + doc.body.appendChild(div); + + var table = doc.createElement("table"); + div.appendChild(table); + table.cellSpacing = 0; + table.cellPadding = 0; + var parent = doc.createElement("tbody"); + table.appendChild(parent); + + var options = this.getContextMenu(target); + for (var i = 0; i < options.length; ++i) { + var option = options[i]; + var item = doc.createElement("tr"); + parent.appendChild(item); + if (Xinha.is_ie) + item.unselectable = "on"; + else item.onmousedown = function(ev) { + Xinha._stopEvent(ev); + return false; + }; + if (!option) { + item.className = "separator"; + var td = doc.createElement("td"); + td.className = "icon"; + var IE_IS_A_FUCKING_SHIT = '>'; + if (Xinha.is_ie) { + td.unselectable = "on"; + IE_IS_A_FUCKING_SHIT = " unselectable='on' style='height=1px'> "; + } + td.innerHTML = ""; + var td1 = td.cloneNode(true); + td1.className = "label"; + item.appendChild(td); + item.appendChild(td1); + } else { + var label = option[0]; + item.className = "item"; + item.__msh = { + item: item, + label: label, + action: option[1], + tooltip: option[2] || null, + icon: option[3] || null, + activate: function() { + self.closeMenu(); + self.editor.focusEditor(); + this.action(); + } + }; + label = label.replace(/_([a-zA-Z0-9])/, "$1"); + if (label != option[0]) + keys.push([ RegExp.$1, item ]); + label = label.replace(/__/, "_"); + var td1 = doc.createElement("td"); + if (Xinha.is_ie) + td1.unselectable = "on"; + item.appendChild(td1); + td1.className = "icon"; + if (item.__msh.icon) + { + var t = Xinha.makeBtnImg(item.__msh.icon, doc); + td1.appendChild(t); + } + var td2 = doc.createElement("td"); + if (Xinha.is_ie) + td2.unselectable = "on"; + item.appendChild(td2); + td2.className = "label"; + td2.innerHTML = label; + item.onmouseover = function() { + this.className += " hover"; + self.editor._statusBarTree.innerHTML = this.__msh.tooltip || ' '; + }; + item.onmouseout = function() { this.className = "item"; }; + item.oncontextmenu = function(ev) { + this.__msh.activate(); + if (!Xinha.is_ie) + Xinha._stopEvent(ev); + return false; + }; + item.onmouseup = function(ev) { + var timeStamp = (new Date()).getTime(); + if (timeStamp - self.timeStamp > 500) + this.__msh.activate(); + if (!Xinha.is_ie) + Xinha._stopEvent(ev); + return false; + }; + //if (typeof option[2] == "string") + //item.title = option[2]; + } + } + + if (!Xinha.is_ie) { + /* keep then menu from overflowing the client window boundaries */ + + /* provide a virtual margin to leave a swoosh of air between the + meny and the window edge. This should probably go into the menu + container css as margin 10px instead... + */ + var margin = 10; + + if (y + div.offsetHeight + margin > window.innerHeight) + y = window.innerHeight - div.offsetHeight - margin; + if (x + div.offsetWidth + margin > window.innerWidth) + x = window.innerWidth - div.offsetWidth - margin; + + div.style.left = x + "px"; + div.style.top = y + "px"; + div.style.visibility = "visible"; + + } else { + // To get the size we need to display the popup with some width/height + // then we can get the actual size of the div and redisplay the popup at the + // correct dimensions. + this.iePopup.show(ev.screenX, ev.screenY, 300,50); + var w = div.offsetWidth; + var h = div.offsetHeight; + this.iePopup.show(ev.screenX, ev.screenY, w, h); + } + + this.currentMenu = div; + this.timeStamp = (new Date()).getTime(); + + Xinha._addEvent(document, "mousedown", documentClick); + Xinha._addEvent(this.editordoc, "mousedown", documentClick); + if (keys.length > 0) + Xinha._addEvent(this.editordoc, "keypress", keyPress); + + Xinha._stopEvent(ev); + return false; +}; \ No newline at end of file Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/ContextMenu/lang/es.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/ContextMenu/lang/es.js,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/ContextMenu/lang/es.js 23 May 2010 11:58:33 -0000 1.1 @@ -0,0 +1,51 @@ +// I18N constants +// LANG: "es", ENCODING: UTF-8 +// translated: Derick Leony +{ + "Cut": "Cortar", + "Copy": "Copiar", + "Paste": "Pegar", + "_Image Properties...": "_Propiedades de imagen...", + "_Modify Link...": "_Modificar hipervínculo...", + "Chec_k Link...": "_Verificar hipervínculo...", + "_Remove Link...": "_Suprimir hipervínculo...", + "C_ell Properties...": "P_ropiedades de celda...", + "Ro_w Properties...": "Pr_opiedades de la fila...", + "I_nsert Row Before": "_Insertar fila delante", + "In_sert Row After": "I_nsertar fila detrás", + "_Delete Row": "S_uprimir fila", + "_Table Properties...": "Propi_edades de la tabla...", + "Insert _Column Before": "Insert_ar columna delante", + "Insert C_olumn After": "Insertar _columna detrás", + "De_lete Column": "_Suprimir columna", + "Justify Left": "Alinear a la izquierda", + "Justify Center": "Centrar", + "Justify Right": "Alinear a la derecha", + "Justify Full": "Justificar", + "Make lin_k...": "Convertir en _hipervínculo...", + "Remove the $elem Element...": "Suprimir el elemento $elem...", + "Insert paragraph before": "Insertar párrafo delante", + "Insert paragraph after": "Insertar párrafo detrás", + "Please confirm that you want to remove this element:": "Por favor, confirme que desea suprimir este elemento:", + "Remove this node from the document": "Suprimir este nodo del documento", + "How did you get here? (Please report!)": "¿Cómo ha llegado aquí? (¡Por favor reporte el error!)", + "Show the image properties dialog": "Mostrar el diálogo de propiedades de imagen", + "Modify URL": "Modificar URL", + "Current URL is": "La URL actual es", + "Opens this link in a new window": "Abre este hipervínculo en una ventana nueva", + "Please confirm that you want to unlink this element.": "Por favor, confirme que desea suprimir el hipervínculo de este elemento.", + "Link points to:": "El hipervínculo apunta a:", + "Unlink the current element": "Suprimir el hipervínculo del elemento actual", + "Show the Table Cell Properties dialog": "Mostrar el diálogo Propiedades de la Celda", + "Show the Table Row Properties dialog": "Mostrar el diálogo Propiedades de la Fila", + "Insert a new row before the current one": "Insertar una fila nueva antes de la actual", + "Insert a new row after the current one": "Insertar una fila nueva después de la actual", + "Delete the current row": "Suprimir la fila actual", + "Show the Table Properties dialog": "Mostrar el diálogo Propiedades de la Tabla", + "Insert a new column before the current one": "Insertar una columna nueva antes de la actual", + "Insert a new column after the current one": "Insertar una columna nueva después de la actual", + "Delete the current column": "Suprimir la columna actual", + "Create a link": "Crear un hipervínculo", + "Insert a paragraph before the current node": "Insertar un párrafo antes del nodo actual", + "Insert a paragraph after the current node": "Insertar un párrafo después del nodo actual" +}; Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/DynamicCSS/DynamicCSS.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/DynamicCSS/DynamicCSS.js,v diff -u -r1.2 -r1.3 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/DynamicCSS/DynamicCSS.js 27 Mar 2009 08:20:43 -0000 1.2 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/DynamicCSS/DynamicCSS.js 23 May 2010 11:58:33 -0000 1.3 @@ -1,3 +1,237 @@ -/* 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 */ -function DynamicCSS(e,d){this.editor=e;var a=e.config;var b=this;var g=null;var c=null;var f={id:"DynamicCSS-class",tooltip:this._lc("Choose stylesheet"),options:{"":""},action:function(h){b.onSelect(h,this)},refresh:function(h){b.updateValue(h,this)}};a.registerDropdown(f);a.addToolbarElement(["T[CSS]","DynamicCSS-class","separator"],"formatblock",-1)}DynamicCSS.parseStyleSheet=function(a){iframe=a._iframe.contentWindow.document;cssArray=DynamicCSS.cssArray;if(!cssArray){cssArray=new Array()}for(i=0;i"}}else{className="none";if(tagName=="all"){cssName=Xinha._lc("Default","DynamicCSS")}else{cssName="<"+Xinha._lc("Default","DynamicCSS")+">"}}b[tagName][className]=cssName;DynamicCSS.cssLength++}}}else{if(a[rule].styleSheet){b=DynamicCSS.applyCSSRule(a[rule].styleSheet.cssRules,b)}}}return b};DynamicCSS._pluginInfo={name:"DynamicCSS",version:"1.5.2",developer:"Holger Hees",developer_url:"http://www.systemconcept.de/",c_owner:"Holger Hees",sponsor:"System Concept GmbH",sponsor_url:"http://www.systemconcept.de/",license:"htmlArea"};DynamicCSS.prototype._lc=function(a){return Xinha._lc(a,"DynamicCSS")};DynamicCSS.prototype.onSelect=function(e,f){var b=e._toolbarObjects[f.id];var a=b.element.selectedIndex;var d=b.element.value;var c=e.getParentElement();if(d!="none"){c.className=d;DynamicCSS.lastClass=d}else{if(Xinha.is_gecko){c.removeAttribute("class")}else{c.removeAttribute("className")}}e.updateToolbar()};DynamicCSS.prototype.reparseTimer=function(b,c,a){if(DynamicCSS.parseCount<9){setTimeout(function(){DynamicCSS.cssLength=0;DynamicCSS.parseStyleSheet(b);if(DynamicCSS.cssOldLength!=DynamicCSS.cssLength){DynamicCSS.cssOldLength=DynamicCSS.cssLength;DynamicCSS.lastClass=null;a.updateValue(b,c)}a.reparseTimer(b,c,a)},DynamicCSS.parseCount*1000);DynamicCSS.parseCount=DynamicCSS.parseCount*2}};DynamicCSS.prototype.updateValue=function(e,b){cssArray=DynamicCSS.cssArray;if(!cssArray){DynamicCSS.cssLength=0;DynamicCSS.parseStyleSheet(e);cssArray=DynamicCSS.cssArray;DynamicCSS.cssOldLength=DynamicCSS.cssLength;DynamicCSS.parseCount=1;this.reparseTimer(e,b,this)}var h=e.getParentElement();var a=h.tagName.toLowerCase();var f=h.className;if(this.lastTag!=a||this.lastClass!=f){this.lastTag=a;this.lastClass=f;var g=e._toolbarObjects[b.id].element;while(g.length>0){g.options[g.length-1]=null}g.options[0]=new Option(this._lc("Default"),"none");if(cssArray){if(a!="body"||e.config.fullPage){if(cssArray[a]){for(cssClass in cssArray[a]){if(typeof cssArray[a][cssClass]!="string"){continue}if(cssClass=="none"){g.options[0]=new Option(cssArray[a][cssClass],cssClass)}else{g.options[g.length]=new Option(cssArray[a][cssClass],cssClass)}}}if(cssArray.all){for(cssClass in cssArray.all){if(typeof cssArray.all[cssClass]!="string"){continue}g.options[g.length]=new Option(cssArray.all[cssClass],cssClass)}}}else{if(cssArray[a]&&cssArray[a]["none"]){g.options[0]=new Option(cssArray[a]["none"],"none")}}}g.selectedIndex=0;if(typeof f!="undefined"&&/\S/.test(f)){var l=g.options;for(var c=l.length;--c>=0;){var d=l[c];if(f==d.value){g.selectedIndex=c;break}}if(g.selectedIndex==0){g.options[g.length]=new Option(this._lc("Undefined"),f);g.selectedIndex=g.length-1}}if(g.length>1){g.disabled=false}else{g.disabled=true}}}; \ No newline at end of file +// Dynamic CSS (className) plugin for HTMLArea +// Sponsored by http://www.systemconcept.de +// Implementation by Holger Hees +// +// (c) systemconcept.de 2004 +// Distributed under the same terms as HTMLArea itself. +// This notice MUST stay intact for use (see license.txt). + +function DynamicCSS(editor, args) { + this.editor = editor; + var cfg = editor.config; + var self = this; + +/*var cssArray=null; + var cssLength=0;*/ + var lastTag=null; + var lastClass=null; + + var css_class = { + id : "DynamicCSS-class", + tooltip : this._lc("Choose stylesheet"), + options : {"":""}, + action : function(editor) { self.onSelect(editor, this); }, + refresh : function(editor) { self.updateValue(editor, this); } + }; + cfg.registerDropdown(css_class); + cfg.addToolbarElement(["T[CSS]", "DynamicCSS-class", "separator"] , "formatblock", -1); +} + +DynamicCSS.parseStyleSheet=function(editor){ + iframe = editor._iframe.contentWindow.document; + + cssArray=DynamicCSS.cssArray; + if(!cssArray) cssArray=new Array(); + + for(i=0;i'; + } + else{ + className='none'; + if(tagName=='all') cssName=Xinha._lc("Default", 'DynamicCSS'); + else cssName='<'+Xinha._lc("Default", 'DynamicCSS')+'>'; + } + cssArray[tagName][className]=cssName; + DynamicCSS.cssLength++; + } + } + } + // ImportRule (Mozilla) + else if(cssRules[rule].styleSheet){ + cssArray=DynamicCSS.applyCSSRule(cssRules[rule].styleSheet.cssRules,cssArray); + } + } + return cssArray; +}; + +DynamicCSS._pluginInfo = { + name : "DynamicCSS", + version : "1.5.2", + developer : "Holger Hees", + developer_url : "http://www.systemconcept.de/", + c_owner : "Holger Hees", + sponsor : "System Concept GmbH", + sponsor_url : "http://www.systemconcept.de/", + license : "htmlArea" +}; + +DynamicCSS.prototype._lc = function(string) { + return Xinha._lc(string, 'DynamicCSS'); +}; + +DynamicCSS.prototype.onSelect = function(editor, obj) { + var tbobj = editor._toolbarObjects[obj.id]; + var index = tbobj.element.selectedIndex; + var className = tbobj.element.value; + + var parent = editor.getParentElement(); + + if(className!='none'){ + parent.className=className; + DynamicCSS.lastClass=className; + } + else{ + if(Xinha.is_gecko) parent.removeAttribute('class'); + else parent.removeAttribute('className'); + } + editor.updateToolbar(); +}; + +/*DynamicCSS.prototype.onMode = function(mode) { + if(mode=='wysiwyg'){ + // reparse possible changed css files + DynamicCSS.cssArray=null; + this.updateValue(this.editor,this.editor.config.customSelects["DynamicCSS-class"]); + } +}*/ + +DynamicCSS.prototype.reparseTimer = function(editor, obj, instance) { + // new attempt of rescan stylesheets in 1,2,4 and 8 second (e.g. for external css-files with longer initialisation) + if(DynamicCSS.parseCount<9){ + setTimeout(function () { + DynamicCSS.cssLength=0; + DynamicCSS.parseStyleSheet(editor); + if(DynamicCSS.cssOldLength!=DynamicCSS.cssLength){ + DynamicCSS.cssOldLength=DynamicCSS.cssLength; + DynamicCSS.lastClass=null; + instance.updateValue(editor, obj); + } + instance.reparseTimer(editor, obj, instance); + },DynamicCSS.parseCount*1000); + DynamicCSS.parseCount=DynamicCSS.parseCount*2; + } +}; + +DynamicCSS.prototype.updateValue = function(editor, obj) { + cssArray=DynamicCSS.cssArray; + // initial style init + if(!cssArray){ + DynamicCSS.cssLength=0; + DynamicCSS.parseStyleSheet(editor); + cssArray=DynamicCSS.cssArray; + DynamicCSS.cssOldLength=DynamicCSS.cssLength; + DynamicCSS.parseCount=1; + this.reparseTimer(editor,obj,this); + } + + var parent = editor.getParentElement(); + var tagName = parent.tagName.toLowerCase(); + var className = parent.className; + + if(this.lastTag!=tagName || this.lastClass!=className){ + this.lastTag=tagName; + this.lastClass=className; + + var select = editor._toolbarObjects[obj.id].element; + + while(select.length>0){ + select.options[select.length-1] = null; + } + + select.options[0]=new Option(this._lc("Default"),'none'); + if(cssArray){ + // style class only allowed if parent tag is not body or editor is in fullpage mode + if(tagName!='body' || editor.config.fullPage){ + if(cssArray[tagName]){ + for(cssClass in cssArray[tagName]){ + if(typeof cssArray[tagName][cssClass] != 'string') continue; + if(cssClass=='none') select.options[0]=new Option(cssArray[tagName][cssClass],cssClass); + else select.options[select.length]=new Option(cssArray[tagName][cssClass],cssClass); + } + } + + if(cssArray['all']){ + for(cssClass in cssArray['all']){ + if(typeof cssArray['all'][cssClass] != 'string') continue; + select.options[select.length]=new Option(cssArray['all'][cssClass],cssClass); + } + } + } + else if(cssArray[tagName] && cssArray[tagName]['none']) select.options[0]=new Option(cssArray[tagName]['none'],'none'); + } + + select.selectedIndex = 0; + + if (typeof className != "undefined" && /\S/.test(className)) { + var options = select.options; + for (var i = options.length; --i >= 0;) { + var option = options[i]; + if (className == option.value) { + select.selectedIndex = i; + break; + } + } + if(select.selectedIndex == 0){ + select.options[select.length]=new Option(this._lc("Undefined"),className); + select.selectedIndex=select.length-1; + } + } + + if(select.length>1) select.disabled=false; + else select.disabled=true; + } +}; \ No newline at end of file Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/DynamicCSS/dynamiccss.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/DynamicCSS/Attic/dynamiccss.js,v diff -u -r1.7 -r1.8 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/DynamicCSS/dynamiccss.js 27 Mar 2009 08:20:43 -0000 1.7 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/DynamicCSS/dynamiccss.js 23 May 2010 11:58:33 -0000 1.8 @@ -1,3 +1,237 @@ -/* 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 */ -function DynamicCSS(e,d){this.editor=e;var a=e.config;var b=this;var g=null;var c=null;var f={id:"DynamicCSS-class",tooltip:this._lc("Choose stylesheet"),options:{"":""},action:function(h){b.onSelect(h,this)},refresh:function(h){b.updateValue(h,this)}};a.registerDropdown(f);a.addToolbarElement(["T[CSS]","DynamicCSS-class","separator"],"formatblock",-1)}DynamicCSS.parseStyleSheet=function(a){iframe=a._iframe.contentWindow.document;cssArray=DynamicCSS.cssArray;if(!cssArray){cssArray=new Array()}for(i=0;i"}}else{className="none";if(tagName=="all"){cssName=Xinha._lc("Default","DynamicCSS")}else{cssName="<"+Xinha._lc("Default","DynamicCSS")+">"}}b[tagName][className]=cssName;DynamicCSS.cssLength++}}}else{if(a[rule].styleSheet){b=DynamicCSS.applyCSSRule(a[rule].styleSheet.cssRules,b)}}}return b};DynamicCSS._pluginInfo={name:"DynamicCSS",version:"1.5.2",developer:"Holger Hees",developer_url:"http://www.systemconcept.de/",c_owner:"Holger Hees",sponsor:"System Concept GmbH",sponsor_url:"http://www.systemconcept.de/",license:"htmlArea"};DynamicCSS.prototype._lc=function(a){return Xinha._lc(a,"DynamicCSS")};DynamicCSS.prototype.onSelect=function(e,f){var b=e._toolbarObjects[f.id];var a=b.element.selectedIndex;var d=b.element.value;var c=e.getParentElement();if(d!="none"){c.className=d;DynamicCSS.lastClass=d}else{if(Xinha.is_gecko){c.removeAttribute("class")}else{c.removeAttribute("className")}}e.updateToolbar()};DynamicCSS.prototype.reparseTimer=function(b,c,a){if(DynamicCSS.parseCount<9){setTimeout(function(){DynamicCSS.cssLength=0;DynamicCSS.parseStyleSheet(b);if(DynamicCSS.cssOldLength!=DynamicCSS.cssLength){DynamicCSS.cssOldLength=DynamicCSS.cssLength;DynamicCSS.lastClass=null;a.updateValue(b,c)}a.reparseTimer(b,c,a)},DynamicCSS.parseCount*1000);DynamicCSS.parseCount=DynamicCSS.parseCount*2}};DynamicCSS.prototype.updateValue=function(e,b){cssArray=DynamicCSS.cssArray;if(!cssArray){DynamicCSS.cssLength=0;DynamicCSS.parseStyleSheet(e);cssArray=DynamicCSS.cssArray;DynamicCSS.cssOldLength=DynamicCSS.cssLength;DynamicCSS.parseCount=1;this.reparseTimer(e,b,this)}var h=e.getParentElement();var a=h.tagName.toLowerCase();var f=h.className;if(this.lastTag!=a||this.lastClass!=f){this.lastTag=a;this.lastClass=f;var g=e._toolbarObjects[b.id].element;while(g.length>0){g.options[g.length-1]=null}g.options[0]=new Option(this._lc("Default"),"none");if(cssArray){if(a!="body"||e.config.fullPage){if(cssArray[a]){for(cssClass in cssArray[a]){if(typeof cssArray[a][cssClass]!="string"){continue}if(cssClass=="none"){g.options[0]=new Option(cssArray[a][cssClass],cssClass)}else{g.options[g.length]=new Option(cssArray[a][cssClass],cssClass)}}}if(cssArray.all){for(cssClass in cssArray.all){if(typeof cssArray.all[cssClass]!="string"){continue}g.options[g.length]=new Option(cssArray.all[cssClass],cssClass)}}}else{if(cssArray[a]&&cssArray[a]["none"]){g.options[0]=new Option(cssArray[a]["none"],"none")}}}g.selectedIndex=0;if(typeof f!="undefined"&&/\S/.test(f)){var l=g.options;for(var c=l.length;--c>=0;){var d=l[c];if(f==d.value){g.selectedIndex=c;break}}if(g.selectedIndex==0){g.options[g.length]=new Option(this._lc("Undefined"),f);g.selectedIndex=g.length-1}}if(g.length>1){g.disabled=false}else{g.disabled=true}}}; \ No newline at end of file +// Dynamic CSS (className) plugin for HTMLArea +// Sponsored by http://www.systemconcept.de +// Implementation by Holger Hees +// +// (c) systemconcept.de 2004 +// Distributed under the same terms as HTMLArea itself. +// This notice MUST stay intact for use (see license.txt). + +function DynamicCSS(editor, args) { + this.editor = editor; + var cfg = editor.config; + var self = this; + +/*var cssArray=null; + var cssLength=0;*/ + var lastTag=null; + var lastClass=null; + + var css_class = { + id : "DynamicCSS-class", + tooltip : this._lc("Choose stylesheet"), + options : {"":""}, + action : function(editor) { self.onSelect(editor, this); }, + refresh : function(editor) { self.updateValue(editor, this); } + }; + cfg.registerDropdown(css_class); + cfg.addToolbarElement(["T[CSS]", "DynamicCSS-class", "separator"] , "formatblock", -1); +} + +DynamicCSS.parseStyleSheet=function(editor){ + iframe = editor._iframe.contentWindow.document; + + cssArray=DynamicCSS.cssArray; + if(!cssArray) cssArray=new Array(); + + for(i=0;i'; + } + else{ + className='none'; + if(tagName=='all') cssName=Xinha._lc("Default", 'DynamicCSS'); + else cssName='<'+Xinha._lc("Default", 'DynamicCSS')+'>'; + } + cssArray[tagName][className]=cssName; + DynamicCSS.cssLength++; + } + } + } + // ImportRule (Mozilla) + else if(cssRules[rule].styleSheet){ + cssArray=DynamicCSS.applyCSSRule(cssRules[rule].styleSheet.cssRules,cssArray); + } + } + return cssArray; +}; + +DynamicCSS._pluginInfo = { + name : "DynamicCSS", + version : "1.5.2", + developer : "Holger Hees", + developer_url : "http://www.systemconcept.de/", + c_owner : "Holger Hees", + sponsor : "System Concept GmbH", + sponsor_url : "http://www.systemconcept.de/", + license : "htmlArea" +}; + +DynamicCSS.prototype._lc = function(string) { + return Xinha._lc(string, 'DynamicCSS'); +}; + +DynamicCSS.prototype.onSelect = function(editor, obj) { + var tbobj = editor._toolbarObjects[obj.id]; + var index = tbobj.element.selectedIndex; + var className = tbobj.element.value; + + var parent = editor.getParentElement(); + + if(className!='none'){ + parent.className=className; + DynamicCSS.lastClass=className; + } + else{ + if(Xinha.is_gecko) parent.removeAttribute('class'); + else parent.removeAttribute('className'); + } + editor.updateToolbar(); +}; + +/*DynamicCSS.prototype.onMode = function(mode) { + if(mode=='wysiwyg'){ + // reparse possible changed css files + DynamicCSS.cssArray=null; + this.updateValue(this.editor,this.editor.config.customSelects["DynamicCSS-class"]); + } +}*/ + +DynamicCSS.prototype.reparseTimer = function(editor, obj, instance) { + // new attempt of rescan stylesheets in 1,2,4 and 8 second (e.g. for external css-files with longer initialisation) + if(DynamicCSS.parseCount<9){ + setTimeout(function () { + DynamicCSS.cssLength=0; + DynamicCSS.parseStyleSheet(editor); + if(DynamicCSS.cssOldLength!=DynamicCSS.cssLength){ + DynamicCSS.cssOldLength=DynamicCSS.cssLength; + DynamicCSS.lastClass=null; + instance.updateValue(editor, obj); + } + instance.reparseTimer(editor, obj, instance); + },DynamicCSS.parseCount*1000); + DynamicCSS.parseCount=DynamicCSS.parseCount*2; + } +}; + +DynamicCSS.prototype.updateValue = function(editor, obj) { + cssArray=DynamicCSS.cssArray; + // initial style init + if(!cssArray){ + DynamicCSS.cssLength=0; + DynamicCSS.parseStyleSheet(editor); + cssArray=DynamicCSS.cssArray; + DynamicCSS.cssOldLength=DynamicCSS.cssLength; + DynamicCSS.parseCount=1; + this.reparseTimer(editor,obj,this); + } + + var parent = editor.getParentElement(); + var tagName = parent.tagName.toLowerCase(); + var className = parent.className; + + if(this.lastTag!=tagName || this.lastClass!=className){ + this.lastTag=tagName; + this.lastClass=className; + + var select = editor._toolbarObjects[obj.id].element; + + while(select.length>0){ + select.options[select.length-1] = null; + } + + select.options[0]=new Option(this._lc("Default"),'none'); + if(cssArray){ + // style class only allowed if parent tag is not body or editor is in fullpage mode + if(tagName!='body' || editor.config.fullPage){ + if(cssArray[tagName]){ + for(cssClass in cssArray[tagName]){ + if(typeof cssArray[tagName][cssClass] != 'string') continue; + if(cssClass=='none') select.options[0]=new Option(cssArray[tagName][cssClass],cssClass); + else select.options[select.length]=new Option(cssArray[tagName][cssClass],cssClass); + } + } + + if(cssArray['all']){ + for(cssClass in cssArray['all']){ + if(typeof cssArray['all'][cssClass] != 'string') continue; + select.options[select.length]=new Option(cssArray['all'][cssClass],cssClass); + } + } + } + else if(cssArray[tagName] && cssArray[tagName]['none']) select.options[0]=new Option(cssArray[tagName]['none'],'none'); + } + + select.selectedIndex = 0; + + if (typeof className != "undefined" && /\S/.test(className)) { + var options = select.options; + for (var i = options.length; --i >= 0;) { + var option = options[i]; + if (className == option.value) { + select.selectedIndex = i; + break; + } + } + if(select.selectedIndex == 0){ + select.options[select.length]=new Option(this._lc("Undefined"),className); + select.selectedIndex=select.length-1; + } + } + + if(select.length>1) select.disabled=false; + else select.disabled=true; + } +}; \ No newline at end of file Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Equation/ASCIIMathML.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Equation/ASCIIMathML.js,v diff -u -r1.6 -r1.7 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Equation/ASCIIMathML.js 27 Mar 2009 08:20:43 -0000 1.6 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Equation/ASCIIMathML.js 23 May 2010 11:58:33 -0000 1.7 @@ -1,3 +1,3365 @@ -/* 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 */ -var checkForMathML=true;var notifyIfNoMathML=true;var alertIfNoMathML=false;if(typeof mathcolor=="undefined"){var mathcolor="red"}if(typeof mathfontfamily=="undefined"){var mathfontfamily="serif"}var displaystyle=true;if(typeof showasciiformulaonhover=="undefined"){var showasciiformulaonhover=true}var decimalsign=".";var AMdelimiter1="`",AMescape1="\\\\`";var AMdelimiter2="$",AMescape2="\\\\\\$",AMdelimiter2regexp="\\$";var doubleblankmathdelimiter=false;var isIE=document.createElementNS==null;if(document.getElementById==null){alert("This webpage requires a recent browser such as\nMozilla/Netscape 7+ or Internet Explorer 6+MathPlayer")}function AMcreateElementXHTML(a){if(isIE){return document.createElement(a)}else{return document.createElementNS("http://www.w3.org/1999/xhtml",a)}}function AMnoMathMLNote(){var b=AMcreateElementXHTML("h3");b.setAttribute("align","center");b.appendChild(AMcreateElementXHTML("p"));b.appendChild(document.createTextNode("To view the "));var a=AMcreateElementXHTML("a");a.appendChild(document.createTextNode("ASCIIMathML"));a.setAttribute("href","http://www.chapman.edu/~jipsen/asciimath.html");b.appendChild(a);b.appendChild(document.createTextNode(" notation use Internet Explorer 6+"));a=AMcreateElementXHTML("a");a.appendChild(document.createTextNode("MathPlayer"));a.setAttribute("href","http://www.dessci.com/en/products/mathplayer/download.htm");b.appendChild(a);b.appendChild(document.createTextNode(" or Netscape/Mozilla/Firefox"));b.appendChild(AMcreateElementXHTML("p"));return b}function AMisMathMLavailable(){if(navigator.appName.slice(0,8)=="Netscape"){if(navigator.appVersion.slice(0,1)>="5"){return null}else{return AMnoMathMLNote()}}else{if(navigator.appName.slice(0,9)=="Microsoft"){try{var b=new ActiveXObject("MathPlayer.Factory.1");return null}catch(a){return AMnoMathMLNote()}}else{return AMnoMathMLNote()}}}var AMcal=[61237,8492,61238,61239,8496,8497,61240,8459,8464,61241,61242,8466,8499,61243,61244,61245,61246,8475,61247,61248,61249,61250,61251,61252,61253,61254];var AMfrk=[61277,61278,8493,61279,61280,61281,61282,8460,8465,61283,61284,61285,61286,61287,61288,61289,61290,8476,61291,61292,61293,61294,61295,61296,61297,8488];var AMbbb=[61324,61325,8450,61326,61327,61328,61329,8461,61330,61331,61332,61333,61334,8469,61335,8473,8474,8477,61336,61337,61338,61339,61340,61341,61342,8484];var CONST=0,UNARY=1,BINARY=2,INFIX=3,LEFTBRACKET=4,RIGHTBRACKET=5,SPACE=6,UNDEROVER=7,DEFINITION=8,LEFTRIGHT=9,TEXT=10;var AMsqrt={input:"sqrt",tag:"msqrt",output:"sqrt",tex:null,ttype:UNARY},AMroot={input:"root",tag:"mroot",output:"root",tex:null,ttype:BINARY},AMfrac={input:"frac",tag:"mfrac",output:"/",tex:null,ttype:BINARY},AMdiv={input:"/",tag:"mfrac",output:"/",tex:null,ttype:INFIX},AMover={input:"stackrel",tag:"mover",output:"stackrel",tex:null,ttype:BINARY},AMsub={input:"_",tag:"msub",output:"_",tex:null,ttype:INFIX},AMsup={input:"^",tag:"msup",output:"^",tex:null,ttype:INFIX},AMtext={input:"text",tag:"mtext",output:"text",tex:null,ttype:TEXT},AMmbox={input:"mbox",tag:"mtext",output:"mbox",tex:null,ttype:TEXT},AMquote={input:'"',tag:"mtext",output:"mbox",tex:null,ttype:TEXT};var AMsymbols=[{input:"alpha",tag:"mi",output:"\u03B1",tex:null,ttype:CONST},{input:"beta",tag:"mi",output:"\u03B2",tex:null,ttype:CONST},{input:"chi",tag:"mi",output:"\u03C7",tex:null,ttype:CONST},{input:"delta",tag:"mi",output:"\u03B4",tex:null,ttype:CONST},{input:"Delta",tag:"mo",output:"\u0394",tex:null,ttype:CONST},{input:"epsi",tag:"mi",output:"\u03B5",tex:"epsilon",ttype:CONST},{input:"varepsilon",tag:"mi",output:"\u025B",tex:null,ttype:CONST},{input:"eta",tag:"mi",output:"\u03B7",tex:null,ttype:CONST},{input:"gamma",tag:"mi",output:"\u03B3",tex:null,ttype:CONST},{input:"Gamma",tag:"mo",output:"\u0393",tex:null,ttype:CONST},{input:"iota",tag:"mi",output:"\u03B9",tex:null,ttype:CONST},{input:"kappa",tag:"mi",output:"\u03BA",tex:null,ttype:CONST},{input:"lambda",tag:"mi",output:"\u03BB",tex:null,ttype:CONST},{input:"Lambda",tag:"mo",output:"\u039B",tex:null,ttype:CONST},{input:"mu",tag:"mi",output:"\u03BC",tex:null,ttype:CONST},{input:"nu",tag:"mi",output:"\u03BD",tex:null,ttype:CONST},{input:"omega",tag:"mi",output:"\u03C9",tex:null,ttype:CONST},{input:"Omega",tag:"mo",output:"\u03A9",tex:null,ttype:CONST},{input:"phi",tag:"mi",output:"\u03C6",tex:null,ttype:CONST},{input:"varphi",tag:"mi",output:"\u03D5",tex:null,ttype:CONST},{input:"Phi",tag:"mo",output:"\u03A6",tex:null,ttype:CONST},{input:"pi",tag:"mi",output:"\u03C0",tex:null,ttype:CONST},{input:"Pi",tag:"mo",output:"\u03A0",tex:null,ttype:CONST},{input:"psi",tag:"mi",output:"\u03C8",tex:null,ttype:CONST},{input:"Psi",tag:"mi",output:"\u03A8",tex:null,ttype:CONST},{input:"rho",tag:"mi",output:"\u03C1",tex:null,ttype:CONST},{input:"sigma",tag:"mi",output:"\u03C3",tex:null,ttype:CONST},{input:"Sigma",tag:"mo",output:"\u03A3",tex:null,ttype:CONST},{input:"tau",tag:"mi",output:"\u03C4",tex:null,ttype:CONST},{input:"theta",tag:"mi",output:"\u03B8",tex:null,ttype:CONST},{input:"vartheta",tag:"mi",output:"\u03D1",tex:null,ttype:CONST},{input:"Theta",tag:"mo",output:"\u0398",tex:null,ttype:CONST},{input:"upsilon",tag:"mi",output:"\u03C5",tex:null,ttype:CONST},{input:"xi",tag:"mi",output:"\u03BE",tex:null,ttype:CONST},{input:"Xi",tag:"mo",output:"\u039E",tex:null,ttype:CONST},{input:"zeta",tag:"mi",output:"\u03B6",tex:null,ttype:CONST},{input:"*",tag:"mo",output:"\u22C5",tex:"cdot",ttype:CONST},{input:"**",tag:"mo",output:"\u22C6",tex:"star",ttype:CONST},{input:"//",tag:"mo",output:"/",tex:null,ttype:CONST},{input:"\\\\",tag:"mo",output:"\\",tex:"backslash",ttype:CONST},{input:"setminus",tag:"mo",output:"\\",tex:null,ttype:CONST},{input:"xx",tag:"mo",output:"\u00D7",tex:"times",ttype:CONST},{input:"-:",tag:"mo",output:"\u00F7",tex:"divide",ttype:CONST},{input:"@",tag:"mo",output:"\u2218",tex:"circ",ttype:CONST},{input:"o+",tag:"mo",output:"\u2295",tex:"oplus",ttype:CONST},{input:"ox",tag:"mo",output:"\u2297",tex:"otimes",ttype:CONST},{input:"o.",tag:"mo",output:"\u2299",tex:"odot",ttype:CONST},{input:"sum",tag:"mo",output:"\u2211",tex:null,ttype:UNDEROVER},{input:"prod",tag:"mo",output:"\u220F",tex:null,ttype:UNDEROVER},{input:"^^",tag:"mo",output:"\u2227",tex:"wedge",ttype:CONST},{input:"^^^",tag:"mo",output:"\u22C0",tex:"bigwedge",ttype:UNDEROVER},{input:"vv",tag:"mo",output:"\u2228",tex:"vee",ttype:CONST},{input:"vvv",tag:"mo",output:"\u22C1",tex:"bigvee",ttype:UNDEROVER},{input:"nn",tag:"mo",output:"\u2229",tex:"cap",ttype:CONST},{input:"nnn",tag:"mo",output:"\u22C2",tex:"bigcap",ttype:UNDEROVER},{input:"uu",tag:"mo",output:"\u222A",tex:"cup",ttype:CONST},{input:"uuu",tag:"mo",output:"\u22C3",tex:"bigcup",ttype:UNDEROVER},{input:"!=",tag:"mo",output:"\u2260",tex:"ne",ttype:CONST},{input:":=",tag:"mo",output:":=",tex:null,ttype:CONST},{input:"lt",tag:"mo",output:"<",tex:null,ttype:CONST},{input:"<=",tag:"mo",output:"\u2264",tex:"le",ttype:CONST},{input:"lt=",tag:"mo",output:"\u2264",tex:"leq",ttype:CONST},{input:">=",tag:"mo",output:"\u2265",tex:"ge",ttype:CONST},{input:"geq",tag:"mo",output:"\u2265",tex:null,ttype:CONST},{input:"-<",tag:"mo",output:"\u227A",tex:"prec",ttype:CONST},{input:"-lt",tag:"mo",output:"\u227A",tex:null,ttype:CONST},{input:">-",tag:"mo",output:"\u227B",tex:"succ",ttype:CONST},{input:"-<=",tag:"mo",output:"\u2AAF",tex:"preceq",ttype:CONST},{input:">-=",tag:"mo",output:"\u2AB0",tex:"succeq",ttype:CONST},{input:"in",tag:"mo",output:"\u2208",tex:null,ttype:CONST},{input:"!in",tag:"mo",output:"\u2209",tex:"notin",ttype:CONST},{input:"sub",tag:"mo",output:"\u2282",tex:"subset",ttype:CONST},{input:"sup",tag:"mo",output:"\u2283",tex:"supset",ttype:CONST},{input:"sube",tag:"mo",output:"\u2286",tex:"subseteq",ttype:CONST},{input:"supe",tag:"mo",output:"\u2287",tex:"supseteq",ttype:CONST},{input:"-=",tag:"mo",output:"\u2261",tex:"equiv",ttype:CONST},{input:"~=",tag:"mo",output:"\u2245",tex:"cong",ttype:CONST},{input:"~~",tag:"mo",output:"\u2248",tex:"approx",ttype:CONST},{input:"prop",tag:"mo",output:"\u221D",tex:"propto",ttype:CONST},{input:"and",tag:"mtext",output:"and",tex:null,ttype:SPACE},{input:"or",tag:"mtext",output:"or",tex:null,ttype:SPACE},{input:"not",tag:"mo",output:"\u00AC",tex:"neg",ttype:CONST},{input:"=>",tag:"mo",output:"\u21D2",tex:"implies",ttype:CONST},{input:"if",tag:"mo",output:"if",tex:null,ttype:SPACE},{input:"<=>",tag:"mo",output:"\u21D4",tex:"iff",ttype:CONST},{input:"AA",tag:"mo",output:"\u2200",tex:"forall",ttype:CONST},{input:"EE",tag:"mo",output:"\u2203",tex:"exists",ttype:CONST},{input:"_|_",tag:"mo",output:"\u22A5",tex:"bot",ttype:CONST},{input:"TT",tag:"mo",output:"\u22A4",tex:"top",ttype:CONST},{input:"|--",tag:"mo",output:"\u22A2",tex:"vdash",ttype:CONST},{input:"|==",tag:"mo",output:"\u22A8",tex:"models",ttype:CONST},{input:"(",tag:"mo",output:"(",tex:null,ttype:LEFTBRACKET},{input:")",tag:"mo",output:")",tex:null,ttype:RIGHTBRACKET},{input:"[",tag:"mo",output:"[",tex:null,ttype:LEFTBRACKET},{input:"]",tag:"mo",output:"]",tex:null,ttype:RIGHTBRACKET},{input:"{",tag:"mo",output:"{",tex:null,ttype:LEFTBRACKET},{input:"}",tag:"mo",output:"}",tex:null,ttype:RIGHTBRACKET},{input:"|",tag:"mo",output:"|",tex:null,ttype:LEFTRIGHT},{input:"(:",tag:"mo",output:"\u2329",tex:"langle",ttype:LEFTBRACKET},{input:":)",tag:"mo",output:"\u232A",tex:"rangle",ttype:RIGHTBRACKET},{input:"<<",tag:"mo",output:"\u2329",tex:null,ttype:LEFTBRACKET},{input:">>",tag:"mo",output:"\u232A",tex:null,ttype:RIGHTBRACKET},{input:"{:",tag:"mo",output:"{:",tex:null,ttype:LEFTBRACKET,invisible:true},{input:":}",tag:"mo",output:":}",tex:null,ttype:RIGHTBRACKET,invisible:true},{input:"int",tag:"mo",output:"\u222B",tex:null,ttype:CONST},{input:"dx",tag:"mi",output:"{:d x:}",tex:null,ttype:DEFINITION},{input:"dy",tag:"mi",output:"{:d y:}",tex:null,ttype:DEFINITION},{input:"dz",tag:"mi",output:"{:d z:}",tex:null,ttype:DEFINITION},{input:"dt",tag:"mi",output:"{:d t:}",tex:null,ttype:DEFINITION},{input:"oint",tag:"mo",output:"\u222E",tex:null,ttype:CONST},{input:"del",tag:"mo",output:"\u2202",tex:"partial",ttype:CONST},{input:"grad",tag:"mo",output:"\u2207",tex:"nabla",ttype:CONST},{input:"+-",tag:"mo",output:"\u00B1",tex:"pm",ttype:CONST},{input:"O/",tag:"mo",output:"\u2205",tex:"emptyset",ttype:CONST},{input:"oo",tag:"mo",output:"\u221E",tex:"infty",ttype:CONST},{input:"aleph",tag:"mo",output:"\u2135",tex:null,ttype:CONST},{input:"...",tag:"mo",output:"...",tex:"ldots",ttype:CONST},{input:":.",tag:"mo",output:"\u2234",tex:"therefore",ttype:CONST},{input:"/_",tag:"mo",output:"\u2220",tex:"angle",ttype:CONST},{input:"\\ ",tag:"mo",output:"\u00A0",tex:null,ttype:CONST},{input:"quad",tag:"mo",output:"\u00A0\u00A0",tex:null,ttype:CONST},{input:"qquad",tag:"mo",output:"\u00A0\u00A0\u00A0\u00A0",tex:null,ttype:CONST},{input:"cdots",tag:"mo",output:"\u22EF",tex:null,ttype:CONST},{input:"vdots",tag:"mo",output:"\u22EE",tex:null,ttype:CONST},{input:"ddots",tag:"mo",output:"\u22F1",tex:null,ttype:CONST},{input:"diamond",tag:"mo",output:"\u22C4",tex:null,ttype:CONST},{input:"square",tag:"mo",output:"\u25A1",tex:null,ttype:CONST},{input:"|__",tag:"mo",output:"\u230A",tex:"lfloor",ttype:CONST},{input:"__|",tag:"mo",output:"\u230B",tex:"rfloor",ttype:CONST},{input:"|~",tag:"mo",output:"\u2308",tex:"lceiling",ttype:CONST},{input:"~|",tag:"mo",output:"\u2309",tex:"rceiling",ttype:CONST},{input:"CC",tag:"mo",output:"\u2102",tex:null,ttype:CONST},{input:"NN",tag:"mo",output:"\u2115",tex:null,ttype:CONST},{input:"QQ",tag:"mo",output:"\u211A",tex:null,ttype:CONST},{input:"RR",tag:"mo",output:"\u211D",tex:null,ttype:CONST},{input:"ZZ",tag:"mo",output:"\u2124",tex:null,ttype:CONST},{input:"f",tag:"mi",output:"f",tex:null,ttype:UNARY,func:true},{input:"g",tag:"mi",output:"g",tex:null,ttype:UNARY,func:true},{input:"lim",tag:"mo",output:"lim",tex:null,ttype:UNDEROVER},{input:"Lim",tag:"mo",output:"Lim",tex:null,ttype:UNDEROVER},{input:"sin",tag:"mo",output:"sin",tex:null,ttype:UNARY,func:true},{input:"cos",tag:"mo",output:"cos",tex:null,ttype:UNARY,func:true},{input:"tan",tag:"mo",output:"tan",tex:null,ttype:UNARY,func:true},{input:"sinh",tag:"mo",output:"sinh",tex:null,ttype:UNARY,func:true},{input:"cosh",tag:"mo",output:"cosh",tex:null,ttype:UNARY,func:true},{input:"tanh",tag:"mo",output:"tanh",tex:null,ttype:UNARY,func:true},{input:"cot",tag:"mo",output:"cot",tex:null,ttype:UNARY,func:true},{input:"sec",tag:"mo",output:"sec",tex:null,ttype:UNARY,func:true},{input:"csc",tag:"mo",output:"csc",tex:null,ttype:UNARY,func:true},{input:"log",tag:"mo",output:"log",tex:null,ttype:UNARY,func:true},{input:"ln",tag:"mo",output:"ln",tex:null,ttype:UNARY,func:true},{input:"det",tag:"mo",output:"det",tex:null,ttype:UNARY,func:true},{input:"dim",tag:"mo",output:"dim",tex:null,ttype:CONST},{input:"mod",tag:"mo",output:"mod",tex:null,ttype:CONST},{input:"gcd",tag:"mo",output:"gcd",tex:null,ttype:UNARY,func:true},{input:"lcm",tag:"mo",output:"lcm",tex:null,ttype:UNARY,func:true},{input:"lub",tag:"mo",output:"lub",tex:null,ttype:CONST},{input:"glb",tag:"mo",output:"glb",tex:null,ttype:CONST},{input:"min",tag:"mo",output:"min",tex:null,ttype:UNDEROVER},{input:"max",tag:"mo",output:"max",tex:null,ttype:UNDEROVER},{input:"uarr",tag:"mo",output:"\u2191",tex:"uparrow",ttype:CONST},{input:"darr",tag:"mo",output:"\u2193",tex:"downarrow",ttype:CONST},{input:"rarr",tag:"mo",output:"\u2192",tex:"rightarrow",ttype:CONST},{input:"->",tag:"mo",output:"\u2192",tex:"to",ttype:CONST},{input:"|->",tag:"mo",output:"\u21A6",tex:"mapsto",ttype:CONST},{input:"larr",tag:"mo",output:"\u2190",tex:"leftarrow",ttype:CONST},{input:"harr",tag:"mo",output:"\u2194",tex:"leftrightarrow",ttype:CONST},{input:"rArr",tag:"mo",output:"\u21D2",tex:"Rightarrow",ttype:CONST},{input:"lArr",tag:"mo",output:"\u21D0",tex:"Leftarrow",ttype:CONST},{input:"hArr",tag:"mo",output:"\u21D4",tex:"Leftrightarrow",ttype:CONST},AMsqrt,AMroot,AMfrac,AMdiv,AMover,AMsub,AMsup,{input:"hat",tag:"mover",output:"\u005E",tex:null,ttype:UNARY,acc:true},{input:"bar",tag:"mover",output:"\u00AF",tex:"overline",ttype:UNARY,acc:true},{input:"vec",tag:"mover",output:"\u2192",tex:null,ttype:UNARY,acc:true},{input:"dot",tag:"mover",output:".",tex:null,ttype:UNARY,acc:true},{input:"ddot",tag:"mover",output:"..",tex:null,ttype:UNARY,acc:true},{input:"ul",tag:"munder",output:"\u0332",tex:"underline",ttype:UNARY,acc:true},AMtext,AMmbox,AMquote,{input:"bb",tag:"mstyle",atname:"fontweight",atval:"bold",output:"bb",tex:null,ttype:UNARY},{input:"mathbf",tag:"mstyle",atname:"fontweight",atval:"bold",output:"mathbf",tex:null,ttype:UNARY},{input:"sf",tag:"mstyle",atname:"fontfamily",atval:"sans-serif",output:"sf",tex:null,ttype:UNARY},{input:"mathsf",tag:"mstyle",atname:"fontfamily",atval:"sans-serif",output:"mathsf",tex:null,ttype:UNARY},{input:"bbb",tag:"mstyle",atname:"mathvariant",atval:"double-struck",output:"bbb",tex:null,ttype:UNARY,codes:AMbbb},{input:"mathbb",tag:"mstyle",atname:"mathvariant",atval:"double-struck",output:"mathbb",tex:null,ttype:UNARY,codes:AMbbb},{input:"cc",tag:"mstyle",atname:"mathvariant",atval:"script",output:"cc",tex:null,ttype:UNARY,codes:AMcal},{input:"mathcal",tag:"mstyle",atname:"mathvariant",atval:"script",output:"mathcal",tex:null,ttype:UNARY,codes:AMcal},{input:"tt",tag:"mstyle",atname:"fontfamily",atval:"monospace",output:"tt",tex:null,ttype:UNARY},{input:"mathtt",tag:"mstyle",atname:"fontfamily",atval:"monospace",output:"mathtt",tex:null,ttype:UNARY},{input:"fr",tag:"mstyle",atname:"mathvariant",atval:"fraktur",output:"fr",tex:null,ttype:UNARY,codes:AMfrk},{input:"mathfrak",tag:"mstyle",atname:"mathvariant",atval:"fraktur",output:"mathfrak",tex:null,ttype:UNARY,codes:AMfrk}];function compareNames(b,a){if(b.input>a.input){return 1}else{return -1}}var AMnames=[];function AMinitSymbols(){var b=[],a;for(a=0;a>1;if(b[a]=AMnames[a]}AMpreviousSymbol=AMcurrentSymbol;if(e!=""){AMcurrentSymbol=AMsymbols[d].ttype;return AMsymbols[d]}AMcurrentSymbol=CONST;a=1;m=g.slice(0,1);var h=true;while("0"<=m&&m<="9"&&a<=g.length){m=g.slice(a,a+1);a++}if(m==decimalsign){m=g.slice(a,a+1);if("0"<=m&&m<="9"){h=false;a++;while("0"<=m&&m<="9"&&a<=g.length){m=g.slice(a,a+1);a++}}}if((h&&a>1)||a>2){m=g.slice(0,a-1);l="mn"}else{a=2;m=g.slice(0,1);l=(("A">m||m>"Z")&&("a">m||m>"z")?"mo":"mi")}if(m=="-"&&AMpreviousSymbol==INFIX){AMcurrentSymbol=INFIX;return{input:m,tag:l,output:m,ttype:UNARY,func:true}}return{input:m,tag:l,output:m,ttype:CONST}}function AMremoveBrackets(b){var a;if(b.nodeName=="mrow"){a=b.firstChild.firstChild.nodeValue;if(a=="("||a=="["||a=="{"){b.removeChild(b.firstChild)}}if(b.nodeName=="mrow"){a=b.lastChild.firstChild.nodeValue;if(a==")"||a=="]"||a=="}"){b.removeChild(b.lastChild)}}}var AMnestingDepth,AMpreviousSymbol,AMcurrentSymbol;function AMparseSexpr(g){var c,b,l,e,k,f=document.createDocumentFragment();g=AMremoveCharsAndBlanks(g,0);c=AMgetSymbol(g);if(c==null||c.ttype==RIGHTBRACKET&&AMnestingDepth>0){return[null,g]}if(c.ttype==DEFINITION){g=c.output+AMremoveCharsAndBlanks(g,c.input.length);c=AMgetSymbol(g)}switch(c.ttype){case UNDEROVER:case CONST:g=AMremoveCharsAndBlanks(g,c.input.length);return[AMcreateMmlNode(c.tag,document.createTextNode(c.output)),g];case LEFTBRACKET:AMnestingDepth++;g=AMremoveCharsAndBlanks(g,c.input.length);l=AMparseExpr(g,true);AMnestingDepth--;if(typeof c.invisible=="boolean"&&c.invisible){b=AMcreateMmlNode("mrow",l[0])}else{b=AMcreateMmlNode("mo",document.createTextNode(c.output));b=AMcreateMmlNode("mrow",b);b.appendChild(l[0])}return[b,l[1]];case TEXT:if(c!=AMquote){g=AMremoveCharsAndBlanks(g,c.input.length)}if(g.charAt(0)=="{"){e=g.indexOf("}")}else{if(g.charAt(0)=="("){e=g.indexOf(")")}else{if(g.charAt(0)=="["){e=g.indexOf("]")}else{if(c==AMquote){e=g.slice(1).indexOf('"')+1}else{e=0}}}}if(e==-1){e=g.length}k=g.slice(1,e);if(k.charAt(0)==" "){b=AMcreateElementMathML("mspace");b.setAttribute("width","1ex");f.appendChild(b)}f.appendChild(AMcreateMmlNode(c.tag,document.createTextNode(k)));if(k.charAt(k.length-1)==" "){b=AMcreateElementMathML("mspace");b.setAttribute("width","1ex");f.appendChild(b)}g=AMremoveCharsAndBlanks(g,e+1);return[AMcreateMmlNode("mrow",f),g];case UNARY:g=AMremoveCharsAndBlanks(g,c.input.length);l=AMparseSexpr(g);if(l[0]==null){return[AMcreateMmlNode(c.tag,document.createTextNode(c.output)),g]}if(typeof c.func=="boolean"&&c.func){k=g.charAt(0);if(k=="^"||k=="_"||k=="/"||k=="|"||k==","){return[AMcreateMmlNode(c.tag,document.createTextNode(c.output)),g]}else{b=AMcreateMmlNode("mrow",AMcreateMmlNode(c.tag,document.createTextNode(c.output)));b.appendChild(l[0]);return[b,l[1]]}}AMremoveBrackets(l[0]);if(c.input=="sqrt"){return[AMcreateMmlNode(c.tag,l[0]),l[1]]}else{if(typeof c.acc=="boolean"&&c.acc){b=AMcreateMmlNode(c.tag,l[0]);b.appendChild(AMcreateMmlNode("mo",document.createTextNode(c.output)));return[b,l[1]]}else{if(!isIE&&typeof c.codes!="undefined"){for(e=0;e64&&k.charCodeAt(d)<91){h=h+String.fromCharCode(c.codes[k.charCodeAt(d)-65])}else{h=h+k.charAt(d)}}if(l[0].nodeName=="mi"){l[0]=AMcreateElementMathML("mo").appendChild(document.createTextNode(h))}else{l[0].replaceChild(AMcreateElementMathML("mo").appendChild(document.createTextNode(h)),l[0].childNodes[e])}}}}b=AMcreateMmlNode(c.tag,l[0]);b.setAttribute(c.atname,c.atval);return[b,l[1]]}}case BINARY:g=AMremoveCharsAndBlanks(g,c.input.length);l=AMparseSexpr(g);if(l[0]==null){return[AMcreateMmlNode("mo",document.createTextNode(c.input)),g]}AMremoveBrackets(l[0]);var a=AMparseSexpr(l[1]);if(a[0]==null){return[AMcreateMmlNode("mo",document.createTextNode(c.input)),g]}AMremoveBrackets(a[0]);if(c.input=="root"||c.input=="stackrel"){f.appendChild(a[0])}f.appendChild(l[0]);if(c.input=="frac"){f.appendChild(a[0])}return[AMcreateMmlNode(c.tag,f),a[1]];case INFIX:g=AMremoveCharsAndBlanks(g,c.input.length);return[AMcreateMmlNode("mo",document.createTextNode(c.output)),g];case SPACE:g=AMremoveCharsAndBlanks(g,c.input.length);b=AMcreateElementMathML("mspace");b.setAttribute("width","1ex");f.appendChild(b);f.appendChild(AMcreateMmlNode(c.tag,document.createTextNode(c.output)));b=AMcreateElementMathML("mspace");b.setAttribute("width","1ex");f.appendChild(b);return[AMcreateMmlNode("mrow",f),g];case LEFTRIGHT:AMnestingDepth++;g=AMremoveCharsAndBlanks(g,c.input.length);l=AMparseExpr(g,false);AMnestingDepth--;var k="";if(l[0].lastChild!=null){k=l[0].lastChild.firstChild.nodeValue}if(k=="|"){b=AMcreateMmlNode("mo",document.createTextNode(c.output));b=AMcreateMmlNode("mrow",b);b.appendChild(l[0]);return[b,l[1]]}else{b=AMcreateMmlNode("mo",document.createTextNode(c.output));b=AMcreateMmlNode("mrow",b);return[b,g]}default:g=AMremoveCharsAndBlanks(g,c.input.length);return[AMcreateMmlNode(c.tag,document.createTextNode(c.output)),g]}}function AMparseIexpr(g){var e,h,f,d,a,c;g=AMremoveCharsAndBlanks(g,0);h=AMgetSymbol(g);a=AMparseSexpr(g);d=a[0];g=a[1];e=AMgetSymbol(g);if(e.ttype==INFIX&&e.input!="/"){g=AMremoveCharsAndBlanks(g,e.input.length);a=AMparseSexpr(g);if(a[0]==null){a[0]=AMcreateMmlNode("mo",document.createTextNode("\u25A1"))}else{AMremoveBrackets(a[0])}g=a[1];if(e.input=="_"){f=AMgetSymbol(g);c=(h.ttype==UNDEROVER);if(f.input=="^"){g=AMremoveCharsAndBlanks(g,f.input.length);var b=AMparseSexpr(g);AMremoveBrackets(b[0]);g=b[1];d=AMcreateMmlNode((c?"munderover":"msubsup"),d);d.appendChild(a[0]);d.appendChild(b[0]);d=AMcreateMmlNode("mrow",d)}else{d=AMcreateMmlNode((c?"munder":"msub"),d);d.appendChild(a[0])}}else{d=AMcreateMmlNode(e.tag,d);d.appendChild(a[0])}}return[d,g]}function AMparseExpr(l,h){var r,o,e,u,y=[],f=document.createDocumentFragment();do{l=AMremoveCharsAndBlanks(l,0);e=AMparseIexpr(l);o=e[0];l=e[1];r=AMgetSymbol(l);if(r.ttype==INFIX&&r.input=="/"){l=AMremoveCharsAndBlanks(l,r.input.length);e=AMparseIexpr(l);if(e[0]==null){e[0]=AMcreateMmlNode("mo",document.createTextNode("\u25A1"))}else{AMremoveBrackets(e[0])}l=e[1];AMremoveBrackets(o);o=AMcreateMmlNode(r.tag,o);o.appendChild(e[0]);f.appendChild(o);r=AMgetSymbol(l)}else{if(o!=undefined){f.appendChild(o)}}}while((r.ttype!=RIGHTBRACKET&&(r.ttype!=LEFTRIGHT||h)||AMnestingDepth==0)&&r!=null&&r.output!="");if(r.ttype==RIGHTBRACKET||r.ttype==LEFTRIGHT){var v=f.childNodes.length;if(v>0&&f.childNodes[v-1].nodeName=="mrow"&&v>1&&f.childNodes[v-2].nodeName=="mo"&&f.childNodes[v-2].firstChild.nodeValue==","){var x=f.childNodes[v-1].lastChild.firstChild.nodeValue;if(x==")"||x=="]"){var b=f.childNodes[v-1].firstChild.firstChild.nodeValue;if(b=="("&&x==")"&&r.output!="}"||b=="["&&x=="]"){var c=[];var s=true;var p=f.childNodes.length;for(u=0;s&&u1){s=c[u].length==c[u-2].length}}if(s){var d,a,g,q,w=document.createDocumentFragment();for(u=0;u2){f.removeChild(f.firstChild);f.removeChild(f.firstChild)}w.appendChild(AMcreateMmlNode("mtr",d))}o=AMcreateMmlNode("mtable",w);if(typeof r.invisible=="boolean"&&r.invisible){o.setAttribute("columnalign","left")}f.replaceChild(o,f.firstChild)}}}}l=AMremoveCharsAndBlanks(l,r.input.length);if(typeof r.invisible!="boolean"||!r.invisible){o=AMcreateMmlNode("mo",document.createTextNode(r.output));f.appendChild(o)}}return[f,l]}function AMparseMath(d){var a,c=AMcreateElementMathML("mstyle");if(mathcolor!=""){c.setAttribute("mathcolor",mathcolor)}if(displaystyle){c.setAttribute("displaystyle","true")}if(mathfontfamily!=""){c.setAttribute("fontfamily",mathfontfamily)}AMnestingDepth=0;c.appendChild(AMparseExpr(d.replace(/^\s+/g,""),false)[0]);c=AMcreateMmlNode("math",c);if(showasciiformulaonhover){c.setAttribute("title",d.replace(/\s+/g," "))}if(mathfontfamily!=""&&(isIE||mathfontfamily!="serif")){var b=AMcreateElementXHTML("font");b.setAttribute("face",mathfontfamily);b.appendChild(c);return b}return c}function AMstrarr2docFrag(a,d){var g=document.createDocumentFragment();var f=false;for(var e=0;e1||j){if(checkForMathML){checkForMathML=false;var c=AMisMathMLavailable();AMnoMathML=c!=null;if(AMnoMathML&¬ifyIfNoMathML){if(alertIfNoMathML){alert("To view the ASCIIMathML notation use Internet Explorer 6 +\nMathPlayer (free from www.dessci.com)\n or Firefox/Mozilla/Netscape")}else{AMbody.insertBefore(c,AMbody.childNodes[0])}}}if(!AMnoMathML){a=AMstrarr2docFrag(e,b.nodeType==8);var f=a.childNodes.length;b.parentNode.replaceChild(a,b);return f-1}else{return 0}}}}else{return 0}}else{if(b.nodeName!="math"){for(d=0;d');document.write('')}function generic(){translate()}if(typeof window.addEventListener!="undefined"){window.addEventListener("load",generic,false)}else{if(typeof document.addEventListener!="undefined"){document.addEventListener("load",generic,false)}else{if(typeof window.attachEvent!="undefined"){window.attachEvent("onload",generic)}else{if(typeof window.onload=="function"){var existing=onload;window.onload=function(){existing();generic()}}else{window.onload=generic}}}}; \ No newline at end of file +/* +ASCIIMathML.js +============== +This file contains JavaScript functions to convert ASCII math notation +and LaTeX to Presentation MathML. Simple graphics commands are also +translated to SVG images. The conversion is done while the (X)HTML +page loads, and should work with Firefox/Mozilla/Netscape 7+ and Internet +Explorer 6/7 + MathPlayer (http://www.dessci.com/en/products/mathplayer/) + +Adobe SVGview 3.03 (http://www.adobe.com/svg/viewer/install/). + +Just add the next line to your (X)HTML page with this file in the same folder: + + + +(using the graphics in IE also requires the file "d.svg" in the same folder). +This is a convenient and inexpensive solution for authoring MathML and SVG. + +Version 2.1 Oct 8, 2008, (c) Peter Jipsen http://www.chapman.edu/~jipsen +This version extends ASCIIMathML.js with LaTeXMathML.js and ASCIIsvg.js. +Latest version at http://www.chapman.edu/~jipsen/mathml/ASCIIMathML.js +If you use it on a webpage, please send the URL to jipsen@chapman.edu + +The LaTeXMathML modifications were made by Douglas Woodall, June 2006. +(for details see header on the LaTeXMathML part in middle of file) +Extensive clean-up and improvements by Paulo Soares, Oct 2007. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or (at +your option) any later version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +(at http://www.gnu.org/licences/lgpl.html) for more details. +*/ + +var mathcolor = "blue"; // change it to "" (to inherit) or another color +var mathfontsize = "1em"; // change to e.g. 1.2em for larger math +var mathfontfamily = "serif"; // change to "" to inherit (works in IE) + // or another family (e.g. "arial") +var automathrecognize = false; // writing "amath" on page makes this true +var checkForMathML = true; // check if browser can display MathML +var notifyIfNoMathML = true; // display note at top if no MathML capability +var alertIfNoMathML = false; // show alert box if no MathML capability +var translateOnLoad = true; // set to false to do call translators from js +var translateLaTeX = true; // false to preserve $..$, $$..$$ +var translateLaTeXformatting = true; // false to preserve \emph,\begin{},\end{} +var translateASCIIMath = true; // false to preserve `..` +var translateASCIIsvg = true; // false to preserve agraph.., \begin{graph}.. +var avoidinnerHTML = false; // set true if assigning to innerHTML gives error +var displaystyle = true; // puts limits above and below large operators +var showasciiformulaonhover = true; // helps students learn ASCIIMath +var decimalsign = "."; // change to "," if you like, beware of `(1,2)`! +var AMdelimiter1 = "`", AMescape1 = "\\\\`"; // can use other characters +var AMdocumentId = "wikitext" // PmWiki element containing math (default=body) +var checkforprocessasciimathinmoodle = false; // true for systems like Moodle +var dsvglocation = ""; // path to d.svg (blank if same as ASCIIMathML.js loc) + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ + +var isIE = document.createElementNS==null; +var noMathML = false, translated = false; + +if (isIE) { // avoid adding MathPlayer info explicitly to each webpage + document.write(""); + document.write(""); +} + +// Add a stylesheet, replacing any previous custom stylesheet (adapted from TW) +function setStylesheet(s) { + var id = "AMMLcustomStyleSheet"; + var n = document.getElementById(id); + if(document.createStyleSheet) { + // Test for IE's non-standard createStyleSheet method + if(n) + n.parentNode.removeChild(n); + // This failed without the   + document.getElementsByTagName("head")[0].insertAdjacentHTML("beforeEnd"," "); + } else { + if(n) { + n.replaceChild(document.createTextNode(s),n.firstChild); + } else { + n = document.createElement("style"); + n.type = "text/css"; + n.id = id; + n.appendChild(document.createTextNode(s)); + document.getElementsByTagName("head")[0].appendChild(n); + } + } +} + +setStylesheet("#AMMLcloseDiv \{font-size:0.8em; padding-top:1em; color:#014\}\n#AMMLwarningBox \{position:absolute; width:100%; top:0; left:0; z-index:200; text-align:center; font-size:1em; font-weight:bold; padding:0.5em 0 0.5em 0; color:#ffc; background:#c30\}"); + +function init(){ + var msg, warnings = new Array(); + if (document.getElementById==null){ + alert("This webpage requires a recent browser such as Mozilla Firefox/Netscape 7+ or Internet Explorer 6+ with MathPlayer and Adobe SVGviewer"); + return null; + } + if (checkForMathML && (msg = checkMathML())) warnings.push(msg); + if (checkIfSVGavailable && (msg = checkSVG())) warnings.push(msg); + if (warnings.length>0) displayWarnings(warnings); + if (!noMathML) initSymbols(); + return true; +} + +function checkMathML(){ + if (navigator.appName.slice(0,8)=="Netscape") + if (navigator.appVersion.slice(0,1)>="5") noMathML = null; + else noMathML = true; + else if (navigator.appName.slice(0,9)=="Microsoft") + try { + var ActiveX = new ActiveXObject("MathPlayer.Factory.1"); + noMathML = null; + } catch (e) { + noMathML = true; + } + else if (navigator.appName.slice(0,5)=="Opera") + if (navigator.appVersion.slice(0,3)>="9.5") noMathML = null; + else noMathML = true; +//noMathML = true; //uncomment to check + if (noMathML && notifyIfNoMathML) { + var msg = "To view the ASCIIMathML notation use Internet Explorer + MathPlayer or Mozilla Firefox 2.0 or later."; + if (alertIfNoMathML) + alert(msg); + else return msg; + } +} + +function hideWarning(){ + var body = document.getElementsByTagName("body")[0]; + body.removeChild(document.getElementById('AMMLwarningBox')); + body.onclick = null; +} + +function displayWarnings(warnings) { + var i, frag, nd = createElementXHTML("div"); + var body = document.getElementsByTagName("body")[0]; + body.onclick=hideWarning; + nd.id = 'AMMLwarningBox'; + for (i=0; i=", tag:"mo", output:"\u2265", tex:"ge", ttype:CONST}, +{input:"geq", tag:"mo", output:"\u2265", tex:null, ttype:CONST}, +{input:"-<", tag:"mo", output:"\u227A", tex:"prec", ttype:CONST}, +{input:"-lt", tag:"mo", output:"\u227A", tex:null, ttype:CONST}, +{input:">-", tag:"mo", output:"\u227B", tex:"succ", ttype:CONST}, +{input:"-<=", tag:"mo", output:"\u2AAF", tex:"preceq", ttype:CONST}, +{input:">-=", tag:"mo", output:"\u2AB0", tex:"succeq", ttype:CONST}, +{input:"in", tag:"mo", output:"\u2208", tex:null, ttype:CONST}, +{input:"!in", tag:"mo", output:"\u2209", tex:"notin", ttype:CONST}, +{input:"sub", tag:"mo", output:"\u2282", tex:"subset", ttype:CONST}, +{input:"sup", tag:"mo", output:"\u2283", tex:"supset", ttype:CONST}, +{input:"sube", tag:"mo", output:"\u2286", tex:"subseteq", ttype:CONST}, +{input:"supe", tag:"mo", output:"\u2287", tex:"supseteq", ttype:CONST}, +{input:"-=", tag:"mo", output:"\u2261", tex:"equiv", ttype:CONST}, +{input:"~=", tag:"mo", output:"\u2245", tex:"cong", ttype:CONST}, +{input:"~~", tag:"mo", output:"\u2248", tex:"approx", ttype:CONST}, +{input:"prop", tag:"mo", output:"\u221D", tex:"propto", ttype:CONST}, + +//logical symbols +{input:"and", tag:"mtext", output:"and", tex:null, ttype:SPACE}, +{input:"or", tag:"mtext", output:"or", tex:null, ttype:SPACE}, +{input:"not", tag:"mo", output:"\u00AC", tex:"neg", ttype:CONST}, +{input:"=>", tag:"mo", output:"\u21D2", tex:"implies", ttype:CONST}, +{input:"if", tag:"mo", output:"if", tex:null, ttype:SPACE}, +{input:"<=>", tag:"mo", output:"\u21D4", tex:"iff", ttype:CONST}, +{input:"AA", tag:"mo", output:"\u2200", tex:"forall", ttype:CONST}, +{input:"EE", tag:"mo", output:"\u2203", tex:"exists", ttype:CONST}, +{input:"_|_", tag:"mo", output:"\u22A5", tex:"bot", ttype:CONST}, +{input:"TT", tag:"mo", output:"\u22A4", tex:"top", ttype:CONST}, +{input:"|--", tag:"mo", output:"\u22A2", tex:"vdash", ttype:CONST}, +{input:"|==", tag:"mo", output:"\u22A8", tex:"models", ttype:CONST}, + +//grouping brackets +{input:"(", tag:"mo", output:"(", tex:null, ttype:LEFTBRACKET}, +{input:")", tag:"mo", output:")", tex:null, ttype:RIGHTBRACKET}, +{input:"[", tag:"mo", output:"[", tex:null, ttype:LEFTBRACKET}, +{input:"]", tag:"mo", output:"]", tex:null, ttype:RIGHTBRACKET}, +{input:"{", tag:"mo", output:"{", tex:null, ttype:LEFTBRACKET}, +{input:"}", tag:"mo", output:"}", tex:null, ttype:RIGHTBRACKET}, +{input:"|", tag:"mo", output:"|", tex:null, ttype:LEFTRIGHT}, +//{input:"||", tag:"mo", output:"||", tex:null, ttype:LEFTRIGHT}, +{input:"(:", tag:"mo", output:"\u2329", tex:"langle", ttype:LEFTBRACKET}, +{input:":)", tag:"mo", output:"\u232A", tex:"rangle", ttype:RIGHTBRACKET}, +{input:"<<", tag:"mo", output:"\u2329", tex:null, ttype:LEFTBRACKET}, +{input:">>", tag:"mo", output:"\u232A", tex:null, ttype:RIGHTBRACKET}, +{input:"{:", tag:"mo", output:"{:", tex:null, ttype:LEFTBRACKET, invisible:true}, +{input:":}", tag:"mo", output:":}", tex:null, ttype:RIGHTBRACKET, invisible:true}, + +//miscellaneous symbols +{input:"int", tag:"mo", output:"\u222B", tex:null, ttype:CONST}, +{input:"dx", tag:"mi", output:"{:d x:}", tex:null, ttype:DEFINITION}, +{input:"dy", tag:"mi", output:"{:d y:}", tex:null, ttype:DEFINITION}, +{input:"dz", tag:"mi", output:"{:d z:}", tex:null, ttype:DEFINITION}, +{input:"dt", tag:"mi", output:"{:d t:}", tex:null, ttype:DEFINITION}, +{input:"oint", tag:"mo", output:"\u222E", tex:null, ttype:CONST}, +{input:"del", tag:"mo", output:"\u2202", tex:"partial", ttype:CONST}, +{input:"grad", tag:"mo", output:"\u2207", tex:"nabla", ttype:CONST}, +{input:"+-", tag:"mo", output:"\u00B1", tex:"pm", ttype:CONST}, +{input:"O/", tag:"mo", output:"\u2205", tex:"emptyset", ttype:CONST}, +{input:"oo", tag:"mo", output:"\u221E", tex:"infty", ttype:CONST}, +{input:"aleph", tag:"mo", output:"\u2135", tex:null, ttype:CONST}, +{input:"...", tag:"mo", output:"...", tex:"ldots", ttype:CONST}, +{input:":.", tag:"mo", output:"\u2234", tex:"therefore", ttype:CONST}, +{input:"/_", tag:"mo", output:"\u2220", tex:"angle", ttype:CONST}, +{input:"\\ ", tag:"mo", output:"\u00A0", tex:null, ttype:CONST}, +{input:"quad", tag:"mo", output:"\u00A0\u00A0", tex:null, ttype:CONST}, +{input:"qquad", tag:"mo", output:"\u00A0\u00A0\u00A0\u00A0", tex:null, ttype:CONST}, +{input:"cdots", tag:"mo", output:"\u22EF", tex:null, ttype:CONST}, +{input:"vdots", tag:"mo", output:"\u22EE", tex:null, ttype:CONST}, +{input:"ddots", tag:"mo", output:"\u22F1", tex:null, ttype:CONST}, +{input:"diamond", tag:"mo", output:"\u22C4", tex:null, ttype:CONST}, +{input:"square", tag:"mo", output:"\u25A1", tex:null, ttype:CONST}, +{input:"|__", tag:"mo", output:"\u230A", tex:"lfloor", ttype:CONST}, +{input:"__|", tag:"mo", output:"\u230B", tex:"rfloor", ttype:CONST}, +{input:"|~", tag:"mo", output:"\u2308", tex:"lceiling", ttype:CONST}, +{input:"~|", tag:"mo", output:"\u2309", tex:"rceiling", ttype:CONST}, +{input:"CC", tag:"mo", output:"\u2102", tex:null, ttype:CONST}, +{input:"NN", tag:"mo", output:"\u2115", tex:null, ttype:CONST}, +{input:"QQ", tag:"mo", output:"\u211A", tex:null, ttype:CONST}, +{input:"RR", tag:"mo", output:"\u211D", tex:null, ttype:CONST}, +{input:"ZZ", tag:"mo", output:"\u2124", tex:null, ttype:CONST}, +{input:"f", tag:"mi", output:"f", tex:null, ttype:UNARY, func:true}, +{input:"g", tag:"mi", output:"g", tex:null, ttype:UNARY, func:true}, + +//standard functions +{input:"lim", tag:"mo", output:"lim", tex:null, ttype:UNDEROVER}, +{input:"Lim", tag:"mo", output:"Lim", tex:null, ttype:UNDEROVER}, +{input:"sin", tag:"mo", output:"sin", tex:null, ttype:UNARY, func:true}, +{input:"cos", tag:"mo", output:"cos", tex:null, ttype:UNARY, func:true}, +{input:"tan", tag:"mo", output:"tan", tex:null, ttype:UNARY, func:true}, +{input:"sinh", tag:"mo", output:"sinh", tex:null, ttype:UNARY, func:true}, +{input:"cosh", tag:"mo", output:"cosh", tex:null, ttype:UNARY, func:true}, +{input:"tanh", tag:"mo", output:"tanh", tex:null, ttype:UNARY, func:true}, +{input:"cot", tag:"mo", output:"cot", tex:null, ttype:UNARY, func:true}, +{input:"sec", tag:"mo", output:"sec", tex:null, ttype:UNARY, func:true}, +{input:"csc", tag:"mo", output:"csc", tex:null, ttype:UNARY, func:true}, +{input:"log", tag:"mo", output:"log", tex:null, ttype:UNARY, func:true}, +{input:"ln", tag:"mo", output:"ln", tex:null, ttype:UNARY, func:true}, +{input:"det", tag:"mo", output:"det", tex:null, ttype:UNARY, func:true}, +{input:"dim", tag:"mo", output:"dim", tex:null, ttype:CONST}, +{input:"mod", tag:"mo", output:"mod", tex:null, ttype:CONST}, +{input:"gcd", tag:"mo", output:"gcd", tex:null, ttype:UNARY, func:true}, +{input:"lcm", tag:"mo", output:"lcm", tex:null, ttype:UNARY, func:true}, +{input:"lub", tag:"mo", output:"lub", tex:null, ttype:CONST}, +{input:"glb", tag:"mo", output:"glb", tex:null, ttype:CONST}, +{input:"min", tag:"mo", output:"min", tex:null, ttype:UNDEROVER}, +{input:"max", tag:"mo", output:"max", tex:null, ttype:UNDEROVER}, + +//arrows +{input:"uarr", tag:"mo", output:"\u2191", tex:"uparrow", ttype:CONST}, +{input:"darr", tag:"mo", output:"\u2193", tex:"downarrow", ttype:CONST}, +{input:"rarr", tag:"mo", output:"\u2192", tex:"rightarrow", ttype:CONST}, +{input:"->", tag:"mo", output:"\u2192", tex:"to", ttype:CONST}, +{input:">->", tag:"mo", output:"\u21A3", tex:"rightarrowtail", ttype:CONST}, +{input:"->>", tag:"mo", output:"\u21A0", tex:"twoheadrightarrow", ttype:CONST}, +{input:">->>", tag:"mo", output:"\u2916", tex:"twoheadrightarrowtail", ttype:CONST}, +{input:"|->", tag:"mo", output:"\u21A6", tex:"mapsto", ttype:CONST}, +{input:"larr", tag:"mo", output:"\u2190", tex:"leftarrow", ttype:CONST}, +{input:"harr", tag:"mo", output:"\u2194", tex:"leftrightarrow", ttype:CONST}, +{input:"rArr", tag:"mo", output:"\u21D2", tex:"Rightarrow", ttype:CONST}, +{input:"lArr", tag:"mo", output:"\u21D0", tex:"Leftarrow", ttype:CONST}, +{input:"hArr", tag:"mo", output:"\u21D4", tex:"Leftrightarrow", ttype:CONST}, +//commands with argument +{input:"sqrt", tag:"msqrt", output:"sqrt", tex:null, ttype:UNARY}, +{input:"root", tag:"mroot", output:"root", tex:null, ttype:BINARY}, +{input:"frac", tag:"mfrac", output:"/", tex:null, ttype:BINARY}, +{input:"/", tag:"mfrac", output:"/", tex:null, ttype:INFIX}, +{input:"stackrel", tag:"mover", output:"stackrel", tex:null, ttype:BINARY}, +{input:"_", tag:"msub", output:"_", tex:null, ttype:INFIX}, +{input:"^", tag:"msup", output:"^", tex:null, ttype:INFIX}, +{input:"hat", tag:"mover", output:"\u005E", tex:null, ttype:UNARY, acc:true}, +{input:"bar", tag:"mover", output:"\u00AF", tex:"overline", ttype:UNARY, acc:true}, +{input:"vec", tag:"mover", output:"\u2192", tex:null, ttype:UNARY, acc:true}, +{input:"dot", tag:"mover", output:".", tex:null, ttype:UNARY, acc:true}, +{input:"ddot", tag:"mover", output:"..", tex:null, ttype:UNARY, acc:true}, +{input:"ul", tag:"munder", output:"\u0332", tex:"underline", ttype:UNARY, acc:true}, +{input:"text", tag:"mtext", output:"text", tex:null, ttype:TEXT}, +{input:"mbox", tag:"mtext", output:"mbox", tex:null, ttype:TEXT}, +AMquote, +{input:"bb", tag:"mstyle", atname:"fontweight", atval:"bold", output:"bb", tex:null, ttype:UNARY}, +{input:"mathbf", tag:"mstyle", atname:"fontweight", atval:"bold", output:"mathbf", tex:null, ttype:UNARY}, +{input:"sf", tag:"mstyle", atname:"fontfamily", atval:"sans-serif", output:"sf", tex:null, ttype:UNARY}, +{input:"mathsf", tag:"mstyle", atname:"fontfamily", atval:"sans-serif", output:"mathsf", tex:null, ttype:UNARY}, +{input:"bbb", tag:"mstyle", atname:"mathvariant", atval:"double-struck", output:"bbb", tex:null, ttype:UNARY, codes:AMbbb}, +{input:"mathbb", tag:"mstyle", atname:"mathvariant", atval:"double-struck", output:"mathbb", tex:null, ttype:UNARY, codes:AMbbb}, +{input:"cc", tag:"mstyle", atname:"mathvariant", atval:"script", output:"cc", tex:null, ttype:UNARY, codes:AMcal}, +{input:"mathcal", tag:"mstyle", atname:"mathvariant", atval:"script", output:"mathcal", tex:null, ttype:UNARY, codes:AMcal}, +{input:"tt", tag:"mstyle", atname:"fontfamily", atval:"monospace", output:"tt", tex:null, ttype:UNARY}, +{input:"mathtt", tag:"mstyle", atname:"fontfamily", atval:"monospace", output:"mathtt", tex:null, ttype:UNARY}, +{input:"fr", tag:"mstyle", atname:"mathvariant", atval:"fraktur", output:"fr", tex:null, ttype:UNARY, codes:AMfrk}, +{input:"mathfrak", tag:"mstyle", atname:"mathvariant", atval:"fraktur", output:"mathfrak", tex:null, ttype:UNARY, codes:AMfrk} +]; + +function compareNames(s1,s2) { + if (s1.input > s2.input) return 1 + else return -1; +} + +var AMnames = []; //list of input symbols + +function initSymbols() { + var texsymbols = [], i; + for (i=0; i=n where str appears or would be inserted +// assumes arr is sorted + if (n==0) { + var h,m; + n = -1; + h = arr.length; + while (n+1> 1; + if (arr[m]=str +} + +function AMgetSymbol(str) { +//return maximal initial substring of str that appears in names +//return null if there is none + var k = 0; //new pos + var j = 0; //old pos + var mk; //match pos + var st; + var tagst; + var match = ""; + var more = true; + for (var i=1; i<=str.length && more; i++) { + st = str.slice(0,i); //initial substring of length i + j = k; + k = position(AMnames, st, j); + if (k=AMnames[k]; + } + AMpreviousSymbol=AMcurrentSymbol; + if (match!=""){ + AMcurrentSymbol=AMsymbols[mk].ttype; + return AMsymbols[mk]; + } +// if str[0] is a digit or - return maxsubstring of digits.digits + AMcurrentSymbol=CONST; + k = 1; + st = str.slice(0,1); + var integ = true; + while ("0"<=st && st<="9" && k<=str.length) { + st = str.slice(k,k+1); + k++; + } + if (st == decimalsign) { + st = str.slice(k,k+1); + if ("0"<=st && st<="9") { + integ = false; + k++; + while ("0"<=st && st<="9" && k<=str.length) { + st = str.slice(k,k+1); + k++; + } + } + } + if ((integ && k>1) || k>2) { + st = str.slice(0,k-1); + tagst = "mn"; + } else { + k = 2; + st = str.slice(0,1); //take 1 character + tagst = (("A">st || st>"Z") && ("a">st || st>"z")?"mo":"mi"); + } + if (st=="-" && AMpreviousSymbol==INFIX) { + AMcurrentSymbol = INFIX; //trick "/" into recognizing "-" on second parse + return {input:st, tag:tagst, output:st, ttype:UNARY, func:true}; + } + return {input:st, tag:tagst, output:st, ttype:CONST}; +} + +function AMremoveBrackets(node) { + var st; + if (node.nodeName=="mrow") { + st = node.firstChild.firstChild.nodeValue; + if (st=="(" || st=="[" || st=="{") node.removeChild(node.firstChild); + } + if (node.nodeName=="mrow") { + st = node.lastChild.firstChild.nodeValue; + if (st==")" || st=="]" || st=="}") node.removeChild(node.lastChild); + } +} + +/*Parsing ASCII math expressions with the following grammar +v ::= [A-Za-z] | greek letters | numbers | other constant symbols +u ::= sqrt | text | bb | other unary symbols for font commands +b ::= frac | root | stackrel binary symbols +l ::= ( | [ | { | (: | {: left brackets +r ::= ) | ] | } | :) | :} right brackets +S ::= v | lEr | uS | bSS Simple expression +I ::= S_S | S^S | S_S^S | S Intermediate expression +E ::= IE | I/I Expression +Each terminal symbol is translated into a corresponding mathml node.*/ + +var AMnestingDepth,AMpreviousSymbol,AMcurrentSymbol; + +function AMparseSexpr(str) { //parses str and returns [node,tailstr] + var symbol, node, result, i, st,// rightvert = false, + newFrag = document.createDocumentFragment(); + str = AMremoveCharsAndBlanks(str,0); + symbol = AMgetSymbol(str); //either a token or a bracket or empty + if (symbol == null || symbol.ttype == RIGHTBRACKET && AMnestingDepth > 0) { + return [null,str]; + } + if (symbol.ttype == DEFINITION) { + str = symbol.output+AMremoveCharsAndBlanks(str,symbol.input.length); + symbol = AMgetSymbol(str); + } + switch (symbol.ttype) { case UNDEROVER: + case CONST: + str = AMremoveCharsAndBlanks(str,symbol.input.length); + return [createMmlNode(symbol.tag, //its a constant + document.createTextNode(symbol.output)),str]; + case LEFTBRACKET: //read (expr+) + AMnestingDepth++; + str = AMremoveCharsAndBlanks(str,symbol.input.length); + result = AMparseExpr(str,true); + AMnestingDepth--; + if (typeof symbol.invisible == "boolean" && symbol.invisible) + node = createMmlNode("mrow",result[0]); + else { + node = createMmlNode("mo",document.createTextNode(symbol.output)); + node = createMmlNode("mrow",node); + node.appendChild(result[0]); + } + return [node,result[1]]; + case TEXT: + if (symbol!=AMquote) str = AMremoveCharsAndBlanks(str,symbol.input.length); + if (str.charAt(0)=="{") i=str.indexOf("}"); + else if (str.charAt(0)=="(") i=str.indexOf(")"); + else if (str.charAt(0)=="[") i=str.indexOf("]"); + else if (symbol==AMquote) i=str.slice(1).indexOf("\"")+1; + else i = 0; + if (i==-1) i = str.length; + st = str.slice(1,i); + if (st.charAt(0) == " ") { + node = createMmlNode("mspace"); + node.setAttribute("width","1ex"); + newFrag.appendChild(node); + } + newFrag.appendChild( + createMmlNode(symbol.tag,document.createTextNode(st))); + if (st.charAt(st.length-1) == " ") { + node = createMmlNode("mspace"); + node.setAttribute("width","1ex"); + newFrag.appendChild(node); + } + str = AMremoveCharsAndBlanks(str,i+1); + return [createMmlNode("mrow",newFrag),str]; + case UNARY: + str = AMremoveCharsAndBlanks(str,symbol.input.length); + result = AMparseSexpr(str); + if (result[0]==null) return [createMmlNode(symbol.tag, + document.createTextNode(symbol.output)),str]; + if (typeof symbol.func == "boolean" && symbol.func) { // functions hack + st = str.charAt(0); + if (st=="^" || st=="_" || st=="/" || st=="|" || st==",") { + return [createMmlNode(symbol.tag, + document.createTextNode(symbol.output)),str]; + } else { + node = createMmlNode("mrow", + createMmlNode(symbol.tag,document.createTextNode(symbol.output))); + node.appendChild(result[0]); + return [node,result[1]]; + } + } + AMremoveBrackets(result[0]); + if (symbol.input == "sqrt") { // sqrt + return [createMmlNode(symbol.tag,result[0]),result[1]]; + } else if (typeof symbol.acc == "boolean" && symbol.acc) { // accent + node = createMmlNode(symbol.tag,result[0]); + node.appendChild(createMmlNode("mo",document.createTextNode(symbol.output))); + return [node,result[1]]; + } else { // font change command + if (!isIE && typeof symbol.codes != "undefined") { + for (i=0; i64 && st.charCodeAt(j)<91) newst = newst + + String.fromCharCode(symbol.codes[st.charCodeAt(j)-65]); + else newst = newst + st.charAt(j); + if (result[0].nodeName=="mi") + result[0]=createMmlNode("mo"). + appendChild(document.createTextNode(newst)); + else result[0].replaceChild(createMmlNode("mo"). + appendChild(document.createTextNode(newst)), + result[0].childNodes[i]); + } + } + node = createMmlNode(symbol.tag,result[0]); + node.setAttribute(symbol.atname,symbol.atval); + return [node,result[1]]; + } + case BINARY: + str = AMremoveCharsAndBlanks(str,symbol.input.length); + result = AMparseSexpr(str); + if (result[0]==null) return [createMmlNode("mo", + document.createTextNode(symbol.input)),str]; + AMremoveBrackets(result[0]); + var result2 = AMparseSexpr(result[1]); + if (result2[0]==null) return [createMmlNode("mo", + document.createTextNode(symbol.input)),str]; + AMremoveBrackets(result2[0]); + if (symbol.input=="root" || symbol.input=="stackrel") + newFrag.appendChild(result2[0]); + newFrag.appendChild(result[0]); + if (symbol.input=="frac") newFrag.appendChild(result2[0]); + return [createMmlNode(symbol.tag,newFrag),result2[1]]; + case INFIX: + str = AMremoveCharsAndBlanks(str,symbol.input.length); + return [createMmlNode("mo",document.createTextNode(symbol.output)),str]; + case SPACE: + str = AMremoveCharsAndBlanks(str,symbol.input.length); + node = createMmlNode("mspace"); + node.setAttribute("width","1ex"); + newFrag.appendChild(node); + newFrag.appendChild( + createMmlNode(symbol.tag,document.createTextNode(symbol.output))); + node = createMmlNode("mspace"); + node.setAttribute("width","1ex"); + newFrag.appendChild(node); + return [createMmlNode("mrow",newFrag),str]; + case LEFTRIGHT: +// if (rightvert) return [null,str]; else rightvert = true; + AMnestingDepth++; + str = AMremoveCharsAndBlanks(str,symbol.input.length); + result = AMparseExpr(str,false); + AMnestingDepth--; + var st = ""; + if (result[0].lastChild!=null) + st = result[0].lastChild.firstChild.nodeValue; + if (st == "|") { // its an absolute value subterm + node = createMmlNode("mo",document.createTextNode(symbol.output)); + node = createMmlNode("mrow",node); + node.appendChild(result[0]); + return [node,result[1]]; + } else { // the "|" is a \mid so use unicode 2223 (divides) for spacing + node = createMmlNode("mo",document.createTextNode("\u2223")); + node = createMmlNode("mrow",node); + return [node,str]; + } + default: +//alert("default"); + str = AMremoveCharsAndBlanks(str,symbol.input.length); + return [createMmlNode(symbol.tag, //its a constant + document.createTextNode(symbol.output)),str]; + } +} + +function AMparseIexpr(str) { + var symbol, sym1, sym2, node, result, underover; + str = AMremoveCharsAndBlanks(str,0); + sym1 = AMgetSymbol(str); + result = AMparseSexpr(str); + node = result[0]; + str = result[1]; + symbol = AMgetSymbol(str); + if (symbol.ttype == INFIX && symbol.input != "/") { + str = AMremoveCharsAndBlanks(str,symbol.input.length); +// if (symbol.input == "/") result = AMparseIexpr(str); else ... + result = AMparseSexpr(str); + if (result[0] == null) // show box in place of missing argument + result[0] = createMmlNode("mo",document.createTextNode("\u25A1")); + else AMremoveBrackets(result[0]); + str = result[1]; +// if (symbol.input == "/") AMremoveBrackets(node); + if (symbol.input == "_") { + sym2 = AMgetSymbol(str); + underover = (sym1.ttype == UNDEROVER); + if (sym2.input == "^") { + str = AMremoveCharsAndBlanks(str,sym2.input.length); + var res2 = AMparseSexpr(str); + AMremoveBrackets(res2[0]); + str = res2[1]; + node = createMmlNode((underover?"munderover":"msubsup"),node); + node.appendChild(result[0]); + node.appendChild(res2[0]); + node = createMmlNode("mrow",node); // so sum does not stretch + } else { + node = createMmlNode((underover?"munder":"msub"),node); + node.appendChild(result[0]); + } + } else { + node = createMmlNode(symbol.tag,node); + node.appendChild(result[0]); + } + } + return [node,str]; +} + +function AMparseExpr(str,rightbracket) { + var symbol, node, result, i, nodeList = [], + newFrag = document.createDocumentFragment(); + do { + str = AMremoveCharsAndBlanks(str,0); + result = AMparseIexpr(str); + node = result[0]; + str = result[1]; + symbol = AMgetSymbol(str); + if (symbol.ttype == INFIX && symbol.input == "/") { + str = AMremoveCharsAndBlanks(str,symbol.input.length); + result = AMparseIexpr(str); + if (result[0] == null) // show box in place of missing argument + result[0] = createMmlNode("mo",document.createTextNode("\u25A1")); + else AMremoveBrackets(result[0]); + str = result[1]; + AMremoveBrackets(node); + node = createMmlNode(symbol.tag,node); + node.appendChild(result[0]); + newFrag.appendChild(node); + symbol = AMgetSymbol(str); + } + else if (node!=undefined) newFrag.appendChild(node); + } while ((symbol.ttype != RIGHTBRACKET && + (symbol.ttype != LEFTRIGHT || rightbracket) + || AMnestingDepth == 0) && symbol!=null && symbol.output!=""); + if (symbol.ttype == RIGHTBRACKET || symbol.ttype == LEFTRIGHT) { +// if (AMnestingDepth > 0) AMnestingDepth--; + var len = newFrag.childNodes.length; + if (len>0 && newFrag.childNodes[len-1].nodeName == "mrow" && len>1 && + newFrag.childNodes[len-2].nodeName == "mo" && + newFrag.childNodes[len-2].firstChild.nodeValue == ",") { //matrix + var right = newFrag.childNodes[len-1].lastChild.firstChild.nodeValue; + if (right==")" || right=="]") { + var left = newFrag.childNodes[len-1].firstChild.firstChild.nodeValue; + if (left=="(" && right==")" && symbol.output != "}" || + left=="[" && right=="]") { + var pos = []; // positions of commas + var matrix = true; + var m = newFrag.childNodes.length; + for (i=0; matrix && i1) matrix = pos[i].length == pos[i-2].length; + } + if (matrix) { + var row, frag, n, k, table = document.createDocumentFragment(); + for (i=0; i(-,-,...,-,-) + n = node.childNodes.length; + k = 0; + node.removeChild(node.firstChild); //remove ( + for (j=1; j2) { + newFrag.removeChild(newFrag.firstChild); //remove ) + newFrag.removeChild(newFrag.firstChild); //remove , + } + table.appendChild(createMmlNode("mtr",row)); + } + node = createMmlNode("mtable",table); + if (typeof symbol.invisible == "boolean" && symbol.invisible) node.setAttribute("columnalign","left"); + newFrag.replaceChild(node,newFrag.firstChild); + } + } + } + } + str = AMremoveCharsAndBlanks(str,symbol.input.length); + if (typeof symbol.invisible != "boolean" || !symbol.invisible) { + node = createMmlNode("mo",document.createTextNode(symbol.output)); + newFrag.appendChild(node); + } + } + return [newFrag,str]; +} + +function parseMath(str,latex) { + var frag, node; + AMnestingDepth = 0; + frag = latex ? LMparseExpr(str.replace(/^\s+/g,""),false,false)[0] : AMparseExpr(str.replace(/^\s+/g,""),false)[0]; + node = createMmlNode("mstyle",frag); + node.setAttribute("mathcolor",mathcolor); + node.setAttribute("fontfamily",mathfontfamily); + node.setAttribute("mathsize",mathfontsize); + if (displaystyle) node.setAttribute("displaystyle","true"); + node = createMmlNode("math",node); + if (showasciiformulaonhover) //fixed by djhsu so newline + node.setAttribute("title",str.replace(/\s+/g," "));//does not show in Gecko + return node; +} + +function strarr2docFrag(arr, linebreaks, latex) { + var newFrag=document.createDocumentFragment(); + var expr = false; + for (var i=0; i,\\|!:;'~]|\\.(?!(?:\x20|$))|"+ambigAMtoken+englishAMtoken+simpleAMtoken; + var re = new RegExp("(^|\\s)((("+token+")\\s?)(("+token+secondenglishAMtoken+")\\s?)+)([,.?]?(?=\\s|$))","g"); + str = str.replace(re," `$2`$7"); + var arr = str.split(AMdelimiter1); + var re1 = new RegExp("(^|\\s)([b-zB-HJ-Z+*<>]|"+texcommand+ambigAMtoken+simpleAMtoken+")(\\s|\\n|$)","g"); + var re2 = new RegExp("(^|\\s)([a-z]|"+texcommand+ambigAMtoken+simpleAMtoken+")([,.])","g"); // removed |\d+ for now + for (i=0; i1 || mtch) { + if (!noMathML) { + frg = strarr2docFrag(arr,n.nodeType==8,latex); + var len = frg.childNodes.length; + n.parentNode.replaceChild(frg,n); + return len-1; + } else return 0; + } + } + } else return 0; + } else if (n.nodeName!="math") { + for (i=0; i +This is a convenient and inexpensive solution for authoring MathML. + +Version 1.4.7 Dec 15, 2005, (c) Peter Jipsen http://www.chapman.edu/~jipsen +Latest version at http://www.chapman.edu/~jipsen/mathml/ASCIIMathML.js +For changes see http://www.chapman.edu/~jipsen/mathml/asciimathchanges.txt +If you use it on a webpage, please send the URL to jipsen@chapman.edu + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or (at +your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +General Public License (at http://www.gnu.org/license/lgpl.html) +for more details. + +LaTeXMathML.js (ctd) +============== + +Content between $...$ and $$...$$ is converted by this part of the file +*/ + +// all further global variables start with "LM" + +// Commented out by DRW to prevent 1/2 turning into a 2-line fraction +// LMdiv = {input:"/", tag:"mfrac", output:"/", ttype:INFIX}, +// Commented out by DRW so that " prints literally in equations +// LMquote = {input:"\"", tag:"mtext", output:"mbox", ttype:TEXT}; + +var LMsymbols = [ +//Greek letters +{input:"\\alpha", tag:"mi", output:"\u03B1", ttype:CONST}, +{input:"\\beta", tag:"mi", output:"\u03B2", ttype:CONST}, +{input:"\\gamma", tag:"mi", output:"\u03B3", ttype:CONST}, +{input:"\\delta", tag:"mi", output:"\u03B4", ttype:CONST}, +{input:"\\epsilon", tag:"mi", output:"\u03B5", ttype:CONST}, +{input:"\\varepsilon", tag:"mi", output:"\u025B", ttype:CONST}, +{input:"\\zeta", tag:"mi", output:"\u03B6", ttype:CONST}, +{input:"\\eta", tag:"mi", output:"\u03B7", ttype:CONST}, +{input:"\\theta", tag:"mi", output:"\u03B8", ttype:CONST}, +{input:"\\vartheta", tag:"mi", output:"\u03D1", ttype:CONST}, +{input:"\\iota", tag:"mi", output:"\u03B9", ttype:CONST}, +{input:"\\kappa", tag:"mi", output:"\u03BA", ttype:CONST}, +{input:"\\lambda", tag:"mi", output:"\u03BB", ttype:CONST}, +{input:"\\mu", tag:"mi", output:"\u03BC", ttype:CONST}, +{input:"\\nu", tag:"mi", output:"\u03BD", ttype:CONST}, +{input:"\\xi", tag:"mi", output:"\u03BE", ttype:CONST}, +{input:"\\pi", tag:"mi", output:"\u03C0", ttype:CONST}, +{input:"\\varpi", tag:"mi", output:"\u03D6", ttype:CONST}, +{input:"\\rho", tag:"mi", output:"\u03C1", ttype:CONST}, +{input:"\\varrho", tag:"mi", output:"\u03F1", ttype:CONST}, +{input:"\\varsigma", tag:"mi", output:"\u03C2", ttype:CONST}, +{input:"\\sigma", tag:"mi", output:"\u03C3", ttype:CONST}, +{input:"\\tau", tag:"mi", output:"\u03C4", ttype:CONST}, +{input:"\\upsilon", tag:"mi", output:"\u03C5", ttype:CONST}, +{input:"\\phi", tag:"mi", output:"\u03C6", ttype:CONST}, +{input:"\\varphi", tag:"mi", output:"\u03D5", ttype:CONST}, +{input:"\\chi", tag:"mi", output:"\u03C7", ttype:CONST}, +{input:"\\psi", tag:"mi", output:"\u03C8", ttype:CONST}, +{input:"\\omega", tag:"mi", output:"\u03C9", ttype:CONST}, +{input:"\\Gamma", tag:"mo", output:"\u0393", ttype:CONST}, +{input:"\\Delta", tag:"mo", output:"\u0394", ttype:CONST}, +{input:"\\Theta", tag:"mo", output:"\u0398", ttype:CONST}, +{input:"\\Lambda", tag:"mo", output:"\u039B", ttype:CONST}, +{input:"\\Xi", tag:"mo", output:"\u039E", ttype:CONST}, +{input:"\\Pi", tag:"mo", output:"\u03A0", ttype:CONST}, +{input:"\\Sigma", tag:"mo", output:"\u03A3", ttype:CONST}, +{input:"\\Upsilon", tag:"mo", output:"\u03A5", ttype:CONST}, +{input:"\\Phi", tag:"mo", output:"\u03A6", ttype:CONST}, +{input:"\\Psi", tag:"mo", output:"\u03A8", ttype:CONST}, +{input:"\\Omega", tag:"mo", output:"\u03A9", ttype:CONST}, + +//fractions +{input:"\\frac12", tag:"mo", output:"\u00BD", ttype:CONST}, +{input:"\\frac14", tag:"mo", output:"\u00BC", ttype:CONST}, +{input:"\\frac34", tag:"mo", output:"\u00BE", ttype:CONST}, +{input:"\\frac13", tag:"mo", output:"\u2153", ttype:CONST}, +{input:"\\frac23", tag:"mo", output:"\u2154", ttype:CONST}, +{input:"\\frac15", tag:"mo", output:"\u2155", ttype:CONST}, +{input:"\\frac25", tag:"mo", output:"\u2156", ttype:CONST}, +{input:"\\frac35", tag:"mo", output:"\u2157", ttype:CONST}, +{input:"\\frac45", tag:"mo", output:"\u2158", ttype:CONST}, +{input:"\\frac16", tag:"mo", output:"\u2159", ttype:CONST}, +{input:"\\frac56", tag:"mo", output:"\u215A", ttype:CONST}, +{input:"\\frac18", tag:"mo", output:"\u215B", ttype:CONST}, +{input:"\\frac38", tag:"mo", output:"\u215C", ttype:CONST}, +{input:"\\frac58", tag:"mo", output:"\u215D", ttype:CONST}, +{input:"\\frac78", tag:"mo", output:"\u215E", ttype:CONST}, + +//binary operation symbols +{input:"\\pm", tag:"mo", output:"\u00B1", ttype:CONST}, +{input:"\\mp", tag:"mo", output:"\u2213", ttype:CONST}, +{input:"\\triangleleft",tag:"mo", output:"\u22B2", ttype:CONST}, +{input:"\\triangleright",tag:"mo",output:"\u22B3", ttype:CONST}, +{input:"\\cdot", tag:"mo", output:"\u22C5", ttype:CONST}, +{input:"\\star", tag:"mo", output:"\u22C6", ttype:CONST}, +{input:"\\ast", tag:"mo", output:"\u002A", ttype:CONST}, +{input:"\\times", tag:"mo", output:"\u00D7", ttype:CONST}, +{input:"\\div", tag:"mo", output:"\u00F7", ttype:CONST}, +{input:"\\circ", tag:"mo", output:"\u2218", ttype:CONST}, +//{input:"\\bullet", tag:"mo", output:"\u2219", ttype:CONST}, +{input:"\\bullet", tag:"mo", output:"\u2022", ttype:CONST}, +{input:"\\oplus", tag:"mo", output:"\u2295", ttype:CONST}, +{input:"\\ominus", tag:"mo", output:"\u2296", ttype:CONST}, +{input:"\\otimes", tag:"mo", output:"\u2297", ttype:CONST}, +{input:"\\bigcirc", tag:"mo", output:"\u25CB", ttype:CONST}, +{input:"\\oslash", tag:"mo", output:"\u2298", ttype:CONST}, +{input:"\\odot", tag:"mo", output:"\u2299", ttype:CONST}, +{input:"\\land", tag:"mo", output:"\u2227", ttype:CONST}, +{input:"\\wedge", tag:"mo", output:"\u2227", ttype:CONST}, +{input:"\\lor", tag:"mo", output:"\u2228", ttype:CONST}, +{input:"\\vee", tag:"mo", output:"\u2228", ttype:CONST}, +{input:"\\cap", tag:"mo", output:"\u2229", ttype:CONST}, +{input:"\\cup", tag:"mo", output:"\u222A", ttype:CONST}, +{input:"\\sqcap", tag:"mo", output:"\u2293", ttype:CONST}, +{input:"\\sqcup", tag:"mo", output:"\u2294", ttype:CONST}, +{input:"\\uplus", tag:"mo", output:"\u228E", ttype:CONST}, +{input:"\\amalg", tag:"mo", output:"\u2210", ttype:CONST}, +{input:"\\bigtriangleup",tag:"mo",output:"\u25B3", ttype:CONST}, +{input:"\\bigtriangledown",tag:"mo",output:"\u25BD", ttype:CONST}, +{input:"\\dag", tag:"mo", output:"\u2020", ttype:CONST}, +{input:"\\dagger", tag:"mo", output:"\u2020", ttype:CONST}, +{input:"\\ddag", tag:"mo", output:"\u2021", ttype:CONST}, +{input:"\\ddagger", tag:"mo", output:"\u2021", ttype:CONST}, +{input:"\\lhd", tag:"mo", output:"\u22B2", ttype:CONST}, +{input:"\\rhd", tag:"mo", output:"\u22B3", ttype:CONST}, +{input:"\\unlhd", tag:"mo", output:"\u22B4", ttype:CONST}, +{input:"\\unrhd", tag:"mo", output:"\u22B5", ttype:CONST}, + + +//BIG Operators +{input:"\\sum", tag:"mo", output:"\u2211", ttype:UNDEROVER}, +{input:"\\prod", tag:"mo", output:"\u220F", ttype:UNDEROVER}, +{input:"\\bigcap", tag:"mo", output:"\u22C2", ttype:UNDEROVER}, +{input:"\\bigcup", tag:"mo", output:"\u22C3", ttype:UNDEROVER}, +{input:"\\bigwedge", tag:"mo", output:"\u22C0", ttype:UNDEROVER}, +{input:"\\bigvee", tag:"mo", output:"\u22C1", ttype:UNDEROVER}, +{input:"\\bigsqcap", tag:"mo", output:"\u2A05", ttype:UNDEROVER}, +{input:"\\bigsqcup", tag:"mo", output:"\u2A06", ttype:UNDEROVER}, +{input:"\\coprod", tag:"mo", output:"\u2210", ttype:UNDEROVER}, +{input:"\\bigoplus", tag:"mo", output:"\u2A01", ttype:UNDEROVER}, +{input:"\\bigotimes", tag:"mo", output:"\u2A02", ttype:UNDEROVER}, +{input:"\\bigodot", tag:"mo", output:"\u2A00", ttype:UNDEROVER}, +{input:"\\biguplus", tag:"mo", output:"\u2A04", ttype:UNDEROVER}, +{input:"\\int", tag:"mo", output:"\u222B", ttype:CONST}, +{input:"\\oint", tag:"mo", output:"\u222E", ttype:CONST}, + +//binary relation symbols +{input:":=", tag:"mo", output:":=", ttype:CONST}, +{input:"\\lt", tag:"mo", output:"<", ttype:CONST}, +{input:"\\gt", tag:"mo", output:">", ttype:CONST}, +{input:"\\ne", tag:"mo", output:"\u2260", ttype:CONST}, +{input:"\\neq", tag:"mo", output:"\u2260", ttype:CONST}, +{input:"\\le", tag:"mo", output:"\u2264", ttype:CONST}, +{input:"\\leq", tag:"mo", output:"\u2264", ttype:CONST}, +{input:"\\leqslant", tag:"mo", output:"\u2264", ttype:CONST}, +{input:"\\ge", tag:"mo", output:"\u2265", ttype:CONST}, +{input:"\\geq", tag:"mo", output:"\u2265", ttype:CONST}, +{input:"\\geqslant", tag:"mo", output:"\u2265", ttype:CONST}, +{input:"\\equiv", tag:"mo", output:"\u2261", ttype:CONST}, +{input:"\\ll", tag:"mo", output:"\u226A", ttype:CONST}, +{input:"\\gg", tag:"mo", output:"\u226B", ttype:CONST}, +{input:"\\doteq", tag:"mo", output:"\u2250", ttype:CONST}, +{input:"\\prec", tag:"mo", output:"\u227A", ttype:CONST}, +{input:"\\succ", tag:"mo", output:"\u227B", ttype:CONST}, +{input:"\\preceq", tag:"mo", output:"\u227C", ttype:CONST}, +{input:"\\succeq", tag:"mo", output:"\u227D", ttype:CONST}, +{input:"\\subset", tag:"mo", output:"\u2282", ttype:CONST}, +{input:"\\supset", tag:"mo", output:"\u2283", ttype:CONST}, +{input:"\\subseteq", tag:"mo", output:"\u2286", ttype:CONST}, +{input:"\\supseteq", tag:"mo", output:"\u2287", ttype:CONST}, +{input:"\\sqsubset", tag:"mo", output:"\u228F", ttype:CONST}, +{input:"\\sqsupset", tag:"mo", output:"\u2290", ttype:CONST}, +{input:"\\sqsubseteq", tag:"mo", output:"\u2291", ttype:CONST}, +{input:"\\sqsupseteq", tag:"mo", output:"\u2292", ttype:CONST}, +{input:"\\sim", tag:"mo", output:"\u223C", ttype:CONST}, +{input:"\\simeq", tag:"mo", output:"\u2243", ttype:CONST}, +{input:"\\approx", tag:"mo", output:"\u2248", ttype:CONST}, +{input:"\\cong", tag:"mo", output:"\u2245", ttype:CONST}, +{input:"\\Join", tag:"mo", output:"\u22C8", ttype:CONST}, +{input:"\\bowtie", tag:"mo", output:"\u22C8", ttype:CONST}, +{input:"\\in", tag:"mo", output:"\u2208", ttype:CONST}, +{input:"\\ni", tag:"mo", output:"\u220B", ttype:CONST}, +{input:"\\owns", tag:"mo", output:"\u220B", ttype:CONST}, +{input:"\\propto", tag:"mo", output:"\u221D", ttype:CONST}, +{input:"\\vdash", tag:"mo", output:"\u22A2", ttype:CONST}, +{input:"\\dashv", tag:"mo", output:"\u22A3", ttype:CONST}, +{input:"\\models", tag:"mo", output:"\u22A8", ttype:CONST}, +{input:"\\perp", tag:"mo", output:"\u22A5", ttype:CONST}, +{input:"\\smile", tag:"mo", output:"\u2323", ttype:CONST}, +{input:"\\frown", tag:"mo", output:"\u2322", ttype:CONST}, +{input:"\\asymp", tag:"mo", output:"\u224D", ttype:CONST}, +{input:"\\notin", tag:"mo", output:"\u2209", ttype:CONST}, + +//matrices +{input:"\\begin{eqnarray}", output:"X", ttype:MATRIX, invisible:true}, +{input:"\\begin{array}", output:"X", ttype:MATRIX, invisible:true}, +{input:"\\\\", output:"}&{", ttype:DEFINITION}, +{input:"\\end{eqnarray}", output:"}}", ttype:DEFINITION}, +{input:"\\end{array}", output:"}}", ttype:DEFINITION}, + +//grouping and literal brackets -- ieval is for IE +{input:"\\big", tag:"mo", output:"X", atval:"1.2", ieval:"2.2", ttype:BIG}, +{input:"\\Big", tag:"mo", output:"X", atval:"1.6", ieval:"2.6", ttype:BIG}, +{input:"\\bigg", tag:"mo", output:"X", atval:"2.2", ieval:"3.2", ttype:BIG}, +{input:"\\Bigg", tag:"mo", output:"X", atval:"2.9", ieval:"3.9", ttype:BIG}, +{input:"\\left", tag:"mo", output:"X", ttype:LEFTBRACKET}, +{input:"\\right", tag:"mo", output:"X", ttype:RIGHTBRACKET}, +{input:"{", output:"{", ttype:LEFTBRACKET, invisible:true}, +{input:"}", output:"}", ttype:RIGHTBRACKET, invisible:true}, + +{input:"(", tag:"mo", output:"(", atval:"1", ttype:STRETCHY}, +{input:"[", tag:"mo", output:"[", atval:"1", ttype:STRETCHY}, +{input:"\\lbrack", tag:"mo", output:"[", atval:"1", ttype:STRETCHY}, +{input:"\\{", tag:"mo", output:"{", atval:"1", ttype:STRETCHY}, +{input:"\\lbrace", tag:"mo", output:"{", atval:"1", ttype:STRETCHY}, +{input:"\\langle", tag:"mo", output:"\u2329", atval:"1", ttype:STRETCHY}, +{input:"\\lfloor", tag:"mo", output:"\u230A", atval:"1", ttype:STRETCHY}, +{input:"\\lceil", tag:"mo", output:"\u2308", atval:"1", ttype:STRETCHY}, + +// rtag:"mi" causes space to be inserted before a following sin, cos, etc. +// (see function LMparseExpr() ) +{input:")", tag:"mo",output:")", rtag:"mi",atval:"1",ttype:STRETCHY}, +{input:"]", tag:"mo",output:"]", rtag:"mi",atval:"1",ttype:STRETCHY}, +{input:"\\rbrack",tag:"mo",output:"]", rtag:"mi",atval:"1",ttype:STRETCHY}, +{input:"\\}", tag:"mo",output:"}", rtag:"mi",atval:"1",ttype:STRETCHY}, +{input:"\\rbrace",tag:"mo",output:"}", rtag:"mi",atval:"1",ttype:STRETCHY}, +{input:"\\rangle",tag:"mo",output:"\u232A", rtag:"mi",atval:"1",ttype:STRETCHY}, +{input:"\\rfloor",tag:"mo",output:"\u230B", rtag:"mi",atval:"1",ttype:STRETCHY}, +{input:"\\rceil", tag:"mo",output:"\u2309", rtag:"mi",atval:"1",ttype:STRETCHY}, + +// "|", "\\|", "\\vert" and "\\Vert" modified later: lspace = rspace = 0em +{input:"|", tag:"mo", output:"\u2223", atval:"1", ttype:STRETCHY}, +{input:"\\|", tag:"mo", output:"\u2225", atval:"1", ttype:STRETCHY}, +{input:"\\vert", tag:"mo", output:"\u2223", atval:"1", ttype:STRETCHY}, +{input:"\\Vert", tag:"mo", output:"\u2225", atval:"1", ttype:STRETCHY}, +{input:"\\mid", tag:"mo", output:"\u2223", atval:"1", ttype:STRETCHY}, +{input:"\\parallel", tag:"mo", output:"\u2225", atval:"1", ttype:STRETCHY}, +{input:"/", tag:"mo", output:"/", atval:"1.01", ttype:STRETCHY}, +{input:"\\backslash", tag:"mo", output:"\u2216", atval:"1", ttype:STRETCHY}, +{input:"\\setminus", tag:"mo", output:"\\", ttype:CONST}, + +//miscellaneous symbols +{input:"\\!", tag:"mspace", atname:"width", atval:"-0.167em", ttype:SPACE}, +{input:"\\,", tag:"mspace", atname:"width", atval:"0.167em", ttype:SPACE}, +{input:"\\>", tag:"mspace", atname:"width", atval:"0.222em", ttype:SPACE}, +{input:"\\:", tag:"mspace", atname:"width", atval:"0.222em", ttype:SPACE}, +{input:"\\;", tag:"mspace", atname:"width", atval:"0.278em", ttype:SPACE}, +{input:"~", tag:"mspace", atname:"width", atval:"0.333em", ttype:SPACE}, +{input:"\\quad", tag:"mspace", atname:"width", atval:"1em", ttype:SPACE}, +{input:"\\qquad", tag:"mspace", atname:"width", atval:"2em", ttype:SPACE}, +//{input:"{}", tag:"mo", output:"\u200B", ttype:CONST}, // zero-width +{input:"\\prime", tag:"mo", output:"\u2032", ttype:CONST}, +{input:"'", tag:"mo", output:"\u02B9", ttype:CONST}, +{input:"''", tag:"mo", output:"\u02BA", ttype:CONST}, +{input:"'''", tag:"mo", output:"\u2034", ttype:CONST}, +{input:"''''", tag:"mo", output:"\u2057", ttype:CONST}, +{input:"\\ldots", tag:"mo", output:"\u2026", ttype:CONST}, +{input:"\\cdots", tag:"mo", output:"\u22EF", ttype:CONST}, +{input:"\\vdots", tag:"mo", output:"\u22EE", ttype:CONST}, +{input:"\\ddots", tag:"mo", output:"\u22F1", ttype:CONST}, +{input:"\\forall", tag:"mo", output:"\u2200", ttype:CONST}, +{input:"\\exists", tag:"mo", output:"\u2203", ttype:CONST}, +{input:"\\Re", tag:"mo", output:"\u211C", ttype:CONST}, +{input:"\\Im", tag:"mo", output:"\u2111", ttype:CONST}, +{input:"\\aleph", tag:"mo", output:"\u2135", ttype:CONST}, +{input:"\\hbar", tag:"mo", output:"\u210F", ttype:CONST}, +{input:"\\ell", tag:"mo", output:"\u2113", ttype:CONST}, +{input:"\\wp", tag:"mo", output:"\u2118", ttype:CONST}, +{input:"\\emptyset", tag:"mo", output:"\u2205", ttype:CONST}, +{input:"\\infty", tag:"mo", output:"\u221E", ttype:CONST}, +{input:"\\surd", tag:"mo", output:"\\sqrt{}", ttype:DEFINITION}, +{input:"\\partial", tag:"mo", output:"\u2202", ttype:CONST}, +{input:"\\nabla", tag:"mo", output:"\u2207", ttype:CONST}, +{input:"\\triangle", tag:"mo", output:"\u25B3", ttype:CONST}, +{input:"\\therefore", tag:"mo", output:"\u2234", ttype:CONST}, +{input:"\\angle", tag:"mo", output:"\u2220", ttype:CONST}, +//{input:"\\\\ ", tag:"mo", output:"\u00A0", ttype:CONST}, +{input:"\\diamond", tag:"mo", output:"\u22C4", ttype:CONST}, +//{input:"\\Diamond", tag:"mo", output:"\u25CA", ttype:CONST}, +{input:"\\Diamond", tag:"mo", output:"\u25C7", ttype:CONST}, +{input:"\\neg", tag:"mo", output:"\u00AC", ttype:CONST}, +{input:"\\lnot", tag:"mo", output:"\u00AC", ttype:CONST}, +{input:"\\bot", tag:"mo", output:"\u22A5", ttype:CONST}, +{input:"\\top", tag:"mo", output:"\u22A4", ttype:CONST}, +{input:"\\square", tag:"mo", output:"\u25AB", ttype:CONST}, +{input:"\\Box", tag:"mo", output:"\u25A1", ttype:CONST}, +{input:"\\wr", tag:"mo", output:"\u2240", ttype:CONST}, + +//standard functions +//Note UNDEROVER *must* have tag:"mo" to work properly +{input:"\\arccos", tag:"mi", output:"arccos", ttype:UNARY, func:true}, +{input:"\\arcsin", tag:"mi", output:"arcsin", ttype:UNARY, func:true}, +{input:"\\arctan", tag:"mi", output:"arctan", ttype:UNARY, func:true}, +{input:"\\arg", tag:"mi", output:"arg", ttype:UNARY, func:true}, +{input:"\\cos", tag:"mi", output:"cos", ttype:UNARY, func:true}, +{input:"\\cosh", tag:"mi", output:"cosh", ttype:UNARY, func:true}, +{input:"\\cot", tag:"mi", output:"cot", ttype:UNARY, func:true}, +{input:"\\coth", tag:"mi", output:"coth", ttype:UNARY, func:true}, +{input:"\\csc", tag:"mi", output:"csc", ttype:UNARY, func:true}, +{input:"\\deg", tag:"mi", output:"deg", ttype:UNARY, func:true}, +{input:"\\det", tag:"mi", output:"det", ttype:UNARY, func:true}, +{input:"\\dim", tag:"mi", output:"dim", ttype:UNARY, func:true}, //CONST? +{input:"\\exp", tag:"mi", output:"exp", ttype:UNARY, func:true}, +{input:"\\gcd", tag:"mi", output:"gcd", ttype:UNARY, func:true}, //CONST? +{input:"\\hom", tag:"mi", output:"hom", ttype:UNARY, func:true}, +{input:"\\inf", tag:"mo", output:"inf", ttype:UNDEROVER}, +{input:"\\ker", tag:"mi", output:"ker", ttype:UNARY, func:true}, +{input:"\\lg", tag:"mi", output:"lg", ttype:UNARY, func:true}, +{input:"\\lim", tag:"mo", output:"lim", ttype:UNDEROVER}, +{input:"\\liminf", tag:"mo", output:"liminf", ttype:UNDEROVER}, +{input:"\\limsup", tag:"mo", output:"limsup", ttype:UNDEROVER}, +{input:"\\ln", tag:"mi", output:"ln", ttype:UNARY, func:true}, +{input:"\\log", tag:"mi", output:"log", ttype:UNARY, func:true}, +{input:"\\max", tag:"mo", output:"max", ttype:UNDEROVER}, +{input:"\\min", tag:"mo", output:"min", ttype:UNDEROVER}, +{input:"\\Pr", tag:"mi", output:"Pr", ttype:UNARY, func:true}, +{input:"\\sec", tag:"mi", output:"sec", ttype:UNARY, func:true}, +{input:"\\sin", tag:"mi", output:"sin", ttype:UNARY, func:true}, +{input:"\\sinh", tag:"mi", output:"sinh", ttype:UNARY, func:true}, +{input:"\\sup", tag:"mo", output:"sup", ttype:UNDEROVER}, +{input:"\\tan", tag:"mi", output:"tan", ttype:UNARY, func:true}, +{input:"\\tanh", tag:"mi", output:"tanh", ttype:UNARY, func:true}, + +//arrows +{input:"\\gets", tag:"mo", output:"\u2190", ttype:CONST}, +{input:"\\leftarrow", tag:"mo", output:"\u2190", ttype:CONST}, +{input:"\\to", tag:"mo", output:"\u2192", ttype:CONST}, +{input:"\\rightarrow", tag:"mo", output:"\u2192", ttype:CONST}, +{input:"\\leftrightarrow", tag:"mo", output:"\u2194", ttype:CONST}, +{input:"\\uparrow", tag:"mo", output:"\u2191", ttype:CONST}, +{input:"\\downarrow", tag:"mo", output:"\u2193", ttype:CONST}, +{input:"\\updownarrow", tag:"mo", output:"\u2195", ttype:CONST}, +{input:"\\Leftarrow", tag:"mo", output:"\u21D0", ttype:CONST}, +{input:"\\Rightarrow", tag:"mo", output:"\u21D2", ttype:CONST}, +{input:"\\Leftrightarrow", tag:"mo", output:"\u21D4", ttype:CONST}, +{input:"\\iff", tag:"mo", output:"~\\Longleftrightarrow~", ttype:DEFINITION}, +{input:"\\Uparrow", tag:"mo", output:"\u21D1", ttype:CONST}, +{input:"\\Downarrow", tag:"mo", output:"\u21D3", ttype:CONST}, +{input:"\\Updownarrow", tag:"mo", output:"\u21D5", ttype:CONST}, +{input:"\\mapsto", tag:"mo", output:"\u21A6", ttype:CONST}, +{input:"\\longleftarrow", tag:"mo", output:"\u2190", ttype:LONG}, +{input:"\\longrightarrow", tag:"mo", output:"\u2192", ttype:LONG}, +{input:"\\longleftrightarrow", tag:"mo", output:"\u2194", ttype:LONG}, +{input:"\\Longleftarrow", tag:"mo", output:"\u21D0", ttype:LONG}, +{input:"\\Longrightarrow", tag:"mo", output:"\u21D2", ttype:LONG}, +{input:"\\implies", tag:"mo", output:"\u21D2", ttype:LONG}, +{input:"\\Longleftrightarrow", tag:"mo", output:"\u21D4", ttype:LONG}, +{input:"\\longmapsto", tag:"mo", output:"\u21A6", ttype:CONST}, + // disaster if LONG + +//commands with argument + +{input:"\\sqrt", tag:"msqrt", output:"sqrt", ttype:UNARY}, +{input:"\\root", tag:"mroot", output:"root", ttype:BINARY}, +{input:"\\frac", tag:"mfrac", output:"/", ttype:BINARY}, +{input:"\\stackrel", tag:"mover", output:"stackrel", ttype:BINARY}, +{input:"\\atop", tag:"mfrac", output:"", ttype:INFIX}, +{input:"\\choose", tag:"mfrac", output:"", ttype:INFIX}, +{input:"_", tag:"msub", output:"_", ttype:INFIX}, +{input:"^", tag:"msup", output:"^", ttype:INFIX}, +{input:"\\mathrm", tag:"mtext", output:"text", ttype:TEXT}, +{input:"\\mbox", tag:"mtext", output:"mbox", ttype:TEXT}, + +//diacritical marks +{input:"\\acute", tag:"mover", output:"\u00B4", ttype:UNARY, acc:true}, +//{input:"\\acute", tag:"mover", output:"\u0317", ttype:UNARY, acc:true}, +//{input:"\\acute", tag:"mover", output:"\u0301", ttype:UNARY, acc:true}, +//{input:"\\grave", tag:"mover", output:"\u0300", ttype:UNARY, acc:true}, +//{input:"\\grave", tag:"mover", output:"\u0316", ttype:UNARY, acc:true}, +{input:"\\grave", tag:"mover", output:"\u0060", ttype:UNARY, acc:true}, +{input:"\\breve", tag:"mover", output:"\u02D8", ttype:UNARY, acc:true}, +{input:"\\check", tag:"mover", output:"\u02C7", ttype:UNARY, acc:true}, +{input:"\\dot", tag:"mover", output:".", ttype:UNARY, acc:true}, +{input:"\\ddot", tag:"mover", output:"..", ttype:UNARY, acc:true}, +//{input:"\\ddot", tag:"mover", output:"\u00A8", ttype:UNARY, acc:true}, +{input:"\\mathring", tag:"mover", output:"\u00B0", ttype:UNARY, acc:true}, +{input:"\\vec", tag:"mover", output:"\u20D7", ttype:UNARY, acc:true}, +{input:"\\overrightarrow",tag:"mover",output:"\u20D7", ttype:UNARY, acc:true}, +{input:"\\overleftarrow",tag:"mover", output:"\u20D6", ttype:UNARY, acc:true}, +{input:"\\hat", tag:"mover", output:"\u005E", ttype:UNARY, acc:true}, +{input:"\\widehat", tag:"mover", output:"\u0302", ttype:UNARY, acc:true}, +{input:"\\tilde", tag:"mover", output:"~", ttype:UNARY, acc:true}, +//{input:"\\tilde", tag:"mover", output:"\u0303", ttype:UNARY, acc:true}, +{input:"\\widetilde", tag:"mover", output:"\u02DC", ttype:UNARY, acc:true}, +{input:"\\bar", tag:"mover", output:"\u203E", ttype:UNARY, acc:true}, +{input:"\\overbrace", tag:"mover", output:"\u23B4", ttype:UNARY, acc:true}, +{input:"\\overline", tag:"mover", output:"\u00AF", ttype:UNARY, acc:true}, +{input:"\\underbrace", tag:"munder", output:"\u23B5", ttype:UNARY, acc:true}, +{input:"\\underline", tag:"munder", output:"\u00AF", ttype:UNARY, acc:true}, +//{input:"underline", tag:"munder", output:"\u0332", ttype:UNARY, acc:true}, + +//typestyles and fonts +{input:"\\displaystyle",tag:"mstyle",atname:"displaystyle",atval:"true", ttype:UNARY}, +{input:"\\textstyle",tag:"mstyle",atname:"displaystyle",atval:"false", ttype:UNARY}, +{input:"\\scriptstyle",tag:"mstyle",atname:"scriptlevel",atval:"1", ttype:UNARY}, +{input:"\\scriptscriptstyle",tag:"mstyle",atname:"scriptlevel",atval:"2", ttype:UNARY}, +{input:"\\textrm", tag:"mstyle", output:"\\mathrm", ttype: DEFINITION}, +{input:"\\mathbf", tag:"mstyle", atname:"mathvariant", atval:"bold", ttype:UNARY}, +{input:"\\textbf", tag:"mstyle", atname:"mathvariant", atval:"bold", ttype:UNARY}, +{input:"\\mathit", tag:"mstyle", atname:"mathvariant", atval:"italic", ttype:UNARY}, +{input:"\\textit", tag:"mstyle", atname:"mathvariant", atval:"italic", ttype:UNARY}, +{input:"\\mathtt", tag:"mstyle", atname:"mathvariant", atval:"monospace", ttype:UNARY}, +{input:"\\texttt", tag:"mstyle", atname:"mathvariant", atval:"monospace", ttype:UNARY}, +{input:"\\mathsf", tag:"mstyle", atname:"mathvariant", atval:"sans-serif", ttype:UNARY}, +{input:"\\mathbb", tag:"mstyle", atname:"mathvariant", atval:"double-struck", ttype:UNARY, codes:AMbbb}, +{input:"\\mathcal",tag:"mstyle", atname:"mathvariant", atval:"script", ttype:UNARY, codes:AMcal}, +{input:"\\mathfrak",tag:"mstyle",atname:"mathvariant", atval:"fraktur",ttype:UNARY, codes:AMfrk} +]; + +var LMnames = []; //list of input symbols + +function LMremoveCharsAndBlanks(str,n) { +//remove n characters and any following blanks + var st; + st = str.slice(n); + for (var i=0; i=LMnames[k]; + } + LMpreviousSymbol=LMcurrentSymbol; + if (match!=""){ + LMcurrentSymbol=LMsymbols[mk].ttype; + return LMsymbols[mk]; + } + LMcurrentSymbol=CONST; + k = 1; + st = str.slice(0,1); //take 1 character + if ("0"<=st && st<="9") tagst = "mn"; + else tagst = (("A">st || st>"Z") && ("a">st || st>"z")?"mo":"mi"); +/* +// Commented out by DRW (not fully understood, but probably to do with +// use of "/" as an INFIX version of "\\frac", which we don't want): +//} +//if (st=="-" && LMpreviousSymbol==INFIX) { +// LMcurrentSymbol = INFIX; //trick "/" into recognizing "-" on second parse +// return {input:st, tag:tagst, output:st, ttype:UNARY, func:true}; +//} +*/ + return {input:st, tag:tagst, output:st, ttype:CONST}; +} + + +/*Parsing ASCII math expressions with the following grammar +v ::= [A-Za-z] | greek letters | numbers | other constant symbols +u ::= sqrt | text | bb | other unary symbols for font commands +b ::= frac | root | stackrel binary symbols +l ::= { | \left left brackets +r ::= } | \right right brackets +S ::= v | lEr | uS | bSS Simple expression +I ::= S_S | S^S | S_S^S | S Intermediate expression +E ::= IE | I/I Expression +Each terminal symbol is translated into a corresponding mathml node.*/ + +var LMpreviousSymbol,LMcurrentSymbol; + +function LMparseSexpr(str) { //parses str and returns [node,tailstr,(node)tag] + var symbol, node, result, result2, i, st,// rightvert = false, + newFrag = document.createDocumentFragment(); + str = LMremoveCharsAndBlanks(str,0); + symbol = LMgetSymbol(str); //either a token or a bracket or empty + if (symbol == null || symbol.ttype == RIGHTBRACKET) + return [null,str,null]; + if (symbol.ttype == DEFINITION) { + str = symbol.output+LMremoveCharsAndBlanks(str,symbol.input.length); + symbol = LMgetSymbol(str); + if (symbol == null || symbol.ttype == RIGHTBRACKET) + return [null,str,null]; + } + str = LMremoveCharsAndBlanks(str,symbol.input.length); + switch (symbol.ttype) { + case SPACE: + node = createMmlNode(symbol.tag); + node.setAttribute(symbol.atname,symbol.atval); + return [node,str,symbol.tag]; + case UNDEROVER: + if (isIE) { + if (symbol.input.substr(0,4) == "\\big") { // botch for missing symbols + str = "\\"+symbol.input.substr(4)+str; // make \bigcup = \cup etc. + symbol = LMgetSymbol(str); + symbol.ttype = UNDEROVER; + str = LMremoveCharsAndBlanks(str,symbol.input.length); + } + } + return [createMmlNode(symbol.tag, + document.createTextNode(symbol.output)),str,symbol.tag]; + case CONST: + var output = symbol.output; + if (isIE) { + if (symbol.input == "'") + output = "\u2032"; + else if (symbol.input == "''") + output = "\u2033"; + else if (symbol.input == "'''") + output = "\u2033\u2032"; + else if (symbol.input == "''''") + output = "\u2033\u2033"; + else if (symbol.input == "\\square") + output = "\u25A1"; // same as \Box + else if (symbol.input.substr(0,5) == "\\frac") { + // botch for missing fractions + var denom = symbol.input.substr(6,1); + if (denom == "5" || denom == "6") { + str = symbol.input.replace(/\\frac/,"\\frac ")+str; + return [node,str,symbol.tag]; + } + } + } + node = createMmlNode(symbol.tag,document.createTextNode(output)); + return [node,str,symbol.tag]; + case LONG: // added by DRW + node = createMmlNode(symbol.tag,document.createTextNode(symbol.output)); + node.setAttribute("minsize","1.5"); + node.setAttribute("maxsize","1.5"); + node = createMmlNode("mover",node); + node.appendChild(createMmlNode("mspace")); + return [node,str,symbol.tag]; + case STRETCHY: // added by DRW + if (isIE && symbol.input == "\\backslash") + symbol.output = "\\"; // doesn't expand, but then nor does "\u2216" + node = createMmlNode(symbol.tag,document.createTextNode(symbol.output)); + if (symbol.input == "|" || symbol.input == "\\vert" || + symbol.input == "\\|" || symbol.input == "\\Vert") { + node.setAttribute("lspace","0em"); + node.setAttribute("rspace","0em"); + } + node.setAttribute("maxsize",symbol.atval); // don't allow to stretch here + if (symbol.rtag != null) + return [node,str,symbol.rtag]; + else + return [node,str,symbol.tag]; + case BIG: // added by DRW + var atval = symbol.atval; + if (isIE) + atval = symbol.ieval; + symbol = LMgetSymbol(str); + if (symbol == null) + return [null,str,null]; + str = LMremoveCharsAndBlanks(str,symbol.input.length); + node = createMmlNode(symbol.tag,document.createTextNode(symbol.output)); + if (isIE) { // to get brackets to expand + var space = createMmlNode("mspace"); + space.setAttribute("height",atval+"ex"); + node = createMmlNode("mrow",node); + node.appendChild(space); + } else { // ignored in IE + node.setAttribute("minsize",atval); + node.setAttribute("maxsize",atval); + } + return [node,str,symbol.tag]; + case LEFTBRACKET: //read (expr+) + if (symbol.input == "\\left") { // left what? + symbol = LMgetSymbol(str); + if (symbol != null) { + if (symbol.input == ".") + symbol.invisible = true; + str = LMremoveCharsAndBlanks(str,symbol.input.length); + } + } + result = LMparseExpr(str,true,false); + if (symbol==null || + (typeof symbol.invisible == "boolean" && symbol.invisible)) + node = createMmlNode("mrow",result[0]); + else { + node = createMmlNode("mo",document.createTextNode(symbol.output)); + node = createMmlNode("mrow",node); + node.appendChild(result[0]); + } + return [node,result[1],result[2]]; + case MATRIX: //read (expr+) + if (symbol.input == "\\begin{array}") { + var mask = ""; + symbol = LMgetSymbol(str); + str = LMremoveCharsAndBlanks(str,0); + if (symbol == null) + mask = "l"; + else { + str = LMremoveCharsAndBlanks(str,symbol.input.length); + if (symbol.input != "{") + mask = "l"; + else do { + symbol = LMgetSymbol(str); + if (symbol != null) { + str = LMremoveCharsAndBlanks(str,symbol.input.length); + if (symbol.input != "}") + mask = mask+symbol.input; + } + } while (symbol != null && symbol.input != "" && symbol.input != "}"); + } + result = LMparseExpr("{"+str,true,true); +// if (result[0]==null) return [createMmlNode("mo", +// document.createTextNode(symbol.input)),str]; + node = createMmlNode("mtable",result[0]); + mask = mask.replace(/l/g,"left "); + mask = mask.replace(/r/g,"right "); + mask = mask.replace(/c/g,"center "); + node.setAttribute("columnalign",mask); + node.setAttribute("displaystyle","false"); + if (isIE) + return [node,result[1],null]; +// trying to get a *little* bit of space around the array +// (IE already includes it) + var lspace = createMmlNode("mspace"); + lspace.setAttribute("width","0.167em"); + var rspace = createMmlNode("mspace"); + rspace.setAttribute("width","0.167em"); + var node1 = createMmlNode("mrow",lspace); + node1.appendChild(node); + node1.appendChild(rspace); + return [node1,result[1],null]; + } else { // eqnarray + result = LMparseExpr("{"+str,true,true); + node = createMmlNode("mtable",result[0]); + if (isIE) + node.setAttribute("columnspacing","0.25em"); // best in practice? + else + node.setAttribute("columnspacing","0.167em"); // correct (but ignored?) + node.setAttribute("columnalign","right center left"); + node.setAttribute("displaystyle","true"); + node = createMmlNode("mrow",node); + return [node,result[1],null]; + } + case TEXT: + if (str.charAt(0)=="{") i=str.indexOf("}"); + else i = 0; + if (i==-1) + i = str.length; + st = str.slice(1,i); + if (st.charAt(0) == " ") { + node = createMmlNode("mspace"); + node.setAttribute("width","0.33em"); // was 1ex + newFrag.appendChild(node); + } + newFrag.appendChild( + createMmlNode(symbol.tag,document.createTextNode(st))); + if (st.charAt(st.length-1) == " ") { + node = createMmlNode("mspace"); + node.setAttribute("width","0.33em"); // was 1ex + newFrag.appendChild(node); + } + str = LMremoveCharsAndBlanks(str,i+1); + return [createMmlNode("mrow",newFrag),str,null]; + case UNARY: + result = LMparseSexpr(str); + if (result[0]==null) return [createMmlNode(symbol.tag, + document.createTextNode(symbol.output)),str]; + if (typeof symbol.func == "boolean" && symbol.func) { // functions hack + st = str.charAt(0); +// if (st=="^" || st=="_" || st=="/" || st=="|" || st==",") { + if (st=="^" || st=="_" || st==",") { + return [createMmlNode(symbol.tag, + document.createTextNode(symbol.output)),str,symbol.tag]; + } else { + node = createMmlNode("mrow", + createMmlNode(symbol.tag,document.createTextNode(symbol.output))); + if (isIE) { + var space = createMmlNode("mspace"); + space.setAttribute("width","0.167em"); + node.appendChild(space); + } + node.appendChild(result[0]); + return [node,result[1],symbol.tag]; + } + } + if (symbol.input == "\\sqrt") { // sqrt + if (isIE) { // set minsize, for \surd + var space = createMmlNode("mspace"); + space.setAttribute("height","1.2ex"); + space.setAttribute("width","0em"); // probably no effect + node = createMmlNode(symbol.tag,result[0]) +// node.setAttribute("minsize","1"); // ignored +// node = createMmlNode("mrow",node); // hopefully unnecessary + node.appendChild(space); + return [node,result[1],symbol.tag]; + } else + return [createMmlNode(symbol.tag,result[0]),result[1],symbol.tag]; + } else if (typeof symbol.acc == "boolean" && symbol.acc) { // accent + node = createMmlNode(symbol.tag,result[0]); + var output = symbol.output; + if (isIE) { + if (symbol.input == "\\hat") + output = "\u0302"; + else if (symbol.input == "\\widehat") + output = "\u005E"; + else if (symbol.input == "\\bar") + output = "\u00AF"; + else if (symbol.input == "\\grave") + output = "\u0300"; + else if (symbol.input == "\\tilde") + output = "\u0303"; + } + var node1 = createMmlNode("mo",document.createTextNode(output)); + if (symbol.input == "\\vec" || symbol.input == "\\check") + // don't allow to stretch + node1.setAttribute("maxsize","1.2"); + // why doesn't "1" work? \vec nearly disappears in firefox + if (isIE && symbol.input == "\\bar") + node1.setAttribute("maxsize","0.5"); + if (symbol.input == "\\underbrace" || symbol.input == "\\underline") + node1.setAttribute("accentunder","true"); + else + node1.setAttribute("accent","true"); + node.appendChild(node1); + if (symbol.input == "\\overbrace" || symbol.input == "\\underbrace") + node.ttype = UNDEROVER; + return [node,result[1],symbol.tag]; + } else { // font change or displaystyle command + if (!isIE && typeof symbol.codes != "undefined") { + for (i=0; i64 && st.charCodeAt(j)<91) newst = newst + + String.fromCharCode(symbol.codes[st.charCodeAt(j)-65]); + else newst = newst + st.charAt(j); + if (result[0].nodeName=="mi") + result[0]=createMmlNode("mo"). + appendChild(document.createTextNode(newst)); + else result[0].replaceChild(createMmlNode("mo"). + appendChild(document.createTextNode(newst)),result[0].childNodes[i]); + } + } + node = createMmlNode(symbol.tag,result[0]); + node.setAttribute(symbol.atname,symbol.atval); + if (symbol.input == "\\scriptstyle" || + symbol.input == "\\scriptscriptstyle") + node.setAttribute("displaystyle","false"); + return [node,result[1],symbol.tag]; + } + case BINARY: + result = LMparseSexpr(str); + if (result[0]==null) return [createMmlNode("mo", + document.createTextNode(symbol.input)),str,null]; + result2 = LMparseSexpr(result[1]); + if (result2[0]==null) return [createMmlNode("mo", + document.createTextNode(symbol.input)),str,null]; + if (symbol.input=="\\root" || symbol.input=="\\stackrel") + newFrag.appendChild(result2[0]); + newFrag.appendChild(result[0]); + if (symbol.input=="\\frac") newFrag.appendChild(result2[0]); + return [createMmlNode(symbol.tag,newFrag),result2[1],symbol.tag]; + case INFIX: + str = LMremoveCharsAndBlanks(str,symbol.input.length); + return [createMmlNode("mo",document.createTextNode(symbol.output)), + str,symbol.tag]; + default: + return [createMmlNode(symbol.tag, //its a constant + document.createTextNode(symbol.output)),str,symbol.tag]; + } +} + +function LMparseIexpr(str) { + var symbol, sym1, sym2, node, result, tag, underover; + str = LMremoveCharsAndBlanks(str,0); + sym1 = LMgetSymbol(str); + result = LMparseSexpr(str); + node = result[0]; + str = result[1]; + tag = result[2]; + symbol = LMgetSymbol(str); + if (symbol.ttype == INFIX) { + str = LMremoveCharsAndBlanks(str,symbol.input.length); + result = LMparseSexpr(str); + if (result[0] == null) // show box in place of missing argument + result[0] = createMmlNode("mo",document.createTextNode("\u25A1")); + str = result[1]; + tag = result[2]; + if (symbol.input == "_" || symbol.input == "^") { + sym2 = LMgetSymbol(str); + tag = null; // no space between x^2 and a following sin, cos, etc. +// This is for \underbrace and \overbrace + underover = ((sym1.ttype == UNDEROVER) || (node.ttype == UNDEROVER)); +// underover = (sym1.ttype == UNDEROVER); + if (symbol.input == "_" && sym2.input == "^") { + str = LMremoveCharsAndBlanks(str,sym2.input.length); + var res2 = LMparseSexpr(str); + str = res2[1]; + tag = res2[2]; // leave space between x_1^2 and a following sin etc. + node = createMmlNode((underover?"munderover":"msubsup"),node); + node.appendChild(result[0]); + node.appendChild(res2[0]); + } else if (symbol.input == "_") { + node = createMmlNode((underover?"munder":"msub"),node); + node.appendChild(result[0]); + } else { + node = createMmlNode((underover?"mover":"msup"),node); + node.appendChild(result[0]); + } + node = createMmlNode("mrow",node); // so sum does not stretch + } else { + node = createMmlNode(symbol.tag,node); + if (symbol.input == "\\atop" || symbol.input == "\\choose") + node.setAttribute("linethickness","0ex"); + node.appendChild(result[0]); + if (symbol.input == "\\choose") + node = createMmlNode("mfenced",node); + } + } + return [node,str,tag]; +} + +function LMparseExpr(str,rightbracket,matrix) { + var symbol, node, result, i, tag, + newFrag = document.createDocumentFragment(); + do { + str = LMremoveCharsAndBlanks(str,0); + result = LMparseIexpr(str); + node = result[0]; + str = result[1]; + tag = result[2]; + symbol = LMgetSymbol(str); + if (node!=undefined) { + if ((tag == "mn" || tag == "mi") && symbol!=null && + typeof symbol.func == "boolean" && symbol.func) { + // Add space before \sin in 2\sin x or x\sin x + var space = createMmlNode("mspace"); + space.setAttribute("width","0.167em"); + node = createMmlNode("mrow",node); + node.appendChild(space); + } + newFrag.appendChild(node); + } + } while ((symbol.ttype != RIGHTBRACKET) + && symbol!=null && symbol.output!=""); + tag = null; + if (symbol.ttype == RIGHTBRACKET) { + if (symbol.input == "\\right") { // right what? + str = LMremoveCharsAndBlanks(str,symbol.input.length); + symbol = LMgetSymbol(str); + if (symbol != null && symbol.input == ".") + symbol.invisible = true; + if (symbol != null) + tag = symbol.rtag; + } + if (symbol!=null) + str = LMremoveCharsAndBlanks(str,symbol.input.length); // ready to return + var len = newFrag.childNodes.length; + if (matrix && + len>0 && newFrag.childNodes[len-1].nodeName == "mrow" && len>1 && + newFrag.childNodes[len-2].nodeName == "mo" && + newFrag.childNodes[len-2].firstChild.nodeValue == "&") { //matrix + var pos = []; // positions of ampersands + var m = newFrag.childNodes.length; + for (i=0; matrix && i -&-&...&-&- + n = node.childNodes.length; + k = 0; + for (j=0; j2) { + newFrag.removeChild(newFrag.firstChild); //remove + newFrag.removeChild(newFrag.firstChild); //remove & + } + table.appendChild(createMmlNode("mtr",row)); + } + return [table,str]; + } + if (typeof symbol.invisible != "boolean" || !symbol.invisible) { + node = createMmlNode("mo",document.createTextNode(symbol.output)); + newFrag.appendChild(node); + } + } + return [newFrag,str,tag]; +} + +var tcnt = 0, dcnt = 0; //theorem and definition counters + +function simpleLaTeXformatting(st) { + st = st.replace(/\$\$((.|\n)*?)\$\$/g,"

$\\displaystyle{$1}$

"); + st = st.replace(/\\begin{(theorem|lemma|proposition|corollary)}((.|\n)*?)\\end{\1}/g,function(r,s,t){tcnt++; return ""+s.charAt(0).toUpperCase()+s.slice(1)+" "+tcnt+". "+t.replace(/^\s*<\/?\w+\/?>|\s*<\/?\w+\/?>$/g,"")+""}); + st = st.replace(/\\begin{(definition|example|remark|problem|exercise|conjecture|solution)}((.|\n)*?)\\end{\1}/g,function(r,s,t){dcnt++; return ""+s.charAt(0).toUpperCase()+s.slice(1)+" "+dcnt+". "+t.replace(/^\s*<\/?\w+\/?>|\s*<\/?\w+\/?>$/g,"")}); + st = st.replace(/\\begin{proof}((.|\n)*?)\\end{proof}/g,function(s,t){return "Proof: "+t.replace(/^\s*<\/?\w+\/?>|\s*<\/?\w+\/?>$/g,"")+" □"}); + st = st.replace(/\\emph{(.*?)}/g,"$1"); + st = st.replace(/\\textbf{(.*?)}/g,"$1"); + st = st.replace(/\\cite{(.*?)}/g,"[$1]"); + st = st.replace(/\\chapter{(.*?)}/g,"

$1

"); + st = st.replace(/\\section{(.*?)}(\s*<\/?(br|p)\s?\/?>)?/g,"

$1

"); + st = st.replace(/\\subsection{((.|\n)*?)}/g,"

$1

"); + st = st.replace(/\\begin{itemize}(\s*<\/?(br|p)\s?\/?>)?/g,"
    "); + st = st.replace(/\\item\s((.|\n)*?)(?=(\\item|\\end))/g,"
  • $1
  • "); + st = st.replace(/\\end{itemize}(\s*<\/?(br|p)\s?\/?>)?/g,"
"); + st = st.replace(/\\begin{enumerate}(\s*<\/?(br|p)\s?\/?>)?/g,"
    "); + st = st.replace(/\\end{enumerate}(\s*<\/?(br|p)\s?\/?>)?/g,"
"); + st = st.replace(/\\item\[(.*?)]{(.*?)}/g,"
$1
$2
"); + st = st.replace(/\\begin{description}/g,"
"); + st = st.replace(/\\end{description}/g,"
"); + st = st.replace(/\\newline\b/g,"
"); + st = st.replace(/\\newpage\b/g,"
"); + st = st.replace(/\\par\b/g,"

 

"); + st = st.replace(/\\bigskip/g,"

 

"); + st = st.replace(/\\medskip/g,"

 

"); + st = st.replace(/\\smallskip/g,"

 

"); + st = st.replace(/\\begin{center}((.|\n)*?)\\end{center}/g,"
$1
"); + return st +} + +function ASCIIandgraphformatting(st) { + st = st.replace(/(.*?)<\/sup>(\s|(\S))/gi,"^{$1} $3"); +//st = st.replace(/<\/?font.*?>/gi,""); // do this only in amath...endamath + st = st.replace(/(Proof:)/g,"$1"); + st = st.replace(/QED/g,"    □"); + st = st.replace(/(\\?end{?a?math}?)/ig,"$1"); + st = st.replace(/(\bamath\b|\\begin{a?math})/ig,"$1"); + st = st.replace(/([>\n])(Theorem|Lemma|Proposition|Corollary|Definition|Example|Remark|Problem|Exercise|Conjecture|Solution)(:|\W\W?(\w|\s|-|\.)*?\W?:)/g,"$1$2$3"); + st = st.replace(/
/gi,"\n")+"\'/>
"}); + st = st.replace(/insertASCIIMathCalculator/g,"
"); +//alert(dsvglocation) + return st +} + +function LMprocessNode(n) { + var frag,st; + try { + st = n.innerHTML; + } catch(err) {} + var am = /amath\b|graph/i.test(st); + if ((st==null || st.indexOf("\$ ")!=-1 || st.indexOf("\$<")!=-1 || + st.indexOf("\\begin")!=-1 || am || st.slice(-1)=="$" || + st.indexOf("\$\n")!=-1)&& !/edit-content|HTMLArea|wikiedit|wpTextbox1/.test(st)){ + if (!avoidinnerHTML && translateLaTeXformatting) + st = simpleLaTeXformatting(st); + if (st!=null && am && !avoidinnerHTML) { + st = ASCIIandgraphformatting(st); + } + st = st.replace(/%7E/g,"~"); // else PmWiki has url issues +//alert(st) + if (!avoidinnerHTML) n.innerHTML = st; + processNodeR(n,false,true); + } +/* if (isIE) { //needed to match size and font of formula to surrounding text + frag = document.getElementsByTagName('math'); + for (var i=0;i|o|* + +// global values used for all pictures (you can change these) +var showcoordinates = true; +var markerstrokewidth = "1"; +var markerstroke = "black"; +var markerfill = "yellow"; +var markersize = 4; +var arrowfill = stroke; +var dotradius = 4; +var ticklength = 4; +var axesstroke = "black"; +var gridstroke = "grey"; +var backgroundstyle = "fill-opacity:1; fill:white"; +var singlelettersitalic = true; + +// internal variables (probably no need to change these) +var picturepos = null; // position of picture relative to top of HTML page +var xunitlength; // in pixels, used to convert to user coordinates +var yunitlength; // in pixels +var origin = [0,0]; // in pixels (default is bottom left corner) +var above = "above"; // shorthands (to avoid typing quotes) +var below = "below"; +var left = "left"; +var right = "right"; +var aboveleft = "aboveleft"; +var aboveright = "aboveright"; +var belowleft = "belowleft"; +var belowright = "belowright"; +var xmin, xmax, ymin, ymax, xscl, yscl, + xgrid, ygrid, xtick, ytick, initialized; +var strokewidth, strokedasharray, stroke, fill, strokeopacity, fillopacity; +var fontstyle, fontfamily, fontsize, fontweight, fontstroke, fontfill; +var marker, endpoints, dynamic = {}; +var picture, svgpicture, doc, width, height; +var isIE = document.createElementNS==null; + +var cpi = "\u03C0", ctheta = "\u03B8"; // character for pi, theta +var log = function(x) { return ln(x)/ln(10) }; +var pi = Math.PI, e = Math.E, ln = Math.log, sqrt = Math.sqrt; +var floor = Math.floor, ceil = Math.ceil, abs = Math.abs; +var sin = Math.sin, cos = Math.cos, tan = Math.tan; +var arcsin = Math.asin, arccos = Math.acos, arctan = Math.atan; +var sec = function(x) { return 1/Math.cos(x) }; +var csc = function(x) { return 1/Math.sin(x) }; +var cot = function(x) { return 1/Math.tan(x) }; +var arcsec = function(x) { return arccos(1/x) }; +var arccsc = function(x) { return arcsin(1/x) }; +var arccot = function(x) { return arctan(1/x) }; +var sinh = function(x) { return (Math.exp(x)-Math.exp(-x))/2 }; +var cosh = function(x) { return (Math.exp(x)+Math.exp(-x))/2 }; +var tanh = + function(x) { return (Math.exp(x)-Math.exp(-x))/(Math.exp(x)+Math.exp(-x)) }; +var sech = function(x) { return 1/cosh(x) }; +var csch = function(x) { return 1/sinh(x) }; +var coth = function(x) { return 1/tanh(x) }; +var arcsinh = function(x) { return ln(x+Math.sqrt(x*x+1)) }; +var arccosh = function(x) { return ln(x+Math.sqrt(x*x-1)) }; +var arctanh = function(x) { return ln((1+x)/(1-x))/2 }; +var sech = function(x) { return 1/cosh(x) }; +var csch = function(x) { return 1/sinh(x) }; +var coth = function(x) { return 1/tanh(x) }; +var arcsech = function(x) { return arccosh(1/x) }; +var arccsch = function(x) { return arcsinh(1/x) }; +var arccoth = function(x) { return arctanh(1/x) }; +var sign = function(x) { return (x==0?0:(x<0?-1:1)) }; + +function factorial(x,n) { // Factorial function + if (n==null) n=1; + if (Math.abs(x-Math.round(x*1000000)/1000000)<1e-15) + x = Math.round(x*1000000)/1000000; + if (x-Math.floor(x)!=0) return NaN; + for (var i=x-n; i>0; i-=n) x*=i; + return (x<0?NaN:(x==0?1:x)); +} + +function C(x,k) { // Binomial coefficient function + var res=1; + for (var i=0; i 2*x + src = src.replace(/([0-9])([a-df-zA-Z]|e^)/g,"$1*$2"); + src = src.replace(/\)([\(0-9a-zA-Z])/g,"\)*$1"); + + try { + with (Math) eval(src); // here the svgpicture object is created + } catch(err) { + if (err!="wait") { +//alert(dsvglocation) + if (typeof err=="object") + errstr = err.name+" "+err.message+" "+err.number+" "+err.description; + else errstr = err; + alert(errstr+"\n"+src) + } + } +} + +var lastSlot = 0; + +function drawPictures() { // main routine; called after webpage has loaded + var src, id, dsvg, nd, node, ht, index, cols, arr, i, node2; + var ASbody = document.getElementsByTagName("body")[0]; + pictures = getElementsByClass(ASbody,"embed","ASCIIsvg"); + var len = pictures.length; + if(len==0) return; + for (index = lastSlot; index < len+lastSlot; index++) { + width = null; height = null; + xmin = null; xmax = null; ymin = null; ymax = null; + xscl = null; xgrid = null; yscl = null; ygrid = null; + initialized = false; + picture = pictures[index-lastSlot]; // current picture object + src = picture.getAttribute("script"); // get the ASCIIsvg code + if (src==null) src = ""; + // insert "axes()" if not present ******** experimental + if (!/axes\b|initPicture/.test(src)) { + var i = 0; + while (/((yscl|ymax|ymin|xscl|xmax|xmin|\bwidth|\bheight)\s*=\s*-?\d*(\d\.|\.\d|\d)\d*\s*;?)/.test(src.slice(i))) i++; + src = (i==0?"axes(); "+src: src.slice(0,i)+src.slice(i).replace(/((scl|max|min|idth|eight)\s*=\s*-?\d*(\d\.|\.\d|\d)\d*\s*;?)/,"$1\naxes();")); + } + ht = picture.getAttribute("height"); + if (isIE) { + picture.setAttribute("wmode","transparent"); +//alert("*"+picture.getAttribute("src")+dsvglocation); +//adding d.svg dynamically greates problems in IE... +// if (picture.getAttribute("src")=="") picture.setAttribute("src",dsvglocation+"d.svg"); + } + if (document.getElementById("picture"+(index+1)+"mml")==null) { + picture.parentNode.style.position = "relative"; + node = createElementXHTML("div"); + node.style.position = "absolute"; + node.style.top = "0px"; + node.style.left = "0px"; + node.setAttribute("id","picture"+(index+1)+"mml"); + picture.parentNode.insertBefore(node,picture.nextSibling); + } + if (ht==null) ht =""; +// if (ht!="") defaultborder = 25; + if (ht=="" || src=="") + if (document.getElementById("picture"+(index+1)+"input")==null) { + node = createElementXHTML("textarea"); + arr = src.split("\n"); + cols = 0; + for (i=0;i= xmax) + alert("Picture requires at least two numbers: xmin < xmax"); + else if (y_max != null && (typeof y_min != "number" || + typeof y_max != "number" || y_min >= y_max)) + alert("initPicture(xmin,xmax,ymin,ymax) requires numbers ymin < ymax"); + else { + if (width==null) { + width = picture.getAttribute("width"); + if (width==null || width=="") width=defaultwidth; + } + picture.setAttribute("width",width); + if (height==null) { + height = picture.getAttribute("height"); + if (height==null || height=="") height=defaultheight; + } + picture.setAttribute("height",height); + xunitlength = (width-2*border)/(xmax-xmin); + yunitlength = xunitlength; +//alert(xmin+" "+xmax+" "+ymin+" "+ymax) + if (ymin==null) { + origin = [-xmin*xunitlength+border,height/2]; + ymin = -(height-2*border)/(2*yunitlength); + ymax = -ymin; + } else { + if (ymax!=null) yunitlength = (height-2*border)/(ymax-ymin); + else ymax = (height-2*border)/yunitlength + ymin; + origin = [-xmin*xunitlength+border,-ymin*yunitlength+border]; + } + if (isIE) { + if (picture.FULLSCREEN==undefined) { + setTimeout('drawPictures()',50); + throw "wait"; + } + svgpicture = picture.getSVGDocument().getElementById("root"); + if (svgpicture==null) { + setTimeout('drawPictures()',50); + throw "wait"; + } + svgpicture = picture.getSVGDocument().getElementById("root"); + while (svgpicture.childNodes.length>0) + svgpicture.removeChild(svgpicture.lastChild); + svgpicture.setAttribute("width",width); + svgpicture.setAttribute("height",height); + svgpicture.setAttribute("name",picture.getAttribute("id")); + doc = picture.getSVGDocument(); + } else { + var qnode = document.createElementNS("http://www.w3.org/2000/svg","svg"); + qnode.setAttribute("id",picture.getAttribute("id")); + qnode.setAttribute("name",picture.getAttribute("id")); +// qnode.setAttribute("style","display:inline"); + qnode.setAttribute("width",picture.getAttribute("width")); + qnode.setAttribute("height",picture.getAttribute("height")); + picturepos = findPos(picture); +// qnode.setAttribute("xmlns:xlink","http://www.w3.org/1999/xlink"); + if (picture.parentNode!=null) { + picture.parentNode.replaceChild(qnode,picture); + } else { + svgpicture.parentNode.replaceChild(qnode,svgpicture); + } + svgpicture = qnode; + doc = document; + } + var nd = document.getElementById(picture.getAttribute("id")+"mml"); + if (nd!=null) // clear out MathML layer + while (nd.childNodes.length>0) nd.removeChild(nd.lastChild); + svgpicture.setAttribute("xunitlength",xunitlength); + svgpicture.setAttribute("yunitlength",yunitlength); + svgpicture.setAttribute("xmin",xmin); + svgpicture.setAttribute("xmax",xmax); + svgpicture.setAttribute("ymin",ymin); + svgpicture.setAttribute("ymax",ymax); + svgpicture.setAttribute("ox",origin[0]); + svgpicture.setAttribute("oy",origin[1]); + var node = myCreateElementSVG("rect"); + node.setAttribute("x","0"); + node.setAttribute("y","0"); + node.setAttribute("width",width); + node.setAttribute("height",height); + node.setAttribute("style",backgroundstyle); + svgpicture.appendChild(node); + svgpicture.setAttribute("onmousemove","displayCoord(evt)"); + svgpicture.setAttribute("onmouseout","removeCoord(evt)"); + svgpicture.setAttribute("onclick","mClick(evt)"); + node = myCreateElementSVG("text"); // used for displayCoord + node.appendChild(doc.createTextNode(" ")); + node.setAttribute("id","coords"); + svgpicture.appendChild(node); + node = myCreateElementSVG("text"); // used for text display + node.appendChild(doc.createTextNode(" ")); + node.setAttribute("id","coords"); + svgpicture.appendChild(node); + border = defaultborder; + } + } +} + +//////////////////////////user graphics commands start///////////////////////// + +function line(p,q,id,endpts) { // segment connecting points p,q (coordinates in units) + var node; + if (id!=null) node = doc.getElementById(id); + if (node==null) { + node = myCreateElementSVG("path"); + node.setAttribute("id", id); + svgpicture.appendChild(node); + } + node.setAttribute("d","M"+(p[0]*xunitlength+origin[0])+","+ + (height-p[1]*yunitlength-origin[1])+" "+ + (q[0]*xunitlength+origin[0])+","+(height-q[1]*yunitlength-origin[1])); + node.setAttribute("stroke-width", strokewidth); + if (strokedasharray!=null) + node.setAttribute("stroke-dasharray", strokedasharray); + node.setAttribute("stroke", stroke); + node.setAttribute("fill", fill); + node.setAttribute("stroke-opacity", strokeopacity); + node.setAttribute("fill-opacity", fillopacity); + if (marker=="dot" || marker=="arrowdot") { + ASdot(p,markersize,markerstroke,markerfill); + if (marker=="arrowdot") arrowhead(p,q); + ASdot(q,markersize,markerstroke,markerfill); + } else if (marker=="arrow") arrowhead(p,q); + if (endpts==null && endpoints!="") endpts = endpoints; + if (endpts!=null) { + if (endpts.indexOf("<-") != -1) arrowhead(q,p); + if (endpts.indexOf("o-") != -1) dot(p, "open"); + if (endpts.indexOf("*-") != -1) dot(p, "closed"); + if (endpts.indexOf("->") != -1) arrowhead(p,q); + if (endpts.indexOf("-o") != -1) dot(q, "open"); + if (endpts.indexOf("-*") != -1) dot(q, "closed"); + } +} + +function path(plist,id,c,endpts) { + if (c==null) c=""; + var node, st, i; + if (id!=null) node = doc.getElementById(id); + if (node==null) { + node = myCreateElementSVG("path"); + node.setAttribute("id", id); + svgpicture.appendChild(node); + } + if (typeof plist == "string") st = plist; + else { + st = "M"; + st += (plist[0][0]*xunitlength+origin[0])+","+ + (height-plist[0][1]*yunitlength-origin[1])+" "+c; + for (i=1; i") != -1) arrowhead(plist[plist.length-2],plist[plist.length-1]); + if (endpts.indexOf("-o") != -1) dot(plist[plist.length-1], "open"); + if (endpts.indexOf("-*") != -1) dot(plist[plist.length-1], "closed"); + } +} + +function curve(plist,id,endpts) { + path(plist,id,"T",endpts); +} + +function vector(p,q,id) { + line(p,q,id,"","->"); +} + +function circle(center,radius,id) { // coordinates in units + var node; + if (id!=null) node = doc.getElementById(id); + if (node==null) { + node = myCreateElementSVG("circle"); + node.setAttribute("id", id); + svgpicture.appendChild(node); + } + node.setAttribute("cx",center[0]*xunitlength+origin[0]); + node.setAttribute("cy",height-center[1]*yunitlength-origin[1]); + node.setAttribute("r",radius*xunitlength); + node.setAttribute("stroke-width", strokewidth); + node.setAttribute("stroke", stroke); + node.setAttribute("fill", fill); + node.setAttribute("stroke-opacity", strokeopacity); + node.setAttribute("fill-opacity", fillopacity); +} + +function loop(p,d,id) { +// d is a direction vector e.g. [1,0] means loop starts in that direction + if (d==null) d=[1,0]; + path([p,[p[0]+d[0],p[1]+d[1]],[p[0]-d[1],p[1]+d[0]],p],id,"C"); + if (marker=="arrow" || marker=="arrowdot") + arrowhead([p[0]+Math.cos(1.4)*d[0]-Math.sin(1.4)*d[1], + p[1]+Math.sin(1.4)*d[0]+Math.cos(1.4)*d[1]],p); +} + +function arc(start,end,radius,id,largearc) { // coordinates in units + var node, v; +//alert([fill, stroke, origin, xunitlength, yunitlength, height]) + if (id!=null) node = doc.getElementById(id); + if (largearc==null) largearc=0; + if (radius==null) { + v=[end[0]-start[0],end[1]-start[1]]; + radius = Math.sqrt(v[0]*v[0]+v[1]*v[1]); + } + if (node==null) { + node = myCreateElementSVG("path"); + node.setAttribute("id", id); + svgpicture.appendChild(node); + } + node.setAttribute("d","M"+(start[0]*xunitlength+origin[0])+","+ + (height-start[1]*yunitlength-origin[1])+" A"+radius*xunitlength+","+ + radius*yunitlength+" 0 "+largearc+",0 "+(end[0]*xunitlength+origin[0])+","+ + (height-end[1]*yunitlength-origin[1])); + node.setAttribute("stroke-width", strokewidth); + node.setAttribute("stroke", stroke); + node.setAttribute("fill", fill); + node.setAttribute("stroke-opacity", strokeopacity); + node.setAttribute("fill-opacity", fillopacity); + if (marker=="arrow" || marker=="arrowdot") { + u = [(end[1]-start[1])/4,(start[0]-end[0])/4]; + v = [(end[0]-start[0])/2,(end[1]-start[1])/2]; +//alert([u,v]) + v = [start[0]+v[0]+u[0],start[1]+v[1]+u[1]]; + } else v=[start[0],start[1]]; + if (marker=="dot" || marker=="arrowdot") { + ASdot(start,markersize,markerstroke,markerfill); + if (marker=="arrowdot") arrowhead(v,end); + ASdot(end,markersize,markerstroke,markerfill); + } else if (marker=="arrow") arrowhead(v,end); +} + +function sector(center,start,end,id) { // center,start,end should be isoceles + var rx = start[0]-center[0], ry = start[1]-center[1]; + arc(start,end,Math.sqrt(rx*rx+ry*ry),id+"arc"); + path([end,center,start],id+"path"); +} + +function ellipse(center,rx,ry,id) { // coordinates in units + var node; + if (id!=null) node = doc.getElementById(id); + if (node==null) { + node = myCreateElementSVG("ellipse"); + node.setAttribute("id", id); + svgpicture.appendChild(node); + } + node.setAttribute("cx",center[0]*xunitlength+origin[0]); + node.setAttribute("cy",height-center[1]*yunitlength-origin[1]); + node.setAttribute("rx",rx*xunitlength); + node.setAttribute("ry",ry*yunitlength); + node.setAttribute("stroke-width", strokewidth); + node.setAttribute("stroke", stroke); + node.setAttribute("fill", fill); + node.setAttribute("stroke-opacity", strokeopacity); + node.setAttribute("fill-opacity", fillopacity); +} + +function triangle(p,q,r,id) { + path([p,q,r,p],id) +} + +function rect(p,q,id,rx,ry) { // opposite corners in units, rounded by radii + var node; + if (id!=null) node = doc.getElementById(id); + if (node==null) { + node = myCreateElementSVG("rect"); + node.setAttribute("id", id); + svgpicture.appendChild(node); + } + node.setAttribute("x",p[0]*xunitlength+origin[0]); + node.setAttribute("y",height-q[1]*yunitlength-origin[1]); + node.setAttribute("width",(q[0]-p[0])*xunitlength); + node.setAttribute("height",(q[1]-p[1])*yunitlength); + if (rx!=null) node.setAttribute("rx",rx*xunitlength); + if (ry!=null) node.setAttribute("ry",ry*yunitlength); + node.setAttribute("stroke-width", strokewidth); + node.setAttribute("stroke", stroke); + node.setAttribute("fill", fill); + node.setAttribute("stroke-opacity", strokeopacity); + node.setAttribute("fill-opacity", fillopacity); +} + +function text(p,st,pos,id,fontsty) { + var dnode, node, dx = 0, dy = fontsize/3, str = st.toString(); + if (/(`|\$)/.test(str)) { // layer for ASCIIMathML and LaTeXMathML + dnode = document.getElementById(svgpicture.getAttribute("name")+"mml"); + if (dnode!=null) { + if (id!=null) node = document.getElementById(id); + if (node==null) { +//alert(dnode.childNodes.length) + node = createElementXHTML("div"); + node.setAttribute("id", id); + node.style.position = "absolute"; + dnode.appendChild(node); + } + while (node.childNodes.length>0) node.removeChild(node.lastChild); + node.appendChild(document.createTextNode(str)); + if (/`/.test(str)) AMprocessNode(node); else LMprocessNode(node); + dx = -node.offsetWidth/2; + dy = -node.offsetHeight/2; + if (pos!=null) { + if (/above/.test(pos)) dy = -node.offsetHeight; + if (/below/.test(pos)) dy = 0; + if (/right/.test(pos)) dx = 0; + if ( /left/.test(pos)) dx = -node.offsetWidth; + } + node.style.left = ""+(p[0]*xunitlength+origin[0]+dx)+"px"; + node.style.top = ""+(height-p[1]*yunitlength-origin[1]+dy)+"px"; + } + return p; + } + var textanchor = "middle"; // regular text goes into SVG + if (pos!=null) { + if (/above/.test(pos)) dy = -fontsize/2; + if (/below/.test(pos)) dy = fontsize-0; + if (/right/.test(pos)) {textanchor = "start"; dx = fontsize/4;} + if ( /left/.test(pos)) {textanchor = "end"; dx = -fontsize/4;} + } + if (id!=null) node = doc.getElementById(id); + if (node==null) { + node = myCreateElementSVG("text"); + node.setAttribute("id", id); + svgpicture.appendChild(node); + node.appendChild(doc.createTextNode(str)); + } + while (node.childNodes.length>1) node.removeChild(node.lastChild); + node.lastChild.nodeValue = "\xA0"+str+"\xA0"; + node.setAttribute("x",p[0]*xunitlength+origin[0]+dx); + node.setAttribute("y",height-p[1]*yunitlength-origin[1]+dy); + node.setAttribute("font-style",(fontsty!=null?fontsty: + (str.search(/^[a-zA-Z]$/)!=-1?"italic":fontstyle))); + node.setAttribute("font-family",fontfamily); + node.setAttribute("font-size",fontsize); + node.setAttribute("font-weight",fontweight); + node.setAttribute("text-anchor",textanchor); + if (fontstroke!="none") node.setAttribute("stroke",fontstroke); + if (fontfill!="none") node.setAttribute("fill",fontfill); + return p; +} + +function mtext(p,st,pos,fontsty,fontsz) { // method for updating text on an svg +// "this" is the text object or the svgpicture object + var textanchor = "middle"; + var dx = 0; var dy = fontsize/3; + if (pos!=null) { + if (pos.slice(0,5)=="above") dy = -fontsize/2; + if (pos.slice(0,5)=="below") dy = fontsize-0; + if (pos.slice(0,5)=="right" || pos.slice(5,10)=="right") { + textanchor = "start"; + dx = fontsize/2; + } + if (pos.slice(0,4)=="left" || pos.slice(5,9)=="left") { + textanchor = "end"; + dx = -fontsize/2; + } + } + var node = this; + if (this.nodeName=="svg") { + node = myCreateElementSVG("text"); + this.appendChild(node); + node.appendChild(doc.createTextNode(st)); + } + node.lastChild.nodeValue = st; + node.setAttribute("x",p[0]+dx); + node.setAttribute("y",p[1]+dy); + node.setAttribute("font-style",(fontsty!=null?fontsty:fontstyle)); + node.setAttribute("font-family",fontfamily); + node.setAttribute("font-size",(fontsz!=null?fontsz:fontsize)); + node.setAttribute("font-weight",fontweight); + node.setAttribute("text-anchor",textanchor); + if (fontstroke!="none") node.setAttribute("stroke",fontstroke); + if (fontfill!="none") node.setAttribute("fill",fontfill); +} + +function image(imgurl,p,w,h,id) { // not working yet + var node; + if (id!=null) node = doc.getElementById(id); + if (node==null) { + node = myCreateElementSVG("image"); + node.setAttribute("id", id); + svgpicture.appendChild(node); + } + node.setAttribute("x",p[0]*xunitlength+origin[0]); + node.setAttribute("y",height-p[1]*yunitlength-origin[1]); + node.setAttribute("width",w); + node.setAttribute("height",h); + node.setAttribute("xlink:href", imgurl); +} + +function ASdot(center,radius,s,f) { // coordinates in units, radius in pixel + if (s==null) s = stroke; if (f==null) f = fill; + var node = myCreateElementSVG("circle"); + node.setAttribute("cx",center[0]*xunitlength+origin[0]); + node.setAttribute("cy",height-center[1]*yunitlength-origin[1]); + node.setAttribute("r",radius); + node.setAttribute("stroke-width", strokewidth); + node.setAttribute("stroke", s); + node.setAttribute("fill", f); + svgpicture.appendChild(node); +} + +function dot(center, typ, label, pos, id) { + var node; + var cx = center[0]*xunitlength+origin[0]; + var cy = height-center[1]*yunitlength-origin[1]; + if (id!=null) node = doc.getElementById(id); + if (typ=="+" || typ=="-" || typ=="|") { + if (node==null) { + node = myCreateElementSVG("path"); + node.setAttribute("id", id); + svgpicture.appendChild(node); + } + if (typ=="+") { + node.setAttribute("d", + " M "+(cx-ticklength)+" "+cy+" L "+(cx+ticklength)+" "+cy+ + " M "+cx+" "+(cy-ticklength)+" L "+cx+" "+(cy+ticklength)); + node.setAttribute("stroke-width", .5); + node.setAttribute("stroke", axesstroke); + } else { + if (typ=="-") node.setAttribute("d", + " M "+(cx-ticklength)+" "+cy+" L "+(cx+ticklength)+" "+cy); + else node.setAttribute("d", + " M "+cx+" "+(cy-ticklength)+" L "+cx+" "+(cy+ticklength)); + node.setAttribute("stroke-width", strokewidth); + node.setAttribute("stroke", stroke); + } + } else { + if (node==null) { + node = myCreateElementSVG("circle"); + node.setAttribute("id", id); + svgpicture.appendChild(node); + } + node.setAttribute("cx",cx); + node.setAttribute("cy",cy); + node.setAttribute("r",dotradius); + node.setAttribute("stroke-width", strokewidth); + node.setAttribute("stroke", stroke); + node.setAttribute("fill", (typ=="open"?"white": + (typ=="closed"?stroke:markerfill))); + } + if (label!=null) + text(center,label,(pos==null?"below":pos),(id==null?id:id+"label")) +} + +point = dot; //alternative name + +function arrowhead(p,q) { // draw arrowhead at q (in units) add size param + var up; + var v = [p[0]*xunitlength+origin[0],height-p[1]*yunitlength-origin[1]]; + var w = [q[0]*xunitlength+origin[0],height-q[1]*yunitlength-origin[1]]; + var u = [w[0]-v[0],w[1]-v[1]]; + var d = Math.sqrt(u[0]*u[0]+u[1]*u[1]); + if (d > 0.00000001) { + u = [u[0]/d, u[1]/d]; + up = [-u[1],u[0]]; + var node = myCreateElementSVG("path"); + node.setAttribute("d","M "+(w[0]-15*u[0]-4*up[0])+" "+ + (w[1]-15*u[1]-4*up[1])+" L "+(w[0]-3*u[0])+" "+(w[1]-3*u[1])+" L "+ + (w[0]-15*u[0]+4*up[0])+" "+(w[1]-15*u[1]+4*up[1])+" z"); + node.setAttribute("stroke-width", markerstrokewidth); + node.setAttribute("stroke", stroke); /*was markerstroke*/ + node.setAttribute("fill", stroke); /*was arrowfill*/ + node.setAttribute("stroke-opacity", strokeopacity); + node.setAttribute("fill-opacity", fillopacity); + svgpicture.appendChild(node); + } +} + +function chopZ(st) { + var k = st.indexOf("."); + if (k==-1) return st; + for (var i=st.length-1; i>k && st.charAt(i)=="0"; i--); + if (i==k) i--; + return st.slice(0,i+1); +} + +function grid(dx,dy) { // for backward compatibility + axes(dx,dy,null,dx,dy) +} + +function noaxes() { + if (!initialized) initPicture(); +} + +function axes(dx,dy,labels,gdx,gdy) { +//xscl=x is equivalent to xtick=x; xgrid=x; labels=true; + var x, y, ldx, ldy, lx, ly, lxp, lyp, pnode, st; + if (!initialized) initPicture(); + if (typeof dx=="string") { labels = dx; dx = null; } + if (typeof dy=="string") { gdx = dy; dy = null; } + if (xscl!=null) {dx = xscl; gdx = xscl; labels = dx} + if (yscl!=null) {dy = yscl; gdy = yscl} + if (xtick!=null) {dx = xtick} + if (ytick!=null) {dy = ytick} + dx = (dx==null?xunitlength:dx*xunitlength); + dy = (dy==null?dx:dy*yunitlength); + fontsize = Math.min(dx/2,dy/2,16); //alert(fontsize) + ticklength = fontsize/4; + if (xgrid!=null) gdx = xgrid; + if (ygrid!=null) gdy = ygrid; + if (gdx!=null) { + gdx = (typeof gdx=="string"?dx:gdx*xunitlength); + gdy = (gdy==null?dy:gdy*yunitlength); + pnode = myCreateElementSVG("path"); + st=""; + for (x = origin[0]; x0; x = x-gdx) + st += " M"+x+",0"+" "+x+","+height; + for (y = height-origin[1]; y0; y = y-gdy) + st += " M0,"+y+" "+width+","+y; + pnode.setAttribute("d",st); + pnode.setAttribute("stroke-width", .5); + pnode.setAttribute("stroke", gridstroke); + pnode.setAttribute("fill", fill); + svgpicture.appendChild(pnode); + } + pnode = myCreateElementSVG("path"); + st="M0,"+(height-origin[1])+" "+width+","+ + (height-origin[1])+" M"+origin[0]+",0 "+origin[0]+","+height; + for (x = origin[0]+dx; x0; x = x-dx) + st += " M"+x+","+(height-origin[1]+ticklength)+" "+x+","+ + (height-origin[1]-ticklength); + for (y = height-origin[1]+dy; y0; y = y-dy) + st += " M"+(origin[0]+ticklength)+","+y+" "+(origin[0]-ticklength)+","+y; + if (labels!=null) with (Math) { + ldx = dx/xunitlength; + ldy = dy/yunitlength; + lx = (xmin>0 || xmax<0?xmin:0); + ly = (ymin>0 || ymax<0?ymin:0); + lxp = (ly==0?"below":"above"); + lyp = (lx==0?"left":"right"); + var ddx = floor(1.1-log(ldx)/log(10))+1; + var ddy = floor(1.1-log(ldy)/log(10))+1; + for (x = ldx; x<=xmax; x = x+ldx) + text([x,ly],chopZ(x.toFixed(ddx)),lxp); + for (x = -ldx; xmin<=x; x = x-ldx) + text([x,ly],chopZ(x.toFixed(ddx)),lxp); + for (y = ldy; y<=ymax; y = y+ldy) + text([lx,y],chopZ(y.toFixed(ddy)),lyp); + for (y = -ldy; ymin<=y; y = y-ldy) + text([lx,y],chopZ(y.toFixed(ddy)),lyp); + } + fontsize = defaultfontsize; + pnode.setAttribute("d",st); + pnode.setAttribute("stroke-width", .5); + pnode.setAttribute("stroke", axesstroke); + pnode.setAttribute("fill", fill); + pnode.setAttribute("stroke-opacity", strokeopacity); + pnode.setAttribute("fill-opacity", fillopacity); + svgpicture.appendChild(pnode); +} + +function mathjs(st) { + //translate a math formula to js function notation + // a^b --> pow(a,b) + // na --> n*a + // (...)d --> (...)*d + // n! --> factorial(n) + // sin^-1 --> arcsin etc. + //while ^ in string, find term on left and right + //slice and concat new formula string + st = st.replace(/\s/g,""); + if (st.indexOf("^-1")!=-1) { + st = st.replace(/sin\^-1/g,"arcsin"); + st = st.replace(/cos\^-1/g,"arccos"); + st = st.replace(/tan\^-1/g,"arctan"); + st = st.replace(/sec\^-1/g,"arcsec"); + st = st.replace(/csc\^-1/g,"arccsc"); + st = st.replace(/cot\^-1/g,"arccot"); + st = st.replace(/sinh\^-1/g,"arcsinh"); + st = st.replace(/cosh\^-1/g,"arccosh"); + st = st.replace(/tanh\^-1/g,"arctanh"); + st = st.replace(/sech\^-1/g,"arcsech"); + st = st.replace(/csch\^-1/g,"arccsch"); + st = st.replace(/coth\^-1/g,"arccoth"); + } + st = st.replace(/^e$/g,"(Math.E)"); + st = st.replace(/^e([^a-zA-Z])/g,"(Math.E)$1"); + st = st.replace(/([^a-zA-Z])e/g,"$1(Math.E)"); +// st = st.replace(/([^a-zA-Z])e([^a-zA-Z])/g,"$1(Math.E)$2"); + st = st.replace(/([0-9])([\(a-zA-Z])/g,"$1*$2"); + st = st.replace(/\)([\(0-9a-zA-Z])/g,"\)*$1"); + var i,j,k, ch, nested; + while ((i=st.indexOf("^"))!=-1) { + //find left argument + if (i==0) return "Error: missing argument"; + j = i-1; + ch = st.charAt(j); + if (ch>="0" && ch<="9") {// look for (decimal) number + j--; + while (j>=0 && (ch=st.charAt(j))>="0" && ch<="9") j--; + if (ch==".") { + j--; + while (j>=0 && (ch=st.charAt(j))>="0" && ch<="9") j--; + } + } else if (ch==")") {// look for matching opening bracket and function name + nested = 1; + j--; + while (j>=0 && nested>0) { + ch = st.charAt(j); + if (ch=="(") nested--; + else if (ch==")") nested++; + j--; + } + while (j>=0 && (ch=st.charAt(j))>="a" && ch<="z" || ch>="A" && ch<="Z") + j--; + } else if (ch>="a" && ch<="z" || ch>="A" && ch<="Z") {// look for variable + j--; + while (j>=0 && (ch=st.charAt(j))>="a" && ch<="z" || ch>="A" && ch<="Z") + j--; + } else { + return "Error: incorrect syntax in "+st+" at position "+j; + } + //find right argument + if (i==st.length-1) return "Error: missing argument"; + k = i+1; + ch = st.charAt(k); + if (ch>="0" && ch<="9" || ch=="-") {// look for signed (decimal) number + k++; + while (k="0" && ch<="9") k++; + if (ch==".") { + k++; + while (k="0" && ch<="9") k++; + } + } else if (ch=="(") {// look for matching closing bracket and function name + nested = 1; + k++; + while (k0) { + ch = st.charAt(k); + if (ch=="(") nested++; + else if (ch==")") nested--; + k++; + } + } else if (ch>="a" && ch<="z" || ch>="A" && ch<="Z") {// look for variable + k++; + while (k="a" && ch<="z" || + ch>="A" && ch<="Z") k++; + } else { + return "Error: incorrect syntax in "+st+" at position "+k; + } + st = st.slice(0,j+1)+"Math.pow("+st.slice(j+1,i)+","+st.slice(i+1,k)+")"+ + st.slice(k); + } + while ((i=st.indexOf("!"))!=-1) { + //find left argument + if (i==0) return "Error: missing argument"; + j = i-1; + ch = st.charAt(j); + if (ch>="0" && ch<="9") {// look for (decimal) number + j--; + while (j>=0 && (ch=st.charAt(j))>="0" && ch<="9") j--; + if (ch==".") { + j--; + while (j>=0 && (ch=st.charAt(j))>="0" && ch<="9") j--; + } + } else if (ch==")") {// look for matching opening bracket and function name + nested = 1; + j--; + while (j>=0 && nested>0) { + ch = st.charAt(j); + if (ch=="(") nested--; + else if (ch==")") nested++; + j--; + } + while (j>=0 && (ch=st.charAt(j))>="a" && ch<="z" || ch>="A" && ch<="Z") + j--; + } else if (ch>="a" && ch<="z" || ch>="A" && ch<="Z") {// look for variable + j--; + while (j>=0 && (ch=st.charAt(j))>="a" && ch<="z" || ch>="A" && ch<="Z") + j--; + } else { + return "Error: incorrect syntax in "+st+" at position "+j; + } + st = st.slice(0,j+1)+"factorial("+st.slice(j+1,i)+")"+st.slice(i+1); + } + return st; +} + +function plot(fun,x_min,x_max,points,id,endpts) { + var pth = []; + var f = function(x) { return x }, g = fun; + var name = null; + if (typeof fun=="string") + eval("g = function(x){ with(Math) return "+mathjs(fun)+" }"); + else if (typeof fun=="object") { + eval("f = function(t){ with(Math) return "+mathjs(fun[0])+" }"); + eval("g = function(t){ with(Math) return "+mathjs(fun[1])+" }"); + } + if (typeof x_min=="string") { name = x_min; x_min = xmin } + else name = id; + var min = (x_min==null?xmin:x_min); + var max = (x_max==null?xmax:x_max); + var inc = max-min-0.000001*(max-min); + inc = (points==null?inc/200:inc/points); + var gt; +//alert(typeof g(min)) + for (var t = min; t <= max; t += inc) { + gt = g(t); + if (!(isNaN(gt)||Math.abs(gt)=="Infinity")) pth[pth.length] = [f(t), gt]; + } + path(pth,name,null,endpts); + return pth; +} + +// make polar plot + +// make Riemann sums + +function slopefield(fun,dx,dy) { + var g = fun; + if (typeof fun=="string") + eval("g = function(x,y){ with(Math) return "+mathjs(fun)+" }"); + var gxy,x,y,u,v,dz; + if (dx==null) dx=1; + if (dy==null) dy=1; + dz = Math.sqrt(dx*dx+dy*dy)/6; + var x_min = Math.ceil(xmin/dx); + var y_min = Math.ceil(ymin/dy); + for (x = x_min; x <= xmax; x += dx) + for (y = y_min; y <= ymax; y += dy) { + gxy = g(x,y); + if (!isNaN(gxy)) { + if (Math.abs(gxy)=="Infinity") {u = 0; v = dz;} + else {u = dz/Math.sqrt(1+gxy*gxy); v = gxy*u;} + line([x-u,y-v],[x+u,y+v]); + } + } +} + +///////////////////////user graphics commands end here///////////////////////// + +function show_props(obj) { + var result = ""; + for (var i=0; i< obj.childNodes.length; i++) + result += obj.childNodes.item(i) + "\n"; + return result; +} + +function displayCoord(evt) { + if (showcoordinates) { + var svgroot = evt.target.parentNode; + var nl = svgroot.childNodes; + for (var i=0; i\n\nASCIIMath Scientific Calculator\n\n\nClick in the box to use your keyboard or use the buttons\n\n\n\nResult:     \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
\n
"; + +// GO1.1 Generic onload by Brothercake +// http://www.brothercake.com/ +//onload function (replaces the onload="translate()" in the tag) +function generic() +{ + if(!init()) return; + if (translateOnLoad) { + var nd = document.getElementById("processasciimathinmoodle"); + if (nd!=null) dsvglocation = nd.className; + if (nd!=null || !checkforprocessasciimathinmoodle) { + translate(); + if (!noSVG && translateASCIIsvg) drawPictures(); + } + var li = getElementsByClass(document,"div","ASCIIMathCalculator"); + if (!noMathML && li.length>0) initASCIIMathCalculators(li); + } +}; +//setup onload function +if(typeof window.addEventListener != 'undefined') +{ + //.. gecko, safari, konqueror and standard + window.addEventListener('load', generic, false); +} +else if(typeof document.addEventListener != 'undefined') +{ + //.. opera 7 + document.addEventListener('load', generic, false); +} +else if(typeof window.attachEvent != 'undefined') +{ + //.. win/ie + window.attachEvent('onload', generic); +} +//** remove this condition to degrade older browsers +else +{ + //.. mac/ie5 and anything else that gets this far + //if there's an existing onload function + if(typeof window.onload == 'function') + { + //store it + var existing = onload; + //add new onload handler + window.onload = function() + { + //call existing onload function + existing(); + //call generic onload function + generic(); + }; + } + else + { + //setup onload function + window.onload = generic; + } +} Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Equation/Equation.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Equation/Equation.js,v diff -u -r1.2 -r1.3 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Equation/Equation.js 27 Mar 2009 08:20:43 -0000 1.2 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Equation/Equation.js 23 May 2010 11:58:33 -0000 1.3 @@ -1,3 +1,232 @@ -/* 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 */ -function Equation(c){this.editor=c;var a=c.config;var b=this;a.registerButton({id:"equation",tooltip:this._lc("Formula Editor"),image:c.imgURL("equation.gif","Equation"),textMode:false,action:function(d,f){b.buttonPress(d,f)}});a.addToolbarElement("equation","inserthorizontalrule",-1);mathcolor=a.Equation.mathcolor;mathfontfamily=a.Equation.mathfontfamily;this.enabled=!Xinha.is_ie;if(this.enabled){this.onBeforeSubmit=this.onBeforeUnload=function(){b.unParse()}}if(typeof AMprocessNode!="function"){Xinha._loadback(Xinha.getPluginDir("Equation")+"/ASCIIMathML.js",function(){translate()})}}Xinha.Config.prototype.Equation={mathcolor:"black",mathfontfamily:"serif"};Equation._pluginInfo={name:"ASCIIMathML Formula Editor",version:"2.3 (2008-01-26)",developer:"Raimund Meyer",developer_url:"http://x-webservice.net",c_owner:"",sponsor:"",sponsor_url:"",license:"GNU/LGPL"};Equation.prototype._lc=function(a){return Xinha._lc(a,"Equation")};Equation.prototype.onGenerate=function(){this.parse()};Equation.prototype.onKeyPress=function(b){if(this.enabled){e=this.editor;var a=e._getFirstAncestor(e.getSelection(),["span"]);if(a&&a.className=="AM"){if(b.keyCode==8||b.keyCode==46||b.charCode){Xinha._stopEvent(b);return true}}}return false};Equation.prototype.onBeforeMode=function(a){if(this.enabled&&a=="textmode"){this.unParse()}};Equation.prototype.onMode=function(a){if(this.enabled&&a=="wysiwyg"){this.parse()}};Equation.prototype.parse=function(){if(this.enabled){var f=this.editor._doc;var b=f.getElementsByTagName("span");for(var a=0;a'+c+"")}}}if(this.enabled){this.parse()}}}; \ No newline at end of file +/*------------------------------------------*\ + AsciiMathML Formula Editor for Xinha + _______________________ + + Based on AsciiMathML by Peter Jipsen http://www.chapman.edu/~jipsen + + Including a table with math symbols for easy input modified from CharacterMap for ASCIIMathML by Peter Jipsen + HTMLSource based on HTMLArea XTD 1.5 (http://mosforge.net/projects/htmlarea3xtd/) modified by Holger Hees + Original Author - Bernhard Pfeifer novocaine@gmx.net + + See readme.txt + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License (at http://www.gnu.org/licenses/lgpl.html) + for more details. + + Raimund Meyer 11/23/2006 + +\*------------------------------------------*/ +function Equation(editor) { + this.editor = editor; + + var cfg = editor.config; + var self = this; + + + // register the toolbar buttons provided by this plugin + cfg.registerButton({ + id : "equation", + tooltip : this._lc("Formula Editor"), + image : editor.imgURL("equation.gif", "Equation"), + textMode : false, + action : function(editor, id) { + self.buttonPress(editor, id); + } + }); + cfg.addToolbarElement("equation", "inserthorizontalrule", -1); + + mathcolor = cfg.Equation.mathcolor; // change it to "" (to inherit) or any other color + mathfontfamily = cfg.Equation.mathfontfamily; + + this.enabled = !Xinha.is_ie; + + if (this.enabled) + { + this.onBeforeSubmit = this.onBeforeUnload = function () {self.unParse();}; + } + + if (typeof AMprocessNode != "function") + { + Xinha._loadback(Xinha.getPluginDir('Equation') + "/ASCIIMathML.js", function () { translate(); }); + } +} + +Xinha.Config.prototype.Equation = +{ + "mathcolor" : "black", // change it to "" (to inherit) or any other color + "mathfontfamily" : "serif" // change to "" to inherit (works in IE) + // or another family (e.g. "arial") +} + +Equation._pluginInfo = { + name : "ASCIIMathML Formula Editor", + version : "2.3 (2008-01-26)", + developer : "Raimund Meyer", + developer_url : "http://x-webservice.net", + c_owner : "", + sponsor : "", + sponsor_url : "", + license : "GNU/LGPL" +}; + +Equation.prototype._lc = function(string) +{ + return Xinha._lc(string, 'Equation'); +}; +Equation.prototype.onGenerate = function() +{ + this.parse(); +}; + +// avoid changing the formula in the editor +Equation.prototype.onKeyPress = function(ev) +{ + if (this.enabled) + { + e = this.editor; + var span = e._getFirstAncestor(e.getSelection(),['span']); + if ( span && span.className == "AM" ) + { + if ( + ev.keyCode == 8 || // delete + ev.keyCode == 46 ||// backspace + ev.charCode // all character keys + ) + { // stop event + Xinha._stopEvent(ev); + return true; + } + } + } + return false; +} +Equation.prototype.onBeforeMode = function( mode ) +{ + if (this.enabled && mode == 'textmode') + { + this.unParse(); + } +} +Equation.prototype.onMode = function( mode ) +{ + if (this.enabled && mode == 'wysiwyg') + { + this.parse(); + } +} + +Equation.prototype.parse = function () +{ + if (this.enabled) + { + var doc = this.editor._doc; + var spans = doc.getElementsByTagName("span"); + for (var i = 0;i'+formula+''); + } + } + + if (this.enabled) this.parse();//AMprocessNode(this.editor._doc.body, false); + } +} Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Equation/equation.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Equation/Attic/equation.js,v diff -u -r1.9 -r1.10 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Equation/equation.js 27 Mar 2009 08:20:43 -0000 1.9 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Equation/equation.js 23 May 2010 11:58:33 -0000 1.10 @@ -1,3 +1,232 @@ -/* 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 */ -function Equation(c){this.editor=c;var a=c.config;var b=this;a.registerButton({id:"equation",tooltip:this._lc("Formula Editor"),image:c.imgURL("equation.gif","Equation"),textMode:false,action:function(d,f){b.buttonPress(d,f)}});a.addToolbarElement("equation","inserthorizontalrule",-1);mathcolor=a.Equation.mathcolor;mathfontfamily=a.Equation.mathfontfamily;this.enabled=!Xinha.is_ie;if(this.enabled){this.onBeforeSubmit=this.onBeforeUnload=function(){b.unParse()}}if(typeof AMprocessNode!="function"){Xinha._loadback(Xinha.getPluginDir("Equation")+"/ASCIIMathML.js",function(){translate()})}}Xinha.Config.prototype.Equation={mathcolor:"black",mathfontfamily:"serif"};Equation._pluginInfo={name:"ASCIIMathML Formula Editor",version:"2.3 (2008-01-26)",developer:"Raimund Meyer",developer_url:"http://x-webservice.net",c_owner:"",sponsor:"",sponsor_url:"",license:"GNU/LGPL"};Equation.prototype._lc=function(a){return Xinha._lc(a,"Equation")};Equation.prototype.onGenerate=function(){this.parse()};Equation.prototype.onKeyPress=function(b){if(this.enabled){e=this.editor;var a=e._getFirstAncestor(e.getSelection(),["span"]);if(a&&a.className=="AM"){if(b.keyCode==8||b.keyCode==46||b.charCode){Xinha._stopEvent(b);return true}}}return false};Equation.prototype.onBeforeMode=function(a){if(this.enabled&&a=="textmode"){this.unParse()}};Equation.prototype.onMode=function(a){if(this.enabled&&a=="wysiwyg"){this.parse()}};Equation.prototype.parse=function(){if(this.enabled){var f=this.editor._doc;var b=f.getElementsByTagName("span");for(var a=0;a'+c+"")}}}if(this.enabled){this.parse()}}}; \ No newline at end of file +/*------------------------------------------*\ + AsciiMathML Formula Editor for Xinha + _______________________ + + Based on AsciiMathML by Peter Jipsen http://www.chapman.edu/~jipsen + + Including a table with math symbols for easy input modified from CharacterMap for ASCIIMathML by Peter Jipsen + HTMLSource based on HTMLArea XTD 1.5 (http://mosforge.net/projects/htmlarea3xtd/) modified by Holger Hees + Original Author - Bernhard Pfeifer novocaine@gmx.net + + See readme.txt + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License (at http://www.gnu.org/licenses/lgpl.html) + for more details. + + Raimund Meyer 11/23/2006 + +\*------------------------------------------*/ +function Equation(editor) { + this.editor = editor; + + var cfg = editor.config; + var self = this; + + + // register the toolbar buttons provided by this plugin + cfg.registerButton({ + id : "equation", + tooltip : this._lc("Formula Editor"), + image : editor.imgURL("equation.gif", "Equation"), + textMode : false, + action : function(editor, id) { + self.buttonPress(editor, id); + } + }); + cfg.addToolbarElement("equation", "inserthorizontalrule", -1); + + mathcolor = cfg.Equation.mathcolor; // change it to "" (to inherit) or any other color + mathfontfamily = cfg.Equation.mathfontfamily; + + this.enabled = !Xinha.is_ie; + + if (this.enabled) + { + this.onBeforeSubmit = this.onBeforeUnload = function () {self.unParse();}; + } + + if (typeof AMprocessNode != "function") + { + Xinha._loadback(Xinha.getPluginDir('Equation') + "/ASCIIMathML.js", function () { translate(); }); + } +} + +Xinha.Config.prototype.Equation = +{ + "mathcolor" : "black", // change it to "" (to inherit) or any other color + "mathfontfamily" : "serif" // change to "" to inherit (works in IE) + // or another family (e.g. "arial") +} + +Equation._pluginInfo = { + name : "ASCIIMathML Formula Editor", + version : "2.3 (2008-01-26)", + developer : "Raimund Meyer", + developer_url : "http://x-webservice.net", + c_owner : "", + sponsor : "", + sponsor_url : "", + license : "GNU/LGPL" +}; + +Equation.prototype._lc = function(string) +{ + return Xinha._lc(string, 'Equation'); +}; +Equation.prototype.onGenerate = function() +{ + this.parse(); +}; + +// avoid changing the formula in the editor +Equation.prototype.onKeyPress = function(ev) +{ + if (this.enabled) + { + e = this.editor; + var span = e._getFirstAncestor(e.getSelection(),['span']); + if ( span && span.className == "AM" ) + { + if ( + ev.keyCode == 8 || // delete + ev.keyCode == 46 ||// backspace + ev.charCode // all character keys + ) + { // stop event + Xinha._stopEvent(ev); + return true; + } + } + } + return false; +} +Equation.prototype.onBeforeMode = function( mode ) +{ + if (this.enabled && mode == 'textmode') + { + this.unParse(); + } +} +Equation.prototype.onMode = function( mode ) +{ + if (this.enabled && mode == 'wysiwyg') + { + this.parse(); + } +} + +Equation.prototype.parse = function () +{ + if (this.enabled) + { + var doc = this.editor._doc; + var spans = doc.getElementsByTagName("span"); + for (var i = 0;i'+formula+''); + } + } + + if (this.enabled) this.parse();//AMprocessNode(this.editor._doc.body, false); + } +} Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Equation/popups/dialog.html =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Equation/popups/dialog.html,v diff -u -r1.3 -r1.4 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Equation/popups/dialog.html 13 May 2008 18:53:48 -0000 1.3 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Equation/popups/dialog.html 23 May 2010 11:58:33 -0000 1.4 @@ -39,6 +39,25 @@ + * + * + * + * @author $Author: gustafn $ + * @version $Id: file-picker.js,v 1.1 2010/05/23 11:58:33 gustafn Exp $ + * @package ImageManager + */ + + +function FilePicker(field) +{ + this.field = field; + var picker = this; + + var but = document.createElement('input'); + but.type = 'button'; + but.value = 'Browse'; + but.onclick = function() { picker.popup_picker(); } + + field.parentNode.insertBefore(but,field.nextSibling); + field.size = '20'; + field.style.textAlign = 'right'; +}; + +FilePicker.prototype.backend = _editor_url + 'plugins/ExtendedFileManager/backend.php?__plugin=ExtendedFileManager&'; +FilePicker.prototype.backend_data = null; + +FilePicker.prototype.append_query_string = true; + +FilePicker.prototype.popup_picker = function() +{ + var picker = this; // closure for later + var outparam = null; + if(picker.field.value) + { + outparam = + { + f_href : picker.field.value, + f_title : '', + f_target : '', + f_usetarget : false, + baseHref: null + }; + + } + + var manager = this.backend + '__function=manager&mode=link'; + if(this.backend_config != null) + { + manager += '&backend_config=' + + encodeURIComponent(this.backend_config); + manager += '&backend_config_hash=' + + encodeURIComponent(this.backend_config_hash); + manager += '&backend_config_secret_key_location=' + + encodeURIComponent(this.backend_config_secret_key_location); + } + + if(this.backend_data != null) + { + for ( var i in this.backend_data ) + { + manager += '&' + i + '=' + encodeURIComponent(this.backend_data[i]); + } + } + + Dialog(manager, function(param) { + if (!param) { // user must have pressed Cancel + return false; + } + + picker.field.value = param.f_href; + + }, outparam); +} + +// Dialog is part of Xinha, but we'll provide it here incase Xinha's not being +// loaded. +if(typeof Dialog == 'undefined') +{ + // htmlArea v3.0 - Copyright (c) 2003-2004 interactivetools.com, inc. + // This copyright notice MUST stay intact for use (see license.txt). + // + // Portions (c) dynarch.com, 2003-2004 + // + // A free WYSIWYG editor replacement for ")},"form"];this.editor.config.btnList.insert_select_field=[this._lc("Insert a select field."),[d,3,0],false,function(){a('')},"form"];this.editor.config.btnList.insert_cb_field=[this._lc("Insert a check box."),[d,4,0],false,function(){a('')},"form"];this.editor.config.btnList.insert_rb_field=[this._lc("Insert a radio button."),[d,5,0],false,function(){a('')},"form"];this.editor.config.btnList.insert_button=[this._lc("Insert a submit/reset button."),[d,6,0],false,function(){a('')},"form"]}FormOperations.prototype.onGenerate=function(){if(Xinha.is_gecko){var a=this.editor;var b=this.editor._doc;Xinha._addEvents(b,["mousemove"],function(c){return a._editorEvent(c)})}};FormOperations.prototype._preparePanel=function(){var a=this;if(this.html==false){Xinha._getback(Xinha.getPluginDir("FormOperations")+"/panel.html",function(b){a.html=b;a._preparePanel()});return false}if(typeof Xinha.Dialog=="undefined"){Xinha._loadback(_editor_url+"modules/Dialogs/XinhaDialog.js",function(){a._preparePanel()});return false}if(typeof Xinha.PanelDialog=="undefined"){Xinha._loadback(_editor_url+"modules/Dialogs/panel-dialog.js",function(){a._preparePanel()});return false}this.panel=new Xinha.PanelDialog(this.editor,"bottom",this.html,"FormOperations");this.panel.hide();this.ready=true};FormOperations.prototype.onUpdateToolbar=function(){if(!this.ready){return true}var b=this.editor._activeElement(this.editor._getSelection());if(b!=null){if(b==this.activeElement){return true}var a=b.tagName.toLowerCase();this.hideAll();if(a==="form"){if(this.editor.config.FormOperations.allow_edit_form){this.showForm(b)}else{this.panel.hide();this.activeElement=null;this.panel.hide();return true}}else{if(this.editor.config.FormOperations.allow_edit_form&&typeof b.form!="undefined"&&b.form){this.showForm(b.form)}switch(a){case"form":this.showForm(b);break;case"input":switch(b.getAttribute("type").toLowerCase()){case"text":case"password":case"hidden":this.showText(b);break;case"radio":case"checkbox":this.showCbRd(b);break;case"submit":case"reset":case"button":this.showButton(b);break}break;case"textarea":this.showTextarea(b);break;case"select":this.showSelect(b);break;default:this.activeElement=null;this.panel.hide();return true}}this.panel.show();this.activeElement=b;return true}else{this.activeElement=null;this.panel.hide();return true}};FormOperations.prototype.hideAll=function(){this.panel.getElementById("fs_form").style.display="none";this.panel.getElementById("fs_text").style.display="none";this.panel.getElementById("fs_textarea").style.display="none";this.panel.getElementById("fs_select").style.display="none";this.panel.getElementById("fs_cbrd").style.display="none";this.panel.getElementById("fs_button").style.display="none"};FormOperations.prototype.showForm=function(a){this.panel.getElementById("fs_form").style.display="";var c={action:a.action,method:a.method.toUpperCase()};this.panel.setValues(c);var b=a;this.panel.getElementById("action").onkeyup=function(){b.action=this.value};this.panel.getElementById("method").onchange=function(){b.method=this.options[this.selectedIndex].value}};FormOperations.prototype.showText=function(b){this.panel.getElementById("fs_text").style.display="";var e={text_name:this.deformatName(b,b.name),text_value:b.value,text_type:b.type.toLowerCase(),text_width:b.style.width?parseFloat(b.style.width.replace(/[^0-9.]/,"")):"",text_width_units:b.style.width?b.style.width.replace(/[0-9.]/,"").toLowerCase():"ex",text_maxlength:b.maxlength?b.maxlength:""};this.panel.setValues(e);var d=b;var c=this;this.panel.getElementById("text_name").onkeyup=function(){d.name=c.formatName(d,this.value)};this.panel.getElementById("text_value").onkeyup=function(){d.value=this.value};this.panel.getElementById("text_type").onchange=function(){if(!Xinha.is_ie){d.type=this.options[this.selectedIndex].value}else{var h=c.editor._doc.createElement("div");if(!/type=/.test(d.outerHTML)){h.innerHTML=d.outerHTML.replace(/0?j.size:1),select_height_units:j.style.height?j.style.height.replace(/[0-9.]/,"").toLowerCase():"items"};this.panel.setValues(f);var b=j;var e=this;this.panel.getElementById("select_name").onkeyup=function(){b.name=e.formatName(b,this.value)};this.panel.getElementById("select_multiple").onclick=function(){b.multiple=this.checked};var k=this.panel.getElementById("select_width");var d=this.panel.getElementById("select_width_units");this.panel.getElementById("select_width").onkeyup=this.panel.getElementById("select_width_units").onchange=function(){if(!k.value||isNaN(parseFloat(k.value))){b.style.width=""}b.style.width=parseFloat(k.value)+d.options[d.selectedIndex].value};var c=this.panel.getElementById("select_height");var g=this.panel.getElementById("select_height_units");this.panel.getElementById("select_height").onkeyup=this.panel.getElementById("select_height_units").onchange=function(){if(!c.value||isNaN(parseFloat(c.value))){b.style.height="";return}if(g.selectedIndex==0){b.style.height="";b.size=parseInt(c.value)}else{b.style.height=parseFloat(c.value)+g.options[g.selectedIndex].value}};var a=this.panel.getElementById("select_options");this.arrayToOpts(this.optsToArray(j.options),a.options);this.panel.getElementById("add_option").onclick=function(){var h=prompt(Xinha._lc("Enter the name for new option.","FormOperations"));if(h==null){return}var l=new Option(h);var i=e.optsToArray(a.options);if(a.selectedIndex>=0){i.splice(a.selectedIndex,0,l)}else{i.push(l)}e.arrayToOpts(i,j.options);e.arrayToOpts(i,a.options)};this.panel.getElementById("del_option").onclick=function(){var m=e.optsToArray(a.options);var l=[];for(var h=0;h0)){return}var i=e.optsToArray(a.options);var h=i.splice(a.selectedIndex,1).pop();i.splice(a.selectedIndex-1,0,h);e.arrayToOpts(i,j.options);e.arrayToOpts(i,a.options)};this.panel.getElementById("down_option").onclick=function(){if(a.selectedIndex==a.options.length-1){return}var i=e.optsToArray(a.options);var h=i.splice(a.selectedIndex,1).pop();i.splice(a.selectedIndex+1,0,h);e.arrayToOpts(i,j.options);e.arrayToOpts(i,a.options)};this.panel.getElementById("select_options").onchange=function(){e.arrayToOpts(e.optsToArray(a.options),j.options)}};FormOperations.prototype.optsToArray=function(d){var b=[];for(var c=0;c=0;c--){d[c]=null}for(var c=0;c FieldName[] + // 'unmodified' => FieldName + 'multiple_field_format': 'php', + 'allow_edit_form' : false, + 'default_form_action' : Xinha.getPluginDir('FormOperations') + '/formmail.php', + 'default_form_html' : Xinha._geturlcontent(Xinha.getPluginDir('FormOperations') + '/default_form.html') +}; + +FormOperations._pluginInfo = +{ + name : "FormOperations", + version : "1.0", + developer: "James Sleeman", + developer_url: "http://www.gogo.co.nz/", + c_owner : "Gogo Internet Services", + license : "htmlArea", + sponsor : "Gogo Internet Services", + sponsor_url : "http://www.gogo.co.nz/" +}; + +function FormOperations(editor) +{ + this.editor = editor; + this.panel = false; + this.html = false; + this.ready = false; + this.activeElement = null; + this._preparePanel(); + + + editor.config.pageStyleSheets.push(Xinha.getPluginDir('FormOperations') + '/iframe.css'); + + var toolbar = + [ + 'separator', + 'insert_form', + 'insert_text_field', + 'insert_textarea_field', + 'insert_select_field', + 'insert_cb_field', + 'insert_rb_field', + 'insert_button' + ]; + + this.editor.config.toolbar.push(toolbar); + + function pasteAndSelect(htmlTag) + { + var id = Xinha.uniq('fo'); + htmlTag = htmlTag.replace(/^<([^ \/>]+)/i, '<$1 id="'+id+'"'); + editor.insertHTML(htmlTag); + var el = editor._doc.getElementById(id); + el.setAttribute('id', ''); + editor.selectNodeContents(el); + editor.updateToolbar(); + return el; + } + + var buttonsImage = editor.imgURL('buttons.gif', 'FormOperations'); + + FormOperations.prototype._lc = function(string) { + return Xinha._lc(string, 'FormOperations'); + }; + + this.editor.config.btnList.insert_form = + [ this._lc("Insert a Form."), + [buttonsImage, 0, 0], + false, + function() + { + var form = null; + if(editor.config.FormOperations.default_form_html) + { + form = pasteAndSelect(editor.config.FormOperations.default_form_html); + } + else + { + form = pasteAndSelect('
 
'); + } + + if(editor.config.FormOperations.default_form_action && !form.action) + { + form.action = editor.config.FormOperations.default_form_action; + } + } + ]; + + this.editor.config.btnList.insert_text_field = + [ this._lc("Insert a text, password or hidden field."), + [buttonsImage, 1, 0], + false, + function() + { + pasteAndSelect(''); + }, + 'form' + ]; + + this.editor.config.btnList.insert_textarea_field = + [ this._lc("Insert a multi-line text field."), + [buttonsImage, 2, 0], + false, + function() + { + pasteAndSelect(''); + }, + 'form' + ]; + + this.editor.config.btnList.insert_select_field = + [ this._lc("Insert a select field."), + [buttonsImage, 3, 0], + false, + function() + { + pasteAndSelect(''); + }, + 'form' + ]; + + this.editor.config.btnList.insert_cb_field = + [ this._lc("Insert a check box."), + [buttonsImage, 4, 0], + false, + function() + { + pasteAndSelect(''); + }, + 'form' + ]; + + this.editor.config.btnList.insert_rb_field = + [ this._lc("Insert a radio button."), + [buttonsImage, 5, 0], + false, + function() + { + pasteAndSelect(''); + }, + 'form' + ]; + + this.editor.config.btnList.insert_button = + [ this._lc("Insert a submit/reset button."), + [buttonsImage, 6, 0], + false, + function() + { + pasteAndSelect(''); + }, + 'form' + ]; +} + +FormOperations.prototype.onGenerate = function() +{ + // Gecko does not register click events on select lists inside the iframe + // so the only way of detecting that is to do an event on mouse move. + if( Xinha.is_gecko) + { + var editor = this.editor; + var doc = this.editor._doc; + Xinha._addEvents + (doc, ["mousemove"], + function (event) { + return editor._editorEvent(event); + }); + } +}; + +FormOperations.prototype._preparePanel = function () +{ + var fo = this; + if(this.html == false) + { + + Xinha._getback(Xinha.getPluginDir('FormOperations') + '/panel.html', + function(txt) + { + fo.html = txt; + fo._preparePanel(); + } + ); + return false; + } + + if(typeof Xinha.Dialog == 'undefined') + { + Xinha._loadback + (_editor_url + 'modules/Dialogs/XinhaDialog.js', function() { fo._preparePanel(); } ); + return false; + } + + if(typeof Xinha.PanelDialog == 'undefined') + { + Xinha._loadback + (_editor_url + 'modules/Dialogs/panel-dialog.js', function() { fo._preparePanel(); } ); + return false; + } + + + + this.panel = new Xinha.PanelDialog(this.editor,'bottom',this.html,'FormOperations'); + this.panel.hide(); + this.ready = true; +}; + +FormOperations.prototype.onUpdateToolbar = function() +{ + if(!this.ready) return true; + var activeElement = this.editor._activeElement(this.editor._getSelection()); + if(activeElement != null) + { + if(activeElement == this.activeElement) return true; + + var tag = activeElement.tagName.toLowerCase(); + + this.hideAll(); + if(tag === 'form') + { + if(this.editor.config.FormOperations.allow_edit_form) + { + this.showForm(activeElement); + } + else + { + this.panel.hide(); + this.activeElement = null; + this.panel.hide(); + return true; + } + } + else + { + + if(this.editor.config.FormOperations.allow_edit_form && typeof activeElement.form != 'undefined' && activeElement.form) + { + this.showForm(activeElement.form); + } + + switch(tag) + { + case 'form': + { + this.showForm(activeElement); + } + break; + + case 'input': + { + switch(activeElement.getAttribute('type').toLowerCase()) + { + case 'text' : + case 'password': + case 'hidden' : + { + this.showText(activeElement); + } + break; + + case 'radio' : + case 'checkbox': + { + this.showCbRd(activeElement); + } + break; + + case 'submit' : + case 'reset' : + case 'button' : + { + this.showButton(activeElement); + } + break; + } + } + break; + + case 'textarea': + { + this.showTextarea(activeElement); + } + break; + + case 'select': + { + this.showSelect(activeElement); + } + break; + + default: + { + this.activeElement = null; + this.panel.hide(); + return true; + } + } + } + + this.panel.show(); + + //this.editor.scrollToElement(activeElement); + this.activeElement = activeElement; + return true; + } + else + { + this.activeElement = null; + this.panel.hide(); + return true; + } +}; + + +FormOperations.prototype.hideAll = function() +{ + this.panel.getElementById('fs_form').style.display = 'none'; + this.panel.getElementById('fs_text').style.display = 'none'; + this.panel.getElementById('fs_textarea').style.display = 'none'; + this.panel.getElementById('fs_select').style.display = 'none'; + this.panel.getElementById('fs_cbrd').style.display = 'none'; + this.panel.getElementById('fs_button').style.display = 'none'; +}; + +FormOperations.prototype.showForm = function(form) +{ + this.panel.getElementById('fs_form').style.display = ''; + var vals = + { + 'action' : form.action, + 'method' : form.method.toUpperCase() + } + this.panel.setValues(vals); + var f = form; + this.panel.getElementById('action').onkeyup = function () { f.action = this.value; }; + this.panel.getElementById('method').onchange = function () { f.method = this.options[this.selectedIndex].value; }; +}; + +FormOperations.prototype.showText = function (input) +{ + this.panel.getElementById('fs_text').style.display = ''; + + var vals = + { + 'text_name' : this.deformatName(input, input.name), + 'text_value' : input.value, + 'text_type' : input.type.toLowerCase(), + 'text_width' : input.style.width ? parseFloat(input.style.width.replace(/[^0-9.]/, '')) : '', + 'text_width_units': input.style.width ? input.style.width.replace(/[0-9.]/, '').toLowerCase() : 'ex', + 'text_maxlength' : input.maxlength ? input.maxlength : '' + } + this.panel.setValues(vals); + + var i = input; + var fo = this; + + this.panel.getElementById('text_name').onkeyup = function () { i.name = fo.formatName(i, this.value); } + this.panel.getElementById('text_value').onkeyup = function () { i.value = this.value; } + this.panel.getElementById('text_type').onchange = function () + { + if(!Xinha.is_ie) + { + i.type = this.options[this.selectedIndex].value; + } + else + { + // IE does not permit modifications of the type of a form field once it is set + // We therefor have to destroy and recreate it. I swear, if I ever + // meet any of the Internet Explorer development team I'm gonna + // kick them in the nuts! + var tmpContainer = fo.editor._doc.createElement('div'); + if(!/type=/.test(i.outerHTML)) + { + tmpContainer.innerHTML = i.outerHTML.replace(/ 0 ? input.size : 1), + 'select_height_units': input.style.height ? input.style.height.replace(/[0-9.]/, '').toLowerCase() : 'items' + }; + + this.panel.setValues(vals); + + var i = input; + var fo = this; + this.panel.getElementById('select_name').onkeyup = function () { i.name = fo.formatName(i, this.value); }; + this.panel.getElementById('select_multiple').onclick = function () { i.multiple = this.checked; }; + + var w = this.panel.getElementById('select_width'); + var wu = this.panel.getElementById('select_width_units'); + + this.panel.getElementById('select_width').onkeyup = + this.panel.getElementById('select_width_units').onchange = + function () + { + if(!w.value || isNaN(parseFloat(w.value))) + { + i.style.width = ''; + } + i.style.width = parseFloat(w.value) + wu.options[wu.selectedIndex].value; + }; + + var h = this.panel.getElementById('select_height'); + var hu = this.panel.getElementById('select_height_units'); + + this.panel.getElementById('select_height').onkeyup = + this.panel.getElementById('select_height_units').onchange = + function () + { + if(!h.value || isNaN(parseFloat(h.value))) + { + i.style.height = ''; + return; + } + + if(hu.selectedIndex == 0) + { + i.style.height = ''; + i.size = parseInt(h.value); + } + else + { + i.style.height = parseFloat(h.value) + hu.options[hu.selectedIndex].value; + } + }; + + + var fo_sel = this.panel.getElementById('select_options'); + this.arrayToOpts(this.optsToArray(input.options), fo_sel.options); + + this.panel.getElementById('add_option').onclick = + function() + { + var txt = prompt(Xinha._lc("Enter the name for new option.", 'FormOperations')); + if(txt == null) return; + var newOpt = new Option(txt); + var opts = fo.optsToArray(fo_sel.options); + if(fo_sel.selectedIndex >= 0) + { + opts.splice(fo_sel.selectedIndex, 0, newOpt); + } + else + { + opts.push(newOpt); + } + fo.arrayToOpts(opts, input.options); + fo.arrayToOpts(opts, fo_sel.options); + }; + + this.panel.getElementById('del_option').onclick = + function() + { + var opts = fo.optsToArray(fo_sel.options); + var newOpts = [ ]; + for(var i = 0; i < opts.length; i++) + { + if(opts[i].selected) continue; + newOpts.push(opts[i]); + } + fo.arrayToOpts(newOpts, input.options); + fo.arrayToOpts(newOpts, fo_sel.options); + }; + + this.panel.getElementById('up_option').onclick = + function() + { + if(!(fo_sel.selectedIndex > 0)) return; + var opts = fo.optsToArray(fo_sel.options); + var move = opts.splice(fo_sel.selectedIndex, 1).pop(); + opts.splice(fo_sel.selectedIndex - 1, 0, move); + fo.arrayToOpts(opts, input.options); + fo.arrayToOpts(opts, fo_sel.options); + }; + + this.panel.getElementById('down_option').onclick = + function() + { + if(fo_sel.selectedIndex == fo_sel.options.length - 1) return; + var opts = fo.optsToArray(fo_sel.options); + var move = opts.splice(fo_sel.selectedIndex, 1).pop(); + opts.splice(fo_sel.selectedIndex+1, 0, move); + fo.arrayToOpts(opts, input.options); + fo.arrayToOpts(opts, fo_sel.options); + }; + + this.panel.getElementById('select_options').onchange = + function() + { + fo.arrayToOpts(fo.optsToArray(fo_sel.options), input.options); + }; +}; + +FormOperations.prototype.optsToArray = function(o) +{ + var a = [ ]; + for(var i = 0; i < o.length; i++) + { + a.push( + { + 'text' : o[i].text, + 'value' : o[i].value, + 'defaultSelected' : o[i].defaultSelected, + 'selected' : o[i].selected + } + ); + } + return a; +}; + +FormOperations.prototype.arrayToOpts = function(a, o) +{ + for(var i = o.length -1; i >= 0; i--) + { + o[i] = null; + } + + for(var i = 0; i < a.length; i++) + { + o[i] = new Option(a[i].text, a[i].value, a[i].defaultSelected, a[i].selected); + } +}; + +FormOperations.prototype.formatName = function(input, name) +{ + + // Multiple name + var mname = name; + switch(this.editor.config.FormOperations.multiple_field_format) + { + case 'php': + { + mname += '[]'; + } + break; + + case 'unmodified': + { + // Leave as is. + } + break; + + default: + { + throw("Unknown multiple field format " + this.editor.config.FormOperations.multiple_field_format); + } + } + + if + ( + (input.tagName.toLowerCase() == 'select' && input.multiple) + || (input.tagName.toLowerCase() == 'input' && input.type.toLowerCase() == 'checkbox') + ) + { + name = mname; + } + + return name; +}; + +FormOperations.prototype.deformatName = function(input, name) +{ + if(this.editor.config.FormOperations.multiple_field_format == 'php') + { + name = name.replace(/\[\]$/, ''); + } + + return name; +}; \ No newline at end of file Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Forms/Forms.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Forms/Forms.js,v diff -u -r1.2 -r1.3 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Forms/Forms.js 27 Mar 2009 08:20:43 -0000 1.2 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Forms/Forms.js 23 May 2010 11:58:33 -0000 1.3 @@ -1,3 +1,353 @@ -/* 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 */ -function Forms(e){this.editor=e;var a=e.config;var h=Forms.btnList;var b=this;var f=["linebreak"];for(var d=0;d")}}},g)}else{var b="";if(typeof c=="undefined"){c=h.getParentElement();var o=c.tagName.toLowerCase();if(c&&(o=="legend")){c=c.parentElement;o=c.tagName.toLowerCase()}if(c&&!(o=="textarea"||o=="select"||o=="input"||o=="label"||o=="fieldset")){c=null}}if(c){k=c.tagName.toLowerCase();g.f_name=c.name;b=c.tagName;if(k=="input"){g.f_type=c.type;k=c.type}switch(k){case"textarea":g.f_cols=c.cols;g.f_rows=c.rows;g.f_text=c.innerHTML;g.f_wrap=c.getAttribute("wrap");g.f_readOnly=c.getAttribute("readOnly");g.f_disabled=c.getAttribute("disabled");g.f_tabindex=c.getAttribute("tabindex");g.f_accesskey=c.getAttribute("accesskey");break;case"select":g.f_size=parseInt(c.size);g.f_multiple=c.getAttribute("multiple");g.f_disabled=c.getAttribute("disabled");g.f_tabindex=c.getAttribute("tabindex");var l=new Array();for(var d=0;d<=c.options.length-1;d++){l[d]=new n(c.options[d].text,c.options[d].value)}g.f_options=l;break;case"text":case"password":g.f_value=c.value;g.f_size=c.size;g.f_maxLength=c.maxLength;g.f_readOnly=c.getAttribute("readOnly");g.f_disabled=c.getAttribute("disabled");g.f_tabindex=c.getAttribute("tabindex");g.f_accesskey=c.getAttribute("accesskey");break;case"hidden":g.f_value=c.value;break;case"submit":case"reset":g.f_value=c.value;g.f_disabled=c.getAttribute("disabled");g.f_tabindex=c.getAttribute("tabindex");g.f_accesskey=c.getAttribute("accesskey");break;case"checkbox":case"radio":g.f_value=c.value;g.f_checked=c.checked;g.f_disabled=c.getAttribute("disabled");g.f_tabindex=c.getAttribute("tabindex");g.f_accesskey=c.getAttribute("accesskey");break;case"button":g.f_value=c.value;g.f_onclick=c.getAttribute("onclick");g.f_disabled=c.getAttribute("disabled");g.f_tabindex=c.getAttribute("tabindex");g.f_accesskey=c.getAttribute("accesskey");break;case"image":g.f_value=c.value;g.f_src=c.src;g.f_disabled=c.getAttribute("disabled");g.f_tabindex=c.getAttribute("tabindex");g.f_accesskey=c.getAttribute("accesskey");break;case"file":g.f_disabled=c.getAttribute("disabled");g.f_tabindex=c.getAttribute("tabindex");g.f_accesskey=c.getAttribute("accesskey");break;case"label":g.f_text=c.innerHTML;g.f_for=c.getAttribute("for");g.f_accesskey=c.getAttribute("accesskey");break;case"fieldset":if(c.firstChild.tagName.toLowerCase()=="legend"){g.f_text=c.firstChild.innerHTML}else{g.f_text=""}break}}else{g.f_name="";switch(e){case"textarea":case"select":case"label":case"fieldset":b=e;break;default:b="input";g.f_type=e;break}g.f_options="";g.f_cols="20";g.f_rows="4";g.f_multiple="false";g.f_value="";g.f_size="";g.f_maxLength="";g.f_checked="";g.f_src="";g.f_onclick="";g.f_wrap="";g.f_readOnly="false";g.f_disabled="false";g.f_tabindex="";g.f_accesskey="";g.f_for="";g.f_text="";g.f_legend=""}h._popupDialog("plugin://Forms/"+b+".html",function(q){if(q){if(q.f_cols){if(isNaN(parseInt(q.f_cols,10))||parseInt(q.f_cols,10)<=0){q.f_cols=""}}if(q.f_rows){if(isNaN(parseInt(q.f_rows,10))||parseInt(q.f_rows,10)<=0){q.f_rows=""}}if(q.f_size){if(isNaN(parseInt(q.f_size,10))||parseInt(q.f_size,10)<=0){q.f_size=""}}if(q.f_maxlength){if(isNaN(parseInt(q.f_maxLength,10))||parseInt(q.f_maxLength,10)<=0){q.f_maxLength=""}}if(c){for(field in q){if((field=="f_text")||(field=="f_options")||(field=="f_onclick")||(field=="f_checked")){continue}if(q[field]!=""){c.setAttribute(field.substring(2,20),q[field])}else{c.removeAttribute(field.substring(2,20))}}if(k=="textarea"){c.innerHTML=q.f_text}else{if(k=="select"){c.options.length=0;var p=q.f_options;for(d=0;d<=p.length-1;d++){c.options[d]=new Option(p[d].text,p[d].value)}}else{if(k=="label"){c.innerHTML=q.f_text}else{if(k=="fieldset"){if(g.f_text!=""){if(c.firstChild.tagName.toLowerCase()=="legend"){c.firstChild.innerHTML=q.f_text}}else{}}else{if((k=="checkbox")||(k=="radio")){if(q.f_checked!=""){c.checked=true}else{c.checked=false}}else{if(q.f_onclick){c.onclick="";if(q.f_onclick!=""){c.onclick=q.f_onclick}}}}}}}}else{var i="";for(field in q){if(!q[field]){continue}if((q[field]=="")||(field=="f_text")||(field=="f_options")){continue}i+=" "+field.substring(2,20)+'="'+q[field]+'"'}if(k=="textarea"){i=""+q.f_text+""}else{if(k=="select"){i="";var p=q.f_options;for(d=0;d<=p.length-1;d++){i+='"}i+=""}else{if(k=="label"){i=""+q.f_text+""}else{if(k=="fieldset"){i="";if(q.f_legend!=""){i+=""+q.f_text+""}i+=""}else{i='"}}}}h.insertHTML(i)}}},g)}}; \ No newline at end of file +// Form plugin for Xinha +// Distributed under the same terms as HTMLArea itself. +// This notice MUST stay intact for use (see license.txt). + + +function Forms(editor) { + this.editor = editor; + var cfg = editor.config; + var bl = Forms.btnList; + var self = this; + // register the toolbar buttons provided by this plugin + var toolbar = ["linebreak"]; + for (var i = 0; i < bl.length; ++i) { + var btn = bl[i]; + if (!btn) { + toolbar.push("separator"); + } else { + var id = btn[0]; + if (i<3) + cfg.registerButton(id, this._lc(btn[1]), editor.imgURL("ed_" + btn[0] + ".gif", "Forms"), false, + function(editor, id) { + // dispatch button press event + self.buttonPress(editor, id); + }); + else + cfg.registerButton(id, this._lc(btn[1]), editor.imgURL("ed_" + btn[0] + ".gif", "Forms"), false, + function(editor, id) { + // dispatch button press event + self.buttonPress(editor, id); + },"form"); + toolbar.push(id); + } + } + // add a new line in the toolbar + cfg.toolbar.push(toolbar); +} + +Forms._pluginInfo = { + name : "Forms", + origin : "version: 1.0, by Nelson Bright, BrightWork, Inc., http://www.brightworkweb.com", + version : "2.0", + developer : "Udo Schmal", + developer_url : "", + sponsor : "L.N.Schaffrath NeueMedien", + sponsor_url : "http://www.schaffrath-neuemedien.de/", + c_owner : "Udo Schmal & Schaffrath-NeueMedien", + license : "htmlArea" +}; + +// the list of buttons added by this plugin +Forms.btnList = [ + // form properties button + null, // separator + ["form", "Form"], + null, // separator + // form elements + ["textarea", "Textarea"], + ["select", "Selection Field"], + ["checkbox", "Checkbox"], + ["radio", "Radio Button"], + ["text", "Text Field"], + ["password", "Password Field"], + ["file", "File Field"], + ["button", "Button"], + ["submit", "Submit Button"], + ["reset", "Reset Button"], + ["image", "Image Button"], + ["hidden", "Hidden Field"], + ["label", "Label"], + ["fieldset", "Field Set"] + ]; + +Forms.prototype._lc = function(string) { + return Xinha._lc(string, 'Forms'); +}; + +Forms.prototype.onGenerate = function() { + this.editor.addEditorStylesheet(Xinha.getPluginDir("Forms") + '/forms.css'); +}; + +Forms.prototype.buttonPress = function(editor,button_id, node) { + function optionValues(text,value) { + this.text = text; + this.value = value; + } + var outparam = new Object(); + var type = button_id; + var sel = editor._getSelection(); + var range = editor._createRange(sel); + if (button_id=="form") { //Form + // see if selection is inside an existing 'form' tag + var pe = editor.getParentElement(); + var frm = null; + while (pe && (pe.nodeType == 1) && (pe.tagName.toLowerCase() != 'body')) { + if(pe.tagName.toLowerCase() == "form") { + frm = pe; + break; + } else + pe = pe.parentNode; + } + if (frm) { + outparam.f_name = frm.name; + outparam.f_action = frm.action; + outparam.f_method = frm.method; + outparam.f_enctype = frm.enctype; + outparam.f_target = frm.target; + } else {; + outparam.f_name = ""; + outparam.f_action = ""; + outparam.f_method = ""; + outparam.f_enctype = ""; + outparam.f_target = ""; + } + editor._popupDialog("plugin://Forms/form", function(param) { + if (param) { + if(frm) { + frm.name = param["f_name"]; + frm.setAttribute("action", param["f_action"]); + frm.setAttribute("method", param["f_method"]); + frm.setAttribute("enctype",param["f_enctype"]); + frm.setAttribute("target", param["f_target"]); + } else { + frm = '
'); + } + } + }, outparam); + + } else { // form element (checkbox, radio, text, password, textarea, select, button, submit, reset, image, hidden) + var tagName = ""; + // see if selection is an form element + if (typeof node == "undefined") { + node = editor.getParentElement(); + var tag = node.tagName.toLowerCase() + if (node && (tag == "legend")) { + node = node.parentElement; + tag = node.tagName.toLowerCase(); + } + if (node && !(tag == "textarea" || tag == "select" || tag == "input" || tag == "label" || tag == "fieldset")) + node = null; + } + + if(node) { + type = node.tagName.toLowerCase(); + outparam.f_name = node.name; + tagName = node.tagName; + if (type == "input") { + outparam.f_type = node.type; + type = node.type; + } + switch (type) { + case "textarea": + outparam.f_cols = node.cols; + outparam.f_rows = node.rows; + outparam.f_text = node.innerHTML; + outparam.f_wrap = node.getAttribute("wrap"); + outparam.f_readOnly = node.getAttribute("readOnly"); + outparam.f_disabled = node.getAttribute("disabled"); + outparam.f_tabindex = node.getAttribute("tabindex"); + outparam.f_accesskey = node.getAttribute("accesskey"); + break; + case "select": + outparam.f_size = parseInt(node.size); + outparam.f_multiple = node.getAttribute("multiple"); + outparam.f_disabled = node.getAttribute("disabled"); + outparam.f_tabindex = node.getAttribute("tabindex"); + var a_options = new Array(); + for (var i=0; i<=node.options.length-1; i++) { + a_options[i] = new optionValues(node.options[i].text, node.options[i].value); + } + outparam.f_options = a_options; + break; + case "text": + case "password": + outparam.f_value = node.value; + outparam.f_size = node.size; + outparam.f_maxLength = node.maxLength; + outparam.f_readOnly = node.getAttribute("readOnly"); + outparam.f_disabled = node.getAttribute("disabled"); + outparam.f_tabindex = node.getAttribute("tabindex"); + outparam.f_accesskey = node.getAttribute("accesskey"); + break; + case "hidden": + outparam.f_value = node.value; + break; + case "submit": + case "reset": + outparam.f_value = node.value; + outparam.f_disabled = node.getAttribute("disabled"); + outparam.f_tabindex = node.getAttribute("tabindex"); + outparam.f_accesskey = node.getAttribute("accesskey"); + break; + case "checkbox": + case "radio": + outparam.f_value = node.value; + outparam.f_checked = node.checked; + outparam.f_disabled = node.getAttribute("disabled"); + outparam.f_tabindex = node.getAttribute("tabindex"); + outparam.f_accesskey = node.getAttribute("accesskey"); + break; + case "button": + outparam.f_value = node.value; + outparam.f_onclick = node.getAttribute("onclick"); + outparam.f_disabled = node.getAttribute("disabled"); + outparam.f_tabindex = node.getAttribute("tabindex"); + outparam.f_accesskey = node.getAttribute("accesskey"); + break; + case "image": + outparam.f_value = node.value; + outparam.f_src = node.src; + outparam.f_disabled = node.getAttribute("disabled"); + outparam.f_tabindex = node.getAttribute("tabindex"); + outparam.f_accesskey = node.getAttribute("accesskey"); + break; + case "file": + outparam.f_disabled = node.getAttribute("disabled"); + outparam.f_tabindex = node.getAttribute("tabindex"); + outparam.f_accesskey = node.getAttribute("accesskey"); + break; + case "label": + outparam.f_text = node.innerHTML; + outparam.f_for = node.getAttribute("for"); + outparam.f_accesskey = node.getAttribute("accesskey"); + break; + case "fieldset": + if(node.firstChild.tagName.toLowerCase()=="legend") + outparam.f_text = node.firstChild.innerHTML; + else + outparam.f_text = ""; + break; + } + } else { + outparam.f_name = ""; + switch (button_id) { + case "textarea": + case "select": + case "label": + case "fieldset": + tagName = button_id; + break; + default: + tagName = "input"; + outparam.f_type = button_id; + break; + } + outparam.f_options = ""; + outparam.f_cols = "20"; + outparam.f_rows = "4"; + outparam.f_multiple = "false"; + outparam.f_value = ""; + outparam.f_size = ""; + outparam.f_maxLength = ""; + outparam.f_checked = ""; + outparam.f_src = ""; + outparam.f_onclick = ""; + outparam.f_wrap = ""; + outparam.f_readOnly = "false"; + outparam.f_disabled = "false"; + outparam.f_tabindex = ""; + outparam.f_accesskey = ""; + outparam.f_for = ""; + outparam.f_text = ""; + outparam.f_legend = ""; + } + editor._popupDialog("plugin://Forms/" + tagName + ".html", function(param) { + if (param) { + if(param["f_cols"]) + if (isNaN(parseInt(param["f_cols"],10)) || parseInt(param["f_cols"],10) <= 0) + param["f_cols"] = ""; + if(param["f_rows"]) + if(isNaN(parseInt(param["f_rows"],10)) || parseInt(param["f_rows"],10) <= 0) + param["f_rows"] = ""; + if(param["f_size"]) + if(isNaN(parseInt(param["f_size"],10)) || parseInt(param["f_size"],10) <= 0) + param["f_size"] = ""; + if(param["f_maxlength"]) + if(isNaN(parseInt(param["f_maxLength"],10)) || parseInt(param["f_maxLength"],10) <= 0) + param["f_maxLength"] = ""; + if(node) { + //prepare existing Element + for (field in param) { + //alert(field.substring(2,20) + '=' + param[field]); //to be silent! by htanaka + if ((field=="f_text") || (field=="f_options") || (field=="f_onclick") || (field=="f_checked"))continue; + if (param[field] != "") + node.setAttribute(field.substring(2,20), param[field]); + else + node.removeAttribute(field.substring(2,20)); + } + if (type == "textarea") { + node.innerHTML = param["f_text"]; + } else if(type == "select") { + node.options.length = 0; + var optionsList = param["f_options"]; + for (i=0; i<= optionsList.length-1; i++) { + node.options[i] = new Option(optionsList[i].text, optionsList[i].value) + } + } else if(type == "label") { + node.innerHTML = param["f_text"]; + } else if(type == "fieldset") { + if(outparam.f_text != "") { + if(node.firstChild.tagName.toLowerCase()=="legend") + node.firstChild.innerHTML = param["f_text"]; + } else {}// not implemented jet + } else if((type == "checkbox") || (type == "radio")) { //input + if(param["f_checked"]!="") + node.checked = true; + else + node.checked = false; + } else { + if(param["f_onclick"]){ + node.onclick = ""; + if(param["f_onclick"]!="") + node.onclick = param["f_onclick"]; + } + } + } else { + //create Element + var text = ""; + for (field in param) { + if (!param[field]) continue; + if ((param[field]=="") || (field=="f_text")|| (field=="f_options"))continue; + text += " " + field.substring(2,20) + '="' + param[field] + '"'; + } + + if(type == "textarea") { + text = '' + param["f_text"] + ''; + } else if(type == "select") { + text = ''; + var optionsList = param["f_options"]; + for (i=0; i<= optionsList.length-1; i++) { + text += ''; + } + text += ''; + } else if(type == "label") { + text = '' + param["f_text"] + ''; + } else if(type == "fieldset") { + text = ''; + if (param["f_legend"] != "") text += '' + param["f_text"] + ''; + text += ''; + } else { + text = ''; + } + editor.insertHTML(text); + } + } + }, outparam); + } +}; \ No newline at end of file Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Forms/forms.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Forms/Attic/forms.js,v diff -u -r1.8 -r1.9 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Forms/forms.js 27 Mar 2009 08:20:43 -0000 1.8 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/Forms/forms.js 23 May 2010 11:58:33 -0000 1.9 @@ -1,3 +1,353 @@ -/* 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 */ -function Forms(e){this.editor=e;var a=e.config;var h=Forms.btnList;var b=this;var f=["linebreak"];for(var d=0;d")}}},g)}else{var b="";if(typeof c=="undefined"){c=h.getParentElement();var o=c.tagName.toLowerCase();if(c&&(o=="legend")){c=c.parentElement;o=c.tagName.toLowerCase()}if(c&&!(o=="textarea"||o=="select"||o=="input"||o=="label"||o=="fieldset")){c=null}}if(c){k=c.tagName.toLowerCase();g.f_name=c.name;b=c.tagName;if(k=="input"){g.f_type=c.type;k=c.type}switch(k){case"textarea":g.f_cols=c.cols;g.f_rows=c.rows;g.f_text=c.innerHTML;g.f_wrap=c.getAttribute("wrap");g.f_readOnly=c.getAttribute("readOnly");g.f_disabled=c.getAttribute("disabled");g.f_tabindex=c.getAttribute("tabindex");g.f_accesskey=c.getAttribute("accesskey");break;case"select":g.f_size=parseInt(c.size);g.f_multiple=c.getAttribute("multiple");g.f_disabled=c.getAttribute("disabled");g.f_tabindex=c.getAttribute("tabindex");var l=new Array();for(var d=0;d<=c.options.length-1;d++){l[d]=new n(c.options[d].text,c.options[d].value)}g.f_options=l;break;case"text":case"password":g.f_value=c.value;g.f_size=c.size;g.f_maxLength=c.maxLength;g.f_readOnly=c.getAttribute("readOnly");g.f_disabled=c.getAttribute("disabled");g.f_tabindex=c.getAttribute("tabindex");g.f_accesskey=c.getAttribute("accesskey");break;case"hidden":g.f_value=c.value;break;case"submit":case"reset":g.f_value=c.value;g.f_disabled=c.getAttribute("disabled");g.f_tabindex=c.getAttribute("tabindex");g.f_accesskey=c.getAttribute("accesskey");break;case"checkbox":case"radio":g.f_value=c.value;g.f_checked=c.checked;g.f_disabled=c.getAttribute("disabled");g.f_tabindex=c.getAttribute("tabindex");g.f_accesskey=c.getAttribute("accesskey");break;case"button":g.f_value=c.value;g.f_onclick=c.getAttribute("onclick");g.f_disabled=c.getAttribute("disabled");g.f_tabindex=c.getAttribute("tabindex");g.f_accesskey=c.getAttribute("accesskey");break;case"image":g.f_value=c.value;g.f_src=c.src;g.f_disabled=c.getAttribute("disabled");g.f_tabindex=c.getAttribute("tabindex");g.f_accesskey=c.getAttribute("accesskey");break;case"file":g.f_disabled=c.getAttribute("disabled");g.f_tabindex=c.getAttribute("tabindex");g.f_accesskey=c.getAttribute("accesskey");break;case"label":g.f_text=c.innerHTML;g.f_for=c.getAttribute("for");g.f_accesskey=c.getAttribute("accesskey");break;case"fieldset":if(c.firstChild.tagName.toLowerCase()=="legend"){g.f_text=c.firstChild.innerHTML}else{g.f_text=""}break}}else{g.f_name="";switch(e){case"textarea":case"select":case"label":case"fieldset":b=e;break;default:b="input";g.f_type=e;break}g.f_options="";g.f_cols="20";g.f_rows="4";g.f_multiple="false";g.f_value="";g.f_size="";g.f_maxLength="";g.f_checked="";g.f_src="";g.f_onclick="";g.f_wrap="";g.f_readOnly="false";g.f_disabled="false";g.f_tabindex="";g.f_accesskey="";g.f_for="";g.f_text="";g.f_legend=""}h._popupDialog("plugin://Forms/"+b+".html",function(q){if(q){if(q.f_cols){if(isNaN(parseInt(q.f_cols,10))||parseInt(q.f_cols,10)<=0){q.f_cols=""}}if(q.f_rows){if(isNaN(parseInt(q.f_rows,10))||parseInt(q.f_rows,10)<=0){q.f_rows=""}}if(q.f_size){if(isNaN(parseInt(q.f_size,10))||parseInt(q.f_size,10)<=0){q.f_size=""}}if(q.f_maxlength){if(isNaN(parseInt(q.f_maxLength,10))||parseInt(q.f_maxLength,10)<=0){q.f_maxLength=""}}if(c){for(field in q){if((field=="f_text")||(field=="f_options")||(field=="f_onclick")||(field=="f_checked")){continue}if(q[field]!=""){c.setAttribute(field.substring(2,20),q[field])}else{c.removeAttribute(field.substring(2,20))}}if(k=="textarea"){c.innerHTML=q.f_text}else{if(k=="select"){c.options.length=0;var p=q.f_options;for(d=0;d<=p.length-1;d++){c.options[d]=new Option(p[d].text,p[d].value)}}else{if(k=="label"){c.innerHTML=q.f_text}else{if(k=="fieldset"){if(g.f_text!=""){if(c.firstChild.tagName.toLowerCase()=="legend"){c.firstChild.innerHTML=q.f_text}}else{}}else{if((k=="checkbox")||(k=="radio")){if(q.f_checked!=""){c.checked=true}else{c.checked=false}}else{if(q.f_onclick){c.onclick="";if(q.f_onclick!=""){c.onclick=q.f_onclick}}}}}}}}else{var i="";for(field in q){if(!q[field]){continue}if((q[field]=="")||(field=="f_text")||(field=="f_options")){continue}i+=" "+field.substring(2,20)+'="'+q[field]+'"'}if(k=="textarea"){i=""+q.f_text+""}else{if(k=="select"){i="";var p=q.f_options;for(d=0;d<=p.length-1;d++){i+='"}i+=""}else{if(k=="label"){i=""+q.f_text+""}else{if(k=="fieldset"){i="";if(q.f_legend!=""){i+=""+q.f_text+""}i+=""}else{i='"}}}}h.insertHTML(i)}}},g)}}; \ No newline at end of file +// Form plugin for Xinha +// Distributed under the same terms as HTMLArea itself. +// This notice MUST stay intact for use (see license.txt). + + +function Forms(editor) { + this.editor = editor; + var cfg = editor.config; + var bl = Forms.btnList; + var self = this; + // register the toolbar buttons provided by this plugin + var toolbar = ["linebreak"]; + for (var i = 0; i < bl.length; ++i) { + var btn = bl[i]; + if (!btn) { + toolbar.push("separator"); + } else { + var id = btn[0]; + if (i<3) + cfg.registerButton(id, this._lc(btn[1]), editor.imgURL("ed_" + btn[0] + ".gif", "Forms"), false, + function(editor, id) { + // dispatch button press event + self.buttonPress(editor, id); + }); + else + cfg.registerButton(id, this._lc(btn[1]), editor.imgURL("ed_" + btn[0] + ".gif", "Forms"), false, + function(editor, id) { + // dispatch button press event + self.buttonPress(editor, id); + },"form"); + toolbar.push(id); + } + } + // add a new line in the toolbar + cfg.toolbar.push(toolbar); +} + +Forms._pluginInfo = { + name : "Forms", + origin : "version: 1.0, by Nelson Bright, BrightWork, Inc., http://www.brightworkweb.com", + version : "2.0", + developer : "Udo Schmal", + developer_url : "", + sponsor : "L.N.Schaffrath NeueMedien", + sponsor_url : "http://www.schaffrath-neuemedien.de/", + c_owner : "Udo Schmal & Schaffrath-NeueMedien", + license : "htmlArea" +}; + +// the list of buttons added by this plugin +Forms.btnList = [ + // form properties button + null, // separator + ["form", "Form"], + null, // separator + // form elements + ["textarea", "Textarea"], + ["select", "Selection Field"], + ["checkbox", "Checkbox"], + ["radio", "Radio Button"], + ["text", "Text Field"], + ["password", "Password Field"], + ["file", "File Field"], + ["button", "Button"], + ["submit", "Submit Button"], + ["reset", "Reset Button"], + ["image", "Image Button"], + ["hidden", "Hidden Field"], + ["label", "Label"], + ["fieldset", "Field Set"] + ]; + +Forms.prototype._lc = function(string) { + return Xinha._lc(string, 'Forms'); +}; + +Forms.prototype.onGenerate = function() { + this.editor.addEditorStylesheet(Xinha.getPluginDir("Forms") + '/forms.css'); +}; + +Forms.prototype.buttonPress = function(editor,button_id, node) { + function optionValues(text,value) { + this.text = text; + this.value = value; + } + var outparam = new Object(); + var type = button_id; + var sel = editor._getSelection(); + var range = editor._createRange(sel); + if (button_id=="form") { //Form + // see if selection is inside an existing 'form' tag + var pe = editor.getParentElement(); + var frm = null; + while (pe && (pe.nodeType == 1) && (pe.tagName.toLowerCase() != 'body')) { + if(pe.tagName.toLowerCase() == "form") { + frm = pe; + break; + } else + pe = pe.parentNode; + } + if (frm) { + outparam.f_name = frm.name; + outparam.f_action = frm.action; + outparam.f_method = frm.method; + outparam.f_enctype = frm.enctype; + outparam.f_target = frm.target; + } else {; + outparam.f_name = ""; + outparam.f_action = ""; + outparam.f_method = ""; + outparam.f_enctype = ""; + outparam.f_target = ""; + } + editor._popupDialog("plugin://Forms/form", function(param) { + if (param) { + if(frm) { + frm.name = param["f_name"]; + frm.setAttribute("action", param["f_action"]); + frm.setAttribute("method", param["f_method"]); + frm.setAttribute("enctype",param["f_enctype"]); + frm.setAttribute("target", param["f_target"]); + } else { + frm = ''); + } + } + }, outparam); + + } else { // form element (checkbox, radio, text, password, textarea, select, button, submit, reset, image, hidden) + var tagName = ""; + // see if selection is an form element + if (typeof node == "undefined") { + node = editor.getParentElement(); + var tag = node.tagName.toLowerCase() + if (node && (tag == "legend")) { + node = node.parentElement; + tag = node.tagName.toLowerCase(); + } + if (node && !(tag == "textarea" || tag == "select" || tag == "input" || tag == "label" || tag == "fieldset")) + node = null; + } + + if(node) { + type = node.tagName.toLowerCase(); + outparam.f_name = node.name; + tagName = node.tagName; + if (type == "input") { + outparam.f_type = node.type; + type = node.type; + } + switch (type) { + case "textarea": + outparam.f_cols = node.cols; + outparam.f_rows = node.rows; + outparam.f_text = node.innerHTML; + outparam.f_wrap = node.getAttribute("wrap"); + outparam.f_readOnly = node.getAttribute("readOnly"); + outparam.f_disabled = node.getAttribute("disabled"); + outparam.f_tabindex = node.getAttribute("tabindex"); + outparam.f_accesskey = node.getAttribute("accesskey"); + break; + case "select": + outparam.f_size = parseInt(node.size); + outparam.f_multiple = node.getAttribute("multiple"); + outparam.f_disabled = node.getAttribute("disabled"); + outparam.f_tabindex = node.getAttribute("tabindex"); + var a_options = new Array(); + for (var i=0; i<=node.options.length-1; i++) { + a_options[i] = new optionValues(node.options[i].text, node.options[i].value); + } + outparam.f_options = a_options; + break; + case "text": + case "password": + outparam.f_value = node.value; + outparam.f_size = node.size; + outparam.f_maxLength = node.maxLength; + outparam.f_readOnly = node.getAttribute("readOnly"); + outparam.f_disabled = node.getAttribute("disabled"); + outparam.f_tabindex = node.getAttribute("tabindex"); + outparam.f_accesskey = node.getAttribute("accesskey"); + break; + case "hidden": + outparam.f_value = node.value; + break; + case "submit": + case "reset": + outparam.f_value = node.value; + outparam.f_disabled = node.getAttribute("disabled"); + outparam.f_tabindex = node.getAttribute("tabindex"); + outparam.f_accesskey = node.getAttribute("accesskey"); + break; + case "checkbox": + case "radio": + outparam.f_value = node.value; + outparam.f_checked = node.checked; + outparam.f_disabled = node.getAttribute("disabled"); + outparam.f_tabindex = node.getAttribute("tabindex"); + outparam.f_accesskey = node.getAttribute("accesskey"); + break; + case "button": + outparam.f_value = node.value; + outparam.f_onclick = node.getAttribute("onclick"); + outparam.f_disabled = node.getAttribute("disabled"); + outparam.f_tabindex = node.getAttribute("tabindex"); + outparam.f_accesskey = node.getAttribute("accesskey"); + break; + case "image": + outparam.f_value = node.value; + outparam.f_src = node.src; + outparam.f_disabled = node.getAttribute("disabled"); + outparam.f_tabindex = node.getAttribute("tabindex"); + outparam.f_accesskey = node.getAttribute("accesskey"); + break; + case "file": + outparam.f_disabled = node.getAttribute("disabled"); + outparam.f_tabindex = node.getAttribute("tabindex"); + outparam.f_accesskey = node.getAttribute("accesskey"); + break; + case "label": + outparam.f_text = node.innerHTML; + outparam.f_for = node.getAttribute("for"); + outparam.f_accesskey = node.getAttribute("accesskey"); + break; + case "fieldset": + if(node.firstChild.tagName.toLowerCase()=="legend") + outparam.f_text = node.firstChild.innerHTML; + else + outparam.f_text = ""; + break; + } + } else { + outparam.f_name = ""; + switch (button_id) { + case "textarea": + case "select": + case "label": + case "fieldset": + tagName = button_id; + break; + default: + tagName = "input"; + outparam.f_type = button_id; + break; + } + outparam.f_options = ""; + outparam.f_cols = "20"; + outparam.f_rows = "4"; + outparam.f_multiple = "false"; + outparam.f_value = ""; + outparam.f_size = ""; + outparam.f_maxLength = ""; + outparam.f_checked = ""; + outparam.f_src = ""; + outparam.f_onclick = ""; + outparam.f_wrap = ""; + outparam.f_readOnly = "false"; + outparam.f_disabled = "false"; + outparam.f_tabindex = ""; + outparam.f_accesskey = ""; + outparam.f_for = ""; + outparam.f_text = ""; + outparam.f_legend = ""; + } + editor._popupDialog("plugin://Forms/" + tagName + ".html", function(param) { + if (param) { + if(param["f_cols"]) + if (isNaN(parseInt(param["f_cols"],10)) || parseInt(param["f_cols"],10) <= 0) + param["f_cols"] = ""; + if(param["f_rows"]) + if(isNaN(parseInt(param["f_rows"],10)) || parseInt(param["f_rows"],10) <= 0) + param["f_rows"] = ""; + if(param["f_size"]) + if(isNaN(parseInt(param["f_size"],10)) || parseInt(param["f_size"],10) <= 0) + param["f_size"] = ""; + if(param["f_maxlength"]) + if(isNaN(parseInt(param["f_maxLength"],10)) || parseInt(param["f_maxLength"],10) <= 0) + param["f_maxLength"] = ""; + if(node) { + //prepare existing Element + for (field in param) { + //alert(field.substring(2,20) + '=' + param[field]); //to be silent! by htanaka + if ((field=="f_text") || (field=="f_options") || (field=="f_onclick") || (field=="f_checked"))continue; + if (param[field] != "") + node.setAttribute(field.substring(2,20), param[field]); + else + node.removeAttribute(field.substring(2,20)); + } + if (type == "textarea") { + node.innerHTML = param["f_text"]; + } else if(type == "select") { + node.options.length = 0; + var optionsList = param["f_options"]; + for (i=0; i<= optionsList.length-1; i++) { + node.options[i] = new Option(optionsList[i].text, optionsList[i].value) + } + } else if(type == "label") { + node.innerHTML = param["f_text"]; + } else if(type == "fieldset") { + if(outparam.f_text != "") { + if(node.firstChild.tagName.toLowerCase()=="legend") + node.firstChild.innerHTML = param["f_text"]; + } else {}// not implemented jet + } else if((type == "checkbox") || (type == "radio")) { //input + if(param["f_checked"]!="") + node.checked = true; + else + node.checked = false; + } else { + if(param["f_onclick"]){ + node.onclick = ""; + if(param["f_onclick"]!="") + node.onclick = param["f_onclick"]; + } + } + } else { + //create Element + var text = ""; + for (field in param) { + if (!param[field]) continue; + if ((param[field]=="") || (field=="f_text")|| (field=="f_options"))continue; + text += " " + field.substring(2,20) + '="' + param[field] + '"'; + } + + if(type == "textarea") { + text = '' + param["f_text"] + ''; + } else if(type == "select") { + text = ''; + var optionsList = param["f_options"]; + for (i=0; i<= optionsList.length-1; i++) { + text += ''; + } + text += ''; + } else if(type == "label") { + text = '' + param["f_text"] + ''; + } else if(type == "fieldset") { + text = ''; + if (param["f_legend"] != "") text += '' + param["f_text"] + ''; + text += ''; + } else { + text = ''; + } + editor.insertHTML(text); + } + } + }, outparam); + } +}; \ No newline at end of file Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/FullPage/FullPage.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/FullPage/FullPage.js,v diff -u -r1.2 -r1.3 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/FullPage/FullPage.js 27 Mar 2009 08:20:44 -0000 1.2 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/plugins/FullPage/FullPage.js 23 May 2010 11:58:33 -0000 1.3 @@ -1,3 +1,209 @@ -/* 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 */ -function FullPage(c){this.editor=c;var a=c.config;a.fullPage=true;var b=this;a.registerButton("FP-docprop",this._lc("Document properties"),c.imgURL("docprop.gif","FullPage"),false,function(d,e){b.buttonPress(d,e)});a.addToolbarElement(["separator","FP-docprop"],"separator",-1)}FullPage._pluginInfo={name:"FullPage",version:"1.0",developer:"Mihai Bazon",developer_url:"http://dynarch.com/mishoo/",c_owner:"Mihai Bazon",sponsor:"Thycotic Software Ltd.",sponsor_url:"http://thycotic.com",license:"htmlArea"};FullPage.prototype._lc=function(a){return Xinha._lc(a,"FullPage")};FullPage.prototype.buttonPress=function(e,a){var q=this;switch(a){case"FP-docprop":var k=e._doc;var n=k.getElementsByTagName("link");var l="";var j="";var d="";var m="";var b="";for(var c=n.length;--c>=0;){var g=n[c];if(/stylesheet/i.test(g.rel)){if(/alternate/i.test(g.rel)){j=g.href}else{l=g.href}}}var f=k.getElementsByTagName("meta");for(var c=f.length;--c>=0;){var p=f[c];if(/content-type/i.test(p.httpEquiv)){r=/^text\/html; *charset=(.*)$/i.exec(p.content);b=r[1]}else{if((/keywords/i.test(p.name))||(/keywords/i.test(p.id))){d=p.content}else{if((/description/i.test(p.name))||(/description/i.test(p.id))){m=p.content}}}}var h=k.getElementsByTagName("title")[0];h=h?h.innerHTML:"";var o={f_doctype:e.doctype,f_title:h,f_body_bgcolor:Xinha._colorToRgb(k.body.style.backgroundColor),f_body_fgcolor:Xinha._colorToRgb(k.body.style.color),f_base_style:l,f_alt_style:j,f_charset:b,f_keywords:d,f_description:m,editor:e};e._popupDialog("plugin://FullPage/docprop",function(i){q.setDocProp(i)},o);break}};FullPage.prototype.setDocProp=function(q){var j="";var u=this.editor._doc;var d=u.getElementsByTagName("head")[0];var b=u.getElementsByTagName("link");var a=u.getElementsByTagName("meta");var o=null;var n=null;var c=null;var f=null;var g=null;var l=null;for(var m=b.length;--m>=0;){var e=b[m];if(/stylesheet/i.test(e.rel)){if(/alternate/i.test(e.rel)){n=e}else{o=e}}}for(var m=a.length;--m>=0;){var h=a[m];if(/content-type/i.test(h.httpEquiv)){r=/^text\/html; *charset=(.*)$/i.exec(h.content);c=r[1];f=h}else{if((/keywords/i.test(h.name))||(/keywords/i.test(h.id))){g=h}else{if((/description/i.test(h.name))||(/description/i.test(h.id))){l=h}}}}function k(v){var i=u.createElement("link");i.rel=v?"alternate stylesheet":"stylesheet";d.appendChild(i);return i}function p(i,v,w){var x=u.createElement("meta");if(i!=""){x.httpEquiv=i}if(v!=""){x.name=v}if(v!=""){x.id=v}x.content=w;d.appendChild(x);return x}if(!o&&q.f_base_style){o=k(false)}if(q.f_base_style){o.href=q.f_base_style}else{if(o){d.removeChild(o)}}if(!n&&q.f_alt_style){n=k(true)}if(q.f_alt_style){n.href=q.f_alt_style}else{if(n){d.removeChild(n)}}if(f){d.removeChild(f);f=null}if(!f&&q.f_charset){f=p("Content-Type","","text/html; charset="+q.f_charset)}if(!g&&q.f_keywords){g=p("","keywords",q.f_keywords)}else{if(q.f_keywords){g.content=q.f_keywords}else{if(g){d.removeChild(g)}}}if(!l&&q.f_description){l=p("","description",q.f_description)}else{if(q.f_description){l.content=q.f_description}else{if(l){d.removeChild(l)}}}for(var m in q){var t=q[m];switch(m){case"f_title":var s=u.getElementsByTagName("title")[0];if(!s){s=u.createElement("title");d.appendChild(s)}else{while(node=s.lastChild){s.removeChild(node)}}if(!Xinha.is_ie){s.appendChild(u.createTextNode(t))}else{u.title=t}break;case"f_doctype":this.editor.setDoctype(t);break;case"f_body_bgcolor":u.body.style.backgroundColor=t;break;case"f_body_fgcolor":u.body.style.color=t;break}}}; \ No newline at end of file +// FullPage Plugin for HTMLArea-3.0 +// Implementation by Mihai Bazon. Sponsored by http://thycotic.com +// +// htmlArea v3.0 - Copyright (c) 2002 interactivetools.com, inc. +// This notice MUST stay intact for use (see license.txt). +// +// A free WYSIWYG editor replacement for