From 5a6ed3b0f12ee84d240874c747c0a106e04a594b Mon Sep 17 00:00:00 2001
From: Jared Hancock <jared@osticket.com>
Date: Thu, 3 Jul 2014 09:38:50 -0500
Subject: [PATCH] i18n: Translate javascript phrases

---
 bootstrap.php                 |   6 +-
 include/ajax.i18n.php         |   2 +
 include/class.translation.php |  32 ++++++----
 js/osticket.js                |  12 +++-
 js/redactor-fonts.js          |   6 +-
 js/redactor-osticket.js       |   2 +-
 scp/admin.inc.php             |  17 ------
 scp/js/nicEdit.js             | 112 ----------------------------------
 scp/js/scp.js                 |  25 +++++---
 scp/js/ticket.js              |  15 +++--
 scp/js/upgrader.js            |  10 +--
 scp/orgs.php                  |   2 +-
 setup/cli/modules/i18n.php    | 100 ++++++++++++++++++++++++------
 13 files changed, 147 insertions(+), 194 deletions(-)
 delete mode 100644 scp/js/nicEdit.js

diff --git a/bootstrap.php b/bootstrap.php
index c782bf208..98583ed09 100644
--- a/bootstrap.php
+++ b/bootstrap.php
@@ -134,12 +134,12 @@ class Bootstrap {
             //Die gracefully on upgraded v1.6 RC5 installation - otherwise script dies with confusing message.
             if(!strcasecmp(basename($_SERVER['SCRIPT_NAME']), 'settings.php'))
                 Http::response(500,
-                    _S('Please rename config file include/settings.php to include/ost-config.php to continue!'));
+                    'Please rename config file include/settings.php to include/ost-config.php to continue!');
         } elseif(file_exists(ROOT_DIR.'setup/'))
             Http::redirect(ROOT_PATH.'setup/');
 
         if(!$configfile || !file_exists($configfile))
-            Http::response(500,'<b>'._S('Error loading settings. Contact admin.').'</b>');
+            Http::response(500,'<b>Error loading settings. Contact admin.</b>');
 
         require($configfile);
         define('CONFIG_FILE',$configfile); //used in admin.php to check perm.
@@ -271,7 +271,7 @@ class Bootstrap {
     function croak($message) {
         $msg = $message."\n\n".THISPAGE;
         Mailer::sendmail(ADMIN_EMAIL, 'osTicket Fatal Error', $msg,
-            '"'.'osTicket Alerts'.sprintf('" <%s>', ADMIN_EMAIL));
+            sprintf('"osTicket Alerts"<%s>', ADMIN_EMAIL));
         //Display generic error to the user
         Http::response(500, "<b>Fatal Error:</b> Contact system administrator.");
     }
diff --git a/include/ajax.i18n.php b/include/ajax.i18n.php
index 099004d8b..3aeaefa13 100644
--- a/include/ajax.i18n.php
+++ b/include/ajax.i18n.php
@@ -26,6 +26,8 @@ class i18nAjaxAPI extends AjaxController {
         case 'js':
             $data = $i18n->getTemplate('js/redactor.js')->getRawData();
             $data .= $i18n->getTemplate('js/jquery.ui.datepicker.js')->getRawData();
+            // Strings from various javascript files
+            $data .= $i18n->getTemplate('js/osticket-strings.js')->getRawData();
             header('Content-Type: text/javascript; charset=UTF-8');
             break;
         default:
diff --git a/include/class.translation.php b/include/class.translation.php
index 1a050167f..dacd54872 100644
--- a/include/class.translation.php
+++ b/include/class.translation.php
@@ -456,10 +456,14 @@ class FileReader {
   var $_length;
 
   function FileReader($filename) {
-    if (file_exists($filename)) {
+    if (is_resource($filename)) {
+        $this->_length = strlen(stream_get_contents($filename));
+        rewind($filename);
+        $this->_fd = $filename;
+    }
+    elseif (file_exists($filename)) {
 
       $this->_length=filesize($filename);
-      $this->_pos = 0;
       $this->_fd = fopen($filename,'rb');
       if (!$this->_fd) {
         $this->error = 3; // Cannot read file, probably permissions
@@ -469,6 +473,7 @@ class FileReader {
       $this->error = 2; // File doesn't exist
       return false;
     }
+    $this->_pos = 0;
   }
 
   function read($bytes) {
@@ -529,6 +534,8 @@ class Translation extends gettext_reader {
 
     var $charset;
 
+    const META_HEADER = 0;
+
     function __construct($reader, $charset=false) {
         if (!$reader || $reader->error)
             return parent::__construct($reader);
@@ -612,7 +619,7 @@ class Translation extends gettext_reader {
         }
 
         // Add in some meta-data
-        $table[0] = array(
+        $table[self::META_HEADER] = array(
             'Revision' => $reader->revision,      // From the MO
             'Total-Strings' => $reader->total,    // From the MO
             'Table-Size' => count($table),      // Sanity check for later
@@ -630,7 +637,6 @@ if (!defined('LC_MESSAGES')) {
     define('LC_MESSAGES',	6);
 }
 
-/* Class to hold a single domain included in $text_domains. */
 class TextDomain {
     var $l10n = array();
     var $path;
@@ -685,7 +691,7 @@ class TextDomain {
         else
             $lang = Internationalization::getDefaultLanguage();
 
-        // User-specific translations
+        // Define locale for C-libraries
         putenv('LC_ALL=' . $lang);
         self::setLocale(LC_ALL, $lang);
     }
@@ -697,7 +703,7 @@ class TextDomain {
     /**
      * Returns passed in $locale, or environment variable $LANG if $locale == ''.
      */
-    static function get_default_locale($locale) {
+    static function get_default_locale($locale='') {
         if ($locale == '') // emulate variable support
             return getenv('LANG');
         else
@@ -817,17 +823,17 @@ function _dcpgettext($domain, $context, $msgid, $category) {
     return TextDomain::lookup($domain)->getTranslation($category)
         ->pgettext($context, $msgid);
 }
-function _npgettext($context, $singular, $plural) {
+function _npgettext($context, $singular, $plural, $n) {
     return TextDomain::lookup()->getTranslation()
-        ->npgettext($context, $singular, $plural);
+        ->npgettext($context, $singular, $plural, $n);
 }
-function _dnpgettext($domain, $context, $singular, $plural) {
+function _dnpgettext($domain, $context, $singular, $plural, $n) {
     return TextDomain::lookup($domain)->getTranslation()
-        ->npgettext($context, $singular, $plural);
+        ->npgettext($context, $singular, $plural, $n);
 }
-function _dcnpgettext($domain, $context, $singular, $plural, $category) {
+function _dcnpgettext($domain, $context, $singular, $plural, $category, $n) {
     return TextDomain::lookup($domain)->getTranslation($category)
-        ->npgettext($context, $singular, $plural);
+        ->npgettext($context, $singular, $plural, $n);
 }
 
 
@@ -836,5 +842,5 @@ do {
   if (empty ($_SERVER['argc']) || $_SERVER['argc'] < 2) break;
   if (empty ($_SERVER['PHP_SELF']) || FALSE === strpos ($_SERVER['PHP_SELF'], basename(__FILE__)) ) break;
   $file = $argv[1];
-  quick_gettext_reader::buildHashFile($file);
+  Translation::buildHashFile($file);
 } while (0);
diff --git a/js/osticket.js b/js/osticket.js
index f5fa852e8..ce9d8d7fb 100644
--- a/js/osticket.js
+++ b/js/osticket.js
@@ -27,7 +27,7 @@ $(document).ready(function(){
             fObj.data('changed', true);
             $('input[type=submit]', fObj).css('color', 'red');
             $(window).bind('beforeunload', function(e) {
-                return "Are you sure you want to leave? Any changes or info you've entered will be discarded!";
+                return __("Are you sure you want to leave? Any changes or info you've entered will be discarded!");
              });
         }
        });
@@ -148,7 +148,7 @@ $(document).ready(function(){
         extra.append($('<a>')
           .addClass("action-button show-images")
           .css({'font-weight':'normal'})
-          .text(' Show Images')
+          .text(' ' + __('Show Images'))
           .click(function(ev) {
             imgs.each(function(i, img) {
               showNonLocalImage(img);
@@ -208,9 +208,15 @@ showImagesInline = function(urls, thread_id) {
                     }
                 ).append($('<div class="caption">')
                     .append('<span class="filename">'+info.filename+'</span>')
-                    .append('<a href="'+info.download_url+'" class="action-button"><i class="icon-download-alt"></i> Download</a>')
+                    .append('<a href="'+info.download_url+'" class="action-button"><i class="icon-download-alt"></i> ' + __('Download') + '</a>')
                 );
             e.data('wrapped', true);
         }
     });
 }
+
+function __(s) {
+  if ($.strings && $.strings[s])
+    return $.strings[s];
+  return s;
+}
diff --git a/js/redactor-fonts.js b/js/redactor-fonts.js
index fc033523a..28cbf3495 100644
--- a/js/redactor-fonts.js
+++ b/js/redactor-fonts.js
@@ -3,7 +3,7 @@ if (!RedactorPlugins) var RedactorPlugins = {};
 RedactorPlugins.fontfamily = {
     init: function ()
     {
-        var fonts = [ 'Arial', 'Helvetica', 'Georgia', 'Times New Roman', 'Monospace' ];
+        var fonts = [ 'Arial', 'Helvetica', 'Georgia', 'Times New Roman', __('Monospace') ];
         var that = this;
         var dropdown = {};
 
@@ -14,7 +14,7 @@ RedactorPlugins.fontfamily = {
 
         dropdown['remove'] = { title: 'Remove font', callback: function() { that.resetFontfamily(); }};
 
-        this.buttonAddBefore('bold', 'fontfamily', 'Change font family', false, dropdown);
+        this.buttonAddBefore('bold', 'fontfamily', __('Change font family'), false, dropdown);
     },
     setFontfamily: function (value)
     {
@@ -122,7 +122,7 @@ RedactorPlugins.fontsize = {
 
 		dropdown['remove'] = { title: 'Remove font size', callback: function() { that.resetFontsize(); } };
 
-		this.buttonAddAfter('formatting', 'fontsize', 'Change font size', false, dropdown);
+		this.buttonAddAfter('formatting', 'fontsize', __('Change font size'), false, dropdown);
 	},
 	setFontsize: function(size)
 	{
diff --git a/js/redactor-osticket.js b/js/redactor-osticket.js
index 6df5edbe6..1aa1dca23 100644
--- a/js/redactor-osticket.js
+++ b/js/redactor-osticket.js
@@ -301,7 +301,7 @@ $(document).ajaxError(function(event, request, settings) {
             }
         });
         $('#overlay').show();
-        alert('Unable to save draft. Refresh the current page to restore and continue your draft.');
+        alert(__('Unable to save draft. Refresh the current page to restore and continue your draft.'));
         $('#overlay').hide();
     }
 });
diff --git a/scp/admin.inc.php b/scp/admin.inc.php
index b1d3af932..434589aca 100644
--- a/scp/admin.inc.php
+++ b/scp/admin.inc.php
@@ -57,23 +57,6 @@ if($ost->isUpgradePending()) {
 
     if(!$sysnotice && ini_get('register_globals'))
         $sysnotice=__('Please consider turning off register globals if possible');
-	if($use_php_gettext==true&&!function_exists('mb_detect_encoding'))
-	{
-		$sysnotice='mbstring extension is required to use php_gettext';
-	}
-	if($use_php_gettext==true&&function_exists('mb_detect_encoding'))
-	{
-		$f = fopen(INCLUDE_DIR.'locale/'.$language.'/LC_MESSAGES/messages.mo', 'r');
-		$meta = stream_get_meta_data($f);
-		if($meta['mode']==NULL)
-		{
-			$sysnotice='The translation file "include/locale/'.$language.'/LC_MESSAGES/messages.mo" isn\'t readable, check permissions.';
-		}
-		else
-		{
-			fclose($f);
-		}
-	}
 }
 
 //System notice displayed as a warning (if any).
diff --git a/scp/js/nicEdit.js b/scp/js/nicEdit.js
deleted file mode 100644
index 37b66e2a3..000000000
--- a/scp/js/nicEdit.js
+++ /dev/null
@@ -1,112 +0,0 @@
-/* NicEdit - Micro Inline WYSIWYG
- * Copyright 2007-2008 Brian Kirchoff
- *
- * NicEdit is distributed under the terms of the MIT license
- * For more information visit http://nicedit.com/
- * Do not remove this copyright message
- */
-var bkExtend=function(){var A=arguments;if(A.length==1){A=[this,A[0]]}for(var B in A[1]){A[0][B]=A[1][B]}return A[0]};function bkClass(){}bkClass.prototype.construct=function(){};bkClass.extend=function(C){var A=function(){if(arguments[0]!==bkClass){return this.construct.apply(this,arguments)}};var B=new this(bkClass);bkExtend(B,C);A.prototype=B;A.extend=this.extend;return A};var bkElement=bkClass.extend({construct:function(B,A){if(typeof (B)=="string"){B=(A||document).createElement(B)}B=$BK(B);return B},appendTo:function(A){A.appendChild(this);return this},appendBefore:function(A){A.parentNode.insertBefore(this,A);return this},addEvent:function(B,A){bkLib.addEvent(this,B,A);return this},setContent:function(A){this.innerHTML=A;return this},pos:function(){var C=curtop=0;var B=obj=this;if(obj.offsetParent){do{C+=obj.offsetLeft;curtop+=obj.offsetTop}while(obj=obj.offsetParent)}var A=(!window.opera)?parseInt(this.getStyle("border-width")||this.style.border)||0:0;return[C+A,curtop+A+this.offsetHeight]},noSelect:function(){bkLib.noSelect(this);return this},parentTag:function(A){var B=this;do{if(B&&B.nodeName&&B.nodeName.toUpperCase()==A){return B}B=B.parentNode}while(B);return false},hasClass:function(A){return this.className.match(new RegExp("(\\s|^)nicEdit-"+A+"(\\s|$)"))},addClass:function(A){if(!this.hasClass(A)){this.className+=" nicEdit-"+A}return this},removeClass:function(A){if(this.hasClass(A)){this.className=this.className.replace(new RegExp("(\\s|^)nicEdit-"+A+"(\\s|$)")," ")}return this},setStyle:function(A){var B=this.style;for(var C in A){switch(C){case"float":B.cssFloat=B.styleFloat=A[C];break;case"opacity":B.opacity=A[C];B.filter="alpha(opacity="+Math.round(A[C]*100)+")";break;case"className":this.className=A[C];break;default:B[C]=A[C]}}return this},getStyle:function(A,C){var B=(!C)?document.defaultView:C;if(this.nodeType==1){return(B&&B.getComputedStyle)?B.getComputedStyle(this,null).getPropertyValue(A):this.currentStyle[bkLib.camelize(A)]}},remove:function(){this.parentNode.removeChild(this);return this},setAttributes:function(A){for(var B in A){this[B]=A[B]}return this}});var bkLib={isMSIE:(navigator.appVersion.indexOf("MSIE")!=-1),addEvent:function(C,B,A){(C.addEventListener)?C.addEventListener(B,A,false):C.attachEvent("on"+B,A)},toArray:function(C){var B=C.length,A=new Array(B);while(B--){A[B]=C[B]}return A},noSelect:function(B){if(B.setAttribute&&B.nodeName.toLowerCase()!="input"&&B.nodeName.toLowerCase()!="textarea"){B.setAttribute("unselectable","on")}for(var A=0;A<B.childNodes.length;A++){bkLib.noSelect(B.childNodes[A])}},camelize:function(A){return A.replace(/\-(.)/g,function(B,C){return C.toUpperCase()})},inArray:function(A,B){return(bkLib.search(A,B)!=null)},search:function(A,C){for(var B=0;B<A.length;B++){if(A[B]==C){return B}}return null},cancelEvent:function(A){A=A||window.event;if(A.preventDefault&&A.stopPropagation){A.preventDefault();A.stopPropagation()}return false},domLoad:[],domLoaded:function(){if(arguments.callee.done){return }arguments.callee.done=true;for(i=0;i<bkLib.domLoad.length;i++){bkLib.domLoad[i]()}},onDomLoaded:function(A){this.domLoad.push(A);if(document.addEventListener){document.addEventListener("DOMContentLoaded",bkLib.domLoaded,null)}else{if(bkLib.isMSIE){document.write("<style>.nicEdit-main p { margin: 0; }</style><script id=__ie_onload defer "+((location.protocol=="https:")?"src='javascript:void(0)'":"src=//0")+"><\/script>");$BK("__ie_onload").onreadystatechange=function(){if(this.readyState=="complete"){bkLib.domLoaded()}}}}window.onload=bkLib.domLoaded}};function $BK(A){if(typeof (A)=="string"){A=document.getElementById(A)}return(A&&!A.appendTo)?bkExtend(A,bkElement.prototype):A}var bkEvent={addEvent:function(A,B){if(B){this.eventList=this.eventList||{};this.eventList[A]=this.eventList[A]||[];this.eventList[A].push(B)}return this},fireEvent:function(){var A=bkLib.toArray(arguments),C=A.shift();if(this.eventList&&this.eventList[C]){for(var B=0;B<this.eventList[C].length;B++){this.eventList[C][B].apply(this,A)}}}};function __(A){return A}Function.prototype.closure=function(){var A=this,B=bkLib.toArray(arguments),C=B.shift();return function(){if(typeof (bkLib)!="undefined"){return A.apply(C,B.concat(bkLib.toArray(arguments)))}}};Function.prototype.closureListener=function(){var A=this,C=bkLib.toArray(arguments),B=C.shift();return function(E){E=E||window.event;if(E.target){var D=E.target}else{var D=E.srcElement}return A.apply(B,[E,D].concat(C))}};
-
-
-
-var nicEditorConfig = bkClass.extend({
-	buttons : {
-		'bold' : {name : __('Click to Bold'), command : 'Bold', tags : ['B','STRONG'], css : {'font-weight' : 'bold'}, key : 'b'},
-		'italic' : {name : __('Click to Italic'), command : 'Italic', tags : ['EM','I'], css : {'font-style' : 'italic'}, key : 'i'},
-		'underline' : {name : __('Click to Underline'), command : 'Underline', tags : ['U'], css : {'text-decoration' : 'underline'}, key : 'u'},
-		'left' : {name : __('Left Align'), command : 'justifyleft', noActive : true},
-		'center' : {name : __('Center Align'), command : 'justifycenter', noActive : true},
-		'right' : {name : __('Right Align'), command : 'justifyright', noActive : true},
-		'justify' : {name : __('Justify Align'), command : 'justifyfull', noActive : true},
-		'ol' : {name : __('Insert Ordered List'), command : 'insertorderedlist', tags : ['OL']},
-		'ul' : 	{name : __('Insert Unordered List'), command : 'insertunorderedlist', tags : ['UL']},
-		'subscript' : {name : __('Click to Subscript'), command : 'subscript', tags : ['SUB']},
-		'superscript' : {name : __('Click to Superscript'), command : 'superscript', tags : ['SUP']},
-		'strikethrough' : {name : __('Click to Strike Through'), command : 'strikeThrough', css : {'text-decoration' : 'line-through'}},
-		'removeformat' : {name : __('Remove Formatting'), command : 'removeformat', noActive : true},
-		'indent' : {name : __('Indent Text'), command : 'indent', noActive : true},
-		'outdent' : {name : __('Remove Indent'), command : 'outdent', noActive : true},
-		'hr' : {name : __('Horizontal Rule'), command : 'insertHorizontalRule', noActive : true}
-	},
-	iconsPath : '../nicEditorIcons.gif',
-	buttonList : ['xhtml','save','bold','italic','underline','left','center','right','justify','ol','ul','fontSize','fontFamily','fontFormat','indent','outdent','image','upload','link','unlink','forecolor','bgcolor'],
-	iconList : {"xhtml":1,"bgcolor":2,"forecolor":3,"bold":4,"center":5,"hr":6,"indent":7,"italic":8,"justify":9,"left":10,"ol":11,"outdent":12,"removeformat":13,"right":14,"save":25,"strikethrough":16,"subscript":17,"superscript":18,"ul":19,"underline":20,"image":21,"link":22,"unlink":23,"close":24,"arrow":26}
-
-});
-;
-var nicEditors={nicPlugins:[],editors:[],registerPlugin:function(B,A){this.nicPlugins.push({p:B,o:A})},allTextAreas:function(C){var A=document.getElementsByTagName("textarea");for(var B=0;B<A.length;B++){nicEditors.editors.push(new nicEditor(C).panelInstance(A[B]))}return nicEditors.editors},findEditor:function(C){var B=nicEditors.editors;for(var A=0;A<B.length;A++){if(B[A].instanceById(C)){return B[A].instanceById(C)}}}};var nicEditor=bkClass.extend({construct:function(C){this.options=new nicEditorConfig();bkExtend(this.options,C);this.nicInstances=new Array();this.loadedPlugins=new Array();var A=nicEditors.nicPlugins;for(var B=0;B<A.length;B++){this.loadedPlugins.push(new A[B].p(this,A[B].o))}nicEditors.editors.push(this);bkLib.addEvent(document.body,"mousedown",this.selectCheck.closureListener(this))},panelInstance:function(B,C){B=this.checkReplace($BK(B));var A=new bkElement("DIV").setStyle({width:(parseInt(B.getStyle("width"))||B.clientWidth)+"px"}).appendBefore(B);this.setPanel(A);return this.addInstance(B,C)},checkReplace:function(B){var A=nicEditors.findEditor(B);if(A){A.removeInstance(B);A.removePanel()}return B},addInstance:function(B,C){B=this.checkReplace($BK(B));if(B.contentEditable||!!window.opera){var A=new nicEditorInstance(B,C,this)}else{var A=new nicEditorIFrameInstance(B,C,this)}this.nicInstances.push(A);return this},removeInstance:function(C){C=$BK(C);var B=this.nicInstances;for(var A=0;A<B.length;A++){if(B[A].e==C){B[A].remove();this.nicInstances.splice(A,1)}}},removePanel:function(A){if(this.nicPanel){this.nicPanel.remove();this.nicPanel=null}},instanceById:function(C){C=$BK(C);var B=this.nicInstances;for(var A=0;A<B.length;A++){if(B[A].e==C){return B[A]}}},setPanel:function(A){this.nicPanel=new nicEditorPanel($BK(A),this.options,this);this.fireEvent("panel",this.nicPanel);return this},nicCommand:function(B,A){if(this.selectedInstance){this.selectedInstance.nicCommand(B,A)}},getIcon:function(D,A){var C=this.options.iconList[D];var B=(A.iconFiles)?A.iconFiles[D]:"";return{backgroundImage:"url('"+((C)?this.options.iconsPath:B)+"')",backgroundPosition:((C)?((C-1)*-18):0)+"px 0px"}},selectCheck:function(C,A){var B=false;do{if(A.className&&A.className.indexOf("nicEdit")!=-1){return false}}while(A=A.parentNode);this.fireEvent("blur",this.selectedInstance,A);this.lastSelectedInstance=this.selectedInstance;this.selectedInstance=null;return false}});nicEditor=nicEditor.extend(bkEvent);
-var nicEditorInstance=bkClass.extend({isSelected:false,construct:function(G,D,C){this.ne=C;this.elm=this.e=G;this.options=D||{};newX=parseInt(G.getStyle("width"))||G.clientWidth;newY=parseInt(G.getStyle("height"))||G.clientHeight;this.initialHeight=newY-8;var H=(G.nodeName.toLowerCase()=="textarea");if(H||this.options.hasPanel){var B=(bkLib.isMSIE&&!((typeof document.body.style.maxHeight!="undefined")&&document.compatMode=="CSS1Compat"));var E={width:newX+"px",border:"1px solid #ccc",borderTop:0,overflowY:"auto",overflowX:"hidden"};E[(B)?"height":"maxHeight"]=(this.ne.options.maxHeight)?this.ne.options.maxHeight+"px":null;this.editorContain=new bkElement("DIV").setStyle(E).appendBefore(G);var A=new bkElement("DIV").setStyle({width:(newX-8)+"px",margin:"4px",minHeight:newY+"px"}).addClass("main").appendTo(this.editorContain);G.setStyle({display:"none"});A.innerHTML=G.innerHTML;if(H){A.setContent(G.value);this.copyElm=G;var F=G.parentTag("FORM");if(F){bkLib.addEvent(F,"submit",this.saveContent.closure(this))}}A.setStyle((B)?{height:newY+"px"}:{overflow:"hidden"});this.elm=A}this.ne.addEvent("blur",this.blur.closure(this));this.init();this.blur()},init:function(){this.elm.setAttribute("contentEditable","true");if(this.getContent()==""){this.setContent("<br />")}this.instanceDoc=document.defaultView;this.elm.addEvent("mousedown",this.selected.closureListener(this)).addEvent("keypress",this.keyDown.closureListener(this)).addEvent("focus",this.selected.closure(this)).addEvent("blur",this.blur.closure(this)).addEvent("keyup",this.selected.closure(this));this.ne.fireEvent("add",this)},remove:function(){this.saveContent();if(this.copyElm||this.options.hasPanel){this.editorContain.remove();this.e.setStyle({display:"block"});this.ne.removePanel()}this.disable();this.ne.fireEvent("remove",this)},disable:function(){this.elm.setAttribute("contentEditable","false")},getSel:function(){return(window.getSelection)?window.getSelection():document.selection},getRng:function(){var A=this.getSel();if(!A||A.rangeCount===0){return }return(A.rangeCount>0)?A.getRangeAt(0):A.createRange()},selRng:function(A,B){if(window.getSelection){B.removeAllRanges();B.addRange(A)}else{A.select()}},selElm:function(){var C=this.getRng();if(!C){return }if(C.startContainer){var D=C.startContainer;if(C.cloneContents().childNodes.length==1){for(var B=0;B<D.childNodes.length;B++){var A=D.childNodes[B].ownerDocument.createRange();A.selectNode(D.childNodes[B]);if(C.compareBoundaryPoints(Range.START_TO_START,A)!=1&&C.compareBoundaryPoints(Range.END_TO_END,A)!=-1){return $BK(D.childNodes[B])}}}return $BK(D)}else{return $BK((this.getSel().type=="Control")?C.item(0):C.parentElement())}},saveRng:function(){this.savedRange=this.getRng();this.savedSel=this.getSel()},restoreRng:function(){if(this.savedRange){this.selRng(this.savedRange,this.savedSel)}},keyDown:function(B,A){if(B.ctrlKey){this.ne.fireEvent("key",this,B)}},selected:function(C,A){if(!A&&!(A=this.selElm)){A=this.selElm()}if(!C.ctrlKey){var B=this.ne.selectedInstance;if(B!=this){if(B){this.ne.fireEvent("blur",B,A)}this.ne.selectedInstance=this;this.ne.fireEvent("focus",B,A)}this.ne.fireEvent("selected",B,A);this.isFocused=true;this.elm.addClass("selected")}return false},blur:function(){this.isFocused=false;this.elm.removeClass("selected")},saveContent:function(){if(this.copyElm||this.options.hasPanel){this.ne.fireEvent("save",this);(this.copyElm)?this.copyElm.value=this.getContent():this.e.innerHTML=this.getContent()}},getElm:function(){return this.elm},getContent:function(){this.content=this.getElm().innerHTML;this.ne.fireEvent("get",this);return this.content},setContent:function(A){this.content=A;this.ne.fireEvent("set",this);this.elm.innerHTML=this.content},nicCommand:function(B,A){document.execCommand(B,false,A)}});
-var nicEditorIFrameInstance=nicEditorInstance.extend({savedStyles:[],init:function(){var B=this.elm.innerHTML.replace(/^\s+|\s+$/g,"");this.elm.innerHTML="";(!B)?B="<br />":B;this.initialContent=B;this.elmFrame=new bkElement("iframe").setAttributes({src:"javascript:;",frameBorder:0,allowTransparency:"true",scrolling:"no"}).setStyle({height:"100px",width:"100%"}).addClass("frame").appendTo(this.elm);if(this.copyElm){this.elmFrame.setStyle({width:(this.elm.offsetWidth-4)+"px"})}var A=["font-size","font-family","font-weight","color"];for(itm in A){this.savedStyles[bkLib.camelize(itm)]=this.elm.getStyle(itm)}setTimeout(this.initFrame.closure(this),50)},disable:function(){this.elm.innerHTML=this.getContent()},initFrame:function(){var B=$BK(this.elmFrame.contentWindow.document);B.designMode="on";B.open();var A=this.ne.options.externalCSS;B.write("<html><head>"+((A)?'<link href="'+A+'" rel="stylesheet" type="text/css" />':"")+'</head><body id="nicEditContent" style="margin: 0 !important; background-color: transparent !important;">'+this.initialContent+"</body></html>");B.close();this.frameDoc=B;this.frameWin=$BK(this.elmFrame.contentWindow);this.frameContent=$BK(this.frameWin.document.body).setStyle(this.savedStyles);this.instanceDoc=this.frameWin.document.defaultView;this.heightUpdate();this.frameDoc.addEvent("mousedown",this.selected.closureListener(this)).addEvent("keyup",this.heightUpdate.closureListener(this)).addEvent("keydown",this.keyDown.closureListener(this)).addEvent("keyup",this.selected.closure(this));this.ne.fireEvent("add",this)},getElm:function(){return this.frameContent},setContent:function(A){this.content=A;this.ne.fireEvent("set",this);this.frameContent.innerHTML=this.content;this.heightUpdate()},getSel:function(){return(this.frameWin)?this.frameWin.getSelection():this.frameDoc.selection},heightUpdate:function(){this.elmFrame.style.height=Math.max(this.frameContent.offsetHeight,this.initialHeight)+"px"},nicCommand:function(B,A){this.frameDoc.execCommand(B,false,A);setTimeout(this.heightUpdate.closure(this),100)}});
-var nicEditorPanel=bkClass.extend({construct:function(E,B,A){this.elm=E;this.options=B;this.ne=A;this.panelButtons=new Array();this.buttonList=bkExtend([],this.ne.options.buttonList);this.panelContain=new bkElement("DIV").setStyle({overflow:"hidden",width:"100%",border:"1px solid #cccccc",backgroundColor:"#efefef"}).addClass("panelContain");this.panelElm=new bkElement("DIV").setStyle({margin:"2px",marginTop:"0px",zoom:1,overflow:"hidden"}).addClass("panel").appendTo(this.panelContain);this.panelContain.appendTo(E);var C=this.ne.options;var D=C.buttons;for(button in D){this.addButton(button,C,true)}this.reorder();E.noSelect()},addButton:function(buttonName,options,noOrder){var button=options.buttons[buttonName];var type=(button.type)?eval("(typeof("+button.type+') == "undefined") ? null : '+button.type+";"):nicEditorButton;var hasButton=bkLib.inArray(this.buttonList,buttonName);if(type&&(hasButton||this.ne.options.fullPanel)){this.panelButtons.push(new type(this.panelElm,buttonName,options,this.ne));if(!hasButton){this.buttonList.push(buttonName)}}},findButton:function(B){for(var A=0;A<this.panelButtons.length;A++){if(this.panelButtons[A].name==B){return this.panelButtons[A]}}},reorder:function(){var C=this.buttonList;for(var B=0;B<C.length;B++){var A=this.findButton(C[B]);if(A){this.panelElm.appendChild(A.margin)}}},remove:function(){this.elm.remove()}});
-var nicEditorButton=bkClass.extend({construct:function(D,A,C,B){this.options=C.buttons[A];this.name=A;this.ne=B;this.elm=D;this.margin=new bkElement("DIV").setStyle({"float":"left",marginTop:"2px"}).appendTo(D);this.contain=new bkElement("DIV").setStyle({width:"20px",height:"20px"}).addClass("buttonContain").appendTo(this.margin);this.border=new bkElement("DIV").setStyle({backgroundColor:"#efefef",border:"1px solid #efefef"}).appendTo(this.contain);this.button=new bkElement("DIV").setStyle({width:"18px",height:"18px",overflow:"hidden",zoom:1,cursor:"pointer"}).addClass("button").setStyle(this.ne.getIcon(A,C)).appendTo(this.border);this.button.addEvent("mouseover",this.hoverOn.closure(this)).addEvent("mouseout",this.hoverOff.closure(this)).addEvent("mousedown",this.mouseClick.closure(this)).noSelect();if(!window.opera){this.button.onmousedown=this.button.onclick=bkLib.cancelEvent}B.addEvent("selected",this.enable.closure(this)).addEvent("blur",this.disable.closure(this)).addEvent("key",this.key.closure(this));this.disable();this.init()},init:function(){},hide:function(){this.contain.setStyle({display:"none"})},updateState:function(){if(this.isDisabled){this.setBg()}else{if(this.isHover){this.setBg("hover")}else{if(this.isActive){this.setBg("active")}else{this.setBg()}}}},setBg:function(A){switch(A){case"hover":var B={border:"1px solid #666",backgroundColor:"#ddd"};break;case"active":var B={border:"1px solid #666",backgroundColor:"#ccc"};break;default:var B={border:"1px solid #efefef",backgroundColor:"#efefef"}}this.border.setStyle(B).addClass("button-"+A)},checkNodes:function(A){var B=A;do{if(this.options.tags&&bkLib.inArray(this.options.tags,B.nodeName)){this.activate();return true}}while(B=B.parentNode&&B.className!="nicEdit");B=$BK(A);while(B.nodeType==3){B=$BK(B.parentNode)}if(this.options.css){for(itm in this.options.css){if(B.getStyle(itm,this.ne.selectedInstance.instanceDoc)==this.options.css[itm]){this.activate();return true}}}this.deactivate();return false},activate:function(){if(!this.isDisabled){this.isActive=true;this.updateState();this.ne.fireEvent("buttonActivate",this)}},deactivate:function(){this.isActive=false;this.updateState();if(!this.isDisabled){this.ne.fireEvent("buttonDeactivate",this)}},enable:function(A,B){this.isDisabled=false;this.contain.setStyle({opacity:1}).addClass("buttonEnabled");this.updateState();this.checkNodes(B)},disable:function(A,B){this.isDisabled=true;this.contain.setStyle({opacity:0.6}).removeClass("buttonEnabled");this.updateState()},toggleActive:function(){(this.isActive)?this.deactivate():this.activate()},hoverOn:function(){if(!this.isDisabled){this.isHover=true;this.updateState();this.ne.fireEvent("buttonOver",this)}},hoverOff:function(){this.isHover=false;this.updateState();this.ne.fireEvent("buttonOut",this)},mouseClick:function(){if(this.options.command){this.ne.nicCommand(this.options.command,this.options.commandArgs);if(!this.options.noActive){this.toggleActive()}}this.ne.fireEvent("buttonClick",this)},key:function(A,B){if(this.options.key&&B.ctrlKey&&String.fromCharCode(B.keyCode||B.charCode).toLowerCase()==this.options.key){this.mouseClick();if(B.preventDefault){B.preventDefault()}}}});
-var nicPlugin=bkClass.extend({construct:function(B,A){this.options=A;this.ne=B;this.ne.addEvent("panel",this.loadPanel.closure(this));this.init()},loadPanel:function(C){var B=this.options.buttons;for(var A in B){C.addButton(A,this.options)}C.reorder()},init:function(){}});
-
-
-var nicPaneOptions = { };
-
-var nicEditorPane=bkClass.extend({construct:function(D,C,B,A){this.ne=C;this.elm=D;this.pos=D.pos();this.contain=new bkElement("div").setStyle({zIndex:"99999",overflow:"hidden",position:"absolute",left:this.pos[0]+"px",top:this.pos[1]+"px"});this.pane=new bkElement("div").setStyle({fontSize:"12px",border:"1px solid #ccc",overflow:"hidden",padding:"4px",textAlign:"left",backgroundColor:"#ffffc9"}).addClass("pane").setStyle(B).appendTo(this.contain);if(A&&!A.options.noClose){this.close=new bkElement("div").setStyle({"float":"right",height:"16px",width:"16px",cursor:"pointer"}).setStyle(this.ne.getIcon("close",nicPaneOptions)).addEvent("mousedown",A.removePane.closure(this)).appendTo(this.pane)}this.contain.noSelect().appendTo(document.body);this.position();this.init()},init:function(){},position:function(){if(this.ne.nicPanel){var B=this.ne.nicPanel.elm;var A=B.pos();var C=A[0]+parseInt(B.getStyle("width"))-(parseInt(this.pane.getStyle("width"))+8);if(C<this.pos[0]){this.contain.setStyle({left:C+"px"})}}},toggle:function(){this.isVisible=!this.isVisible;this.contain.setStyle({display:((this.isVisible)?"block":"none")})},remove:function(){if(this.contain){this.contain.remove();this.contain=null}},append:function(A){A.appendTo(this.pane)},setContent:function(A){this.pane.setContent(A)}});
-
-var nicEditorAdvancedButton=nicEditorButton.extend({init:function(){this.ne.addEvent("selected",this.removePane.closure(this)).addEvent("blur",this.removePane.closure(this))},mouseClick:function(){if(!this.isDisabled){if(this.pane&&this.pane.pane){this.removePane()}else{this.pane=new nicEditorPane(this.contain,this.ne,{width:(this.width||"270px"),backgroundColor:"#fff"},this);this.addPane();this.ne.selectedInstance.saveRng()}}},addForm:function(C,G){this.form=new bkElement("form").addEvent("submit",this.submit.closureListener(this));this.pane.append(this.form);this.inputs={};for(itm in C){var D=C[itm];var F="";if(G){F=G.getAttribute(itm)}if(!F){F=D.value||""}var A=C[itm].type;if(A=="title"){new bkElement("div").setContent(D.txt).setStyle({fontSize:"14px",fontWeight:"bold",padding:"0px",margin:"2px 0"}).appendTo(this.form)}else{var B=new bkElement("div").setStyle({overflow:"hidden",clear:"both"}).appendTo(this.form);if(D.txt){new bkElement("label").setAttributes({"for":itm}).setContent(D.txt).setStyle({margin:"2px 4px",fontSize:"13px",width:"50px",lineHeight:"20px",textAlign:"right","float":"left"}).appendTo(B)}switch(A){case"text":this.inputs[itm]=new bkElement("input").setAttributes({id:itm,value:F,type:"text"}).setStyle({margin:"2px 0",fontSize:"13px","float":"left",height:"20px",border:"1px solid #ccc",overflow:"hidden"}).setStyle(D.style).appendTo(B);break;case"select":this.inputs[itm]=new bkElement("select").setAttributes({id:itm}).setStyle({border:"1px solid #ccc","float":"left",margin:"2px 0"}).appendTo(B);for(opt in D.options){var E=new bkElement("option").setAttributes({value:opt,selected:(opt==F)?"selected":""}).setContent(D.options[opt]).appendTo(this.inputs[itm])}break;case"content":this.inputs[itm]=new bkElement("textarea").setAttributes({id:itm}).setStyle({border:"1px solid #ccc","float":"left"}).setStyle(D.style).appendTo(B);this.inputs[itm].value=F}}}new bkElement("input").setAttributes({type:"submit"}).setStyle({backgroundColor:"#efefef",border:"1px solid #ccc",margin:"3px 0","float":"left",clear:"both"}).appendTo(this.form);this.form.onsubmit=bkLib.cancelEvent},submit:function(){},findElm:function(B,A,E){var D=this.ne.selectedInstance.getElm().getElementsByTagName(B);for(var C=0;C<D.length;C++){if(D[C].getAttribute(A)==E){return $BK(D[C])}}},removePane:function(){if(this.pane){this.pane.remove();this.pane=null;this.ne.selectedInstance.restoreRng()}}});
-
-var nicButtonTips=bkClass.extend({construct:function(A){this.ne=A;A.addEvent("buttonOver",this.show.closure(this)).addEvent("buttonOut",this.hide.closure(this))},show:function(A){this.timer=setTimeout(this.create.closure(this,A),400)},create:function(A){this.timer=null;if(!this.pane){this.pane=new nicEditorPane(A.button,this.ne,{fontSize:"12px",marginTop:"5px"});this.pane.setContent(A.options.name)}},hide:function(A){if(this.timer){clearTimeout(this.timer)}if(this.pane){this.pane=this.pane.remove()}}});nicEditors.registerPlugin(nicButtonTips);
-
-
-var nicSelectOptions = {
-	buttons : {
-		'fontSize' : {name : __('Select Font Size'), type : 'nicEditorFontSizeSelect', command : 'fontsize'},
-		'fontFamily' : {name : __('Select Font Family'), type : 'nicEditorFontFamilySelect', command : 'fontname'},
-		'fontFormat' : {name : __('Select Font Format'), type : 'nicEditorFontFormatSelect', command : 'formatBlock'}
-	}
-};
-
-var nicEditorSelect=bkClass.extend({construct:function(D,A,C,B){this.options=C.buttons[A];this.elm=D;this.ne=B;this.name=A;this.selOptions=new Array();this.margin=new bkElement("div").setStyle({"float":"left",margin:"2px 1px 0 1px"}).appendTo(this.elm);this.contain=new bkElement("div").setStyle({width:"90px",height:"20px",cursor:"pointer",overflow:"hidden"}).addClass("selectContain").addEvent("click",this.toggle.closure(this)).appendTo(this.margin);this.items=new bkElement("div").setStyle({overflow:"hidden",zoom:1,border:"1px solid #ccc",paddingLeft:"3px",backgroundColor:"#fff"}).appendTo(this.contain);this.control=new bkElement("div").setStyle({overflow:"hidden","float":"right",height:"18px",width:"16px"}).addClass("selectControl").setStyle(this.ne.getIcon("arrow",C)).appendTo(this.items);this.txt=new bkElement("div").setStyle({overflow:"hidden","float":"left",width:"66px",height:"14px",marginTop:"1px",fontFamily:"sans-serif",textAlign:"center",fontSize:"12px"}).addClass("selectTxt").appendTo(this.items);if(!window.opera){this.contain.onmousedown=this.control.onmousedown=this.txt.onmousedown=bkLib.cancelEvent}this.margin.noSelect();this.ne.addEvent("selected",this.enable.closure(this)).addEvent("blur",this.disable.closure(this));this.disable();this.init()},disable:function(){this.isDisabled=true;this.close();this.contain.setStyle({opacity:0.6})},enable:function(A){this.isDisabled=false;this.close();this.contain.setStyle({opacity:1})},setDisplay:function(A){this.txt.setContent(A)},toggle:function(){if(!this.isDisabled){(this.pane)?this.close():this.open()}},open:function(){this.pane=new nicEditorPane(this.items,this.ne,{width:"88px",padding:"0px",borderTop:0,borderLeft:"1px solid #ccc",borderRight:"1px solid #ccc",borderBottom:"0px",backgroundColor:"#fff"});for(var C=0;C<this.selOptions.length;C++){var B=this.selOptions[C];var A=new bkElement("div").setStyle({overflow:"hidden",borderBottom:"1px solid #ccc",width:"88px",textAlign:"left",overflow:"hidden",cursor:"pointer"});var D=new bkElement("div").setStyle({padding:"0px 4px"}).setContent(B[1]).appendTo(A).noSelect();D.addEvent("click",this.update.closure(this,B[0])).addEvent("mouseover",this.over.closure(this,D)).addEvent("mouseout",this.out.closure(this,D)).setAttributes("id",B[0]);this.pane.append(A);if(!window.opera){D.onmousedown=bkLib.cancelEvent}}},close:function(){if(this.pane){this.pane=this.pane.remove()}},over:function(A){A.setStyle({backgroundColor:"#ccc"})},out:function(A){A.setStyle({backgroundColor:"#fff"})},add:function(B,A){this.selOptions.push(new Array(B,A))},update:function(A){this.ne.nicCommand(this.options.command,A);this.close()}});var nicEditorFontSizeSelect=nicEditorSelect.extend({sel:{1:"1&nbsp;(8pt)",2:"2&nbsp;(10pt)",3:"3&nbsp;(12pt)",4:"4&nbsp;(14pt)",5:"5&nbsp;(18pt)",6:"6&nbsp;(24pt)"},init:function(){this.setDisplay("Font&nbsp;Size...");for(itm in this.sel){this.add(itm,'<font size="'+itm+'">'+this.sel[itm]+"</font>")}}});var nicEditorFontFamilySelect=nicEditorSelect.extend({sel:{arial:"Arial","comic sans ms":"Comic Sans","courier new":"Courier New",georgia:"Georgia",helvetica:"Helvetica",impact:"Impact","times new roman":"Times","trebuchet ms":"Trebuchet",verdana:"Verdana"},init:function(){this.setDisplay("Font&nbsp;Family...");for(itm in this.sel){this.add(itm,'<font face="'+itm+'">'+this.sel[itm]+"</font>")}}});var nicEditorFontFormatSelect=nicEditorSelect.extend({sel:{p:"Paragraph",pre:"Pre",h6:"Heading&nbsp;6",h5:"Heading&nbsp;5",h4:"Heading&nbsp;4",h3:"Heading&nbsp;3",h2:"Heading&nbsp;2",h1:"Heading&nbsp;1"},init:function(){this.setDisplay("Font&nbsp;Format...");for(itm in this.sel){var A=itm.toUpperCase();this.add("<"+A+">","<"+itm+' style="padding: 0px; margin: 0px;">'+this.sel[itm]+"</"+A+">")}}});nicEditors.registerPlugin(nicPlugin,nicSelectOptions);
-
-
-var nicLinkOptions = {
-	buttons : {
-		'link' : {name : 'Add Link', type : 'nicLinkButton', tags : ['A']},
-		'unlink' : {name : 'Remove Link',  command : 'unlink', noActive : true}
-	}
-};
-
-var nicLinkButton=nicEditorAdvancedButton.extend({addPane:function(){this.ln=this.ne.selectedInstance.selElm().parentTag("A");this.addForm({"":{type:"title",txt:"Add/Edit Link"},href:{type:"text",txt:"URL",value:"http://",style:{width:"150px"}},title:{type:"text",txt:"Title"},target:{type:"select",txt:"Open In",options:{"":"Current Window",_blank:"New Window"},style:{width:"100px"}}},this.ln)},submit:function(C){var A=this.inputs.href.value;if(A=="http://"||A==""){alert("You must enter a URL to Create a Link");return false}this.removePane();if(!this.ln){var B="javascript:nicTemp();";this.ne.nicCommand("createlink",B);this.ln=this.findElm("A","href",B)}if(this.ln){this.ln.setAttributes({href:this.inputs.href.value,title:this.inputs.title.value,target:this.inputs.target.options[this.inputs.target.selectedIndex].value})}}});nicEditors.registerPlugin(nicPlugin,nicLinkOptions);
-
-
-var nicColorOptions = {
-	buttons : {
-		'forecolor' : {name : __('Change Text Color'), type : 'nicEditorColorButton', noClose : true},
-		'bgcolor' : {name : __('Change Background Color'), type : 'nicEditorBgColorButton', noClose : true}
-	}
-};
-
-var nicEditorColorButton=nicEditorAdvancedButton.extend({addPane:function(){var D={0:"00",1:"33",2:"66",3:"99",4:"CC",5:"FF"};var H=new bkElement("DIV").setStyle({width:"270px"});for(var A in D){for(var F in D){for(var E in D){var I="#"+D[A]+D[E]+D[F];var C=new bkElement("DIV").setStyle({cursor:"pointer",height:"15px","float":"left"}).appendTo(H);var G=new bkElement("DIV").setStyle({border:"2px solid "+I}).appendTo(C);var B=new bkElement("DIV").setStyle({backgroundColor:I,overflow:"hidden",width:"11px",height:"11px"}).addEvent("click",this.colorSelect.closure(this,I)).addEvent("mouseover",this.on.closure(this,G)).addEvent("mouseout",this.off.closure(this,G,I)).appendTo(G);if(!window.opera){C.onmousedown=B.onmousedown=bkLib.cancelEvent}}}}this.pane.append(H.noSelect())},colorSelect:function(A){this.ne.nicCommand("foreColor",A);this.removePane()},on:function(A){A.setStyle({border:"2px solid #000"})},off:function(A,B){A.setStyle({border:"2px solid "+B})}});var nicEditorBgColorButton=nicEditorColorButton.extend({colorSelect:function(A){this.ne.nicCommand("hiliteColor",A);this.removePane()}});nicEditors.registerPlugin(nicPlugin,nicColorOptions);
-
-
-var nicImageOptions = {
-	buttons : {
-		'image' : {name : 'Add Image', type : 'nicImageButton', tags : ['IMG']}
-	}
-
-};
-
-var nicImageButton=nicEditorAdvancedButton.extend({addPane:function(){this.im=this.ne.selectedInstance.selElm().parentTag("IMG");this.addForm({"":{type:"title",txt:"Add/Edit Image"},src:{type:"text",txt:"URL",value:"http://",style:{width:"150px"}},alt:{type:"text",txt:"Alt Text",style:{width:"100px"}},align:{type:"select",txt:"Align",options:{none:"Default",left:"Left",right:"Right"}}},this.im)},submit:function(B){var C=this.inputs.src.value;if(C==""||C=="http://"){alert("You must enter a Image URL to insert");return false}this.removePane();if(!this.im){var A="javascript:nicImTemp();";this.ne.nicCommand("insertImage",A);this.im=this.findElm("IMG","src",A)}if(this.im){this.im.setAttributes({src:this.inputs.src.value,alt:this.inputs.alt.value,align:this.inputs.align.value})}}});nicEditors.registerPlugin(nicPlugin,nicImageOptions);
-
-
-var nicSaveOptions = {
-	buttons : {
-		'save' : {name : __('Save this content'), type : 'nicEditorSaveButton'}
-	}
-};
-
-var nicEditorSaveButton=nicEditorButton.extend({init:function(){if(!this.ne.options.onSave){this.margin.setStyle({display:"none"})}},mouseClick:function(){var B=this.ne.options.onSave;var A=this.ne.selectedInstance;B(A.getContent(),A.elm.id,A)}});nicEditors.registerPlugin(nicPlugin,nicSaveOptions);
-
-
-var nicCodeOptions = {
-	buttons : {
-		'xhtml' : {name : 'Edit HTML', type : 'nicCodeButton'}
-	}
-
-};
-
-var nicCodeButton=nicEditorAdvancedButton.extend({width:"750px",addPane:function(){this.addForm({"":{type:"title",txt:"Edit HTML"},code:{type:"content",value:this.ne.selectedInstance.getContent(),style:{width:"740px",height:"200px"}}})},submit:function(B){var A=this.inputs.code.value;this.ne.selectedInstance.setContent(A);this.removePane()}});nicEditors.registerPlugin(nicPlugin,nicCodeOptions);
-
diff --git a/scp/js/scp.js b/scp/js/scp.js
index d79b03955..466446bde 100644
--- a/scp/js/scp.js
+++ b/scp/js/scp.js
@@ -13,15 +13,17 @@ function checkbox_checker(formObj, min, max) {
     var checked=$('input:checkbox:checked', formObj).length;
     var action= action?action:"process";
     if (max>0 && checked > max ){
-        msg="You're limited to only " + max + " selections.\n"
-        msg=msg + "You have made " + checked + " selections.\n"
-        msg=msg + "Please remove " + (checked-max) + " selection(s)."
+        msg=__("You're limited to only {0} selections.\n") .replace('{0}', max);
+        msg=msg + __("You have made {0} selections.\n").replace('{0}', checked);
+        msg=msg + __("Please remove {0} selection(s).").replace('{0}', checked-max);
         alert(msg)
         return (false);
     }
 
     if (checked< min ){
-        alert("Please make at least " + min + " selections. " + checked + " checked so far.")
+        alert(__("Please make at least {0} selections. {1} checked so far.")
+            .replace('{0}', min)
+            .replace('{1}', checked);
         return (false);
     }
 
@@ -138,10 +140,10 @@ var scp_prep = function() {
             fObj.data('changed', true);
             $('input[type=submit]', fObj).css('color', 'red');
             $(window).bind('beforeunload', function(e) {
-                return 'Are you sure you want to leave? Any changes or info you\'ve entered will be discarded!';
+                return __('Are you sure you want to leave? Any changes or info you\'ve entered will be discarded!');
             });
             $(document).on('pjax:beforeSend.changed', function(e) {
-                return confirm('Are you sure you want to leave? Any changes or info you\'ve entered will be discarded!');
+                return confirm(__('Are you sure you want to leave? Any changes or info you\'ve entered will be discarded!'));
             });
         }
     };
@@ -185,7 +187,7 @@ var scp_prep = function() {
     //Canned attachments.
     $('.canned_attachments, .faq_attachments').delegate('input:checkbox', 'click', function(e) {
         var elem = $(this);
-        if(!$(this).is(':checked') && confirm("Are you sure you want to remove this attachment?")==true) {
+        if(!$(this).is(':checked') && confirm(__("Are you sure you want to remove this attachment?"))==true) {
             elem.parent().addClass('strike');
         } else {
             elem.attr('checked', 'checked');
@@ -412,7 +414,8 @@ var scp_prep = function() {
             .done( function () {
              })
             .fail( function () {
-                $('#result-count').html('<div class="fail">Advanced search failed - try again!</div>');
+                $('#result-count').html('<div class="fail">'
+                    + __('Advanced search failed - try again!'). + '</div>');
             })
             .always( function () {
                 $('.spinner', elem).hide();
@@ -766,3 +769,9 @@ $('#new-note').live('click', function() {
     $(T).redactor('focus');
     return false;
 });
+
+function __(s) {
+  if ($.strings && $.strings[s])
+    return $.strings[s];
+  return s;
+}
diff --git a/scp/js/ticket.js b/scp/js/ticket.js
index e65906028..ba70ec2df 100644
--- a/scp/js/ticket.js
+++ b/scp/js/ticket.js
@@ -52,10 +52,10 @@ var autoLock = {
 
         if(!autoLock.lasteventTime) { //I hate nav away warnings..but
             $(document).on('pjax:beforeSend.changed', function(e) {
-                return confirm("Any changes or info you've entered will be discarded!");
+                return confirm(__("Any changes or info you've entered will be discarded!"));
             });
             $(window).bind('beforeunload', function(e) {
-                return "Any changes or info you've entered will be discarded!";
+                return __("Any changes or info you've entered will be discarded!");
              });
         }
 
@@ -135,8 +135,7 @@ var autoLock = {
             $(window).unbind('beforeunload');
             //Only warn if we had a failed lock attempt.
             if(autoLock.warn && !autoLock.lockId && autoLock.lasteventTime) {
-                var answer=confirm('Unable to acquire a lock on the ticket. Someone else could be working on the same ticket. \
-                    Please confirm if you wish to continue anyways.');
+                var answer=confirm(__('Unable to acquire a lock on the ticket. Someone else could be working on the same ticket.  Please confirm if you wish to continue anyways.'));
                 if(!answer) {
                     e.returnValue=false;
                     e.cancelBubble=true;
@@ -236,7 +235,7 @@ var autoLock = {
                     autoLock.lockAttempts++;
                     if(warn && (!lock.retry || autoLock.lockAttempts>=autoLock.maxattempts)) {
                         autoLock.retry=false;
-                        alert('Unable to lock the ticket. Someone else could be working on the same ticket.');
+                        alert(__('Unable to lock the ticket. Someone else could be working on the same ticket.'));
                     }
                 }
                 break;
@@ -244,7 +243,7 @@ var autoLock = {
     },
 
     discardWarning: function(e) {
-        e.returnValue="Any changes or info you've entered will be discarded!";
+        e.returnValue=__("Any changes or info you've entered will be discarded!");
     },
 
     //TODO: Monitor events and elapsed time and warn user when the lock is about to expire.
@@ -307,7 +306,7 @@ $.showImagesInline = function(urls, thread_id) {
                     }
                 ).append($('<div class="caption">')
                     .append('<span class="filename">'+info.filename+'</span>')
-                    .append('<a href="'+info.download_url+'" class="action-button no-pjax"><i class="icon-download-alt"></i> Download</a>')
+                    .append('<a href="'+info.download_url+'" class="action-button no-pjax"><i class="icon-download-alt"></i> '+__('Download')+'</a>')
                 );
             e.data('wrapped', true);
         }
@@ -415,7 +414,7 @@ var ticket_onload = function($) {
         extra.append($('<a>')
           .addClass("action-button show-images")
           .css({'font-weight':'normal'})
-          .text(' Show Images')
+          .text(' ' + __('Show Images'))
           .click(function(ev) {
             imgs.each(function(i, img) {
               $.showNonLocalImage(img);
diff --git a/scp/js/upgrader.js b/scp/js/upgrader.js
index 2ac77d99f..a10c02626 100644
--- a/scp/js/upgrader.js
+++ b/scp/js/upgrader.js
@@ -38,7 +38,7 @@ jQuery(function($) {
                 success: function(res) {
                     $('#main #task').html(res);
                     $('#upgrading #action').html(res);
-                    $('#upgrading #msg').html('Still busy... smile #'+count);
+                    $('#upgrading #msg').html(__('Still busy... smile #')+count);
                 },
                 statusCode: {
                     200: function() {
@@ -46,19 +46,19 @@ jQuery(function($) {
                     },
 
                     201: function() {
-                        $('#upgrading #msg').html("Cleaning up!...");
+                        $('#upgrading #msg').html(__("Cleaning up!..."));
                         setTimeout(function() { location.href =url+'?c='+count+'&r='+Math.floor((Math.random()*100)+1); }, 3000);
                     }
                 },
                 error: function(jqXHR, textStatus, errorThrown) {
-                    $('#upgrading #action').html('Error occurred. Aborting...');
+                    $('#upgrading #action').html(__('Error occurred.  Aborting...'));
                     switch(jqXHR.status) {
                         case 404:
-                            $('#upgrading #msg').html("Manual upgrade required (ajax failed)");
+                            $('#upgrading #msg').html(__("Manual upgrade required (ajax failed)"));
                             setTimeout(function() { location.href =url+'?m=manual&c='+count+'&r='+Math.floor((Math.random()*100)+1); }, 2000);
                             break;
                         default:
-                            $('#upgrading #msg').html("Something went wrong");
+                            $('#upgrading #msg').html(__("Something went wrong"));
                             setTimeout(function() { location.href =url+'?c='+count+'&r='+Math.floor((Math.random()*100)+1); }, 2000);
                     }
                 }
diff --git a/scp/orgs.php b/scp/orgs.php
index 833e87c31..6ffdf3575 100644
--- a/scp/orgs.php
+++ b/scp/orgs.php
@@ -48,7 +48,7 @@ if ($_POST) {
             if ($i && $i == $num)
                 $msg = __('Selected users removed successfully');
             elseif ($i > 0)
-                $warn = sprintf(__('%1$d or %2$d selected users removed'), $i, $count);
+                $warn = sprintf(__('%1$d of %2$d selected users removed'), $i, $count);
             elseif (!$errors['err'])
                 $errors['err'] = __('Unable to remove selected users');
         }
diff --git a/setup/cli/modules/i18n.php b/setup/cli/modules/i18n.php
index fb5b0687f..2875eb398 100644
--- a/setup/cli/modules/i18n.php
+++ b/setup/cli/modules/i18n.php
@@ -124,9 +124,17 @@ class i18n_Compiler extends Module {
         @unlink(I18N_DIR."$lang.phar");
         $phar = new Phar(I18N_DIR."$lang.phar");
 
+        $mo_file = false;
+
         for ($i=0; $i<$zip->numFiles; $i++) {
             $info = $zip->statIndex($i);
-            $phar->addFromString($info['name'], $zip->getFromIndex($i));
+            $contents = $zip->getFromIndex($i);
+            if (strpos($info['name'], '/messages.mo') !== false) {
+                $mo_file = $contents;
+                // Don't add the MO file as-is to the PHAR file
+                continue;
+            }
+            $phar->addFromString($info['name'], $contents);
         }
 
         // TODO: Add i18n extras (like fonts)
@@ -150,6 +158,8 @@ class i18n_Compiler extends Module {
             list($code, $js) = $this->_http_get(
                 'http://jquery-ui.googlecode.com/svn/tags/latest/ui/i18n/jquery.ui.datepicker-'
                     .str_replace('_','-',$l).'.js');
+            // If locale-specific version is not available, use the base
+            // language version (de if de_CH is not available)
             if ($code == 200)
                 break;
         }
@@ -159,6 +169,28 @@ class i18n_Compiler extends Module {
             $this->stderr->write(str_replace('_','-',$lang)
                 .": Unable to fetch jQuery UI Datepicker locale file\n");
 
+        // Add in the messages.mo.php file
+        if ($mo_file) {
+            $mo_output = fopen('php://temp', 'r+b');
+            Translation::buildHashFile($mo_file, $mo_output);
+            rewind($mo_output);
+            $phar->addFile($mo_output, 'LC_MESSAGES/messages.mo.php');
+        }
+
+        // Add in translation of javascript strings
+        if ($mo_output && ($js = self::__getAllJsPhrases())) {
+            $mo = unserialize($mo);
+            foreach ($js as $c) {
+                foreach ($c['forms'] as $f) {
+                    $phrases[$f] = @$mo[$f] ?: $f;
+                }
+            }
+            $phar->addFromStrings(
+                sprintf('(function($){$.strings=%s;})(jQuery);',
+                    JsonDataEncoder::encode($phrases)),
+                'js/osticket-strings.js');
+        }
+
         // TODO: Sign files
 
         // Use a very small stub
@@ -264,7 +296,7 @@ class i18n_Compiler extends Module {
                 break;
             case T_COMMENT:
             case T_DOC_COMMENT:
-                if (strpos($T[1], '* trans *') !== false) {
+                if (preg_match('`\*\s*trans\s*\*`', $T[1])) {
                     // Find the next textual token
                     list($S, $T) = $this->__read_next_string($tokens);
                     $string = array('forms'=>array($S['form']), 'line'=>$S['line']);
@@ -367,28 +399,56 @@ class i18n_Compiler extends Module {
             $F = str_replace(ROOT_DIR, '', $f);
             $this->stderr->write("$F\n");
             $tokens = new ArrayObject(token_get_all(fread(fopen($f, 'r'), filesize($f))));
-            foreach ($this->__find_strings($tokens, $funcs, 1) as $calls) {
-                if (!($forms = $calls['forms']))
-                    // Transation of non-constant
-                    continue;
-                $primary = $forms[0];
-                // Normalize the $primary string
-                $primary = preg_replace(array("`\\\(['$])`", '`(?<!\\\)"`'), array("$1", '\"'), $primary);
-                if (!isset($strings[$primary])) {
-                    $strings[$primary] = array('forms' => $forms);
-                }
-                $E = &$strings[$primary];
-
-                if (isset($calls['line']))
-                    $E['usage'][] = "{$F}:{$calls['line']}";
-                if (isset($calls['flags']))
-                    $E['flags'] = array_unique(array_merge(@$E['flags'] ?: array(), $calls['flags']));
-                if (isset($calls['comments']))
-                    $E['comments'] = array_merge(@$E['comments'] ?: array(), $calls['comments']);
+            foreach ($this->__find_strings($tokens, $funcs, 1) as $call) {
+                self::__addString($strings, $call, $F);
             }
         }
         $this->__write_pot($strings);
     }
+
+    static function __addString(&$strings, $call, $file=false) {
+        if (!($forms = $call['forms']))
+            // Transation of non-constant
+            return;
+        $primary = $forms[0];
+        // Normalize the $primary string
+        $primary = preg_replace(array("`\\\(['$])`", '`(?<!\\\)"`'), array("$1", '\"'), $primary);
+        if (!isset($strings[$primary])) {
+            $strings[$primary] = array('forms' => $forms);
+        }
+        $E = &$strings[$primary];
+
+        if (isset($call['line']) && $file)
+            $E['usage'][] = "{$file}:{$call['line']}";
+        if (isset($call['flags']))
+            $E['flags'] = array_unique(array_merge(@$E['flags'] ?: array(), $call['flags']));
+        if (isset($call['comments']))
+            $E['comments'] = array_merge(@$E['comments'] ?: array(), $call['comments']);
+
+        $strings = array_merge($strings, self::__getAllJsPhrases());
+    }
+
+    function __getAllJsPhrases() {
+        $strings = array();
+        foreach (glob_recursive(ROOT_DIR . "*.js") as $s) {
+            $script = file_get_contents($s);
+            $s = str_replace(ROOT_DIR, '', $s);
+            $this->stderr->write($s."\n");
+            $calls = array();
+            preg_match_all('/__\(\s*[^\'"]*(([\'"])(?:(?<!\\\\)\2|.)+\2)\s*[^)]*\)/',
+                $script, $calls, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
+            foreach ($calls as $c) {
+                $call = $this->__find_strings(token_get_all('<?php '.$c[0][0]), $funcs, 0);
+                $call = $call[0];
+
+                list($lhs) = str_split($script, $c[1][1]);
+                $call['line'] = strlen($lhs) - strlen(str_replace("\n", "", $lhs)) + 1;
+
+                self::__addString($strings, $call, $s);
+            }
+        }
+        return $strings;
+    }
 }
 
 Module::register('i18n', 'i18n_Compiler');
-- 
GitLab