<commit_before>var mongoose = require('mongoose')
var express = require('express')
var config = require('lib/config')
var api = require('lib/db-api')
var utils = require('lib/utils')
var restrict = utils.restrict
var maintenance = utils.maintenance
var expose = utils.expose
var privileges = require('lib/models/forum/privileges')
var middlewares = require('lib/forum-middlewares')
var Log = require('debug')

var log = new Log('democracyos:forum-api')

var app = module.exports = express()

function getUserKeys (scope) {
  scope = scope ? scope + '.' : ''
  return '%id %fullName %displayName %avatar %locale'.replace(/%/g, scope)
}

var ownerKeys = getUserKeys('owner')
var keys = ['id name title summary visibility createdAt', ownerKeys].join(' ')
var permissionsKeys = ['role', getUserKeys('user')].join(' ')

app.get('/forum/all', function (req, res) {
  var page = parseInt(req.query.page, 10) || 0
  var limit = 20

  var options = {
    limit: limit,
    skip: page * limit,
    owner: req.query.owner
  }

  if (req.query['privileges.canChangeTopics']) {
    if (!req.user) return res.status(400).send()
    options['privileges.canChangeTopics'] = req.user
  }

  api.forum.all(options, function (err, forums) {
    if (err) return _handleError(err, req, res)
    res.json(200, forums.map(forum => {
      var json = expose(keys)(forum)
      json.privileges = req.user ? privileges.all(forum, req.user) : {}
      return json
    })).send()
  })
})

app.get('/forum/exists', function (req, res, next) {
  if (!api.forum.nameIsValid(req.query.name)) {
    return res.json(200, {exists: true})
  }

  api.forum.exists(req.query.name, function (err, exists) {
    if (err) return next(err)
    return res.json(200, {exists: exists})
  })
})

app.get('/forum/mine', restrict, function (req, res) {
  if (!req.user) return res.status(404).send()

  api.forum.findByOwner(req.user.id, function (err, forums) {
    if (err) return _handleError(err, req, res)

    if (forums) {
      return res.json(200, forums.map(expose(keys)))
    } else {
      return res.status(404).send()
    }
  })
})

function respondForum (req, res) {
  var forum = expose(keys)(req.forum)
  if (req.user) {
    forum.privileges = privileges.all(req.forum, req.user)
  } else {
    forum.privileges = {}
  }
  return res.json(200, forum)
}

app.get('/forum',
  middlewares.findForumByName,
  middlewares.privileges('canView'),
  respondForum
)

app.get('/forum/:id',
  middlewares.findForum,
  middlewares.privileges('canView'),
  respondForum
)

app.get('/forum/:id/permissions',
  restrict,
  middlewares.findForum,
  middlewares.privileges('canEdit'),
  function (req, res) {
    if (!mongoose.Types.ObjectId.isValid(req.params.id)) {
      return _handleError(new Error('Invalid :id'), req, res)
    }

    api.forum.getPermissions(req.params.id, function (err, permissions) {
      if (err) return _handleError(err, req, res)
      res.json(200, permissions.map(expose(permissionsKeys))).send()
    })
  }
)

app.post('/forum/:id/permissions',
  restrict,
  maintenance,
  middlewares.findForum,
  middlewares.privileges('canEdit'),
  function (req, res) {
    api.forum.grantPermission(req.params.id, req.body.user, req.body.role)
      .then(() => res.status(200).send())
      .catch(err => {
        log('Found error: ', err)
        res.status(500).send()
      })
  }
)

app.delete('/forum/:id/permissions',
  restrict,
  maintenance,
  middlewares.findForum,
  middlewares.privileges('canEdit'),
  function (req, res) {
    api.forum.revokePermission(req.params.id, req.body.user, req.body.role)
      .then(() => res.status(200).send())
      .catch(err => {
        log('Found error: ', err)
        res.status(500).send()
      })
  }
)

app.post('/forum', middlewares.singleForumStaff, restrict, maintenance, function (req, res) {
  var data = {
    name: req.body.name,
    title: req.body.title,
    owner: req.user._id.toString(),
    body: req.body.body,
    summary: req.body.summary,
    permissions: req.body.permissions
  }

  log('Trying to create forum with name: %s', data.name)

  api.forum.create(data, function (err, forum) {
    if (err) return _handleError(err, req, res)
    log('Forum document created successfully: %s', forum.name)

    // Adopt orphan topics
    if (!config.multiForum) {
      log('Searching for old topics without forum')
      api.topic.all({forum: null}, function (err, topics) {
        if (err) {
          log('Error fetching all orphan topics: %s', err)
          return
        }

        if (!topics.length) return

        topics.forEach(function (topic) {
          topic.forum = forum
          topic.save(function (err) {
            if (err) {
              log('There was an error when trying to un-orphanate topic with id %s: ', topic._id, err)
            } else {
              log('Un-orphanated topic with id %s: ', topic._id)
            }
          })
        })
      })
    }

    return res.json(200, expose(keys)(forum))
  })
})

app.put('/forum/:id',
  restrict,
  maintenance,
  middlewares.findForum,
  middlewares.privileges('canEdit'),
  function (req, res) {
    if (!req.body.data) return res.status(400).send()
    if (typeof req.body.data !== 'object') return res.status(400).send()

    api.forum.update(req.forum, req.body.data, function (err, forum) {
      if (err) return _handleError(err, req, res)
      log('Forum updated successfully')
      return res.json(200, expose(keys)(forum))
    })
  }
)

app.delete('/forum/:id',
  restrict,
  maintenance,
  middlewares.findForum,
  middlewares.privileges('canDelete'),
  function (req, res) {
    var forum = req.forum

    log('Trying to delete forum: %s', forum.id)

    api.forum.del(forum, function (err) {
      if (err) return res.json(500, err)
      res.status(200).send()
    })
  }
)

/**
 * Helper functions
 */

function _handleError (err, req, res) {
  log('Error found: %s', err)
  var error = err
  if (err.errors && err.errors.text) error = err.errors.text
  if (error.type) error = error.type

  res.json(400, { error: error })
}
<commit_msg>fix forum-api log<commit_after>var mongoose = require('mongoose')
var express = require('express')
var config = require('lib/config')
var api = require('lib/db-api')
var utils = require('lib/utils')
var restrict = utils.restrict
var maintenance = utils.maintenance
var expose = utils.expose
var privileges = require('lib/models/forum/privileges')
var middlewares = require('lib/forum-middlewares')
var Log = require('debug')

var log = new Log('democracyos:forum-api')

var app = module.exports = express()

function getUserKeys (scope) {
  scope = scope ? scope + '.' : ''
  return '%id %fullName %displayName %avatar %locale'.replace(/%/g, scope)
}

var ownerKeys = getUserKeys('owner')
var keys = ['id name title summary visibility createdAt', ownerKeys].join(' ')
var permissionsKeys = ['role', getUserKeys('user')].join(' ')

app.get('/forum/all', function (req, res) {
  var page = parseInt(req.query.page, 10) || 0
  var limit = 20

  var options = {
    limit: limit,
    skip: page * limit,
    owner: req.query.owner
  }

  if (req.query['privileges.canChangeTopics']) {
    if (!req.user) return res.status(400).send()
    options['privileges.canChangeTopics'] = req.user
  }

  api.forum.all(options, function (err, forums) {
    if (err) return _handleError(err, req, res)
    res.json(200, forums.map(forum => {
      var json = expose(keys)(forum)
      json.privileges = req.user ? privileges.all(forum, req.user) : {}
      return json
    })).send()
  })
})

app.get('/forum/exists', function (req, res, next) {
  if (!api.forum.nameIsValid(req.query.name)) {
    return res.json(200, {exists: true})
  }

  api.forum.exists(req.query.name, function (err, exists) {
    if (err) return next(err)
    return res.json(200, {exists: exists})
  })
})

app.get('/forum/mine', restrict, function (req, res) {
  if (!req.user) return res.status(404).send()

  api.forum.findByOwner(req.user.id, function (err, forums) {
    if (err) return _handleError(err, req, res)

    if (forums) {
      return res.json(200, forums.map(expose(keys)))
    } else {
      return res.status(404).send()
    }
  })
})

function respondForum (req, res) {
  var forum = expose(keys)(req.forum)
  if (req.user) {
    forum.privileges = privileges.all(req.forum, req.user)
  } else {
    forum.privileges = {}
  }
  return res.json(200, forum)
}

app.get('/forum',
  middlewares.findForumByName,
  middlewares.privileges('canView'),
  respondForum
)

app.get('/forum/:id',
  middlewares.findForum,
  middlewares.privileges('canView'),
  respondForum
)

app.get('/forum/:id/permissions',
  restrict,
  middlewares.findForum,
  middlewares.privileges('canEdit'),
  function (req, res) {
    if (!mongoose.Types.ObjectId.isValid(req.params.id)) {
      return _handleError(new Error('Invalid :id'), req, res)
    }

    api.forum.getPermissions(req.params.id, function (err, permissions) {
      if (err) return _handleError(err, req, res)
      res.json(200, permissions.map(expose(permissionsKeys))).send()
    })
  }
)

app.post('/forum/:id/permissions',
  restrict,
  maintenance,
  middlewares.findForum,
  middlewares.privileges('canEdit'),
  function (req, res) {
    api.forum.grantPermission(req.params.id, req.body.user, req.body.role)
      .then(() => res.status(200).send())
      .catch(err => {
        log('Found error: ', err)
        res.status(500).send()
      })
  }
)

app.delete('/forum/:id/permissions',
  restrict,
  maintenance,
  middlewares.findForum,
  middlewares.privileges('canEdit'),
  function (req, res) {
    api.forum.revokePermission(req.params.id, req.body.user, req.body.role)
      .then(() => res.status(200).send())
      .catch(err => {
        log('Found error: ', err)
        res.status(500).send()
      })
  }
)

app.post('/forum', middlewares.singleForumStaff, restrict, maintenance, function (req, res) {
  var data = {
    name: req.body.name,
    title: req.body.title,
    owner: req.user._id.toString(),
    body: req.body.body,
    summary: req.body.summary,
    permissions: req.body.permissions
  }

  log('Trying to create forum with name: %s', data.name)

  api.forum.create(data, function (err, forum) {
    if (err) return _handleError(err, req, res)
    log('Forum document created successfully: %s', forum.name)

    // Adopt orphan topics
    if (!config.multiForum) {
      log('Searching for old topics without forum')
      api.topic.all({forum: null}, function (err, topics) {
        if (err) {
          log('Error fetching all orphan topics: %s', err)
          return
        }

        if (!topics.length) return

        topics.forEach(function (topic) {
          topic.forum = forum
          topic.save(function (err) {
            if (err) {
              log('There was an error when trying to un-orphanate topic with id: %s', topic._id, err)
            } else {
              log('Un-orphanated topic with id: %s', topic._id)
            }
          })
        })
      })
    }

    return res.json(200, expose(keys)(forum))
  })
})

app.put('/forum/:id',
  restrict,
  maintenance,
  middlewares.findForum,
  middlewares.privileges('canEdit'),
  function (req, res) {
    if (!req.body.data) return res.status(400).send()
    if (typeof req.body.data !== 'object') return res.status(400).send()

    api.forum.update(req.forum, req.body.data, function (err, forum) {
      if (err) return _handleError(err, req, res)
      log('Forum updated successfully')
      return res.json(200, expose(keys)(forum))
    })
  }
)

app.delete('/forum/:id',
  restrict,
  maintenance,
  middlewares.findForum,
  middlewares.privileges('canDelete'),
  function (req, res) {
    var forum = req.forum

    log('Trying to delete forum: %s', forum.id)

    api.forum.del(forum, function (err) {
      if (err) return res.json(500, err)
      res.status(200).send()
    })
  }
)

/**
 * Helper functions
 */

function _handleError (err, req, res) {
  log('Error found: %s', err)
  var error = err
  if (err.errors && err.errors.text) error = err.errors.text
  if (error.type) error = error.type

  res.json(400, { error: error })
}
<|endoftext|>


<commit_before>// Karma configuration
// Generated on Sun May 18 2014 21:00:11 GMT+0000 (GMT)

module.exports = function(config) {
  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['mocha', 'chai', 'commonjs'],

    // Preprocess
    preprocessors: {
      'tests/*.js': ['commonjs']
    },

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress'],

    // web server port
    port: 9876,

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['Chrome'],

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: false
  });
};
<commit_msg>Added files in config.<commit_after>// Karma configuration
// Generated on Sun May 18 2014 21:00:11 GMT+0000 (GMT)

module.exports = function(config) {
  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['mocha', 'chai', 'commonjs'],

    // Preprocess
    preprocessors: {
      'tests/*.js': ['commonjs']
    },

    // list of files / patterns to load in the browser
    files: [
      'tests/**/*.js'
    ],

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress'],

    // web server port
    port: 9876,

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['Chrome'],

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: false
  });
};
<|endoftext|>


<commit_before>var fs = require('fs'),
	notice = require('../notice.js'),
	util = require('../util.js'),
	_conf = require('../../conf.js'),
	AppConfig = require('../appconfig.js');

var _exampleRoot = __dirname+'/../../template/',
	_appTpls = {},
	_appnameReg = /\{@appName\}/;

// 初始化所有的配置方案
fs.readdirSync(_exampleRoot).forEach(function(v){
	if (v == '..' || v=='.') return;

	var planRoot = _exampleRoot + v+'/',
		confFile = planRoot + _conf.AppConfigFile;

	if (!fs.existsSync(confFile)) {
		notice.warn('deploy', 'The plan('+v+') do not has appconf file');
		return;
	}

	_appTpls[v] = {};
	readDir(planRoot, '', _appTpls[v]);
});

// 必须检查 default是否存在
if (!_appTpls['default']) notice.error('deploy', 'Do not has `default` plan');

function readDir(root, path, appTpl){
	fs.readdirSync(root).forEach(function(v){
		if (v == '..' || v=='.') return;

		if (fs.statSync(root+v).isFile()) {
			appTpl[path+v] = fs.readFileSync(root+v);
		} else {
			readDir(root+v+'/', path+v+'/', appTpl);
		}
	});
}

module.exports = function(appName, plan){
	var root = _conf.DocumentRoot + appName +'/',
		confFile = root+_conf.AppConfigFile;
	if (!fs.existsSync(confFile)) {

		if (!plan || !_appTpls[plan]) {
			notice.warn('deploy', 'The plan('+plan+') switch to `default`');
			plan = _appTpls['default'];
		} else {
			plan = _appTpls[plan];
		}
		try {
			util.eachObject(plan, function(i, v){
				if (_appnameReg.test(v.toString())) v = v.toString().replace(_appnameReg, appName);
				util.writeFile(root+i, v);
			});

			AppConfig.init(util.parsePath(root));

			// 创建可能遗漏的目录
			var path = root+_conf.SourcePath;
			if (!fs.existsSync(path)) util.mkdirs(path);
			path = root+_conf.DynamicDataPath;
			if (!fs.existsSync(path)) util.mkdirs(path);

			notice.log('Object Init', 'Object create success');
		} catch(err) {
			notice.error('', err);
		}
	} else {
		notice.warn('Object Init', 'Object is exists', root);
	}
};

<commit_msg>修正init后appName未全部替换的问题<commit_after>var fs = require('fs'),
	notice = require('../notice.js'),
	util = require('../util.js'),
	_conf = require('../../conf.js'),
	AppConfig = require('../appconfig.js');

var _exampleRoot = __dirname+'/../../template/',
	_appTpls = {},
	_appnameReg = /\{@appName\}/g;

// 初始化所有的配置方案
fs.readdirSync(_exampleRoot).forEach(function(v){
	if (v == '..' || v=='.') return;

	var planRoot = _exampleRoot + v+'/',
		confFile = planRoot + _conf.AppConfigFile;

	if (!fs.existsSync(confFile)) {
		notice.warn('deploy', 'The plan('+v+') do not has appconf file');
		return;
	}

	_appTpls[v] = {};
	readDir(planRoot, '', _appTpls[v]);
});

// 必须检查 default是否存在
if (!_appTpls['default']) notice.error('deploy', 'Do not has `default` plan');

function readDir(root, path, appTpl){
	fs.readdirSync(root).forEach(function(v){
		if (v == '..' || v=='.') return;

		if (fs.statSync(root+v).isFile()) {
			appTpl[path+v] = fs.readFileSync(root+v);
		} else {
			readDir(root+v+'/', path+v+'/', appTpl);
		}
	});
}

module.exports = function(appName, plan){
	var root = _conf.DocumentRoot + appName +'/',
		confFile = root+_conf.AppConfigFile;
	if (!fs.existsSync(confFile)) {

		if (!plan || !_appTpls[plan]) {
			notice.warn('deploy', 'The plan('+plan+') switch to `default`');
			plan = _appTpls['default'];
		} else {
			plan = _appTpls[plan];
		}
		try {
			util.eachObject(plan, function(i, v){
				if (_appnameReg.test(v.toString())) v = v.toString().replace(_appnameReg, appName);
				util.writeFile(root+i, v);
			});

			AppConfig.init(util.parsePath(root));

			// 创建可能遗漏的目录
			var path = root+_conf.SourcePath;
			if (!fs.existsSync(path)) util.mkdirs(path);
			path = root+_conf.DynamicDataPath;
			if (!fs.existsSync(path)) util.mkdirs(path);

			notice.log('Object Init', 'Object create success');
		} catch(err) {
			notice.error('', err);
		}
	} else {
		notice.warn('Object Init', 'Object is exists', root);
	}
};

<|endoftext|>


<commit_before>/*
Copyright (c) 2011, Claus Augusti <claus@formatvorlage.de>
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the <organization> nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

"use strict";

/**
 *
 */

module.exports = function (tagmgr, resmgr, modcontainer) {
    if (!tagmgr || !resmgr || !modcontainer) { throw new Error('dependencies missing'); }
    var mod_sys                 = require('sys')
        , mod_path              = require('path')
        , mod_fs                = require('fs')
        , mod_promise           = require('promised-io/lib/promise')
        , mod_url               = require('url')
        , mod_querystring       = require('querystring')
        , Resource              = require('./resources.js').Resource
        , mod_resourcemanager   = resmgr
        , tagmanager            = tagmgr
        , logger                = require('./logger.js').getLogger(mod_path.basename(module.filename))
        , Renderer              = require('.//renderer.js')(tagmanager, modcontainer).Renderer
        , parser                = require('./parser.js').parseHtmlView
        , modulecontainer       = modcontainer
        , RAIN_INSTANCE_PARAM   = 'rain.instanceid'
        , MAX_RESPONSE_TIME     = 3000
        , ins = mod_sys.inspect
        , c = console.log;

    function handleControllerRequest (req, res, next) {
        logger.info('handleControllerRequest ' + req.url);
        (function (res) {
            modulecontainer.handleControllerRequest(req, res);
            setTimeout(function () {
                if (!res.finished) {
                    res.writeHead(404, { 'Content-Type' : 'text/plain'} );
                    res.end('timeout');
                }
            }, MAX_RESPONSE_TIME);
        })(res);
    }

    function handleViewRequest (req, res, next) {
        console.time('render');
        var parse         = mod_url.parse(req.url),
            query         = mod_querystring.parse(parse.query),
            outputMode    = (query['rain.output'] || 'html').toLowerCase(),
            modulename    = req.params[0],
            viewname      = req.params[1],
            module,                       // component config object associated to request
            resource,                     // view template resource of requested view
            data          = {},
            key;

        logger.info('handleViewRequest, module: ' + req.params[0] + ', view: ' + req.params[1]);
        for (key in query) {
            data['req_' + key] = query[key];
        }
        module = modulecontainer.resolveFromRequestPath(parse.pathname);
        var renderer = new Renderer(parse.pathname, outputMode, null, data);
        renderer.once('stateChanged', function (renderer) {
            if (renderer.state == Renderer.STATES.RENDERED) {
                if (renderer.mode == 'html') {
                    res.setHeader('Content-Type', 'text/html');
                    res.end(renderer.renderresult.content);
                } else {
                    res.setHeader('Content-Type', 'application/json');
                    res.end(JSON.stringify(renderer.renderresult));
                }
                console.timeEnd('render');
                //Renderer.showTree(render1);
            }
        });

        /*
        if (query[RAIN_INSTANCE_PARAM]) {
            logger.debug('requiring instance per request parameter');
            if (query[RAIN_INSTANCE_PARAM] instanceof Array) {
                query[RAIN_INSTANCE_PARAM].forEach(function (iid) { addInstance(iid, resource, renderer); });
            } else {
                addInstance(query[RAIN_INSTANCE_PARAM], resource, renderer);
            }
        }

        function addInstance (id, resource, renderer) {
            var dataurl = 'file://' + mod_path.join(__dirname, '..', 'instances', id);
            var instanceresource = mod_resourcemanager.loadResourceByUrl(dataurl);
            instanceresource.load();
            resource.addDependency(instanceresource);
            renderer.instanceResources[id] = instanceresource;
        }
        */
    }

    return {
        'handleControllerRequest'   : handleControllerRequest
        , 'handleViewRequest'       : handleViewRequest
    }
}
<commit_msg>added module container as a dependency to Renderer<commit_after>/*
Copyright (c) 2011, Claus Augusti <claus@formatvorlage.de>
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the <organization> nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

"use strict";

/**
 *
 */

module.exports = function (tagmgr, resmgr, modcontainer) {
    if (!tagmgr || !resmgr || !modcontainer) { throw new Error('dependencies missing'); }
    var mod_sys                 = require('sys')
        , mod_path              = require('path')
        , mod_fs                = require('fs')
        , mod_promise           = require('promised-io/lib/promise')
        , mod_url               = require('url')
        , mod_querystring       = require('querystring')
        , Resource              = require('./resources.js').Resource
        , mod_resourcemanager   = resmgr
        , tagmanager            = tagmgr
        , logger                = require('./logger.js').getLogger(mod_path.basename(module.filename))
        , Renderer              = require('.//renderer.js')(tagmanager, modcontainer, mod_resourcemanager).Renderer
        , parser                = require('./parser.js').parseHtmlView
        , modulecontainer       = modcontainer
        , RAIN_INSTANCE_PARAM   = 'rain.instanceid'
        , MAX_RESPONSE_TIME     = 3000
        , ins = mod_sys.inspect
        , c = console.log;

    function handleControllerRequest (req, res, next) {
        logger.info('handleControllerRequest ' + req.url);
        (function (res) {
            modulecontainer.handleControllerRequest(req, res);
            setTimeout(function () {
                if (!res.finished) {
                    res.writeHead(404, { 'Content-Type' : 'text/plain'} );
                    res.end('timeout');
                }
            }, MAX_RESPONSE_TIME);
        })(res);
    }

    function handleViewRequest (req, res, next) {
        console.time('render');
        var parse         = mod_url.parse(req.url),
            query         = mod_querystring.parse(parse.query),
            outputMode    = (query['rain.output'] || 'html').toLowerCase(),
            modulename    = req.params[0],
            viewname      = req.params[1],
            module,                       // component config object associated to request
            resource,                     // view template resource of requested view
            data          = {},
            key;

        logger.info('handleViewRequest, module: ' + req.params[0] + ', view: ' + req.params[1]);
        for (key in query) {
            data['req_' + key] = query[key];
        }
        module = modulecontainer.resolveFromRequestPath(parse.pathname);
        var renderer = new Renderer(parse.pathname, outputMode, null, data);
        renderer.once('stateChanged', function (renderer) {
            if (renderer.state == Renderer.STATES.RENDERED) {
                if (renderer.mode == 'html') {
                    res.setHeader('Content-Type', 'text/html');
                    res.end(renderer.renderresult.content);
                } else {
                    res.setHeader('Content-Type', 'application/json');
                    res.end(JSON.stringify(renderer.renderresult));
                }
                console.timeEnd('render');
                //Renderer.showTree(render1);
            }
        });

        /*
        if (query[RAIN_INSTANCE_PARAM]) {
            logger.debug('requiring instance per request parameter');
            if (query[RAIN_INSTANCE_PARAM] instanceof Array) {
                query[RAIN_INSTANCE_PARAM].forEach(function (iid) { addInstance(iid, resource, renderer); });
            } else {
                addInstance(query[RAIN_INSTANCE_PARAM], resource, renderer);
            }
        }

        function addInstance (id, resource, renderer) {
            var dataurl = 'file://' + mod_path.join(__dirname, '..', 'instances', id);
            var instanceresource = mod_resourcemanager.loadResourceByUrl(dataurl);
            instanceresource.load();
            resource.addDependency(instanceresource);
            renderer.instanceResources[id] = instanceresource;
        }
        */
    }

    return {
        'handleControllerRequest'   : handleControllerRequest
        , 'handleViewRequest'       : handleViewRequest
    }
}
<|endoftext|>


<commit_before>/**
 * Implementation of standard Array methods (introduced in ECMAScript 5th
 * edition) and shorthand generics (JavaScript 1.8.5)
 *
 * Copyright (c) 2013 Alex K @plusdude
 * http://opensource.org/licenses/MIT
 */
(function (global, infinity, undefined) {
    /*jshint bitwise:false, maxlen:95, plusplus:false, validthis:true*/
    "use strict";

    /**
     * Local references to constructors in global scope.
     * This may speed up access and slightly reduce file size of minified version.
     */
    var Array = global.Array;
    var Object = global.Object;
    var Math = global.Math;
    var Number = global.Number;

    /**
     * Converts argument to an integral numeric value.
     * @see http://www.ecma-international.org/ecma-262/5.1/#sec-9.4
     */
    function toInteger(value) {
        var number;

        // let number be the result of calling ToNumber on the input argument
        number = Number(value);
        return (
            // if number is NaN, return 0
            number !== number ? 0 :

            // if number is 0, Infinity, or -Infinity, return number
            0 === number || infinity === number || -infinity === number ? number :

            // return the result of computing sign(number) * floor(abs(number))
            (0 < number || -1) * Math.floor(Math.abs(number))
        );
    }

    /**
     * Returns a shallow copy of a portion of an array.
     * @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.10
     */
    function slice(begin, end) {
        /*jshint newcap:false*/
        var result, elements, length, index, count;

        // convert elements to object
        elements = Object(this);

        // convert length to unsigned 32 bit integer
        length = elements.length >>> 0;

        // calculate begin index, if is set
        if (undefined !== begin) {

            // convert to integer
            begin = toInteger(begin);

            // handle -begin, begin > length
            index = 0 > begin ? Math.max(length + begin, 0) : Math.min(begin, length);
        } else {
            // default value
            index = 0;
        }
        // calculate end index, if is set
        if (undefined !== end) {

            // convert to integer
            end = toInteger(end);

            // handle -end, end > length
            length = 0 > end ? Math.max(length + end, 0) : Math.min(end, length);
        }
        // create result array
        result = new Array(length - index);

        // iterate over elements
        for (count = 0; index < length; ++index, ++count) {

            // current index exists
            if (index in elements) {

                // copy current element to result array
                result[count] = elements[index];
            }
        }
        return result;
    }

    /**
     * Returns the first index at which a given element
     * can be found in the array.
     * @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.14
     */
    function indexOf(target, begin) {
        /*jshint newcap:false*/
        var elements, length, index;

        // convert elements to object
        elements = Object(this);

        // convert length to unsigned 32 bit integer
        length = elements.length >>> 0;

        // calculate begin index, if is set
        if (undefined !== begin) {

            // convert to integer
            begin = toInteger(begin);

            // handle -begin, begin > length
            index = 0 > begin ? Math.max(length + begin, 0) : Math.min(begin, length);
        } else {
            // default value
            index = 0;
        }
        // iterate over elements
        for (; index < length; ++index) {

            // current index exists, target element is equal to current element
            if (index in elements && target === elements[index]) {

                // break loop, target element found
                return index;
            }
        }
        // target element not found
        return -1;
    }

    /**
     * Returns the last index at which a given element
     * can be found in the array.
     * @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.15
     */
    function lastIndexOf(target, begin) {
        /*jshint newcap:false*/
        var elements, length, index;

        // convert elements to object
        elements = Object(this);

        // convert length to unsigned 32 bit integer
        length = elements.length >>> 0;

        // calculate begin index, if is set
        if (undefined !== begin) {

            // convert to integer
            begin = toInteger(begin);

            // handle -begin, begin > length - 1
            index = 0 > begin ? length - Math.abs(begin) : Math.min(begin, length - 1);
        } else {
            // default value
            index = length - 1;
        }
        // iterate over elements backwards
        for (; -1 < index; --index) {

            // current index exists, target element is equal to current element
            if (index in elements && target === elements[index]) {

                // break loop, target element found
                return index;
            }
        }
        // target element not found
        return -1;
    }

    /**
     * Executes a provided function once per array element.
     * @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18
     */
    function forEach(callback, scope) {
        /*jshint newcap:false*/
        var elements, length, index;

        // convert elements to object
        elements = Object(this);

        // make sure callback is a function
        requireFunction(callback);

        // convert length to unsigned 32 bit integer
        length = elements.length >>> 0;

        // iterate over elements
        for (index = 0; index < length; ++index) {

            // current index exists
            if (index in elements) {

                // execute callback
                callback.call(scope, elements[index], index, elements);
            }
        }
    }

    /**
     * Tests whether all elements in the array pass the test
     * implemented by the provided function.
     * @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.16
     */
    function every(callback, scope) {
        /*jshint newcap:false*/
        var elements, length, index;

        // convert elements to object
        elements = Object(this);

        // make sure callback is a function
        requireFunction(callback);

        // convert length to unsigned 32 bit integer
        length = elements.length >>> 0;

        // iterate over elements
        for (index = 0; index < length; ++index) {

            // current index exists
            if (index in elements &&

            // callback returns false
            !callback.call(scope, elements[index], index, elements)) {

                // break loop, test failed
                return false;
            }
        }
        // test passed, controversy began..
        return true;
    }

    /**
     * Tests whether some element in the array passes the test
     * implemented by the provided function.
     * @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.17
     */
    function some(callback, scope) {
        /*jshint newcap:false*/
        var elements, length, index;

        // convert elements to object
        elements = Object(this);

        // make sure callback is a function
        requireFunction(callback);

        // convert length to unsigned 32 bit integer
        length = elements.length >>> 0;

        // iterate over elements
        for (index = 0; index < length; ++index) {

            // current index exists
            if (index in elements &&

            // callback returns true
            callback.call(scope, elements[index], index, elements)) {

                // break loop, test passed
                return true;
            }
        }
        // test failed
        return false;
    }

    /**
     * Creates a new array with all elements that pass the test
     * implemented by the provided function.
     * @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.20
     */
    function filter(callback, scope) {
        /*jshint newcap:false*/
        var result = [], elements, length, index, count;

        // convert elements to object
        elements = Object(this);

        // make sure callback is a function
        requireFunction(callback);

        // convert length to unsigned 32 bit integer
        length = elements.length >>> 0;

        // iterate over elements
        for (index = count = 0; index < length; ++index) {

            // current index exists
            if (index in elements &&

            // callback returns true
            callback.call(scope, elements[index], index, elements)) {

                // copy current element to result array
                result[count++] = elements[index];
            }
        }
        return result;
    }

    /**
     * Creates a new array with the results of calling a provided function
     * on every element in this array.
     * @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.19
     */
    function map(callback, scope) {
        /*jshint newcap:false*/
        var result = [], elements, length, index;

        // convert elements to object
        elements = Object(this);

        // make sure callback is a function
        requireFunction(callback);

        // convert length to unsigned 32 bit integer
        length = elements.length >>> 0;

        // iterate over elements
        for (index = 0; index < length; ++index) {

            // current index exists
            if (index in elements) {

                // copy a return value of callback to result array
                result[index] = callback.call(scope, elements[index], index, elements);
            }
        }
        return result;
    }

    /**
     * Apply a function against values of the array (from left-to-right)
     * as to reduce it to a single value.
     * @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.21
     */
    function reduce(callback, value) {
        /*jshint newcap:false*/
        var elements, isset, length, index;

        // convert elements to object
        elements = Object(this);

        // make sure callback is a function
        requireFunction(callback);

        // status of the initial value
        isset = undefined !== value;

        // convert length to unsigned 32 bit integer
        length = elements.length >>> 0;

        // iterate over elements
        for (index = 0; index < length; ++index) {

            // current index exists
            if (index in elements) {

                // initial value is set
                if (isset) {

                    // replace initial value with a return value of callback
                    value = callback(value, elements[index], index, elements);
                } else {
                    // current element becomes initial value
                    value = elements[index];

                    // status of the initial value
                    isset = true;
                }
            }
        }
        // make sure the initial value exists after iteration
        requireValue(isset);
        return value;
    }

    /**
     * Apply a function against values of the array (from right-to-left)
     * as to reduce it to a single value.
     * @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.22
     */
    function reduceRight(callback, value) {
        /*jshint newcap:false*/
        var elements, isset, index;

        // convert elements to object
        elements = Object(this);

        // make sure callback is a function
        requireFunction(callback);

        // status of the initial value
        isset = undefined !== value;

        // index of the last element
        index = (elements.length >>> 0) - 1;

        // iterate over elements backwards
        for (; -1 < index; --index) {

            // current index exists
            if (index in elements) {

                // initial value is set
                if (isset) {

                    // replace initial value with a return value of callback
                    value = callback(value, elements[index], index, elements);
                } else {
                    // current element becomes initial value
                    value = elements[index];

                    // status of the initial value
                    isset = true;
                }
            }
        }
        // make sure the initial value exists after iteration
        requireValue(isset);
        return value;
    }

    /**
     * Returns true if an argument is an array, false if it is not.
     * @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.3.2
     */
    function isArray(value) {
        return "[object Array]" === Object.prototype.toString.call(value);
    }

    /**
     * Helper function.
     * Tests if an argument is callable and throws an error if it is not.
     */
    function requireFunction(value) {
        if ("[object Function]" !== Object.prototype.toString.call(value)) {
            throw new Error(value + " is not a function");
        }
    }

    /**
     * Helper function.
     * Throws an error if an argument can be converted to true.
     */
    function requireValue(isset) {
        if (!isset) {
            throw new Error("reduce of empty array with no initial value");
        }
    }

    /**
     * Helper function.
     * Tests implementation of standard Array method.
     */
    function supportsStandard(key) {
        var support = true;
        if (Array.prototype[key]) {
            try {
                Array.prototype[key].call(undefined, /test/, null);
                support = false;
            } catch (e) {}
        } else {
            support = false;
        }
        return support;
    }

    /**
     * Helper function.
     * Tests implementation of generic Array method.
     */
    function supportsGeneric(key) {
        var support = true;
        if (Array[key]) {
            try {
                Array[key](undefined, /test/, null);
                support = false;
            } catch (e) {}
        } else {
            support = false;
        }
        return support;
    }

    /**
     * Helper function.
     * Assigns method to Array constructor.
     */
    function extendArray(key) {
        if (!supportsGeneric(key)) {
            Array[key] = makeStatic(key);
        }
    }

    /**
     * Helper function.
     * Creates generic method from an instance method.
     */
    function makeStatic(key) {
        return function (value) {
            var extras;

            if (undefined === value || null === value) {
                throw new Error("Array.prototype." + key + " called on " + value);
            }
            extras = Array.prototype.slice.call(arguments, 1);
            return Array.prototype[key].apply(value, extras);
        };
    }

    /**
     * Assign ES-5 methods to Array constructor,
     * and Array prototype.
     */
    var ES5 = {
        "indexOf": indexOf,
        "lastIndexOf": lastIndexOf,
        "forEach": forEach,
        "every": every,
        "some": some,
        "filter": filter,
        "map": map,
        "reduce": reduce,
        "reduceRight": reduceRight
    };

    for (var key in ES5) {
        if (ES5.hasOwnProperty(key)) {

            if (!supportsStandard(key)) {
                Array.prototype[key] = ES5[key];
            }
            extendArray(key);
        }
    }
    Array.isArray = Array.isArray || isArray;

    /**
     * Assign ES-3 methods to Array constructor.
     * The toString method is omitted.
     */
    var ES3 = [
        "concat",
        "join",
        "slice",
        "pop",
        "push",
        "reverse",
        "shift",
        "sort",
        "splice",
        "unshift"
    ];
    ES3.forEach(extendArray);

    /**
     * Support: IE < 9
     * Make the slice method to work with the DOM nodes.
     */
    try {
        Array.slice({});
    } catch (e) {
        Array.prototype.slice = slice;
    }

}(this, 1 / 0));
<commit_msg>Update comments<commit_after>/**
 * Implementation of standard Array methods (introduced in ECMAScript 5th
 * edition) and shorthand generics (JavaScript 1.8.5)
 *
 * Copyright (c) 2013 Alex K @plusdude
 * http://opensource.org/licenses/MIT
 */
(function (global, infinity, undefined) {
    /*jshint bitwise:false, maxlen:95, plusplus:false, validthis:true*/
    "use strict";

    /**
     * Local references to constructors at global scope.
     * This may speed up access and slightly reduce file size of minified version.
     */
    var Array = global.Array;
    var Object = global.Object;
    var Math = global.Math;
    var Number = global.Number;

    /**
     * Converts argument to an integral numeric value.
     * @see http://www.ecma-international.org/ecma-262/5.1/#sec-9.4
     */
    function toInteger(value) {
        var number;

        // let number be the result of calling ToNumber on the input argument
        number = Number(value);
        return (
            // if number is NaN, return 0
            number !== number ? 0 :

            // if number is 0, Infinity, or -Infinity, return number
            0 === number || infinity === number || -infinity === number ? number :

            // return the result of computing sign(number) * floor(abs(number))
            (0 < number || -1) * Math.floor(Math.abs(number))
        );
    }

    /**
     * Returns a shallow copy of a portion of an array.
     * @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.10
     */
    function slice(begin, end) {
        /*jshint newcap:false*/
        var result, elements, length, index, count;

        // convert elements to object
        elements = Object(this);

        // convert length to unsigned 32 bit integer
        length = elements.length >>> 0;

        // calculate begin index, if is set
        if (undefined !== begin) {

            // convert to integer
            begin = toInteger(begin);

            // handle -begin, begin > length
            index = 0 > begin ? Math.max(length + begin, 0) : Math.min(begin, length);
        } else {
            // default value
            index = 0;
        }
        // calculate end index, if is set
        if (undefined !== end) {

            // convert to integer
            end = toInteger(end);

            // handle -end, end > length
            length = 0 > end ? Math.max(length + end, 0) : Math.min(end, length);
        }
        // create result array
        result = new Array(length - index);

        // iterate over elements
        for (count = 0; index < length; ++index, ++count) {

            // current index exists
            if (index in elements) {

                // copy current element to result array
                result[count] = elements[index];
            }
        }
        return result;
    }

    /**
     * Returns the first index at which a given element
     * can be found in the array.
     * @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.14
     */
    function indexOf(target, begin) {
        /*jshint newcap:false*/
        var elements, length, index;

        // convert elements to object
        elements = Object(this);

        // convert length to unsigned 32 bit integer
        length = elements.length >>> 0;

        // calculate begin index, if is set
        if (undefined !== begin) {

            // convert to integer
            begin = toInteger(begin);

            // handle -begin, begin > length
            index = 0 > begin ? Math.max(length + begin, 0) : Math.min(begin, length);
        } else {
            // default value
            index = 0;
        }
        // iterate over elements
        for (; index < length; ++index) {

            // current index exists, target element is equal to current element
            if (index in elements && target === elements[index]) {

                // break loop, target element found
                return index;
            }
        }
        // target element not found
        return -1;
    }

    /**
     * Returns the last index at which a given element
     * can be found in the array.
     * @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.15
     */
    function lastIndexOf(target, begin) {
        /*jshint newcap:false*/
        var elements, length, index;

        // convert elements to object
        elements = Object(this);

        // convert length to unsigned 32 bit integer
        length = elements.length >>> 0;

        // calculate begin index, if is set
        if (undefined !== begin) {

            // convert to integer
            begin = toInteger(begin);

            // handle -begin, begin > length - 1
            index = 0 > begin ? length - Math.abs(begin) : Math.min(begin, length - 1);
        } else {
            // default value
            index = length - 1;
        }
        // iterate over elements backwards
        for (; -1 < index; --index) {

            // current index exists, target element is equal to current element
            if (index in elements && target === elements[index]) {

                // break loop, target element found
                return index;
            }
        }
        // target element not found
        return -1;
    }

    /**
     * Executes a provided function once per array element.
     * @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18
     */
    function forEach(callback, scope) {
        /*jshint newcap:false*/
        var elements, length, index;

        // convert elements to object
        elements = Object(this);

        // make sure callback is a function
        requireFunction(callback);

        // convert length to unsigned 32 bit integer
        length = elements.length >>> 0;

        // iterate over elements
        for (index = 0; index < length; ++index) {

            // current index exists
            if (index in elements) {

                // execute callback
                callback.call(scope, elements[index], index, elements);
            }
        }
    }

    /**
     * Tests whether all elements in the array pass the test
     * implemented by the provided function.
     * @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.16
     */
    function every(callback, scope) {
        /*jshint newcap:false*/
        var elements, length, index;

        // convert elements to object
        elements = Object(this);

        // make sure callback is a function
        requireFunction(callback);

        // convert length to unsigned 32 bit integer
        length = elements.length >>> 0;

        // iterate over elements
        for (index = 0; index < length; ++index) {

            // current index exists
            if (index in elements &&

            // callback returns false
            !callback.call(scope, elements[index], index, elements)) {

                // break loop, test failed
                return false;
            }
        }
        // test passed, controversy began..
        return true;
    }

    /**
     * Tests whether some element in the array passes the test
     * implemented by the provided function.
     * @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.17
     */
    function some(callback, scope) {
        /*jshint newcap:false*/
        var elements, length, index;

        // convert elements to object
        elements = Object(this);

        // make sure callback is a function
        requireFunction(callback);

        // convert length to unsigned 32 bit integer
        length = elements.length >>> 0;

        // iterate over elements
        for (index = 0; index < length; ++index) {

            // current index exists
            if (index in elements &&

            // callback returns true
            callback.call(scope, elements[index], index, elements)) {

                // break loop, test passed
                return true;
            }
        }
        // test failed
        return false;
    }

    /**
     * Creates a new array with all elements that pass the test
     * implemented by the provided function.
     * @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.20
     */
    function filter(callback, scope) {
        /*jshint newcap:false*/
        var result = [], elements, length, index, count;

        // convert elements to object
        elements = Object(this);

        // make sure callback is a function
        requireFunction(callback);

        // convert length to unsigned 32 bit integer
        length = elements.length >>> 0;

        // iterate over elements
        for (index = count = 0; index < length; ++index) {

            // current index exists
            if (index in elements &&

            // callback returns true
            callback.call(scope, elements[index], index, elements)) {

                // copy current element to result array
                result[count++] = elements[index];
            }
        }
        return result;
    }

    /**
     * Creates a new array with the results of calling a provided function
     * on every element in this array.
     * @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.19
     */
    function map(callback, scope) {
        /*jshint newcap:false*/
        var result = [], elements, length, index;

        // convert elements to object
        elements = Object(this);

        // make sure callback is a function
        requireFunction(callback);

        // convert length to unsigned 32 bit integer
        length = elements.length >>> 0;

        // iterate over elements
        for (index = 0; index < length; ++index) {

            // current index exists
            if (index in elements) {

                // copy a return value of callback to result array
                result[index] = callback.call(scope, elements[index], index, elements);
            }
        }
        return result;
    }

    /**
     * Apply a function against values of the array (from left-to-right)
     * as to reduce it to a single value.
     * @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.21
     */
    function reduce(callback, value) {
        /*jshint newcap:false*/
        var elements, isset, length, index;

        // convert elements to object
        elements = Object(this);

        // make sure callback is a function
        requireFunction(callback);

        // status of the initial value
        isset = undefined !== value;

        // convert length to unsigned 32 bit integer
        length = elements.length >>> 0;

        // iterate over elements
        for (index = 0; index < length; ++index) {

            // current index exists
            if (index in elements) {

                // initial value is set
                if (isset) {

                    // replace initial value with a return value of callback
                    value = callback(value, elements[index], index, elements);
                } else {
                    // current element becomes initial value
                    value = elements[index];

                    // status of the initial value
                    isset = true;
                }
            }
        }
        // make sure the initial value exists after iteration
        requireValue(isset);
        return value;
    }

    /**
     * Apply a function against values of the array (from right-to-left)
     * as to reduce it to a single value.
     * @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.22
     */
    function reduceRight(callback, value) {
        /*jshint newcap:false*/
        var elements, isset, index;

        // convert elements to object
        elements = Object(this);

        // make sure callback is a function
        requireFunction(callback);

        // status of the initial value
        isset = undefined !== value;

        // index of the last element
        index = (elements.length >>> 0) - 1;

        // iterate over elements backwards
        for (; -1 < index; --index) {

            // current index exists
            if (index in elements) {

                // initial value is set
                if (isset) {

                    // replace initial value with a return value of callback
                    value = callback(value, elements[index], index, elements);
                } else {
                    // current element becomes initial value
                    value = elements[index];

                    // status of the initial value
                    isset = true;
                }
            }
        }
        // make sure the initial value exists after iteration
        requireValue(isset);
        return value;
    }

    /**
     * Returns true if an argument is an array, false if it is not.
     * @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.3.2
     */
    function isArray(value) {
        return "[object Array]" === Object.prototype.toString.call(value);
    }

    /**
     * Tests if an argument is callable and throws an error if it is not.
     * @private
     */
    function requireFunction(value) {
        if ("[object Function]" !== Object.prototype.toString.call(value)) {
            throw new Error(value + " is not a function");
        }
    }

    /**
     * Throws an error if an argument can be converted to true.
     * @private
     */
    function requireValue(isset) {
        if (!isset) {
            throw new Error("reduce of empty array with no initial value");
        }
    }

    /**
     * Tests implementation of standard Array method.
     * @private
     */
    function supportsStandard(key) {
        var support = true;

        // a method exists
        if (Array.prototype[key]) {
            try {
                // apply dummy arguments
                Array.prototype[key].call(undefined, /test/, null);

                // passed? implemented wrong
                support = false;
            } catch (e) {
                // do nothing
            }
        } else {
            support = false;
        }
        return support;
    }

    /**
     * Tests implementation of generic Array method.
     * @private
     */
    function supportsGeneric(key) {
        var support = true;

        // a method exists
        if (Array[key]) {
            try {
                // apply dummy arguments
                Array[key](undefined, /test/, null);

                // passed? implemented wrong
                support = false;
            } catch (e) {
                // do nothing
            }
        } else {
            support = false;
        }
        return support;
    }

    /**
     * Assigns method to Array constructor.
     * @private
     */
    function extendArray(key) {
        if (!supportsGeneric(key)) {
            Array[key] = createGeneric(key);
        }
    }

    /**
     * Creates generic method from an instance method.
     * @private
     */
    function createGeneric(key) {
        /** @public */
        return function (elements) {
            var list;

            if (undefined === elements || null === elements) {
                throw new Error("Array.prototype." + key + " called on " + elements);
            }
            list = Array.prototype.slice.call(arguments, 1);
            return Array.prototype[key].apply(elements, list);
        };
    }

    /**
     * Assign ES-5 methods to Array constructor,
     * and Array prototype.
     */
    var ES5 = {
        "indexOf": indexOf,
        "lastIndexOf": lastIndexOf,
        "forEach": forEach,
        "every": every,
        "some": some,
        "filter": filter,
        "map": map,
        "reduce": reduce,
        "reduceRight": reduceRight
    };
    for (var key in ES5) {
        if (ES5.hasOwnProperty(key)) {

            if (!supportsStandard(key)) {
                Array.prototype[key] = ES5[key];
            }
            extendArray(key);
        }
    }
    Array.isArray = Array.isArray || isArray;

    /**
     * Assign ES-3 methods to Array constructor.
     * The toString method is omitted.
     */
    var ES3 = [
        "concat",
        "join",
        "slice",
        "pop",
        "push",
        "reverse",
        "shift",
        "sort",
        "splice",
        "unshift"
    ];
    ES3.forEach(extendArray);

    /**
     * Make the slice method to work with the DOM nodes.
     * Support: IE < 9
     */
    try {
        Array.slice({});
    } catch (e) {
        Array.prototype.slice = slice;
    }

}(this, 1 / 0));
<|endoftext|>


<commit_before>
var ELEMENT = 'tag';
var ATTRIBUTE = 'attr';
var TEXT = 'text';
var COMMENT = 'comment';

function getContext(module, extContext){
  var BaseContext = module.at_base_context;

  if (!BaseContext)
  {
    var tmpl = module;
    var typeMap = {};
    typeMap[tmpl.TYPE_ELEMENT] = ELEMENT;
    typeMap[tmpl.TYPE_ATTRIBUTE] = ATTRIBUTE;
    typeMap[4] = ATTRIBUTE;
    typeMap[5] = ATTRIBUTE;
    typeMap[6] = ATTRIBUTE;
    typeMap[tmpl.TYPE_TEXT] = TEXT;
    typeMap[tmpl.TYPE_COMMENT] = COMMENT;

    BaseContext = module.at_base_context = function(ext){
      if (ext)
        for (var key in ext)
          this[key] = ext[key];
    };
    BaseContext.prototype = {
      version: module.DECLARATION_VERSION || 1,
      hasBindings: function(token){
        return !!token[tmpl.TOKEN_BINDINGS];
      },
      getAttrs: function(token){
        return (token[tmpl.TOKEN_TYPE] == tmpl.TYPE_ELEMENT && token[tmpl.ELEMENT_ATTRS]) || [];
      },
      getChildren: function(token){
        return (token[tmpl.TOKEN_TYPE] == tmpl.TYPE_ELEMENT && token[tmpl.ELEMENT_CHILDS]) || [];
      },
      tokenType: function(token){
        return typeMap[token[tmpl.TOKEN_TYPE]];
      },
      tokenName: function(token){
        switch (token[tmpl.TOKEN_TYPE])
        {
          case tmpl.TYPE_ELEMENT:
            return token[tmpl.ELEMENT_NAME];

          case tmpl.TYPE_ATTRIBUTE:
            return token[tmpl.ATTR_NAME];

          case 4:
            return 'class';

          case 5:
            return 'style';

          case 6:
            return 'event-' + token[1];

          default:
            return '#' + token[tmpl.TOKEN_TYPE];
        }
      },
      tokenValue: function(token, value){
        switch (token[tmpl.TOKEN_TYPE])
        {
          case tmpl.TYPE_ATTRIBUTE:
            if (arguments.length > 1)
              token[tmpl.ATTR_VALUE] = value;
            return token[tmpl.ATTR_VALUE] || '';

          case 4:
          case 5:
            if (arguments.length > 1)
              token[tmpl.ATTR_VALUE - 1] = value;
            return token[tmpl.ATTR_VALUE - 1] || '';

          case 6:
            if (arguments.length > 1)
              token[2] = value;
            return token[2] || token[1];

          case tmpl.TYPE_TEXT:
            if (arguments.length > 1)
              token[tmpl.TEXT_VALUE] = value;
            return token[tmpl.TEXT_VALUE];

          case tmpl.TYPE_COMMENT:
            if (arguments.length > 1)
              token[tmpl.COMMENT_VALUE] = value;
            return token[tmpl.COMMENT_VALUE];

          default:
            return '';
        }
      },
      walk: function(ast, handlers, parentToken, stIdx){
        for (var i = stIdx || 0, token; token = ast[i]; i++)
        {
          var tokenType = this.tokenType(token);
          var handler = handlers[tokenType];

          if (typeof handler == 'function')
            handler.call(this, token, parentToken);

          if (tokenType == ELEMENT)
          {
            switch (this.version)
            {
              case 1:
                var attrs = this.getAttrs(token);
                if (attrs)
                  this.walk(attrs, handlers, token);

                var children = this.getChildren(token);
                if (children)
                  this.walk(children, handlers, token);
                break;

              case 2:
              case 3:
                this.walk(token, handlers, token, 4);
                break;

              default:
                throw new Error('[ERROR] unsupported template declaration version: ' + this.version);
                process.exit(8);
            }
          }
        }
      },
      getBindingClassNames: function(binding){
        switch (this.version)
        {
          case 1:
          case 2:
            // unpredictable
            if (binding.length == 2 || binding[2] == -1)
              return false;

            if (binding.length == 3)
              // bool
              return [binding[0] + binding[1]];
            else
              // enum
              return binding[3].map(function(name){
                return binding[0] + name;
              });

          case 3:
            if (Array.isArray(binding[0]))
              return binding[0];

            switch (binding[2])
            {
              case tmpl.CLASS_BINDING_BOOL:
                return [binding[0] + binding[3]];

              case tmpl.CLASS_BINDING_ENUM:
                return binding[5].map(function(name){
                  return binding[0] + name;
                });
              default:
                throw new Error('[ERROR] unknown binding type: ' + binding[2]);
            }

          default:
            throw new Error('[ERROR] unsupported version: ' + this.version);
        }
      },
      setBindingClassNames: function(binding, classes){
        switch (this.version)
        {
          case 1:
          case 2:
            switch (binding.length)
            {
              case 3: // bool
                binding[0] = '';
                binding[3] = Array.isArray(classes) ? classes[0] : classes;
                return;

              case 4: // enum
                binding[0] = '';
                binding[4] = Array.isArray(classes) ? classes : [classes];
                return;
            }
            break;

          case 3:
            binding[0] = Array.isArray(classes) ? classes : [classes];
            binding[3] = 0; // drop name as prefix overrided too
            break;

          default:
            throw new Error('[ERROR] unsupported version: ' + this.version);
        }
      }
    };
  }

  return new BaseContext(extContext);
}

function resolveContext(module, extContext){
  if (module && module.Template)
    return getContext(module, extContext);
  else
    throw new Error('[ERROR] basis.template module not found');
}

module.exports = {
  walk: function(ast, module, handlers, extContext){
    return resolveContext(module, extContext).walk(ast, handlers);
  },
  getBindingClassNames: function(module, token){
    return resolveContext(module).getBindingClassNames(token);
  },
  setBindingClassNames: function(module, token, classNames){
    return resolveContext(module).setBindingClassNames(token, classNames);
  },
  translate: function(ast){
    return JSON.stringify(ast);
  }
};
<commit_msg>extract: fix unpredictable binding processing in declaration v3<commit_after>
var ELEMENT = 'tag';
var ATTRIBUTE = 'attr';
var TEXT = 'text';
var COMMENT = 'comment';

function getContext(module, extContext){
  var BaseContext = module.at_base_context;

  if (!BaseContext)
  {
    var tmpl = module;
    var typeMap = {};
    typeMap[tmpl.TYPE_ELEMENT] = ELEMENT;
    typeMap[tmpl.TYPE_ATTRIBUTE] = ATTRIBUTE;
    typeMap[4] = ATTRIBUTE;
    typeMap[5] = ATTRIBUTE;
    typeMap[6] = ATTRIBUTE;
    typeMap[tmpl.TYPE_TEXT] = TEXT;
    typeMap[tmpl.TYPE_COMMENT] = COMMENT;

    BaseContext = module.at_base_context = function(ext){
      if (ext)
        for (var key in ext)
          this[key] = ext[key];
    };
    BaseContext.prototype = {
      version: module.DECLARATION_VERSION || 1,
      hasBindings: function(token){
        return !!token[tmpl.TOKEN_BINDINGS];
      },
      getAttrs: function(token){
        return (token[tmpl.TOKEN_TYPE] == tmpl.TYPE_ELEMENT && token[tmpl.ELEMENT_ATTRS]) || [];
      },
      getChildren: function(token){
        return (token[tmpl.TOKEN_TYPE] == tmpl.TYPE_ELEMENT && token[tmpl.ELEMENT_CHILDS]) || [];
      },
      tokenType: function(token){
        return typeMap[token[tmpl.TOKEN_TYPE]];
      },
      tokenName: function(token){
        switch (token[tmpl.TOKEN_TYPE])
        {
          case tmpl.TYPE_ELEMENT:
            return token[tmpl.ELEMENT_NAME];

          case tmpl.TYPE_ATTRIBUTE:
            return token[tmpl.ATTR_NAME];

          case 4:
            return 'class';

          case 5:
            return 'style';

          case 6:
            return 'event-' + token[1];

          default:
            return '#' + token[tmpl.TOKEN_TYPE];
        }
      },
      tokenValue: function(token, value){
        switch (token[tmpl.TOKEN_TYPE])
        {
          case tmpl.TYPE_ATTRIBUTE:
            if (arguments.length > 1)
              token[tmpl.ATTR_VALUE] = value;
            return token[tmpl.ATTR_VALUE] || '';

          case 4:
          case 5:
            if (arguments.length > 1)
              token[tmpl.ATTR_VALUE - 1] = value;
            return token[tmpl.ATTR_VALUE - 1] || '';

          case 6:
            if (arguments.length > 1)
              token[2] = value;
            return token[2] || token[1];

          case tmpl.TYPE_TEXT:
            if (arguments.length > 1)
              token[tmpl.TEXT_VALUE] = value;
            return token[tmpl.TEXT_VALUE];

          case tmpl.TYPE_COMMENT:
            if (arguments.length > 1)
              token[tmpl.COMMENT_VALUE] = value;
            return token[tmpl.COMMENT_VALUE];

          default:
            return '';
        }
      },
      walk: function(ast, handlers, parentToken, stIdx){
        for (var i = stIdx || 0, token; token = ast[i]; i++)
        {
          var tokenType = this.tokenType(token);
          var handler = handlers[tokenType];

          if (typeof handler == 'function')
            handler.call(this, token, parentToken);

          if (tokenType == ELEMENT)
          {
            switch (this.version)
            {
              case 1:
                var attrs = this.getAttrs(token);
                if (attrs)
                  this.walk(attrs, handlers, token);

                var children = this.getChildren(token);
                if (children)
                  this.walk(children, handlers, token);
                break;

              case 2:
              case 3:
                this.walk(token, handlers, token, 4);
                break;

              default:
                throw new Error('[ERROR] unsupported template declaration version: ' + this.version);
                process.exit(8);
            }
          }
        }
      },
      getBindingClassNames: function(binding){
        switch (this.version)
        {
          case 1:
          case 2:
            // unpredictable
            if (binding.length == 2 || binding[2] == -1)
              return false;

            if (binding.length == 3)
              // bool
              return [binding[0] + binding[1]];
            else
              // enum
              return binding[3].map(function(name){
                return binding[0] + name;
              });

          case 3:
            if (Array.isArray(binding[0]))
              return binding[0];

            // unpredictable
            if (!binding[2])
              return false;

            switch (binding[2])
            {
              case tmpl.CLASS_BINDING_BOOL:
                return [binding[0] + binding[3]];

              case tmpl.CLASS_BINDING_ENUM:
                return binding[5].map(function(name){
                  return binding[0] + name;
                });
              default:
                throw new Error('[ERROR] unknown binding type: ' + binding[2]);
            }

          default:
            throw new Error('[ERROR] unsupported version: ' + this.version);
        }
      },
      setBindingClassNames: function(binding, classes){
        switch (this.version)
        {
          case 1:
          case 2:
            switch (binding.length)
            {
              case 3: // bool
                binding[0] = '';
                binding[3] = Array.isArray(classes) ? classes[0] : classes;
                return;

              case 4: // enum
                binding[0] = '';
                binding[4] = Array.isArray(classes) ? classes : [classes];
                return;
            }
            break;

          case 3:
            binding[0] = Array.isArray(classes) ? classes : [classes];
            binding[3] = 0; // drop name as prefix overrided too
            break;

          default:
            throw new Error('[ERROR] unsupported version: ' + this.version);
        }
      }
    };
  }

  return new BaseContext(extContext);
}

function resolveContext(module, extContext){
  if (module && module.Template)
    return getContext(module, extContext);
  else
    throw new Error('[ERROR] basis.template module not found');
}

module.exports = {
  walk: function(ast, module, handlers, extContext){
    return resolveContext(module, extContext).walk(ast, handlers);
  },
  getBindingClassNames: function(module, token){
    return resolveContext(module).getBindingClassNames(token);
  },
  setBindingClassNames: function(module, token, classNames){
    return resolveContext(module).setBindingClassNames(token, classNames);
  },
  translate: function(ast){
    return JSON.stringify(ast);
  }
};
<|endoftext|>


<commit_before>/*
 * Copyright 2009-2011 Mozilla Foundation and contributors
 * Licensed under the New BSD license. See LICENSE.txt or:
 * http://opensource.org/licenses/BSD-3-Clause
 */

define(function(require, exports, module) {

'use strict';

var Promise = require('util/promise');
var l10n = require('util/l10n');

var types = require('gcli/types');
var Type = require('gcli/types').Type;
var Status = require('gcli/types').Status;
var Conversion = require('gcli/types').Conversion;

function DateType(typeSpec) {
  // ECMA 5.1 §15.9.1.1
  // @see http://stackoverflow.com/questions/11526504/minimum-and-maximum-date
  typeSpec = typeSpec || {};

  this._step = typeSpec.step || 1;
  this._min = new Date(-8640000000000000);
  this._max = new Date(8640000000000000);

  if (typeSpec.min != null) {
    if (typeof typeSpec.min === 'string') {
      this._min = toDate(typeSpec.min);
    }
    else if (isDate(typeSpec.min) || typeof typeSpec.min === 'function') {
      this._min = typeSpec.min;
    }
    else {
      throw new Error('date min value must be a string a date or a function');
    }
  }

  if (typeSpec.max != null) {
    if (typeof typeSpec.max === 'string') {
      this._max = toDate(typeSpec.max);
    }
    else if (isDate(typeSpec.max) || typeof typeSpec.max === 'function') {
      this._max = typeSpec.max;
    }
    else {
      throw new Error('date max value must be a string a date or a function');
    }
  }
}

DateType.prototype = Object.create(Type.prototype);

DateType.prototype.stringify = function(value) {
  if (!isDate(value)) {
    return '';
  }

  var str = value.toISOString();

  if (value.getHours() === 0 && value.getMinutes() === 0 &&
      value.getSeconds() === 0 && value.getMilliseconds() === 0) {
    var tpos = str.indexOf('T');
    str = str.substr(0, tpos);
  }

  return str;
};

DateType.prototype.getMin = function(context) {
  if (typeof this._min === 'function') {
    return this._min(context);
  }
  if (isDate(this._min)) {
    return this._min;
  }
  return undefined;
};

DateType.prototype.getMax = function(context) {
  if (typeof this._max === 'function') {
    return this._max(context);
  }
  if (isDate(this._max)) {
    return this._max;
  }
  return undefined;
};

DateType.prototype.parse = function(arg, context) {
  var value;

  if (arg.text.replace(/\s/g, '').length === 0) {
    return Promise.resolve(new Conversion(undefined, arg, Status.INCOMPLETE, ''));
  }

  // Lots of room for improvement here: 1h ago, in two days, etc.
  // Should "1h ago" dynamically update the step?
  if (arg.text === 'now') {
    value = new Date();
  }
  else if (arg.text === 'yesterday') {
    value = new Date().setDate(new Date().getDate() - 1);
  }
  else if (arg.text === 'tomorrow') {
    value = new Date().setDate(new Date().getDate() + 1);
  }
  else {
    var millis = Date.parse(arg.text);

    if (isNaN(millis)) {
      var msg = l10n.lookupFormat('typesDateNan', [ arg.text ]);
      return Promise.resolve(new Conversion(undefined, arg, Status.ERROR, msg));
    }

    value = new Date(millis);
  }

  return Promise.resolve(new Conversion(value, arg));
};

DateType.prototype.decrement = function(value, context) {
  if (!isDate(value)) {
    return new Date();
  }

  var newValue = new Date(value);
  newValue.setDate(value.getDate() - this._step);

  if (newValue >= this.getMin(context)) {
    return newValue;
  }
  else {
    return this.getMin(context);
  }
};

DateType.prototype.increment = function(value, context) {
  if (!isDate(value)) {
    return new Date();
  }

  var newValue = new Date(value);
  newValue.setDate(value.getDate() + this._step);

  if (newValue <= this.getMax(context)) {
    return newValue;
  }
  else {
    return this.getMax();
  }
};

DateType.prototype.name = 'date';

/**
 * Utility to convert a string to a date, throwing if the date can't be
 * parsed rather than having an invalid date
 */
function toDate(str) {
  var millis = Date.parse(str);
  if (isNaN(millis)) {
    throw new Error(l10n.lookupFormat('typesDateNan', [ str ]));
  }
  return new Date(millis);
}

/**
 * Is |thing| a valid date?
 * @see http://stackoverflow.com/questions/1353684/detecting-an-invalid-date-date-instance-in-javascript
 */
function isDate(thing) {
  return Object.prototype.toString.call(thing) === '[object Date]'
          && !isNaN(thing.getTime());
};

/**
 * Registration and de-registration.
 */
exports.startup = function() {
  types.addType(DateType);
};

exports.shutdown = function() {
  types.removeType(DateType);
};

});
<commit_msg>date-773271: Use custom toISOString()<commit_after>/*
 * Copyright 2009-2011 Mozilla Foundation and contributors
 * Licensed under the New BSD license. See LICENSE.txt or:
 * http://opensource.org/licenses/BSD-3-Clause
 */

define(function(require, exports, module) {

'use strict';

var Promise = require('util/promise');
var l10n = require('util/l10n');

var types = require('gcli/types');
var Type = require('gcli/types').Type;
var Status = require('gcli/types').Status;
var Conversion = require('gcli/types').Conversion;

function DateType(typeSpec) {
  // ECMA 5.1 §15.9.1.1
  // @see http://stackoverflow.com/questions/11526504/minimum-and-maximum-date
  typeSpec = typeSpec || {};

  this._step = typeSpec.step || 1;
  this._min = new Date(-8640000000000000);
  this._max = new Date(8640000000000000);

  if (typeSpec.min != null) {
    if (typeof typeSpec.min === 'string') {
      this._min = toDate(typeSpec.min);
    }
    else if (isDate(typeSpec.min) || typeof typeSpec.min === 'function') {
      this._min = typeSpec.min;
    }
    else {
      throw new Error('date min value must be a string a date or a function');
    }
  }

  if (typeSpec.max != null) {
    if (typeof typeSpec.max === 'string') {
      this._max = toDate(typeSpec.max);
    }
    else if (isDate(typeSpec.max) || typeof typeSpec.max === 'function') {
      this._max = typeSpec.max;
    }
    else {
      throw new Error('date max value must be a string a date or a function');
    }
  }
}

DateType.prototype = Object.create(Type.prototype);

/**
 * Helper for stringify() to left pad a single digit number with a single '0'
 * so 1 -> '01', 42 -> '42', etc.
 */
function pad(number) {
  var r = String(number);
  return r.length === 1 ? '0' + r : r;
}

DateType.prototype.stringify = function(value) {
  if (!isDate(value)) {
    return '';
  }

  var str = pad(value.getFullYear()) + '-' +
            pad(value.getMonth() + 1) + '-' +
            pad(value.getDate());

  // Only add in the time if it's not midnight
  if (value.getHours() !== 0 || value.getMinutes() !== 0 ||
      value.getSeconds() !== 0 || value.getMilliseconds() !== 0) {

    // What string should we use to separate the date from the time?
    // There are 3 options:
    // 'T': This is the standard from ISO8601. i.e. 2013-05-20T11:05
    //      The good news - it's a standard. The bad news - it's weird and
    //      alien to many if not most users
    // ' ': This looks nicest, but needs escaping (which GCLI will do
    //      automatically) so it would look like: '2013-05-20 11:05'
    //      Good news: looks best, bad news: on completion we place the cursor
    //      after the final ', so repeated increment/decrement doesn't work
    // '\ ': It's possible that we could find a way to use a \ to escape the
    //      space, so the output would look like: 2013-05-20\ 11:05
    //      This would involve changes to a number of parts, and is probably
    //      too complex a solution for this problem for now
    // In the short term I'm going for ' ', and raising the priority of cursor
    // positioning on actions like increment/decrement/tab.

    str += ' ' + pad(value.getHours());
    str += ':' + pad(value.getMinutes());

    // Only add in seconds/milliseconds if there is anything to report
    if (value.getSeconds() !== 0 || value.getMilliseconds() !== 0) {
      str += ':' + pad(value.getSeconds());
      if (value.getMilliseconds() !== 0) {
        str += '.' + String((value.getUTCMilliseconds()/1000).toFixed(3)).slice(2, 5);
      }
    }
  }

  return str;
};

DateType.prototype.getMin = function(context) {
  if (typeof this._min === 'function') {
    return this._min(context);
  }
  if (isDate(this._min)) {
    return this._min;
  }
  return undefined;
};

DateType.prototype.getMax = function(context) {
  if (typeof this._max === 'function') {
    return this._max(context);
  }
  if (isDate(this._max)) {
    return this._max;
  }
  return undefined;
};

DateType.prototype.parse = function(arg, context) {
  var value;

  if (arg.text.replace(/\s/g, '').length === 0) {
    return Promise.resolve(new Conversion(undefined, arg, Status.INCOMPLETE, ''));
  }

  // Lots of room for improvement here: 1h ago, in two days, etc.
  // Should "1h ago" dynamically update the step?
  if (arg.text === 'now') {
    value = new Date();
  }
  else if (arg.text === 'yesterday') {
    value = new Date().setDate(new Date().getDate() - 1);
  }
  else if (arg.text === 'tomorrow') {
    value = new Date().setDate(new Date().getDate() + 1);
  }
  else {
    var millis = Date.parse(arg.text);

    if (isNaN(millis)) {
      var msg = l10n.lookupFormat('typesDateNan', [ arg.text ]);
      return Promise.resolve(new Conversion(undefined, arg, Status.ERROR, msg));
    }

    value = new Date(millis);
  }

  return Promise.resolve(new Conversion(value, arg));
};

DateType.prototype.decrement = function(value, context) {
  if (!isDate(value)) {
    return new Date();
  }

  var newValue = new Date(value);
  newValue.setDate(value.getDate() - this._step);

  if (newValue >= this.getMin(context)) {
    return newValue;
  }
  else {
    return this.getMin(context);
  }
};

DateType.prototype.increment = function(value, context) {
  if (!isDate(value)) {
    return new Date();
  }

  var newValue = new Date(value);
  newValue.setDate(value.getDate() + this._step);

  if (newValue <= this.getMax(context)) {
    return newValue;
  }
  else {
    return this.getMax();
  }
};

DateType.prototype.name = 'date';

/**
 * Utility to convert a string to a date, throwing if the date can't be
 * parsed rather than having an invalid date
 */
function toDate(str) {
  var millis = Date.parse(str);
  if (isNaN(millis)) {
    throw new Error(l10n.lookupFormat('typesDateNan', [ str ]));
  }
  return new Date(millis);
}

/**
 * Is |thing| a valid date?
 * @see http://stackoverflow.com/questions/1353684/detecting-an-invalid-date-date-instance-in-javascript
 */
function isDate(thing) {
  return Object.prototype.toString.call(thing) === '[object Date]'
          && !isNaN(thing.getTime());
};

/**
 * Registration and de-registration.
 */
exports.startup = function() {
  types.addType(DateType);
};

exports.shutdown = function() {
  types.removeType(DateType);
};

});
<|endoftext|>