<commit_before>var uri = require('url')
  , State = require('../state')
  , loadState = require('./load')
  , finishState = require('./complete')
  , failState = require('./completeError')
  , dispatch = require('../utils').dispatch
  , swizzleRedirect = require('../utils/swizzle-redirect')
  , swizzleRender = require('../utils/swizzle-render')
  , swizzleEnd = require('../utils/swizzle-end')
  , debug = require('debug')('flowstate');

module.exports = function(dispatcher, store, name, _rest) {
  var options = {}
    , completeState, completeErrorState;

  var fns = Array.prototype.slice.call(arguments, 3);
  if (typeof fns[fns.length - 1] == 'object') {
    options = fns.pop();
  }

  var actionMethods = options.actionMethods;

  //options.name = name;
  // TODO: Default `name` to req.path
  // set it on `req.stateName`, and check things there

  var state = dispatcher._states[name];

  if (options.external) {
    fns.unshift(initialize);
  } else {
    fns.unshift(loadState(store, name, options));
    fns.unshift(initialize);
  }

  if (state) {
    // TODO: test cases for this
    //fns.push.apply(fns, state.prompt);
    fns.push(require('./prompt')(state, actionMethods));
  }

  completeState = finishState(dispatcher, options);
  completeErrorState = failState(dispatcher, options);

  fns.push(completeState);
  fns.push(completeErrorState);

  // TODO: test case for this, both external and internal
  if (state && state.exit) {
    fns.push.apply(fns, state.exit);
  }

  function initialize(req, res, next) {
    // self-awareness
    if (req.state) {
      next();
      return;
    }

    // expose store
    req.stateStore = store;

    req.state = new State(req, { name: name }, undefined, options.external);

    /**
     * Trigger the prompt specified by `name`, with optional `options`.
     *
     * A flow can trigger a prompt, which will interact with the user.  For
     * example, a login flow might trigger multi-factor authentication in order
     * to step up the authentication to a higher degree of confidence.
     *
     * When triggered, the `begin` phase of the specified prompt will be
     * invoked.  The begin phase will typically redirect the user to an endpoint
     * which then displays the prompt and, subsequently, processes user input.
     * In some circumstances, the prompt may render a view directly; however,
     * redirects are recommended.
     *
     * Conceptually, a prompt is its own flow and the triggering flow is the
     * parent flow.  The parent flow is a flow which may trigger zero or more
     * subflows.
     *
     * Examples:
     *
     *     res.prompt('mfa', { method: 'otp' });
     *
     * @param {String} name
     * @param {Object} options
     * @api public
     */
    res.prompt = function prompt(name, options) {
      debug('prompt %s, %O (handle: %s, modified: %o, saved: %o)', name, req.state,
        req.state.handle, req.state.isModified(), req.state.isSaved());

      options = options || {};

      var nx;
      if (typeof name == 'function') {
        nx = name;
        name = undefined;
      }

      if (!name) {
        // reprompt existing state.
        var state = dispatcher._states[req.state.name]
        if (state && state.prompt) {
          dispatch(state.prompt)(null, req, res, function() {
            // TODO, error here, because no prompt is available

            console.log('PROMPT OVER?');
            nx();
          });
        } else {
          // TODO: Forcibly error, developer failuer.  Skip remaining middleware
        }
        return;
      }

      function proceed() {
        dispatcher.goto(name, options, req, res, function(err) {
          // TODO: Implement this
          //console.log('ERROR?')
          //console.log(err);
        });
      }

      if (req.state.isSaved()) {
        req.state = new State(req, { name: name, parent: req.state.handle });
        proceed();
      } else if (req.state.isNew() || req.state.isModified()) {
        req.state.save(function(err) {
          // TODO: error handling
          debug('saved state (%s)', req.state.handle);
          req.state = new State(req, { name: name, parent: req.state.handle });
          proceed();
        });
      } else {
        // TODO: set req.state here, without prev
        proceed();
      }
    };

    // swizzle redirect to commit the state
    swizzleRedirect(res, function commitState(url, cb) {
      debug('redirect %s, %O (handle: %s, modified: %o, saved: %o, complete: %o, external: %o)', url, req.state,
        req.state.handle, req.state.isModified(), req.state.isSaved(), req.state.isComplete(), req.state.isExternal());

      var qstate = (req.query && req.query.state) || (req.body && req.body.state);

      function proceed(h) {
        if (!h) { return cb(); }

        var loc = uri.parse(url, true);
        delete loc.search;
        loc.query.state = h;
        cb(null, uri.format(loc));
      }

      if (req.state.isComplete()) {
        proceed();
      } else if ((req.state.isNew() && req.state.isTouched()) || req.state.isModified() || req.state.isExternal()) {
        req.state.save(function(err) {
          // TODO: error handling
          debug('set `state` query parameter to saved state (%s)', req.state.handle);
          proceed(req.state.handle);
        });
      } else if (req.state.isNew()) {
        if (req.state.parent) {
          debug('set `state` query parameter to parent state (%s)', req.state.parent);
          proceed(req.state.parent);
        } else {
          debug('set `state` query parameter to query state (%s)', qstate);
          proceed(qstate);
        }
      } else { // current state
        debug('set `state` query parameter to current state (%s)', req.state.handle);
        proceed(req.state.handle);
      }
    });

    // swizzle render to commit the state
    swizzleRender(res, function commitState(cb) {
      debug('render %O (handle: %s, modified: %o, saved: %o, complete: %o, external: %o)', req.state,
        req.state.handle, req.state.isModified(), req.state.isSaved(), req.state.isComplete(), req.state.isExternal());

      var qstate = (req.query && req.query.state) || (req.body && req.body.state);

      if (req.state.isComplete()) {
        if (req.state.isKeeped()) {
          res.locals.state = req.state.handle;
        }
        cb();
      } else if ((req.state.isNew() && req.state.isTouched()) || req.state.isModified() || req.state.isExternal()) {
        req.state.save(function(err) {
          // TODO: error handling
          debug('set `res.locals.state` to saved state (%s)', req.state.handle);
          res.locals.state = req.state.handle;
          cb();
        });
      } else if (req.state.isNew()) {
        if (req.state.parent) {
          debug('set `res.locals.state` to parent state (%s)', req.state.parent);
          res.locals.state = req.state.parent;
        } else if (qstate) {
          debug('set `res.locals.state` to query state (%s)', qstate);
          res.locals.state = qstate;
        }
        cb();
      } else { // current state
        debug('set `res.locals.state` to current state (%s)', req.state.handle);
        res.locals.state = req.state.handle;
        cb();
      }
    });

    swizzleEnd(res, function autoDestroy(cb) {
      debug('end %O (handle: %s, complete: %o, keep: %o)', req.state,
        req.state.handle, req.state.isComplete(), req.state.isKeeped());

      // WIP: figure out how to maintain incomplete states
      // probably best to mandate external states be  flagged complete ??

      // TODO: also check fo unchanged?
      //if (!req.state.isNew()) {
      if (req.state.isComplete() && !req.state.isKeeped()) {
        debug('destroying complete state (%s)', req.state.handle);
        req.state.destroy(function(err) {
          //if (err) { return next(err); }
          //return proceed(state.parent, state);
          cb();
        });
      } else {
        cb();
      }
    });

    next();
  };

  return fns;
};
<commit_msg>Add error logging.<commit_after>var uri = require('url')
  , State = require('../state')
  , loadState = require('./load')
  , finishState = require('./complete')
  , failState = require('./completeError')
  , dispatch = require('../utils').dispatch
  , swizzleRedirect = require('../utils/swizzle-redirect')
  , swizzleRender = require('../utils/swizzle-render')
  , swizzleEnd = require('../utils/swizzle-end')
  , debug = require('debug')('flowstate');

module.exports = function(dispatcher, store, name, _rest) {
  var options = {}
    , completeState, completeErrorState;

  var fns = Array.prototype.slice.call(arguments, 3);
  if (typeof fns[fns.length - 1] == 'object') {
    options = fns.pop();
  }

  var actionMethods = options.actionMethods;

  //options.name = name;
  // TODO: Default `name` to req.path
  // set it on `req.stateName`, and check things there

  var state = dispatcher._states[name];

  if (options.external) {
    fns.unshift(initialize);
  } else {
    fns.unshift(loadState(store, name, options));
    fns.unshift(initialize);
  }

  if (state) {
    // TODO: test cases for this
    //fns.push.apply(fns, state.prompt);
    fns.push(require('./prompt')(state, actionMethods));
  }

  completeState = finishState(dispatcher, options);
  completeErrorState = failState(dispatcher, options);

  fns.push(completeState);
  fns.push(completeErrorState);

  // TODO: test case for this, both external and internal
  if (state && state.exit) {
    fns.push.apply(fns, state.exit);
  }

  function initialize(req, res, next) {
    // self-awareness
    if (req.state) {
      next();
      return;
    }

    // expose store
    req.stateStore = store;

    req.state = new State(req, { name: name }, undefined, options.external);

    /**
     * Trigger the prompt specified by `name`, with optional `options`.
     *
     * A flow can trigger a prompt, which will interact with the user.  For
     * example, a login flow might trigger multi-factor authentication in order
     * to step up the authentication to a higher degree of confidence.
     *
     * When triggered, the `begin` phase of the specified prompt will be
     * invoked.  The begin phase will typically redirect the user to an endpoint
     * which then displays the prompt and, subsequently, processes user input.
     * In some circumstances, the prompt may render a view directly; however,
     * redirects are recommended.
     *
     * Conceptually, a prompt is its own flow and the triggering flow is the
     * parent flow.  The parent flow is a flow which may trigger zero or more
     * subflows.
     *
     * Examples:
     *
     *     res.prompt('mfa', { method: 'otp' });
     *
     * @param {String} name
     * @param {Object} options
     * @api public
     */
    res.prompt = function prompt(name, options) {
      debug('prompt %s, %O (handle: %s, modified: %o, saved: %o)', name, req.state,
        req.state.handle, req.state.isModified(), req.state.isSaved());

      options = options || {};

      var nx;
      if (typeof name == 'function') {
        nx = name;
        name = undefined;
      }

      if (!name) {
        // reprompt existing state.
        var state = dispatcher._states[req.state.name]
        if (state && state.prompt) {
          dispatch(state.prompt)(null, req, res, function(err) {
            // TODO, error here, because no prompt is available

            console.log('PROMPT OVER?');
            if (err) {
              console.log(err);
              return;
            }
            nx();
          });
        } else {
          // TODO: Forcibly error, developer failuer.  Skip remaining middleware
        }
        return;
      }

      function proceed() {
        dispatcher.goto(name, options, req, res, function(err) {
          // TODO: Implement this
          //console.log('ERROR?')
          //console.log(err);
        });
      }

      if (req.state.isSaved()) {
        req.state = new State(req, { name: name, parent: req.state.handle });
        proceed();
      } else if (req.state.isNew() || req.state.isModified()) {
        req.state.save(function(err) {
          // TODO: error handling
          debug('saved state (%s)', req.state.handle);
          req.state = new State(req, { name: name, parent: req.state.handle });
          proceed();
        });
      } else {
        // TODO: set req.state here, without prev
        proceed();
      }
    };

    // swizzle redirect to commit the state
    swizzleRedirect(res, function commitState(url, cb) {
      debug('redirect %s, %O (handle: %s, modified: %o, saved: %o, complete: %o, external: %o)', url, req.state,
        req.state.handle, req.state.isModified(), req.state.isSaved(), req.state.isComplete(), req.state.isExternal());

      var qstate = (req.query && req.query.state) || (req.body && req.body.state);

      function proceed(h) {
        if (!h) { return cb(); }

        var loc = uri.parse(url, true);
        delete loc.search;
        loc.query.state = h;
        cb(null, uri.format(loc));
      }

      if (req.state.isComplete()) {
        proceed();
      } else if ((req.state.isNew() && req.state.isTouched()) || req.state.isModified() || req.state.isExternal()) {
        req.state.save(function(err) {
          // TODO: error handling
          debug('set `state` query parameter to saved state (%s)', req.state.handle);
          proceed(req.state.handle);
        });
      } else if (req.state.isNew()) {
        if (req.state.parent) {
          debug('set `state` query parameter to parent state (%s)', req.state.parent);
          proceed(req.state.parent);
        } else {
          debug('set `state` query parameter to query state (%s)', qstate);
          proceed(qstate);
        }
      } else { // current state
        debug('set `state` query parameter to current state (%s)', req.state.handle);
        proceed(req.state.handle);
      }
    });

    // swizzle render to commit the state
    swizzleRender(res, function commitState(cb) {
      debug('render %O (handle: %s, modified: %o, saved: %o, complete: %o, external: %o)', req.state,
        req.state.handle, req.state.isModified(), req.state.isSaved(), req.state.isComplete(), req.state.isExternal());

      var qstate = (req.query && req.query.state) || (req.body && req.body.state);

      if (req.state.isComplete()) {
        if (req.state.isKeeped()) {
          res.locals.state = req.state.handle;
        }
        cb();
      } else if ((req.state.isNew() && req.state.isTouched()) || req.state.isModified() || req.state.isExternal()) {
        req.state.save(function(err) {
          // TODO: error handling
          debug('set `res.locals.state` to saved state (%s)', req.state.handle);
          res.locals.state = req.state.handle;
          cb();
        });
      } else if (req.state.isNew()) {
        if (req.state.parent) {
          debug('set `res.locals.state` to parent state (%s)', req.state.parent);
          res.locals.state = req.state.parent;
        } else if (qstate) {
          debug('set `res.locals.state` to query state (%s)', qstate);
          res.locals.state = qstate;
        }
        cb();
      } else { // current state
        debug('set `res.locals.state` to current state (%s)', req.state.handle);
        res.locals.state = req.state.handle;
        cb();
      }
    });

    swizzleEnd(res, function autoDestroy(cb) {
      debug('end %O (handle: %s, complete: %o, keep: %o)', req.state,
        req.state.handle, req.state.isComplete(), req.state.isKeeped());

      // WIP: figure out how to maintain incomplete states
      // probably best to mandate external states be  flagged complete ??

      // TODO: also check fo unchanged?
      //if (!req.state.isNew()) {
      if (req.state.isComplete() && !req.state.isKeeped()) {
        debug('destroying complete state (%s)', req.state.handle);
        req.state.destroy(function(err) {
          //if (err) { return next(err); }
          //return proceed(state.parent, state);
          cb();
        });
      } else {
        cb();
      }
    });

    next();
  };

  return fns;
};
<|endoftext|>


<commit_before>const path = require('path');
const webpack = require('webpack');
const { CheckerPlugin } = require('awesome-typescript-loader');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');

const util = require('./util');

module.exports = {
  entry: {
    polyfills: util.root('src/polyfills.ts'),
    vendor: util.root('src/vendor.ts'),
    app: util.root('src/main.ts')
  },
  // stats: { // 定制日志
  //   colors: true,
  //   hash: false,
  //   timings: true,
  //   chunks: true,
  //   chunkModules: false,
  //   modules: false,
  //   children: false,
  //   version: true,
  //   cached: true,
  //   cachedAssets: true,
  //   reasons: false,
  //   source: false,
  //   errorDetails: false
  // },
  stats: 'minimal',
  output: {
    path: util.root('dist'),
    filename: '[name].js'
  },
  resolve: {
    extensions: ['.ts', '.js']
  },
  module: {
    rules: [
      {
        test: /.ts$/, use: [
          { loader: 'awesome-typescript-loader', options: { configFileName: util.root('tsconfig.json') } },
          'angular2-template-loader'
        ], exclude: /node_modules/
      },
      { test: /\.html$/, use: 'html-loader' },
      { test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/, loader: 'file-loader?name=assets/[name].[hash].[ext]' },
      { test: /\.css$/, use: ExtractTextPlugin.extract({ use: 'css-loader' }) },
      { test: /\.styl$/, use: ExtractTextPlugin.extract({ use: 'css-loader!stylus-loader' }) }
    ]
  },
  plugins: [
    new CheckerPlugin(),
    new webpack.optimize.CommonsChunkPlugin({
      name: ['app', 'vendor', 'polyfills']
    }),
    new HtmlWebpackPlugin({
      template: util.root('src/index.html')
    })
  ]
};
<commit_msg>change stats<commit_after>const path = require('path');
const webpack = require('webpack');
const { CheckerPlugin } = require('awesome-typescript-loader');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');

const util = require('./util');

module.exports = {
  entry: {
    polyfills: util.root('src/polyfills.ts'),
    vendor: util.root('src/vendor.ts'),
    app: util.root('src/main.ts')
  },
  stats: { // 定制日志
    colors: true,
    hash: false,
    timings: true,
    chunks: true,
    chunkModules: false,
    modules: false,
    children: false,
    version: true,
    cached: true,
    cachedAssets: true,
    reasons: false,
    source: false,
    errorDetails: false
  },
  // stats: 'minimal',
  output: {
    path: util.root('dist'),
    filename: '[name].js'
  },
  resolve: {
    extensions: ['.ts', '.js']
  },
  module: {
    rules: [
      {
        test: /.ts$/, use: [
          { loader: 'awesome-typescript-loader', options: { configFileName: util.root('tsconfig.json') } },
          'angular2-template-loader'
        ], exclude: /node_modules/
      },
      { test: /\.html$/, use: 'html-loader' },
      { test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/, loader: 'file-loader?name=assets/[name].[hash].[ext]' },
      { test: /\.css$/, use: ExtractTextPlugin.extract({ use: 'css-loader' }) },
      { test: /\.styl$/, use: ExtractTextPlugin.extract({ use: 'css-loader!stylus-loader' }) }
    ]
  },
  plugins: [
    new CheckerPlugin(),
    new webpack.optimize.CommonsChunkPlugin({
      name: ['app', 'vendor', 'polyfills']
    }),
    new HtmlWebpackPlugin({
      template: util.root('src/index.html')
    })
  ]
};
<|endoftext|>


<commit_before>import {Should, expect, assert} from "chai"
import ASTVisitor from "../../../src/visitors/ASTVisitor"
import ProgramVisitor from "../../../src/visitors/ProgramVisitor"

Should()

/** @test {ProgramVisitor} */
describe("ProgramVisitor", () => {
  let visitor
  beforeEach(() => visitor = new ProgramVisitor())

  it('is a subclass of ASTVisitor', () => {
    ProgramVisitor.prototype.should.be.an.instanceof(ASTVisitor)
  })

  it("creates an instance of ProgramVisitor", () => visitor.should.be.an.instanceOf(ProgramVisitor))

  /** @test {ProgramVisitor#constructor} */
  describe("#constructor", () => {
    let visitor
    beforeEach(() => visitor = new ProgramVisitor())

    it("populates ~nestedVisitors", () => {
      const One = {}, Two = {}, nestedVisitors = { One, Two }

      visitor = new ProgramVisitor(nestedVisitors)
      visitor.nestedVisitors.should.be.ok.and.not.be.equal(nestedVisitors)
      visitor.nestedVisitors.One.should.be.equal(One)
      visitor.nestedVisitors.Two.should.be.equal(Two)
    })

    it("creates the ~state property", () => {
      visitor.state.should.be.ok
      visitor.state.should.be.an.instanceof(Object)
    })
  })

  describe("~state", () => {
    let visitor
    const properties = {
      "name"    : String,
      "path"    : String,
      "imports" : Array,
      "exports" : Array,
    }
    beforeEach(() => visitor = new ProgramVisitor())

    Object.keys(properties).forEach((name) => {
      const type = properties[name]
      it(`has '${name}' x of type '${type.name}'`, () => {
        let value = visitor.state[name]
        assert((value instanceof type) || (typeof(value) === type.name.toLowerCase()))
      })
    })

  })

  /** @test {ProgramVisitor#enter} */
  describe("#enter(path, state)", () => {
    let visitor
    beforeEach(() => visitor = new ProgramVisitor())

    it('sets the ~state.name property')

    it('sets the ~state.path property')

    it('traverses nested visitors')
  })

  describe("#exit(path, state)", () => {
    it('patches the AST')
  })

})
<commit_msg>#3 added pending tests<commit_after>import {Should, expect, assert} from "chai"
import ASTVisitor from "../../../src/visitors/ASTVisitor"
import ProgramVisitor from "../../../src/visitors/ProgramVisitor"

Should()

/** @test {ProgramVisitor} */
describe("ProgramVisitor", () => {
  let visitor
  beforeEach(() => visitor = new ProgramVisitor())

  it('is a subclass of ASTVisitor', () => {
    ProgramVisitor.prototype.should.be.an.instanceof(ASTVisitor)
  })

  it("creates an instance of ProgramVisitor", () => visitor.should.be.an.instanceOf(ProgramVisitor))

  /** @test {ProgramVisitor#constructor} */
  describe("#constructor", () => {
    let visitor
    beforeEach(() => visitor = new ProgramVisitor())

    it("populates ~nestedVisitors", () => {
      const One = {}, Two = {}, nestedVisitors = { One, Two }

      visitor = new ProgramVisitor(nestedVisitors)
      visitor.nestedVisitors.should.be.ok.and.not.be.equal(nestedVisitors)
      visitor.nestedVisitors.One.should.be.equal(One)
      visitor.nestedVisitors.Two.should.be.equal(Two)
    })

    it("creates the ~state property", () => {
      visitor.state.should.be.ok
      visitor.state.should.be.an.instanceof(Object)
    })
  })

  describe("~state", () => {
    let visitor
    const properties = {
      "name"    : String,
      "absPath" : String,
      "relPath" : String,
      "imports" : Array,
      "exports" : Array,
    }
    beforeEach(() => visitor = new ProgramVisitor())

    Object.keys(properties).forEach((name) => {
      const type = properties[name]
      it(`has '${name}' x of type '${type.name}'`, () => {
        let value = visitor.state[name]
        assert((value instanceof type) || (typeof(value) === type.name.toLowerCase()))
      })
    })

  })

  /** @test {ProgramVisitor#enter} */
  describe("#enter(path, state)", () => {
    let visitor
    beforeEach(() => visitor = new ProgramVisitor())

    it('sets the ~state.name property')

    it('sets the ~state.absPath property')

    it('sets the ~state.relPath property')

    it('initializes the ~state.import property')

    it('initializes the ~state.exports property')

    it('traverses nested visitors')

    it('passes the state to nested visitors while traversing')
  })

  describe("#exit(path, state)", () => {
    it('patches the AST')
  })

})
<|endoftext|>


<commit_before>import isVoid from '../util/isVoid';
import isStringOrNumber from '../util/isStringOrNumber';
import isSVGElement from '../util/isSVGElement';
import isMathMLElement from '../util/isMathMLElement';
import createRootNodeWithDynamicText from './shapes/rootNodeWithDynamicText';
import createNodeWithDynamicText from './shapes/nodeWithDynamicText';
import createRootNodeWithStaticChild from './shapes/rootNodeWithStaticChild';
import createNodeWithStaticChild from './shapes/nodeWithStaticChild';
import createRootNodeWithDynamicChild from './shapes/rootNodeWithDynamicChild';
import createNodeWithDynamicChild from './shapes/nodeWithDynamicChild';
import createRootNodeWithDynamicSubTreeForChildren from './shapes/rootNodeWithDynamicSubTreeForChildren';
import createNodeWithDynamicSubTreeForChildren from './shapes/nodeWithDynamicSubTreeForChildren';
import createDynamicNode from './shapes/dynamicNode';
import createRootNodeWithComponent from './shapes/rootNodeWithComponent';
import createNodeWithComponent from './shapes/nodeWithComponent';
import createRootDynamicTextNode from './shapes/rootDynamicTextNode';
import { ObjectTypes, getDynamicNode } 	from '../core/variables';
import isArray from '../util/isArray';
import { addDOMStaticAttributes } from './addAttributes';
import { isRecyclingEnabled } from './recycling';
import createRootVoidNode from './shapes/rootVoidNode';
import createVoidNode from './shapes/voidNode';

function createElement(schema, domNamespace, parentNode) {
	const MathNamespace = 'http://www.w3.org/1998/Math/MathML';
	const SVGNamespace = 'http://www.w3.org/2000/svg';
	const nodeName = schema && typeof schema.tag === 'string' && schema.tag.toLowerCase();
	const is = schema.attrs && schema.attrs.is;

	let templateNode;

	if (domNamespace === undefined) {
		if (schema.attrs && schema.attrs.xmlns) {
			domNamespace = schema.attrs.xmlns;
		} else {
			switch (nodeName) {
				case 'svg':
					domNamespace = SVGNamespace;
					break;
				case 'math':
					domNamespace = MathNamespace;
					break;
				default:
					// Edge case. In case a namespace element are wrapped inside a non-namespace element, it will inherit wrong namespace.
					// E.g.
<svg><svg>
 - will not work
					if (parentNode) { // only used by static children
						// check only for top-level element for both mathML and SVG
						if (nodeName === 'svg' && (parentNode.namespaceURI !== SVGNamespace)) {
							domNamespace = SVGNamespace;
						} else if (nodeName === 'math' && (parentNode.namespaceURI !== MathNamespace)) {
							domNamespace = MathNamespace;
						}
					} else if (isSVGElement(nodeName)) {
						domNamespace = SVGNamespace;
					} else if (isMathMLElement(nodeName)) {
						domNamespace = MathNamespace;
					}
			}
		}
	}
	templateNode = domNamespace ?
		is ?
			document.createElementNS(domNamespace, nodeName, is) :
			document.createElementNS(domNamespace, nodeName) :
		is ?
			document.createElement(nodeName, is) :
			document.createElement(nodeName);

	return {
		namespace: domNamespace,
		node: templateNode
	};
}

const recyclingEnabled = isRecyclingEnabled();
if (process.env.NODE_ENV !== 'production') {
	const invalidTemplateError = 'Inferno Error: A valid template node must be returned. You may have returned undefined, an array or some other invalid object.';
}

function createStaticAttributes(node, domNode, excludeAttrs) {
	const attrs = node.attrs;

	if (!isVoid(attrs)) {
		if (excludeAttrs) {
			const newAttrs = { ...attrs };

			for (const attr in excludeAttrs) {
				if (newAttrs[attr]) {
					delete newAttrs[attr];
				}
			}
			addDOMStaticAttributes(node, domNode, newAttrs);
		} else {
			addDOMStaticAttributes(node, domNode, attrs);
		}
	}
}

function createStaticTreeChildren(children, parentNode, domNamespace) {
	if (isArray(children)) {
		for (let i = 0; i < children.length; i++) {
			const childItem = children[i];

			if (isStringOrNumber(childItem)) {
				const textNode = document.createTextNode(childItem);

				parentNode.appendChild(textNode);
			} else {
				createStaticTreeNode(childItem, parentNode, domNamespace);
			}
		}
	} else {
		if (isStringOrNumber(children)) {
			parentNode.textContent = children;
		} else {
			createStaticTreeNode(children, parentNode, domNamespace);
		}
	}
}

function createStaticTreeNode(node, parentNode, domNamespace) {
	let staticNode;

	if (!isVoid(node)) {
		if (isStringOrNumber(node)) {
			staticNode = document.createTextNode(node);
		} else {
			const tag = node.tag;

			if (tag) {
				const Element = createElement(node, domNamespace, parentNode);

				staticNode = Element.node;
				domNamespace = Element.namespace;
				const text = node.text;
				const children = node.children;

				if (!isVoid(text)) {
					if (process.env.NODE_ENV !== 'production') {
						if (!isVoid(children)) {
							throw Error(invalidTemplateError);
						}
						if (!isStringOrNumber(text)) {
							throw Error('Inferno Error: Template nodes with TEXT must only have a StringLiteral or NumericLiteral as a value, this is intended for low-level optimisation purposes.');
						}
					}
					staticNode.textContent = text;
				} else {
					if (!isVoid(children)) {
						createStaticTreeChildren(children, staticNode, domNamespace);
					}
				}
				createStaticAttributes(node, staticNode);
			} else if (node.text) {
				staticNode = document.createTextNode(node.text);
			}
		}
		if (process.env.NODE_ENV !== 'production') {
			if (staticNode === undefined) {
				throw Error(invalidTemplateError);
			}
		}
		if (parentNode === null) {
			return staticNode;
		} else {
			parentNode.appendChild(staticNode);
		}
	}
}

export default function createDOMTree(schema, isRoot, dynamicNodes, domNamespace) {
	if (process.env.NODE_ENV !== 'production') {
		if (isVoid(schema)) {
			throw Error(invalidTemplateError);
		}
		if (isArray(schema)) {
			throw Error(invalidTemplateError);
		}
	}

	const dynamicFlags = getDynamicNode(dynamicNodes, schema);
	let node;
	let templateNode;

	if (!dynamicFlags) {
		templateNode = createStaticTreeNode(schema, null, domNamespace, schema);
		if (process.env.NODE_ENV !== 'production') {
			if (!templateNode) {
				throw Error(invalidTemplateError);
			}
		}
		if (isRoot) {
			node = createRootVoidNode(templateNode, null, recyclingEnabled, true);
		} else {
			node = createVoidNode(templateNode, true);
		}
	} else {
		if (dynamicFlags.NODE === true) {
			node = createDynamicNode(schema.index, domNamespace);
		} else {
			const tag = schema.tag;
			const text = schema.text;

			if (tag) {
				if (tag.index !== undefined) {
					const lastAttrs = schema.attrs;
					const attrs = { ...lastAttrs };
					const children = schema.children;

					if (children) {
						if (isArray(children)) {
							if (children.length > 1) {
								attrs.children = [];
								for (let i = 0; i < children.length; i++) {
									const childNode = children[i];

									attrs.children.push(createDOMTree(childNode, false, dynamicNodes, domNamespace));
								}
							} else if (children.length === 1) {
								attrs.children = createDOMTree(children[0], false, dynamicNodes, domNamespace);
							}
						} else {
							attrs.children = createDOMTree(children, false, dynamicNodes, domNamespace);
						}
					}
					if (isRoot) {
						return createRootNodeWithComponent(tag.index, attrs, children, domNamespace, recyclingEnabled);
					} else {
						return createNodeWithComponent(tag.index, attrs, children, domNamespace);
					}
				}
				templateNode = createElement(schema, domNamespace, null).node;

				const attrs = schema.attrs;
				let dynamicAttrs = null;

				if (!isVoid(attrs)) {
					if (dynamicFlags.ATTRS === true) {
						dynamicAttrs = attrs;
					} else if (dynamicFlags.ATTRS !== false) {
						dynamicAttrs = dynamicFlags.ATTRS;
						createStaticAttributes(schema, templateNode, dynamicAttrs);
					} else {
						createStaticAttributes(schema, templateNode);
					}
				}
				const children = schema.children;

				if (!isVoid(text)) {
					if (process.env.NODE_ENV !== 'production') {
						if (!isVoid(children)) {
							throw Error('Inferno Error: Template nodes cannot contain both TEXT and a CHILDREN properties, they must only use one or the other.');
						}
					}
					if (dynamicFlags.TEXT === true) {
						if (isRoot) {
							node = createRootNodeWithDynamicText(templateNode, text.index, dynamicAttrs, recyclingEnabled);
						} else {
							node = createNodeWithDynamicText(templateNode, text.index, dynamicAttrs);
						}
					} else {
						if (isStringOrNumber(text)) {
							templateNode.textContent = text;
						} else {
							if (process.env.NODE_ENV !== 'production') {
								throw Error('Inferno Error: Template nodes with TEXT must only have a StringLiteral or NumericLiteral as a value, this is intended for low-level optimisation purposes.');
							}
						}
						if (isRoot) {
							node = createRootNodeWithStaticChild(templateNode, dynamicAttrs, recyclingEnabled);
						} else {
							node = createNodeWithStaticChild(templateNode, dynamicAttrs);
						}
					}
				} else {
					if (!isVoid(children)) {
						if (children.index !== undefined) {
							if (isRoot) {
								node = createRootNodeWithDynamicChild(
									templateNode, children.index, dynamicAttrs, recyclingEnabled
								);
							} else {
								node = createNodeWithDynamicChild(
									templateNode, children.index, dynamicAttrs
								);
							}
						} else if (dynamicFlags.CHILDREN === true) {
							let subTreeForChildren = [];

							if (typeof children === 'object') {
								if (isArray(children)) {
									for (let i = 0; i < children.length; i++) {
										const childItem = children[i];

										subTreeForChildren.push(createDOMTree(childItem, false, dynamicNodes));
									}
								} else {
									subTreeForChildren = createDOMTree(children, false, dynamicNodes);
								}
							}
							if (isRoot) {
								node = createRootNodeWithDynamicSubTreeForChildren(
									templateNode, subTreeForChildren, dynamicAttrs, recyclingEnabled
								);
							} else {
								node = createNodeWithDynamicSubTreeForChildren(
									templateNode, subTreeForChildren, dynamicAttrs
								);
							}
						} else if (isStringOrNumber(children)) {
							templateNode.textContent = children;
							if (isRoot) {
								node = createRootNodeWithStaticChild(templateNode, dynamicAttrs, recyclingEnabled);
							} else {
								node = createNodeWithStaticChild(templateNode, dynamicAttrs);
							}
						} else {
							const childNodeDynamicFlags = getDynamicNode(dynamicNodes, children);

							if (childNodeDynamicFlags === undefined) {
								createStaticTreeChildren(children, templateNode);

								if (isRoot) {
									node = createRootNodeWithStaticChild(templateNode, dynamicAttrs, recyclingEnabled);
								} else {
									node = createNodeWithStaticChild(templateNode, dynamicAttrs);
								}
							}
						}
					} else {
						if (isRoot) {
							node = createRootVoidNode(templateNode, dynamicAttrs, recyclingEnabled, false);
						} else {
							node = createVoidNode(templateNode, dynamicAttrs, false);
						}
					}
				}
			} else if (text) {
				node = createRootDynamicTextNode(document.createTextNode(''), text.index);
			}
		}
	}
	return node;
}
<commit_msg>quick fix<commit_after>import isVoid from '../util/isVoid';
import isStringOrNumber from '../util/isStringOrNumber';
import isSVGElement from '../util/isSVGElement';
import isMathMLElement from '../util/isMathMLElement';
import createRootNodeWithDynamicText from './shapes/rootNodeWithDynamicText';
import createNodeWithDynamicText from './shapes/nodeWithDynamicText';
import createRootNodeWithStaticChild from './shapes/rootNodeWithStaticChild';
import createNodeWithStaticChild from './shapes/nodeWithStaticChild';
import createRootNodeWithDynamicChild from './shapes/rootNodeWithDynamicChild';
import createNodeWithDynamicChild from './shapes/nodeWithDynamicChild';
import createRootNodeWithDynamicSubTreeForChildren from './shapes/rootNodeWithDynamicSubTreeForChildren';
import createNodeWithDynamicSubTreeForChildren from './shapes/nodeWithDynamicSubTreeForChildren';
import createDynamicNode from './shapes/dynamicNode';
import createRootNodeWithComponent from './shapes/rootNodeWithComponent';
import createNodeWithComponent from './shapes/nodeWithComponent';
import createRootDynamicTextNode from './shapes/rootDynamicTextNode';
import { ObjectTypes, getDynamicNode } 	from '../core/variables';
import isArray from '../util/isArray';
import { addDOMStaticAttributes } from './addAttributes';
import { isRecyclingEnabled } from './recycling';
import createRootVoidNode from './shapes/rootVoidNode';
import createVoidNode from './shapes/voidNode';

function createElement(schema, domNamespace, parentNode) {
	const MathNamespace = 'http://www.w3.org/1998/Math/MathML';
	const SVGNamespace = 'http://www.w3.org/2000/svg';
	const nodeName = schema && typeof schema.tag === 'string' && schema.tag.toLowerCase();
	const is = schema.attrs && schema.attrs.is;

	let templateNode;

	if (domNamespace === undefined) {
		if (schema.attrs && schema.attrs.xmlns) {
			domNamespace = schema.attrs.xmlns;
		} else {
			switch (nodeName) {
				case 'svg':
					domNamespace = SVGNamespace;
					break;
				case 'math':
					domNamespace = MathNamespace;
					break;
				default:
					// Edge case. In case a namespace element are wrapped inside a non-namespace element, it will inherit wrong namespace.
					// E.g.
<svg><svg>
 - will not work
					if (parentNode) { // only used by static children
						// check only for top-level element for both mathML and SVG
						if (nodeName === 'svg' && (parentNode.namespaceURI !== SVGNamespace)) {
							domNamespace = SVGNamespace;
						} else if (nodeName === 'math' && (parentNode.namespaceURI !== MathNamespace)) {
							domNamespace = MathNamespace;
						}
					} else if (isSVGElement(nodeName)) {
						domNamespace = SVGNamespace;
					} else if (isMathMLElement(nodeName)) {
						domNamespace = MathNamespace;
					}
			}
		}
	}
	templateNode = domNamespace ?
		is ?
			document.createElementNS(domNamespace, nodeName, is) :
			document.createElementNS(domNamespace, nodeName) :
		is ?
			document.createElement(nodeName, is) :
			document.createElement(nodeName);

	const isSVG = domNamespace === SVGNamespace;

	return {
		isSVG: isSVG,
		namespace: domNamespace,
		node: templateNode
	};
}

const recyclingEnabled = isRecyclingEnabled();
if (process.env.NODE_ENV !== 'production') {
	const invalidTemplateError = 'Inferno Error: A valid template node must be returned. You may have returned undefined, an array or some other invalid object.';
}

function createStaticAttributes(node, domNode, excludeAttrs) {
	const attrs = node.attrs;

	if (!isVoid(attrs)) {
		if (excludeAttrs) {
			const newAttrs = { ...attrs };

			for (const attr in excludeAttrs) {
				if (newAttrs[attr]) {
					delete newAttrs[attr];
				}
			}
			addDOMStaticAttributes(node, domNode, newAttrs);
		} else {
			addDOMStaticAttributes(node, domNode, attrs);
		}
	}
}

function createStaticTreeChildren(children, parentNode, domNamespace) {
	if (isArray(children)) {
		for (let i = 0; i < children.length; i++) {
			const childItem = children[i];

			if (isStringOrNumber(childItem)) {
				const textNode = document.createTextNode(childItem);

				parentNode.appendChild(textNode);
			} else {
				createStaticTreeNode(childItem, parentNode, domNamespace);
			}
		}
	} else {
		if (isStringOrNumber(children)) {
			parentNode.textContent = children;
		} else {
			createStaticTreeNode(children, parentNode, domNamespace);
		}
	}
}

function createStaticTreeNode(node, parentNode, domNamespace) {
	let staticNode;

	if (!isVoid(node)) {
		if (isStringOrNumber(node)) {
			staticNode = document.createTextNode(node);
		} else {
			const tag = node.tag;

			if (tag) {
				const Element = createElement(node, domNamespace, parentNode);

				staticNode = Element.node;
				domNamespace = Element.namespace;
				const text = node.text;
				const children = node.children;

				if (!isVoid(text)) {
					if (process.env.NODE_ENV !== 'production') {
						if (!isVoid(children)) {
							throw Error(invalidTemplateError);
						}
						if (!isStringOrNumber(text)) {
							throw Error('Inferno Error: Template nodes with TEXT must only have a StringLiteral or NumericLiteral as a value, this is intended for low-level optimisation purposes.');
						}
					}
					staticNode.textContent = text;
				} else {
					if (!isVoid(children)) {
						createStaticTreeChildren(children, staticNode, domNamespace);
					}
				}
				createStaticAttributes(node, staticNode);
			} else if (node.text) {
				staticNode = document.createTextNode(node.text);
			}
		}
		if (process.env.NODE_ENV !== 'production') {
			if (staticNode === undefined) {
				throw Error(invalidTemplateError);
			}
		}
		if (parentNode === null) {
			return staticNode;
		} else {
			parentNode.appendChild(staticNode);
		}
	}
}

export default function createDOMTree(schema, isRoot, dynamicNodes, domNamespace) {
	if (process.env.NODE_ENV !== 'production') {
		if (isVoid(schema)) {
			throw Error(invalidTemplateError);
		}
		if (isArray(schema)) {
			throw Error(invalidTemplateError);
		}
	}

	const dynamicFlags = getDynamicNode(dynamicNodes, schema);
	let node;
	let templateNode;

	if (!dynamicFlags) {
		templateNode = createStaticTreeNode(schema, null, domNamespace, schema);
		if (process.env.NODE_ENV !== 'production') {
			if (!templateNode) {
				throw Error(invalidTemplateError);
			}
		}
		if (isRoot) {
			node = createRootVoidNode(templateNode, null, recyclingEnabled, true);
		} else {
			node = createVoidNode(templateNode, true);
		}
	} else {
		if (dynamicFlags.NODE === true) {
			node = createDynamicNode(schema.index, domNamespace);
		} else {
			const tag = schema.tag;
			const text = schema.text;

			if (tag) {
				if (tag.index !== undefined) {
					const lastAttrs = schema.attrs;
					const attrs = { ...lastAttrs };
					const children = schema.children;

					if (children) {
						if (isArray(children)) {
							if (children.length > 1) {
								attrs.children = [];
								for (let i = 0; i < children.length; i++) {
									const childNode = children[i];

									attrs.children.push(createDOMTree(childNode, false, dynamicNodes, domNamespace));
								}
							} else if (children.length === 1) {
								attrs.children = createDOMTree(children[0], false, dynamicNodes, domNamespace);
							}
						} else {
							attrs.children = createDOMTree(children, false, dynamicNodes, domNamespace);
						}
					}
					if (isRoot) {
						return createRootNodeWithComponent(tag.index, attrs, children, domNamespace, recyclingEnabled);
					} else {
						return createNodeWithComponent(tag.index, attrs, children, domNamespace);
					}
				}
				templateNode = createElement(schema, domNamespace, null).node;

				const attrs = schema.attrs;
				let dynamicAttrs = null;

				if (!isVoid(attrs)) {
					if (dynamicFlags.ATTRS === true) {
						dynamicAttrs = attrs;
					} else if (dynamicFlags.ATTRS !== false) {
						dynamicAttrs = dynamicFlags.ATTRS;
						createStaticAttributes(schema, templateNode, dynamicAttrs);
					} else {
						createStaticAttributes(schema, templateNode);
					}
				}
				const children = schema.children;

				if (!isVoid(text)) {
					if (process.env.NODE_ENV !== 'production') {
						if (!isVoid(children)) {
							throw Error('Inferno Error: Template nodes cannot contain both TEXT and a CHILDREN properties, they must only use one or the other.');
						}
					}
					if (dynamicFlags.TEXT === true) {
						if (isRoot) {
							node = createRootNodeWithDynamicText(templateNode, text.index, dynamicAttrs, recyclingEnabled);
						} else {
							node = createNodeWithDynamicText(templateNode, text.index, dynamicAttrs);
						}
					} else {
						if (isStringOrNumber(text)) {
							templateNode.textContent = text;
						} else {
							if (process.env.NODE_ENV !== 'production') {
								throw Error('Inferno Error: Template nodes with TEXT must only have a StringLiteral or NumericLiteral as a value, this is intended for low-level optimisation purposes.');
							}
						}
						if (isRoot) {
							node = createRootNodeWithStaticChild(templateNode, dynamicAttrs, recyclingEnabled);
						} else {
							node = createNodeWithStaticChild(templateNode, dynamicAttrs);
						}
					}
				} else {
					if (!isVoid(children)) {
						if (children.index !== undefined) {
							if (isRoot) {
								node = createRootNodeWithDynamicChild(
									templateNode, children.index, dynamicAttrs, recyclingEnabled
								);
							} else {
								node = createNodeWithDynamicChild(
									templateNode, children.index, dynamicAttrs
								);
							}
						} else if (dynamicFlags.CHILDREN === true) {
							let subTreeForChildren = [];

							if (typeof children === 'object') {
								if (isArray(children)) {
									for (let i = 0; i < children.length; i++) {
										const childItem = children[i];

										subTreeForChildren.push(createDOMTree(childItem, false, dynamicNodes));
									}
								} else {
									subTreeForChildren = createDOMTree(children, false, dynamicNodes);
								}
							}
							if (isRoot) {
								node = createRootNodeWithDynamicSubTreeForChildren(
									templateNode, subTreeForChildren, dynamicAttrs, recyclingEnabled
								);
							} else {
								node = createNodeWithDynamicSubTreeForChildren(
									templateNode, subTreeForChildren, dynamicAttrs
								);
							}
						} else if (isStringOrNumber(children)) {
							templateNode.textContent = children;
							if (isRoot) {
								node = createRootNodeWithStaticChild(templateNode, dynamicAttrs, recyclingEnabled);
							} else {
								node = createNodeWithStaticChild(templateNode, dynamicAttrs);
							}
						} else {
							const childNodeDynamicFlags = getDynamicNode(dynamicNodes, children);

							if (childNodeDynamicFlags === undefined) {
								createStaticTreeChildren(children, templateNode);

								if (isRoot) {
									node = createRootNodeWithStaticChild(templateNode, dynamicAttrs, recyclingEnabled);
								} else {
									node = createNodeWithStaticChild(templateNode, dynamicAttrs);
								}
							}
						}
					} else {
						if (isRoot) {
							node = createRootVoidNode(templateNode, dynamicAttrs, recyclingEnabled, false);
						} else {
							node = createVoidNode(templateNode, dynamicAttrs, false);
						}
					}
				}
			} else if (text) {
				node = createRootDynamicTextNode(document.createTextNode(''), text.index);
			}
		}
	}
	return node;
}
<|endoftext|>


<commit_before>"use strict";

/**
 * Module Container, Manager, Factory, whatever. All module related thingz must belong to us.
 *
 * Todos:
 *
 * * scan the local module folder for web component descriptors as resources.
 * * download module descriptors from remote web component hosts as resources.
 * * provide web component objects.
 * * crud on module instances.
 *
 */
var mod_fs 					= require('fs')
	, mod_resourcemanager 	= require('./resourcemanager.js')
	, mod_path 				= require('path')
    , logger            	= require('./logger.js').getLogger(mod_path.basename(module.filename))
    , mod_sys               = require('sys')
    , moduleRootPath = mod_path.join(__dirname, '..', 'modules')
    , config = mod_fs.readFileSync(mod_path.join(__dirname, '..', 'module.conf.local')).toString()
    , moduleMap = JSON.parse(config)
    , c = console.log
    //, DEFAULT_VIEW = 'main.html';
    , COMPONENT_PROTOCOL    = 'webcomponent://'

function getViewUrl (module, view, tagmanager) {
    var view = view || 'main';
    var p = getModuleFolderFilePath(module, tagmanager);
    return p + mod_path.join('htdocs', view + '.html');
}

function getModuleFolderFilePath (module, tagmanager) {
    var tag = tagmanager.getTagByModule(module);
    logger.debug('getModuleFolderFilePath module ' + module);
    if (tag) {
        if (tag.module.indexOf('http://') == 0) {
            return tag.module;
        } else {
            return 'file://' + mod_path.join(moduleRootPath, module, '/');
        }
    }
    throw new Error('module ' + module + ' not found');
}

function getModuleFolder (module) {

}

function getModulePath (moduleId) {
    var moddesc;
    logger.debug('gmp ' + moduleId);
    if (moduleId.indexOf(COMPONENT_PROTOCOL) == 0) { moduleId = moduleId.substring(COMPONENT_PROTOCOL.length); }
    for (module in moduleMap) {
        if (moduleId.indexOf(';') == -1) {
            var i = module.indexOf(';');
            var m = module.substring(0, i > -1 ? i : module.length);
        } else {
            m = module;
        }
        if (moduleId == m) {
            moddesc = moduleMap[module];
            break;
        }
    }
    if (moddesc) {
        c('module ' + moduleId + "->" + moddesc.urls[0]);
        return moddesc.urls[0];
    }
}

// getModulePath('app');
// getModulePath('app;1.0');
// getModulePath('ap;1.0');
// getModulePath('app;1.0.1');
// getModulePath('webcomponent://app');
// getModulePath('webcomponent://app;1.0');

function getModuleFromPath (path) {
    c('getModuleFromPath ' + path);
    var module = null;
    for (module in moduleMap) {
        var item = moduleMap[module];
        c(item)
        for (var i = 0; i < item.urls.length; i++) {
            if (path == item.urls[i]) {
                module = item;
                break;
            }
        }
    }
    return module;
}

exports.getViewUrl 				= getViewUrl;
exports.moduleRootPath 			= moduleRootPath;
exports.getModuleFolderFilePath = getModuleFolderFilePath;
exports.getModulePath           = getModulePath;
exports.COMPONENT_PROTOCOL      = COMPONENT_PROTOCOL;
exports.getModuleFromPath       = getModuleFromPath;<commit_msg>moved default config<commit_after>"use strict";

/**
 * Module Container, Manager, Factory, whatever. All module related thingz must belong to us.
 *
 * Todos:
 *
 * * scan the local module folder for web component descriptors as resources.
 * * download module descriptors from remote web component hosts as resources.
 * * provide web component objects.
 * * crud on module instances.
 *
 */
var mod_fs 					= require('fs')
	, mod_resourcemanager 	= require('./resourcemanager.js')
	, mod_path 				= require('path')
    , logger            	= require('./logger.js').getLogger(mod_path.basename(module.filename))
    , mod_sys               = require('sys')
    , moduleRootPath = mod_path.join(__dirname, '..', 'modules')
    , config = mod_fs.readFileSync(mod_path.join(__dirname, '..', 'conf', 'module.conf.local')).toString()
    , moduleMap = JSON.parse(config)
    , c = console.log
    //, DEFAULT_VIEW = 'main.html';
    , COMPONENT_PROTOCOL    = 'webcomponent://'

function getViewUrl (module, view, tagmanager) {
    var view = view || 'main';
    var p = getModuleFolderFilePath(module, tagmanager);
    return p + mod_path.join('htdocs', view + '.html');
}

function getModuleFolderFilePath (module, tagmanager) {
    var tag = tagmanager.getTagByModule(module);
    logger.debug('getModuleFolderFilePath module ' + module);
    if (tag) {
        if (tag.module.indexOf('http://') == 0) {
            return tag.module;
        } else {
            return 'file://' + mod_path.join(moduleRootPath, module, '/');
        }
    }
    throw new Error('module ' + module + ' not found');
}

function getModuleFolder (module) {

}

function getModulePath (moduleId) {
    var moddesc;
    logger.debug('gmp ' + moduleId);
    if (moduleId.indexOf(COMPONENT_PROTOCOL) == 0) { moduleId = moduleId.substring(COMPONENT_PROTOCOL.length); }
    for (module in moduleMap) {
        if (moduleId.indexOf(';') == -1) {
            var i = module.indexOf(';');
            var m = module.substring(0, i > -1 ? i : module.length);
        } else {
            m = module;
        }
        if (moduleId == m) {
            moddesc = moduleMap[module];
            break;
        }
    }
    if (moddesc) {
        c('module ' + moduleId + "->" + moddesc.urls[0]);
        return moddesc.urls[0];
    }
}

// getModulePath('app');
// getModulePath('app;1.0');
// getModulePath('ap;1.0');
// getModulePath('app;1.0.1');
// getModulePath('webcomponent://app');
// getModulePath('webcomponent://app;1.0');

function getModuleFromPath (path) {
    c('getModuleFromPath ' + path);
    var module = null;
    for (module in moduleMap) {
        var item = moduleMap[module];
        c(item)
        for (var i = 0; i < item.urls.length; i++) {
            if (path == item.urls[i]) {
                module = item;
                break;
            }
        }
    }
    return module;
}

exports.getViewUrl 				= getViewUrl;
exports.moduleRootPath 			= moduleRootPath;
exports.getModuleFolderFilePath = getModuleFolderFilePath;
exports.getModulePath           = getModulePath;
exports.COMPONENT_PROTOCOL      = COMPONENT_PROTOCOL;
exports.getModuleFromPath       = getModuleFromPath;
<|endoftext|>


<commit_before>/**
 * @class This class bridges the Router with the Document Object Model and DOM events. It
 * hijacks the process by adding the target element of the DOM event and route as the
 * second to last argument to the controller methods, and the event object as the last
 * argument.
 *
 * @requires Router
 * @extends Object
 */
function DOMEventRouter() {
	this.constructor.apply(this, arguments);
}

DOMEventRouter.prototype = {

	/**
	 * @property {Object} A hash object of event names and nodes to which they are bound
	 */
	events: null,

	/**
	 * @property {Object} A router object supporting the getRoute and processRoute
	 *                    methods. See Router.
	 */
	router: null,

	/**
	 * Class constructor.
	 *
	 * @param {Object} A router object, supporting the same interface as Router.
	 *
	 * @throws Error
	 */
	constructor: function(router) {
		if (!router) {
			throw new Error("A router must be passed as the first argument to DOMEventRoute.prototype.constructor, supporting getRoute() and processRoute() methods.");
		}

		this.router = router;
		this.events = {};
		this.handleDOMEvent = this.bindFunction(this.handleDOMEvent);

		router = null;
	},

	destructor: function() {
		this.router = null;

		if (this.events) {
			for (var type in this.events) {
				if (!this.events.hasOwnProperty(type)) {
					continue;
				}

				for (var i = 0, length = this.events[type].length; i < length; i++) {
					this.events[type][i].unbind(type, this.handleDOMEvent);
					this.events[type][i] = null;
				}

				this.events[type] = null;
			}

			this.events = null;
		}
	},

	/**
	 * A hack to add a route to a document object. You cannot set attributes on the
	 * #document node, so instead we add a property called data-route-<type> to the
	 * object itself.
	 *
	 * If you call registerRoutes(jQuery(document) ...), be sure to call addDocumentRoute
	 * to ensure the route actually gets processed.
	 *
	 * @param {String} type The type of route to add, e.g. click, mousedown, etc.
	 * @param {String} rawRoute A raw route string.
	 * @param {HTMLDocumentObject} doc The document to add the route to. Defaults to the
	 *                                 global document variable.
	 */
	addDocumentRoute: function(type, rawRoute, doc) {
		doc = doc || document;
		doc["data-route-" + type] = rawRoute;
		doc = null;
	},

	addEvent: function(type, node) {
		if (!this.events[type]) {
			this.events[type] = [];
		}

		this.events[type].push(node);
		node = null;
	},

	controllerExists: function(id, instance) {
		return this.router.controllerExists(id, instance);
	},

	handleDOMEvent: function(event) {
		var type = event.type;
		var node = event.target;
		var propagate = this.processRoutes(type, node, event);

		event = node = null;

		return propagate;
	},

	/**
	 * Process routes starting in the DOM tree at node. If no route was found at node,
	 * then traverse up the DOM tree until one is found or the top of the tree is reached.
	 *
	 * @param {String} type The type of route to process. Cooresponds to a DOM event name.
	 * @param {HTMLElement} node The DOM node to start processing routes at
	 * @return {Boolean} True if all routes were processed, or no routes were found.
	 */
	processRoutes: function(type, node, event) {
		var rawRoute = null;
		var propagate = true;
		var route = null;

		if (node.nodeName === "#document") {
			// treat the #document node specially because we cannot add data-foo attributes. See addDocumentRoute().
			if (node["data-route-" + type]) {
				// grab the raw route from a property on the document object
				rawRoute = node["data-route-" + type];
			}
		}
		else {
			// grab the raw route from an attribute on the HTML tag.
			rawRoute = node.getAttribute("data-route-" + type);
		}

		if (rawRoute) {
			// get the route object from the router
			route = this.router.getRoute(rawRoute);

			// make event target and route element second to last argument to the controller method
			route.args.push(node);

			// make event object last argument to controller method
			route.args.push(event);

			// process the route like normal
			propagate = this.router.processRoute(route);

			// remove the event object from the route arguments
			route.args.pop();

			// remove the DOM element from the route arguments
			route.args.pop();
		}
		else if (node.parentNode) {
			// try the next node up the tree
			propagate = this.processRoutes(type, node.parentNode);
		}

		return propagate;
	},

	/**
	 * Register routes on a DOM node. The first argument is the DOM node for which routes
	 * should be registered. Remaining parameters are DOM event names.
	 *
	 * var router = new DOMEventRouter(Router);
	 * var el = document.getElementById("foo");
	 *
	 * router.registerRoutes(el, "click", "mousedown", "mouseup");
	 *
	 * @param {HTMLElement} 0 The DOM node for which routes should be registered
	 * @params {String} 1 - n DOM event names
	 */
	registerRoutes: function() {
		var node = arguments[0];
		var types = Array.prototype.slice.call(arguments, 1);

		for (var i = 0, length = types.length; i < length; i++) {
			node.bind(types[i], this.handleDOMEvent);
			this.addEvent(types[i], node);
		}

		node = null;
	},

	removeEvent: function(type, node) {
		if (this.events[type]) {
			for (var i = 0, length = this.events[type].length; i < length; i++) {
				if (node === this.events[type][i]) {
					this.events[type].splice(i, 0);
				}
			}
		}

		node = null;
	},

	/**
	 * Unregister routes on a DOM node. The first argument is the DOM node for which
	 * routes should be registered. Remaining parameters are DOM event names.
	 *
	 * @param {HTMLElement} 0 The DOM node for which routes should be unregistered
	 * @params {String} 1 - n DOM event names
	 */
	unregisterRoutes: function() {
		var node = arguments[0];
		var types = Array.prototype.slice.call(arguments, 1);

		for (var i = 0, length = types.length; i < length; i++) {
			node.unbind(type, this.handleDOMEvent);
			this.removeEvent(type, node);
		}

		node = null;
	},

	bindFunction: function(fn, ctx) {
		ctx = ctx || this;

		return function() {
			return fn.apply(ctx, arguments);
		};
	}

};<commit_msg>Decoupling DOMEventRouter from jQuery.<commit_after>/**
 * @class This class bridges the Router with the Document Object Model and DOM events. It
 * hijacks the process by adding the target element of the DOM event and route as the
 * second to last argument to the controller methods, and the event object as the last
 * argument.
 *
 * @requires Router
 * @extends Object
 */
function DOMEventRouter() {
	this.constructor.apply(this, arguments);
}

DOMEventRouter.prototype = {

	/**
	 * @property {Object} A hash object of event names and nodes to which they are bound
	 */
	events: null,

	/**
	 * @property {Object} A router object supporting the getRoute and processRoute
	 *                    methods. See Router.
	 */
	router: null,

	/**
	 * Class constructor.
	 *
	 * @param {Object} A router object, supporting the same interface as Router.
	 *
	 * @throws Error
	 */
	constructor: function(router) {
		if (!router) {
			throw new Error("A router must be passed as the first argument to DOMEventRoute.prototype.constructor, supporting getRoute() and processRoute() methods.");
		}

		this.router = router;
		this.events = {};
		this.handleDOMEvent = this.bindFunction(this.handleDOMEvent);

		router = null;
	},

	destructor: function() {
		this.router = null;

		if (this.events) {
			for (var type in this.events) {
				if (!this.events.hasOwnProperty(type)) {
					continue;
				}

				for (var i = 0, length = this.events[type].length; i < length; i++) {
					this.removeEventListener(this.events[type][i], type, this.handleDOMEvent);
					this.events[type][i] = null;
				}

				this.events[type] = null;
			}

			this.events = null;
		}
	},

	/**
	 * A hack to add a route to a document object. You cannot set attributes on the
	 * #document node, so instead we add a property called data-route-<type> to the
	 * object itself.
	 *
	 * If you call registerRoutes(jQuery(document) ...), be sure to call addDocumentRoute
	 * to ensure the route actually gets processed.
	 *
	 * @param {String} type The type of route to add, e.g. click, mousedown, etc.
	 * @param {String} rawRoute A raw route string.
	 * @param {HTMLDocumentObject} doc The document to add the route to. Defaults to the
	 *                                 global document variable.
	 */
	addDocumentRoute: function(type, rawRoute, doc) {
		doc = doc || document;
		doc["data-route-" + type] = rawRoute;
		doc = null;
	},

	addEvent: function(type, node) {
		if (!this.events[type]) {
			this.events[type] = [];
		}

		this.events[type].push(node);
		node = null;
	},

	controllerExists: function(id, instance) {
		return this.router.controllerExists(id, instance);
	},

	handleDOMEvent: function(event) {
		event = event || window.event;
		var type = event.type;
		var node = event.target || event.srcElement || null;
		var propagate;
		var returnFalse = false;

		try {
			propagate = this.processRoutes(type, node, event);
		}
		catch (error) {
			propagate = false;

			if (window.console) {
				if (window.console.error) {
					window.console.error(error);
				}
				else {
					window.console.log(error);
				}
			}
		}

		if (propagate === false) {
			if (event.preventDefault) {
				event.preventDefault();
				event.stopPropagation();
			}
			else {
				returnFalse = true;
			}
		}

		event = node = null;

		if (returnFalse) {
			return false;
		}
	},

	/**
	 * Process routes starting in the DOM tree at node. If no route was found at node,
	 * then traverse up the DOM tree until one is found or the top of the tree is reached.
	 *
	 * @param {String} type The type of route to process. Cooresponds to a DOM event name.
	 * @param {HTMLElement} node The DOM node to start processing routes at
	 * @return {Boolean} True if all routes were processed, or no routes were found.
	 */
	processRoutes: function(type, node, event) {
		var rawRoute = null;
		var propagate = true;
		var route = null;

		if (node.nodeName === "#document") {
			// treat the #document node specially because we cannot add data-foo attributes. See addDocumentRoute().
			if (node["data-route-" + type]) {
				// grab the raw route from a property on the document object
				rawRoute = node["data-route-" + type];
			}
		}
		else {
			// grab the raw route from an attribute on the HTML tag.
			rawRoute = node.getAttribute("data-route-" + type);
		}

		if (rawRoute) {
			// get the route object from the router
			route = this.router.getRoute(rawRoute);

			// make event target and route element second to last argument to the controller method
			route.args.push(node);

			// make event object last argument to controller method
			route.args.push(event);

			// process the route like normal
			propagate = this.router.processRoute(route);

			// remove the event object from the route arguments
			route.args.pop();

			// remove the DOM element from the route arguments
			route.args.pop();
		}
		else if (node.parentNode) {
			// try the next node up the tree
			propagate = this.processRoutes(type, node.parentNode);
		}

		return propagate;
	},

	/**
	 * Register routes on a DOM node. The first argument is the DOM node for which routes
	 * should be registered. Remaining parameters are DOM event names.
	 *
	 * var router = new DOMEventRouter(Router);
	 * var el = document.getElementById("foo");
	 *
	 * router.registerRoutes(el, "click", "mousedown", "mouseup");
	 *
	 * @param {HTMLElement} 0 The DOM node for which routes should be registered
	 * @params {String} 1 - n DOM event names
	 */
	registerRoutes: function() {
		var node = arguments[0];
		var types = Array.prototype.slice.call(arguments, 1);

		for (var i = 0, length = types.length; i < length; i++) {
			this.addEventListener(node, types[i], this.handleDOMEvent);
			this.addEvent(types[i], node);
		}

		node = null;
	},

	removeEvent: function(type, node) {
		if (this.events[type]) {
			for (var i = 0, length = this.events[type].length; i < length; i++) {
				if (node === this.events[type][i]) {
					this.events[type].splice(i, 0);
				}
			}
		}

		node = null;
	},

	/**
	 * Unregister routes on a DOM node. The first argument is the DOM node for which
	 * routes should be registered. Remaining parameters are DOM event names.
	 *
	 * @param {HTMLElement} 0 The DOM node for which routes should be unregistered
	 * @params {String} 1 - n DOM event names
	 */
	unregisterRoutes: function() {
		var node = arguments[0];
		var types = Array.prototype.slice.call(arguments, 1);

		for (var i = 0, length = types.length; i < length; i++) {
			this.removeEventListener(node, type, this.handleDOMEvent);
			this.removeEvent(type, node);
		}

		node = null;
	},

	addEventListener: function(node, type, callback) {
		if (node.addEventListener) {
			node.addEventListerner(type, callback, false);
		}
		else if (node.attachEvent) {
			node.attachEvent("on" + type, callback);
		}
	},

	bindFunction: function(fn, ctx) {
		ctx = ctx || this;

		return function() {
			return fn.apply(ctx, arguments);
		};
	},

	removeEventListener: function(node, type, callback) {
		if (node.addEventListener) {
			node.removeEventListerner(type, callback, false);
		}
		else if (node.attachEvent) {
			node.detachEvent("on" + type, callback);
		}
	}

};<|endoftext|>


<commit_before>/**
 * Created by Scott Bishop on 4/07/2015.
 *
 * Accepts a URI such as 'json/topic_01' which will request ../json/topic_01.json
 * Optionally pass arguments on the URI 'json/topic_01?author_id=1'
 * Returns a Promise passing the entire xhr object to a function in the 'then' block
 * Arguments passed on the URI can be retrieved via the uriArgs property of the xhr object
 *
 */
define(function() {
    return function(uri, post) {
        uri = decodeURI(uri);
        return new Promise(function(resolve, reject) {
            var xhr = new XMLHttpRequest(),
                separatorIndex = uri.indexOf('?'),
                sliceIndex = separatorIndex < 0 ? uri.length : separatorIndex,
                resource = uri.slice(0, sliceIndex),
                args = uri.slice(sliceIndex + 1).split('&');

            xhr.open(!post ? 'GET' : 'POST', resource);

            if (post) {
                xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
            }

            xhr.onload = function() {
                var i = 0, len, prop, val, temp;

                if (xhr.response) {
                    if (args.length) {
                        // if arguments were passed on the URI, attach them to the xhr object
                        xhr.uriArgs = {};
                        len = args.length;
                        for (i; i < len; ++i) {
                            temp = args[i].split('=');
                            prop = temp[0];
                            val = temp[1];
                            xhr.uriArgs[prop] = val;
                        }
                    }
                    // promise completed successfully
                    resolve(xhr);
                } else {
                    // reject promise - request error
                    reject(Error(xhr.statusText));
                }
            };

            xhr.onerror = function() {
                // reject promise - xhr epic fail
                reject(Error("Network Error"));
            };

            xhr.send(!post ? null : args);
        });
    };
});<commit_msg>resource is called with query params<commit_after>/**
 * Created by Scott Bishop on 4/07/2015.
 *
 * Accepts a URI such as 'json/topic_01' which will request ../json/topic_01.json
 * Optionally pass arguments on the URI 'json/topic_01?author_id=1'
 * Returns a Promise passing the entire xhr object to a function in the 'then' block
 * Arguments passed on the URI can be retrieved via the uriArgs property of the xhr object
 *
 */
define(function() {
    return function(uri, post) {
        uri = decodeURI(uri);
        return new Promise(function(resolve, reject) {
            var xhr = new XMLHttpRequest(),
                separatorIndex = uri.indexOf('?'),
                sliceIndex = separatorIndex < 0 ? uri.length : separatorIndex,
                args = uri.slice(sliceIndex + 1).split('&');

            xhr.open(!post ? 'GET' : 'POST', uri);

            if (post) {
                xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
            }

            xhr.onload = function() {
                var i = 0, len, prop, val, temp;

                if (xhr.response) {
                    if (args.length) {
                        // if arguments were passed on the URI, attach them to the xhr object
                        xhr.uriArgs = {};
                        len = args.length;
                        for (i; i < len; ++i) {
                            temp = args[i].split('=');
                            prop = temp[0];
                            val = temp[1];
                            xhr.uriArgs[prop] = val;
                        }
                    }
                    // promise completed successfully
                    resolve(xhr);
                } else {
                    // reject promise - request error
                    reject(Error(xhr.statusText));
                }
            };

            xhr.onerror = function() {
                // reject promise - xhr epic fail
                reject(Error("Network Error"));
            };

            xhr.send(!post ? null : args);
        });
    };
});
<|endoftext|>


<commit_before>/**
 * Copyright 2015 The AMP HTML Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {makeCorrelator} from './correlator';
import {validateData, loadScript} from '../../3p/3p';
import {user} from '../../src/log';

/**
 * @enum {number}
 * @private
 */
const GladeExperiment = {
  NO_EXPERIMENT: 0,
  GLADE_CONTROL: 1,
  GLADE_EXPERIMENT: 2,
  GLADE_OPT_OUT: 3,
};

/**
 * @param {!Window} global
 * @param {!Object} data
 */
export function doubleclick(global, data) {
  const experimentFraction = 0.1;

  // TODO: check mandatory fields
  validateData(data, [], [
    'slot', 'targeting', 'categoryExclusions',
    'tagForChildDirectedTreatment', 'cookieOptions',
    'overrideWidth', 'overrideHeight', 'loadingStrategy',
    'consentNotificationId', 'useSameDomainRenderingUntilDeprecated',
    'experimentId', 'multiSize', 'multiSizeValidation',
  ]);

  if (global.context.clientId) {
    // Read by GPT/Glade for GA/Doubleclick integration.
    global.gaGlobal = {
      vid: global.context.clientId,
      hid: global.context.pageViewId,
    };
  }

  if (data.useSameDomainRenderingUntilDeprecated != undefined ||
      data.multiSize) {
    doubleClickWithGpt(global, data, GladeExperiment.GLADE_OPT_OUT);
  } else {
    const dice = Math.random();
    const href = global.context.location.href;
    if ((href.indexOf('google_glade=0') > 0 || dice < experimentFraction)
        && href.indexOf('google_glade=1') < 0) {
      doubleClickWithGpt(global, data, GladeExperiment.GLADE_CONTROL);
    } else {
      const exp = (dice < 2 * experimentFraction) ?
        GladeExperiment.GLADE_EXPERIMENT : GladeExperiment.NO_EXPERIMENT;
      doubleClickWithGlade(global, data, exp);
    }
  }
}

/**
 * A helper function for determining whether a given width or height violates
 * some condition.
 *
 * Checks the width and height against their corresponding conditions. If
 * either of the conditions fail, the errorBuilder function will be called with
 * the appropriate arguments, its result will be logged to user().error, and
 * validateDimensions will return false. Otherwise, validateDimensions will
 * only return true.
 *
 * @param {(number|string)} width
 * @param {(number|string)} height
 * @param {!function((number|string)): boolean} widthCond
 * @param {!function((number|string)): boolean} heightCond
 * @param {!function(!{badDim: string, badVal: (string|number)}): string}
 *    errorBuilder A function that will produce an informative error message.
 * @return {boolean}
 */
function validateDimensions(width, height, widthCond, heightCond,
    errorBuilder) {
  let badParams = null;
  if (widthCond(width) && heightCond(height)) {
    badParams = {
      badDim: 'width and height',
      badVal: width + 'x' + height,
    };
  }
  else if (widthCond(width)) {
    badParams = {
      badDim: 'width',
      badVal: width,
    };
  }
  else if (heightCond(height)) {
    badParams = {
      badDim: 'height',
      badVal: height,
    };
  }
  if (badParams) {
    user().error('AMP-AD', errorBuilder(badParams));
    return false;
  }
  return true;
}

/**
 * @param {!Window} global
 * @param {!Object} data
 * @param {!GladeExperiment} gladeExperiment
 */
function doubleClickWithGpt(global, data, gladeExperiment) {
  const dimensions = [[
    parseInt(data.overrideWidth || data.width, 10),
    parseInt(data.overrideHeight || data.height, 10),
  ]];

  // Center the ad in the container.
  const container = global.document.querySelector('#c');
  container.style.top = '50%';
  container.style.left = '50%';
  container.style.bottom = '';
  container.style.right = '';
  container.style.transform = 'translate(-50%, -50%)';

  // Get multi-size ad request data, if any, and validate it in the following
  // ways:
  // 1) Ensure that the data string is a comma-separated list of sizes of the
  //    form WxH;
  // 2) that each secondary dimension is strictly less than its primary
  //    dimension counterpart;
  // 3) and, if data-mutli-size-validation is not set to false, that each
  //    secondary dimension is at least 2/3rds of its primary dimension
  //    counterpart.
  const multiSizeDataStr = data.multiSize || null;
  if (multiSizeDataStr) {
    const sizesStr = multiSizeDataStr.split(',');
    sizesStr.forEach(sizeStr => {

      const size = sizeStr.split('x');

      // Make sure that each size is specified in the form val1xval2.
      if (size.length != 2) {
        user().error('AMP-AD', `Invalid multi-size data format '${sizeStr}'.`);
        return;
      }

      const widthStr = size[0];
      const heightStr = size[1];

      // Make sure that both dimensions given are numbers.
      if (!validateDimensions(widthStr, heightStr,
            w => isNaN(Number(w)),
            h => isNaN(Number(h)),
            ({badDim, badVal}) =>
            `Invalid ${badDim} of ${badVal} given for secondary size.`)) {
        return;
      }

      const width = Number(widthStr);
      const height = Number(heightStr);
      const primarySize = dimensions[0];
      const primaryWidth = primarySize[0];
      const primaryHeight = primarySize[1];

      // Check that secondary size is not larger than primary size.
      if (!validateDimensions(width, height,
            w => w > primaryWidth,
            h => h > primaryHeight,
            ({badDim, badVal}) => `Secondary ${badDim} ${badVal} ` +
              `can't be larger than the primary ${badDim}.`)) {
        return;
      }

      // Check that if multi-size-validation is on, that the secondary sizes
      // are at least minRatio of the primary size.
      const validate = data.multiSizeValidation || 'true';
      if (validate != 'false' && validate != false) {

        // The minimum ratio of each secondary dimension to its corresponding
        // primary dimension.
        const minRatio = 2 / 3;
        const minWidth = minRatio * primaryWidth;
        const minHeight = minRatio * primaryHeight;
        if (!validateDimensions(width, height,
              w => w < minWidth,
              h => h < minHeight,
              ({badDim, badVal}) => `Secondary ${badDim} ${badVal} is ` +
              `smaller than 2/3rds of the primary ${badDim}.`)) {
          return;
        }
      }

      // Passed all checks! Push additional size to dimensions.
      dimensions.push([width, height]);
    });
  }

  loadScript(global, 'https://www.googletagservices.com/tag/js/gpt.js', () => {
    global.googletag.cmd.push(() => {
      const googletag = global.googletag;
      const pubads = googletag.pubads();
      const slot = googletag.defineSlot(data.slot, dimensions, 'c')
          .addService(pubads);

      if (gladeExperiment === GladeExperiment.GLADE_CONTROL) {
        pubads.markAsGladeControl();
      } else if (gladeExperiment === GladeExperiment.GLADE_OPT_OUT) {
        pubads.markAsGladeOptOut();
      }

      if (data['experimentId']) {
        const experimentIdList = data['experimentId'].split(',');
        pubads.forceExperiment = pubads.forceExperiment || function() {};
        experimentIdList &&
            experimentIdList.forEach(eid => pubads.forceExperiment(eid));
      }

      pubads.markAsAmp();
      pubads.set('page_url', global.context.canonicalUrl);
      pubads.setCorrelator(Number(getCorrelator(global)));
      googletag.enableServices();

      if (data.categoryExclusions) {
        if (Array.isArray(data.categoryExclusions)) {
          for (let i = 0; i < data.categoryExclusions.length; i++) {
            slot.setCategoryExclusion(data.categoryExclusions[i]);
          }
        } else {
          slot.setCategoryExclusion(data.categoryExclusions);
        }
      }

      if (data.cookieOptions) {
        pubads.setCookieOptions(data.cookieOptions);
      }

      if (data.tagForChildDirectedTreatment != undefined) {
        pubads.setTagForChildDirectedTreatment(
            data.tagForChildDirectedTreatment);
      }

      if (data.targeting) {
        for (const key in data.targeting) {
          slot.setTargeting(key, data.targeting[key]);
        }
      }

      pubads.addEventListener('slotRenderEnded', event => {
        const primaryInvSize = dimensions[0];
        const pWidth = primaryInvSize[0];
        const pHeight = primaryInvSize[1];
        const returnedSize = event.size;
        const rWidth = returnedSize ? returnedSize[0] : null;
        const rHeight = returnedSize ? returnedSize[1] : null;

        let creativeId = event.creativeId || '_backfill_';

        // If the creative is empty, or either dimension of the returned size
        // is larger than its counterpart in the primary size, then we don't
        // want to render the creative.
        if (event.isEmpty ||
            returnedSize && (rWidth > pWidth || rHeight > pHeight)) {
          global.context.noContentAvailable();
          creativeId = '_empty_';
        } else {
          // We don't want to call renderStart() on ads that failed to load, for
          // whatever reasons, because then the fallback will not be shown.
          global.context.renderStart();
          // TODO(levitzky) This call will no longer be necessary once
          // renderStart() can take a size as an argument.
          global.context.requestResize(rWidth, rHeight);
        }
        global.context.reportRenderedEntityIdentifier('dfp-' + creativeId);
      });

      // Exported for testing.
      global.document.getElementById('c')['slot'] = slot;
      googletag.display('c');
    });
  });
}

/**
 * @param {!Window} global
 * @param {!Object} data
 * @param {!GladeExperiment} gladeExperiment
 */
function doubleClickWithGlade(global, data, gladeExperiment) {
  const requestHeight = parseInt(data.overrideHeight || data.height, 10);
  const requestWidth = parseInt(data.overrideWidth || data.width, 10);

  const jsonParameters = {};
  if (data.categoryExclusions) {
    jsonParameters.categoryExclusions = data.categoryExclusions;
  }
  if (data.cookieOptions) {
    jsonParameters.cookieOptOut = data.cookieOptions;
  }
  if (data.tagForChildDirectedTreatment != undefined) {
    jsonParameters.tagForChildDirectedTreatment =
        data.tagForChildDirectedTreatment;
  }
  if (data.targeting) {
    jsonParameters.targeting = data.targeting;
  }
  if (gladeExperiment === GladeExperiment.GLADE_EXPERIMENT) {
    jsonParameters.gladeExp = '1';
    jsonParameters.gladeEids = '108809102';
  }
  const expIds = data['experimentId'];
  if (expIds) {
    jsonParameters.gladeEids = jsonParameters.gladeEids ?
        jsonParameters.gladeEids + ',' + expIds : expIds;
  }

  const slot = global.document.querySelector('#c');
  slot.setAttribute('data-glade', '');
  slot.setAttribute('data-amp-ad', '');
  slot.setAttribute('data-ad-unit-path', data.slot);
  if (Object.keys(jsonParameters).length > 0) {
    slot.setAttribute('data-json', JSON.stringify(jsonParameters));
  }
  slot.setAttribute('data-page-url', global.context.canonicalUrl);

  // Center the ad in the container.
  slot.setAttribute('height', requestHeight);
  slot.setAttribute('width', requestWidth);
  slot.style.top = '50%';
  slot.style.left = '50%';
  slot.style.bottom = '';
  slot.style.right = '';
  slot.style.transform = 'translate(-50%, -50%)';

  slot.addEventListener('gladeAdFetched', event => {
    if (event.detail.empty) {
      global.context.noContentAvailable();
    }
    global.context.renderStart();
  });

  window.glade = {correlator: getCorrelator(global)};
  loadScript(global, 'https://securepubads.g.doubleclick.net/static/glade.js');
}

/**
 * @param {!Window} global
 * @return {number}
 */
function getCorrelator(global) {
  return makeCorrelator(global.context.clientId, global.context.pageViewId);
}
<commit_msg>Removing call to requestResize() (#4984)<commit_after>/**
 * Copyright 2015 The AMP HTML Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {makeCorrelator} from './correlator';
import {validateData, loadScript} from '../../3p/3p';
import {user} from '../../src/log';

/**
 * @enum {number}
 * @private
 */
const GladeExperiment = {
  NO_EXPERIMENT: 0,
  GLADE_CONTROL: 1,
  GLADE_EXPERIMENT: 2,
  GLADE_OPT_OUT: 3,
};

/**
 * @param {!Window} global
 * @param {!Object} data
 */
export function doubleclick(global, data) {
  const experimentFraction = 0.1;

  // TODO: check mandatory fields
  validateData(data, [], [
    'slot', 'targeting', 'categoryExclusions',
    'tagForChildDirectedTreatment', 'cookieOptions',
    'overrideWidth', 'overrideHeight', 'loadingStrategy',
    'consentNotificationId', 'useSameDomainRenderingUntilDeprecated',
    'experimentId', 'multiSize', 'multiSizeValidation',
  ]);

  if (global.context.clientId) {
    // Read by GPT/Glade for GA/Doubleclick integration.
    global.gaGlobal = {
      vid: global.context.clientId,
      hid: global.context.pageViewId,
    };
  }

  if (data.useSameDomainRenderingUntilDeprecated != undefined ||
      data.multiSize) {
    doubleClickWithGpt(global, data, GladeExperiment.GLADE_OPT_OUT);
  } else {
    const dice = Math.random();
    const href = global.context.location.href;
    if ((href.indexOf('google_glade=0') > 0 || dice < experimentFraction)
        && href.indexOf('google_glade=1') < 0) {
      doubleClickWithGpt(global, data, GladeExperiment.GLADE_CONTROL);
    } else {
      const exp = (dice < 2 * experimentFraction) ?
        GladeExperiment.GLADE_EXPERIMENT : GladeExperiment.NO_EXPERIMENT;
      doubleClickWithGlade(global, data, exp);
    }
  }
}

/**
 * A helper function for determining whether a given width or height violates
 * some condition.
 *
 * Checks the width and height against their corresponding conditions. If
 * either of the conditions fail, the errorBuilder function will be called with
 * the appropriate arguments, its result will be logged to user().error, and
 * validateDimensions will return false. Otherwise, validateDimensions will
 * only return true.
 *
 * @param {(number|string)} width
 * @param {(number|string)} height
 * @param {!function((number|string)): boolean} widthCond
 * @param {!function((number|string)): boolean} heightCond
 * @param {!function(!{badDim: string, badVal: (string|number)}): string}
 *    errorBuilder A function that will produce an informative error message.
 * @return {boolean}
 */
function validateDimensions(width, height, widthCond, heightCond,
    errorBuilder) {
  let badParams = null;
  if (widthCond(width) && heightCond(height)) {
    badParams = {
      badDim: 'width and height',
      badVal: width + 'x' + height,
    };
  }
  else if (widthCond(width)) {
    badParams = {
      badDim: 'width',
      badVal: width,
    };
  }
  else if (heightCond(height)) {
    badParams = {
      badDim: 'height',
      badVal: height,
    };
  }
  if (badParams) {
    user().error('AMP-AD', errorBuilder(badParams));
    return false;
  }
  return true;
}

/**
 * @param {!Window} global
 * @param {!Object} data
 * @param {!GladeExperiment} gladeExperiment
 */
function doubleClickWithGpt(global, data, gladeExperiment) {
  const dimensions = [[
    parseInt(data.overrideWidth || data.width, 10),
    parseInt(data.overrideHeight || data.height, 10),
  ]];

  // Center the ad in the container.
  const container = global.document.querySelector('#c');
  container.style.top = '50%';
  container.style.left = '50%';
  container.style.bottom = '';
  container.style.right = '';
  container.style.transform = 'translate(-50%, -50%)';

  // Get multi-size ad request data, if any, and validate it in the following
  // ways:
  // 1) Ensure that the data string is a comma-separated list of sizes of the
  //    form WxH;
  // 2) that each secondary dimension is strictly less than its primary
  //    dimension counterpart;
  // 3) and, if data-mutli-size-validation is not set to false, that each
  //    secondary dimension is at least 2/3rds of its primary dimension
  //    counterpart.
  const multiSizeDataStr = data.multiSize || null;
  if (multiSizeDataStr) {
    const sizesStr = multiSizeDataStr.split(',');
    sizesStr.forEach(sizeStr => {

      const size = sizeStr.split('x');

      // Make sure that each size is specified in the form val1xval2.
      if (size.length != 2) {
        user().error('AMP-AD', `Invalid multi-size data format '${sizeStr}'.`);
        return;
      }

      const widthStr = size[0];
      const heightStr = size[1];

      // Make sure that both dimensions given are numbers.
      if (!validateDimensions(widthStr, heightStr,
            w => isNaN(Number(w)),
            h => isNaN(Number(h)),
            ({badDim, badVal}) =>
            `Invalid ${badDim} of ${badVal} given for secondary size.`)) {
        return;
      }

      const width = Number(widthStr);
      const height = Number(heightStr);
      const primarySize = dimensions[0];
      const primaryWidth = primarySize[0];
      const primaryHeight = primarySize[1];

      // Check that secondary size is not larger than primary size.
      if (!validateDimensions(width, height,
            w => w > primaryWidth,
            h => h > primaryHeight,
            ({badDim, badVal}) => `Secondary ${badDim} ${badVal} ` +
              `can't be larger than the primary ${badDim}.`)) {
        return;
      }

      // Check that if multi-size-validation is on, that the secondary sizes
      // are at least minRatio of the primary size.
      const validate = data.multiSizeValidation || 'true';
      if (validate != 'false' && validate != false) {

        // The minimum ratio of each secondary dimension to its corresponding
        // primary dimension.
        const minRatio = 2 / 3;
        const minWidth = minRatio * primaryWidth;
        const minHeight = minRatio * primaryHeight;
        if (!validateDimensions(width, height,
              w => w < minWidth,
              h => h < minHeight,
              ({badDim, badVal}) => `Secondary ${badDim} ${badVal} is ` +
              `smaller than 2/3rds of the primary ${badDim}.`)) {
          return;
        }
      }

      // Passed all checks! Push additional size to dimensions.
      dimensions.push([width, height]);
    });
  }

  loadScript(global, 'https://www.googletagservices.com/tag/js/gpt.js', () => {
    global.googletag.cmd.push(() => {
      const googletag = global.googletag;
      const pubads = googletag.pubads();
      const slot = googletag.defineSlot(data.slot, dimensions, 'c')
          .addService(pubads);

      if (gladeExperiment === GladeExperiment.GLADE_CONTROL) {
        pubads.markAsGladeControl();
      } else if (gladeExperiment === GladeExperiment.GLADE_OPT_OUT) {
        pubads.markAsGladeOptOut();
      }

      if (data['experimentId']) {
        const experimentIdList = data['experimentId'].split(',');
        pubads.forceExperiment = pubads.forceExperiment || function() {};
        experimentIdList &&
            experimentIdList.forEach(eid => pubads.forceExperiment(eid));
      }

      pubads.markAsAmp();
      pubads.set('page_url', global.context.canonicalUrl);
      pubads.setCorrelator(Number(getCorrelator(global)));
      googletag.enableServices();

      if (data.categoryExclusions) {
        if (Array.isArray(data.categoryExclusions)) {
          for (let i = 0; i < data.categoryExclusions.length; i++) {
            slot.setCategoryExclusion(data.categoryExclusions[i]);
          }
        } else {
          slot.setCategoryExclusion(data.categoryExclusions);
        }
      }

      if (data.cookieOptions) {
        pubads.setCookieOptions(data.cookieOptions);
      }

      if (data.tagForChildDirectedTreatment != undefined) {
        pubads.setTagForChildDirectedTreatment(
            data.tagForChildDirectedTreatment);
      }

      if (data.targeting) {
        for (const key in data.targeting) {
          slot.setTargeting(key, data.targeting[key]);
        }
      }

      pubads.addEventListener('slotRenderEnded', event => {
        const primaryInvSize = dimensions[0];
        const pWidth = primaryInvSize[0];
        const pHeight = primaryInvSize[1];
        const returnedSize = event.size;
        const rWidth = returnedSize ? returnedSize[0] : null;
        const rHeight = returnedSize ? returnedSize[1] : null;

        let creativeId = event.creativeId || '_backfill_';

        // If the creative is empty, or either dimension of the returned size
        // is larger than its counterpart in the primary size, then we don't
        // want to render the creative.
        if (event.isEmpty ||
            returnedSize && (rWidth > pWidth || rHeight > pHeight)) {
          global.context.noContentAvailable();
          creativeId = '_empty_';
        } else {
          // We don't want to call renderStart() on ads that failed to load, for
          // whatever reasons, because then the fallback will not be shown.
          global.context.renderStart({width: rWidth, height: rHeight});
        }
        global.context.reportRenderedEntityIdentifier('dfp-' + creativeId);
      });

      // Exported for testing.
      global.document.getElementById('c')['slot'] = slot;
      googletag.display('c');
    });
  });
}

/**
 * @param {!Window} global
 * @param {!Object} data
 * @param {!GladeExperiment} gladeExperiment
 */
function doubleClickWithGlade(global, data, gladeExperiment) {
  const requestHeight = parseInt(data.overrideHeight || data.height, 10);
  const requestWidth = parseInt(data.overrideWidth || data.width, 10);

  const jsonParameters = {};
  if (data.categoryExclusions) {
    jsonParameters.categoryExclusions = data.categoryExclusions;
  }
  if (data.cookieOptions) {
    jsonParameters.cookieOptOut = data.cookieOptions;
  }
  if (data.tagForChildDirectedTreatment != undefined) {
    jsonParameters.tagForChildDirectedTreatment =
        data.tagForChildDirectedTreatment;
  }
  if (data.targeting) {
    jsonParameters.targeting = data.targeting;
  }
  if (gladeExperiment === GladeExperiment.GLADE_EXPERIMENT) {
    jsonParameters.gladeExp = '1';
    jsonParameters.gladeEids = '108809102';
  }
  const expIds = data['experimentId'];
  if (expIds) {
    jsonParameters.gladeEids = jsonParameters.gladeEids ?
        jsonParameters.gladeEids + ',' + expIds : expIds;
  }

  const slot = global.document.querySelector('#c');
  slot.setAttribute('data-glade', '');
  slot.setAttribute('data-amp-ad', '');
  slot.setAttribute('data-ad-unit-path', data.slot);
  if (Object.keys(jsonParameters).length > 0) {
    slot.setAttribute('data-json', JSON.stringify(jsonParameters));
  }
  slot.setAttribute('data-page-url', global.context.canonicalUrl);

  // Center the ad in the container.
  slot.setAttribute('height', requestHeight);
  slot.setAttribute('width', requestWidth);
  slot.style.top = '50%';
  slot.style.left = '50%';
  slot.style.bottom = '';
  slot.style.right = '';
  slot.style.transform = 'translate(-50%, -50%)';

  slot.addEventListener('gladeAdFetched', event => {
    if (event.detail.empty) {
      global.context.noContentAvailable();
    }
    global.context.renderStart();
  });

  window.glade = {correlator: getCorrelator(global)};
  loadScript(global, 'https://securepubads.g.doubleclick.net/static/glade.js');
}

/**
 * @param {!Window} global
 * @return {number}
 */
function getCorrelator(global) {
  return makeCorrelator(global.context.clientId, global.context.pageViewId);
}
<|endoftext|>