/* Copyright 2014 qTranslate Team (email : qTranslateTeam@gmail.com ) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** * Search for 'Designed as interface for other plugin integration' in comments to functions * to find out which functions are safe to use in the 3rd-party integration. * Avoid accessing internal variables directly, as they are subject to be re-designed at any time. * Single global variable 'qTranslateConfig' is an entry point to the interface. * - qTranslateConfig.qtx - is a shorthand reference to the only global object of type 'qTranslateX'. * - qTranslateConfig.js - is a place where custom Java script functions are stored, if needed. * Read Integration Guide, https://qtranslatexteam.wordpress.com/integration/, for more information. */ /* // debugging tools, do not check in var cc=0; function c(v){ ++cc; console.log('== '+cc+': '+v); } function ct(v){ c(v); console.trace(); } function co(t,o){ ++cc; console.log('== '+cc+': '+t+'%o',o); } */ /** * since 3.2.7 */ qtranxj_get_split_blocks = function(text) { var split_regex = /(||\[:[a-z]{2}\]|\[:\]|\{:[a-z]{2}\}|\{:\})/gi; // @since 3.3.6 swirly brackets return text.xsplit(split_regex); } /** * since 3.2.7 */ qtranxj_split = function(text) { var blocks = qtranxj_get_split_blocks(text); return qtranxj_split_blocks(blocks); } /** * since 3.1-b1 - closing tag [:] */ qtranxj_split_blocks = function(blocks) { var result = new Object; //for(var i=0; i/gi; var blang_regex=/\[:([a-z]{2})\]/gi; var slang_regex=/\{:([a-z]{2})\}/gi; // @since 3.3.6 swirly brackets var lang = false; var matches; for(var i = 0;i' || b == '[:]' || b == '{:}' ){ lang = false; continue; } if(lang){ if(!result[lang]) result[lang] = b; else result[lang] += b; lang = false; }else{ //keep neutral text for(var key in result){ result[key] += b; } } } return result; } function qtranxj_get_cookie(cname) { var nm = cname + "="; var ca = document.cookie.split(';'); //c('ca='+ca); for(var i=0; i 1) arr.push(result[1]); start = _regEx.lastIndex; } if(start < this.length) arr.push(this.slice(start)); if(start == this.length) arr.push(''); //delim at the end return arr; }; //Since 3.2.7 removed: function qtranxj_isArray(obj){ return obj.constructor.toString().indexOf('Array') >= 0; } function qtranxj_ce(tagName, props, pNode, isFirst) { var el= document.createElement(tagName); if (props) { for(prop in props) { //try { el[prop]=props[prop]; } //catch(err) { //Handle errors here } } } if (pNode) { if (isFirst && pNode.firstChild) { pNode.insertBefore(el, pNode.firstChild); } else { pNode.appendChild(el); } } return el; } var qTranslateX=function(pg) { var qtx = this; qTranslateConfig.qtx = this; /** * Designed as interface for other plugin integration. The documentation is available at * https://qtranslatexteam.wordpress.com/integration/ * return array keyed by two-letter language code. Example of usage: * var langs = getLanguages(); * for(var lang_code in langs){ * var lang_conf = langs[lang_code]; * // variables available: * //lang_conf.name * //lang_conf.flag * //lang_conf.locale * // and may be more properties later * } */ this.getLanguages=function(){ return qTranslateConfig.language_config; } /** * Designed as interface for other plugin integration. The documentation is available at * https://qtranslatexteam.wordpress.com/integration/ * return URL to folder with flag images. */ this.getFlagLocation=function(){ return qTranslateConfig.flag_location; } /** * Designed as interface for other plugin integration. The documentation is available at * https://qtranslatexteam.wordpress.com/integration/ * return true if 'lang' is in the hash of enabled languages. * This function maybe needed, as function qtranxj_split may return languages, * which are not enabled, in case they were previously enabled and had some data. * Such data is preserved and re-saved until user deletes it manually. */ this.isLanguageEnabled=function(lang){ return !!qTranslateConfig.language_config[lang]; } var setLangCookie=function(lang) { document.cookie='qtrans_edit_language='+lang; } qTranslateConfig.activeLanguage; if(qTranslateConfig.LSB){ qTranslateConfig.activeLanguage = qtranxj_get_cookie('qtrans_edit_language'); if(!qTranslateConfig.activeLanguage || !this.isLanguageEnabled(qTranslateConfig.activeLanguage)){ qTranslateConfig.activeLanguage = qTranslateConfig.language; if(this.isLanguageEnabled(qTranslateConfig.activeLanguage)){ setLangCookie(qTranslateConfig.activeLanguage); }else{//no languages are enabled qTranslateConfig.LSB = false; } } }else{ qTranslateConfig.activeLanguage = qTranslateConfig.language; setLangCookie(qTranslateConfig.activeLanguage); } /** * Designed as interface for other plugin integration. The documentation is available at * https://qtranslatexteam.wordpress.com/integration/ * * @since 3.3 */ this.getActiveLanguage = function() { return qTranslateConfig.activeLanguage; } //this.getActiveLanguageName = function() { return qTranslateConfig.language_name[qTranslateConfig.activeLanguage]; } /** * Designed as interface for other plugin integration. The documentation is available at * https://qtranslatexteam.wordpress.com/integration/ * * @since 3.3 */ this.getLanguages = function() { return qTranslateConfig.language_config; } var contentHooks={}; var contentHookId = 0; var updateFusedValueH=function(id,value) { var h = contentHooks[id]; var text = value.trim(); //c('updateFusedValueH['+id+'] lang='+h.lang+'; text:'+text); h.fields[h.lang].value = text; } /** * Designed as interface for other plugin integration. The documentation is available at * https://qtranslatexteam.wordpress.com/integration/ * * @since 3.3.4 */ this.hasContentHook=function(id){ return contentHooks[id]; } /** * Designed as interface for other plugin integration. The documentation is available at * https://qtranslatexteam.wordpress.com/integration/ * * @since 3.3.2 */ this.addContentHook=function(inpField,encode,field_name) { //co('addContentHook: inpField:',inpField); //co('addContentHook: encode:',encode); //co('addContentHook: field_name:',field_name); if( !inpField ) return false; switch(inpField.tagName){ case 'TEXTAREA': break; case 'INPUT': //co('addContentHook: inpField.type=',inpField.type); //reject the types which cannot be multilingual switch (inpField.type) { case 'button': case 'checkbox': case 'password': case 'radio': case 'submit': return false; } //if(inpField.type.match(/(button|checkbox|password|radio|submit)/)) return false; break; default: return false; } if(!field_name){ if( !inpField.name ) return false; field_name = inpField.name; } //if( typeof inpField.value !== 'string' ) return false; if(inpField.id){ if(contentHooks[inpField.id]){ if(jQuery.contains(document,inpField)) return contentHooks[inpField.id]; //otherwise some Java script already removed previously hooked element qtx.removeContentHook(inpField); } } else if (!contentHooks[field_name]) { inpField.id = field_name; } else { var idx = 0; do { ++idx; inpField.id = field_name + idx; } while (contentHooks[field_name]); //jQuery(inpField).uniqueId();//does not work //jQuery(inpField).each(function (i,e) { e.uniqueId(); });//does not work } //co('addContentHook: id=',inpField.id); var h = contentHooks[inpField.id]={}; //h.id = inpField.id; h.name = field_name; h.contentField=inpField; //c('addContentHook: inpField.value='+inpField.value); h.lang = qTranslateConfig.activeLanguage; var contents = qtranxj_split(inpField.value);//keep neutral text from older times, just in case. //inpField.tagName inpField.value = contents[h.lang]; var qtx_prefix; if(encode){ switch(encode){ case 'slug': qtx_prefix = 'qtranslate-slugs['; break; case 'term': qtx_prefix = 'qtranslate-terms['; break; default: qtx_prefix = 'qtranslate-fields['; break; } }else{ //if(inpField.tagName==='TEXTAREA') // encode='<'; //else encode = '[';//since 3.1 we get rid of <--:--> encoding qtx_prefix = 'qtranslate-fields['; } var bfnm, sfnm, p = h.name.indexOf('['); if(p<0){ bfnm = qtx_prefix + h.name+']'; }else{ bfnm = qtx_prefix + h.name.substring(0,p)+']'; if(h.name.lastIndexOf('[]') < 0){ bfnm += h.name.substring(p); }else{ var len = h.name.length-2; if(len > p) bfnm += h.name.substring(p,len); sfnm = '[]'; } } h.fields={}; for(var lang in contents){ var text = contents[lang]; var fnm = bfnm+'['+lang+']'; if(sfnm) fnm += sfnm; var f = qtranxj_ce('input', {name: fnm, type: 'hidden', className: 'hidden', value: text}); h.fields[lang] = f; inpField.parentNode.insertBefore(f,inpField); } // since 3.2.9.8 - h.contents -> h.fields // since 3.3.8.7 - slug & term switch(encode){ case 'slug': case 'term': h.sepfield = qtranxj_ce('input', {name: bfnm+'[qtranslate-original-value]', type: 'hidden', className: 'hidden', value: contents[qTranslateConfig.default_language] }); break; default: h.sepfield = qtranxj_ce('input', {name: bfnm+'[qtranslate-separator]', type: 'hidden', className: 'hidden', value: encode }); break; } inpField.parentNode.insertBefore(h.sepfield,inpField); h.encode=encode; /** * Highlighting the translatable fields * @since 3.2-b3 */ inpField.className += ' qtranxs-translatable'; /* if(window.tinyMCE){ //c('addContentHook: window.tinyMCE: tinyMCE.editors.length='+tinyMCE.editors.length); //tinyMCE.editors are not yet set up at this point. for(var i=0; i h.fields inpField.onblur = function(){}; inpField.name=inpField.name.replace(/^edit-/,''); inpField.value=h.mlContentField.value; jQuery(h.mlContentField).remove(); */ jQuery(inpField).removeClass('qtranxs-translatable'); return true; }; /** * Designed as interface for other plugin integration. The documentation is available at * https://qtranslatexteam.wordpress.com/integration/ * Re-create a hook, after a piece of HTML is dynamically replaced with a custom Java script. */ this.refreshContentHook=function(inpField) { if( !inpField ) return false; if( !inpField.id ) return false; var h = contentHooks[inpField.id]; if( h ) removeContentHookH(h); return qtx.addContentHook(inpField); } /** * @since 3.2.7 */ var displayHookNodes=[]; var addDisplayHookNode = function (nd) { if(!nd.nodeValue) return 0; var blocks = qtranxj_get_split_blocks(nd.nodeValue); if( !blocks || !blocks.length || blocks.length == 1 ) return 0; //co('addDisplayHookNode: nd: ',nd); //co('addDisplayHookNode: blocks: ',blocks); var h={}; h.nd=nd; //co('addDisplayHookNode: nd=',nd); //c('addDisplayHookNode: nodeValue: "'+nd.nodeValue+'"'); //c('addDisplayHookNode: content='+content); h.contents = qtranxj_split_blocks(blocks); nd.nodeValue=h.contents[qTranslateConfig.activeLanguage]; displayHookNodes.push(h); return 1; } /** * @since 3.2.7 */ var displayHookAttrs=[]; var addDisplayHookAttr = function (nd) { if(!nd.value) return 0; var blocks = qtranxj_get_split_blocks(nd.value); if( !blocks || !blocks.length || blocks.length == 1 ) return 0; //co('addDisplayHookAttr: nd: ',nd); var h={}; h.nd=nd; h.contents = qtranxj_split_blocks(blocks); nd.value=h.contents[qTranslateConfig.activeLanguage]; displayHookAttrs.push(h); return 1; } /** * Designed as interface for other plugin integration. The documentation is available at * https://qtranslatexteam.wordpress.com/integration/ * * @since 3.2.7 switched to use of nodeValue instead of innerHTML. */ this.addDisplayHook = function (elem) { //co('addDisplayHook: this: ',this); if(!elem || !elem.tagName) return 0; switch(elem.tagName){ case 'TEXTAREA': return 0; case 'INPUT': switch(elem.type){ case 'submit': if(elem.value) return addDisplayHookAttr(elem); default: return 0; } default: break; } //co('addDisplayHook: elem: ',elem); var cnt = 0; if(elem.childNodes && elem.childNodes.length){ for(var i = 0; i < elem.childNodes.length; ++i){ var nd = elem.childNodes[i]; switch(nd.nodeType){//http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-1950641247 case 1://ELEMENT_NODE cnt += qtx.addDisplayHook(nd);//recursive call break; case 2://ATTRIBUTE_NODE case 3://TEXT_NODE cnt += addDisplayHookNode(nd); break; default: break; } } } return cnt; } /** * Designed as interface for other plugin integration. The documentation is available at * https://qtranslatexteam.wordpress.com/integration/ * * @since 3.0 */ this.addDisplayHookById=function(id) { return qtx.addDisplayHook(document.getElementById(id)); } var updateTinyMCE = function (h) { text = h.contentField.value; //co('updateTinyMCE: window.switchEditors: ',window.switchEditors); //c('updateTinyMCE: text:'+text); if(h.wpautop && window.switchEditors){ //text = window.switchEditors.pre_wpautop( text ); text = window.switchEditors.wpautop(text); //c('updateTinyMCE:wpautop:'+text); } h.mce.setContent(text,{format: 'html'}); } var onTabSwitch = function (lang) { //var qtx = this; setLangCookie(lang); for(var i = displayHookNodes.length; --i >= 0; ){ var h=displayHookNodes[i]; if(h.nd.parentNode){ h.nd.nodeValue = h.contents[lang];//IE gets upset here if node was removed }else{ displayHookNodes.splice(i,1);//node was removed by some other function } } for(var i = displayHookAttrs.length; --i >= 0;){ var h=displayHookAttrs[i]; if(h.nd.parentNode){ h.nd.value = h.contents[lang]; }else{ displayHookAttrs.splice(i,1);//node was removed by some other function } } for(var key in contentHooks){ var h=contentHooks[key]; var mce = h.mce && !h.mce.hidden; if(mce){ h.mce.save({format: 'html'}); } h.fields[h.lang].value = h.contentField.value; h.lang = lang; var value = h.fields[h.lang].value; if(h.contentField.placeholder && value != ''){//since 3.2.7 h.contentField.placeholder=''; } h.contentField.value = value; if(mce){ updateTinyMCE(h); } } } /* onTabSwitchCustom=function() { //co('onTabSwitch: this',this); //co('onTabSwitch: qtx',qTranslateConfig.qtx); pg.onTabSwitch(this.lang,qTranslateConfig.qtx); } */ /** * Designed as interface for other plugin integration. The documentation is available at * https://qtranslatexteam.wordpress.com/integration/ * * @since 3.0 */ this.addDisplayHooks = function (elems) { //c('addDisplayHooks: elems.length='+elems.length); for(var i=0; i= 0 ){ //var langSwitchWrap=qtranxj_ce('ul', {className: qTranslateConfig.lsb_style_wrap_class}); //var languageSwitch = new qtranxj_LanguageSwitch(langSwitchWrap); var langSwitchWrap = createSetOfLSB(); anchor.f.parentNode.insertBefore( langSwitchWrap, anchor.f ); } if( anchor.where && anchor.where.indexOf('after') >= 0 ){ //var langSwitchWrap=qtranxj_ce('ul', {className: qTranslateConfig.lsb_style_wrap_class}); //var languageSwitch = new qtranxj_LanguageSwitch(langSwitchWrap); var langSwitchWrap = createSetOfLSB(); anchor.f.parentNode.insertBefore( langSwitchWrap, anchor.f.nextSibling ); } if( anchor.where && anchor.where.indexOf('first') >= 0 ){ var langSwitchWrap = createSetOfLSB(); anchor.f.insertBefore( langSwitchWrap, anchor.f.firstChild ); } if( anchor.where && anchor.where.indexOf('last') >= 0 ){ var langSwitchWrap = createSetOfLSB(); anchor.f.insertBefore( langSwitchWrap, null ); } } /** * @since 3.2.4 Synchronization of multiple sets of Language Switching Buttons */ this.addLanguageSwitchListener(onTabSwitch); if(pg.onTabSwitch){ this.addLanguageSwitchListener(pg.onTabSwitch); } } } /** * Designed as interface for other plugin integration. The documentation is available at * https://qtranslatexteam.wordpress.com/integration/ * * qTranslateX instance is saved in global variable qTranslateConfig.qtx, * which can be used by theme or plugins to dynamically change content hooks. * @since 3.4 */ qTranslateConfig.js.get_qtx = function(){ //co('get_qtx: qtx: ', qTranslateConfig.qtx); if(!qTranslateConfig.qtx) new qTranslateX(qTranslateConfig.js); return qTranslateConfig.qtx; } jQuery(document).ready(qTranslateConfig.js.get_qtx);