<commit_before>'use strict';

const _handlers = Symbol('handlers');
const _queue = Symbol('queue');

class Handlers {
  constructor () {
    this.fulfill = null;
    this.reject = null;
  }
}

/**
 * @class promise
 */
class FidelityPromise {
  constructor (state = PENDING, value) {
    this.state = state;
    this.value = value;

    this[_queue] = [];
    this[_handlers] = new Handlers();
  }

  /**
   * Follows the [Promises/A+](https://promisesaplus.com/) spec
   * for a `then` function.
   * @returns a promise
   */
  then (onFulfilled, onRejected) {
    const next = new FidelityPromise();
    if (typeof onFulfilled === 'function') {
      next[_handlers].fulfill = onFulfilled;
    }
    if (typeof onRejected === 'function') {
      next[_handlers].reject = onRejected;
    }
    this[_queue].push(next);
    process(this);
    return next;
  }

  catch (onRejected) {
    return this.then(null, onRejected);
  }
}

const PENDING = 0;
const FULFILLED = 1;
const REJECTED = 2;
const TRUE = new FidelityPromise(FULFILLED, true);
const FALSE = new FidelityPromise(FULFILLED, false);
const NULL = new FidelityPromise(FULFILLED, null);
const UNDEFINED = new FidelityPromise(undefined);
const ZERO = new FidelityPromise(FULFILLED, 0);
const EMPTYSTRING = new FidelityPromise(FULFILLED, '');

/**
 * @module fidelity
*/
module.exports = {
  promise: promise,
  deferred: deferred,
  resolve: resolve,
  PENDING: PENDING,
  FULFILLED: FULFILLED,
  REJECTED: REJECTED
};

/**
 * Creates a promise that will be resolved or rejected at some time
 * in the future.
 * @param {function} fn The function that will do the work of this promise.
 * The function is passed two function arguments, `resolve()` and `reject()`.
 * Call one of these when the work has completed (or failed).
 * @returns {object} A promise object
 * @instance
 */
function promise (fn) {
  const p = new FidelityPromise(PENDING, null);
  if (typeof fn === 'function') {
    try {
      fn((v) => resolvePromise(p, v), (r) => transition(p, REJECTED, r));
    } catch (e) {
      transition(p, REJECTED, e);
    }
  }
  return p;
}

/**
 * Returns a promise that is resolved with `value`.
 * @param {any} value The value to resolve the returned promise with
 * @returns {object} A promise resolved with `value`
 * @method resolve
 * @instance resolve
 */
function resolve (value) {
  if (value && value.then) return value;

  switch (value) {
    case null:
      return NULL;
    case undefined:
      return UNDEFINED;
    case true:
      return TRUE;
    case false:
      return FALSE;
    case 0:
      return ZERO;
    case '':
      return EMPTYSTRING;
  }

  return new FidelityPromise(FULFILLED, value);
}

/**
 * Creates a `deferred` object, containing a promise which may
 * be resolved or rejected at some point in the future.
 * @returns {object} deferred The deferred object
 * @returns {function} deferred.resolve(value) The resolve function
 * @returns {function} deferred.reject(cause) The reject function
 * @returns {object} deferred.promise The inner promise object
 * @instance
 */
function deferred () {
  var resolver;
  var rejecter;
  var p = promise((resolve, reject) => {
    resolver = resolve;
    rejecter = reject;
  });

  function resolve (value) {
    resolver(value);
  }

  function reject (cause) {
    rejecter(cause);
  }

  return {
    promise: p,
    resolve: resolve,
    reject: reject
  };
}

function resolvePromise (p, x) {
  if (x === p) {
    transition(p, REJECTED, new TypeError('The promise and its value are the same.'));
    return;
  }

  if (x && ((typeof x === 'function') || (typeof x === 'object'))) {
    let called = false;
    let thenFunction;
    try {
      thenFunction = x.then;
      if (thenFunction && (typeof thenFunction === 'function')) {
        thenFunction.call(x, (y) => {
          if (!called) {
            resolvePromise(p, y);
            called = true;
          }
        }, (r) => {
          if (!called) {
            transition(p, REJECTED, r);
            called = true;
          }
        });
      } else {
        transition(p, FULFILLED, x);
        called = true;
      }
    } catch (e) {
      if (!called) {
        transition(p, REJECTED, e);
        called = true;
      }
    }
  } else {
    transition(p, FULFILLED, x);
  }
}

function process (p) {
  if (p.state === PENDING) return;
  global.process.nextTick(() => {
    let qp, handler, value;
    while (p[_queue].length) {
      qp = p[_queue].shift();
      if (p.state === FULFILLED) {
        handler = qp[_handlers].fulfill || ((v) => v);
      } else if (p.state === REJECTED) {
        handler = qp[_handlers].reject || ((r) => {
          throw r;
        });
      }
      try {
        value = handler(p.value);
      } catch (e) {
        transition(qp, REJECTED, e);
        continue;
      }
      resolvePromise(qp, value);
    }
  });
  return p;
}

function transition (p, state, value) {
  if (p.state === state ||
    p.state !== PENDING ||
    arguments.length !== 3) return;
  p.state = state;
  p.value = value;
  return process(p);
}
<commit_msg>If we want Node 4, we cannot use default parameters<commit_after>'use strict';

const _handlers = Symbol('handlers');
const _queue = Symbol('queue');

class Handlers {
  constructor () {
    this.fulfill = null;
    this.reject = null;
  }
}

/**
 * @class promise
 */
class FidelityPromise {
  constructor (state, value) {
    this.state = state || PENDING;
    this.value = value;

    this[_queue] = [];
    this[_handlers] = new Handlers();
  }

  /**
   * Follows the [Promises/A+](https://promisesaplus.com/) spec
   * for a `then` function.
   * @returns a promise
   */
  then (onFulfilled, onRejected) {
    const next = new FidelityPromise();
    if (typeof onFulfilled === 'function') {
      next[_handlers].fulfill = onFulfilled;
    }
    if (typeof onRejected === 'function') {
      next[_handlers].reject = onRejected;
    }
    this[_queue].push(next);
    process(this);
    return next;
  }

  catch (onRejected) {
    return this.then(null, onRejected);
  }
}

const PENDING = 0;
const FULFILLED = 1;
const REJECTED = 2;
const TRUE = new FidelityPromise(FULFILLED, true);
const FALSE = new FidelityPromise(FULFILLED, false);
const NULL = new FidelityPromise(FULFILLED, null);
const UNDEFINED = new FidelityPromise(undefined);
const ZERO = new FidelityPromise(FULFILLED, 0);
const EMPTYSTRING = new FidelityPromise(FULFILLED, '');

/**
 * @module fidelity
*/
module.exports = {
  promise: promise,
  deferred: deferred,
  resolve: resolve,
  PENDING: PENDING,
  FULFILLED: FULFILLED,
  REJECTED: REJECTED
};

/**
 * Creates a promise that will be resolved or rejected at some time
 * in the future.
 * @param {function} fn The function that will do the work of this promise.
 * The function is passed two function arguments, `resolve()` and `reject()`.
 * Call one of these when the work has completed (or failed).
 * @returns {object} A promise object
 * @instance
 */
function promise (fn) {
  const p = new FidelityPromise(PENDING, null);
  if (typeof fn === 'function') {
    try {
      fn((v) => resolvePromise(p, v), (r) => transition(p, REJECTED, r));
    } catch (e) {
      transition(p, REJECTED, e);
    }
  }
  return p;
}

/**
 * Returns a promise that is resolved with `value`.
 * @param {any} value The value to resolve the returned promise with
 * @returns {object} A promise resolved with `value`
 * @method resolve
 * @instance resolve
 */
function resolve (value) {
  if (value && value.then) return value;

  switch (value) {
    case null:
      return NULL;
    case undefined:
      return UNDEFINED;
    case true:
      return TRUE;
    case false:
      return FALSE;
    case 0:
      return ZERO;
    case '':
      return EMPTYSTRING;
  }

  return new FidelityPromise(FULFILLED, value);
}

/**
 * Creates a `deferred` object, containing a promise which may
 * be resolved or rejected at some point in the future.
 * @returns {object} deferred The deferred object
 * @returns {function} deferred.resolve(value) The resolve function
 * @returns {function} deferred.reject(cause) The reject function
 * @returns {object} deferred.promise The inner promise object
 * @instance
 */
function deferred () {
  var resolver;
  var rejecter;
  var p = promise((resolve, reject) => {
    resolver = resolve;
    rejecter = reject;
  });

  function resolve (value) {
    resolver(value);
  }

  function reject (cause) {
    rejecter(cause);
  }

  return {
    promise: p,
    resolve: resolve,
    reject: reject
  };
}

function resolvePromise (p, x) {
  if (x === p) {
    transition(p, REJECTED, new TypeError('The promise and its value are the same.'));
    return;
  }

  if (x && ((typeof x === 'function') || (typeof x === 'object'))) {
    let called = false;
    let thenFunction;
    try {
      thenFunction = x.then;
      if (thenFunction && (typeof thenFunction === 'function')) {
        thenFunction.call(x, (y) => {
          if (!called) {
            resolvePromise(p, y);
            called = true;
          }
        }, (r) => {
          if (!called) {
            transition(p, REJECTED, r);
            called = true;
          }
        });
      } else {
        transition(p, FULFILLED, x);
        called = true;
      }
    } catch (e) {
      if (!called) {
        transition(p, REJECTED, e);
        called = true;
      }
    }
  } else {
    transition(p, FULFILLED, x);
  }
}

function process (p) {
  if (p.state === PENDING) return;
  global.process.nextTick(() => {
    let qp, handler, value;
    while (p[_queue].length) {
      qp = p[_queue].shift();
      if (p.state === FULFILLED) {
        handler = qp[_handlers].fulfill || ((v) => v);
      } else if (p.state === REJECTED) {
        handler = qp[_handlers].reject || ((r) => {
          throw r;
        });
      }
      try {
        value = handler(p.value);
      } catch (e) {
        transition(qp, REJECTED, e);
        continue;
      }
      resolvePromise(qp, value);
    }
  });
  return p;
}

function transition (p, state, value) {
  if (p.state === state ||
    p.state !== PENDING ||
    arguments.length !== 3) return;
  p.state = state;
  p.value = value;
  return process(p);
}
<|endoftext|>


<commit_before>'use strict';
const Hoek = require('hoek');
const Joi = require('joi');
const fastSafeStringify = require('fast-safe-stringify');

const Parameters = require('../lib/parameters');
const Definitions = require('../lib/definitions');
const Properties = require('../lib/properties');
const Responses = require('../lib/responses');
const Utilities = require('../lib/utilities');

const internals = {};

exports = module.exports = internals.paths = function (settings) {

    this.settings = settings;
    this.definitions = new Definitions(settings);
    this.properties = new Properties(settings, {}, {});
    this.responses = new Responses(settings, {}, {});

    this.defaults = {
        responses: {}
    };

    this.schema = Joi.object({
        tags: Joi.array().items(Joi.string()),
        summary: Joi.string(),
        description: Joi.string(),
        externalDocs: Joi.object({
            description: Joi.string(),
            url: Joi.string().uri()
        }),
        operationId: Joi.string(),
        consumes: Joi.array().items(Joi.string()),
        produces: Joi.array().items(Joi.string()),
        parameters: Joi.array().items(Joi.object()),
        responses: Joi.object().required(),
        schemes: Joi.array().items(Joi.string().valid(['http', 'https', 'ws', 'wss'])),
        deprecated: Joi.boolean(),
        security: Joi.array().items(Joi.object())
    });
};

/**
 * build the swagger path section
 *
 * @param  {Object} setting
 * @param  {Object} routes
 * @return {Object}
 */
internals.paths.prototype.build = function (routes) {

    let self = this;

    let routesData = [];

    // loop each route
    routes.forEach((route) => {

        let routeOptions = Hoek.reach(route, 'settings.plugins.hapi-swagger') || {};
        let routeData = {
            path: route.path,
            method: route.method.toUpperCase(),
            description: route.settings.description,
            notes: route.settings.notes,
            tags: Hoek.reach(route, 'settings.tags'),
            queryParams: Hoek.reach(route, 'settings.validate.query'),
            pathParams: Hoek.reach(route, 'settings.validate.params'),
            payloadParams: Hoek.reach(route, 'settings.validate.payload'),
            responseSchema: Hoek.reach(route, 'settings.response.schema'),
            responseStatus: Hoek.reach(route, 'settings.response.status'),
            headerParams: Hoek.reach(route, 'settings.validate.headers'),
            consumes: Hoek.reach(routeOptions, 'consumes') || null,
            produces: Hoek.reach(routeOptions, 'produces') || null,
            responses: Hoek.reach(routeOptions, 'responses') || null,
            payloadType: Hoek.reach(routeOptions, 'payloadType') || null,
            security: Hoek.reach(routeOptions, 'security') || null,
            order: Hoek.reach(routeOptions, 'order') || null,
            deprecated: Hoek.reach(routeOptions, 'deprecated') || null,
            id: Hoek.reach(routeOptions, 'id') || null,
            groups: route.group,
            'x-meta': Hoek.reach(routeOptions, 'x-meta') || null
        };

        routeData.path = Utilities.replaceInPath(routeData.path, ['endpoints'], this.settings.pathReplacements);

        // user configured interface through route plugin options
        if (Hoek.reach(routeOptions, 'validate.query')) {
            routeData.queryParams = Utilities.toJoiObject(Hoek.reach(routeOptions, 'validate.query'));
        }
        if (Hoek.reach(routeOptions, 'validate.params')) {
            routeData.pathParams = Utilities.toJoiObject(Hoek.reach(routeOptions, 'validate.params'));
        }
        if (Hoek.reach(routeOptions, 'validate.headers')) {
            routeData.headerParams = Utilities.toJoiObject(Hoek.reach(routeOptions, 'validate.headers'));
        }
        if (Hoek.reach(routeOptions, 'validate.payload')) {
            // has different structure, just pass straight through
            routeData.payloadParams = Hoek.reach(routeOptions, 'validate.payload');
            // if its a native javascript object convert it to JOI
            if (!routeData.payloadParams.isJoi) {
                routeData.payloadParams = Joi.object(routeData.payloadParams);
            }
        }

        // swap out any custom validation function for Joi object/string
        [
            'queryParams',
            'pathParams',
            'headerParams',
            'payloadParams'
        ].forEach(function (property) {

            // swap out any custom validation function for Joi object/string
            if (Utilities.isFunction(routeData[property])) {
                if (property !== 'pathParams') {
                    self.settings.log(['vaildation', 'warning'], 'Using a Joi.function for a query, header or payload is not supported.');
                    if (property === 'payloadParams') {
                        routeData[property] = Joi.object().label('Hidden Model');
                    } else {
                        routeData[property] = Joi.object({ 'Hidden Model': Joi.string() });
                    }
                } else {
                    self.settings.log(['vaildation', 'error'], 'Using a Joi.function for a params is not supported and has been removed.');
                    routeData[property] = null;
                }
            }
        });

        // hapi wildcard method support
        if (routeData.method === '*') {
            // OPTIONS not supported by Swagger and HEAD not support by HAPI
            ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'].forEach((method) => {

                var newRoute = Hoek.clone(routeData);
                newRoute.method = method.toUpperCase();
                routesData.push(newRoute);
            });
        } else {
            routesData.push(routeData);
        }
    });

    return this.buildRoutes(routesData);
};

/**
 * build the swagger path section from hapi routes data
 *
 * @param  {Object} setting
 * @param  {Object} routes
 * @return {Object}
 */
internals.paths.prototype.buildRoutes = function (routes) {

    let self = this;
    let pathObj = {};
    let swagger = {
        'definitions': {},
        'x-alt-definitions': {}
    };
    let definitionCache = [
        new WeakMap(),
        new WeakMap()
    ];

    // reset properties
    this.properties = new Properties(this.settings, swagger.definitions, swagger['x-alt-definitions'], definitionCache);
    this.responses = new Responses(this.settings, swagger.definitions, swagger['x-alt-definitions'], definitionCache);

    routes.forEach((route) => {

        let method = route.method;
        let path = internals.removeBasePath(route.path, this.settings.basePath, this.settings.pathReplacements);
        let out = {
            'summary': route.description,
            'operationId': route.id || Utilities.createId(route.method, path),
            'description': route.notes,
            'parameters': [],
            'consumes': [],
            'produces': []
        };

        // tags in swagger are used for grouping
        if (this.settings.grouping === 'tags') {
            out.tags = route.tags.filter(this.settings.tagsGroupingFilter);
        }
        else {
            out.tags = route.groups;
        }

        out.description = Array.isArray(route.notes) ? route.notes.join('

') : route.notes;

        if (route.security) {
            out.security = route.security;
        }

        // set from plugin options or from route options
        let payloadType = internals.overload(this.settings.payloadType, route.payloadType);

        // build payload either with JSON or form input
        let payloadStructures = this.getDefaultStructures();
        let payloadJoi = internals.getJOIObj(route, 'payloadParams');
        if (payloadType.toLowerCase() === 'json') {
            // set as json
            payloadStructures = this.getSwaggerStructures(payloadJoi, 'body', true, false);
        } else {
            // set as formData
            if (Utilities.hasJoiChildren(payloadJoi)) {
                payloadStructures = this.getSwaggerStructures(payloadJoi, 'formData', false, false);
            } else {
                self.testParameterError(payloadJoi, 'payload form-urlencoded', path);
            }
            // add form data mimetype
            out.consumes = ['application/x-www-form-urlencoded'];
        }

        // change form mimetype based on meta property 'swaggerType'
        if (internals.hasFileType(route)) {
            out.consumes = ['multipart/form-data'];
        }

        // add user defined over automaticlly discovered
        if (this.settings.consumes || route.consumes) {
            out.consumes = internals.overload(this.settings.consumes, route.consumes);
        }

        if (this.settings.produces || route.produces) {
            out.produces = internals.overload(this.settings.produces, route.produces);
        }

        // set required true/false for each path params
        //var pathParams = this.properties.toParameters (internals.getJOIObj(route, 'pathParams'), 'path', false);
        let pathStructures = this.getDefaultStructures();
        let pathJoi = internals.getJOIObj(route, 'pathParams');
        if (Utilities.hasJoiChildren(pathJoi)) {
            pathStructures = this.getSwaggerStructures(pathJoi, 'path', false, false);
            pathStructures.parameters.forEach(function (item) {

                // add required based on path pattern {prama} and {prama?}
                if (item.required === undefined) {
                    if (path.indexOf('{' + item.name + '}') > -1) {
                        item.required = true;
                    }
                    if (path.indexOf('{' + item.name + '?}') > -1) {
                        delete item.required;
                    }
                }
                if (item.required === false) {
                    delete item.required;
                }
                if (!item.required) {
                    self.settings.log(['validation', 'warning'], 'The ' + path + ' params parameter {' + item.name + '} is set as optional. This will work in the UI, but is invalid in the swagger spec');
                }

            });
        } else {
            self.testParameterError(pathJoi, 'params', path);
        }

        // removes ? from {prama?} after we have set required/optional for path params
        path = internals.cleanPathParameters(path);

        //let headerParams = this.properties.toParameters (internals.getJOIObj(route, 'headerParams'), 'header', false);
        let headerStructures = this.getDefaultStructures();
        let headerJoi = internals.getJOIObj(route, 'headerParams');
        if (Utilities.hasJoiChildren(headerJoi)) {
            headerStructures = this.getSwaggerStructures(headerJoi, 'header', false, false);
        } else {
            self.testParameterError(headerJoi, 'headers', path);
        }
        // if the API has a user set accept header with a enum convert into the produces array
        if (this.settings.acceptToProduce === true) {
            headerStructures.parameters = headerStructures.parameters.filter(function (header) {

                if (header.name.toLowerCase() === 'accept') {
                    if (header.enum) {
                        out.produces = Utilities.sortFirstItem(header.enum, header.default);
                        return false;
                    }
                }
                return true;
            });
        }

        let queryStructures = this.getDefaultStructures();
        let queryJoi = internals.getJOIObj(route, 'queryParams');
        if (Utilities.hasJoiChildren(queryJoi)) {
            queryStructures = this.getSwaggerStructures(queryJoi, 'query', false, false);
        } else {
            self.testParameterError(queryJoi, 'query', path);
        }

        out.parameters = out.parameters.concat(
            headerStructures.parameters,
            pathStructures.parameters,
            queryStructures.parameters,
            payloadStructures.parameters
        );

        // if the api sets the content-type header pramater use that
        if (internals.hasContentTypeHeader(out)) {
            delete out.consumes;
        }

        //let name = out.operationId + method;
        //userDefindedSchemas, defaultSchema, statusSchemas, useDefinitions, isAlt
        out.responses = this.responses.build(
            route.responses,
            route.responseSchema,
            route.responseStatus,
            true,
            false
        );

        if (route.order) {
            out['x-order'] = route.order;
        }
        if (route['x-meta']) {
            out['x-meta'] = route['x-meta'];
        }
        if (route.deprecated !== null) {
            out.deprecated = route.deprecated;
        }

        if (!pathObj[path]) {
            pathObj[path] = {};
        }
        pathObj[path][method.toLowerCase()] = Utilities.deleteEmptyProperties(out);
    });

    swagger.paths = pathObj;
    return swagger;
};

/**
 * gets the JOI object from route object
 *
 * @param  {Object} base
 * @param  {String} name
 * @return {Object}
 */
internals.getJOIObj = function (route, name) {

    let prama = route[name];
    //  if (Utilities.hasJoiChildren(route[name])) {
    //      prama = route[name]._inner.children;
    //  }
    return prama;
};

/**
 * overload one object with another
 *
 * @param  {Object} base
 * @param  {Object} priority
 * @return {Object}
 */
internals.overload = function (base, priority) {

    return (priority) ? priority : base;
};

/**
 * does route have property swaggerType of file
 *
 * @param  {Object} route
 * @return {Boolean}
 */
internals.hasFileType = function (route) {

    let routeString = fastSafeStringify(route);
    return routeString.indexOf('swaggerType') > -1;
};

/**
 * clear path parameters of optional char flag
 *
 * @param  {String} path
 * @return {String}
 */
internals.cleanPathParameters = function (path) {

    return path.replace('?}', '}');
};

/**
 * remove the base path from endpoint
 *
 * @param  {String} path
 * @param  {String} basePath
 * @param  {Array} pathReplacements
 * @return {String}
 */
internals.removeBasePath = function (path, basePath, pathReplacements) {

    if (basePath !== '/' && Utilities.startsWith(path, basePath)) {
        path = path.replace(basePath, '');
        path = Utilities.replaceInPath(path, ['endpoints'], pathReplacements);
    }
    return path;
};

/**
 * does path parameters have a content-type header
 *
 * @param  {String} path
 * @return {boolean}
 */
internals.hasContentTypeHeader = function (path) {

    let out = false;
    path.parameters.forEach(function (prama) {

        if (prama.in === 'header' && prama.name.toLowerCase() === 'content-type') {
            out = true;
        }
    });
    return out;
};

/**
 * builds an object containing different swagger structures that can be use to represent one object
 *
 * @param  {Object} joiObj
 * @param  {String} parameterType
 * @param  {Boolean} useDefinitions
 * @param  {Boolean} isAlt
 * @return {Object}
 */
internals.paths.prototype.getSwaggerStructures = function (joiObj, parameterType, useDefinitions, isAlt) {

    let outProperties;
    let outParameters;

    if (joiObj) {
        // name, joiObj, parent, parameterType, useDefinitions, isAlt
        outProperties = this.properties.parseProperty(null, joiObj, null, parameterType, useDefinitions, isAlt);
        outParameters = Parameters.fromProperties(outProperties, parameterType);
    }
    let out = {
        properties: outProperties || {},
        parameters: outParameters || []
    };
    return out;
};

internals.paths.prototype.getDefaultStructures = function () {

    return {
        'properties': {},
        'parameters': []
    };
};

internals.paths.prototype.testParameterError  = function (joiObj, parameterType, path) {

    if (joiObj && !Utilities.hasJoiChildren(joiObj)) {
        this.settings.log(['validation', 'error'], 'The ' + path  + ' route ' + parameterType + ' parameter was set, but not as a Joi.object() with child properties');
    }
};
<commit_msg>fix 'vaildation' typoes<commit_after>'use strict';
const Hoek = require('hoek');
const Joi = require('joi');
const fastSafeStringify = require('fast-safe-stringify');

const Parameters = require('../lib/parameters');
const Definitions = require('../lib/definitions');
const Properties = require('../lib/properties');
const Responses = require('../lib/responses');
const Utilities = require('../lib/utilities');

const internals = {};

exports = module.exports = internals.paths = function (settings) {

    this.settings = settings;
    this.definitions = new Definitions(settings);
    this.properties = new Properties(settings, {}, {});
    this.responses = new Responses(settings, {}, {});

    this.defaults = {
        responses: {}
    };

    this.schema = Joi.object({
        tags: Joi.array().items(Joi.string()),
        summary: Joi.string(),
        description: Joi.string(),
        externalDocs: Joi.object({
            description: Joi.string(),
            url: Joi.string().uri()
        }),
        operationId: Joi.string(),
        consumes: Joi.array().items(Joi.string()),
        produces: Joi.array().items(Joi.string()),
        parameters: Joi.array().items(Joi.object()),
        responses: Joi.object().required(),
        schemes: Joi.array().items(Joi.string().valid(['http', 'https', 'ws', 'wss'])),
        deprecated: Joi.boolean(),
        security: Joi.array().items(Joi.object())
    });
};

/**
 * build the swagger path section
 *
 * @param  {Object} setting
 * @param  {Object} routes
 * @return {Object}
 */
internals.paths.prototype.build = function (routes) {

    let self = this;

    let routesData = [];

    // loop each route
    routes.forEach((route) => {

        let routeOptions = Hoek.reach(route, 'settings.plugins.hapi-swagger') || {};
        let routeData = {
            path: route.path,
            method: route.method.toUpperCase(),
            description: route.settings.description,
            notes: route.settings.notes,
            tags: Hoek.reach(route, 'settings.tags'),
            queryParams: Hoek.reach(route, 'settings.validate.query'),
            pathParams: Hoek.reach(route, 'settings.validate.params'),
            payloadParams: Hoek.reach(route, 'settings.validate.payload'),
            responseSchema: Hoek.reach(route, 'settings.response.schema'),
            responseStatus: Hoek.reach(route, 'settings.response.status'),
            headerParams: Hoek.reach(route, 'settings.validate.headers'),
            consumes: Hoek.reach(routeOptions, 'consumes') || null,
            produces: Hoek.reach(routeOptions, 'produces') || null,
            responses: Hoek.reach(routeOptions, 'responses') || null,
            payloadType: Hoek.reach(routeOptions, 'payloadType') || null,
            security: Hoek.reach(routeOptions, 'security') || null,
            order: Hoek.reach(routeOptions, 'order') || null,
            deprecated: Hoek.reach(routeOptions, 'deprecated') || null,
            id: Hoek.reach(routeOptions, 'id') || null,
            groups: route.group,
            'x-meta': Hoek.reach(routeOptions, 'x-meta') || null
        };

        routeData.path = Utilities.replaceInPath(routeData.path, ['endpoints'], this.settings.pathReplacements);

        // user configured interface through route plugin options
        if (Hoek.reach(routeOptions, 'validate.query')) {
            routeData.queryParams = Utilities.toJoiObject(Hoek.reach(routeOptions, 'validate.query'));
        }
        if (Hoek.reach(routeOptions, 'validate.params')) {
            routeData.pathParams = Utilities.toJoiObject(Hoek.reach(routeOptions, 'validate.params'));
        }
        if (Hoek.reach(routeOptions, 'validate.headers')) {
            routeData.headerParams = Utilities.toJoiObject(Hoek.reach(routeOptions, 'validate.headers'));
        }
        if (Hoek.reach(routeOptions, 'validate.payload')) {
            // has different structure, just pass straight through
            routeData.payloadParams = Hoek.reach(routeOptions, 'validate.payload');
            // if its a native javascript object convert it to JOI
            if (!routeData.payloadParams.isJoi) {
                routeData.payloadParams = Joi.object(routeData.payloadParams);
            }
        }

        // swap out any custom validation function for Joi object/string
        [
            'queryParams',
            'pathParams',
            'headerParams',
            'payloadParams'
        ].forEach(function (property) {

            // swap out any custom validation function for Joi object/string
            if (Utilities.isFunction(routeData[property])) {
                if (property !== 'pathParams') {
                    self.settings.log(['validation', 'warning'], 'Using a Joi.function for a query, header or payload is not supported.');
                    if (property === 'payloadParams') {
                        routeData[property] = Joi.object().label('Hidden Model');
                    } else {
                        routeData[property] = Joi.object({ 'Hidden Model': Joi.string() });
                    }
                } else {
                    self.settings.log(['validation', 'error'], 'Using a Joi.function for a params is not supported and has been removed.');
                    routeData[property] = null;
                }
            }
        });

        // hapi wildcard method support
        if (routeData.method === '*') {
            // OPTIONS not supported by Swagger and HEAD not support by HAPI
            ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'].forEach((method) => {

                var newRoute = Hoek.clone(routeData);
                newRoute.method = method.toUpperCase();
                routesData.push(newRoute);
            });
        } else {
            routesData.push(routeData);
        }
    });

    return this.buildRoutes(routesData);
};

/**
 * build the swagger path section from hapi routes data
 *
 * @param  {Object} setting
 * @param  {Object} routes
 * @return {Object}
 */
internals.paths.prototype.buildRoutes = function (routes) {

    let self = this;
    let pathObj = {};
    let swagger = {
        'definitions': {},
        'x-alt-definitions': {}
    };
    let definitionCache = [
        new WeakMap(),
        new WeakMap()
    ];

    // reset properties
    this.properties = new Properties(this.settings, swagger.definitions, swagger['x-alt-definitions'], definitionCache);
    this.responses = new Responses(this.settings, swagger.definitions, swagger['x-alt-definitions'], definitionCache);

    routes.forEach((route) => {

        let method = route.method;
        let path = internals.removeBasePath(route.path, this.settings.basePath, this.settings.pathReplacements);
        let out = {
            'summary': route.description,
            'operationId': route.id || Utilities.createId(route.method, path),
            'description': route.notes,
            'parameters': [],
            'consumes': [],
            'produces': []
        };

        // tags in swagger are used for grouping
        if (this.settings.grouping === 'tags') {
            out.tags = route.tags.filter(this.settings.tagsGroupingFilter);
        }
        else {
            out.tags = route.groups;
        }

        out.description = Array.isArray(route.notes) ? route.notes.join('

') : route.notes;

        if (route.security) {
            out.security = route.security;
        }

        // set from plugin options or from route options
        let payloadType = internals.overload(this.settings.payloadType, route.payloadType);

        // build payload either with JSON or form input
        let payloadStructures = this.getDefaultStructures();
        let payloadJoi = internals.getJOIObj(route, 'payloadParams');
        if (payloadType.toLowerCase() === 'json') {
            // set as json
            payloadStructures = this.getSwaggerStructures(payloadJoi, 'body', true, false);
        } else {
            // set as formData
            if (Utilities.hasJoiChildren(payloadJoi)) {
                payloadStructures = this.getSwaggerStructures(payloadJoi, 'formData', false, false);
            } else {
                self.testParameterError(payloadJoi, 'payload form-urlencoded', path);
            }
            // add form data mimetype
            out.consumes = ['application/x-www-form-urlencoded'];
        }

        // change form mimetype based on meta property 'swaggerType'
        if (internals.hasFileType(route)) {
            out.consumes = ['multipart/form-data'];
        }

        // add user defined over automaticlly discovered
        if (this.settings.consumes || route.consumes) {
            out.consumes = internals.overload(this.settings.consumes, route.consumes);
        }

        if (this.settings.produces || route.produces) {
            out.produces = internals.overload(this.settings.produces, route.produces);
        }

        // set required true/false for each path params
        //var pathParams = this.properties.toParameters (internals.getJOIObj(route, 'pathParams'), 'path', false);
        let pathStructures = this.getDefaultStructures();
        let pathJoi = internals.getJOIObj(route, 'pathParams');
        if (Utilities.hasJoiChildren(pathJoi)) {
            pathStructures = this.getSwaggerStructures(pathJoi, 'path', false, false);
            pathStructures.parameters.forEach(function (item) {

                // add required based on path pattern {prama} and {prama?}
                if (item.required === undefined) {
                    if (path.indexOf('{' + item.name + '}') > -1) {
                        item.required = true;
                    }
                    if (path.indexOf('{' + item.name + '?}') > -1) {
                        delete item.required;
                    }
                }
                if (item.required === false) {
                    delete item.required;
                }
                if (!item.required) {
                    self.settings.log(['validation', 'warning'], 'The ' + path + ' params parameter {' + item.name + '} is set as optional. This will work in the UI, but is invalid in the swagger spec');
                }

            });
        } else {
            self.testParameterError(pathJoi, 'params', path);
        }

        // removes ? from {prama?} after we have set required/optional for path params
        path = internals.cleanPathParameters(path);

        //let headerParams = this.properties.toParameters (internals.getJOIObj(route, 'headerParams'), 'header', false);
        let headerStructures = this.getDefaultStructures();
        let headerJoi = internals.getJOIObj(route, 'headerParams');
        if (Utilities.hasJoiChildren(headerJoi)) {
            headerStructures = this.getSwaggerStructures(headerJoi, 'header', false, false);
        } else {
            self.testParameterError(headerJoi, 'headers', path);
        }
        // if the API has a user set accept header with a enum convert into the produces array
        if (this.settings.acceptToProduce === true) {
            headerStructures.parameters = headerStructures.parameters.filter(function (header) {

                if (header.name.toLowerCase() === 'accept') {
                    if (header.enum) {
                        out.produces = Utilities.sortFirstItem(header.enum, header.default);
                        return false;
                    }
                }
                return true;
            });
        }

        let queryStructures = this.getDefaultStructures();
        let queryJoi = internals.getJOIObj(route, 'queryParams');
        if (Utilities.hasJoiChildren(queryJoi)) {
            queryStructures = this.getSwaggerStructures(queryJoi, 'query', false, false);
        } else {
            self.testParameterError(queryJoi, 'query', path);
        }

        out.parameters = out.parameters.concat(
            headerStructures.parameters,
            pathStructures.parameters,
            queryStructures.parameters,
            payloadStructures.parameters
        );

        // if the api sets the content-type header pramater use that
        if (internals.hasContentTypeHeader(out)) {
            delete out.consumes;
        }

        //let name = out.operationId + method;
        //userDefindedSchemas, defaultSchema, statusSchemas, useDefinitions, isAlt
        out.responses = this.responses.build(
            route.responses,
            route.responseSchema,
            route.responseStatus,
            true,
            false
        );

        if (route.order) {
            out['x-order'] = route.order;
        }
        if (route['x-meta']) {
            out['x-meta'] = route['x-meta'];
        }
        if (route.deprecated !== null) {
            out.deprecated = route.deprecated;
        }

        if (!pathObj[path]) {
            pathObj[path] = {};
        }
        pathObj[path][method.toLowerCase()] = Utilities.deleteEmptyProperties(out);
    });

    swagger.paths = pathObj;
    return swagger;
};

/**
 * gets the JOI object from route object
 *
 * @param  {Object} base
 * @param  {String} name
 * @return {Object}
 */
internals.getJOIObj = function (route, name) {

    let prama = route[name];
    //  if (Utilities.hasJoiChildren(route[name])) {
    //      prama = route[name]._inner.children;
    //  }
    return prama;
};

/**
 * overload one object with another
 *
 * @param  {Object} base
 * @param  {Object} priority
 * @return {Object}
 */
internals.overload = function (base, priority) {

    return (priority) ? priority : base;
};

/**
 * does route have property swaggerType of file
 *
 * @param  {Object} route
 * @return {Boolean}
 */
internals.hasFileType = function (route) {

    let routeString = fastSafeStringify(route);
    return routeString.indexOf('swaggerType') > -1;
};

/**
 * clear path parameters of optional char flag
 *
 * @param  {String} path
 * @return {String}
 */
internals.cleanPathParameters = function (path) {

    return path.replace('?}', '}');
};

/**
 * remove the base path from endpoint
 *
 * @param  {String} path
 * @param  {String} basePath
 * @param  {Array} pathReplacements
 * @return {String}
 */
internals.removeBasePath = function (path, basePath, pathReplacements) {

    if (basePath !== '/' && Utilities.startsWith(path, basePath)) {
        path = path.replace(basePath, '');
        path = Utilities.replaceInPath(path, ['endpoints'], pathReplacements);
    }
    return path;
};

/**
 * does path parameters have a content-type header
 *
 * @param  {String} path
 * @return {boolean}
 */
internals.hasContentTypeHeader = function (path) {

    let out = false;
    path.parameters.forEach(function (prama) {

        if (prama.in === 'header' && prama.name.toLowerCase() === 'content-type') {
            out = true;
        }
    });
    return out;
};

/**
 * builds an object containing different swagger structures that can be use to represent one object
 *
 * @param  {Object} joiObj
 * @param  {String} parameterType
 * @param  {Boolean} useDefinitions
 * @param  {Boolean} isAlt
 * @return {Object}
 */
internals.paths.prototype.getSwaggerStructures = function (joiObj, parameterType, useDefinitions, isAlt) {

    let outProperties;
    let outParameters;

    if (joiObj) {
        // name, joiObj, parent, parameterType, useDefinitions, isAlt
        outProperties = this.properties.parseProperty(null, joiObj, null, parameterType, useDefinitions, isAlt);
        outParameters = Parameters.fromProperties(outProperties, parameterType);
    }
    let out = {
        properties: outProperties || {},
        parameters: outParameters || []
    };
    return out;
};

internals.paths.prototype.getDefaultStructures = function () {

    return {
        'properties': {},
        'parameters': []
    };
};

internals.paths.prototype.testParameterError  = function (joiObj, parameterType, path) {

    if (joiObj && !Utilities.hasJoiChildren(joiObj)) {
        this.settings.log(['validation', 'error'], 'The ' + path  + ' route ' + parameterType + ' parameter was set, but not as a Joi.object() with child properties');
    }
};
<|endoftext|>


<commit_before>'use strict';

(function(scope) {
  /**
   * @param {*} value
   * @this {Attr}
   */
  function bindAttribute(value) {
    this.value = /** @type {string} */ (value);
  }

  /**
   * @param {*} value
   * @this {Node}
   */
  function bindTextContent(value) {
    this.textContent = /** @type {string} */ (value);
  }

  /**
   * @param {!HTMLTemplateElement} template
   * @param {!Node} placeholder
   * @param {!Set<!JSBindTemplateBuilder>} live
   * @return {function(*, string)}
   */
  function buildEach(template, placeholder, live) {
    const curr = new Map();
    const each = template.getAttribute('each');

    function add(value, key, rest) {
      let config = curr.get(key);
      if (!config) {
        const binding = new JSBindTemplateBuilder();
        const frag = template.content.cloneNode(true);
        convertNode(frag, binding, live);

        config = {
          binding,
          outer: frag,
          nodes: [...frag.childNodes],  // set before insertion and children disappearing
        };
        curr.set(key, config);
        placeholder.parentNode.insertBefore(frag, placeholder);

        live.add(binding);
      }

      const flatKey = '$' + (rest !== undefined ? '.' + rest : '');
      config.binding.update(flatKey, value);
    }

    function remove(key) {
      const config = curr.get(key);
      if (config) {
        curr.delete(key);
        live.delete(config.binding);
        config.binding.update('$', null);  // tell children to disappear
        config.nodes.forEach(node => node.remove());
      }
    }

    // TODO: make this into a class helper?
    return (value, key, rest) => {
      if (key === undefined) {
        if (rest !== undefined) {
          throw new TypeError('can\'t pass rest with each replace');
        }

        // This is a set of the each key directly, presumably with a new Map/Array etc.
        // TODO(samthor): Don't nuke/recreate, try to maintain parity. Of course values might be
        // different but if the keys are the same, just "change" instead of delete.
        for (const key of curr.keys()) {
          remove(key);
        }
        if (curr.size) {
          throw new Error('curr should now be empty');
        }
        forEach(value, (value, key) => add(value, key, undefined));  // rest must be undefined
      } else if (value === null && rest === undefined) {
        // Delete a specific key, e.g. "each.0" => null.
        remove(key);
      } else {
        // Create a specific key. Note that `undefined` is valid here, and creates without set.
        // Rest is passed to allow implicit creation/update of some subpath.
        add(value, key, rest);
      }
    };
  }

  /**
   * Fetches and clears the bound attributes from the passed Element.
   *
   * @param {!Element} el to process
   * @return {!Object<string>} map of bound attribute to literal bound value
   */
  function fetchBoundAttributes(el) {
    // TODO: looks for attr$="bound", but Polymer uses attr$="{{bound}}" syntax
    const found = {};
    for (let i = 0, curr; curr = el.attributes[i]; ++i) {
      if (curr.name.substring(curr.name.length - 1) === '$') {
        const attr = curr.name.substr(0, curr.name.length - 1);
        found[attr] = curr.value;
      }
    }
    for (const attr in found) {
      el.removeAttribute(attr + '$');
      el.setAttribute(attr, '');
    }
    return found;
  }

  /**
   * Normalizes and copies the passed argument to be a real, useful outer Node containing live DOM.
   * This is just a helper for user input of strings or other HTML elements.
   *
   * @param {(HTMLElement|string)} code HTMLElement, HTMLTemplateElement or string for innerHTML
   * @return {!Node}
   */
  function cloneArgument(code) {
    if (code instanceof HTMLTemplateElement) {
      return document.importNode(code.content, true) || document.createDocumentFragment();
    } else if (code instanceof HTMLElement) {
      return document.cloneNode(true);
    }

    code = code.valueOf();
    if (typeof code !== 'string') {
      throw new TypeError('expected HTMLTemplateElement, HTMLElement or string');
    }

    const fragment = document.createDocumentFragment();
    const holder = document.createElement('div');
    holder.innerHTML = code;
    while (holder.childNodes.length) {
      fragment.appendChild(holder.childNodes[0]);
    }
    return fragment;
  }

  /**
   * @param {string} s
   * @return escaped version of string suitable for regexp
   */
  function escapeReLiteral(s) {
    return s.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
  }

  class JSBindTemplateNode {
    constructor() {
      /** @type {!Array<function(string)>} */
      this.helpers = [];

      /** @type {!Object<!JSBindTemplateNode>} */
      this.children = {};
    }

    must(x) {
      const out = this.children[x];
      if (!out) {
        return this.children[x] = new JSBindTemplateNode();
      }
      return out;
    }

    run(value) {
      this.helpers.forEach(helper => helper(value));
    }
  }

  /**
   * JSBindTemplateBuilder presents JSBindTemplateNode instances, in both a flat map and a tree.
   */
  class JSBindTemplateBuilder {
    constructor() {
      this.root = new JSBindTemplateNode();

      /**
       * @type {!Object<!JSBindTemplateNode>}
       */
      this.all_ = {'': this.root};

      // TODO: typedef/something function?
      /**
       * @type {!Object<!Array<function(*, string)>>}
       */
      this.each_ = {};
      this.eachRe_ = false;
    }

    /**
     * @param {string} k
     * @param {function(*)} fn
     */
    add(k, fn) {
      const more = k.split('.');
      const flatKey = [];

      if (more[0] === '') {
        more.shift();  // nb. this eats '.foo' as well as '.' or ''
      }

      let node = this.root;
      while (more.length) {
        const next = more.shift();
        flatKey.push(next);
        const key = flatKey.join('.');

        if (!next.startsWith('$')) {
          node = node.must(next);
        } else {
          node = this.all_[key] || new JSBindTemplateNode();  // start new root for $-prefix
        }

        const prev = this.all_[key];
        if (prev !== undefined && prev !== node) {
          throw new Error('unexpected node in flat map: `' + key + '`');
        }
        this.all_[key] = node;
      }
      node.helpers.push(fn);
    }

    /**
     * @param {string} k
     * @param {function(*, string)} fn
     */
    addEach(k, fn) {
      this.add(k, /** @type {function(*)} */ (fn));

      // TODO: this is a bit ugly vs using nodes properly
      if (!(k in this.each_)) {
        this.each_[k] = [];
      }
      this.each_[k].push(fn);

      this.eachRe_ = null;
    }

    /**
     * @param {string} k to update at
     * @param {*} value to update with
     */
    update(k, value) {
      const first = this.all_[k];
      if (!first) {
        if (this.eachRe_ === null) {
          // TODO: each could still break regep
          const safe = Object.keys(this.each_).map(escapeReLiteral);
          const s = '^(' + safe.join('|') + ')\\.(\\w+)(?:\\.(.*)|)$';
          this.eachRe_ = new RegExp(s);
        } else if (!this.eachRe_) {
          return;  // no each here
        }

        const m = this.eachRe_.exec(k);
        if (m) {
          // This found an update for a key _under_ an each.
          k = m[1];
          const key = m[2];
          const rest = m[3];  // there's more!

          this.each_[k].forEach(fn => fn(value, key, rest));
        }
        return;  // not matched
      }

      const pending = [{node: first, value}];
      while (pending.length) {
        const {node, value} = pending.shift();
        node.run(value);

        for (let k in node.children) {
          const nextValue = (value == null ? undefined : value[k]);
          pending.push({node: node.children[k], value: nextValue});
        }
      }
    }
  }

  const re = new RegExp(/{{(.*?)}}/g);

  /**
   * @param {string} text of HTML nodes to generate bindings for
   * @param {!JSBindTemplateBuilder} binding
   * @return {DocumentFragment}
   */
  function convertTextNode(text, binding) {
    const fragment = document.createDocumentFragment();
    function appendTextNode(text) {
      const node = document.createTextNode(text);
      fragment.appendChild(node);
      return node;
    }

    let atIndex = -1;
    let match;
    while ((match = re.exec(text)) !== null) {
      if (match.index > atIndex) {
        appendTextNode(text.substring(atIndex, match.index));
        atIndex = re.lastIndex;  // step over match
      }

      const node = appendTextNode('');
      fragment.appendChild(node);
      binding.add(match[1], bindTextContent.bind(node));
    }
    re.lastIndex = NaN;  // nb. Safari doesn't like -1
    if (!atIndex) {
      return null;  // don't do anything, no matches
    }

    const tail = text.substring(atIndex);
    if (tail.length) {
      appendTextNode(tail);
    }
    return fragment;
  }

  /**
   * @template T
   * @param {(!Object<T>|!Map<string, T>|!Array<T>|null)} arg
   * @param {function(T, (string|number))} fn
   */
  function forEach(arg, fn) {
    if (!arg) { return; }
    if (typeof arg.forEach === 'function') {
      arg.forEach(fn);
    } else {
      const o = /** @type {!Object|!Array} */ (arg);
      for (let k in o) {
        fn(o[k], k);
      }
    }
  }

  /**
   * @param {!Node} node to generate bindings for
   * @param {!JSBindTemplateBuilder} binding
   * @param {!Set<!JSBindTemplateBuilder>} live
   */
  function convertNode(node, binding, live) {
    const pending = [node];
    let n;
    while ((n = pending.shift())) {
      if (n instanceof HTMLTemplateElement) {
        const each = n.getAttribute('each');
        if (each === undefined) {
          throw new Error('expected template each');
        }

        const placeholder = document.createComment(' ' + each + ' ');
        n.parentNode.replaceChild(placeholder, n);
        binding.addEach(each, buildEach(n, placeholder, live));
        continue;
      }

      pending.push(...n.childNodes);

      if (n instanceof Text) {
        const out = convertTextNode(n.wholeText, binding);
        if (out) {
          n.parentNode.replaceChild(out, n);
        }
      } else if (n instanceof Element) {
        const found = fetchBoundAttributes(n);
        for (const attr in found) {
          binding.add(found[attr], bindAttribute.bind(n.attributes[attr]));
        }
      }
    }
  }

  /**
   * @param {(HTMLElement|string)} code
   * @param {*=} opt_data
   * @return {{root: !Node, update: function(string, *)}}
   */
  scope['JSBind'] = function(code, opt_data) {
    const outer = cloneArgument(code);

    /**
     * @type {!Set<!JSBindTemplateBuilder>}
     */
    const live = new Set();
    const binding = new JSBindTemplateBuilder();

    // Traverse the entire DOM, finding insertion points.
    convertNode(outer, binding, live);
    live.add(binding);

    /**
     * @param {string} k to update at
     * @param {*} value to update with
     */
    function update(k, value) {
        // This always updates, because every outer can still have top-level args.
      live.forEach(binding => binding.update(k, value));
    }

    update('', opt_data);
    return {root: outer, update};
  };
}(window));
<commit_msg>convertTextNode as callback<commit_after>'use strict';

(function(scope) {
  /**
   * @param {*} value
   * @this {Attr}
   */
  function bindAttribute(value) {
    this.value = /** @type {string} */ (value);
  }

  /**
   * @param {*} value
   * @this {Node}
   */
  function bindTextContent(value) {
    this.textContent = /** @type {string} */ (value);
  }

  /**
   * @param {!HTMLTemplateElement} template
   * @param {!Node} placeholder
   * @param {!Set<!JSBindTemplateBuilder>} live
   * @return {function(*, string)}
   */
  function buildEach(template, placeholder, live) {
    const curr = new Map();
    const each = template.getAttribute('each');

    function add(value, key, rest) {
      let config = curr.get(key);
      if (!config) {
        const binding = new JSBindTemplateBuilder();
        const frag = template.content.cloneNode(true);
        convertNode(frag, binding, live);

        config = {
          binding,
          outer: frag,
          nodes: [...frag.childNodes],  // set before insertion and children disappearing
        };
        curr.set(key, config);
        placeholder.parentNode.insertBefore(frag, placeholder);

        live.add(binding);
      }

      const flatKey = '$' + (rest !== undefined ? '.' + rest : '');
      config.binding.update(flatKey, value);
    }

    function remove(key) {
      const config = curr.get(key);
      if (config) {
        curr.delete(key);
        live.delete(config.binding);
        config.binding.update('$', null);  // tell children to disappear
        config.nodes.forEach(node => node.remove());
      }
    }

    // TODO: make this into a class helper?
    return (value, key, rest) => {
      if (key === undefined) {
        if (rest !== undefined) {
          throw new TypeError('can\'t pass rest with each replace');
        }

        // This is a set of the each key directly, presumably with a new Map/Array etc.
        // TODO(samthor): Don't nuke/recreate, try to maintain parity. Of course values might be
        // different but if the keys are the same, just "change" instead of delete.
        for (const key of curr.keys()) {
          remove(key);
        }
        if (curr.size) {
          throw new Error('curr should now be empty');
        }
        forEach(value, (value, key) => add(value, key, undefined));  // rest must be undefined
      } else if (value === null && rest === undefined) {
        // Delete a specific key, e.g. "each.0" => null.
        remove(key);
      } else {
        // Create a specific key. Note that `undefined` is valid here, and creates without set.
        // Rest is passed to allow implicit creation/update of some subpath.
        add(value, key, rest);
      }
    };
  }

  /**
   * Fetches and clears the bound attributes from the passed Element.
   *
   * @param {!Element} el to process
   * @return {!Object<string>} map of bound attribute to literal bound value
   */
  function fetchBoundAttributes(el) {
    // TODO: looks for attr$="bound", but Polymer uses attr$="{{bound}}" syntax
    const found = {};
    for (let i = 0, curr; curr = el.attributes[i]; ++i) {
      if (curr.name.substring(curr.name.length - 1) === '$') {
        const attr = curr.name.substr(0, curr.name.length - 1);
        found[attr] = curr.value;
      }
    }
    for (const attr in found) {
      el.removeAttribute(attr + '$');
      el.setAttribute(attr, '');
    }
    return found;
  }

  /**
   * Normalizes and copies the passed argument to be a real, useful outer Node containing live DOM.
   * This is just a helper for user input of strings or other HTML elements.
   *
   * @param {(HTMLElement|string)} code HTMLElement, HTMLTemplateElement or string for innerHTML
   * @return {!Node}
   */
  function cloneArgument(code) {
    if (code instanceof HTMLTemplateElement) {
      return document.importNode(code.content, true) || document.createDocumentFragment();
    } else if (code instanceof HTMLElement) {
      return document.cloneNode(true);
    }

    code = code.valueOf();
    if (typeof code !== 'string') {
      throw new TypeError('expected HTMLTemplateElement, HTMLElement or string');
    }

    const fragment = document.createDocumentFragment();
    const holder = document.createElement('div');
    holder.innerHTML = code;
    while (holder.childNodes.length) {
      fragment.appendChild(holder.childNodes[0]);
    }
    return fragment;
  }

  /**
   * @param {string} s
   * @return escaped version of string suitable for regexp
   */
  function escapeReLiteral(s) {
    return s.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
  }

  class JSBindTemplateNode {
    constructor() {
      /** @type {!Array<function(string)>} */
      this.helpers = [];

      /** @type {!Object<!JSBindTemplateNode>} */
      this.children = {};
    }

    must(x) {
      const out = this.children[x];
      if (!out) {
        return this.children[x] = new JSBindTemplateNode();
      }
      return out;
    }

    run(value) {
      this.helpers.forEach(helper => helper(value));
    }
  }

  /**
   * JSBindTemplateBuilder presents JSBindTemplateNode instances, in both a flat map and a tree.
   */
  class JSBindTemplateBuilder {
    constructor() {
      this.root = new JSBindTemplateNode();

      /**
       * @type {!Object<!JSBindTemplateNode>}
       */
      this.all_ = {'': this.root};

      // TODO: typedef/something function?
      /**
       * @type {!Object<!Array<function(*, string)>>}
       */
      this.each_ = {};
      this.eachRe_ = false;
    }

    /**
     * @param {string} k
     * @param {function(*)} fn
     */
    add(k, fn) {
      const more = k.split('.');
      const flatKey = [];

      if (more[0] === '') {
        more.shift();  // nb. this eats '.foo' as well as '.' or ''
      }

      let node = this.root;
      while (more.length) {
        const next = more.shift();
        flatKey.push(next);
        const key = flatKey.join('.');

        if (!next.startsWith('$')) {
          node = node.must(next);
        } else {
          node = this.all_[key] || new JSBindTemplateNode();  // start new root for $-prefix
        }

        const prev = this.all_[key];
        if (prev !== undefined && prev !== node) {
          throw new Error('unexpected node in flat map: `' + key + '`');
        }
        this.all_[key] = node;
      }
      node.helpers.push(fn);
    }

    /**
     * @param {string} k
     * @param {function(*, string)} fn
     */
    addEach(k, fn) {
      this.add(k, /** @type {function(*)} */ (fn));

      // TODO: this is a bit ugly vs using nodes properly
      if (!(k in this.each_)) {
        this.each_[k] = [];
      }
      this.each_[k].push(fn);

      this.eachRe_ = null;
    }

    /**
     * @param {string} k to update at
     * @param {*} value to update with
     */
    update(k, value) {
      const first = this.all_[k];
      if (!first) {
        if (this.eachRe_ === null) {
          // TODO: each could still break regep
          const safe = Object.keys(this.each_).map(escapeReLiteral);
          const s = '^(' + safe.join('|') + ')\\.(\\w+)(?:\\.(.*)|)$';
          this.eachRe_ = new RegExp(s);
        } else if (!this.eachRe_) {
          return;  // no each here
        }

        const m = this.eachRe_.exec(k);
        if (m) {
          // This found an update for a key _under_ an each.
          k = m[1];
          const key = m[2];
          const rest = m[3];  // there's more!

          this.each_[k].forEach(fn => fn(value, key, rest));
        }
        return;  // not matched
      }

      const pending = [{node: first, value}];
      while (pending.length) {
        const {node, value} = pending.shift();
        node.run(value);

        for (let k in node.children) {
          const nextValue = (value == null ? undefined : value[k]);
          pending.push({node: node.children[k], value: nextValue});
        }
      }
    }
  }

  const re = new RegExp(/{{(.*?)}}/g);

  /**
   * @param {string} text of HTML nodes to generate bindings for
   * @param {function(string, !Text, number)} callback for {{name}} and newly created node
   * @return {DocumentFragment}
   */
  function convertTextNode(text, callback) {
    const fragment = document.createDocumentFragment();
    let count = -1;  // index in newly created, replaced node
    function appendTextNode(text) {
      ++count;
      const node = document.createTextNode(text);
      fragment.appendChild(node);
      return node;
    }

    let atIndex = -1;
    let match;
    while ((match = re.exec(text)) !== null) {
      if (match.index > atIndex) {
        appendTextNode(text.substring(atIndex, match.index));
        atIndex = re.lastIndex;  // step over match
      }

      const node = appendTextNode('');
      fragment.appendChild(node);
      callback(match[1], node, count);
    }
    re.lastIndex = NaN;  // nb. Safari doesn't like -1
    if (!atIndex) {
      return null;  // don't do anything, no matches
    }

    const tail = text.substring(atIndex);
    if (tail.length) {
      appendTextNode(tail);
    }
    return fragment;
  }

  /**
   * @template T
   * @param {(!Object<T>|!Map<string, T>|!Array<T>|null)} arg
   * @param {function(T, (string|number))} fn
   */
  function forEach(arg, fn) {
    if (!arg) { return; }
    if (typeof arg.forEach === 'function') {
      arg.forEach(fn);
    } else {
      const o = /** @type {!Object|!Array} */ (arg);
      for (let k in o) {
        fn(o[k], k);
      }
    }
  }

  /**
   * @param {!Node} node to generate bindings for
   * @param {!JSBindTemplateBuilder} binding
   * @param {!Set<!JSBindTemplateBuilder>} live
   */
  function convertNode(node, binding, live) {
    const pending = [node];
    let n;
    while ((n = pending.shift())) {
      if (n instanceof HTMLTemplateElement) {
        const each = n.getAttribute('each');
        if (each === undefined) {
          throw new Error('expected template each');
        }

        const placeholder = document.createComment(' ' + each + ' ');
        n.parentNode.replaceChild(placeholder, n);
        binding.addEach(each, buildEach(n, placeholder, live));
        continue;
      }

      pending.push(...n.childNodes);

      if (n instanceof Text) {
        const out = convertTextNode(n.wholeText, (bound, node, i) => {
          binding.add(bound, bindTextContent.bind(node));
        });
        if (out) {
          n.parentNode.replaceChild(out, n);
        }
      } else if (n instanceof Element) {
        const found = fetchBoundAttributes(n);
        for (const attr in found) {
          binding.add(found[attr], bindAttribute.bind(n.attributes[attr]));
        }
      }
    }
  }

  /**
   * @param {(HTMLElement|string)} code
   * @param {*=} opt_data
   * @return {{root: !Node, update: function(string, *)}}
   */
  scope['JSBind'] = function(code, opt_data) {
    const outer = cloneArgument(code);

    /**
     * @type {!Set<!JSBindTemplateBuilder>}
     */
    const live = new Set();
    const binding = new JSBindTemplateBuilder();

    // Traverse the entire DOM, finding insertion points.
    convertNode(outer, binding, live);
    live.add(binding);

    /**
     * @param {string} k to update at
     * @param {*} value to update with
     */
    function update(k, value) {
        // This always updates, because every outer can still have top-level args.
      live.forEach(binding => binding.update(k, value));
    }

    update('', opt_data);
    return {root: outer, update};
  };
}(window));
<|endoftext|>


<commit_before>/**
 * Created by Leo.H & MC.Gong on 2017/3/6.
 */

// This file is the main script file.
// This file is used to move the block.

/*-----------------------------keypressed Function---------------------------*/
	//keypressed function to move blocks
/*	document.KeyPressed=function(event)
	{
		if(!isGameOver)
		{
			//Move Up
			if(event.keycode == 38)
			{
				if(UpMoving())
				getRandomFreeCell();
			}

			//Move Down
			if(event.keycode == 40)
			{
				if(DownMoving(())
				{
					getRandomFreeCell();
				}
			}

			//Move Left
			if(event.keycode == 37)
			{
				if(LeftMoving(())
				{
					getRandomFreeCell();
				}
			}

			//Move Right
			if(event.keycode == 49)
			{
				if(RightMoving())
				{
					getRandomFreeCell();
				}
			}

			if(noMove())
			{
				isGameOver = true;
			}

		}
	};
*/
/*-------------------------------Moving Function---------------------------*/
	// when up key pressed, run UpMoving
	function UpMoving()
	{
		if(!canMoveUp())
		{
			return false;
		}

		for(var i = 0; i < 4; i++ ){
	        for( var j = 3;j > 0; j--){
	            if (BlockTable[j][i]!=0) {
	            	if (BlockTable[j-1][i] == 0){
	            		BlockTable[j-1][i] = BlockTable[j][i];
	            	}else if (BlockTable[j][i] == BlockTable[j-1][i]){
	            		BlockTable[j-1][i] = 2*BlockTable[j][i];
	            		BlockTable[j][i] = 0;
	            		j--;
	            		while (BlockTable[j-1][i] == 0 || j>0){
	            			BlockTable[j-1][i] = BlockTable[j][i];
	            			BlockTable[j][i] = 0;
	            			j--;
	            		}
	            	}
	            }
	        }
	    }

		return true;
	};

	// when down key pressed, run DownMoving
	function DownMoving()
	{
		if(!canMoveDown())
		{
			return false;
		}

		for(var i = 0; i < 4; i++ ){
	        for( var j = 0;j < 3; j++){
	            if (BlockTable[j][i]!=0) {
	            	if (BlockTable[j+1][i] == 0){
	            		BlockTable[j+1][i] = BlockTable[j][i];
	            	}else if (BlockTable[j][i] == BlockTable[j+1][i]){
	            		BlockTable[j+1][i] = 2*BlockTable[j][i];
	            		BlockTable[j][i] = 0;
	            		j++;
	            		while (BlockTable[j+1][i] == 0 || j<3){
	            			BlockTable[j+1][i] = BlockTable[j][i];
	            			BlockTable[j][i] = 0;
	            			j++;
	            		}
	            	}
	            }
	        }
	    }

		return true;
	};

	// when left key pressed, run LeftMoving
	function LeftMoving()
	{
		if(!canMoveLeft())
		{
			return false;
		}

		for(var j = 0; j < 4; j++ ){
	        for( var i = 3;i > 0; i--){
	            if (BlockTable[j][i]!=0) {
	            	if (BlockTable[j][i-1] == 0){
	            		BlockTable[j][i-1] = BlockTable[j][i];
	            	}else if (BlockTable[j][i] == BlockTable[j][i-1]){
	            		BlockTable[j][i-1] = 2*BlockTable[j][i];
	            		BlockTable[j][i] = 0;
	            		i--;
	            		while (BlockTable[j][i-1] == 0 || i>0){
	            			BlockTable[j][i-1] = BlockTable[j][i];
	            			BlockTable[j][i] = 0;
	            			i--;
	            		}
	            	}
	            }
	        }
	    }

		return true;
	};

	// when right key pressed, run RightMoving
	function RightMoving()
	{
		if(!canMoveRight())
		{
			return false;
		}

		for(var j = 0; j < 4; j++ ){
	        for( var i = 0;i < 3; i++){
	            if (BlockTable[j][i]!=0) {
	            	if (BlockTable[j][i+1] == 0){
	            		BlockTable[j][i+1] = BlockTable[j][i];
	            	}else if (BlockTable[j][i] == BlockTable[j][i+1]){
	            		BlockTable[j][i+1] = 2*BlockTable[j][i];
	            		BlockTable[j][i] = 0;
	            		i++;
	            		while (BlockTable[j][i+1] == 0 || i<3){
	            			BlockTable[j][i+1] = BlockTable[j][i];
	            			BlockTable[j][i] = 0;
	            			i++;
	            		}
	            	}
	            }
	        }
	    }

		return true;
	};

/*-------------------------------Display Function---------------------------*/
	var getRandomNumber = function () {
		return Math.random() > 0.3 ? 2 : 4;
	};

	//generate a valid to store an random num created from getRandomNumber
	var getRandomFreeCell = function () {
		var count = 4;
		var RandColumn = Math.floor(Math.random() * count);
		var RandRow = Math.floor(Math.random() * count);
		var cellValue = document.getElementById("block"+RandRow+"-"+RandColumn).innerHTML;

		if (cellValue == ''){
			return "block"+RandRow+"-"+RandColumn;
		}
	};

	var updateBlockTable = function (id, num) {
		var row = id.substring(5,5);
		var col = id.substring(7,7);
		BlockTable[row][col] = num;
	};

<commit_msg>edit move<commit_after>/**
 * Created by Leo.H & MC.Gong on 2017/3/6.
 */

// This file is the main script file.
// This file is used to move the block.

/*-----------------------------keypressed Function---------------------------*/
	//keypressed function to move blocks
	document.KeyPressed=function(event)
	{
		if(!isGameOver)
		{
			//Move Up
			if(event.keycode == 38)
			{
				if(canMoveUp()){
					UpMoving();
					show();
					showValue(getRandomFreeCell(),getRandomNumber());
				}

			}

			//Move Down
			if(event.keycode == 40)
			{
				if(canMoveDown(())
				{
					DownMoving();
					show();
					showValue(getRandomFreeCell(),getRandomNumber());
				}
			}

			//Move Left
			if(event.keycode == 37)
			{
				if(canMoveLeft(())
				{
					LeftMoving();
					show();
					showValue(getRandomFreeCell(),getRandomNumber());
				}
			}

			//Move Right
			if(event.keycode == 49)
			{
				if(canMoveRight())
				{
					RightMoving();
					show();
					showValue(getRandomFreeCell(),getRandomNumber());
				}
			}

			if(noMove())
			{
				isGameOver = true;
			}

		}
	};

/*-------------------------------Moving Function---------------------------*/
	// when up key pressed, run UpMoving
	/*
		move from BOTTOM to TOP
		if the top cell of the current cell is 0
			=> just move up
		if it can merge
			=> merge the cell to top and clean the current cell
				=> after the merge, check the top cell is 0
					=> if it is 0, move the cell the top UNTIL it move to the FINAL cell
	*/
	function UpMoving()
	{
		for(var i = 0; i < 4; i++ ){
	        for( var j = 3;j > 0; j--){
	            if (BlockTable[j][i]!=0) {
	            	if (BlockTable[j-1][i] == 0){
	            		BlockTable[j-1][i] = BlockTable[j][i];
	            	}else if (BlockTable[j][i] == BlockTable[j-1][i]){
	            		BlockTable[j-1][i] = 2*BlockTable[j][i];
	            		BlockTable[j][i] = 0;
	            		j--;
	            		while (BlockTable[j-1][i] == 0 || j>0){
	            			BlockTable[j-1][i] = BlockTable[j][i];
	            			BlockTable[j][i] = 0;
	            			j--;
	            		}
	            	}
	            }
	        }
	    }
	};

	// when down key pressed, run DownMoving
	function DownMoving()
	{
		for(var i = 0; i < 4; i++ ){
	        for( var j = 0;j < 3; j++){
	            if (BlockTable[j][i]!=0) {
	            	if (BlockTable[j+1][i] == 0){
	            		BlockTable[j+1][i] = BlockTable[j][i];
	            	}else if (BlockTable[j][i] == BlockTable[j+1][i]){
	            		BlockTable[j+1][i] = 2*BlockTable[j][i];
	            		BlockTable[j][i] = 0;
	            		j++;
	            		while (BlockTable[j+1][i] == 0 || j<3){
	            			BlockTable[j+1][i] = BlockTable[j][i];
	            			BlockTable[j][i] = 0;
	            			j++;
	            		}
	            	}
	            }
	        }
	    }
	};

	// when left key pressed, run LeftMoving
	function LeftMoving()
	{

		for(var j = 0; j < 4; j++ ){
	        for( var i = 3;i > 0; i--){
	            if (BlockTable[j][i]!=0) {
	            	if (BlockTable[j][i-1] == 0){
	            		BlockTable[j][i-1] = BlockTable[j][i];
	            	}else if (BlockTable[j][i] == BlockTable[j][i-1]){
	            		BlockTable[j][i-1] = 2*BlockTable[j][i];
	            		BlockTable[j][i] = 0;
	            		i--;
	            		while (BlockTable[j][i-1] == 0 || i>0){
	            			BlockTable[j][i-1] = BlockTable[j][i];
	            			BlockTable[j][i] = 0;
	            			i--;
	            		}
	            	}
	            }
	        }
	    }
	};

	// when right key pressed, run RightMoving
	function RightMoving()
	{
		for(var j = 0; j < 4; j++ ){
	        for( var i = 0;i < 3; i++){
	            if (BlockTable[j][i]!=0) {
	            	if (BlockTable[j][i+1] == 0){
	            		BlockTable[j][i+1] = BlockTable[j][i];
	            	}else if (BlockTable[j][i] == BlockTable[j][i+1]){
	            		BlockTable[j][i+1] = 2*BlockTable[j][i];
	            		BlockTable[j][i] = 0;
	            		i++;
	            		while (BlockTable[j][i+1] == 0 || i<3){
	            			BlockTable[j][i+1] = BlockTable[j][i];
	            			BlockTable[j][i] = 0;
	            			i++;
	            		}
	            	}
	            }
	        }
	    }
	};

/*-------------------------------Display Function---------------------------*/
	var getRandomNumber = function () {
		return Math.random() > 0.3 ? 2 : 4;
	};

	//generate a valid to store an random num created from getRandomNumber
	var getRandomFreeCell = function () {
		var count = 4;
		var RandColumn = Math.floor(Math.random() * count);
		var RandRow = Math.floor(Math.random() * count);
		var cellValue = document.getElementById("block"+RandRow+"-"+RandColumn).innerHTML;

		if (cellValue == ''){
			return "block"+RandRow+"-"+RandColumn;
		}
	};

	var updateBlockTable = function (id, num) {
		var row = id.substring(5,5);
		var col = id.substring(7,7);
		BlockTable[row][col] = num;
	};

<|endoftext|>


<commit_before>/*
    JsonML.js
    2006-11-12

    This file adds these methods to JavaScript:

        array.parseJsonML()

            This method produces a JSON text from an array. The
            array must not contain any cyclical references.

        string.parseJsonML()

            This method parses a JSON text to produce a tree of
            DOM elements.

    http://jsonml.org/License.htm
*/

Array.prototype.parseJsonML = function () {

	var re = /^\s*(\s*?[\w-]+)\s*[:]\s*(.+?)\s*$/;// styles regex

	//addAttributes
	/*void*/ function aa(/*element*/ el, /*object*/ a) {
		// foreach attributeName
		for (var an in a) {
			if (typeof(a[an]) !== "string") {
				continue;
			}
			if (an === "style") {
				var s = a[an];// styles
				s = s.split(";");
				for (var i=0; i<s.length; i++) {
					if (!s[i]) {
						continue;
					}
					if (s[i].match(re)) {
						var n = RegExp.$1; // style property
						var v = RegExp.$2; // style value
						if (n && v) {
							if (n === "float") {
								n = "styleFloat";
							} else {
								// convert property name to camelCase
								n = n.split('-');
								n[0] = n[0].toLowerCase();
								for (var j=1; j<n.length; j++) {
									n[j] = n[j].charAt(0).toUpperCase()+n[j].substr(1).toLowerCase();
								}
								n = n.join("");
							}
							el.style[n] = v;
						}
					}
				}
			} else if (an === "class") {
				el.className = a[an];
			} else {
				el.setAttribute(an, a[an]);
			}
		}
	}

	//appendChild
	/*void*/ function ac(/*element*/ el, /*array or string*/ c) {
		if (c) {
			if (el.tagName.toLowerCase() === "table" && el.tBodies) {
				// in IE must explicitly nest TDs in TBODY
				var ct = c.tagName ? c.tagName.toLowerCase() : null;// child tagName
				if (ct && ct!=="tbody" && ct!=="thead") {
					// insert in last tbody
					var tb = el.tBodies.length>0 ? el.tBodies[el.tBodies.length-1] : null;// tBody
					if (!tb) {
						tb = document.createElement("tbody");
						el.appendChild(tb);
					}
					tb.appendChild(c);
				}
			} else {
				el.appendChild(c);
			}
		}
	}

	//parseJsonML
	/*element*/ function p(/*JsonML*/ jml) {
		if (!jml) {
			return null;
		}
		if (typeof(jml) === "string") {
			return document.createTextNode(jml);
		}

		if (!(jml instanceof Array) || jml.length < 1 || typeof(jml[0]) !== "string") {
			throw new Error("parseJsonML");
		}

		var t = jml[0]; // tagName
		var x = (t.toLowerCase() === "script"); // check for scripts
		var el = x ? null : document.createElement(t);

		for (var i=1; i<jml.length; i++) {
			if (!x) {
				if (jml[i] instanceof Array || typeof(jml[i]) === "string") {
					// append children
					ac(el, p(jml[i]));
				} else if (typeof(jml[i]) === "object") {
					// add attributes
					aa(el, jml[i]);
				}
			//} else if (typeof(jml[i]) === "string") {
				/*	JSLint: "eval is evil"
					uncomment at your own risk, executes script elements */
				//eval(jml[i]);
			}
		}

		return el;
	}

	return p(this);
};

String.prototype.parseJsonML = function () {
	try {
		var jml = this.parseJSON();
		return (jml instanceof Array) ? jml.p() : null;
	} catch (ex) {
		return null;
	}
};
<commit_msg>- making attribute setting more robost for multiword attributes (IE is case sensitive)<commit_after>/*
    JsonML.js
    2007-01-07

    This file adds these methods to JavaScript:

        array.parseJsonML()

            This method produces a JSON text from an array. The
            array must not contain any cyclical references.

        string.parseJsonML()

            This method parses a JSON text to produce a tree of
            DOM elements.

    http://jsonml.org/License.htm
*/

Array.prototype.parseJsonML = function () {

	var re = /^\s*(\s*?[\w-]+)\s*[:]\s*(.+?)\s*$/;// styles regex

	//addAttributes
	/*void*/ function aa(/*element*/ el, /*object*/ a) {
		// foreach attributeName
		for (var an in a) {
			if (!an || typeof(a[an]) !== "string") {
				continue;
			}
			switch(an.toLowerCase()) {
				case "style":
					var s = a[an];// styles
					s = s.split(";");
					for (var i=0; i<s.length; i++) {
						if (!s[i]) {
							continue;
						}
						if (s[i].match(re)) {
							var n = RegExp.$1; // style property
							var v = RegExp.$2; // style value
							if (n && v) {
								if (n === "float") {
									n = "styleFloat";
								} else {
									// convert property name to camelCase
									n = n.split('-');
									n[0] = n[0].toLowerCase();
									for (var j=1; j<n.length; j++) {
										n[j] = n[j].charAt(0).toUpperCase()+n[j].substr(1).toLowerCase();
									}
									n = n.join("");
								}
								el.style[n] = v;
							}
						}
					}
					break;
				case "class":
					el.className = a[an];
					break;
				case "tabindex":
					el.tabIndex = a[an];
					break;
				case "accesskey":
					el.accessKey = a[an];
					break;
				case "hidefocus":
					el.hideFocus = a[an];
					break;
				default:
					el.setAttribute(an, a[an]);
					break;
			}
		}
	}

	//appendChild
	/*void*/ function ac(/*element*/ el, /*array or string*/ c) {
		if (c) {
			if (el.tagName.toLowerCase() === "table" && el.tBodies) {
				// in IE must explicitly nest TDs in TBODY
				var ct = c.tagName ? c.tagName.toLowerCase() : null;// child tagName
				if (ct && ct!=="tbody" && ct!=="thead") {
					// insert in last tbody
					var tb = el.tBodies.length>0 ? el.tBodies[el.tBodies.length-1] : null;// tBody
					if (!tb) {
						tb = document.createElement("tbody");
						el.appendChild(tb);
					}
					tb.appendChild(c);
				}
			} else {
				el.appendChild(c);
			}
		}
	}

	//parseJsonML
	/*element*/ function p(/*JsonML*/ jml) {
		if (!jml) {
			return null;
		}
		if (typeof(jml) === "string") {
			return document.createTextNode(jml);
		}

		if (!(jml instanceof Array) || jml.length < 1 || typeof(jml[0]) !== "string") {
			throw new Error("parseJsonML");
		}

		var t = jml[0]; // tagName
		var x = (t.toLowerCase() === "script"); // check for scripts
		var el = x ? null : document.createElement(t);

		for (var i=1; i<jml.length; i++) {
			if (!x) {
				if (jml[i] instanceof Array || typeof(jml[i]) === "string") {
					// append children
					ac(el, p(jml[i]));
				} else if (typeof(jml[i]) === "object") {
					// add attributes
					aa(el, jml[i]);
				}
			//} else if (typeof(jml[i]) === "string") {
				/*	JSLint: "eval is evil"
					uncomment at your own risk, executes script elements */
				//eval(jml[i]);
			}
		}

		return el;
	}

	return p(this);
};

String.prototype.parseJsonML = function () {
	try {
		var jml = this.parseJSON();
		return (jml instanceof Array) ? jml.p() : null;
	} catch (ex) {
		return null;
	}
};
<|endoftext|>


<commit_before>var debug                 = require('debug')('tastypie')
exports.Serializer        = require('./serializer')
exports.Resource          = require('./resource')
try{
	exports.Resource.Mongoose = require('tastypie-mongoose')
}catch( err ){
	debug("unable to load tastypie-mongoose");
	debug("MongooseResource not avaible");
	debug("npm install tastypie-mongoose --save"):
}

try{

	exports.Resource.Rethink  = require('tastypie-rethink')
catch( err ){
	debug("unable to load tastypie-rethink");
	debug("RethinkResource not avaible");
	debug("npm install tastypie-rethink --save"):
}
exports.Api               = require('./api')
exports.http              = require('./http')
exports.Paginator         = require('./paginator')
exports.Cache             = require('./cache')
exports.exceptions        = require('./exceptions')
exports.Class             = require('./class')
exports.Class.Options     = require('./class/options')
exports.http              = require('./http')
exports.constants         = require('./constants')
exports.urljoin           = require('urljoin')
<commit_msg>[NOISSUE] fix a typo in lib/index<commit_after>var debug                 = require('debug')('tastypie')
exports.Serializer        = require('./serializer')
exports.Resource          = require('./resource')
try{
	exports.Resource.Mongoose = require('tastypie-mongoose')
}catch( err ){
	debug("unable to load tastypie-mongoose");
	debug("MongooseResource not avaible");
	debug("npm install tastypie-mongoose --save")
}

try{

	exports.Resource.Rethink  = require('tastypie-rethink')
catch( err ){
	debug("unable to load tastypie-rethink");
	debug("RethinkResource not avaible");
	debug("npm install tastypie-rethink --save"):
}
exports.Api               = require('./api')
exports.http              = require('./http')
exports.Paginator         = require('./paginator')
exports.Cache             = require('./cache')
exports.exceptions        = require('./exceptions')
exports.Class             = require('./class')
exports.Class.Options     = require('./class/options')
exports.http              = require('./http')
exports.constants         = require('./constants')
exports.urljoin           = require('urljoin')
<|endoftext|>


<commit_before>/**
 * Shopware 4.0
 * Copyright © 2012 shopware AG
 *
 * According to our dual licensing model, this program can be used either
 * under the terms of the GNU Affero General Public License, version 3,
 * or under a proprietary license.
 *
 * The texts of the GNU Affero General Public License with an additional
 * permission and of our proprietary license can be found at and
 * in the LICENSE file you have received along with this program.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * "Shopware" is a registered trademark of shopware AG.
 * The licensing of the program under the AGPLv3 does not imply a
 * trademark license. Therefore any rights, title and interest in
 * our trademarks remain entirely with us.
 */
;(function(Manager, global) {
    /**
     * Checks if a class has already been created.
     *
     * Overridden because Ext does not usually handle situations correctly, where namespaces and classes with the same name exist.
     * Shopware uses this feature extensively:
     *
     * @example
     *
     *     Shopware.apps.Index
     *     Shopware.apps.Login
     *     Shopware.apps.Login.controller.Main
     *     ...
     *
     *
     * @param { String } className
     * @return { Boolean } exist
     */
    Manager.isCreated = function(className) {
        var existCache = this.existCache,
            i, ln, part, root, parts;

        if (this.classes[className] || existCache[className]) {
            return true;
        }

        root = global;
        parts = this.parseNamespace(className);

        for (i = 0, ln = parts.length; i < ln; i++) {
            part = parts[i];

            if (typeof part != 'string') {
                root = part;
            } else {
                if (!root || !root[part]) {
                    return false;
                }

                root = root[part];
            }
        }

        // We need to on [a-z].apps.[a-z] to vertify that sub app classes are ready instead of only their namespace being present.
        if (/^.*\.apps\./.test(className) && typeof root != 'function') {
            return false;
        }

        existCache[className] = true;

        this.triggerCreated(className);

        return true;
    };

    Manager.createOverride = function(className, data, createdFn) {
        var me = this,
            overriddenClassName = data.override,
            requires = data.requires,
            uses = data.uses,
            check = true,
            classReady = function () {
                var cls, temp;

                if (requires) {
                    temp = requires;
                    requires = null; // do the real thing next time (which may be now)

                    // Since the override is going to be used (its target class is now
                    // created), we need to fetch the required classes for the override
                    // and call us back once they are loaded:
                    Ext.Loader.require(temp, classReady);
                } else {
                    // The target class and the required classes for this override are
                    // ready, so we can apply the override now:
                    cls = me.get(overriddenClassName);

                    // Check if the override want to replace the article window
                    if(data.override === 'Shopware.apps.Article.view.detail.Window') {
                        check = me.checkOverride(cls, data);
                    }

                    // We don't want to apply these:
                    delete data.override;
                    delete data.requires;
                    delete data.uses;

                    // The override check was not successful
                    if(!check) {
                        data = { '_invalidPlugin': true, '_invalidClassName': className };
                    } else {
                        data['_invalidPlugin'] = false;
                    }

                    Ext.override(cls, data);

                    // This pushes the overridding file itself into Ext.Loader.history
                    // Hence if the target class never exists, the overriding file will
                    // never be included in the build.
                    me.triggerCreated(className);

                    if (uses) {
                        Ext.Loader.addUsedClasses(uses); // get these classes too!
                    }

                    if (createdFn) {
                        createdFn.call(cls); // last but not least!
                    }
                }
            };

        me.existCache[className] = true;

        // Override the target class right after it's created
        me.onCreated(classReady, me, overriddenClassName);

        return me;
    };

    /**
     * Helper method which checks the override if it's suppports
     * the new 4.1 way to extend the product mask module.
     *
     * The method transforms all functions of the override
     * to a string and checks if one of the functions contains
     * the string "registerAdditionalTab".
     *
     * @param { Ext.Class } cls - The class which should be overridden
     * @param { Object } data - Object which contains all functions which should be
     *        merged into the class.
     * @returns { Boolean } Truthy if the override provides the new way, falsy if not.
     */
    Manager.checkOverride =  function(cls, data) {
        var match = true, fnName, fn;

        for(fnName in data) {
            fn = data[fnName].toString();
            if(fn.match(/createMainTabPanel/i)) {
                match = false;
                break;
            }
        }

        // ..alright, we found the old method to extend the product module,
        // now check if the developer supports the new way as well.
        if(!match) {
            for(fnName in data) {
                fn = data[fnName].toString();
                if(fn.match(/registerAdditionalTab/i)) {
                    match = true;
                    break;
                }
            }
        }
    };
})(Ext.ClassManager, Ext.global);<commit_msg>SW-6329 - Added: The return value now will be returned<commit_after>/**
 * Shopware 4.0
 * Copyright © 2012 shopware AG
 *
 * According to our dual licensing model, this program can be used either
 * under the terms of the GNU Affero General Public License, version 3,
 * or under a proprietary license.
 *
 * The texts of the GNU Affero General Public License with an additional
 * permission and of our proprietary license can be found at and
 * in the LICENSE file you have received along with this program.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * "Shopware" is a registered trademark of shopware AG.
 * The licensing of the program under the AGPLv3 does not imply a
 * trademark license. Therefore any rights, title and interest in
 * our trademarks remain entirely with us.
 */
;(function(Manager, global) {
    /**
     * Checks if a class has already been created.
     *
     * Overridden because Ext does not usually handle situations correctly, where namespaces and classes with the same name exist.
     * Shopware uses this feature extensively:
     *
     * @example
     *
     *     Shopware.apps.Index
     *     Shopware.apps.Login
     *     Shopware.apps.Login.controller.Main
     *     ...
     *
     *
     * @param { String } className
     * @return { Boolean } exist
     */
    Manager.isCreated = function(className) {
        var existCache = this.existCache,
            i, ln, part, root, parts;

        if (this.classes[className] || existCache[className]) {
            return true;
        }

        root = global;
        parts = this.parseNamespace(className);

        for (i = 0, ln = parts.length; i < ln; i++) {
            part = parts[i];

            if (typeof part != 'string') {
                root = part;
            } else {
                if (!root || !root[part]) {
                    return false;
                }

                root = root[part];
            }
        }

        // We need to on [a-z].apps.[a-z] to vertify that sub app classes are ready instead of only their namespace being present.
        if (/^.*\.apps\./.test(className) && typeof root != 'function') {
            return false;
        }

        existCache[className] = true;

        this.triggerCreated(className);

        return true;
    };

    Manager.createOverride = function(className, data, createdFn) {
        var me = this,
            overriddenClassName = data.override,
            requires = data.requires,
            uses = data.uses,
            check = true,
            classReady = function () {
                var cls, temp;

                if (requires) {
                    temp = requires;
                    requires = null; // do the real thing next time (which may be now)

                    // Since the override is going to be used (its target class is now
                    // created), we need to fetch the required classes for the override
                    // and call us back once they are loaded:
                    Ext.Loader.require(temp, classReady);
                } else {
                    // The target class and the required classes for this override are
                    // ready, so we can apply the override now:
                    cls = me.get(overriddenClassName);

                    // Check if the override want to replace the article window
                    if(data.override === 'Shopware.apps.Article.view.detail.Window') {
                        check = me.checkOverride(cls, data);
                    }

                    // We don't want to apply these:
                    delete data.override;
                    delete data.requires;
                    delete data.uses;

                    // The override check was not successful
                    if(!check) {
                        data = { '_invalidPlugin': true, '_invalidClassName': className };
                    } else {
                        data['_invalidPlugin'] = false;
                    }

                    Ext.override(cls, data);

                    // This pushes the overridding file itself into Ext.Loader.history
                    // Hence if the target class never exists, the overriding file will
                    // never be included in the build.
                    me.triggerCreated(className);

                    if (uses) {
                        Ext.Loader.addUsedClasses(uses); // get these classes too!
                    }

                    if (createdFn) {
                        createdFn.call(cls); // last but not least!
                    }
                }
            };

        me.existCache[className] = true;

        // Override the target class right after it's created
        me.onCreated(classReady, me, overriddenClassName);

        return me;
    };

    /**
     * Helper method which checks the override if it's suppports
     * the new 4.1 way to extend the product mask module.
     *
     * The method transforms all functions of the override
     * to a string and checks if one of the functions contains
     * the string "registerAdditionalTab".
     *
     * @param { Ext.Class } cls - The class which should be overridden
     * @param { Object } data - Object which contains all functions which should be
     *        merged into the class.
     * @returns { Boolean } Truthy if the override provides the new way, falsy if not.
     */
    Manager.checkOverride =  function(cls, data) {
        var match = true, fnName, fn;

        for(fnName in data) {
            fn = data[fnName].toString();
            if(fn.match(/createMainTabPanel/i)) {
                match = false;
                break;
            }
        }

        // ..alright, we found the old method to extend the product module,
        // now check if the developer supports the new way as well.
        if(!match) {
            for(fnName in data) {
                fn = data[fnName].toString();
                if(fn.match(/registerAdditionalTab/i)) {
                    match = true;
                    break;
                }
            }
        }

        return match;
    };
})(Ext.ClassManager, Ext.global);<|endoftext|>