text: /*
   * Copyright (c) 2009 and 2010 Frank G. Bennett, Jr. All Rights
   * Reserved.
   *
   * The contents of this file are subject to the Common Public
   * Attribution License Version 1.0 (the “License”); you may not use
   * this file except in compliance with the License. You may obtain a
   * copy of the License at:
   *
   * http://bitbucket.org/fbennett/citeproc-js/src/tip/LICENSE.
   *
   * The License is based on the Mozilla Public License Version 1.1 but
   * Sections 14 and 15 have been added to cover use of software over a
   * computer network and provide for limited attribution for the
   * Original Developer. In addition, Exhibit A has been modified to be
   * consistent with Exhibit B.
   *
   * Software distributed under the License is distributed on an “AS IS”
   * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
   * the License for the specific language governing rights and limitations
   * under the License.
   *
   * The Original Code is the citation formatting software known as
   * "citeproc-js" (an implementation of the Citation Style Language
   * [CSL]), including the original test fixtures and software located
   * under the ./std subdirectory of the distribution archive.
   *
   * The Original Developer is not the Initial Developer and is
   * __________. If left blank, the Original Developer is the Initial
   * Developer.
   *
   * The Initial Developer of the Original Code is Frank G. Bennett,
   * Jr. All portions of the code written by Frank G. Bennett, Jr. are
   * Copyright (c) 2009 and 2010 Frank G. Bennett, Jr. All Rights Reserved.
   *
   * Alternatively, the contents of this file may be used under the
   * terms of the GNU Affero General Public License (the [AGPLv3]
   * License), in which case the provisions of [AGPLv3] License are
   * applicable instead of those above. If you wish to allow use of your
   * version of this file only under the terms of the [AGPLv3] License
   * and not to allow others to use your version of this file under the
   * CPAL, indicate your decision by deleting the provisions above and
   * replace them with the notice and other provisions required by the
   * [AGPLv3] License. If you do not delete the provisions above, a
   * recipient may use your version of this file under either the CPAL
   * or the [AGPLv3] License.”
   */
  var zotero = {};
  zotero.Debug = function(s, level){
      return;
      console.log(s);
  }
  
  var Encoder = {
  
      // When encoding do we convert characters into html or numerical entities
      EncodeType : "entity",  // entity OR numerical
  
      isEmpty : function(val){
          if(val){
              //replaced by fcheslack so whitespace separators are not stripped from CSL
              //return ((val===null) || val.length==0 || /^\s+$/.test(val));
              return ((val===null) || val.length==0);
          }else{
              return true;
          }
      },
      // Convert HTML entities into numerical entities
      HTML2Numerical : function(s){
          var arr1 = new Array('&nbsp;','&iexcl;','&cent;','&pound;','&curren;','&yen;','&brvbar;','&sect;','&uml;','&copy;','&ordf;','&laquo;','&not;','&shy;','&reg;','&macr;','&deg;','&plusmn;','&sup2;','&sup3;','&acute;','&micro;','&para;','&middot;','&cedil;','&sup1;','&ordm;','&raquo;','&frac14;','&frac12;','&frac34;','&iquest;','&agrave;','&aacute;','&acirc;','&atilde;','&Auml;','&aring;','&aelig;','&ccedil;','&egrave;','&eacute;','&ecirc;','&euml;','&igrave;','&iacute;','&icirc;','&iuml;','&eth;','&ntilde;','&ograve;','&oacute;','&ocirc;','&otilde;','&Ouml;','&times;','&oslash;','&ugrave;','&uacute;','&ucirc;','&Uuml;','&yacute;','&thorn;','&szlig;','&agrave;','&aacute;','&acirc;','&atilde;','&auml;','&aring;','&aelig;','&ccedil;','&egrave;','&eacute;','&ecirc;','&euml;','&igrave;','&iacute;','&icirc;','&iuml;','&eth;','&ntilde;','&ograve;','&oacute;','&ocirc;','&otilde;','&ouml;','&divide;','&Oslash;','&ugrave;','&uacute;','&ucirc;','&uuml;','&yacute;','&thorn;','&yuml;','&quot;','&amp;','&lt;','&gt;','&oelig;','&oelig;','&scaron;','&scaron;','&yuml;','&circ;','&tilde;','&ensp;','&emsp;','&thinsp;','&zwnj;','&zwj;','&lrm;','&rlm;','&ndash;','&mdash;','&lsquo;','&rsquo;','&sbquo;','&ldquo;','&rdquo;','&bdquo;','&dagger;','&dagger;','&permil;','&lsaquo;','&rsaquo;','&euro;','&fnof;','&alpha;','&beta;','&gamma;','&delta;','&epsilon;','&zeta;','&eta;','&theta;','&iota;','&kappa;','&lambda;','&mu;','&nu;','&xi;','&omicron;','&pi;','&rho;','&sigma;','&tau;','&upsilon;','&phi;','&chi;','&psi;','&omega;','&alpha;','&beta;','&gamma;','&delta;','&epsilon;','&zeta;','&eta;','&theta;','&iota;','&kappa;','&lambda;','&mu;','&nu;','&xi;','&omicron;','&pi;','&rho;','&sigmaf;','&sigma;','&tau;','&upsilon;','&phi;','&chi;','&psi;','&omega;','&thetasym;','&upsih;','&piv;','&bull;','&hellip;','&prime;','&prime;','&oline;','&frasl;','&weierp;','&image;','&real;','&trade;','&alefsym;','&larr;','&uarr;','&rarr;','&darr;','&harr;','&crarr;','&larr;','&uarr;','&rarr;','&darr;','&harr;','&forall;','&part;','&exist;','&empty;','&nabla;','&isin;','&notin;','&ni;','&prod;','&sum;','&minus;','&lowast;','&radic;','&prop;','&infin;','&ang;','&and;','&or;','&cap;','&cup;','&int;','&there4;','&sim;','&cong;','&asymp;','&ne;','&equiv;','&le;','&ge;','&sub;','&sup;','&nsub;','&sube;','&supe;','&oplus;','&otimes;','&perp;','&sdot;','&lceil;','&rceil;','&lfloor;','&rfloor;','&lang;','&rang;','&loz;','&spades;','&clubs;','&hearts;','&diams;');
          var arr2 = new Array('&#160;','&#161;','&#162;','&#163;','&#164;','&#165;','&#166;','&#167;','&#168;','&#169;','&#170;','&#171;','&#172;','&#173;','&#174;','&#175;','&#176;','&#177;','&#178;','&#179;','&#180;','&#181;','&#182;','&#183;','&#184;','&#185;','&#186;','&#187;','&#188;','&#189;','&#190;','&#191;','&#192;','&#193;','&#194;','&#195;','&#196;','&#197;','&#198;','&#199;','&#200;','&#201;','&#202;','&#203;','&#204;','&#205;','&#206;','&#207;','&#208;','&#209;','&#210;','&#211;','&#212;','&#213;','&#214;','&#215;','&#216;','&#217;','&#218;','&#219;','&#220;','&#221;','&#222;','&#223;','&#224;','&#225;','&#226;','&#227;','&#228;','&#229;','&#230;','&#231;','&#232;','&#233;','&#234;','&#235;','&#236;','&#237;','&#238;','&#239;','&#240;','&#241;','&#242;','&#243;','&#244;','&#245;','&#246;','&#247;','&#248;','&#249;','&#250;','&#251;','&#252;','&#253;','&#254;','&#255;','&#34;','&#38;','&#60;','&#62;','&#338;','&#339;','&#352;','&#353;','&#376;','&#710;','&#732;','&#8194;','&#8195;','&#8201;','&#8204;','&#8205;','&#8206;','&#8207;','&#8211;','&#8212;','&#8216;','&#8217;','&#8218;','&#8220;','&#8221;','&#8222;','&#8224;','&#8225;','&#8240;','&#8249;','&#8250;','&#8364;','&#402;','&#913;','&#914;','&#915;','&#916;','&#917;','&#918;','&#919;','&#920;','&#921;','&#922;','&#923;','&#924;','&#925;','&#926;','&#927;','&#928;','&#929;','&#931;','&#932;','&#933;','&#934;','&#935;','&#936;','&#937;','&#945;','&#946;','&#947;','&#948;','&#949;','&#950;','&#951;','&#952;','&#953;','&#954;','&#955;','&#956;','&#957;','&#958;','&#959;','&#960;','&#961;','&#962;','&#963;','&#964;','&#965;','&#966;','&#967;','&#968;','&#969;','&#977;','&#978;','&#982;','&#8226;','&#8230;','&#8242;','&#8243;','&#8254;','&#8260;','&#8472;','&#8465;','&#8476;','&#8482;','&#8501;','&#8592;','&#8593;','&#8594;','&#8595;','&#8596;','&#8629;','&#8656;','&#8657;','&#8658;','&#8659;','&#8660;','&#8704;','&#8706;','&#8707;','&#8709;','&#8711;','&#8712;','&#8713;','&#8715;','&#8719;','&#8721;','&#8722;','&#8727;','&#8730;','&#8733;','&#8734;','&#8736;','&#8743;','&#8744;','&#8745;','&#8746;','&#8747;','&#8756;','&#8764;','&#8773;','&#8776;','&#8800;','&#8801;','&#8804;','&#8805;','&#8834;','&#8835;','&#8836;','&#8838;','&#8839;','&#8853;','&#8855;','&#8869;','&#8901;','&#8968;','&#8969;','&#8970;','&#8971;','&#9001;','&#9002;','&#9674;','&#9824;','&#9827;','&#9829;','&#9830;');
          return this.swapArrayVals(s,arr1,arr2);
      },
  
      // Convert Numerical entities into HTML entities
      NumericalToHTML : function(s){
          var arr1 = new Array('&#160;','&#161;','&#162;','&#163;','&#164;','&#165;','&#166;','&#167;','&#168;','&#169;','&#170;','&#171;','&#172;','&#173;','&#174;','&#175;','&#176;','&#177;','&#178;','&#179;','&#180;','&#181;','&#182;','&#183;','&#184;','&#185;','&#186;','&#187;','&#188;','&#189;','&#190;','&#191;','&#192;','&#193;','&#194;','&#195;','&#196;','&#197;','&#198;','&#199;','&#200;','&#201;','&#202;','&#203;','&#204;','&#205;','&#206;','&#207;','&#208;','&#209;','&#210;','&#211;','&#212;','&#213;','&#214;','&#215;','&#216;','&#217;','&#218;','&#219;','&#220;','&#221;','&#222;','&#223;','&#224;','&#225;','&#226;','&#227;','&#228;','&#229;','&#230;','&#231;','&#232;','&#233;','&#234;','&#235;','&#236;','&#237;','&#238;','&#239;','&#240;','&#241;','&#242;','&#243;','&#244;','&#245;','&#246;','&#247;','&#248;','&#249;','&#250;','&#251;','&#252;','&#253;','&#254;','&#255;','&#34;','&#38;','&#60;','&#62;','&#338;','&#339;','&#352;','&#353;','&#376;','&#710;','&#732;','&#8194;','&#8195;','&#8201;','&#8204;','&#8205;','&#8206;','&#8207;','&#8211;','&#8212;','&#8216;','&#8217;','&#8218;','&#8220;','&#8221;','&#8222;','&#8224;','&#8225;','&#8240;','&#8249;','&#8250;','&#8364;','&#402;','&#913;','&#914;','&#915;','&#916;','&#917;','&#918;','&#919;','&#920;','&#921;','&#922;','&#923;','&#924;','&#925;','&#926;','&#927;','&#928;','&#929;','&#931;','&#932;','&#933;','&#934;','&#935;','&#936;','&#937;','&#945;','&#946;','&#947;','&#948;','&#949;','&#950;','&#951;','&#952;','&#953;','&#954;','&#955;','&#956;','&#957;','&#958;','&#959;','&#960;','&#961;','&#962;','&#963;','&#964;','&#965;','&#966;','&#967;','&#968;','&#969;','&#977;','&#978;','&#982;','&#8226;','&#8230;','&#8242;','&#8243;','&#8254;','&#8260;','&#8472;','&#8465;','&#8476;','&#8482;','&#8501;','&#8592;','&#8593;','&#8594;','&#8595;','&#8596;','&#8629;','&#8656;','&#8657;','&#8658;','&#8659;','&#8660;','&#8704;','&#8706;','&#8707;','&#8709;','&#8711;','&#8712;','&#8713;','&#8715;','&#8719;','&#8721;','&#8722;','&#8727;','&#8730;','&#8733;','&#8734;','&#8736;','&#8743;','&#8744;','&#8745;','&#8746;','&#8747;','&#8756;','&#8764;','&#8773;','&#8776;','&#8800;','&#8801;','&#8804;','&#8805;','&#8834;','&#8835;','&#8836;','&#8838;','&#8839;','&#8853;','&#8855;','&#8869;','&#8901;','&#8968;','&#8969;','&#8970;','&#8971;','&#9001;','&#9002;','&#9674;','&#9824;','&#9827;','&#9829;','&#9830;');
          var arr2 = new Array('&nbsp;','&iexcl;','&cent;','&pound;','&curren;','&yen;','&brvbar;','&sect;','&uml;','&copy;','&ordf;','&laquo;','&not;','&shy;','&reg;','&macr;','&deg;','&plusmn;','&sup2;','&sup3;','&acute;','&micro;','&para;','&middot;','&cedil;','&sup1;','&ordm;','&raquo;','&frac14;','&frac12;','&frac34;','&iquest;','&agrave;','&aacute;','&acirc;','&atilde;','&Auml;','&aring;','&aelig;','&ccedil;','&egrave;','&eacute;','&ecirc;','&euml;','&igrave;','&iacute;','&icirc;','&iuml;','&eth;','&ntilde;','&ograve;','&oacute;','&ocirc;','&otilde;','&Ouml;','&times;','&oslash;','&ugrave;','&uacute;','&ucirc;','&Uuml;','&yacute;','&thorn;','&szlig;','&agrave;','&aacute;','&acirc;','&atilde;','&auml;','&aring;','&aelig;','&ccedil;','&egrave;','&eacute;','&ecirc;','&euml;','&igrave;','&iacute;','&icirc;','&iuml;','&eth;','&ntilde;','&ograve;','&oacute;','&ocirc;','&otilde;','&ouml;','&divide;','&Oslash;','&ugrave;','&uacute;','&ucirc;','&uuml;','&yacute;','&thorn;','&yuml;','&quot;','&amp;','&lt;','&gt;','&oelig;','&oelig;','&scaron;','&scaron;','&yuml;','&circ;','&tilde;','&ensp;','&emsp;','&thinsp;','&zwnj;','&zwj;','&lrm;','&rlm;','&ndash;','&mdash;','&lsquo;','&rsquo;','&sbquo;','&ldquo;','&rdquo;','&bdquo;','&dagger;','&dagger;','&permil;','&lsaquo;','&rsaquo;','&euro;','&fnof;','&alpha;','&beta;','&gamma;','&delta;','&epsilon;','&zeta;','&eta;','&theta;','&iota;','&kappa;','&lambda;','&mu;','&nu;','&xi;','&omicron;','&pi;','&rho;','&sigma;','&tau;','&upsilon;','&phi;','&chi;','&psi;','&omega;','&alpha;','&beta;','&gamma;','&delta;','&epsilon;','&zeta;','&eta;','&theta;','&iota;','&kappa;','&lambda;','&mu;','&nu;','&xi;','&omicron;','&pi;','&rho;','&sigmaf;','&sigma;','&tau;','&upsilon;','&phi;','&chi;','&psi;','&omega;','&thetasym;','&upsih;','&piv;','&bull;','&hellip;','&prime;','&prime;','&oline;','&frasl;','&weierp;','&image;','&real;','&trade;','&alefsym;','&larr;','&uarr;','&rarr;','&darr;','&harr;','&crarr;','&larr;','&uarr;','&rarr;','&darr;','&harr;','&forall;','&part;','&exist;','&empty;','&nabla;','&isin;','&notin;','&ni;','&prod;','&sum;','&minus;','&lowast;','&radic;','&prop;','&infin;','&ang;','&and;','&or;','&cap;','&cup;','&int;','&there4;','&sim;','&cong;','&asymp;','&ne;','&equiv;','&le;','&ge;','&sub;','&sup;','&nsub;','&sube;','&supe;','&oplus;','&otimes;','&perp;','&sdot;','&lceil;','&rceil;','&lfloor;','&rfloor;','&lang;','&rang;','&loz;','&spades;','&clubs;','&hearts;','&diams;');
          return this.swapArrayVals(s,arr1,arr2);
      },
  
  
      // Numerically encodes all unicode characters
      numEncode : function(s){
  
          if(this.isEmpty(s)) return "";
  
          var e = "";
          for (var i = 0; i < s.length; i++)
          {
              var c = s.charAt(i);
              if (c < " " || c > "~")
              {
                  c = "&#" + c.charCodeAt() + ";";
              }
              e += c;
          }
          return e;
      },
  
      // HTML Decode numerical and HTML entities back to original values
      htmlDecode : function(s){
  
          var c,m,d = s;
  
          if(this.isEmpty(d)) return "";
  
          // convert HTML entites back to numerical entites first
          d = this.HTML2Numerical(d);
  
          // look for numerical entities &#34;
          arr=d.match(/&#[0-9]{1,5};/g);
  
          // if no matches found in string then skip
          if(arr!=null){
              for(var x=0;x<arr.length;x++){
                  m = arr[x];
                  c = m.substring(2,m.length-1); //get numeric part which is refernce to unicode character
                  // if its a valid number we can decode
                  if(c >= -32768 && c <= 65535){
                      // decode every single match within string
                      d = d.replace(m, String.fromCharCode(c));
                  }else{
                      d = d.replace(m, ""); //invalid so replace with nada
                  }
              }
          }
  
          return d;
      },
  
      // encode an input string into either numerical or HTML entities
      htmlEncode : function(s,dbl){
  
          if(this.isEmpty(s)) return "";
  
          // do we allow double encoding? E.g will &amp; be turned into &amp;amp;
          dbl = dbl || false; //default to prevent double encoding
  
          // if allowing double encoding we do ampersands first
          if(dbl){
              if(this.EncodeType=="numerical"){
                  s = s.replace(/&/g, "&#38;");
              }else{
                  s = s.replace(/&/g, "&amp;");
              }
          }
  
          // convert the xss chars to numerical entities ' " < >
          s = this.XSSEncode(s,false);
  
          if(this.EncodeType=="numerical" || !dbl){
              // Now call function that will convert any HTML entities to numerical codes
              s = this.HTML2Numerical(s);
          }
  
          // Now encode all chars above 127 e.g unicode
          s = this.numEncode(s);
  
          // now we know anything that needs to be encoded has been converted to numerical entities we
          // can encode any ampersands & that are not part of encoded entities
          // to handle the fact that I need to do a negative check and handle multiple ampersands &&&
          // I am going to use a placeholder
  
          // if we don't want double encoded entities we ignore the & in existing entities
          if(!dbl){
              s = s.replace(/&#/g,"##AMPHASH##");
  
              if(this.EncodeType=="numerical"){
                  s = s.replace(/&/g, "&#38;");
              }else{
                  s = s.replace(/&/g, "&amp;");
              }
  
              s = s.replace(/##AMPHASH##/g,"&#");
          }
  
          // replace any malformed entities
          s = s.replace(/&#\d*([^\d;]|$)/g, "$1");
  
          if(!dbl){
              // safety check to correct any double encoded &amp;
              s = this.correctEncoding(s);
          }
  
          // now do we need to convert our numerical encoded string into entities
          if(this.EncodeType=="entity"){
              s = this.NumericalToHTML(s);
          }
  
          return s;
      },
  
      // Encodes the basic 4 characters used to malform HTML in XSS hacks
      XSSEncode : function(s,en){
          if(!this.isEmpty(s)){
              en = en || true;
              // do we convert to numerical or html entity?
              if(en){
                  s = s.replace(/\'/g,"&#39;"); //no HTML equivalent as &apos is not cross browser supported
                  s = s.replace(/\"/g,"&quot;");
                  s = s.replace(/</g,"&lt;");
                  s = s.replace(/>/g,"&gt;");
              }else{
                  s = s.replace(/\'/g,"&#39;"); //no HTML equivalent as &apos is not cross browser supported
                  s = s.replace(/\"/g,"&#34;");
                  s = s.replace(/</g,"&#60;");
                  s = s.replace(/>/g,"&#62;");
              }
              return s;
          }else{
              return "";
          }
      },
  
      // returns true if a string contains html or numerical encoded entities
      hasEncoded : function(s){
          if(/&#[0-9]{1,5};/g.test(s)){
              return true;
          }else if(/&[A-Z]{2,6};/gi.test(s)){
              return true;
          }else{
              return false;
          }
      },
  
      // will remove any unicode characters
      stripUnicode : function(s){
          return s.replace(/[^\x20-\x7E]/g,"");
  
      },
  
      // corrects any double encoded &amp; entities e.g &amp;amp;
      correctEncoding : function(s){
          return s.replace(/(&amp;)(amp;)+/,"$1");
      },
  
  
      // Function to loop through an array swaping each item with the value from another array e.g swap HTML entities with Numericals
      swapArrayVals : function(s,arr1,arr2){
          if(this.isEmpty(s)) return "";
          var re;
          if(arr1 && arr2){
              //ShowDebug("in swapArrayVals arr1.length = " + arr1.length + " arr2.length = " + arr2.length)
              // array lengths must match
              if(arr1.length == arr2.length){
                  for(var x=0,i=arr1.length;x<i;x++){
                      re = new RegExp(arr1[x], 'g');
                      s = s.replace(re,arr2[x]); //swap arr1 item with matching item from arr2
                  }
              }
          }
          return s;
      },
  
      inArray : function( item, arr ) {
          for ( var i = 0, x = arr.length; i < x; i++ ){
              if ( arr[i] === item ){
                  return i;
              }
          }
          return -1;
      }
  
  };
  
  var DOMParser;
  var CSL_NODEJS_JSDOM = function () {
          DOMParser = function() {};
          DOMParser.prototype.parseFromString = function(str, contentType) {
              var jsdom = require("jsdom").jsdom;
              return jsdom(str);
              /*
              if ("undefined" != typeof ActiveXObject) {
                  var xmldata = new ActiveXObject('MSXML.DomDocument');
                  xmldata.async = false;
                  xmldata.loadXML(str);
                  return xmldata;
              } else if ("undefined" != typeof XMLHttpRequest) {
                  var xmldata = new XMLHttpRequest;
                  if (!contentType) {
                      contentType = 'text/xml';
                  }
                  xmldata.open('GET', 'data:' + contentType + ';charset=utf-8,' + encodeURIComponent(str), false);
                  if(xmldata.overrideMimeType) {
                      xmldata.overrideMimeType(contentType);
                  }
                  xmldata.send(null);
                  return xmldata.responseXML;
              }
              */
          };
          /*
          this.hasAttributes = function (node) {
              var ret;
              if (node.attributes && node.attributes.length) {
                  ret = true;
              } else {
                  ret = false;
              }
              return ret;
          };
          */
      this.hasAttributes = function (node) {
          zotero.Debug('CSL_NODEJS.hasAttributes', 3);
          zotero.Debug("node.hasAttributes(): " + node.hasAttributes(), 3);
          //zotero.Debug(node);
          return node.hasAttributes();
      };
      this.importNode = function (doc, srcElement) {
          zotero.Debug('CSL_NODEJS.importNode', 3);
          if ("undefined" == typeof doc.importNode) {
              var ret = this._importNode(doc, srcElement, true);
          } else {
              var ret = doc.importNode(srcElement, true);
          }
          return ret;
      };
      this._importNode = function(doc, node, allChildren) {
          zotero.Debug('CSL_NODEJS._importNode', 3);
          switch (node.nodeType) {
              case 1:
                  var newNode = doc.createElement(node.nodeName);
                  if (node.attributes && node.attributes.length > 0)
                      for (var i = 0, il = node.attributes.length; i < il;)
                          newNode.setAttribute(node.attributes[i].nodeName, node.getAttribute(node.attributes[i++].nodeName));
                      if (allChildren && node.childNodes && node.childNodes.length > 0)
                          for (var i = 0, il = node.childNodes.length; i < il;)
                              newNode.appendChild(this._importNode(doc, node.childNodes[i++], allChildren));
                  return newNode;
                  break;
              case 3:
              case 4:
              case 8:
          }
      };
      this.parser = new DOMParser();
      // This seems horribly tormented, but there might be a reason for it.
      // Perhaps this was the only way I found to get namespacing to work ... ?
      var str = "<docco><institution institution-parts=\"long\" delimiter=\", \" substitute-use-first=\"1\" use-last=\"1\"><institution-part name=\"long\"></institution></docco>";
      var inst_doc = this.parser.parseFromString(str, "text/xml");
      var inst_node = inst_doc.getElementsByTagName("institution");
      this.institution = inst_node.item(0);
      var inst_part_node = inst_doc.getElementsByTagName("institution-part");
      this.institutionpart = inst_part_node.item(0);
      this.ns = "http://purl.org/net/xbiblio/csl";
  };
  CSL_NODEJS_JSDOM.prototype.clean = function (xml) {
      zotero.Debug('CSL_NODEJS.clean', 3);
      xml = xml.replace(/<\?[^?]+\?>/g, "");
      xml = xml.replace(/<![^>]+>/g, "");
      xml = xml.replace(/^\s+/, "");
      xml = xml.replace(/\s+$/, "");
      xml = xml.replace(/^\n*/, "");
      return xml;
  };
  CSL_NODEJS_JSDOM.prototype.getStyleId = function (myxml) {
      var text = "";
      var node = myxml.getElementsByTagName("id");
      if (node && node.length) {
          node = node.item(0);
      }
      if (node) {
          // W3C conformant browsers
          text = node.textContent;
      }
      return text;
  };
  
  CSL_NODEJS_JSDOM.prototype.children = function (myxml) {
      zotero.Debug("children called on: " + myxml, 3);
      var children, pos, len, ret;
      if (myxml) {
          ret = [];
          children = myxml.childNodes;
          for (pos = 0, len = children.length; pos < len; pos += 1) {
              if (children[pos].nodeName != "#text") {
                  ret.push(children[pos]);
              }
          }
          return ret;
      } else {
          return [];
      }
  };
  CSL_NODEJS_JSDOM.prototype.nodename = function (myxml) {
      zotero.Debug('CSL_NODEJS.nodename ' + myxml, 3);
      //zotero.Debug(myxml);
      var ret = myxml.nodeName;
      if(ret) ret = ret.toLowerCase();
      zotero.Debug("node name = " + ret, 3);
      return ret;
  };
  CSL_NODEJS_JSDOM.prototype.attributes = function (myxml) {
      zotero.Debug('CSL_NODEJS.attributes');
      var ret, attrs, attr, key, xml, pos, len;
      ret = new Object();
      if (myxml && this.hasAttributes(myxml)) {
          attrs = myxml.attributes;
          for (pos = 0, len=attrs.length; pos < len; pos += 1) {
              attr = attrs[pos];
              var v = attr.value;
              //v = v.replace('&lt;', '&60;').replace('&gt;', '&62').replace('&quot;', '&34').replace('&amp;', '&38;');
              v = Encoder.htmlDecode(v);
              ret["@" + attr.name] = v;
          }
      }
      return ret;
  };
  CSL_NODEJS_JSDOM.prototype.content = function (myxml) {
      zotero.Debug('CSL_NODEJS.content', 3);
      var ret;
      if ("undefined" != typeof myxml.textContent) {
          ret = myxml.textContent;
      } else if ("undefined" != typeof myxml.innerText) {
          ret = myxml.innerText;
      } else {
          ret = myxml.txt;
      }
      return ret;
  };
  CSL_NODEJS_JSDOM.prototype.namespace = {
      "xml":"http://www.w3.org/XML/1998/namespace"
  }
  CSL_NODEJS_JSDOM.prototype.numberofnodes = function (myxml) {
      zotero.Debug('CSL_NODEJS.numberofnodes', 3);
      if (myxml) {
          zotero.Debug(myxml.length);
          return myxml.length;
      } else {
          zotero.Debug('0');
          return 0;
      }
  };
  CSL_NODEJS_JSDOM.prototype.getAttributeName = function (attr) {
      zotero.Debug('CSL_NODEJS.getAttributeName', 3);
      var ret = attr.name;
      return ret;
  }
  CSL_NODEJS_JSDOM.prototype.getAttributeValue = function (myxml,name,namespace) {
      zotero.Debug('CSL_NODEJS.getAttributeValue', 3);
      var ret = "";
      if (myxml && this.hasAttributes(myxml) && myxml.getAttribute(name)) {
          ret = myxml.getAttribute(name);
      }
      return ret;
  }
  CSL_NODEJS_JSDOM.prototype.getNodeValue = function (myxml,name) {
      zotero.Debug('CSL_NODEJS.getNodeValue : ' + name, 3);
      var ret = "";
      if (name){
          var vals = myxml.getElementsByTagName(name);
          if (vals.length > 0) {
              if ("undefined" != typeof vals[0].textContent) {
                  ret = vals[0].textContent;
              } else if ("undefined" != typeof vals[0].innerText) {
                  ret = vals[0].innerText;
              } else {
                  ret = vals[0].text;
              }
          }
      } else {
          ret = myxml;
      }
      if (ret && ret.childNodes && (ret.childNodes.length == 0 || (ret.childNodes.length == 1 && ret.firstChild.nodeName == "#text"))) {
          if ("undefined" != typeof ret.textContent) {
              ret = ret.textContent;
          } else if ("undefined" != typeof ret.innerText) {
              ret = ret.innerText;
          } else {
              ret = ret.text;
          }
      }
      return ret;
  }
  CSL_NODEJS_JSDOM.prototype.setAttributeOnNodeIdentifiedByNameAttribute = function (myxml,nodename,partname,attrname,val) {
      zotero.Debug('CSL_NODEJS.setAttributeOnNodeIdentifiedByNameAttribute', 3);
      var pos, len, xml, nodes, node;
      if (attrname.slice(0,1) === '@'){
          attrname = attrname.slice(1);
      }
      nodes = myxml.getElementsByTagName(nodename);
      for (pos = 0, len = nodes.length; pos < len; pos += 1) {
          node = nodes[pos];
          if (node.getAttribute("name") != partname) {
              continue;
          }
          node.setAttribute(attrname, val);
      }
  }
  CSL_NODEJS_JSDOM.prototype.deleteNodeByNameAttribute = function (myxml,val) {
      zotero.Debug('CSL_NODEJS.deleteNodeByNameAttribute', 3);
      var pos, len, node, nodes;
      nodes = myxml.childNodes;
      for (pos = 0, len = nodes.length; pos < len; pos += 1) {
          node = nodes[pos];
          if (!node || node.nodeType == node.TEXT_NODE) {
              continue;
          }
          if (this.hasAttributes(node) && node.getAttribute("name") == val) {
              myxml.removeChild(nodes[pos]);
          }
      }
  }
  CSL_NODEJS_JSDOM.prototype.deleteAttribute = function (myxml,attr) {
      zotero.Debug('CSL_NODEJS.deleteAttribute', 3);
      myxml.removeAttribute(attr);
  }
  CSL_NODEJS_JSDOM.prototype.setAttribute = function (myxml,attr,val) {
      zotero.Debug('CSL_NODEJS.setAttribute', 3);
      var attribute;
      if (!myxml.ownerDocument) {
          myxml = myxml.firstChild;
      }
      attribute = myxml.ownerDocument.createAttribute(attr);
      zotero.Debug('created attribute:');
      //zotero.Debug(attribute);
      myxml.setAttribute(attr, val);
      return false;
  }
  CSL_NODEJS_JSDOM.prototype.nodeCopy = function (myxml) {
      zotero.Debug('CSL_NODEJS.nodeCopy', 3);
      var cloned_node = myxml.cloneNode(true);
      return cloned_node;
  }
  CSL_NODEJS_JSDOM.prototype.getNodesByName = function (myxml,name,nameattrval) {
      zotero.Debug('CSL_NODEJS.getNodesByName : ' + name + " = " + nameattrval, 3);
      var ret, nodes, node, pos, len;
      ret = [];
      if(typeof myxml.getElementsByTagName == 'undefined'){
          return ret;
      }
      nodes = myxml.getElementsByTagName(name);
      for (pos = 0, len = nodes.length; pos < len; pos += 1) {
          node = nodes.item(pos);
          if (nameattrval && !(this.hasAttributes(node) && node.getAttribute("name") == nameattrval)) {
              continue;
          }
          ret.push(node);
      }
      return ret;
  }
  CSL_NODEJS_JSDOM.prototype.nodeNameIs = function (myxml,name) {
      zotero.Debug('CSL_NODEJS.nodeNameIs', 3);
      if (name == myxml.nodeName.toLowerCase()) {
          return true;
      }
      return false;
  }
  CSL_NODEJS_JSDOM.prototype.makeXml = function (myxml) {
      zotero.Debug('CSL_NODEJS.makeXml', 3);
      var ret, topnode;
      if (!myxml) {
          myxml = "<docco><bogus/></docco>";
      }
      myxml = myxml.replace(/\s*<\?[^>]*\?>\s*\n*/g, "");
      myxml = myxml.replace(/<style\s/, "<cslstyle ").replace("</style", "</cslstyle");
      myxml = myxml.trim();
      //console.log(myxml);
      //process.exit();
      //console.error(myxml);
      //process.exit();
      var doc = this.parser.parseFromString(myxml, "application/xml");
      //console.log("doc : " + doc.nodeName + " : " + doc.nodeValue);
      //console.log(doc);
      var firstChild = doc.firstChild;
      //console.log("firstChild : " + firstChild.nodeName + " : " + firstChild.nodeValue);
      var cslstylenodes = doc.getElementsByTagName('cslstyle');
      var snode = cslstylenodes.item(0);
      //zotero.Debug("snode : " + snode.nodeName + " : " + snode.nodeValue);
  
      if(snode){
          return snode;
      }
      else{
          return firstChild;
      }
  };
  CSL_NODEJS_JSDOM.prototype.insertChildNodeAfter = function (parent,node,pos,datexml) {
      zotero.Debug('CSL_NODEJS.insertChildNodeAfter', 3);
      var myxml, xml;
      myxml = this.importNode(node.ownerDocument, datexml);
      parent.replaceChild(myxml, node);
      return parent;
   };
  CSL_NODEJS_JSDOM.prototype.insertPublisherAndPlace = function(myxml) {
      zotero.Debug('CSL_NODEJS.insertPublisherAndPlace', 5);
      var group = myxml.getElementsByTagName("group");
      for (var i = 0, ilen = group.length; i < ilen; i += 1) {
          var node = group.item(i);
          if (node.childNodes.length === 2) {
              var twovars = [];
              for (var j = 0, jlen = 2; j < jlen; j += 1) {
                  var child = node.childNodes.item(j);
                  if (child.childNodes.length === 0) {
                      twovars.push(child.getAttribute('variable'));
                      if (child.getAttribute('suffix')
                          || child.getAttribute('prefix')) {
                          twovars = [];
                          break;
                      }
                  }
              }
              if (twovars.indexOf("publisher") > -1 && twovars.indexOf("publisher-place") > -1) {
                  node.setAttribute('has-publisher-and-publisher-place', true);
              }
          }
      }
  };
  CSL_NODEJS_JSDOM.prototype.addMissingNameNodes = function(myxml) {
      var nameslist = myxml.getElementsByTagName("names");
      for (var i = 0, ilen = nameslist.length; i < ilen; i += 1) {
          var names = nameslist.item(i);
          var namelist = names.getElementsByTagName("name");
          if ((!namelist || namelist.length === 0)
              && names.parentNode.tagName.toLowerCase() !== "substitute") {
  
              var doc = names.ownerDocument;
              var name = doc.createElement("name");
              names.appendChild(name);
          }
      };
  };
  
  CSL_NODEJS_JSDOM.prototype.addInstitutionNodes = function(myxml) {
      zotero.Debug('CSL_NODEJS.addInstitutionNodes');
      //zotero.Debug("myxml : " + myxml.nodeName + " : " + myxml.nodeValue);
      /*
      var names, thenames, institution, theinstitution, name, thename, xml, pos, len;
      names = myxml.getElementsByTagName("names");
      for (pos = 0, len = names.length; pos < len; pos += 1) {
          thenames = names.item(pos);
          name = thenames.getElementsByTagName("name");
          if (name.length == 0) {
              continue;
          }
          institution = thenames.getElementsByTagName("institution");
          if (institution.length == 0) {
              theinstitution = this.importNode(myxml.ownerDocument, this.institution);
              theinstitutionpart = theinstitution.getElementsByTagName("institution-part").item(0);
              thename = name.item(0);
              thenames.insertBefore(theinstitution, thename.nextSibling);
          }
      }
      */
      var names, thenames, institution, theinstitution, name, thename, xml, pos, len;
      var CSL_INSTITUTION_KEYS = [
          "font-style",
          "font-variant",
          "font-weight",
          "text-decoration",
          "text-case"
      ];
      names = myxml.getElementsByTagName("names");
      for (pos = 0, len = names.length; pos < len; pos += 1) {
          thenames = names.item(pos);
          name = thenames.getElementsByTagName("name");
          if (name.length == 0) {
              continue;
          }
          institution = thenames.getElementsByTagName("institution");
          if (institution.length == 0) {
              theinstitution = this.importNode(myxml.ownerDocument, this.institution);
              theinstitutionpart = theinstitution.getElementsByTagName("institution-part").item(0);
              thename = name.item(0);
              thenames.insertBefore(theinstitution, thename.nextSibling);
              for (var j = 0, jlen = CSL_INSTITUTION_KEYS.length; j < jlen; j += 1) {
                  var attrname = CSL_INSTITUTION_KEYS[j];
                  var attrval = thename.getAttribute(attrname);
                  if (attrval) {
                      theinstitutionpart.setAttribute(attrname, attrval);
                  }
              }
              var nameparts = thename.getElementsByTagName("name-part");
              for (var j = 0, jlen = nameparts.length; j < jlen; j += 1) {
                  if ('family' === nameparts[j].getAttribute('name')) {
                      for (var k = 0, klen = CSL_INSTITUTION_KEYS.length; k < klen; k += 1) {
                          var attrname = CSL_INSTITUTION_KEYS[k];
                          var attrval = nameparts[j].getAttribute(attrname);
                          if (attrval) {
                              theinstitutionpart.setAttribute(attrname, attrval);
                          }
                      }
                  }
              }
          }
      }
      zotero.Debug("done with addInstitutionNodes");
  };
  
  CSL_NODEJS_JSDOM.prototype.flagDateMacros = function(myxml) {
      var pos, len, thenode, thedate;
      nodes = myxml.getElementsByTagName("macro");
      for (pos = 0, len = nodes.length; pos < len; pos += 1) {
          thenode = nodes.item(pos);
          thedate = thenode.getElementsByTagName("date");
          if (thedate.length) {
              thenode.setAttribute('macro-has-date', 'true');
          }
      }
  };
  
  
  exports.CSL_NODEJS_JSDOM = CSL_NODEJS_JSDOM;
  