<commit_before>'use strict';

const util = require('./util');
const buildOptions = require('./util').buildOptions;
const xmlNode = require('./xmlNode');
const toNumber = require("strnum");

const regx =
  '<((!\\[CDATA\\[([\\s\\S]*?)(]]>))|((NAME:)?(NAME))([^>]*)>|((\\/)(NAME)\\s*>))([^<]*)'
  .replace(/NAME/g, util.nameRegexp);

//const tagsRegx = new RegExp("<(\\/?[\\w:\\-\._]+)([^>]*)>(\\s*"+cdataRegx+")*([^<]+)?","g");
//const tagsRegx = new RegExp("<(\\/?)((\\w*:)?([\\w:\\-\._]+))([^>]*)>([^<]*)("+cdataRegx+"([^<]*))*([^<]+)?","g");

//polyfill
if (!Number.parseInt && window.parseInt) {
  Number.parseInt = window.parseInt;
}
if (!Number.parseFloat && window.parseFloat) {
  Number.parseFloat = window.parseFloat;
}

const defaultOptions = {
  attributeNamePrefix: '@_',
  attrNodeName: false,
  textNodeName: '#text',
  ignoreAttributes: true,
  ignoreNameSpace: false,
  allowBooleanAttributes: false, //a tag can have attributes without any value
  //ignoreRootElement : false,
  parseNodeValue: true,
  parseAttributeValue: false,
  trimValues: true, //Trim string values of tag and attributes
  cdataTagName: false,
  numParseOptions: {
    hex: true,
    leadingZeros: true
  },
  tagValueProcessor: function(tagName, val) {
    return val;
  },
  attrValueProcessor: function(attrName, val) {
    return val;
  },
  stopNodes: [],
  alwaysCreateTextNode: false,
  //decodeStrict: false,
  isArray: () => false
};

exports.defaultOptions = defaultOptions;

const props = [
  'attributeNamePrefix', //done
  'attrNodeName', //done
  'textNodeName', //done
  'ignoreAttributes', //done
  'ignoreNameSpace', //done
  'allowBooleanAttributes', //done
  'parseNodeValue', //done
  'parseAttributeValue', //done
  'trimValues', //done
  'cdataTagName',//done
  'tagValueProcessor',//done
  'attrValueProcessor',//done
  'numParseOptions', //done
  'stopNodes', //done
  'alwaysCreateTextNode', //done
  'isArray', //done
];
exports.props = props;

/**
 * @param {string} val
 * @param {object} options
 */
function parseValue(val, options, tagName, jPath) {
  if (val !== undefined) {
    if (options.trimValues) {
      val = val.trim();
    }
    if(val.length > 0){
      const newval = options.tagValueProcessor(tagName, val, jPath);
      if(newval === null || newval === undefined){
        //don't parse
        return val;
      }else if(typeof newval !== typeof val || newval !== val){
        //overwrite
        return newval;
      }else{
        return _parseValue(val, options.parseNodeValue, options.numParseOptions);
      }
    }
  }
}

function resolveNameSpace(tagname, options) {
  if (options.ignoreNameSpace) {
    const tags = tagname.split(':');
    const prefix = tagname.charAt(0) === '/' ? '/' : '';
    if (tags[0] === 'xmlns') {
      return '';
    }
    if (tags.length === 2) {
      tagname = prefix + tags[1];
    }
  }
  return tagname;
}

function _parseValue(val, shouldParse, options) {
  if (shouldParse && typeof val === 'string') {
    //console.log(options)
    const newval = val.trim();
    if(newval === 'true' ) return true;
    else if(newval === 'false' ) return false;
    else return toNumber(val, options);
  } else {
    if (util.isExist(val)) {
      return val;
    } else {
      return '';
    }
  }
}

//TODO: change regex to capture NS
//const attrsRegx = new RegExp("([\\w\\-\\.\\:]+)\\s*=\\s*(['\"])((.|\n)*?)\\2","gm");
const attrsRegx = new RegExp('([^\\s=]+)\\s*(=\\s*([\'"])(.*?)\\3)?', 'g');

function buildAttributesMap(attrStr, jPath, options) {
  if (!options.ignoreAttributes && typeof attrStr === 'string') {
    attrStr = attrStr.replace(/\r?\n/g, ' ');
    //attrStr = attrStr || attrStr.trim();

    const matches = util.getAllMatches(attrStr, attrsRegx);
    const len = matches.length; //don't make it inline
    const attrs = {};
    for (let i = 0; i < len; i++) {
      const attrName = resolveNameSpace(matches[i][1], options);
      let oldVal = matches[i][4];
      const aName = options.attributeNamePrefix + attrName;
      if (attrName.length) {
        if (oldVal !== undefined) {
          if (options.trimValues) {
            oldVal = oldVal.trim();
          }

          const newVal = options.attrValueProcessor(attrName, oldVal, jPath);
          if(newVal === null || newVal === undefined){
            //don't parse
            attrs[aName] = oldVal;
          }else if(typeof newVal !== typeof oldVal || newVal !== oldVal){
            //overwrite
            attrs[aName] = newVal;
          }else{
            //parse
            attrs[aName] = _parseValue(
              oldVal,
              options.parseAttributeValue,
              options.numParseOptions
            );
          }
        } else if (options.allowBooleanAttributes) {
          attrs[aName] = true;
        }
      }
    }
    if (!Object.keys(attrs).length) {
      return;
    }
    if (options.attrNodeName) {
      const attrCollection = {};
      attrCollection[options.attrNodeName] = attrs;
      return attrCollection;
    }
    return attrs;
  }
}

const getTraversalObj = function(xmlData, options) {
  xmlData = xmlData.replace(/\r\n?/g, "\n");
  options = buildOptions(options, defaultOptions, props);
  const xmlObj = new xmlNode('!xml');
  let currentNode = xmlObj;
  let textData = "";
  const tagsNodeStack = [];
  let jPath = "";

  for(let i=0; i< xmlData.length; i++){//for each char in XML data
    const ch = xmlData[i];
    if(ch === '<'){
      // const nextIndex = i+1;
      // const _2ndChar = xmlData[nextIndex];
      if( xmlData[i+1] === '/') {//Closing Tag
        const closeIndex = findClosingIndex(xmlData, ">", i, "Closing Tag is not closed.")
        let tagName = xmlData.substring(i+2,closeIndex).trim();

        if(options.ignoreNameSpace){
          const colonIndex = tagName.indexOf(":");
          if(colonIndex !== -1){
            tagName = tagName.substr(colonIndex+1);
          }
        }

        if(currentNode){
          textData = parseValue(textData, options, currentNode.tagname, jPath)
          if(textData !== undefined &&  textData !== "") currentNode.add(options.textNodeName, textData);
          textData = "";
        }

        if (isItStopNode(options.stopNodes, tagsNodeStack, currentNode.tagname)) { //TODO: namespace
          const top = tagsNodeStack[tagsNodeStack.length - 1];
          const stopNode = top.child[ top.child.length -1 ];
          stopNode[currentNode.tagname] = [ { [options.textNodeName] :xmlData.substr(currentNode.startIndex + 1, i - currentNode.startIndex - 1) }];
        }

        jPath = jPath.substr(0, jPath.lastIndexOf("."));

        currentNode = tagsNodeStack.pop();//avoid recurssion, set the parent tag scope
        textData = "";
        i = closeIndex;
      } else if( xmlData[i+1] === '?') {
        i = findClosingIndex(xmlData, "?>", i, "Pi Tag is not closed.")
      } else if(xmlData.substr(i + 1, 3) === '!--') {
        i = findClosingIndex(xmlData, "-->", i, "Comment is not closed.")
      } else if( xmlData.substr(i + 1, 2) === '!D') {
        const closeIndex = findClosingIndex(xmlData, ">", i, "DOCTYPE is not closed.")
        const tagExp = xmlData.substring(i, closeIndex);
        if(tagExp.indexOf("[") >= 0){
          i = xmlData.indexOf("]>", i) + 1;
        }else{
          i = closeIndex;
        }
      }else if(xmlData.substr(i + 1, 2) === '![') {
        const closeIndex = findClosingIndex(xmlData, "]]>", i, "CDATA is not closed.") - 2;
        const tagExp = xmlData.substring(i + 9,closeIndex);

        if(textData){ //store previously collected data as textNode
          textData = parseValue(textData, options, currentNode.tagname, jPath);
          if(textData !== undefined &&  textData !== "") currentNode.add(options.textNodeName, textData);
          textData = "";
        }

        if(options.cdataTagName){
          currentNode.add(options.cdataTagName, [ { [options.textNodeName] : parseValue(tagExp, options, options.cdataTagName, jPath + "." + options.cdataTagName) } ]);
        }else{
          currentNode.add(options.textNodeName, parseValue(tagExp, options, currentNode.tagname, jPath));
        }

        i = closeIndex + 2;
      }else {//Opening tag
        const result = tagExpWithClosingIndex(xmlData, i+1)
        let tagExp = result.data;
        const closeIndex = result.index;
        const separatorIndex = tagExp.indexOf(" ");
        let tagName = tagExp;
        let shouldBuildAttributesMap = true;
        if(separatorIndex !== -1){//separate tag name and attributes expression
          tagName = tagExp.substr(0, separatorIndex).replace(/\s\s*$/, '');
          tagExp = tagExp.substr(separatorIndex + 1);
        }

        if(options.ignoreNameSpace){
          const colonIndex = tagName.indexOf(":");
          if(colonIndex !== -1){
            tagName = tagName.substr(colonIndex+1);
            shouldBuildAttributesMap = tagName !== result.data.substr(colonIndex + 1);
          }
        }
        if(tagName !== xmlObj.tagname){
          jPath += jPath ? "." + tagName : tagName;
        }
        //save text as child node
        if (currentNode && textData) {
          if(currentNode.tagname !== '!xml'){
            textData = parseValue(textData, options, currentNode.tagname, jPath );
            if(textData !== undefined &&  textData !== "") currentNode.add(options.textNodeName, textData);
            textData = "";
          }
        }

        if(tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1){//selfClosing tag

          if(tagName[tagName.length - 1] === "/"){ //remove trailing '/'
            tagName = tagName.substr(0, tagName.length - 1);
            tagExp = tagName;
          }else{
            tagExp = tagExp.substr(0, tagExp.length - 1);
          }

          const childNode = new xmlNode(tagName);
          if(tagName !== tagExp && shouldBuildAttributesMap){
            childNode.attrsMap = buildAttributesMap(tagExp, jPath , options);
          }
          jPath = jPath.substr(0, jPath.lastIndexOf("."));
          tagsNodeStack.push(currentNode);
          currentNode.addChild(childNode);
        }else{//opening tag

          const childNode = new xmlNode( tagName);
          tagsNodeStack.push(currentNode);

          childNode.startIndex=closeIndex; //for further processing

          if(tagName !== tagExp && shouldBuildAttributesMap){
            childNode.attrsMap = buildAttributesMap(tagExp, jPath, options);
          }
          currentNode.addChild(childNode);
          currentNode = childNode;
        }
        textData = "";
        i = closeIndex;
      }
    }else{
      textData += xmlData[i];
    }
  }
  return xmlObj.child[0];
}

/**
 *
 * @param {string[]} stopNodes
 * @param {XmlNode[]} tagsNodeStack
 */
function isItStopNode(stopNodes, tagsNodeStack, currentTagName){
  const matchingStopNodes = [];
  //filter the list of stopNodes as per current tag
  stopNodes.forEach( jPath => {
    if( jPath.substr( jPath.length - currentTagName.length) === currentTagName) matchingStopNodes.push(jPath);
  });

  if(matchingStopNodes.length > 0){
    let jPath = "";
    for (let i = 1; i < tagsNodeStack.length; i++) {
      const node = tagsNodeStack[i];
      jPath += "." + node.tagname;
    }
    jPath += "." + currentTagName;
    jPath = jPath.substr(1);
    for (let i = 0; i < matchingStopNodes.length; i++) {
      if(matchingStopNodes[i] === jPath) return true;
    }
  }else return false;
}

/**
 * Returns the tag Expression and where it is ending handling single-dobule quotes situation
 * @param {string} xmlData
 * @param {number} i starting index
 * @returns
 */
function tagExpWithClosingIndex(xmlData, i){
  let attrBoundary;
  let tagExp = "";
  for (let index = i; index < xmlData.length; index++) {
    let ch = xmlData[index];
    if (attrBoundary) {
        if (ch === attrBoundary) attrBoundary = "";//reset
    } else if (ch === '"' || ch === "'") {
        attrBoundary = ch;
    } else if (ch === '>') {
        return {
          data: tagExp,
          index: index
        }
    } else if (ch === '\t') {
      ch = " "
    }
    tagExp += ch;
  }
}

function findClosingIndex(xmlData, str, i, errMsg){
  const closingIndex = xmlData.indexOf(str, i);
  if(closingIndex === -1){
    throw new Error(errMsg)
  }else{
    return closingIndex + str.length - 1;
  }
}

exports.getTraversalObj = getTraversalObj;
<commit_msg>fix: self-closing tag should not be pushed in stack<commit_after>'use strict';

const util = require('./util');
const buildOptions = require('./util').buildOptions;
const xmlNode = require('./xmlNode');
const toNumber = require("strnum");

const regx =
  '<((!\\[CDATA\\[([\\s\\S]*?)(]]>))|((NAME:)?(NAME))([^>]*)>|((\\/)(NAME)\\s*>))([^<]*)'
  .replace(/NAME/g, util.nameRegexp);

//const tagsRegx = new RegExp("<(\\/?[\\w:\\-\._]+)([^>]*)>(\\s*"+cdataRegx+")*([^<]+)?","g");
//const tagsRegx = new RegExp("<(\\/?)((\\w*:)?([\\w:\\-\._]+))([^>]*)>([^<]*)("+cdataRegx+"([^<]*))*([^<]+)?","g");

//polyfill
if (!Number.parseInt && window.parseInt) {
  Number.parseInt = window.parseInt;
}
if (!Number.parseFloat && window.parseFloat) {
  Number.parseFloat = window.parseFloat;
}

const defaultOptions = {
  attributeNamePrefix: '@_',
  attrNodeName: false,
  textNodeName: '#text',
  ignoreAttributes: true,
  ignoreNameSpace: false,
  allowBooleanAttributes: false, //a tag can have attributes without any value
  //ignoreRootElement : false,
  parseNodeValue: true,
  parseAttributeValue: false,
  trimValues: true, //Trim string values of tag and attributes
  cdataTagName: false,
  numParseOptions: {
    hex: true,
    leadingZeros: true
  },
  tagValueProcessor: function(tagName, val) {
    return val;
  },
  attrValueProcessor: function(attrName, val) {
    return val;
  },
  stopNodes: [],
  alwaysCreateTextNode: false,
  //decodeStrict: false,
  isArray: () => false
};

exports.defaultOptions = defaultOptions;

const props = [
  'attributeNamePrefix', //done
  'attrNodeName', //done
  'textNodeName', //done
  'ignoreAttributes', //done
  'ignoreNameSpace', //done
  'allowBooleanAttributes', //done
  'parseNodeValue', //done
  'parseAttributeValue', //done
  'trimValues', //done
  'cdataTagName',//done
  'tagValueProcessor',//done
  'attrValueProcessor',//done
  'numParseOptions', //done
  'stopNodes', //done
  'alwaysCreateTextNode', //done
  'isArray', //done
];
exports.props = props;

/**
 * @param {string} val
 * @param {object} options
 */
function parseValue(val, options, tagName, jPath) {
  if (val !== undefined) {
    if (options.trimValues) {
      val = val.trim();
    }
    if(val.length > 0){
      const newval = options.tagValueProcessor(tagName, val, jPath);
      if(newval === null || newval === undefined){
        //don't parse
        return val;
      }else if(typeof newval !== typeof val || newval !== val){
        //overwrite
        return newval;
      }else{
        return _parseValue(val, options.parseNodeValue, options.numParseOptions);
      }
    }
  }
}

function resolveNameSpace(tagname, options) {
  if (options.ignoreNameSpace) {
    const tags = tagname.split(':');
    const prefix = tagname.charAt(0) === '/' ? '/' : '';
    if (tags[0] === 'xmlns') {
      return '';
    }
    if (tags.length === 2) {
      tagname = prefix + tags[1];
    }
  }
  return tagname;
}

function _parseValue(val, shouldParse, options) {
  if (shouldParse && typeof val === 'string') {
    //console.log(options)
    const newval = val.trim();
    if(newval === 'true' ) return true;
    else if(newval === 'false' ) return false;
    else return toNumber(val, options);
  } else {
    if (util.isExist(val)) {
      return val;
    } else {
      return '';
    }
  }
}

//TODO: change regex to capture NS
//const attrsRegx = new RegExp("([\\w\\-\\.\\:]+)\\s*=\\s*(['\"])((.|\n)*?)\\2","gm");
const attrsRegx = new RegExp('([^\\s=]+)\\s*(=\\s*([\'"])(.*?)\\3)?', 'g');

function buildAttributesMap(attrStr, jPath, options) {
  if (!options.ignoreAttributes && typeof attrStr === 'string') {
    attrStr = attrStr.replace(/\r?\n/g, ' ');
    //attrStr = attrStr || attrStr.trim();

    const matches = util.getAllMatches(attrStr, attrsRegx);
    const len = matches.length; //don't make it inline
    const attrs = {};
    for (let i = 0; i < len; i++) {
      const attrName = resolveNameSpace(matches[i][1], options);
      let oldVal = matches[i][4];
      const aName = options.attributeNamePrefix + attrName;
      if (attrName.length) {
        if (oldVal !== undefined) {
          if (options.trimValues) {
            oldVal = oldVal.trim();
          }

          const newVal = options.attrValueProcessor(attrName, oldVal, jPath);
          if(newVal === null || newVal === undefined){
            //don't parse
            attrs[aName] = oldVal;
          }else if(typeof newVal !== typeof oldVal || newVal !== oldVal){
            //overwrite
            attrs[aName] = newVal;
          }else{
            //parse
            attrs[aName] = _parseValue(
              oldVal,
              options.parseAttributeValue,
              options.numParseOptions
            );
          }
        } else if (options.allowBooleanAttributes) {
          attrs[aName] = true;
        }
      }
    }
    if (!Object.keys(attrs).length) {
      return;
    }
    if (options.attrNodeName) {
      const attrCollection = {};
      attrCollection[options.attrNodeName] = attrs;
      return attrCollection;
    }
    return attrs;
  }
}

const getTraversalObj = function(xmlData, options) {
  xmlData = xmlData.replace(/\r\n?/g, "\n");
  options = buildOptions(options, defaultOptions, props);
  const xmlObj = new xmlNode('!xml');
  let currentNode = xmlObj;
  let textData = "";
  const tagsNodeStack = [];
  let jPath = "";

  for(let i=0; i< xmlData.length; i++){//for each char in XML data
    const ch = xmlData[i];
    if(ch === '<'){
      // const nextIndex = i+1;
      // const _2ndChar = xmlData[nextIndex];
      if( xmlData[i+1] === '/') {//Closing Tag
        const closeIndex = findClosingIndex(xmlData, ">", i, "Closing Tag is not closed.")
        let tagName = xmlData.substring(i+2,closeIndex).trim();

        if(options.ignoreNameSpace){
          const colonIndex = tagName.indexOf(":");
          if(colonIndex !== -1){
            tagName = tagName.substr(colonIndex+1);
          }
        }

        if(currentNode){
          textData = parseValue(textData, options, currentNode.tagname, jPath)
          if(textData !== undefined &&  textData !== "") currentNode.add(options.textNodeName, textData);
          textData = "";
        }

        if (isItStopNode(options.stopNodes, tagsNodeStack, currentNode.tagname)) { //TODO: namespace
          const top = tagsNodeStack[tagsNodeStack.length - 1];
          const stopNode = top.child[ top.child.length -1 ];
          stopNode[currentNode.tagname] = [ { [options.textNodeName] :xmlData.substr(currentNode.startIndex + 1, i - currentNode.startIndex - 1) }];
        }

        jPath = jPath.substr(0, jPath.lastIndexOf("."));

        currentNode = tagsNodeStack.pop();//avoid recurssion, set the parent tag scope
        textData = "";
        i = closeIndex;
      } else if( xmlData[i+1] === '?') {
        i = findClosingIndex(xmlData, "?>", i, "Pi Tag is not closed.")
      } else if(xmlData.substr(i + 1, 3) === '!--') {
        i = findClosingIndex(xmlData, "-->", i, "Comment is not closed.")
      } else if( xmlData.substr(i + 1, 2) === '!D') {
        const closeIndex = findClosingIndex(xmlData, ">", i, "DOCTYPE is not closed.")
        const tagExp = xmlData.substring(i, closeIndex);
        if(tagExp.indexOf("[") >= 0){
          i = xmlData.indexOf("]>", i) + 1;
        }else{
          i = closeIndex;
        }
      }else if(xmlData.substr(i + 1, 2) === '![') {
        const closeIndex = findClosingIndex(xmlData, "]]>", i, "CDATA is not closed.") - 2;
        const tagExp = xmlData.substring(i + 9,closeIndex);

        if(textData){ //store previously collected data as textNode
          textData = parseValue(textData, options, currentNode.tagname, jPath);
          if(textData !== undefined &&  textData !== "") currentNode.add(options.textNodeName, textData);
          textData = "";
        }

        if(options.cdataTagName){
          currentNode.add(options.cdataTagName, [ { [options.textNodeName] : parseValue(tagExp, options, options.cdataTagName, jPath + "." + options.cdataTagName) } ]);
        }else{
          currentNode.add(options.textNodeName, parseValue(tagExp, options, currentNode.tagname, jPath));
        }

        i = closeIndex + 2;
      }else {//Opening tag
        const result = tagExpWithClosingIndex(xmlData, i+1)
        let tagExp = result.data;
        const closeIndex = result.index;
        const separatorIndex = tagExp.indexOf(" ");
        let tagName = tagExp;
        let shouldBuildAttributesMap = true;
        if(separatorIndex !== -1){//separate tag name and attributes expression
          tagName = tagExp.substr(0, separatorIndex).replace(/\s\s*$/, '');
          tagExp = tagExp.substr(separatorIndex + 1);
        }

        if(options.ignoreNameSpace){
          const colonIndex = tagName.indexOf(":");
          if(colonIndex !== -1){
            tagName = tagName.substr(colonIndex+1);
            shouldBuildAttributesMap = tagName !== result.data.substr(colonIndex + 1);
          }
        }
        if(tagName !== xmlObj.tagname){
          jPath += jPath ? "." + tagName : tagName;
        }
        //save text as child node
        if (currentNode && textData) {
          if(currentNode.tagname !== '!xml'){
            textData = parseValue(textData, options, currentNode.tagname, jPath );
            if(textData !== undefined &&  textData !== "") currentNode.add(options.textNodeName, textData);
            textData = "";
          }
        }

        if(tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1){//selfClosing tag

          if(tagName[tagName.length - 1] === "/"){ //remove trailing '/'
            tagName = tagName.substr(0, tagName.length - 1);
            tagExp = tagName;
          }else{
            tagExp = tagExp.substr(0, tagExp.length - 1);
          }

          const childNode = new xmlNode(tagName);
          if(tagName !== tagExp && shouldBuildAttributesMap){
            childNode.attrsMap = buildAttributesMap(tagExp, jPath , options);
          }
          jPath = jPath.substr(0, jPath.lastIndexOf("."));
          // tagsNodeStack.push(currentNode);
          currentNode.addChild(childNode);
        }else{//opening tag

          const childNode = new xmlNode( tagName);
          tagsNodeStack.push(currentNode);

          childNode.startIndex=closeIndex; //for further processing

          if(tagName !== tagExp && shouldBuildAttributesMap){
            childNode.attrsMap = buildAttributesMap(tagExp, jPath, options);
          }
          currentNode.addChild(childNode);
          currentNode = childNode;
        }
        textData = "";
        i = closeIndex;
      }
    }else{
      textData += xmlData[i];
    }
  }
  return xmlObj.child[0];
}

/**
 *
 * @param {string[]} stopNodes
 * @param {XmlNode[]} tagsNodeStack
 */
function isItStopNode(stopNodes, tagsNodeStack, currentTagName){
  const matchingStopNodes = [];
  //filter the list of stopNodes as per current tag
  stopNodes.forEach( jPath => {
    if( jPath.substr( jPath.length - currentTagName.length) === currentTagName) matchingStopNodes.push(jPath);
  });

  if(matchingStopNodes.length > 0){
    let jPath = "";
    for (let i = 1; i < tagsNodeStack.length; i++) {
      const node = tagsNodeStack[i];
      jPath += "." + node.tagname;
    }
    jPath += "." + currentTagName;
    jPath = jPath.substr(1);
    for (let i = 0; i < matchingStopNodes.length; i++) {
      if(matchingStopNodes[i] === jPath) return true;
    }
  }else return false;
}

/**
 * Returns the tag Expression and where it is ending handling single-dobule quotes situation
 * @param {string} xmlData
 * @param {number} i starting index
 * @returns
 */
function tagExpWithClosingIndex(xmlData, i){
  let attrBoundary;
  let tagExp = "";
  for (let index = i; index < xmlData.length; index++) {
    let ch = xmlData[index];
    if (attrBoundary) {
        if (ch === attrBoundary) attrBoundary = "";//reset
    } else if (ch === '"' || ch === "'") {
        attrBoundary = ch;
    } else if (ch === '>') {
        return {
          data: tagExp,
          index: index
        }
    } else if (ch === '\t') {
      ch = " "
    }
    tagExp += ch;
  }
}

function findClosingIndex(xmlData, str, i, errMsg){
  const closingIndex = xmlData.indexOf(str, i);
  if(closingIndex === -1){
    throw new Error(errMsg)
  }else{
    return closingIndex + str.length - 1;
  }
}

exports.getTraversalObj = getTraversalObj;
<|endoftext|>


<commit_before>/*
 Trivial Components (https://github.com/trivial-components/trivial-components)

 Copyright 2015 Yann Massard (https://github.com/yamass) and other contributors

 Licensed under the Apache License, Version 2.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://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */
(function (factory) {
        "use strict";

        if (typeof define === 'function' && define.amd) {
            // Define as an AMD module if possible
            define('trivial-treebox', ['trivial-core', 'jquery', 'mustache'], factory);
        } else if (typeof exports === 'object') {
            // Node/CommonJS
            module.exports = factory(require('trivial-core'), require('jquery'), require('mustache'));
        } else if (jQuery && !jQuery.fn.TrivialTreeBox) {
            // Define using browser globals otherwise
            // Prevent multiple instantiations if the script is loaded twice
            window.TrivialTreeBox = factory(TrivialComponents, jQuery, Mustache);
        }
    }(function (TrivialComponents, $, Mustache) {

        function TrivialTreeBox($container, options) {

            options = options || {};
            var defaultOptions = {
                valueProperty: 'id',
                childrenProperty: "children",
                lazyChildrenFlagProperty: "hasLazyChildren",
                lazyChildrenQueryFunction: function (node, resultCallback) {
                    resultCallback(node.children || []);
                },
                expandedProperty: 'expanded',
                templates: [TrivialComponents.iconSingleLineTemplate],
                spinnerTemplate: TrivialComponents.defaultSpinnerTemplate,
                noEntriesTemplate: TrivialComponents.defaultNoEntriesTemplate,
                entries: null,
                selectedEntryId: null,
                matchingOptions: {
                    matchingMode: 'contains',
                    ignoreCase: true,
                    maxLevenshteinDistance: 2
                },
                animationDuration: 70
            };
            var config = $.extend(defaultOptions, options);

            var entries = config.entries;
            var selectedEntryId;
            var highlightedEntry = null;

            var $componentWrapper = $('
').appendTo($container);
            var $tree = $('

').appendTo($componentWrapper);

            $tree.mouseout(function () {
                setHighlightedEntry(null);
            });

            if (entries) { // if config.entries was set...
                updateTreeEntryElements(entries);
            }

            selectNode(config.selectedEntryId ? findEntryById(config.selectedEntryId) : null);

            function isLeaf(entry) {
                return (entry[config.childrenProperty] == null || entry[config.childrenProperty].length == 0) && !entry[config.lazyChildrenFlagProperty];
            }

            function createEntryDisplay(entry, depth) {
                return $(Mustache.render(config.templates[Math.min(config.templates.length - 1, depth)], entry));
            }

            function createEntryElement(entry, depth) {
                var leaf = isLeaf(entry);
                var $outerEntryWrapper = $('

');
                entry._trEntryElement = $outerEntryWrapper;
                var $entryAndExpanderWrapper = $('

')
                    .appendTo($outerEntryWrapper);
                for (var k = 0; k < depth; k++) {
                    $entryAndExpanderWrapper.append('
');
                }
                var $expander = $('

')
                    .appendTo($entryAndExpanderWrapper);
                var $entry = createEntryDisplay(entry, depth);
                $entry.addClass("tr-tree-entry filterable-item").appendTo($entryAndExpanderWrapper);

                $entryAndExpanderWrapper
                    .mousedown(function (e) {
                        $componentWrapper.trigger("mousedown", e);
                        selectNode(entry);
                    }).mouseup(function (e) {
                        $componentWrapper.trigger("mouseup", e);
                    }).mouseout(function (e) {
                        $componentWrapper.trigger("mouseout", e);
                    }).mouseover(function () {
                        setHighlightedEntry(entry);
                    });

                if (!leaf) {
                    var $childrenWrapper = $('

')
                        .appendTo($outerEntryWrapper);
                    $expander.mousedown(function (e) {
                        return false;
                    }).click(function (e) {
                        setNodeExpanded(entry, !entry[config.expandedProperty]);
                    });
                    if (entry[config.childrenProperty]) {
                        for (var i = 0; i < entry[config.childrenProperty].length; i++) {
                            createEntryElement(entry[config.childrenProperty][i], depth + 1).appendTo($childrenWrapper);
                        }
                    } else if (entry[config.lazyChildrenFlagProperty]) {
                        $childrenWrapper.append(config.spinnerTemplate);
                    }
                    setNodeExpanded(entry, entry[config.expandedProperty]);
                }
                return $outerEntryWrapper;
            }

            function updateTreeEntryElements(entries) {
                $tree.empty();

                if (entries.length > 0) {
                    for (var i = 0; i < entries.length; i++) {
                        createEntryElement(entries[i], 0).appendTo($tree);
                    }
                } else {
                    $tree.append(config.noEntriesTemplate);
                }
            }

            function setNodeExpanded(node, expanded) {
                node[config.expandedProperty] = !!expanded;
                node._trEntryElement.toggleClass("expanded", !!expanded);

                if (expanded) {
                    node._trEntryElement.find("> .tr-tree-entry-children-wrapper").slideDown(config.animationDuration);
                } else {
                    node._trEntryElement.find("> .tr-tree-entry-children-wrapper").slideUp(config.animationDuration);
                }

                if (expanded && node[config.lazyChildrenFlagProperty] && !node[config.childrenProperty]) {
                    config.lazyChildrenQueryFunction(node, function (children) {
                        setChildren(node, children);
                    });
                }
                if (expanded) {
                    minimallyScrollTo(node._trEntryElement);
                }
            }

            function nodeDepth(node) {
                return node ? parseInt(node._trEntryElement.attr('data-depth')) : 0;
            }

            function setChildren(node, children) {
                node[config.childrenProperty] = children;
                node[config.lazyChildrenFlagProperty] = false;

                var $childrenWrapper = node._trEntryElement.find('> .tr-tree-entry-children-wrapper');
                $childrenWrapper.empty();
                if (children && children.length > 0) {
                    var depth = nodeDepth(node);
                    for (var i = 0; i < children.length; i++) {
                        var child = children[i];
                        createEntryElement(child, depth + 1).appendTo($childrenWrapper);
                    }
                } else {
                    node._trEntryElement.removeClass('has-children expanded');
                }
            }

            function updateEntries(newEntries, highlightDirection) {  // TODO remove hightlightDirection - no more needed!
                highlightedEntry = null;
                entries = newEntries;
                updateTreeEntryElements(entries);

                var selectedEntry = findEntryById(selectedEntryId);
                if (selectedEntry) {
                    // selected entry in filtered tree? then mark it as selected!
                    markSelectedEntry(selectedEntry);
                }
            }

            function findEntries(filterFunction) {
                function findEntriesInSubTree(node, listOfFoundEntries) {
                    if (filterFunction.call(this, node)) {
                        listOfFoundEntries.push(node);
                    }
                    if (node[config.childrenProperty]) {
                        for (var i = 0; i < node[config.childrenProperty].length; i++) {
                            var child = node[config.childrenProperty][i];
                            findEntriesInSubTree(child, listOfFoundEntries);
                        }
                    }
                }

                var matchingEntries = [];
                for (var i = 0; i < entries.length; i++) {
                    var rootEntry = entries[i];
                    findEntriesInSubTree(rootEntry, matchingEntries);
                }
                return matchingEntries;
            }

            function findEntryById(id) {
                return findEntries(function (entry) {
                    return entry[config.valueProperty] == id
                })[0];
            }

            function findParentNode(childNode) {
                return findEntries(function (entry) {
                    return entry[config.childrenProperty] && entry[config.childrenProperty].indexOf(childNode) != -1;
                })[0];
            }

            function selectNode(entry) {
                selectedEntryId = entry ? entry[config.valueProperty] : null;
                markSelectedEntry(entry);
                fireChangeEvents();
            }

            function minimallyScrollTo($entryWrapper) {
                $componentWrapper.parent().minimallyScrollTo($entryWrapper);
            }

            function markSelectedEntry(entry) {
                $tree.find(".tr-selected-entry").removeClass("tr-selected-entry");
                if (entry) {
                    var $entryWrapper = entry._trEntryElement.find('>.tr-tree-entry-and-expander-wrapper');
                    $entryWrapper.addClass("tr-selected-entry");
                    minimallyScrollTo($entryWrapper);
                }
            }

            function fireChangeEvents() {
                $componentWrapper.trigger("change");
            }

            function selectNextEntry(direction) {
                var nextVisibleEntry = getNextVisibleEntry(getSelectedEntry(), direction);
                if (nextVisibleEntry != null) {
                    selectNode(nextVisibleEntry);
                }
            }

            function setHighlightedEntry(entry) {
                highlightedEntry = entry;
                $tree.find('.tr-highlighted-entry').removeClass('tr-highlighted-entry');
                if (entry != null) {
                    var $entry = entry._trEntryElement.find('>.tr-tree-entry-and-expander-wrapper');
                    $entry.addClass('tr-highlighted-entry');
                    minimallyScrollTo($entry);
                } else {
                    var selectedEntry = getSelectedEntry();
                    if (selectedEntry) {
                        highlightedEntry = selectedEntry;
                    }
                }
            }

            function highlightNextEntry(direction) {
                var nextVisibleEntry = getNextVisibleEntry(highlightedEntry, direction);
                if (nextVisibleEntry != null) {
                    setHighlightedEntry(nextVisibleEntry);
                }
            }

            function getNextVisibleEntry(currentEntry, direction, onlyEntriesWithTextMatches) {
                var newSelectedElementIndex;
                var visibleEntriesAsList = findEntries(function (entry) {
                    if (onlyEntriesWithTextMatches) {
                        return entry._trEntryElement.is(':visible') && entry._trEntryElement.has('>.tr-tree-entry-and-expander-wrapper .tr-highlighted-text').length > 0;
                    } else {
                        return entry._trEntryElement.is(':visible') || entry === currentEntry;
                    }
                });
                if (visibleEntriesAsList == null || visibleEntriesAsList.length == 0) {
                    return null;
                } else if (currentEntry == null && direction > 0) {
                    newSelectedElementIndex = -1 + direction;
                } else if (currentEntry == null && direction < 0) {
                    newSelectedElementIndex = visibleEntriesAsList.length + direction;
                } else {
                    var currentSelectedElementIndex = visibleEntriesAsList.indexOf(currentEntry);
                    newSelectedElementIndex = (currentSelectedElementIndex + visibleEntriesAsList.length + direction) % visibleEntriesAsList.length;
                }
                return visibleEntriesAsList[newSelectedElementIndex];
            }

            function highlightTextMatches(searchString) {
                for (var i = 0; i < entries.length; i++) {
                    var entry = entries[i];
                    var $entryElement = entry._trEntryElement.find('.tr-tree-entry');
                    $entryElement.trivialHighlight(searchString, config.matchingOptions);
                }
            }

            function getSelectedEntry() {
                return selectedEntryId ? findEntryById(selectedEntryId) : null;
            }

            this.$ = $componentWrapper;
            $componentWrapper[0].trivialTreeBox = this;

            this.updateEntries = updateEntries;
            this.getSelectedEntry = getSelectedEntry;
            this.selectNode = function(nodeId) {
                selectNode(findEntryById(nodeId));
            };
            this.setHighlightedEntry = setHighlightedEntry;
            this.highlightNextEntry = highlightNextEntry;
            this.highlightNextMatchingEntry = function (direction) {
                var nextMatchingEntry = getNextVisibleEntry(highlightedEntry, direction, true);
                if (nextMatchingEntry != null) {
                    setHighlightedEntry(nextMatchingEntry);
                }
            };
            this.getHighlightedEntry = function () {
                return highlightedEntry
            };
            this.highlightTextMatches = highlightTextMatches;
            this.setHighlightedNodeExpanded = function (expanded) {
                if (!highlightedEntry || isLeaf(highlightedEntry)) {
                    return false;
                } else {
                    var wasExpanded = highlightedEntry[config.expandedProperty];
                    setNodeExpanded(highlightedEntry, expanded);
                    return !wasExpanded != !expanded;
                }
            };
            this.updateChildren = function updateChildren(parentNodeId, children) {
                var node = findEntryById(parentNodeId);
                if (node) {
                    setChildren(node, children);
                } else {
                    console.error("Could not set the children of unknown node with id " + parentNodeId);
                }
            };
            this.updateNode = function (node) {
                var oldNode = findEntryById(node.id);
                var parent = findParentNode(oldNode);
                if (parent) {
                    parent[config.childrenProperty][parent[config.childrenProperty].indexOf(oldNode)] = node;
                } else {
                    entries[entries.indexOf(oldNode)] = node;
                }
                createEntryElement(node, nodeDepth(oldNode)).insertAfter(oldNode._trEntryElement);
                oldNode._trEntryElement.remove();
            };
            this.removeNode = function (nodeId) {
                var childNode = findEntryById(nodeId);
                if (childNode) {
                    var parentNode = findParentNode(childNode);
                    if (parentNode) {
                        parentNode[config.childrenProperty].splice(parentNode[config.childrenProperty].indexOf(childNode), 1);
                    } else {
                        entries.splice(entries.indexOf(childNode), 1);
                    }
                    childNode._trEntryElement.remove();
                }
            };
            this.addNode = function (parentNodeId, node) {
                var parentNode = findEntryById(parentNodeId);
                if (isLeaf(parentNode)) {
                    console.error('The parent node is a leaf node, so you cannot add children to it!');
                }
                if (!parentNode[config.childrenProperty]) {
                    parentNode[config.childrenProperty] = [];
                }
                parentNode[config.childrenProperty].push(node);
                var entryElement = createEntryElement(node, nodeDepth(parentNode) + 1);
                entryElement
                    .appendTo(parentNode._trEntryElement.find('>.tr-tree-entry-children-wrapper'));
                parentNode._trEntryElement.addClass('has-children');
            }
        }

        TrivialComponents.registerJqueryPlugin(TrivialTreeBox, "TrivialTreeBox", "tr-treebox");

        return $.fn.TrivialTreeBox;
    })
);
<commit_msg>treebox: fixed: no animation on initialization of treebox<commit_after>/*
 Trivial Components (https://github.com/trivial-components/trivial-components)

 Copyright 2015 Yann Massard (https://github.com/yamass) and other contributors

 Licensed under the Apache License, Version 2.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://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */
(function (factory) {
        "use strict";

        if (typeof define === 'function' && define.amd) {
            // Define as an AMD module if possible
            define('trivial-treebox', ['trivial-core', 'jquery', 'mustache'], factory);
        } else if (typeof exports === 'object') {
            // Node/CommonJS
            module.exports = factory(require('trivial-core'), require('jquery'), require('mustache'));
        } else if (jQuery && !jQuery.fn.TrivialTreeBox) {
            // Define using browser globals otherwise
            // Prevent multiple instantiations if the script is loaded twice
            window.TrivialTreeBox = factory(TrivialComponents, jQuery, Mustache);
        }
    }(function (TrivialComponents, $, Mustache) {

        function TrivialTreeBox($container, options) {

            options = options || {};
            var defaultOptions = {
                valueProperty: 'id',
                childrenProperty: "children",
                lazyChildrenFlagProperty: "hasLazyChildren",
                lazyChildrenQueryFunction: function (node, resultCallback) {
                    resultCallback(node.children || []);
                },
                expandedProperty: 'expanded',
                templates: [TrivialComponents.iconSingleLineTemplate],
                spinnerTemplate: TrivialComponents.defaultSpinnerTemplate,
                noEntriesTemplate: TrivialComponents.defaultNoEntriesTemplate,
                entries: null,
                selectedEntryId: null,
                matchingOptions: {
                    matchingMode: 'contains',
                    ignoreCase: true,
                    maxLevenshteinDistance: 2
                },
                animationDuration: 70
            };
            var config = $.extend(defaultOptions, options);

            var entries = config.entries;
            var selectedEntryId;
            var highlightedEntry = null;

            var $componentWrapper = $('
').appendTo($container);
            var $tree = $('

').appendTo($componentWrapper);

            $tree.mouseout(function () {
                setHighlightedEntry(null);
            });

            if (entries) { // if config.entries was set...
                updateTreeEntryElements(entries);
            }

            selectNode(config.selectedEntryId ? findEntryById(config.selectedEntryId) : null);

            function isLeaf(entry) {
                return (entry[config.childrenProperty] == null || entry[config.childrenProperty].length == 0) && !entry[config.lazyChildrenFlagProperty];
            }

            function createEntryDisplay(entry, depth) {
                return $(Mustache.render(config.templates[Math.min(config.templates.length - 1, depth)], entry));
            }

            function createEntryElement(entry, depth) {
                var leaf = isLeaf(entry);
                var $outerEntryWrapper = $('

');
                entry._trEntryElement = $outerEntryWrapper;
                var $entryAndExpanderWrapper = $('

')
                    .appendTo($outerEntryWrapper);
                for (var k = 0; k < depth; k++) {
                    $entryAndExpanderWrapper.append('
');
                }
                var $expander = $('

')
                    .appendTo($entryAndExpanderWrapper);
                var $entry = createEntryDisplay(entry, depth);
                $entry.addClass("tr-tree-entry filterable-item").appendTo($entryAndExpanderWrapper);

                $entryAndExpanderWrapper
                    .mousedown(function (e) {
                        $componentWrapper.trigger("mousedown", e);
                        selectNode(entry);
                    }).mouseup(function (e) {
                        $componentWrapper.trigger("mouseup", e);
                    }).mouseout(function (e) {
                        $componentWrapper.trigger("mouseout", e);
                    }).mouseover(function () {
                        setHighlightedEntry(entry);
                    });

                if (!leaf) {
                    var $childrenWrapper = $('

')
                        .appendTo($outerEntryWrapper);
                    $expander.mousedown(function (e) {
                        return false;
                    }).click(function (e) {
                        setNodeExpanded(entry, !entry[config.expandedProperty], true);
                    });
                    if (entry[config.childrenProperty]) {
                        for (var i = 0; i < entry[config.childrenProperty].length; i++) {
                            createEntryElement(entry[config.childrenProperty][i], depth + 1).appendTo($childrenWrapper);
                        }
                    } else if (entry[config.lazyChildrenFlagProperty]) {
                        $childrenWrapper.hide().append(config.spinnerTemplate).fadeIn();
                    }
                    setNodeExpanded(entry, entry[config.expandedProperty], false);
                }
                return $outerEntryWrapper;
            }

            function updateTreeEntryElements(entries) {
                $tree.empty();

                if (entries.length > 0) {
                    for (var i = 0; i < entries.length; i++) {
                        createEntryElement(entries[i], 0).appendTo($tree);
                    }
                } else {
                    $tree.append(config.noEntriesTemplate);
                }
            }

            function setNodeExpanded(node, expanded, animate) {
                node[config.expandedProperty] = !!expanded;
                node._trEntryElement.toggleClass("expanded", !!expanded);

                if (expanded) {
                    node._trEntryElement.find("> .tr-tree-entry-children-wrapper").slideDown(animate ? config.animationDuration : 0);
                } else {
                    node._trEntryElement.find("> .tr-tree-entry-children-wrapper").slideUp(animate ? config.animationDuration : 0);
                }

                if (expanded && node[config.lazyChildrenFlagProperty] && !node[config.childrenProperty]) {
                    config.lazyChildrenQueryFunction(node, function (children) {
                        setChildren(node, children);
                    });
                }
                if (expanded) {
                    minimallyScrollTo(node._trEntryElement);
                }
            }

            function nodeDepth(node) {
                return node ? parseInt(node._trEntryElement.attr('data-depth')) : 0;
            }

            function setChildren(node, children) {
                node[config.childrenProperty] = children;
                node[config.lazyChildrenFlagProperty] = false;

                var $childrenWrapper = node._trEntryElement.find('> .tr-tree-entry-children-wrapper');
                $childrenWrapper.empty();
                if (children && children.length > 0) {
                    var depth = nodeDepth(node);
                    for (var i = 0; i < children.length; i++) {
                        var child = children[i];
                        createEntryElement(child, depth + 1).appendTo($childrenWrapper);
                    }
                } else {
                    node._trEntryElement.removeClass('has-children expanded');
                }
            }

            function updateEntries(newEntries, highlightDirection) {  // TODO remove hightlightDirection - no more needed!
                highlightedEntry = null;
                entries = newEntries;
                updateTreeEntryElements(entries);

                var selectedEntry = findEntryById(selectedEntryId);
                if (selectedEntry) {
                    // selected entry in filtered tree? then mark it as selected!
                    markSelectedEntry(selectedEntry);
                }
            }

            function findEntries(filterFunction) {
                function findEntriesInSubTree(node, listOfFoundEntries) {
                    if (filterFunction.call(this, node)) {
                        listOfFoundEntries.push(node);
                    }
                    if (node[config.childrenProperty]) {
                        for (var i = 0; i < node[config.childrenProperty].length; i++) {
                            var child = node[config.childrenProperty][i];
                            findEntriesInSubTree(child, listOfFoundEntries);
                        }
                    }
                }

                var matchingEntries = [];
                for (var i = 0; i < entries.length; i++) {
                    var rootEntry = entries[i];
                    findEntriesInSubTree(rootEntry, matchingEntries);
                }
                return matchingEntries;
            }

            function findEntryById(id) {
                return findEntries(function (entry) {
                    return entry[config.valueProperty] == id
                })[0];
            }

            function findParentNode(childNode) {
                return findEntries(function (entry) {
                    return entry[config.childrenProperty] && entry[config.childrenProperty].indexOf(childNode) != -1;
                })[0];
            }

            function selectNode(entry) {
                selectedEntryId = entry ? entry[config.valueProperty] : null;
                markSelectedEntry(entry);
                fireChangeEvents();
            }

            function minimallyScrollTo($entryWrapper) {
                $componentWrapper.parent().minimallyScrollTo($entryWrapper);
            }

            function markSelectedEntry(entry) {
                $tree.find(".tr-selected-entry").removeClass("tr-selected-entry");
                if (entry) {
                    var $entryWrapper = entry._trEntryElement.find('>.tr-tree-entry-and-expander-wrapper');
                    $entryWrapper.addClass("tr-selected-entry");
                    minimallyScrollTo($entryWrapper);
                }
            }

            function fireChangeEvents() {
                $componentWrapper.trigger("change");
            }

            function selectNextEntry(direction) {
                var nextVisibleEntry = getNextVisibleEntry(getSelectedEntry(), direction);
                if (nextVisibleEntry != null) {
                    selectNode(nextVisibleEntry);
                }
            }

            function setHighlightedEntry(entry) {
                highlightedEntry = entry;
                $tree.find('.tr-highlighted-entry').removeClass('tr-highlighted-entry');
                if (entry != null) {
                    var $entry = entry._trEntryElement.find('>.tr-tree-entry-and-expander-wrapper');
                    $entry.addClass('tr-highlighted-entry');
                    minimallyScrollTo($entry);
                } else {
                    var selectedEntry = getSelectedEntry();
                    if (selectedEntry) {
                        highlightedEntry = selectedEntry;
                    }
                }
            }

            function highlightNextEntry(direction) {
                var nextVisibleEntry = getNextVisibleEntry(highlightedEntry, direction);
                if (nextVisibleEntry != null) {
                    setHighlightedEntry(nextVisibleEntry);
                }
            }

            function getNextVisibleEntry(currentEntry, direction, onlyEntriesWithTextMatches) {
                var newSelectedElementIndex;
                var visibleEntriesAsList = findEntries(function (entry) {
                    if (onlyEntriesWithTextMatches) {
                        return entry._trEntryElement.is(':visible') && entry._trEntryElement.has('>.tr-tree-entry-and-expander-wrapper .tr-highlighted-text').length > 0;
                    } else {
                        return entry._trEntryElement.is(':visible') || entry === currentEntry;
                    }
                });
                if (visibleEntriesAsList == null || visibleEntriesAsList.length == 0) {
                    return null;
                } else if (currentEntry == null && direction > 0) {
                    newSelectedElementIndex = -1 + direction;
                } else if (currentEntry == null && direction < 0) {
                    newSelectedElementIndex = visibleEntriesAsList.length + direction;
                } else {
                    var currentSelectedElementIndex = visibleEntriesAsList.indexOf(currentEntry);
                    newSelectedElementIndex = (currentSelectedElementIndex + visibleEntriesAsList.length + direction) % visibleEntriesAsList.length;
                }
                return visibleEntriesAsList[newSelectedElementIndex];
            }

            function highlightTextMatches(searchString) {
                for (var i = 0; i < entries.length; i++) {
                    var entry = entries[i];
                    var $entryElement = entry._trEntryElement.find('.tr-tree-entry');
                    $entryElement.trivialHighlight(searchString, config.matchingOptions);
                }
            }

            function getSelectedEntry() {
                return selectedEntryId ? findEntryById(selectedEntryId) : null;
            }

            this.$ = $componentWrapper;
            $componentWrapper[0].trivialTreeBox = this;

            this.updateEntries = updateEntries;
            this.getSelectedEntry = getSelectedEntry;
            this.selectNode = function(nodeId) {
                selectNode(findEntryById(nodeId));
            };
            this.setHighlightedEntry = setHighlightedEntry;
            this.highlightNextEntry = highlightNextEntry;
            this.highlightNextMatchingEntry = function (direction) {
                var nextMatchingEntry = getNextVisibleEntry(highlightedEntry, direction, true);
                if (nextMatchingEntry != null) {
                    setHighlightedEntry(nextMatchingEntry);
                }
            };
            this.getHighlightedEntry = function () {
                return highlightedEntry
            };
            this.highlightTextMatches = highlightTextMatches;
            this.setHighlightedNodeExpanded = function (expanded) {
                if (!highlightedEntry || isLeaf(highlightedEntry)) {
                    return false;
                } else {
                    var wasExpanded = highlightedEntry[config.expandedProperty];
                    setNodeExpanded(highlightedEntry, expanded, true);
                    return !wasExpanded != !expanded;
                }
            };
            this.updateChildren = function updateChildren(parentNodeId, children) {
                var node = findEntryById(parentNodeId);
                if (node) {
                    setChildren(node, children);
                } else {
                    console.error("Could not set the children of unknown node with id " + parentNodeId);
                }
            };
            this.updateNode = function (node) {
                var oldNode = findEntryById(node.id);
                var parent = findParentNode(oldNode);
                if (parent) {
                    parent[config.childrenProperty][parent[config.childrenProperty].indexOf(oldNode)] = node;
                } else {
                    entries[entries.indexOf(oldNode)] = node;
                }
                createEntryElement(node, nodeDepth(oldNode)).insertAfter(oldNode._trEntryElement);
                oldNode._trEntryElement.remove();
            };
            this.removeNode = function (nodeId) {
                var childNode = findEntryById(nodeId);
                if (childNode) {
                    var parentNode = findParentNode(childNode);
                    if (parentNode) {
                        parentNode[config.childrenProperty].splice(parentNode[config.childrenProperty].indexOf(childNode), 1);
                    } else {
                        entries.splice(entries.indexOf(childNode), 1);
                    }
                    childNode._trEntryElement.remove();
                }
            };
            this.addNode = function (parentNodeId, node) {
                var parentNode = findEntryById(parentNodeId);
                if (isLeaf(parentNode)) {
                    console.error('The parent node is a leaf node, so you cannot add children to it!');
                }
                if (!parentNode[config.childrenProperty]) {
                    parentNode[config.childrenProperty] = [];
                }
                parentNode[config.childrenProperty].push(node);
                var entryElement = createEntryElement(node, nodeDepth(parentNode) + 1);
                entryElement
                    .appendTo(parentNode._trEntryElement.find('>.tr-tree-entry-children-wrapper'));
                parentNode._trEntryElement.addClass('has-children');
            }
        }

        TrivialComponents.registerJqueryPlugin(TrivialTreeBox, "TrivialTreeBox", "tr-treebox");

        return $.fn.TrivialTreeBox;
    })
);
<|endoftext|>


<commit_before>(function($){
	$.fn.unoSlider = function(options) {
	    // support multiple elements
	    if (this.length > 1){
            this.each(function() { $(this).unoSlider(options); });
	        return this;
	    }

	    // setup options
	    var defaults = {
	                        //default options go here.
	                        animSpeed: 250,
	                        speed: 5,	//seconds
	                        auto: true,
	                        easing: 'swing',
	                        next: false,
	                        prev: false,
	                        transition: 'slide',
	                        pause: false,
	                        bullets: false,
	                        selector: 'li',
                            callback: function(){},
                            width: 300
                        };

        options = $.extend({}, defaults, options);

	    // SETUP private variabls;
        var s           = this;
        s.$views        = $( s.find(options.selector) ).addClass('sliderView');
        s.$views.css({ width: options.width });
        s.$nav          = $( s.find('.unoSliderNav') );
        s.navItems      = [];
        s.viewWidth     = s.width();
        s.animTimer     = options.animSpeed;
        s.timerSpeed    = options.speed*1000;
        s.easing        = options.easing;
        s.transition    = options.transition;

		//Set the current Slide
		s.current = s.$views[0];
		s.addClass('unoSlider');

		//Loop through each view
		for(var i=0; i < s.$views.length; i++){
			var	$view 			= $(s.$views[i]);
				$bullet 		= $('•');		//Create a bullet

			//Create a jQuery object out of the view
			s.$views[i] = $view;

			//Add the index as data to each view and bullet
			$view.add($bullet).data('idx', i);

			//Add bullets to nav and collect them in an array
			s.$nav.append($bullet);
			s.navItems[i]= $bullet;

			//Bullet Click events
			$bullet.bind('click', function(){ navClick($(this)); });
		}//For each views

		//Bullets click Handler
		var navClick = function($b){
			var	next = $b.data('idx'), //Current Bullet's index
					prev = s.current.data('idx');

			initSlide(prev, next); //pass in previous index
			s.resetTimer();
        };   //Bullets Click

		//Set Current
		var setCurrent = function(idx){
			//remove the "current" class from all elements
			s.$views.add(s.navItems).each(function(){
				this.removeClass('current');
            });

			//Add "current" class to the items with the index that was passe in
			$(s.$views[idx]).add($(s.navItems[idx])).addClass('current');
			//Set current
			s.current = s.$views[idx];
        }; //Set Current

		var initSlide = function(prev, next){
			//If this is the last view, set next as the first view so that it loops around
			if(next >= s.$views.length){
				next = 0;
			}else if( next < 0  ){
                next = s.$views.length-1;
			}

			if( prev < next ){
				animateForward(prev, next);
			} else if (prev > next){
				animateBackwards(prev,next);
			} //if else prev >next

			if (typeof options.callback == 'function') { // make sure the callback is a function
		       options.callback.call(next); // passback the current index
		    }
			setCurrent(next);
        };   //initSlide

		if(options.next){
			$(options.next).live('click', function(e) {
			  // Live handler called.
              e.preventDefault();
			  s.goForward();
			});
		}

		if(options.prev){
			$(options.prev).live('click', function(e) {
			  // Live handler called.
              e.preventDefault();
			  s.goBack();
			});
		}

		/***********************************
		Functions
		***********************************/
		function animateForward(sl1, sl2){
				//Animate Prev out
				s.$views[sl1].stop().animate({
					'left': (s.viewWidth*-1)/4+'px',
					'opacity': '0'
				},s.animTimer, s.easing ,function(){
					s.$views[sl1].css({'left':s.viewWidth+'px'});
				});

				//Animate next in
				s.$views[sl2].css({'left':s.viewWidth/4+'px'}).stop().animate({
					'left':'0',
					'opacity':'1'
				  },s.animTimer, s.easing, function(){
				  	//setCurrent(next);
				});

		}
		function animateBackwards(sl1, sl2){
				//Animate prev out
				s.$views[sl1].stop().animate({
						'left':s.viewWidth/4+'px',
						'opacity':'0'
					},s.animTimer, s.easing, function(){
						s.$views[sl1].css({'left': s.viewWidth/4+'px'});
				});

				//Animate Next in
				s.$views[sl2].css({'left': (s.viewWidth*-1)/4+'px'}).stop().animate({
							'left':'0',
							'opacity': '1'
						},s.animTimer, s.easing, function(){
							//setCurrent(next);
				});
		}

		/***************************************
		Public Functions
		***************************************/
	    s.initialize = function() {
	    	//first run? set the current as the first item
	        setCurrent(0);
	        s.startTimer();
	        return this;
	    };

	    s.startTimer = function(){
            s.t = window.setInterval(function(){
	    		if(options.auto){
	    			s.goForward();
	    		}
			}, s.timerSpeed);
        };
	    s.killTimer = function(){
            window.clearInterval(s.t);
        };
	    s.resetTimer = function(){
	    	s.killTimer();
	    	s.startTimer();
        };

	    s.goForward = function(){
	    	//go forward one view forward
	    	var	prev = s.current.data('idx'),
	    			next = prev+1; //Current Bullet's index

			initSlide(prev, next);
        };

	    s.goBack = function(){
	    	//go back one view forward
	    	var	prev = s.current.data('idx'),
	    			next = prev-1; //Current Bullet's index

			initSlide(prev, next);
        };

	    s.goTo = function(i /*num*/){
	    	//go specific view
	    	var	prev = s.current.data('idx'),
	    			next = i-1; //Current Bullet's index

			initSlide(prev, next);
        };

	    return this.initialize();
    };  //End namespace $.fn.unoSlider

})(jQuery);
<commit_msg>Normalised whitespace<commit_after>(function($){
    $.fn.unoSlider = function(options) {
        // support multiple elements
        if (this.length > 1){
            this.each(function() { $(this).unoSlider(options); });
            return this;
        }

        // setup options
        var defaults = {
                            //default options go here.
                            animSpeed: 250,
                            speed: 5,   //seconds
                            auto: true,
                            easing: 'swing',
                            next: false,
                            prev: false,
                            transition: 'slide',
                            pause: false,
                            bullets: false,
                            selector: 'li',
                            callback: function(){},
                            width: 300
                        };

        options = $.extend({}, defaults, options);

        // SETUP private variabls;
        var s          = this;
        s.$views        = $( s.find(options.selector) ).addClass('sliderView');
        s.$views.css({ width: options.width });
        s.$nav        = $( s.find('.unoSliderNav') );
        s.navItems    = [];
        s.viewWidth  = s.width();
        s.animTimer  = options.animSpeed;
        s.timerSpeed    = options.speed*1000;
        s.easing        = options.easing;
        s.transition    = options.transition;

        //Set the current Slide
        s.current = s.$views[0];
        s.addClass('unoSlider');

        //Loop through each view
        for(var i=0; i < s.$views.length; i++){
            var $view           = $(s.$views[i]);
                $bullet         = $('•');     //Create a bullet

            //Create a jQuery object out of the view
            s.$views[i] = $view;

            //Add the index as data to each view and bullet
            $view.add($bullet).data('idx', i);

            //Add bullets to nav and collect them in an array
            s.$nav.append($bullet);
            s.navItems[i]= $bullet;

            //Bullet Click events
            $bullet.bind('click', function(){ navClick($(this)); });
        }//For each views

        //Bullets click Handler
        var navClick = function($b){
            var next = $b.data('idx'), //Current Bullet's index
                    prev = s.current.data('idx');

            initSlide(prev, next); //pass in previous index
            s.resetTimer();
        };   //Bullets Click

        //Set Current
        var setCurrent = function(idx){
            //remove the "current" class from all elements
            s.$views.add(s.navItems).each(function(){
                this.removeClass('current');
            });

            //Add "current" class to the items with the index that was passe in
            $(s.$views[idx]).add($(s.navItems[idx])).addClass('current');
            //Set current
            s.current = s.$views[idx];
        }; //Set Current

        var initSlide = function(prev, next){
            //If this is the last view, set next as the first view so that it loops around
            if(next >= s.$views.length){
                next = 0;
            }else if( next < 0  ){
                next = s.$views.length-1;
            }

            if( prev < next ){
                animateForward(prev, next);
            } else if (prev > next){
                animateBackwards(prev,next);
            } //if else prev >next

            if (typeof options.callback == 'function') { // make sure the callback is a function
               options.callback.call(next); // passback the current index
            }
            setCurrent(next);
        };   //initSlide

        if(options.next){
            $(options.next).live('click', function(e) {
              // Live handler called.
              e.preventDefault();
              s.goForward();
            });
        }

        if(options.prev){
            $(options.prev).live('click', function(e) {
              // Live handler called.
              e.preventDefault();
              s.goBack();
            });
        }

        /***********************************
        Functions
        ***********************************/
        function animateForward(sl1, sl2){
                //Animate Prev out
                s.$views[sl1].stop().animate({
                    'left': (s.viewWidth*-1)/4+'px',
                    'opacity': '0'
                },s.animTimer, s.easing ,function(){
                    s.$views[sl1].css({'left':s.viewWidth+'px'});
                });

                //Animate next in
                s.$views[sl2].css({'left':s.viewWidth/4+'px'}).stop().animate({
                    'left':'0',
                    'opacity':'1'
                  },s.animTimer, s.easing, function(){
                    //setCurrent(next);
                });

        }
        function animateBackwards(sl1, sl2){
                //Animate prev out
                s.$views[sl1].stop().animate({
                        'left':s.viewWidth/4+'px',
                        'opacity':'0'
                    },s.animTimer, s.easing, function(){
                        s.$views[sl1].css({'left': s.viewWidth/4+'px'});
                });

                //Animate Next in
                s.$views[sl2].css({'left': (s.viewWidth*-1)/4+'px'}).stop().animate({
                            'left':'0',
                            'opacity': '1'
                        },s.animTimer, s.easing, function(){
                            //setCurrent(next);
                });
        }

        /***************************************
        Public Functions
        ***************************************/
        s.initialize = function() {
            //first run? set the current as the first item
            setCurrent(0);
            s.startTimer();
            return this;
        };

        s.startTimer = function(){
            s.t = window.setInterval(function(){
                if(options.auto){
                    s.goForward();
                }
            }, s.timerSpeed);
        };
        s.killTimer = function(){
            window.clearInterval(s.t);
        };
        s.resetTimer = function(){
            s.killTimer();
            s.startTimer();
        };

        s.goForward = function(){
            //go forward one view forward
            var prev = s.current.data('idx'),
                    next = prev+1; //Current Bullet's index

            initSlide(prev, next);
        };

        s.goBack = function(){
            //go back one view forward
            var prev = s.current.data('idx'),
                    next = prev-1; //Current Bullet's index

            initSlide(prev, next);
        };

        s.goTo = function(i /*num*/){
            //go specific view
            var prev = s.current.data('idx'),
                    next = i-1; //Current Bullet's index

            initSlide(prev, next);
        };

        return this.initialize();
    };  //End namespace $.fn.unoSlider

})(jQuery);
<|endoftext|>


<commit_before>//-----------------------------------------updating service list function-------------------------------------------------------------------------------------------
function updateServicelist(){
activeService=$('#serviceModal').attr('service');
	doFetch({ action: 'serviceList', data: activeService, user: localStorage.getItem("bits-user-name")}).then(
	function(e){
    if (e.status=="ok"){
    serviceList = e.data;
    for(var ii = 0; ii < serviceList.length; ++ii) {

		$('.serviceListHolder').append('
'+serviceList[ii].name+'

 Second Line

');

             };

           }else{

           }
        });
}
//-----------------------------------------------save to local storage------------------------------------------------------------------
function serviceOpener(){
if (getBitsWinOpt('s') != undefined )
 {
servicePageLoader();
         }else{
              $(".promoHome").removeClass("displayNone");
  $(".fixed-a").removeClass("displayNone");
	 updatePromos();
	 }
  }
//------------------------------------------end save--------------------------------------------------------------------------------------
//---------------------------------------- subscription function--------------------------------------------------------------------------
function doSubscribe(){
	$(".promoSubButton").bind( "touchstart click", function(event, ui) {
	checkanon();
	showLogin();
	starting();
     event.preventDefault();
	var pid = $(this).attr('pid');
	var dr = $(this).attr('dailyR');
     if (!flag) {
    flag = true;
    setTimeout(function(){ flag = false; }, 100);
	localConverter().then(function(e){
	var infiat = parseInt(localStorage.getItem('bitsoko-wallets-bal'))/100000000;
    var infiat=infiat*parseInt(e.xrate)*parseInt(e.rate);
        console.log(infiat);
		if($( ".promoSubButton-"+pid ).prop( "checked" )){
			var action='unsubscribe';
           // Materialize.toast('unsubscribing..', 1000);
		}else{
			var action='subscribe';
            //Materialize.toast('subscribing..', 1000);
		}

	doFetch({ action: 'doSubscription', todo: action, pid: pid, uid: localStorage.getItem('bits-user-name')}).then(function(e){
           if (e.status=="ok"){
           	    if(infiat<(dr*1.1)){
		 Materialize.toast(action+'d. Insufficient Funds', 6500);
	}else{
		Materialize.toast(action+'d successfully', 5000);
	}

//---------------------------------------send promo data to db-----------------------------------------------------------------------------
		   if(action=='subscribe'){

    var walsvar = getObjectStore('data', 'readwrite').get('bits-mypromos');
	walsvar.onsuccess = function (event) {

		try{var oold=JSON.parse(event.target.result);oold.push(e.prom);}catch(err){var oold=[];oold.push(e.prom);}
	  getObjectStore('data', 'readwrite').put(JSON.stringify(oold), 'bits-mypromos');

		   $( ".promoSubButton-"+pid ).prop( "checked",true);
			   $(".promoSubState-"+pid).html("Subscribed");
	   }

	          }else{
		  $( ".promoSubButton-"+pid ).prop( "checked",false);
			  $(".promoSubState-"+pid).html("Not Subscribed");
	           }
		         }else{
		Materialize.toast('unable to subscribe '+e.msg, 3000);
           }
        })
         .catch(function(){
		Materialize.toast('temporary error. please try again', 3000);
        });
    //$( ".bitsoko-balance" ).html(infiat.toFixed(2));

    });

     }

});

}

//----------------------------------------------populateService function----------------------------------------------------------------------------------------------------------------------------------------
 function populateService(id){
 	var mDet = JSON.parse(localStorage.getItem('bits-merchant-id-'+id));
$(".resDisplay").html( mDet.name);
        document.querySelector('.serviceName').innerHTML = mDet.name;
        document.querySelector('.cardimage').src = 'https://bitsoko.io'+mDet.bannerPath;
         document.querySelector('.cardLogo').src = mDet.icon;
		document.querySelector('.serviceDescription').innerHTML = mDet.description;
		 $('.serviceListHolder').html("");
//-----------------------------------------------incase the user is the owner of this shop, then show POS button------------------------------------------------------------------------------------------------
	 if(mDet.owner==parseInt(localStorage.getItem('bits-user-name'))){
	 $('#manage-store').css("display","block");
	 }else{
	  $('#manage-store').css("display","none");
	 }
	 	callMerchant();
	 console.log(mDet.promotions);
	 	if(mDet.promotions.length == 0){
	 		 console.log("no promos")
		 $('.serviceListHolder').prepend('

No Promotions found

');

	 	} else{
           for(var ii = 0,subs=subs; ii < mDet.promotions.length; ++ii) {
		 var dailyCost=(parseInt(mDet.promotions[ii].discount)/100)*mDet.promotions[ii].promoPrice;
		 $('.serviceListHolder').prepend('
'+
						 ''+
						 ' '+mDet.promotions[ii].promoName+' '+
						 '
 '+Math.ceil(dailyCost)+' Ksh daily
'+
						 '
 Not Subscribed <input type="checkbox" dailyR="'+Math.ceil(dailyCost)+'" pid="'+mDet.promotions[ii].id+'" class="promoSubButton promoSubButton-'+mDet.promotions[ii].id+'" style="background: rgb(128, 210, 147);">

');
       	 subs=mDet.promotions[ii].promoSubs;
		   var nnew=[];

	for(var iii = 0,subs=subs,nnew=nnew,mDet=mDet; iii < subs.length; ++iii) {
			 if(parseInt(subs[iii].id)==parseInt(localStorage.getItem('bits-user-name'))){
			 //console.log('im subscribed to ',mDet.promotions[ii]);
			   nnew.push(mDet.promotions[ii]);
				 $( ".promoSubButton-"+mDet.promotions[ii].id ).prop( "checked", true );
				 $(".promoSubState-"+mDet.promotions[ii].id).html("Subscribed");
			 };
		 }

	 if(nnew.length>0){
		   getObjectStore('data', 'readwrite').get('bits-mypromos').onsuccess = function (event) {

		try{var oold=JSON.parse(event.target.result);var oold=oold.concat(nnew);}catch(err){var oold=[];var oold=oold.concat(nnew);}

	  getObjectStore('data', 'readwrite').put(JSON.stringify(squashById(oold)), 'bits-mypromos');

	   }
		   }

		};

		}

	 doSubscribe();
	 checkPayments();
// -------------------------------------------------loads the shops product lists --------------------------------------------------------------------------------------------------------------------------
 if(mDet.list.length == 0){
 	 console.log("no promos")
		 $('.serviceListHolder').append('

No products found

');
 }
 else{
 	for(var ii = 0; ii < mDet.list.length; ++ii) {
 $('.serviceListHolder').append('
 '+mDet.list[ii].name+'
 '+mDet.list[ii].price+'  '+mDet.list[ii].metric+'

 <input class="number bitsInputQty" price="'+mDet.list[ii].price+'" type="number" placeholder="0" min="0.25" max="10" id='+mDet.list[ii].name+'> '+mDet.list[ii].metric+' (s)

');};
 $('.materialboxed').materialbox();
var addproducts = document.querySelectorAll(".bitsInputQty");
for(var i = 0; i< addproducts.length; ++i){
	addproducts[i].addEventListener("change",tabulateTotals,false);
 }
 $( "body" ).scrollTop( 156 );
 }
 }
//---------------------------------------------------end populateService function------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------  function handleModal---------------------------------------------------------------------------------------------------------------------------------------
function handleModal(){
      console.log(this);
      $('#serviceModal').attr('service',$(this).attr('service'));
      $('#serviceModal').attr('account',$(this).attr('account'));
      populateModal($(this).attr('service'),$(this).attr('account'));
  }
      var items =  [
      {dataModal:'#modal'}
      ]
        for(var ii = 0; ii < items.length; ++ii) {
             };
 var activeService;
          allServices =  [
      //{name:'Merchants',id:'3',desc:'Merchant desription',cardimage:'/bits/images/merchantsBanner',cardLogo:'/bits/images/merchants',image:'mdi-maps-store-mall-directory',list:[]},
       {name:'Contacts',id:'2',desc:'Contacts desription',cardimage:'/bits/images/contactsBanner',cardLogo:'/bits/images/contacts',image:'mdi-social-group',list:[]}
      ]
        for(var ii = 0; ii < allServices.length; ++ii) {
		$('.serviceButtonsHolder').append('
 '+allServices[ii].name+'
');
           };
 var bts = document.querySelectorAll(".serviceButtons");
    for(var ii = 0; ii < bts.length; ++ii) {
bts[ii].addEventListener('touchstart', function(event) {
 	console.log($(this).attr('service'));
if ($(this).attr('service')=='2'){
	contact();
}
 });
             };
     function populateModal(x,y){
         console.log(x,y)
    for(var ii = 0,x=x,activeService=activeService; ii < allServices.length; ++ii) {
        if(allServices[ii].id==x){
            activeService=allServices[ii];
        }
		};
             if ( activeService.list.length==0){
             	$('.serviceListHolder').html("");
             }
        document.querySelector('.serviceName').innerHTML = activeService.name;
        document.querySelector('.cardimage').src = "https://bitsoko.io"+activeService.cardimage+".png";
         document.querySelector('.cardLogo').src = "https://bitsoko.io"+activeService.cardLogo+".png";
		document.querySelector('.serviceDescription').innerHTML = activeService.desription;
//add button

updateServicelist();
    var el = document.querySelectorAll(".loadButtons");
for(var l=0, el=el; l < el.length; ++l){
  el[l].addEventListener("touchstart", handleModal, false);
}
     function populateModal(x,y){
         console.log(x,y)
    for(var ii = 0,x=x,activeService=activeService; ii < serviceList.length; ++ii) {
        if(serviceList[ii].id==x){
            activeService=serviceList[ii];
        }
			if (serviceList[ii].id==1){
			 document.querySelector('.service-banner').src = 'https://bitsoko.io/app/images/services/contacts.png';
		}else if(serviceList[ii].id==2){
			 document.querySelector('.service-banner').src = 'https://bitsoko.io/app/images/services/contacts.png';
		}
             };
        document.querySelector('.btnname').innerHTML = activeService.name;
             };
     }
<commit_msg>Update update_services.js<commit_after>//-----------------------------------------updating service list function-------------------------------------------------------------------------------------------
function updateServicelist(){
activeService=$('#serviceModal').attr('service');
	doFetch({ action: 'serviceList', data: activeService, user: localStorage.getItem("bits-user-name")}).then(
	function(e){
    if (e.status=="ok"){
    serviceList = e.data;
    for(var ii = 0; ii < serviceList.length; ++ii) {

		$('.serviceListHolder').append('
'+serviceList[ii].name+'

 Second Line

');

             };

           }else{

           }
        });
}
//-----------------------------------------------save to local storage------------------------------------------------------------------
function serviceOpener(){
if (getBitsWinOpt('s') != undefined )
 {
servicePageLoader();
         }else{
              $(".promoHome").removeClass("displayNone");
  $(".fixed-a").removeClass("displayNone");
	 updatePromos();
	 }
  }
//------------------------------------------end save--------------------------------------------------------------------------------------
//---------------------------------------- subscription function--------------------------------------------------------------------------
function doSubscribe(){
	$(".promoSubButton").bind( "touchstart click", function(event, ui) {
	checkanon();
	showLogin();
	starting();
     event.preventDefault();
	var pid = $(this).attr('pid');
	var dr = $(this).attr('dailyR');
     if (!flag) {
    flag = true;
    setTimeout(function(){ flag = false; }, 100);
	localConverter().then(function(e){
	var infiat = parseInt(localStorage.getItem('bitsoko-wallets-bal'))/100000000;
    var infiat=infiat*parseInt(e.xrate)*parseInt(e.rate);
        console.log(infiat);
		if($( ".promoSubButton-"+pid ).prop( "checked" )){
			var action='unsubscribe';
           // Materialize.toast('unsubscribing..', 1000);
		}else{
			var action='subscribe';
            //Materialize.toast('subscribing..', 1000);
		}

	doFetch({ action: 'doSubscription', todo: action, pid: pid, uid: localStorage.getItem('bits-user-name')}).then(function(e){
           if (e.status=="ok"){
           	    if(infiat<(dr*1.1)){
		 Materialize.toast(action+'d. Insufficient Funds', 6500);
	}else{
		Materialize.toast(action+'d successfully', 5000);
	}

//---------------------------------------send promo data to db-----------------------------------------------------------------------------
		   if(action=='subscribe'){

    var walsvar = getObjectStore('data', 'readwrite').get('bits-mypromos');
	walsvar.onsuccess = function (event) {

		try{var oold=JSON.parse(event.target.result);oold.push(e.prom);}catch(err){var oold=[];oold.push(e.prom);}
	  getObjectStore('data', 'readwrite').put(JSON.stringify(oold), 'bits-mypromos');

		   $( ".promoSubButton-"+pid ).prop( "checked",true);
			   $(".promoSubState-"+pid).html("Subscribed");
	   }

	          }else{
		  $( ".promoSubButton-"+pid ).prop( "checked",false);
			  $(".promoSubState-"+pid).html("Not Subscribed");
	           }
		         }else{
		Materialize.toast('unable to subscribe '+e.msg, 3000);
           }
        })
         .catch(function(){
		Materialize.toast('temporary error. please try again', 3000);
        });
    //$( ".bitsoko-balance" ).html(infiat.toFixed(2));

    });

     }

});

}

//----------------------------------------------populateService function----------------------------------------------------------------------------------------------------------------------------------------
 function populateService(id){
 	var mDet = JSON.parse(localStorage.getItem('bits-merchant-id-'+id));
$(".resDisplay").html( mDet.name);
        document.querySelector('.serviceName').innerHTML = mDet.name;
        document.querySelector('.cardimage').src = 'https://bitsoko.io'+mDet.bannerPath;
         document.querySelector('.cardLogo').src = mDet.icon;
		document.querySelector('.serviceDescription').innerHTML = mDet.description;
		 $('.serviceListHolder').html("");
//-----------------------------------------------incase the user is the owner of this shop, then show POS button------------------------------------------------------------------------------------------------
	 if(mDet.owner==parseInt(localStorage.getItem('bits-user-name'))){
	 $('#manage-store').css("display","block");
	 }else{
	  $('#manage-store').css("display","none");
	 }
	 	callMerchant();
	 console.log(mDet.promotions);
	 	if(mDet.promotions.length == 0){
	 		 console.log("no promos")
		 $('.serviceListHolder').prepend('

No Promotions found

');

	 	} else{
           var nnew=[];
           for(var ii = 0,nnew=nnew,subs=subs; ii < mDet.promotions.length; ++ii) {
		 var dailyCost=(parseInt(mDet.promotions[ii].discount)/100)*mDet.promotions[ii].promoPrice;
		 $('.serviceListHolder').prepend('
'+
						 ''+
						 ' '+mDet.promotions[ii].promoName+' '+
						 '
 '+Math.ceil(dailyCost)+' Ksh daily
'+
						 '
 Not Subscribed <input type="checkbox" dailyR="'+Math.ceil(dailyCost)+'" pid="'+mDet.promotions[ii].id+'" class="promoSubButton promoSubButton-'+mDet.promotions[ii].id+'" style="background: rgb(128, 210, 147);">

');
       	 subs=mDet.promotions[ii].promoSubs;

	for(var iii = 0,subs=subs,nnew=nnew,mDet=mDet; iii < subs.length; ++iii) {
			 if(parseInt(subs[iii].id)==parseInt(localStorage.getItem('bits-user-name'))){
			 //console.log('im subscribed to ',mDet.promotions[ii]);
			   nnew.push(mDet.promotions[ii]);
				 $( ".promoSubButton-"+mDet.promotions[ii].id ).prop( "checked", true );
				 $(".promoSubState-"+mDet.promotions[ii].id).html("Subscribed");
			 };
		 }

		};

	 if(nnew.length>0){
		   getObjectStore('data', 'readwrite').get('bits-mypromos').onsuccess = function (event) {

		try{var oold=JSON.parse(event.target.result);var oold=oold.concat(nnew);}catch(err){var oold=[];var oold=oold.concat(nnew);}

	  getObjectStore('data', 'readwrite').put(JSON.stringify(squash(oold)), 'bits-mypromos');

	   }
		   }

		}

	 doSubscribe();
	 checkPayments();
// -------------------------------------------------loads the shops product lists --------------------------------------------------------------------------------------------------------------------------
 if(mDet.list.length == 0){
 	 console.log("no promos")
		 $('.serviceListHolder').append('

No products found

');
 }
 else{
 	for(var ii = 0; ii < mDet.list.length; ++ii) {
 $('.serviceListHolder').append('
 '+mDet.list[ii].name+'
 '+mDet.list[ii].price+'  '+mDet.list[ii].metric+'

 <input class="number bitsInputQty" price="'+mDet.list[ii].price+'" type="number" placeholder="0" min="0.25" max="10" id='+mDet.list[ii].name+'> '+mDet.list[ii].metric+' (s)

');};
 $('.materialboxed').materialbox();
var addproducts = document.querySelectorAll(".bitsInputQty");
for(var i = 0; i< addproducts.length; ++i){
	addproducts[i].addEventListener("change",tabulateTotals,false);
 }
 $( "body" ).scrollTop( 156 );
 }
 }
//---------------------------------------------------end populateService function------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------  function handleModal---------------------------------------------------------------------------------------------------------------------------------------
function handleModal(){
      console.log(this);
      $('#serviceModal').attr('service',$(this).attr('service'));
      $('#serviceModal').attr('account',$(this).attr('account'));
      populateModal($(this).attr('service'),$(this).attr('account'));
  }
      var items =  [
      {dataModal:'#modal'}
      ]
        for(var ii = 0; ii < items.length; ++ii) {
             };
 var activeService;
          allServices =  [
      //{name:'Merchants',id:'3',desc:'Merchant desription',cardimage:'/bits/images/merchantsBanner',cardLogo:'/bits/images/merchants',image:'mdi-maps-store-mall-directory',list:[]},
       {name:'Contacts',id:'2',desc:'Contacts desription',cardimage:'/bits/images/contactsBanner',cardLogo:'/bits/images/contacts',image:'mdi-social-group',list:[]}
      ]
        for(var ii = 0; ii < allServices.length; ++ii) {
		$('.serviceButtonsHolder').append('
 '+allServices[ii].name+'
');
           };
 var bts = document.querySelectorAll(".serviceButtons");
    for(var ii = 0; ii < bts.length; ++ii) {
bts[ii].addEventListener('touchstart', function(event) {
 	console.log($(this).attr('service'));
if ($(this).attr('service')=='2'){
	contact();
}
 });
             };
     function populateModal(x,y){
         console.log(x,y)
    for(var ii = 0,x=x,activeService=activeService; ii < allServices.length; ++ii) {
        if(allServices[ii].id==x){
            activeService=allServices[ii];
        }
		};
             if ( activeService.list.length==0){
             	$('.serviceListHolder').html("");
             }
        document.querySelector('.serviceName').innerHTML = activeService.name;
        document.querySelector('.cardimage').src = "https://bitsoko.io"+activeService.cardimage+".png";
         document.querySelector('.cardLogo').src = "https://bitsoko.io"+activeService.cardLogo+".png";
		document.querySelector('.serviceDescription').innerHTML = activeService.desription;
//add button

updateServicelist();
    var el = document.querySelectorAll(".loadButtons");
for(var l=0, el=el; l < el.length; ++l){
  el[l].addEventListener("touchstart", handleModal, false);
}
     function populateModal(x,y){
         console.log(x,y)
    for(var ii = 0,x=x,activeService=activeService; ii < serviceList.length; ++ii) {
        if(serviceList[ii].id==x){
            activeService=serviceList[ii];
        }
			if (serviceList[ii].id==1){
			 document.querySelector('.service-banner').src = 'https://bitsoko.io/app/images/services/contacts.png';
		}else if(serviceList[ii].id==2){
			 document.querySelector('.service-banner').src = 'https://bitsoko.io/app/images/services/contacts.png';
		}
             };
        document.querySelector('.btnname').innerHTML = activeService.name;
             };
     }
<|endoftext|>