<commit_before>/*
  mustache.js — Logic-less templates in JavaScript

  See http://mustache.github.com/ for more info.
*/

var Mustache = function() {
  var Renderer = function() {};

  Renderer.prototype = {
    otag: "{{",
    ctag: "}}",
    escaped_otag: "\{\{",
    escaped_ctag: "\}\}",
    pragmas: {},
    pragmas_implemented: {
      "IMPLICIT-ITERATOR": true
    },
    context: {},

    render: function(template, context, partials) {
      // reset context
      this.context = context;

      // fail fast
      if(!this.includes("", template)) {
        return template;
      }

      template = this.render_pragmas(template);

      return this.render_recursive(template, context, partials);
    },

    render_recursive: function(template, context, partials) {
      return this.render_delimiter(template, context, partials);
    },

    /*
      Looks for %PRAGMAS
    */
    render_pragmas: function(template) {
      // no pragmas
      if(!this.includes("%", template)) {
        return template;
      }

      var that = this;
      var regex = new RegExp(this.escaped_otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" +
            this.escaped_ctag);
      return template.replace(regex, function(match, pragma, options) {
        if(!that.pragmas_implemented[pragma]) {
          throw({message:
            "This implementation of mustache doesn't understand the '" +
            pragma + "' pragma"});
        }
        that.pragmas[pragma] = {};
        if(options) {
          var opts = options.split("=");
          that.pragmas[pragma][opts[0]] = opts[1];
        }
        return "";
        // ignore unknown pragmas silently
      });
    },

    render_delimiter: function(template, context, partials) {
      if(!this.includes("=", template)) {
        return this.render_section(template, context, partials);
      }

      var regex = new RegExp("(" + this.escaped_otag + "=(?:\\S+)\\s+(?:\\S+)=" + this.escaped_ctag + "\\n*)", "mg");
      var fragments = template.split(regex);

      var i, n;
      var r = new RegExp(this.escaped_otag + "=(\\S+)\\s+(\\S+)=" + this.escaped_ctag);
      var matches;

      for (i=0, n=fragments.length; i<n; ++i) {
        matches = fragments[i].match(r);
        if(matches && matches.length===3) {
          var old_otag = this.otag;
          var old_ctag = this.ctag;

          this.set_delimiters(matches[1], matches[2]);

          fragments[i] = this.render_delimiter(fragments.slice(i+1).join(""), context, partials);

          this.set_delimiters(old_otag, old_ctag);

          fragments = fragments.slice(0,i+1);

          break;
        } else {
          fragments[i]=this.render_section(fragments[i], context, partials);
        }
      }

      return fragments.join("");
    },

    /*
      Tries to find a partial in the current scope and render it
    */
    render_partial: function(name, context, partials) {
      name = this.trim(name);
      if(!partials || partials[name] === undefined) {
        throw({message: "unknown_partial '" + name + "'"});
      }
      if(typeof(context[name]) != "object") {
        return this.render_recursive(partials[name], context, partials);
      }
      return this.render_recursive(partials[name], context[name], partials);
    },

    /*
      Renders inverted (^) and normal (#) sections
    */
    render_section: function(template, context, partials) {
      if(!this.includes("#", template) && !this.includes("^", template)) {
        return this.render_tags(template, context, partials);
      }

      var regex = new RegExp(
        "(" + this.escaped_otag + "(?:\\^|\\#)\\s*(.+?)(?:\\(.*\\))?\\s*" + this.escaped_ctag +
        "\n*[\\s\\S]+?" + this.escaped_otag + "\\/\\s*\\2\\s*" + this.escaped_ctag + "\\s*)",
        "mg");

      var i, n;
      var lastWasSection = false;
      var fragments = template.split(regex);

      for (i=0, n=fragments.length; i<n; ++i) {
        if(lastWasSection) {
          fragments[i] = "";
          lastWasSection = false;
          continue;
        }

        if(fragments[i].indexOf(this.otag+"#")===0 || fragments[i].indexOf(this.otag+"^")===0) {
          lastWasSection = true;
          fragments[i] = this.render_section_internal(fragments[i], context, partials);
        } else {
          fragments[i] = this.render_tags(fragments[i], context, partials);
        }
      }

      return fragments.join("");
    },

    render_section_internal: function(template, context, partials) {
      var that = this;
      // CSW - Added "+?" so it finds the tighest bound, not the widest
      var regex = new RegExp(this.escaped_otag + "(\\^|\\#)\\s*((.+?)(\\(.*\\))?)\\s*" + this.escaped_ctag +
              "\n*([\\s\\S]+?)" + this.escaped_otag + "\\/\\s*\\3\\s*" + this.escaped_ctag +
              "\\s*", "mg");

      // for each {{#foo}}{{/foo}} section do...
      var fragment = template.replace(regex, function(match, type, name, reserved1, reserved2, content) {
        // the reserved variables are not being used
        var value = that.find(name, context);
        if(type == "^") { // inverted section
          if(!value || that.is_array(value) && value.length === 0) {
            // false or empty list, render it
            return that.render_recursive(content, context, partials);
          } else {
            return "";
          }
        } else if(type == "#") { // normal section
          if(that.is_array(value)) { // Enumerable, Let's loop!
            return that.map(value, function(row) {
              return that.render_recursive(content, that.create_context(row),
                partials, true);
            }).join("");
          } else if(that.is_object(value)) { // Object, Use it as subcontext!
            return that.render_recursive(content, that.create_context(value),
              partials, true);
          } else if(typeof value === "function") {
            // higher order section
            return value.call(context, content, function(text) {
              return that.render_recursive(text, context, partials);
            });
          } else if(value) { // boolean section
            return that.render_recursive(content, context, partials);
          } else {
            return "";
          }
        }
      });

      return fragment;
    },

    /*
      Replace {{foo}} and friends with values from our view
    */
    render_tags: function(template, context, partials) {
      // tit for tat
      var that = this;

      var new_regex = function() {
        return new RegExp(that.escaped_otag + "(!|>|\\{|&|%)?([^\\/#\\^=]+?)\\1?" +
          that.escaped_ctag + "+", "g");
      };

      var regex = new_regex();
      var tag_replace_callback = function(match, operator, name) {
        switch(operator) {
        case "!": // ignore comments
          return "";
        case ">": // render partial
          return that.render_partial(name, context, partials);
        case "{": // the triple mustache is unescaped
        case "&": // the ampersand is also unescaped
          return that.find(name, context);
        default: // escape the value
          return that.escape(that.find(name, context));
        }
      };
      var lines = template.split("\n");
      var oldContent;
      for(var i = 0; i < lines.length; i++) {
        lines[i] = lines[i].replace(regex, tag_replace_callback, this);
      }

      return lines.join("\n");
    },

    set_delimiters: function(open, close) {
      this.otag = open;
      this.ctag = close;

      this.escaped_otag = this.escape_regex(open);
      this.escaped_ctag = this.escape_regex(close);
    },

    escape_regex: function(text) {
      // thank you Simon Willison
      if(!arguments.callee.sRE) {
        var specials = [
          '/', '.', '*', '+', '?', '|',
          '(', ')', '[', ']', '{', '}', '\\'
        ];
        arguments.callee.sRE = new RegExp(
          '(\\' + specials.join('|\\') + ')', 'g'
        );
      }
      return text.replace(arguments.callee.sRE, '\\$1');
    },

    /*
      find `name` in current `context`. That is find me a value
      from the view object
    */
    find: function(name, context) {
      name = this.trim(name);

      // Checks whether a value is truthy or false or 0
      function is_kinda_truthy(bool) {
        return bool === false || bool === 0 || bool;
      }

      var value;
      if(is_kinda_truthy(context[name])) {
        value = context[name];
      } else if(is_kinda_truthy(this.context[name])) {
        value = this.context[name];
      }
      if(typeof value === "function") {
        return value.apply(context);
      }
      if(value !== undefined) {
        return value;
      }
      if (value === undefined && name.indexOf('(') != -1) {
        // if value turned out to not exist on the context, then check to see if the function is parameterized
        var matches = name.match(/(.+)\((.+,?)+\)/);

        if (matches.length === 3) {
          name = matches[1];
          value = this.context[name];

          if (typeof value === "function" && matches[2]) {
            var that = this;
            var args = this.map(matches[2].split(/\s*,/),
              function(ele) { return that.find(ele, that.context); });
            return value.apply(context, args);
          }
        }
      }
      // silently ignore unknown variables
      return "";
    },

    // Utility methods

    /* includes tag */
    includes: function(needle, haystack) {
      return haystack.indexOf(this.otag + needle) != -1;
    },

    /*
      Does away with nasty characters
    */
    escape: function(s) {
      s = String(s === null ? "" : s);
      return s.replace(/&(?!\w+;)|["<>\\]/g, function(s) {
        switch(s) {
        case "&": return "&";
        case "\\": return "\\\\";
        case '"': return '\"';
        case "<": return "<";
        case ">": return ">";
        default: return s;
        }
      });
    },

    // by @langalex, support for arrays of strings
    create_context: function(_context) {
      if(this.is_object(_context)) {
        return _context;
      } else {
        var iterator = ".";
        if(this.pragmas["IMPLICIT-ITERATOR"] && this.pragmas["IMPLICIT-ITERATOR"].iterator) {
          iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator;
        }
        var ctx = {};
        ctx[iterator] = _context;
        return ctx;
      }
    },

    is_object: function(a) {
      return a && typeof a == "object";
    },

    is_array: function(a) {
      return Object.prototype.toString.call(a) === '[object Array]';
    },

    /*
      Gets rid of leading and trailing whitespace
    */
    trim: function(s) {
      return s.replace(/^\s*|\s*$/g, "");
    },

    /*
      Why, why, why? Because IE. Cry, cry cry.
    */
    map: function(array, fn) {
      if (typeof array.map == "function") {
        return array.map(fn);
      } else {
        var r = [];
        var l = array.length;
        for(var i = 0; i < l; i++) {
          r.push(fn(array[i]));
        }
        return r;
      }
    }
  };

  return({
    name: "mustache.js",
    version: "0.3.0-dev",

    /*
      Turns a template and view into HTML
    */
    to_html: function(template, view, partials) {
      var renderer = new Renderer();
      return renderer.render(template, view, partials);
    }
  });
}();
<commit_msg>first checkin of token based template parser<commit_after>/*
  mustache.js — Logic-less templates in JavaScript

  See http://mustache.github.com/ for more info.
*/

var Mustache = function() {
	var ParserException = function(message) {
		this.message = message;
	}

	var Renderer = function(send_func) {
		this.send_func = send_func;
	};
	Renderer.prototype = {
		render: function(template, context, partials) {
			var tokens = this.tokenize(template, '{{', '}}');

			this.parse(this.createParserContext(tokens, partials, '{{', '}}'), [context]);
		},

		createParserContext: function(tokens, partials, openTag, closeTag) {
			return {
				tokens: tokens,
				index: 0,
				length: tokens.length,
				partials: partials,
				stack: [],
				openTag: openTag,
				closeTag: closeTag,
			};
		},

		tokenize: function(template, openTag, closeTag) {
			var delimiters = [
				'\\{',
				'&',
				'\\}',
				'#',
				'\\^',
				'\\/',
				'>',
				'=',
				'%',
				'!',
				'\\s+'
			];
			delimiters.unshift(this.escape_regex(openTag));
			delimiters.unshift(this.escape_regex(closeTag));

			var regex = new RegExp('(' + delimiters.join('|') + ')');

			var tokens = template.split(regex);
			var cleaned_tokens = [];
			for (var i = 0, n = tokens.length; i<n; ++i) {
				if (tokens[i]!=='') {
					cleaned_tokens.push(tokens[i]);
				}
			}

			return cleaned_tokens;
		},

		parse: function(parserContext, contextStack) {
			var state = 'text';

			for (; parserContext.index<parserContext.length; ++parserContext.index) {
				state = this.stateMachine[state].call(this, parserContext, contextStack);
			}

			// make sure the parser finished at an appropriate terminal state
			if (state!=='text') {
				this.stateMachine['endOfDoc'].call(this, parserContext, contextStack);
			}
		},

		escape_regex: function(text) {
			// thank you Simon Willison
			var specials = [
				'/', '.', '*', '+', '?', '|',
				'(', ')', '[', ']', '{', '}', '\\'
			];
			var compiled_regex = new RegExp('(\\' + specials.join('|\\') + ')', 'g');

			return text.replace(compiled_regex, '\\$1');
		},

		isWhitespace: function(token) {
			return token.match(/^\s+$/)!==null;
		},

		stateMachine: {
			text: function(parserContext, contextStack) {
				switch (parserContext.tokens[parserContext.index]) {
					case parserContext.openTag:
						return 'openMustache';
					default:
						this.send_func(parserContext.tokens[parserContext.index]);

						return 'text';
				}
			},
			openMustache: function(parserContext, contextStack) {
				switch (parserContext.tokens[parserContext.index]) {
					case '{':
						parserContext.stack.push({tagType:'unescapedVariable', subtype: 'tripleMustache'});
						return 'keyName';
					case '&':
						parserContext.stack.push({tagType:'unescapedVariable'});
						return 'keyName';
					case '#':
						parserContext.stack.push({tagType:'section'});
						return 'keyName';
					case '^':
						parserContext.stack.push({tagType:'invertedSection'});
						return 'keyName';
					case '>':
						parserContext.stack.push({tagType:'partial'});

						return 'simpleKeyName';
					case '=':
						parserContext.stack.push({tagType: 'setDelimiter'});

						return 'setDelimiterStart';
					case '!':
						return 'discard';
					case '%':
						throw new ParserException('Pragmas are currently unsupported.');
					case '/': // close mustache
						throw new ParserException('Unexpected closing tag.');
					case '}': // close triple mustache
						throw new ParserException('Unexpected token encountered.');
					default:
						parserContext.stack.push({tagType:'variable'});

						return this.stateMachine.keyName.call(this, parserContext, contextStack);
				}
			},
			closeMustache: function(parserContext, contextStack) {
				if (this.isWhitespace(parserContext.tokens[parserContext.index])) {
					return 'closeMustache';
				} else if (parserContext.tokens[parserContext.index]===parserContext.closeTag) {
					return this.dispatchCommand(parserContext, contextStack);
				}
			},
			expectClosingMustache: function(parserContext, contextStack) {
				if (parserContext.closeTag==='}}' &&
					parserContext.tokens[parserContext.index]==='}}') {
					return 'expectClosingParenthesis';
				} else if (parserContext.tokens[parserContext.index]==='}') {
					return 'closeMustache';
				} else {
					throw new ParserException('Unexpected token encountered.');
				}
			},
			expectClosingParenthesis: function(parserContext, contextStack) {
				if (parserContext.tokens[parserContext.index]==='}') {
					return this.dispatchCommand(parserContext, contextStack);
				} else {
					throw new ParserException('Unexpected token encountered.');
				}
			},
			keyName: function(parserContext, contextStack) {
				var result = this.stateMachine.simpleKeyName.call(this, parserContext, contextStack);

				if (result==='closeMustache') {
					var tagKey = parserContext.stack.pop();
					var tag = parserContext.stack.pop();

					if (tag.tagType==='unescapedVariable' && tag.subtype==='tripleMustache') {
						parserContext.stack.push({tagType:'unescapedVariable'});
						parserContext.stack.push(tagKey);

						return 'expectClosingMustache';
					} else {
						parserContext.stack.push(tag);
						parserContext.stack.push(tagKey);

						return 'closeMustache';
					}
				} else if (result==='simpleKeyName') {
					return 'keyName';
				} else {
					throw new ParserException('Unexpected branch in tag name: ' + result);
				}
			},
			simpleKeyName: function(parserContext, contextStack) {
				if (this.isWhitespace(parserContext.tokens[parserContext.index])) {
					return 'simpleKeyName';
				} else {
					parserContext.stack.push(parserContext.tokens[parserContext.index]);

					return 'closeMustache';
				}
			},

			setDelimiterStart: function(parserContext, contextStack) {
				if (this.isWhitespace(parserContext.tokens[parserContext.index]) ||
					parserContext.tokens[parserContext.index]==='=') {
					throw new ParserException('Syntax error in Set Delimiter tag');
				} else {
					parserContext.stack.push(parserContext.tokens[parserContext.index]);
					return 'setDelimiterStartOrWhitespace';
				}
			},
			setDelimiterStartOrWhitespace: function(parserContext, contextStack) {
				if (this.isWhitespace(parserContext.tokens[parserContext.index])) {
					return 'setDelimiterEnd';
				} else if (parserContext.tokens[parserContext.index]==='='){
					throw new ParserException('Syntax error in Set Delimiter tag');
				} else {
					parserContext.stack.push(parserContext.stack.pop() + parserContext.tokens[parserContext.index]);

					return 'setDelimiterStartOrWhitespace';
				}
			},
			setDelimiterEnd: function(parserContext, contextStack) {
				if (this.isWhitespace(parserContext.tokens[parserContext.index])) {
					return 'setDelimiterEnd';
				} else if (parserContext.tokens[parserContext.index]==='=') {
					throw new ParserException('Syntax error in Set Delimiter tag');
				} else {
					parserContext.stack.push(parserContext.tokens[parserContext.index]);

					return 'setDelimiterEndOrEqualSign';
				}
			},
			setDelimiterEndOrEqualSign: function(parserContext, contextStack) {
				if (parserContext.tokens[parserContext.index]==='=') {
					return 'setDelimiterExpectClosingTag';
				} else if (this.isWhitespace(parserContext.tokens[parserContext.index])) {
					throw new ParserException('Syntax error in Set Delimiter tag');
				} else {
					parserContext.stack.push(parserContext.stack.pop() + parserContext.tokens[parserContext.index]);

					return 'setDelimiterEndOrEqualSign';
				}
			},
			setDelimiterExpectClosingTag: function(parserContext, contextStack) {
				if (parserContext.tokens[parserContext.index]===parserContext.closeTag) {
					var newCloseTag = parserContext.stack.pop();
					var newOpenTag = parserContext.stack.pop();
					var command = parserContext.stack.pop();

					if (command.tagType!=='setDelimiter') {
						throw new ParserException('Syntax error in Set Delimiter tag');
					} else {
						var tokens = this.tokenize(
							parserContext.tokens.slice(parserContext.index+1).join(''),
							newOpenTag,
							newCloseTag);

						var newParserContext = this.createParserContext(tokens,
							parserContext.partials,
							newOpenTag,
							newCloseTag);

						parserContext.tokens = newParserContext.tokens;
						parserContext.index = -1;
						parserContext.length = newParserContext.length;
						parserContext.openTag = newParserContext.openTag;
						parserContext.closeTag = newParserContext.closeTag;

						return 'text';
					}
				} else {
					throw new ParserException('Syntax error in Set Delimiter tag');
				}
			},

			endSectionScan: function(parserContext, contextStack) {
				switch (parserContext.tokens[parserContext.index]) {
					case parserContext.openTag:
						return 'expectSectionOrEndSection';
					default:
						parserContext.stack[parserContext.stack.length-1].content.push(parserContext.tokens[parserContext.index]);
						return 'endSectionScan';
				}
			},
			expectSectionOrEndSection: function(parserContext, contextStack) {
				switch (parserContext.tokens[parserContext.index]) {
					case '#':
					case '^':
						parserContext.stack[parserContext.stack.length-1].depth++;
						parserContext.stack[parserContext.stack.length-1].content.push(parserContext.openTag + parserContext.tokens[parserContext.index]);
						return 'endSectionScan';
					case '/':
						parserContext.stack.push({tagType:'endSection'});
						return 'simpleKeyName';
					default:
						parserContext.stack[parserContext.stack.length-1].content.push(parserContext.openTag + parserContext.tokens[parserContext.index]);
						return 'endSectionScan';
				}
			},

			discard: function(parserContext, contextStack) {
				if (parserContext.tokens[parserContext.index]===parserContext.closeTag) {
					return 'text';
				} else {
					return 'discard';
				}
			},

			endOfDoc: function(parserContext, contextStack) {
				// eventually we may want to give better error messages
				throw new ParserException('Unexpected end of document.');
			}
		},

		dispatchCommand: function(parserContext, contextStack) {
			var key = parserContext.stack.pop();
			var command = parserContext.stack.pop();

			switch (command.tagType) {
				case 'section':
					parserContext.stack.push({sectionType:'section', key:key, content:[], depth:1});
					return 'endSectionScan';
				case 'invertedSection':
					parserContext.stack.push({sectionType:'invertedSection', key:key, content:[], depth:1});
					return 'endSectionScan';
				case 'variable':
					this.render_variable(key, contextStack);
					return 'text';
				case 'unescapedVariable':
					this.render_unescaped_variable(key, contextStack);
					return 'text';
				case 'partial':
					this.render_partial(key,
						contextStack,
						parserContext.partials,
						parserContext.openTag,
						parserContext.closeTag);

					return 'text';
				case 'endSection':
					var section = parserContext.stack.pop();
					if (--section.depth === 0) {
						if (section.key === key) {
							this.render_section(section.sectionType,
								section.content.join(''),
								key,
								contextStack,
								parserContext.partials,
								parserContext.openTag,
								parserContext.closeTag);

							return 'text';
						} else {
							throw new ParserException('Unbalanced open/close section tags');
						}
					} else {
						section.content.push('{{/' + key + '}}');

						parserContext.stack.push(section);

						return 'endSectionScan';
					}
				default:
					throw new ParserException('Unknown dispatch command: ' + command.tagType);
			}
		},

		/*
		find `name` in current `context`. That is find me a value
		from the view object
		*/
		find: function(name, context) {
			// Checks whether a value is truthy or false or 0
			function is_kinda_truthy(bool) {
				return bool === false || bool === 0 || bool;
			}

			var value;
			if (is_kinda_truthy(context[name])) {
				value = context[name];
			}

			if (this.is_function(value)) {
				return value.apply(context);
			}

			return value;
		},

		find_in_stack: function(name, contextStack) {
			var value;

			for (var i=contextStack.length-1; i>=0; --i) {
				value = this.find(name, contextStack[i]);
				if (value) {
					return value;
				}
			}

			return undefined;
		},

		is_function: function(a) {
			return a && typeof a === 'function';
		},

		is_object: function(a) {
			return a && typeof a === 'object';
		},

		is_array: function(a) {
			return Object.prototype.toString.call(a) === '[object Array]';
		},

		render_variable: function(key, contextStack) {
			function escapeHTML(str) {
				return ('' + str).replace(/&/g,'&')
					.replace(/</g,'<')
					.replace(/>/g,'>');
			}

			var result = this.find(key, contextStack);
			if (result!==undefined) {
				this.send_func(escapeHTML(result));
			}
		},
		render_unescaped_variable: function(key, contextStack) {
			var result = this.find(key, contextStack);
			if (result!==undefined) {
				this.send_func(result);
			}
		},
		render_partial: function(key, contextStack, partials, openTag, closeTag) {
			if (!partials || partials[key] === undefined) {
				throw new ParserException('Unknown partial \'' + key + '\'');
			}

			var res = this.find(key, contextStack[contextStack.length-1]);
			if (this.is_object(res)) {
				contextStack.push(res);
			}

			var tokens = this.tokenize(partials[key], openTag, closeTag);

			this.parse(this.createParserContext(tokens, partials, openTag, closeTag), contextStack);

			if (this.is_object(res)) {
				contextStack.pop();
			}
		},
		render_section: function(sectionType, mustacheFragment, key, contextStack, partials, openTag, closeTag) {
			// by @langalex, support for arrays of strings
			var that = this;
			function create_context(_context) {
				if(that.is_object(_context)) {
					return _context;
				} else {
					var iterator = '.';
					// NO IMPLICIT-ITERATOR implementation yet
					//if(that.pragmas["IMPLICIT-ITERATOR"] &&
					//	that.pragmas["IMPLICIT-ITERATOR"].iterator) {
					//	iterator = that.pragmas["IMPLICIT-ITERATOR"].iterator;
					//}
					var ctx = {};
					ctx[iterator] = _context;
					return ctx;
				}
			}

			var value = this.find(key, contextStack[contextStack.length-1]);

			var tokens;
			if (sectionType==='invertedSection') {
				if (!value || this.is_array(value) && value.length === 0) {
					// false or empty list, render it
					tokens = this.tokenize(mustacheFragment, openTag, closeTag);

					this.parse(this.createParserContext(tokens, partials, openTag, closeTag), contextStack);
				}
			} else if (sectionType==='section') {
				if (this.is_array(value)) { // Enumerable, Let's loop!
					tokens = this.tokenize(mustacheFragment, openTag, closeTag);

					for (var i=0, n=value.length; i<n; ++i) {
						contextStack.push(create_context(value[i]));
						this.parse(this.createParserContext(tokens, partials, openTag, closeTag), contextStack);
						contextStack.pop();
					}
				} else if (this.is_object(value)) { // Object, Use it as subcontext!
					tokens = this.tokenize(mustacheFragment, openTag, closeTag);
					contextStack.push(value);
					this.parse(this.createParserContext(tokens, partials, openTag, closeTag), contextStack);
					contextStack.pop();
				} else if (this.is_function(value)) {
					// higher order section
					var that = this;

					var result = value.call(contextStack[contextStack.length-1], mustacheFragment, function(resultFragment) {
						var tempStream = [];
						var old_send_func = that.send_func;
						that.send_func = function(text) { tempStream.push(text); };

						tokens = that.tokenize(resultFragment, openTag, closeTag);
						that.parse(that.createParserContext(tokens, partials, openTag, closeTag), contextStack);

						that.send_func = old_send_func;

						return tempStream.join('');
					});

					this.send_func(result);
				} else if (value) { // boolean section
					tokens = this.tokenize(mustacheFragment, openTag, closeTag);
					this.parse(this.createParserContext(tokens, partials, openTag, closeTag), contextStack);
				}
			} else {
				throw new ParserException('Unknown section type ' + sectionType);
			}
		}
	}

	return({
		name: "mustache.js",
		version: "0.4.0-vcs",

		/*
		Turns a template and view into HTML
		*/
		to_html: function(template, view, partials, send_func) {
			var o = [];
			var s = send_func || function(output) { o.push(output); };

			var renderer = new Renderer(s);
			renderer.render(template, view, partials);

			if (!send_func) {
				return o.join('');
			}
		}
	});
}();
<|endoftext|>


<commit_before>"use strict";
var
  extend = require("extend"),
  util = require("util"),
  assert = require("chai").assert,
  moment = require("moment"),
  Rules = require("./rules"),
  HttpClient = require("./httpClient");

var Constants = {
  Method: {
    GET: "GET",
    POST: "POST",
    PUT: "PUT",
    DELETE: "DELETE"
  },
  ContentType: {
    JSON: "application/json",
    URLENCODED: "application/x-www-form-urlencoded",
    MULTIPART: "multipart/form-data"
  },
  DataType: {
    STRING: "string",
    INT: "int",
    NUMBER: "number",
    BOOLEAN: "boolean",
    DATE: "date",
    DATETIME: "datetime",
    BIT: "bit"
  }
};

function removeElement(array, obj) {
  var n = array.indexOf(obj);
  if (n === -1) {
    return false;
  }
  array.splice(n, 1);
  return true;
}

function isValidDataType(type) {
  var ret = Object.keys(Constants.DataType).filter(function(key) {
    return Constants.DataType[key] === type;
  });
  return ret.length > 0;
}

function Param(name, value, strict) {
  function init() {
    switch (typeof(value)) {
      case "string":
        dataType = value;
        break;
      case "object":
        var obj = value;
        if (util.isArray(value)) {
          isArray = true;
          obj = value[0];
        } else {
          dataType = "object";
        }
        if (typeof(obj) === "string") {
          dataType = obj;
        } else if (util.isArray(obj)) {
          dataType = "array";
          children = {
            "array": new Param("array", obj, strict)
          };
        } else {
          dataType = "object";
          children = {};
          Object.keys(obj).forEach(function(key) {
            children[key] = new Param(key, obj[key], strict);
          });
        }
        break;
    }
  }
  function test() {
    function testDataType() {
      if (dataType === "object" || dataType === "array") {
        return;
      }
      assert.ok(isValidDataType(dataType), "Invalid datatype: " + name + ": " + dataType);
    }
    function testArray() {
      assert.equal(value.length, 1,
        "The array in parameter definition must has only one element.");
    }
    function testRules() {
      Object.keys(tempRules).map(function(key) {
        if (key !== "format") {
          it("rule." + key, function() {
            var value = tempRules[key];
            Rules.testRule(key, value);
          });
        }
      });
    }
    describe("Validate " + name + " param.", function() {
      it("datatype", testDataType);
      if (isArray) {
        it("array", testArray);
      }
      describe("rules", testRules);
      if (children) {
        Object.keys(children).forEach(function(key) {
          children[key].test();
        });
      }
    });
  }
  function build() {
    if (dataType === "date") {
      format = rules.format || "YYYY-MM-DD";
    } else if (dataType === "datetime") {
      format = rules.format || "YYYY-MM-DD HH:mm:SS";
    }
    Object.keys(tempRules).map(function(key) {
      if (key !== "format") {
        if (Rules.hasRule(key)) {
          rules.push(Rules.newInstance(key, tempRules[key]));
        }
      }
    });
    if (children) {
      Object.keys(children).forEach(function(key) {
        children[key].build();
      });
    }
  }
  function validateType(value) {
    var type = typeof(value);
    switch (dataType) {
      case Constants.DataType.STRING:
      case Constants.DataType.NUMBER:
      case Constants.DataType.BOOLEAN:
        return type === dataType;
      case Constants.DataType.INT:
        return type === "number" && Math.floor(value) === value;
      case Constants.DataType.DATE:
      case Constants.DataType.DATETIME:
        var d = moment(value);
        return d && d.format(format) === value;
      case Constants.DataType.BIT:
        return value == "0" || value == "1";
    }
    return false;
  }
  function validate(value) {
    var target = [value];
    if (value) {
      if (isArray) {
        assert.ok(util.isArray(value), name + " must be array.");
        target = value;
      }
    }
    target.forEach(function(obj) {
      var dataNames = null;
      if (typeof(obj) === "object") {
        if (util.isArray(obj)) {
          dataNames = ["array"];
        } else if (obj !== null) {
          dataNames = Object.keys(obj);
        }
      } else {
        assert.ok(validateType(obj),
          obj + ": " + name + " must be " + (isArray ? "array of " : "") + dataType);
      }
      self.rules.forEach(function(rule) {
        assert.ok(rule.validate(obj),
          obj + ": " + rule.message(name, obj));
      });
      if (children && dataNames) {
        Object.keys(children).forEach(function(key) {
          var
            p = children[key],
            v = removeElement(dataNames, key) ? obj[key] : null;
          p.validate(v);
        });
        if (strict) {
          assert.ok(dataNames.length === 0, "Unknown keys: " + JSON.stringify(dataNames));
        }
      }
    });
  }
  function addRules(v) {
    extend(tempRules, v);
  }
  function hasChildren() {
    return !!children;
  }
  function childNames() {
    return Object.keys(children);
  }
  function getChild(name) {
    return children ? children[name] : null;
  }
  var
    self = this,
    dataType = null,
    isArray = false,
    tempRules = {},
    rules = [],
    children = null,
    format = null;

  init();

  extend(this, {
    "name": name,
    "type": dataType,
    "isArray": isArray,
    "build": build,
    "test": test,
    "validate": validate,
    "addRules": addRules,
    "rules": rules,
    "hasChildren": hasChildren,
    "childNames": childNames,
    "getChild": getChild
  });
}

function API(config) {
  function buildParamNames(param) {
    function buildNames(prefix, param) {
      var obj = names[param.name];
      if (!obj) {
        obj = [];
        names[param.name] = obj;
      }
      obj.push(param);
      if (prefix) {
        names[prefix + param.name] = [param];
      }
      if (param.hasChildren()) {
        param.childNames().forEach(function(key) {
          buildNames(prefix + param.name + ".", param.getChild(key));
        });
      }
    }
    var names = {};
    if (param.hasChildren()) {
      param.childNames().forEach(function(key) {
        buildNames("", param.getChild(key));
      });
    }
    return names;
  }
  function isSuccess(data, res) {
    return res.statusCode >= 200 && res.statusCode < 300;
  }
  function isBadRequest(data, res) {
    return res.statusCode === 400;
  }
  function isNotFound(data, res) {
    return res.statusCode === 404;
  }
  function isUnauthorized(data, res) {
    return res.statusCode === 401;
  }
  function isClientError(data, res) {
    return res.statusCode >= 400 && res.statusCode < 500;
  }
  function validateResponse(data, res, req) {
    var resContentType = res.headers["content-type"];
    assert.equal(config.response.contentType, resContentType,
      "Content-Type is not match: " + resContentType);
    if (resContentType === Constants.ContentType.JSON) {
      config.response.data.validate(data);
    }
  }
  function init() {
    function applyRules(param, rules) {
      var
        names = buildParamNames(param),
        ret = [];
      Object.keys(rules).forEach(function(key) {
        var array = names[key];
        if (array) {
          array.forEach(function(p) {
            p.addRules(rules[key]);
          });
        }
      });
    }
    config.name = config.name || config.endpoint;
    var req = ("request" in config) ? config.request : {};
    req.contentType = ("contentType" in req) ? req.contentType : Constants.ContentType.URLENCODED;
    if (req.params) {
      req.params = new Param("request", req.params, !!req.strict);
      if (req.rules) {
        applyRules(req.params, req.rules);
      }
    }
    config.request = req;

    var res = config.response;
    if (res) {
      res.contentType = ("contentType" in res) ? res.contentType : Constants.ContentType.JSON;
      if (res.data) {
        res.data = new Param("response", res.data, !!res.strict);
        if (res.rules) {
          applyRules(res.data, res.rules);
        }
      }
    }
  }
  function test() {
    function testContentType(value) {
      var ret = false;
      Object.keys(Constants.ContentType).forEach(function(key) {
        if (Constants.ContentType[key] === value) {
          ret = true;
        }
      });
      assert.ok(ret, "Invlaid contentType: " + value);
    }
    function testEndpoint() {
      assert.ok(config.endpoint, "endpoint is not defined");
      assert.ok(config.endpoint.charAt(0) === "/", "endpoint must starts with '/'");
    }
    function testMethod() {
      assert.ok(config.method, "method is not defined.");
      var values = Object.keys(Constants.Method).map(function(key) {
        return Constants.Method[key];
      });
      assert.include(values, config.method, "Invlaid method name: " + config.method);
    }
    function testRulesReference(param, rules) {
      it("Validate rules reference.", function() {
        var
          names = buildParamNames(param),
          ret = [];
        Object.keys(rules).forEach(function(key) {
          if (!names[key]) {
            ret.push(key);
          }
        });
        assert.ok(ret.length === 0, "Undefined parameters.: " + JSON.stringify(ret));
      });
    }
    function testRequest() {
      var req = config.request;
      it("contentType is correct.", function() {
        testContentType(req.contentType);
      });
      if (req.params) {
        req.params.test();
      }
      if (req.params && req.rules) {
        testRulesReference(req.params, req.rules);
      }
    }
    function testResponse() {
      var res = config.response;
      it("response is defined.", function() {
        assert.ok(res, "response is not defined.");
      });
      it("data is defined.", function() {
        assert.ok(res.data, "data is not defined.");
      });
      if (res.data) {
        res.data.test();
      }
      if (res.data && res.rules) {
        testRulesReference(res.data, res.rules);
      }
    }
    function testLogin() {
      var api = config.login;
      assert.equal("API", api.constructor.name, "login must be API object.");
    }
    function testUnknownKeys() {
      var
        keys = [
          "name",
          "description",
          "endpoint",
          "method",
          "login",
          "request",
          "response",
          "isSuccess",
          "isBadRequest",
          "isNotFound",
          "isUnauthorized",
          "isClientError",
        ],
        ret = [];
      Object.keys(config).forEach(function(key) {
        if (keys.indexOf(key) === -1) {
          ret.push(key);
        }
      });
      assert.ok(ret.length === 0, "config has unknown keys: " + JSON.stringify(ret));

    }
    describe("Validate API spec. " + config.name, function() {
      it("endpoint is correct.", testEndpoint);
      it("method is correct.", testMethod);
      describe("Validate request spec.", testRequest);
      describe("Validate response spec.", testResponse);
      if (config.login) {
        it("login is correct", testLogin);
      }
      it("unknown config keys", testUnknownKeys);
    });
  }
  function build() {
    if (config.endpoint) {
      var urlParams = config.endpoint.match(/\[[^\]]*\]/g);
      if (urlParams && urlParams.length > 0) {
        config.urlParams = urlParams.map(function(v) {
          return v.substring(1, v.length - 1);
        });
      }
    }
    if (config.request.params) {
      config.request.params.build();
    }
    if (config.response && config.response.data) {
      config.response.data.build();
    }
    extend(self, config);
  }
  var self = this;
  extend(self, {
    "isSuccess": isSuccess,
    "isBadRequest": isBadRequest,
    "isNotFound": isNotFound,
    "isUnauthorized": isUnauthorized,
    "isClientError": isClientError,
    "validateResponse": validateResponse
  });

  init();
  test();
  build();
}

module.exports = extend({
  define: function(config) {
    return new API(config);
  },
  host: function(host, ssl) {
    return new HttpClient(host, ssl);
  }
}, Constants);
<commit_msg>Implement ParamPool<commit_after>"use strict";
var
  extend = require("extend"),
  util = require("util"),
  assert = require("chai").assert,
  moment = require("moment"),
  Rules = require("./rules"),
  HttpClient = require("./httpClient");

var Constants = {
  Method: {
    GET: "GET",
    POST: "POST",
    PUT: "PUT",
    DELETE: "DELETE"
  },
  ContentType: {
    JSON: "application/json",
    URLENCODED: "application/x-www-form-urlencoded",
    MULTIPART: "multipart/form-data"
  },
  DataType: {
    STRING: "string",
    INT: "int",
    NUMBER: "number",
    BOOLEAN: "boolean",
    DATE: "date",
    DATETIME: "datetime",
    BIT: "bit"
  }
};

function removeElement(array, obj) {
  var n = array.indexOf(obj);
  if (n === -1) {
    return false;
  }
  array.splice(n, 1);
  return true;
}

function isValidDataType(type) {
  var ret = Object.keys(Constants.DataType).filter(function(key) {
    return Constants.DataType[key] === type;
  });
  return ret.length > 0;
}

function ParamPool(param) {
  function build(p) {
    if (p.hasChildren()) {
      p.childNames().forEach(function(name) {
        var
          child = p.getChild(name),
          array = map[name];
        if (!array) {
          array = [];
          map[name] = array;
        }
        array.push(child);
        build(child);
      });
    }
  }
  function getAllParams() {
    var ret = [];
    Object.keys(map).forEach(function(key) {
      ret.push.apply(ret, map[key]);
    });
    return ret;
  }
  function getParams(key) {
    function doGet(p, keys) {
      var ret = p.getChild(keys.shift());
      if (!ret) {
        return null;
      } else if (keys.length === 0) {
        return ret;
      } else {
        return doGet(ret, keys);
      }
    }
    if (key === "*") {
      return getAllParams();
    }
    var
      keys = key.split("."),
      ret = map[keys.shift()];
    if (!ret) {
      return null;
    } else if (keys.length === 0) {
      return ret;
    } else {
      var array = [];
      ret.forEach(function(p) {
        var ret2 = doGet(p, [].concat(keys));
        if (ret2) {
          array.push(ret2);
        }
      });
      return array.length > 0 ? array : null;
    }
  }
  var map = {};
  build(param);
  extend(this, {
    getParams: getParams
  });
}

function Param(name, value, strict) {
  function init() {
    switch (typeof(value)) {
      case "string":
        dataType = value;
        break;
      case "object":
        var obj = value;
        if (util.isArray(value)) {
          isArray = true;
          obj = value[0];
        } else {
          dataType = "object";
        }
        if (typeof(obj) === "string") {
          dataType = obj;
        } else if (util.isArray(obj)) {
          dataType = "array";
          children = {
            "array": new Param("array", obj, strict)
          };
        } else {
          dataType = "object";
          children = {};
          Object.keys(obj).forEach(function(key) {
            children[key] = new Param(key, obj[key], strict);
          });
        }
        break;
    }
  }
  function test() {
    function testDataType() {
      if (dataType === "object" || dataType === "array") {
        return;
      }
      assert.ok(isValidDataType(dataType), "Invalid datatype: " + name + ": " + dataType);
    }
    function testArray() {
      assert.equal(value.length, 1,
        "The array in parameter definition must has only one element.");
    }
    function testRules() {
      Object.keys(tempRules).map(function(key) {
        if (key !== "format") {
          it("rule." + key, function() {
            var value = tempRules[key];
            Rules.testRule(key, value);
          });
        }
      });
    }
    describe("Validate " + name + " param.", function() {
      it("datatype", testDataType);
      if (isArray) {
        it("array", testArray);
      }
      describe("rules", testRules);
      if (children) {
        Object.keys(children).forEach(function(key) {
          children[key].test();
        });
      }
    });
  }
  function build() {
    if (dataType === "date") {
      format = rules.format || "YYYY-MM-DD";
    } else if (dataType === "datetime") {
      format = rules.format || "YYYY-MM-DD HH:mm:SS";
    }
    Object.keys(tempRules).map(function(key) {
      if (key !== "format") {
        if (Rules.hasRule(key)) {
          rules.push(Rules.newInstance(key, tempRules[key]));
        }
      }
    });
    if (children) {
      Object.keys(children).forEach(function(key) {
        children[key].build();
      });
    }
  }
  function validateType(value) {
    var type = typeof(value);
    switch (dataType) {
      case Constants.DataType.STRING:
      case Constants.DataType.NUMBER:
      case Constants.DataType.BOOLEAN:
        return type === dataType;
      case Constants.DataType.INT:
        return type === "number" && Math.floor(value) === value;
      case Constants.DataType.DATE:
      case Constants.DataType.DATETIME:
        var d = moment(value);
        return d && d.format(format) === value;
      case Constants.DataType.BIT:
        return value == "0" || value == "1";
    }
    return false;
  }
  function validate(value) {
    var target = [value];
    if (value) {
      if (isArray) {
        assert.ok(util.isArray(value), name + " must be array.");
        target = value;
      }
    }
    target.forEach(function(obj) {
      var dataNames = null;
      if (typeof(obj) === "object") {
        if (util.isArray(obj)) {
          dataNames = ["array"];
        } else if (obj !== null) {
          dataNames = Object.keys(obj);
        }
      } else {
        assert.ok(validateType(obj),
          obj + ": " + name + " must be " + (isArray ? "array of " : "") + dataType);
      }
      self.rules.forEach(function(rule) {
        assert.ok(rule.validate(obj),
          obj + ": " + rule.message(name, obj));
      });
      if (children && dataNames) {
        Object.keys(children).forEach(function(key) {
          var
            p = children[key],
            v = removeElement(dataNames, key) ? obj[key] : null;
          p.validate(v);
        });
        if (strict) {
          assert.ok(dataNames.length === 0, "Unknown keys: " + JSON.stringify(dataNames));
        }
      }
    });
  }
  function addRules(v) {
    extend(tempRules, v);
  }
  function hasChildren() {
    return !!children;
  }
  function childNames() {
    return Object.keys(children);
  }
  function getChild(name) {
    return children ? children[name] : null;
  }
  var
    self = this,
    dataType = null,
    isArray = false,
    tempRules = {},
    rules = [],
    children = null,
    format = null;

  init();

  extend(this, {
    "name": name,
    "type": dataType,
    "isArray": isArray,
    "build": build,
    "test": test,
    "validate": validate,
    "addRules": addRules,
    "rules": rules,
    "hasChildren": hasChildren,
    "childNames": childNames,
    "getChild": getChild
  });
}

function API(config) {
  function isSuccess(data, res) {
    return res.statusCode >= 200 && res.statusCode < 300;
  }
  function isBadRequest(data, res) {
    return res.statusCode === 400;
  }
  function isNotFound(data, res) {
    return res.statusCode === 404;
  }
  function isUnauthorized(data, res) {
    return res.statusCode === 401;
  }
  function isClientError(data, res) {
    return res.statusCode >= 400 && res.statusCode < 500;
  }
  function validateResponse(data, res, req) {
    var resContentType = res.headers["content-type"];
    assert.equal(config.response.contentType, resContentType,
      "Content-Type is not match: " + resContentType);
    if (resContentType === Constants.ContentType.JSON) {
      config.response.data.validate(data);
    }
  }
  function init() {
    function applyRules(param, rules) {
      var pool = new ParamPool(param);
      Object.keys(rules).forEach(function(key) {
        var array = pool.getParams(key);
        if (array) {
          array.forEach(function(p) {
console.log("***** Rule: " + p.name + ", " + key);
            p.addRules(rules[key]);
          });
        }
      });
    }
    config.name = config.name || config.endpoint;
    var req = ("request" in config) ? config.request : {};
    req.contentType = ("contentType" in req) ? req.contentType : Constants.ContentType.URLENCODED;
    if (req.params) {
      req.params = new Param("request", req.params, !!req.strict);
      if (req.rules) {
        applyRules(req.params, req.rules);
      }
    }
    config.request = req;

    var res = config.response;
    if (res) {
      res.contentType = ("contentType" in res) ? res.contentType : Constants.ContentType.JSON;
      if (res.data) {
        res.data = new Param("response", res.data, !!res.strict);
        if (res.rules) {
          applyRules(res.data, res.rules);
        }
      }
    }
  }
  function test() {
    function testContentType(value) {
      var ret = false;
      Object.keys(Constants.ContentType).forEach(function(key) {
        if (Constants.ContentType[key] === value) {
          ret = true;
        }
      });
      assert.ok(ret, "Invlaid contentType: " + value);
    }
    function testEndpoint() {
      assert.ok(config.endpoint, "endpoint is not defined");
      assert.ok(config.endpoint.charAt(0) === "/", "endpoint must starts with '/'");
    }
    function testMethod() {
      assert.ok(config.method, "method is not defined.");
      var values = Object.keys(Constants.Method).map(function(key) {
        return Constants.Method[key];
      });
      assert.include(values, config.method, "Invlaid method name: " + config.method);
    }
    function testRulesReference(param, rules) {
      it("Validate rules reference.", function() {
        var
          pool = new ParamPool(param),
          ret = [];
        Object.keys(rules).forEach(function(key) {
          if (!pool.getParams(key)) {
            ret.push(key);
          }
        });
        assert.ok(ret.length === 0, "Undefined parameters.: " + JSON.stringify(ret));
      });
    }
    function testRequest() {
      var req = config.request;
      it("contentType is correct.", function() {
        testContentType(req.contentType);
      });
      if (req.params) {
        req.params.test();
      }
      if (req.params && req.rules) {
        testRulesReference(req.params, req.rules);
      }
    }
    function testResponse() {
      var res = config.response;
      it("response is defined.", function() {
        assert.ok(res, "response is not defined.");
      });
      it("data is defined.", function() {
        assert.ok(res.data, "data is not defined.");
      });
      if (res.data) {
        res.data.test();
      }
      if (res.data && res.rules) {
        testRulesReference(res.data, res.rules);
      }
    }
    function testLogin() {
      var api = config.login;
      assert.equal("API", api.constructor.name, "login must be API object.");
    }
    function testUnknownKeys() {
      var
        keys = [
          "name",
          "description",
          "endpoint",
          "method",
          "login",
          "request",
          "response",
          "isSuccess",
          "isBadRequest",
          "isNotFound",
          "isUnauthorized",
          "isClientError",
        ],
        ret = [];
      Object.keys(config).forEach(function(key) {
        if (keys.indexOf(key) === -1) {
          ret.push(key);
        }
      });
      assert.ok(ret.length === 0, "config has unknown keys: " + JSON.stringify(ret));

    }
    describe("Validate API spec. " + config.name, function() {
      it("endpoint is correct.", testEndpoint);
      it("method is correct.", testMethod);
      describe("Validate request spec.", testRequest);
      describe("Validate response spec.", testResponse);
      if (config.login) {
        it("login is correct", testLogin);
      }
      it("unknown config keys", testUnknownKeys);
    });
  }
  function build() {
    if (config.endpoint) {
      var urlParams = config.endpoint.match(/\[[^\]]*\]/g);
      if (urlParams && urlParams.length > 0) {
        config.urlParams = urlParams.map(function(v) {
          return v.substring(1, v.length - 1);
        });
      }
    }
    if (config.request.params) {
      config.request.params.build();
    }
    if (config.response && config.response.data) {
      config.response.data.build();
    }
    extend(self, config);
  }
  var self = this;
  extend(self, {
    "isSuccess": isSuccess,
    "isBadRequest": isBadRequest,
    "isNotFound": isNotFound,
    "isUnauthorized": isUnauthorized,
    "isClientError": isClientError,
    "validateResponse": validateResponse
  });

  init();
  test();
  build();
}

module.exports = extend({
  define: function(config) {
    return new API(config);
  },
  host: function(host, ssl) {
    return new HttpClient(host, ssl);
  }
}, Constants);
<|endoftext|>


<commit_before>/*
	used to setup/update company name, address, notifications...
*/
const slapp       = require('./slackSetup.js').slapp
const firebase    = require('./firebaseSetup.js')
let msgAttachments = require('./msgAttachments.js')
let notify = require('./notifySlash.js').notify

console.log(msgAttachments)

let teamSettings = function(teamInfo,msg,state){
// console.log(msg.type)
// console.log(msg.body.token)
// console.log(msg.body.message_ts)
// console.log(msg.body.channel.id)

  msg.respond({
  text: "",
    attachments: [{
    	pretext: `Click a button to change settings`,
      text: `Your current team is located at ${teamInfo.address}.`,
      title: `Team settings for ${teamInfo.location_name}.`,
      callback_id: 'settings_callback',
      color: 'info',
      actions:
      [{
        name: 'answer',
        text: `New Name`,
        type: 'button',
        value: 'teamName'
      },{
        name: 'answer',
        text: `New Location`,
        type: 'button',
        value: 'teamLocation'
      },{
        name: 'answer',
        text: `Notifications`,
        type: 'button',
        value: 'notifications'
      }
      // ,{
      //   name: 'answer',
      //   text: `<Back`,
      //   type: 'button',
      //   value: 'back'
      // }
      ]
    }]
  })
  slapp.action('settings_callback', 'answer',(msg,value) => {
  	if(value=='back'){
  		msg
    		.route('main',state,60)
  		  // .route('requestToDatabase', state, 60)

  	}else if(value=='notifications'){
      notify(teamInfo,msg,state)
  	}else{
			msg.respond({
  			text:`${value}`
  		})
  	}
  })

}
module.exports = {
	teamSettings:teamSettings
}

<commit_msg>notify on settings<commit_after>/*
	used to setup/update company name, address, notifications...
*/
const slapp       = require('./slackSetup.js').slapp
const firebase    = require('./firebaseSetup.js')
let msgAttachments = require('./msgAttachments.js')
let notify = require('./slashCommands/notifySlash.js').notify

console.log(msgAttachments)

let teamSettings = function(teamInfo,msg,state){
// console.log(msg.type)
// console.log(msg.body.token)
// console.log(msg.body.message_ts)
// console.log(msg.body.channel.id)

  msg.respond({
  text: "",
    attachments: [{
    	pretext: `Click a button to change settings`,
      text: `Your current team is located at ${teamInfo.address}.`,
      title: `Team settings for ${teamInfo.location_name}.`,
      callback_id: 'settings_callback',
      color: 'info',
      actions:
      [{
        name: 'answer',
        text: `New Name`,
        type: 'button',
        value: 'teamName'
      },{
        name: 'answer',
        text: `New Location`,
        type: 'button',
        value: 'teamLocation'
      },{
        name: 'answer',
        text: `Notifications`,
        type: 'button',
        value: 'notifications'
      }
      // ,{
      //   name: 'answer',
      //   text: `<Back`,
      //   type: 'button',
      //   value: 'back'
      // }
      ]
    }]
  })
  slapp.action('settings_callback', 'answer',(msg,value) => {
  	if(value=='back'){
  		msg
    		.route('main',state,60)
  		  // .route('requestToDatabase', state, 60)

  	}else if(value=='notifications'){
      notify(teamInfo,msg,state)
  	}else{
			msg.respond({
  			text:`${value}`
  		})
  	}
  })

}
module.exports = {
	teamSettings:teamSettings
}

<|endoftext|>


<commit_before>import { isSSR, fromSSR } from './platform'
import { listenOpts } from '../utils/event'
import { debounce } from '../utils/debounce'

export default {
  width: 0,

  sizes: {
    sm: 576,
    md: 768,
    lg: 992,
    xl: 1200
  },

  lt: {
    sm: true,
    md: true,
    xl: true
  },
  gt: {},
  xs: true,

  setSizes () {},
  setDebounce () {},

  install ({ $q, queues, Vue }) {
    if (isSSR) {
      $q.screen = this
      return
    }

    let update = () => {
      const
        w = window.innerWidth,
        s = this.sizes

      if (w === this.width) {
        return
      }

      this.width = w

      this.gt.xs = w >= s.sm
      this.gt.sm = w >= s.md
      this.gt.md = w >= s.lg
      this.gt.lg = w >= s.xl
      this.lt.sm = w < s.sm
      this.lt.md = w < s.md
      this.lt.lg = w < s.lg
      this.lt.xl = w < s.xl
      this.xs = this.lt.sm
      this.sm = this.gt.xs && this.lt.md
      this.md = this.gt.sm && this.lt.lg
      this.lg = this.gt.md && this.lt.xl
      this.xl = w > s.xl
    }

    let
      updateEvt = debounce(update, 100),
      updateSizes = {},
      updateDebounce

    this.setSizes = sizes => {
      sizes.forEach(name => {
        updateSizes[name] = sizes[name]
      })
    }
    this.setDebounce = deb => {
      updateDebounce = deb
    }

    const start = () => {
      const style = getComputedStyle(document.body)

      // if css props available
      if (style.getPropertyValue('--q-size-sm')) {
        ['sm', 'md', 'lg', 'xl'].forEach(name => {
          this.sizes[name] = parseInt(style.getPropertyValue(`--q-size-${name}`), 10)
        })
      }

      this.setSizes = sizes => {
        ['sm', 'md', 'lg', 'xl'].forEach(name => {
          if (sizes[name]) {
            this.sizes[name] = sizes[name]
          }
        })
        update()
      }
      this.setDebounce = delay => {
        window.removeEventListener('resize', updateEvt, listenOpts.passive)
        updateEvt = delay > 0
          ? debounce(update, delay)
          : update
        window.addEventListener('resize', updateEvt, listenOpts.passive)
      }

      updateDebounce && this.setDebounce(updateDebounce)
      Object.keys(updateSizes).length > 0 && this.setSizes(updateSizes)
      update()

      window.addEventListener('resize', updateEvt, listenOpts.passive)
    }

    Vue.util.defineReactive($q, 'screen', this)

    if (fromSSR) {
      queues.takeover.push(start)
    }
    else {
      start()
    }
  }
}
<commit_msg>fix: Screen plugin doesn't updates values after setting new sizes #2221<commit_after>import { isSSR, fromSSR } from './platform'
import { listenOpts } from '../utils/event'
import { debounce } from '../utils/debounce'

export default {
  width: 0,

  sizes: {
    sm: 576,
    md: 768,
    lg: 992,
    xl: 1200
  },

  lt: {
    sm: true,
    md: true,
    xl: true
  },
  gt: {},
  xs: true,

  setSizes () {},
  setDebounce () {},

  install ({ $q, queues, Vue }) {
    if (isSSR) {
      $q.screen = this
      return
    }

    let update = resized => {
      const
        w = window.innerWidth,
        s = this.sizes

      if (resized && w === this.width) {
        return
      }

      this.width = w

      this.gt.xs = w >= s.sm
      this.gt.sm = w >= s.md
      this.gt.md = w >= s.lg
      this.gt.lg = w >= s.xl
      this.lt.sm = w < s.sm
      this.lt.md = w < s.md
      this.lt.lg = w < s.lg
      this.lt.xl = w < s.xl
      this.xs = this.lt.sm
      this.sm = this.gt.xs && this.lt.md
      this.md = this.gt.sm && this.lt.lg
      this.lg = this.gt.md && this.lt.xl
      this.xl = w > s.xl
    }

    let updateEvt, updateSizes = {}, updateDebounce

    this.setSizes = sizes => {
      sizes.forEach(name => {
        updateSizes[name] = sizes[name]
      })
    }
    this.setDebounce = deb => {
      updateDebounce = deb
    }

    const start = () => {
      const style = getComputedStyle(document.body)

      // if css props available
      if (style.getPropertyValue('--q-size-sm')) {
        ['sm', 'md', 'lg', 'xl'].forEach(name => {
          this.sizes[name] = parseInt(style.getPropertyValue(`--q-size-${name}`), 10)
        })
      }

      this.setSizes = sizes => {
        ['sm', 'md', 'lg', 'xl'].forEach(name => {
          if (sizes[name]) {
            this.sizes[name] = sizes[name]
          }
        })
        update()
      }
      this.setDebounce = delay => {
        const fn = () => { update(true) }
        updateEvt && window.removeEventListener('resize', updateEvt, listenOpts.passive)
        updateEvt = delay > 0
          ? debounce(fn, delay)
          : fn
        window.addEventListener('resize', updateEvt, listenOpts.passive)
      }

      this.setDebounce(updateDebounce || 100)

      if (Object.keys(updateSizes).length > 0) {
        this.setSizes(updateSizes)
        updateSizes = null
      }
      else {
        update()
      }
    }

    Vue.util.defineReactive($q, 'screen', this)

    if (fromSSR) {
      queues.takeover.push(start)
    }
    else {
      start()
    }
  }
}
<|endoftext|>


<commit_before>
import isActiveElement from '../is/active-element';
import queryTabsequence from '../query/tabsequence';
import whenKey from '../when/key';

export default function({ context } = {}) {
  if (!context) {
    context = document.documentElement;
  }

  return whenKey({
    '?shift+tab': function(event) {
      // we're completely taking over the Tab key handling
      event.preventDefault();

      const sequence = queryTabsequence({
        context,
      });

      const backward = event.shiftKey;
      const first = sequence[0];
      const last = sequence[sequence.length - 1];

      // wrap around first to last, last to first
      const source = backward ? first : last;
      const target = backward ? last : first;
      if (isActiveElement(source)) {
        target.focus();
        return;
      }

      // find current position in tabsequence
      let currentIndex;
      const found = sequence.some(function(element, index) {
        if (!isActiveElement(element)) {
          return false;
        }

        currentIndex = index;
        return true;
      });

      if (!found) {
        // redirect to first as we're not in our tabsequence
        first.focus();
        return;
      }

      // shift focus to previous/next element in the sequence
      const offset = backward ? -1 : 1;
      sequence[currentIndex + offset].focus();
    },
  });
}
<commit_msg>fix(maintain/tab-focus): make sure supports tests are performed before first Tab key event<commit_after>
import isActiveElement from '../is/active-element';
import queryTabsequence from '../query/tabsequence';
import whenKey from '../when/key';

export default function({ context } = {}) {
  if (!context) {
    context = document.documentElement;
  }

  // Make sure the supports tests are run before intercepting the Tab key,
  // or IE10 and IE11 will fail to process the first Tab key event. Not
  // limiting this warm-up to IE because it may be a problem elsewhere, too.
  queryTabsequence();

  return whenKey({
    '?shift+tab': function(event) {
      // we're completely taking over the Tab key handling
      event.preventDefault();

      const sequence = queryTabsequence({
        context,
      });

      const backward = event.shiftKey;
      const first = sequence[0];
      const last = sequence[sequence.length - 1];

      // wrap around first to last, last to first
      const source = backward ? first : last;
      const target = backward ? last : first;
      if (isActiveElement(source)) {
        target.focus();
        return;
      }

      // find current position in tabsequence
      let currentIndex;
      const found = sequence.some(function(element, index) {
        if (!isActiveElement(element)) {
          return false;
        }

        currentIndex = index;
        return true;
      });

      if (!found) {
        // redirect to first as we're not in our tabsequence
        first.focus();
        return;
      }

      // shift focus to previous/next element in the sequence
      const offset = backward ? -1 : 1;
      sequence[currentIndex + offset].focus();
    },
  });
}
<|endoftext|>


<commit_before>/*
 * BSD 3-Clause License
 *
 * Copyright (c) 2017, MapCreator
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 *
 *  Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 *  Neither the name of the copyright holder nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

import PaginatedResourceListing from './PaginatedResourceListing';
import ResourceCache from './ResourceCache';
import {hashObject} from './utils/hash';

/**
 * Used for wrapping {@link PaginatedResourceListing} to make it spa friendly
 * @todo Allow for manual cache updates, ex: a resource has been modified, deleted, created
 */
export default class PaginatedResourceWrapper {
  /**
   *
   * @param {PaginatedResourceListing} listing - Listing result
   * @param {Maps4News} api - Instance of the api
   * @param {Boolean} shareCache - Share cache across instances
   */
  constructor(listing, api = listing.api, shareCache = api.defaults.shareCache) {

    // Fields
    this._api = api;
    this._shareCache = shareCache;
    this._currentPage = 1;
    this._context = [];

    /**
     * Available data assembled from the cache
     * @type {Array<ResourceBase>} - Available data
     */
    this.data = [];

    // Internal
    this._localCache = new ResourceCache(api, this.api.defaults.cacheSeconds);
    this._inflight = [];
    this._last = listing;
    this._waiting = false;

    this.on('invalidate', () => this.rebuild());

    this._promiseCallback(listing);
  }

  get _promiseCallback() {
    return result => {
      const query = this.query();

      this._last = result;
      this._query = query;

      this.cache.push(result);

      const inflightId = this.inflight.findIndex(x => x === result.page);

      if (inflightId >= 0) {
        this._inflight.splice(inflightId, 1);
      }

      this._waiting = this.inflight.length > 0;

      this.rebuild();
    };
  }

  /**
   * Manually fetch a page. This will change the current page.
   * @param {Number|Array<Number>} pageId - Page(s) to fetch
   * @returns {void}
   */
  get(pageId) {
    if (pageId instanceof Array) {
      pageId.map(this.get);
    } else {
      this._waiting = true;

      this._inflight.push(pageId);
      this._last.getPage(pageId).then(this._promiseCallback);
    }
  }

  /**
   * Grab the next page
   * @returns {void}
   */
  next() {
    this.get(++this.currentPage);
  }

  /**
   * Grab the previous page
   * @returns {void}
   */
  previous() {
    this.get(--this.currentPage);
  }

  /**
   * Manually rebuild the data
   * @returns {void}
   */
  rebuild() {
    this.data = this.cache
      .resolve(this.route, this._last.cacheToken)
      .filter(value => typeof value !== 'undefined');

    this.cache.emitter.emit('post-rebuild', {resourceUrl: this._last.route});
  }

  /**
   * Updates the cached pages.
   * @param {Boolean} flush - Clear the cached route data
   * @returns {void}
   * @example
   * function onRefresh() {
   *   if(wrapper.waiting) {
   *     return; // not done yet
   *   }
   *
   *   wrapper.off('post-rebuild', onRefresh);
   *
   *   // Do stuff here
   * }
   *
   * wrapper.on('post-rebuild', onRefresh);
   * wrapper.refresh();
   */
  refresh(flush = false) {
    if (flush) {
      this.cache.clear(this.route);
    }

    this.cache
      .collectPages(this.route, this._last.cacheToken)
      .map(page => this.get(page.page));
  }

  /**
   * Returns the page number that is currently being used as a reference point
   * @returns {Number} - The current page
   * @see {@link PaginatedResourceWrapper#next}
   * @see {@link PaginatedResourceWrapper#previous}
   */
  get currentPage() {
    return this._currentPage;
  }

  /**
   * Set the current page number
   * @param {Number} value - page number
   */
  set currentPage(value) {
    this._currentPage = Math.max(1, value);
  }

  /**
   * Get the route of the resource
   * @returns {String} - route
   */
  get route() {
    return this._last.route;
  }

  /**
   * Override the resource route
   * @param {String} value - route
   */
  set route(value) {
    this._route = value;
  }

  /**
   * Get the number of pages available
   * @returns {Number} - Page count
   */
  get pageCount() {
    return this._last.pageCount;
  }

  /**
   * Set the request params and submit
   * @param {?Object<String, *>} value - Query
   * @throws TypeError
   * @default {}
   * @see {@link ResourceProxy#search}
   * @returns {Object<String, String|Array<String>>} - query
   */
  query(value = null) {
    if (!value || value === this.query()) {
      return this._last.query;
    }

    this._context[this._last.cacheToken] = this._last;

    const token = hashObject(value);

    if (this._context[token]) {
      this._last = this._context[token];
    } else {
      const parameters = this._last.parameters.copy();

      parameters.page = 1;
      parameters.apply(value);

      this._last = new PaginatedResourceListing(this.api, this._last.route, this._last.Target, parameters);
      this.get(parameters.page);
      this.currentPage = 1;
    }

    this.rebuild();

    return this.query();
  }

  /**
   * Get api instance
   * @returns {Maps4News} - Api instance
   */
  get api() {
    return this._api;
  }

  /**
   * Get the active cache instance
   * @returns {ResourceCache} - Cache instance
   */
  get cache() {
    return this.shareCache ? this.api.cache : this._localCache;
  }

  /**
   * Get if the shared cache should be used
   * @returns {Boolean} - Should the shared cache be used
   */
  get shareCache() {
    return this._shareCache;
  }

  /**
   * Sets if the shared cache should be used
   * @param {Boolean} value - Should the shared cache be used
   */
  set shareCache(value) {
    this._shareCache = Boolean(value);
  }

  /**
   * If there is a next page
   * @returns {boolean} - If there is a next page
   */
  get hasNext() {
    return this.inflight.length === 0 ? this._last.hasNext : this.currentPage < this.pageCount;
  }

  /**
   * If there is a previous page
   * @returns {boolean} - If there is a previous page
   */
  get hasPrevious() {
    return this._last.hasPrevious;
  }

  /**
   * List of page numbers that are still mid-flight
   * @returns {Array} - Page numbers that are still mid-flight
   */
  get inflight() {
    return this._inflight;
  }

  /**
   * Returns if there are still requests mid-flight
   * @returns {boolean} - Returns if the wrapper is waiting for requests to finish
   */
  get waiting() {
    return this._waiting;
  }

  /**
   * Register an event handler for the given type.
   *
   * @param {string} type - Type of event to listen for, or `"*"` for all events.
   * @param {function(eventType: string, event: any): void|function(event: any): void} handler - Function to call in response to the given event.
   * @returns {void}
   */
  on(type, handler) {
    this.cache.emitter.on(type, (t, e) => {
      if (type === '*' && e.resourceUrl === this.route) {
        handler(t, e);
      } else if (type !== '*' && t.resourceUrl === this.route) {
        handler(t);
      }
    });
  }

  /**
   * Function to call in response to the given event
   *
   * @param {string} type - Type of event to unregister `handler` from, or `"*"`
   * @param {function(event: any): void} handler - Handler function to remove.
   * @returns {void}
   */
  off(type, handler) {
    this.cache.emitter.off(type, handler);
  }
}
<commit_msg>Fixes #100<commit_after>/*
 * BSD 3-Clause License
 *
 * Copyright (c) 2017, MapCreator
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 *
 *  Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 *  Neither the name of the copyright holder nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

import PaginatedResourceListing from './PaginatedResourceListing';
import ResourceCache from './ResourceCache';
import {hashObject} from './utils/hash';

/**
 * Used for wrapping {@link PaginatedResourceListing} to make it spa friendly
 * @todo Allow for manual cache updates, ex: a resource has been modified, deleted, created
 */
export default class PaginatedResourceWrapper {
  /**
   *
   * @param {PaginatedResourceListing} listing - Listing result
   * @param {Maps4News} api - Instance of the api
   * @param {Boolean} shareCache - Share cache across instances
   */
  constructor(listing, api = listing.api, shareCache = api.defaults.shareCache) {

    // Fields
    this._api = api;
    this._shareCache = shareCache;
    this._currentPage = 1;
    this._context = [];

    /**
     * Available data assembled from the cache
     * @type {Array<ResourceBase>} - Available data
     */
    this.data = [];

    // Internal
    this._localCache = new ResourceCache(api, this.api.defaults.cacheSeconds);
    this._inflight = [];
    this._last = listing;
    this._waiting = false;

    this.on('invalidate', () => this.rebuild());

    this._promiseCallback(listing);
  }

  get _promiseCallback() {
    return result => {
      const query = this.query();

      this._last = result;
      this._query = query;

      this.cache.push(result);

      const inflightId = this.inflight.findIndex(x => x === result.page);

      if (inflightId >= 0) {
        this._inflight.splice(inflightId, 1);
      }

      this._waiting = this.inflight.length > 0;

      this.rebuild();
    };
  }

  /**
   * Manually fetch a page. This will change the current page.
   * @param {Number|Array<Number>} pageId - Page(s) to fetch
   * @returns {void}
   */
  get(pageId) {
    if (pageId instanceof Array) {
      pageId.map(this.get);
    } else {
      this._waiting = true;

      this._inflight.push(pageId);
      this._last.getPage(pageId).then(this._promiseCallback);
    }
  }

  /**
   * Grab the next page
   * @returns {void}
   */
  next() {
    this.get(++this.currentPage);
  }

  /**
   * Grab the previous page
   * @returns {void}
   */
  previous() {
    this.get(--this.currentPage);
  }

  /**
   * Manually rebuild the data
   * @returns {void}
   */
  rebuild() {
    this.data = this.cache
      .resolve(this.route, this._last.cacheToken)
      .filter(value => typeof value !== 'undefined');

    this.cache.emitter.emit('post-rebuild', {resourceUrl: this._last.route});
  }

  /**
   * Updates the cached pages.
   * @param {Boolean} flush - Clear the cached route data
   * @returns {void}
   * @example
   * function onRefresh() {
   *   if(wrapper.waiting) {
   *     return; // not done yet
   *   }
   *
   *   wrapper.off('post-rebuild', onRefresh);
   *
   *   // Do stuff here
   * }
   *
   * wrapper.on('post-rebuild', onRefresh);
   * wrapper.refresh();
   */
  refresh(flush = false) {
    if (flush) {
      this.cache.clear(this.route);
    }

    this.cache
      .collectPages(this.route, this._last.cacheToken)
      .map(page => this.get(page.page));
  }

  /**
   * Returns the page number that is currently being used as a reference point
   * @returns {Number} - The current page
   * @see {@link PaginatedResourceWrapper#next}
   * @see {@link PaginatedResourceWrapper#previous}
   */
  get currentPage() {
    return this._currentPage;
  }

  /**
   * Set the current page number
   * @param {Number} value - page number
   */
  set currentPage(value) {
    this._currentPage = Math.max(1, value);
  }

  /**
   * Get the route of the resource
   * @returns {String} - route
   */
  get route() {
    return this._last.route;
  }

  /**
   * Override the resource route
   * @param {String} value - route
   */
  set route(value) {
    this._route = value;
  }

  /**
   * Row count
   * @returns {Number} - Row count
   */
  get rows() {
    return this._last.rows;
  }

  /**
   * Get the number of pages available
   * @returns {Number} - Page count
   */
  get pageCount() {
    return this._last.pageCount;
  }

  /**
   * Set the request params and submit
   * @param {?Object<String, *>} value - Query
   * @throws TypeError
   * @default {}
   * @see {@link ResourceProxy#search}
   * @returns {Object<String, String|Array<String>>} - query
   */
  query(value = null) {
    if (!value || value === this.query()) {
      return this._last.query;
    }

    this._context[this._last.cacheToken] = this._last;

    const token = hashObject(value);

    if (this._context[token]) {
      this._last = this._context[token];
    } else {
      const parameters = this._last.parameters.copy();

      parameters.page = 1;
      parameters.apply(value);

      this._last = new PaginatedResourceListing(this.api, this._last.route, this._last.Target, parameters);
      this.get(parameters.page);
      this.currentPage = 1;
    }

    this.rebuild();

    return this.query();
  }

  /**
   * Get api instance
   * @returns {Maps4News} - Api instance
   */
  get api() {
    return this._api;
  }

  /**
   * Get the active cache instance
   * @returns {ResourceCache} - Cache instance
   */
  get cache() {
    return this.shareCache ? this.api.cache : this._localCache;
  }

  /**
   * Get if the shared cache should be used
   * @returns {Boolean} - Should the shared cache be used
   */
  get shareCache() {
    return this._shareCache;
  }

  /**
   * Sets if the shared cache should be used
   * @param {Boolean} value - Should the shared cache be used
   */
  set shareCache(value) {
    this._shareCache = Boolean(value);
  }

  /**
   * If there is a next page
   * @returns {boolean} - If there is a next page
   */
  get hasNext() {
    return this.inflight.length === 0 ? this._last.hasNext : this.currentPage < this.pageCount;
  }

  /**
   * If there is a previous page
   * @returns {boolean} - If there is a previous page
   */
  get hasPrevious() {
    return this._last.hasPrevious;
  }

  /**
   * List of page numbers that are still mid-flight
   * @returns {Array} - Page numbers that are still mid-flight
   */
  get inflight() {
    return this._inflight;
  }

  /**
   * Returns if there are still requests mid-flight
   * @returns {boolean} - Returns if the wrapper is waiting for requests to finish
   */
  get waiting() {
    return this._waiting;
  }

  /**
   * Register an event handler for the given type.
   *
   * @param {string} type - Type of event to listen for, or `"*"` for all events.
   * @param {function(eventType: string, event: any): void|function(event: any): void} handler - Function to call in response to the given event.
   * @returns {void}
   */
  on(type, handler) {
    this.cache.emitter.on(type, (t, e) => {
      if (type === '*' && e.resourceUrl === this.route) {
        handler(t, e);
      } else if (type !== '*' && t.resourceUrl === this.route) {
        handler(t);
      }
    });
  }

  /**
   * Function to call in response to the given event
   *
   * @param {string} type - Type of event to unregister `handler` from, or `"*"`
   * @param {function(event: any): void} handler - Handler function to remove.
   * @returns {void}
   */
  off(type, handler) {
    this.cache.emitter.off(type, handler);
  }
}
<|endoftext|>