module.exports = inherit(require('enb/lib/tech/base-tech.js'), { getName: function () { return 'files'; }, configure: function () { var logger = this.node.getLogger(); this._filesTarget = this.node.unmaskTargetName(this.getOption('filesTarget', '?.files')); this._dirsTarget = this.node.unmaskTargetName(this.getOption('dirsTarget', '?.dirs')); this._levelsTarget = this.node.unmaskTargetName(this.getOption('levelsTarget', '?.levels')); this._depsFile = this.getOption('depsTarget'); if (this._depsFile) { logger.logOptionIsDeprecated(this._filesTarget, 'enb-bem', this.getName(), 'depsTarget', 'depsFile'); logger.logOptionIsDeprecated(this._dirsTarget, 'enb-bem', this.getName(), 'depsTarget', 'depsFile'); } else { this._depsFile = this.getOption('depsFile', '?.deps.js'); } this._depsFile = this.node.unmaskTargetName(this._depsFile); }, getTargets: function () { return [ this._filesTarget, this._dirsTarget ]; }, build: function () { var _this = this, depsFilename = this.node.resolvePath(this._depsFile), filesTarget = this._filesTarget, dirsTarget = this._dirsTarget; return this.node.requireSources([this._depsFile, this._levelsTarget]) .spread(function (data, levels) { return requireSourceDeps(data, depsFilename) .then(function (sourceDeps) { var fileList = new FileList(), dirList = new FileList(), files = {}, dirs = {}; for (var i = 0, l = sourceDeps.length; i < l; i++) { var dep = sourceDeps[i], entities; if (dep.elem) { entities = levels.getElemEntities(dep.block, dep.elem, dep.mod, dep.val); } else { entities = levels.getBlockEntities(dep.block, dep.mod, dep.val); } addEntityFiles(entities); } fileList.addFiles(Object.keys(files).map(function (filename) { return files[filename]; })); dirList.addFiles(Object.keys(dirs).map(function (dirname) { return dirs[dirname]; })); function addEntityFiles(entities) { entities.files.forEach(function (file) { files[file.fullname] = file; }); entities.dirs.forEach(function (dir) { dirs[dir.fullname] = dir; }); } _this.node.resolveTarget(filesTarget, fileList); _this.node.resolveTarget(dirsTarget, dirList); }); }); }, clean: function () {} });
var User = require(global.MODEL_PATH + '/user'); var inherit = require('inherit'); module.exports = inherit({}, { 'all /index' : function (req, res) { var params = '\n'; for (var key in req.headers) { params += (key + ' : ' + req.headers[key] + '\n'); } res.render('post', { content : params }); } });
module.exports = inherit(/** @lends MakePlatform.prototype */ { /** * Конструктор. */ __constructor: function () { this._nodes = {}; this._nodeInitPromises = {}; this._cacheStorage = null; this._cache = null; this._projectConfig = null; this._cdir = null; this._languages = null; this._env = {}; this._mode = null; this._makefiles = []; this._graph = null; this._profiler = null; this._levelNamingSchemes = {}; this._sharedResources = new SharedResources(); }, /** * Инициализация make-платформы. * Создает директорию для хранения временных файлов, загружает конфиг для сборки. * @param {String} cdir Путь к директории с проектом. * @param {String} [mode] Режим сборки. Например, development. * @param {Function} [config] Функция, которая инициирует конфиг сборки. По умолчанию загружается из `.enb/make.js`. * @param {Object} [opts] * @param {Boolean} [opts.graph=false] * @returns {Promise} */ init: function (cdir, mode, config, opts) { opts = opts || {}; this._mode = mode = mode || process.env.YENV || 'development'; this._cdir = cdir; var _this = this, projectName = path.basename(cdir), configDir = this._getConfigDir(), projectConfig = this._projectConfig = new ProjectConfig(cdir); this._projectName = projectName; this._logger = new Logger(); this._buildState = {}; if (opts.graph || opts.profiler) { this._graph = new BuildGraph(projectName); } if (opts.profiler) { this._profiler = new BuildProfiler(); } try { if (config) { config(projectConfig); } else { var makefilePath = this._getMakeFile('make'), personalMakefilePath = this._getMakeFile('make.personal'); if (!makefilePath) { throw new Error('Cannot find make configuration file.'); } this._makefiles = [makefilePath, personalMakefilePath]; clearRequire(makefilePath); require(makefilePath)(projectConfig); if (personalMakefilePath) { clearRequire(personalMakefilePath); require(personalMakefilePath)(projectConfig); } } } catch (err) { return vow.reject(err); } this._makefiles = this._makefiles.concat(projectConfig.getIncludedConfigFilenames()); var modeConfig = projectConfig.getModeConfig(mode); if (modeConfig) { modeConfig.exec(null, projectConfig); } this._languages = projectConfig.getLanguages(); this._env = projectConfig.getEnvValues(); this._levelNamingSchemes = projectConfig.getLevelNamingSchemes(); projectConfig.task('clean', function (task) { return task.cleanTargets([].slice.call(arguments, 1)); }); var tmpDir = path.join(configDir, 'tmp'); return vowFs.makeDir(tmpDir).then(function () { _this._cacheStorage = new CacheStorage(path.join(tmpDir, 'cache.json')); _this._nodes = {}; }); }, /** * Возвращает профайлер для указанной ноды * @param {String} nodeName * @returns {{setStartTime: function, setEndTime: function}} */ getNodeProfiler: function (nodeName) { var profiler = this._profiler; return { setStartTime: function (target) { var filename = path.join(nodeName, target); return profiler.setStartTime(filename); }, setEndTime: function (target) { var filename = path.join(nodeName, target); return profiler.setEndTime(filename); } }; }, /** * @returns {SharedResources} */ getSharedResources: function () { return this._sharedResources; }, /** * Возвращает абсолютный путь к директории с проектом. * @returns {String} */ getDir: function () { return this._cdir; }, /** * Возвращает абсолютный путь к директории с конфигурационными файлами. * В качестве директории ожидается либо .enb/, либо .bem/. * @returns {string} * @private */ _getConfigDir: function () { var cdir = this.getDir(), possibleDirs = ['.enb', '.bem'], configDir, isConfigDirExists = possibleDirs.some(function (dir) { configDir = path.join(cdir, dir); return fs.existsSync(configDir); }); if (isConfigDirExists) { return configDir; } else { throw new Error('Cannot find enb config directory. Should be either .enb/ or .bem/.'); } }, /** * Возвращает путь к указанному конфигу сборки. * Если файлов make.js и make.personal.js не существует, то пробуем искать файлы с префиксом enb-. * @param {String} file Название конфига (основной или персональный). * @returns {String} * @private */ _getMakeFile: function (file) { var configDir = this._getConfigDir(), possiblePrefixes = ['enb-', ''], makeFile, isMakeFileExists = possiblePrefixes.some(function (prefix) { makeFile = path.join(configDir, prefix + file + '.js'); return fs.existsSync(makeFile); }); if (isMakeFileExists) { return makeFile; } }, /** * Возвращает построитель графа сборки. * @returns {BuildGraph} */ getBuildGraph: function () { return this._graph; }, /** * Возвращает Инстанс BuildProfiler. * @returns {BuildProfiler} */ getBuildProfiler: function () { return this._profiler; }, /** * Загружает кэш из временной папки. * В случае, если обновился пакет enb, либо изменился режим сборки, либо изменились make-файлы, сбрасывается кэш. */ loadCache: function () { this._cacheStorage.load(); var version = require('../package.json').version, mtimes = this._cacheStorage.get(':make', 'makefiles') || {}, dropCache = false; // Invalidate cache if mode was changed. if (this._cacheStorage.get(':make', 'mode') !== this._mode) { dropCache = true; } // Invalidate cache if ENB package was updated. if (this._cacheStorage.get(':make', 'version') !== version) { dropCache = true; } // Invalidate cache if any of makefiles were updated. var currentMTimes = this._getMakefileMTimes(); Object.keys(currentMTimes).forEach(function (makefilePath) { if (currentMTimes[makefilePath] !== mtimes[makefilePath]) { dropCache = true; } }); if (dropCache) { this._cacheStorage.drop(); } }, /** * Возвращает время изменения каждого загруженного make-файла в виде unix-time (.bem/enb-make.js). * @returns {Object} * @private */ _getMakefileMTimes: function () { var res = {}; this._makefiles.forEach(function (makefilePath) { if (fs.existsSync(makefilePath)) { res[makefilePath] = fs.statSync(makefilePath).mtime.getTime(); } }); return res; }, /** * Сохраняет кэш во временную папку. */ saveCache: function () { this._setCacheAttrs(); return this._cacheStorage.save(); }, /** * Сохраняет кэш во временную папку асинхронно. */ saveCacheAsync: function () { this._setCacheAttrs(); return this._cacheStorage.saveAsync(); }, _setCacheAttrs: function () { this._cacheStorage.set(':make', 'mode', this._mode); this._cacheStorage.set(':make', 'version', require('../package.json').version); this._cacheStorage.set(':make', 'makefiles', this._getMakefileMTimes()); }, /** * Возвращает переменные окружения. * @returns {Object} */ getEnv: function () { return this._env; }, /** * Устанавливает переменные окружения. * @param {Object} env */ setEnv: function (env) { this._env = env; }, /** * Возвращает хранилище кэша. * @returns {CacheStorage} */ getCacheStorage: function () { return this._cacheStorage; }, /** * Устанавливает хранилище кэша. * @param {CacheStorage} cacheStorage */ setCacheStorage: function (cacheStorage) { this._cacheStorage = cacheStorage; }, /** * Возвращает языки для проекта. * Вроде, уже больше не нужно. Надо избавиться в будущих версиях. * @returns {String[]} * @deprecated */ getLanguages: function () { return this._languages; }, /** * Устанавливает языки для проекта. * Вроде, уже больше не нужно. Надо избавиться в будущих версиях. * @param {String[]} languages * @deprecated */ setLanguages: function (languages) { this._languages = languages; }, /** * Возвращает логгер для сборки. * @returns {Logger} */ getLogger: function () { return this._logger; }, /** * Устанавливает логгер для сборки. * Позволяет перенаправить вывод процесса сборки. * * @param {Logger} logger */ setLogger: function (logger) { this._logger = logger; }, /** * Инициализирует ноду по нужному пути. * @param {String} nodePath * @returns {Promise} */ initNode: function (nodePath) { if (!this._nodeInitPromises[nodePath]) { var _this = this, cdir = this.getDir(), nodeConfig = this._projectConfig.getNodeConfig(nodePath), node = nodeFactory.mkNode(nodePath, this, this._cache, this._graph); node.setProfiler(this.getBuildProfiler(nodePath)); node.setLogger(this._logger.subLogger(nodePath)); this._nodes[nodePath] = node; this._nodeInitPromises[nodePath] = vowFs.makeDir(path.join(cdir, nodePath)) .then(function () { return vow.when(nodeConfig.exec()); }) .then(function () { return vow.all(_this._projectConfig.getNodeMaskConfigs(nodePath).map(function (nodeMaskConfig) { return nodeMaskConfig.exec([], nodeConfig); })); }) .then(function () { var mode = nodeConfig.getModeConfig(_this._mode); return mode && mode.exec(null, nodeConfig); }) .then(function () { node.setLanguages(nodeConfig.getLanguages() || _this._languages); node.setTargetsToBuild(nodeConfig.getTargets()); node.setTargetsToClean(nodeConfig.getCleanTargets()); node.setTechs(nodeConfig.getTechs()); node.setBuildState(_this._buildState); node.loadTechs(); }); } return this._nodeInitPromises[nodePath]; }, /** * Требует сборки таргетов для указанной ноды. * @param {String} nodePath Например, "pages/index". * @param {String[]} sources Таргеты, которые необходимо собрать. * @returns {Promise} */ requireNodeSources: function (nodePath, sources) { var _this = this; return this.initNode(nodePath).then(function () { return _this._nodes[nodePath].requireSources(sources); }); }, /** * Сбрасывает кэш. */ dropCache: function () { this._cacheStorage.drop(); }, /** * Возвращает массив строк путей к нодам, упорядоченные по убыванию длины. * Сортировка по убыванию нужна для случаев, когда на файловой системе одна нода находится * внутри другой (например, `bundles/page` и `bundles/page/bundles/header`). * * @returns {String[]} * @private */ _getNodePathsLenDesc: function () { return Object.keys(this._projectConfig.getNodeConfigs()).sort(function (a, b) { return b.length - a.length; }); }, /** * Вычисляет (на основе переданного пути к таргету и списка путей к нодам) * к какой ноде принадлежит переданный таргет. * @param {String} target * @param {String[]} nodePaths * @returns {{node: *, targets: String[]}} * @private */ _resolveTarget: function (target, nodePaths) { target = target.replace(/^(\.\/)+|\/$/g, ''); for (var i = 0, l = nodePaths.length; i < l; i++) { var nodePath = nodePaths[i]; if (target.indexOf(nodePath) === 0) { var npl = nodePath.length, charAtNpl = target.charAt(npl); if (target.length === npl) { return { node: nodePath, targets: ['*'] }; } else if (charAtNpl === '/' || charAtNpl === '\\') { return { node: nodePath, targets: [target.substr(npl + 1)] }; } } } throw TargetNotFoundError('Target not found: ' + target); }, /** * Вычисляет для списка таргетов, к каким нодам они принадлежат. * @param {String[]} targets * @returns {Object[]} * @private */ _resolveTargets: function (targets) { var _this = this, buildTargets = [], nodeConfigs = this._projectConfig.getNodeConfigs(), nodePathsDesc = this._getNodePathsLenDesc(); if (targets.length) { var targetIndex = {}; targets.forEach(function (targetName) { var target = _this._resolveTarget(targetName, nodePathsDesc); if (targetIndex[target.node]) { var currentTargetList = targetIndex[target.node].targets; target.targets.forEach(function (resTargetName) { if (currentTargetList.indexOf(resTargetName) === -1) { currentTargetList.push(resTargetName); } }); } else { targetIndex[target.node] = target; buildTargets.push(target); } }); } else { Object.keys(nodeConfigs).forEach(function (nodePath) { buildTargets.push({ node: nodePath, targets: ['*'] }); }); } return buildTargets; }, /** * Запускает сборку переданного списка таргетов. * @param {String[]} targets * @returns {Promise} */ buildTargets: function (targets) { var _this = this; this._cache = new Cache(this._cacheStorage, this._projectName); try { var targetList = this._resolveTargets(targets); return vow.all(targetList.map(function (target) { return _this.initNode(target.node); })).then(function () { return vow.all(targetList.map(function (target) { return _this._nodes[target.node].build(target.targets); })).then(function (builtInfoList) { var builtTargets = []; builtInfoList.forEach(function (builtInfo) { builtTargets = builtTargets.concat(builtInfo.builtTargets); }); return { builtTargets: builtTargets }; }); }); } catch (err) { return vow.reject(err); } }, /** * @returns {ProjectConfig} */ getProjectConfig: function () { return this._projectConfig; }, /** * Запускает удаление переданного списка таргетов. * @param {String[]} targets * @returns {Promise} */ cleanTargets: function (targets) { var _this = this; this._cache = new Cache(this._cacheStorage, this._projectName); try { var targetList = this._resolveTargets(targets); return vow.all(targetList.map(function (target) { return _this.initNode(target.node); })).then(function () { return vow.all(targetList.map(function (target) { return _this._nodes[target.node].clean(target.targets); })); }); } catch (err) { return vow.reject(err); } }, /** * Запускает выполнение таска. * @param {String} taskName * @param {String[]} args * @returns {Promise} */ buildTask: function (taskName, args) { var task = this._projectConfig.getTaskConfig(taskName); task.setMakePlatform(this); return vow.when(task.exec(args)); }, /** * Деструктор. */ destruct: function () { this._sharedResources.destruct(); this._buildState = null; delete this._projectConfig; var nodes = this._nodes; Object.keys(nodes).forEach(function (nodeName) { nodes[nodeName].destruct(); }); delete this._nodes; if (this._cacheStorage) { this._cacheStorage.drop(); delete this._cacheStorage; } if (this._cache) { this._cache.destruct(); delete this._cache; } delete this._levelNamingSchemes; }, /** * Заменяет слэши в путях к таргетам на обратные, если используется ОС Windows * @param {Array} targets * @returns {Array} */ _fixPath: function (targets) { return path.sep === '/' ? targets : targets.map(function (target) { return target.replace(/\//g, '\\'); }); }, /** * Запускает сборку. * Может запустить либо сборку таргетов, либо запуск тасков. * @param {String[]} targets * @returns {Promise} */ build: function (targets) { targets = this._fixPath(targets); var deferred = vow.defer(), _this = this, targetTask; try { if (targets.length && this._projectConfig.getTaskConfig(targets[0])) { targetTask = this.buildTask(targets[0], targets.slice(1)); } else { targetTask = this.buildTargets(targets); } targetTask.then(function () { Object.keys(_this._nodes).forEach(function (nodeName) { _this._nodes[nodeName].getLogger().setEnabled(false); }); return deferred.resolve(); }, function (err) { return deferred.reject(err); }); } catch (err) { deferred.reject(err); } return deferred.promise(); }, /** * Возвращает схему именования для уровня переопределения. * Схема именования содержит два метода: * ```javascript * // Выполняет построение структуры файлов уровня переопределения, используя методы инстанции класса LevelBuilder. * {Promise} buildLevel( {String} levelPath, {LevelBuilder} levelBuilder ) * // Возвращает путь к файлу на основе пути к уровню переопределения и BEM-описания. * {String} buildFilePath( * {String} levelPath, {String} blockName, {String} elemName, {String} modName, {String} modVal * ) * ``` * @returns {Object|undefined} */ getLevelNamingScheme: function (levelPath) { return this._levelNamingSchemes[levelPath]; } });
module.exports = inherit({ __constructor: function (fileSystemStructure, root) { this._structure = fileSystemStructure; this._root = root || process.cwd(); this._originalFsFunctions = null; this._originalAsyncFsFunctions = null; }, setup: function () { var mocks = createMocks(this._structure, this._root); this._originalAsyncFsFunctions = {}; for (var asyncFsMethodName in asyncFs) { this._originalAsyncFsFunctions[asyncFsMethodName] = asyncFs[asyncFsMethodName]; // Заменяем пустой функцией для безопасности. asyncFs[asyncFsMethodName] = createEmptyFunction(asyncFsMethodName); } for (var asyncFsMockName in mocks.vowFs) { asyncFs[asyncFsMockName] = mocks.vowFs[asyncFsMockName]; } this._originalFsFunctions = {}; for (var fsMethodName in fs) { this._originalFsFunctions[fsMethodName] = fs[fsMethodName]; // Заменяем пустой функцией для безопасности. fs[fsMethodName] = createEmptyFunction(fsMethodName); } for (var fsMockName in mocks.fs) { fs[fsMockName] = mocks.fs[fsMockName]; } }, teardown: function () { for (var asyncFsMethodName in this._originalAsyncFsFunctions) { asyncFs[asyncFsMethodName] = this._originalAsyncFsFunctions[asyncFsMethodName]; } for (var fsMethodName in this._originalFsFunctions) { fs[fsMethodName] = this._originalFsFunctions[fsMethodName]; } } });
var Page = inherit({ /** * Page * * @param {String} id Relative path to page (pages/index, for example) * @param {String} pageName Page name. * @param {Request} req Node request Object * @param {Response} res Node response Object */ __constructor: function (id, pageName, req, res) { this._id = id; this._pageName = pageName; this._req = req; this._res = res; this._lang = req.query.lang || 'ru'; this._query = req.query; }, /** * Build page * * @returns {Promise} promise */ handle: function () { return vow.all([ this._getPages(), this._getTemplate(), this._getI18n() ]) .spread(function (pages, bt, buildI18n) { var i18n = buildI18n(); pages.setI18n(i18n); bt.lib.i18n = i18n; return pages.exec(this._pageName, { query: this._query, options: this._getPageOptions(), lang: this._lang }).then(function (btJson) { return this._applyTemplate(btJson, bt); }.bind(this)); }.bind(this)); }, /** * Returns page handlers * * @returns {Promise} promise */ _getPages: function () { return assets.requirePageAsset(this._buildAssetPath('page')); }, /** * Returns bt templates * * @returns {Promise} promise */ _getTemplate: function () { return assets.requirePageAsset(this._buildAssetPath('bt')); }, /** * Returns i18n * * @returns {Promise} promise */ _getI18n: function () { return assets.requirePageAsset(this._buildAssetPath('lang.' + this._lang)); }, /** * Returns path to a technology * * @param {String} tech * @returns {String} path */ _buildAssetPath: function (tech) { return ['/', this._id, '/', path.basename(this._id), '.', tech, '.js'].join(''); }, /** * Returns path for css and js files * * @returns {Object} */ _getPageOptions: function () { return { assetsPath: [ 'host' in assets ? assets.host : '', '/', this._id, '/_', path.basename(this._id) ].join('') }; }, /** * Returns path to a technology * * @param {Object} btJson * @param {BT} template * @returns {Promise} promise */ _applyTemplate: function (btJson, template) { var startTime = Date.now(); logger.verbose('bt running'); var res = template.apply(btJson); logger.verbose('bt completed at %d ms', Date.now() - startTime); return res; } }, { /** * @param {String} pageName */ createHandler: function (pageName) { return function (req, res, next) { new Page('build/' + pageName, pageName + '-page', req, res) .handle() .then( function (html) { res.end(html); }, next ); }; } });
var Import = inherit({ __constructor : function (opts) { if (!opts) throw new Error('Input file required'); this._opts = opts || {}; }, _js : [], _css : [], _temp : [ INCLUDES_DEFAULT_NAME + 'css', INCLUDES_DEFAULT_NAME + 'js' ], _opts : null, push : function(tech) { this['_' + tech.getExtension()].push(tech.withInclude(this._opts)); if (tech.type !== 'file') this._temp.push(tech.getName()); else this._temp.push(tech.getName()); }, getContent : function(ext) { return this['_' + ext].join('\n'); }, getFileName : function(ext, isResolved) { if (isResolved) return FINAL_DEFAULT_NAME + ext; return INCLUDES_DEFAULT_NAME + ext; } });
module.exports = INHERIT(/** @lends APW.prototype */ { /** * Creates an instance of APW. * * @class APW * @constructs * @param {Arch} arch The Arch to work with. * @param {Number} [maxWorkers] Maximum number of workers to run simultaneously. * @param {Object} [ctx] The hash to mix with default context in node 'run' function. */ __constructor: function(arch, maxWorkers, ctx) { this.arch = arch; if (arguments.length == 2) { if (!isFinite(maxWorkers)) { maxWorkers = undefined; ctx = arguments[1]; } } this.workers = new this.__self.Workers(maxWorkers, ctx); }, /** * Run targets processing. * * @param {String[]|String} targets IDs (or ID) of jobs to process. * @returns {Promise * Undefined} Promise of this process to complete. */ process: function(targets) { return this.workers.start(this.arch.createPlan(targets)); } }, /** @lends APW */ { /** @type COA.api */ api: require('./coa').api, /** @type Arch */ Arch: require('./arch'), /** @type Plan */ Plan: require('./plan'), /** @type Workers */ Workers: require('./workers') });
module.exports = inherit(Block, { run : function(params, promise) { var parsedUrl = url.parse(params.url, true), hasBody = params.method === 'POST' || params.method === 'PUT', queryParams = querystring.stringify( hasBody? parsedUrl.query : utils.merge(parsedUrl.query, params.data)), body = hasBody? querystring.stringify(params.data) : ''; this._redirCounter = params.maxRedirects || 5; this._encoding = params.encoding || 'utf8'; this._dataType = params.dataType; this._doHttp( { method : params.method, headers : utils.merge( params.headers, hasBody? { 'Content-Type' : 'application/x-www-form-urlencoded', 'Content-length' : Buffer.byteLength(body) } : null), protocol : parsedUrl.protocol, hostname : parsedUrl.hostname, port : parsedUrl.port, path : parsedUrl.pathname + (queryParams? '?' + queryParams : ''), auth : params.auth }, promise, params.dataType, body); }, abort : function() { this._curReq && this._curReq.abort(); }, _doHttp : function(params, promise, dataType, body) { var _this = this; _this._curReq = (params.protocol === 'https:'? https : http).request( params, function(res) { if(res.statusCode === 301 || res.statusCode === 302) { return --_this._redirCounter? _this._doHttp(url.parse(res.headers['location'], true), promise, dataType) : promise.reject(Error('too many redirects')); } else if(res.statusCode >= 400) { return promise.reject(new HttpError(res.statusCode)); } var resp = ''; res.setEncoding(_this._encoding); res .on('data', function(chunk) { resp += chunk; }) .once('end', function() { try { promise.fulfill(processResponse( resp, _this._dataType || extractDataTypeFromHeaders(res.headers))); } catch(e) { promise.reject(e); } }) .once('close', function() { promise.reject(Error('connection closed')); }); }); body && _this._curReq.write(body); _this._curReq .once('error', function(e) { promise.reject(e); }) .end(); } });
if (!name) name = 'popup-' + (Math.random() * 0x10000000 | 0).toString(36); Emitter.call(this); this.name = name; this.opts = opts; this.optsStr = optsStr; // finally, open and return the popup window this.window = window.open(src, name, optsStr); this.focus(); this.interval = setInterval(checkClose(this), interval); } // inherit from Emitter inherit(Popup, Emitter); /** * Closes the popup window. */ Popup.prototype.close = function () { this.window.close(); } /** * Focuses the popup window (brings to front). */ Popup.prototype.focus = function () { this.window.focus();
module.exports = inherit({ /** * */ __constructor: function(stats, customOpts) { customOpts = customOpts || {}; this._options = _.merge({ legend: {position: 'bottom'}, title: {display: true, text: stats.login} }, this._options, customOpts.options); this._datasetsOptions = _.merge({}, this._datasetsOptions, customOpts.datasetsOptions); this._config = this._configure(stats); }, /** * */ _configure: function(stats) { return { type: this._type, data: this._formatData(stats), options: this._options }; }, /** * */ _formatData: function(stats) { return { labels: stats.labels, datasets: _.map(stats.data, function(stat, index) { return _.extend(_.clone(this._datasetsOptions), { label: this._datasetsOptions.labels && this._datasetsOptions.labels[index] || formatLabel_(index), data: stat }); }.bind(this)) }; /// function formatLabel_(index) { return (index === 0 && '1st' || index === 1 && '2nd' || index === 2 && '3rd' || format('%sth', index + 1)) + ' learning'; } }, /** * */ getConfig: function() { return this._config; } });
var Process = inherit({ __constructor: function(attrs, model3d) { /* * Process a pour vocation d'être étendue, c'est pourquoi tous ses champs sont précédés d'un underscore : on évite ainsi que des champs se fassent écrasés, ce qui pourrait produire des bugs bizarres */ this._attrs = attrs; this._model3d = model3d; this._running = false; /* * Quand ce champ passe à vrai, on ignore toutes les erreurs qui peuvent arriver. * Utilité : on vient d'avoir une erreur (par ex. : impossible de trouver le fichier x) : la première est souvent l'erreur source, et elle est suivie par d'autres (puisqu'on fait ensuite un kill, on pourrait avoir des erreurs dues à des fermetures prématurées de processus). * Ces dernières sont moins intéressantes et écraseraient l'erreur d'origine. C'est pourquoi on met une sécurité. */ this._ignoreError = false; this._stepCurrent = null; }, step: function(options, cb) { if(_.isFunction(options)) { cb = options; options = {}; } _.extend(options, {process_id: this._attrs.id}); Step.get(options, this, cb); }, update: function(fields, cb) { var self = this; // on met à jour les attributs de l'objet _.extend(self._attrs, fields); sqlCon.query('UPDATE process SET ? WHERE id=?', [fields, self._attrs.id], function(err) { if(err) { var message = '[Process] Erreur lors de la mise à jour de l\'enregistrement ' + self._attrs.id + ' en BDD : ' + err + '.'; console.log(message); cb(new Error(message), null); return; } cb(null); }); }, /* * Démarre la chaine de Step associée au Process courant * * @param {Function} cb appelé quand le démarrage de la chaine de Step est effectif */ start: function(cb) { var self = this; if(!self._running) { self._running = true; self._ignoreError = false; console.info('[Process] Processus "' + self._attrs.name + '" (ID = ' + self._attrs.id + ') lancé'); self.update({ state: Constants.STATE_RUNNING }, function(err) { if(!err) self.startNextStep(cb); }); } }, /** * Trouve la prochaine Step à démarrer et la démarre * * @param {Function} cb appelé quand le démarrage de la Step est effectif */ startNextStep: function(cb) { var self = this; if(self._attrs.state !== Constants.STATE_RUNNING) { return; } self.step(function(err, steps) { if(err) { cb(err); return; } steps.sort(function(a, b) { return a._attrs.ordering - b._attrs.ordering; }); self._stepCurrent = undefined; for(var i = 0; i < steps.length; i++) { if(steps[i]._attrs.state == Constants.STATE_STOPPED) continue; self._stepCurrent = steps[i]; self._stepCurrent.start(cb); break; } if(!self._stepCurrent) { self.done(function(err) { if(err) console.log("[Process] N'a pas pu mettre fin au Process : " + err); cb(err); }); } }); }, /** * Met en pause le Process courant * * @param {boolean} hurry si le traitement actuel doit être interrompu dès que possible au risque de devoir par la suite recommencer la Step interrompue * @param {Function} cb appelé quand la mise en pause est effective, c'est-à-dire quand plus aucune Step lié à ce Model3d n'est en cours d'exécution */ pause: function(hurry, cb) { var self = this; console.info('[Process] Processus "' + self._attrs.name + '" (ID = ' + self._attrs.id + ') mis en pause'); if(hurry) self._ignoreError = true; if(self._stepCurrent) { self._stepCurrent.pause(hurry, function() { self._running = false; self._stepCurrent = null; self.update({ state: Constants.STATE_PAUSED }, cb); }); return; } else { if(self._running) self._running = false; self.update({ state: Constants.STATE_PAUSED }, cb); } }, /* * * * @param {Function} cb appelé quand la chaine de traitement est vraiment terminée */ stop: function(cb) { var self = this; console.info('[Process] Processus "' + self._attrs.name + '" (ID = ' + self._attrs.id + ') arrêté'); self._ignoreError = true; if(self._stepCurrent) { self._stepCurrent.stop(function() { self._running = false; self._stepCurrent = null; self.update({ state: Constants.STATE_STOPPED }, cb); }); return; } else { if(self._running) self._running = false; self.update({ state: Constants.STATE_STOPPED }, cb); } self.removeCache(); }, error: function(err) { if(this._ignoreError) return; if(err.fatal) this._ignoreError = true; this._model3d.error(err); }, done: function(cb) { var self = this; self._ignoreError = true; console.info('[Process] Processus "' + self._attrs.name + '" (ID = ' + self._attrs.id + ') terminé'); self.update({ state: Constants.STATE_STOPPED }, function(err) { if(err) { cb(err); return; } if(self._running) self._running = false; self._stepCurrent = null; self._model3d.startNextProcess(cb); }); }, sendNotification: function(message) { message.process_id = this._attrs.id; this._model3d.sendNotification(message); }, removeCache: function() { Step.removeCache(this); } }, { tabCachedModels: {}, get: function(cond, model3d, cb) { var self = this; var queryArgs = Utils.getQueryArgs(cond); sqlCon.query('SELECT p.*, sp.name, sp.library_directory, sp.library_name, sp.ordering FROM process p INNER JOIN spec_process sp ON p.spec_process_id=sp.id WHERE ' + queryArgs.where, queryArgs.args, function(err, rows) { if(err) { var message = '[Process] Erreur lors de la récupération des enregistrements en BDD : ' + err + '.'; console.error(message); cb(new Error(message), null); } else { var tabModels = _.map(rows, function(row) { if(self.tabCachedModels.hasOwnProperty(row.id)) _.extend(self.tabCachedModels[row.id]._attrs, row); else { // require met en cache les fichiers déjà chargés var ProcessObject = require('./process/' + row.library_directory + '/' + row.library_name + '.process'); self.tabCachedModels[row.id] = new ProcessObject(row, model3d); } return self.tabCachedModels[row.id]; }); cb(null, tabModels); } }); }, /** * Pour pouvoir ré-utiliser les mêmes objects entre chaque pause, ils sont mis en cache dans des tableaux associatifs (en JavaScript, ce sont tout simplement des objets) : tabCachedModels * On peut rencontrer les mêmes problèmes qu'en Java : tant qu'on garde une référence vers un objet, il ne sera pas nettoyé par le Garbage Collector : c'est donc le problème que résout cette fonction */ removeCache: function(model3d) { _.forEach(this.tabCachedModels, function(cachedModel, index) { if(cachedModel._model3d == model3d) delete this.tabCachedModels[index]; }, this); } });
*/ module.exports = inherit({ /** * Осуществляет обработку файла Борщиком. * @param {String} sourceFilename Исходный файл. * @param {String} destFilename Результирующий файл. * @param {Boolean} freeze Осуществлять ли фризинг. * @param {Boolean} minimize Осуществлять ли минимизацию. * @param {Tech} [tech] * @param {Object} [techOptions] Параметры для технологии. * @param {Boolean} comments Добавлять ли комментарии. * @returns {*} */ preprocessFile: function (sourceFilename, destFilename, freeze, minimize, tech, techOptions, comments) { var opts = { input: sourceFilename, output: destFilename, freeze: freeze, minimize: minimize, comments: comments }; if (tech) { opts.tech = tech; if (techOptions) { opts.techOptions = techOptions; } } return vow.when(borschik.api(opts)); } });
var Config = module.exports = inherit({ __constructor: function(configPath, configText, overrides) { overrides = overrides || {}; var config; try { config = yaml.load(configText); } catch (e) { throw new GeminiError('Error while parsing a config file: ' + configPath + '\n' + e.reason + ' ' + e.mark, 'Gemini config should be valid YAML file.' ); } this.root = path.dirname(configPath); this.rootUrl = overrides.rootUrl || config.rootUrl; if (!this.rootUrl) { throw new GeminiError( 'Required field "rootUrl" is not specified in config file: ' + configPath, '"rootUrl" should point to the root of website under test.\nSuite URLs are resolved relative to it.' ); } this.gridUrl = overrides.gridUrl || config.gridUrl; this.browsers = this._parseBrowsers(config.browsers || {phantomjs: 'phantomjs'}); this.capabilities = config.capabilities || {}; this._validateCapabilities(); this.http = this._parseHttp(config.http || {}); if ('debug' in config && typeof config.debug !== 'boolean') { throw new GeminiError('Field "debug" must contain a boolean value'); } this.debug = !!config.debug; if ('parallelLimit' in config) { if (typeof config.parallelLimit !== 'number') { throw new GeminiError('Field "parallelLimit" must be an integer number'); } if (config.parallelLimit < 0) { throw new GeminiError('Field "parallelLimit" must be non-negative'); } if (Math.floor(config.parallelLimit) !== config.parallelLimit) { throw new GeminiError('Field "parallelLimit" must be an integer number'); } this.parallelLimit = config.parallelLimit; } if (this._requiresGrid() && !this.gridUrl) { throw new GeminiError( 'Field "gridUrl" is required for using non-phantomjs browsers', [ 'Specify selenium grid URL in your config file or use only', 'phantomjs browser.', 'Selenium server installation instructions:', '', 'https://code.google.com/p/selenium/wiki/Grid2' ].join('\n') ); } this.screenshotsDir = path.resolve(this.root, config.screenshotsDir || DEFAULT_SCREENSHOTS_DIR); }, _requiresGrid: function() { return Object.keys(this.browsers).some(function(name) { return this.browsers[name].browserName !== 'phantomjs'; }, this); }, _validateCapabilities: function() { if ('takesScreenshot' in this.capabilities) { throw new GeminiError( 'Setting `takesScreenshot` capability for all browsers is not allowed.\n' + 'It is required and will be set automatically.' ); } }, _parseBrowsers: function(browsers) { var parsedBrowsers = {}; // legacy browsers config if (Array.isArray(browsers)) { // Convert legacy browsers config value to the current one parsedBrowsers = this._parseLegacyBrowsers(browsers); // legacy browsers config option warning console.error([ chalk.yellow('Warning! You are using deprecated format of "browsers" option'), chalk.yellow('in gemini config. Replace it\'s value with the following code:'), '', yaml.safeDump({browsers: parsedBrowsers}), chalk.yellow('For more info on project configuration see the instructions:'), chalk.yellow('https://github.com/bem/gemini#configuration') ].join('\n')); return parsedBrowsers; } else if (browsers !== null && typeof browsers === 'object') { // Convert `browser-id: browser-name` to `browser-id: {browserName: browser-name}` Object.keys(browsers).forEach(function(id) { if (typeof browsers[id] === 'string') { parsedBrowsers[id] = {browserName: browsers[id]}; } else { parsedBrowsers[id] = browsers[id]; } }, this); return parsedBrowsers; } throw new GeminiError('Field "browsers" must be an object or an array (deprecated).', 'Configure your project by following the instructions:\n\n', 'https://github.com/bem/gemini#configuration'); }, _parseLegacyBrowsers: function(browsers) { var parsedBrowsers = {}; browsers .map(function(browser) { if (typeof browser === 'string') { return {browserName: browser}; } if ('name' in browser) { browser = extend(true, {}, browser); browser.browserName = browser.name; delete browser.name; } return browser; }) .forEach(function(browser) { var id = browser.browserName; if (browser.version) { id += '-v' + browser.version; } parsedBrowsers[id] = browser; }); return parsedBrowsers; }, _parseHttp: function(http) { var conf = {}; ['timeout', 'retries', 'retryDelay'] .forEach(function(option) { if (option in http) { if (typeof http[option] !== 'number') { throw new GeminiError('Field "http.' + option + '" must be a number'); } conf[option] = http[option]; } }); return conf; }, getAbsoluteUrl: function getAbsoluteUrl(relUrl) { return url.resolve(this.rootUrl, relUrl); }, getScreenshotsDir: function(suite, state) { return path.resolve(this.screenshotsDir, this._getPathForSuite(suite), state); }, getScreenshotPath: function getScrenshotPath(suite, state, browser) { return path.join(this.getScreenshotsDir(suite, state), browser + '.png'); }, _getPathForSuite: function(suite) { var result = ''; while (suite) { result = path.join(suite.name, result); suite = suite.parent; } return result; } }, { read: function read(configPath, overrides) { return fs.read(configPath) .then(function(content) { return new Config(configPath, content, overrides); }) .fail(function(e) { if (e.code === 'ENOENT') { throw new GeminiError( 'Config file does not exists: ' + configPath, 'Specify config file or configure your project by following\nthe instructions:\n\n' + 'https://github.com/bem/gemini#configuration' ); } throw e; }); } });
module.exports = inherit(require('../lib/tech/base-tech'), { getName: function() { return 'html-from-bemjson-i18n'; }, configure: function() { this._bemhtmlSource = this.node.unmaskTargetName( this.getOption('bemhtmlTarget', this.node.getTargetName('bemhtml.js')) ); this._bemjsonSource = this.node.unmaskTargetName( this.getOption('bemjsonTarget', this.node.getTargetName('bemjson.js')) ); this._allLangSource = this.node.unmaskTargetName( this.getOption('langAllTarget', this.node.getTargetName('lang.all.js')) ); this._langSource = this.node.unmaskTargetName( this.getOption( 'langTarget', this.node.getTargetName('lang.' + this.getOption('lang', this.node.getLanguages()[0]) + '.js') ) ); this._target = this.node.unmaskTargetName( this.getOption( 'destTarget', this.node.getTargetName(this.getOption('lang', this.node.getLanguages()[0]) + '.html') ) ); }, getTargets: function() { return [this.node.unmaskTargetName(this._target)]; }, getBuildResult: function(target, bemhtmlFile, bemjson, allLangFile, langFile) { var _this = this; delete require.cache[bemhtmlFile]; delete require.cache[allLangFile]; return Vow.all([ asyncRequire(bemhtmlFile), asyncRequire(allLangFile) ]).spread(function(bemhtml, i18n) { delete require.cache[langFile]; return asyncRequire(langFile).then(function(keysets) { if ((typeof i18n === 'function' || typeof keysets === 'function') && bemhtml.lib) { if (typeof keysets === 'function') { i18n = keysets(i18n); } bemhtml.lib.i18n = i18n; } var global = bemhtml.lib && bemhtml.lib.global; if (global) { global.lang = _this.getOption('lang'); global.setTld(_this.getOption('lang')); } if (!bemhtml.BEMHTML && bemhtml.lib) { return bemhtml.apply(bemjson); } else { return bemhtml.BEMHTML.apply(bemjson); } }); }); }, isRebuildRequired: function(target) { var cache = this.node.getNodeCache(target); return cache.needRebuildFile('bemhtml-file', this.node.resolvePath(this._bemhtmlSource)) || cache.needRebuildFile('bemjson-file', this.node.resolvePath(this._bemjsonSource)) || cache.needRebuildFile('allLang-file', this.node.resolvePath(this._allLangSource)) || cache.needRebuildFile('lang-file', this.node.resolvePath(this._langSource)) || cache.needRebuildFile('html-file', this.node.resolvePath(target)); }, storeCache: function(target) { var cache = this.node.getNodeCache(target); cache.cacheFileInfo('bemhtml-file', this.node.resolvePath(this._bemhtmlSource)); cache.cacheFileInfo('bemjson-file', this.node.resolvePath(this._bemjsonSource)); cache.cacheFileInfo('allLang-file', this.node.resolvePath(this._allLangSource)); cache.cacheFileInfo('lang-file', this.node.resolvePath(this._langSource)); cache.cacheFileInfo('html-file', this.node.resolvePath(target)); }, build: function() { var _this = this; return this.node.requireSources( [this._bemhtmlSource, this._bemjsonSource, this._allLangSource, this._langSource] ).then(function() { return Vow.when(_this.getTargets()).then(function(targets) { var targetsToBuild = []; return Vow.when(targets.map(function(target) { return Vow.when(_this.isRebuildRequired(target)).then(function(rebuildRequired) { if (!rebuildRequired) { _this.node.isValidTarget(target); _this.node.resolveTarget(target); } else { targetsToBuild.push(target); } }); })).then(function() { if (targetsToBuild.length) { return requireOrEval(_this.node.resolvePath(_this._bemjsonSource)).then(function (bemjson) { return Vow.all(targetsToBuild.map(function(target) { return Vow.when(_this.getBuildResult( target, _this.node.resolvePath(_this._bemhtmlSource), bemjson, _this.node.resolvePath(_this._allLangSource), _this.node.resolvePath(_this._langSource) )).then(function(res) { return vowFs.write(_this.node.resolvePath(target), res, 'utf8'); }).then(function() { _this.node.resolveTarget(target); _this.storeCache(target); }); })); }); } return null; }); }); }); } });
module.exports = inherit(require('enb/lib/tech/base-tech'), { getName: function () { return 'deps'; }, configure: function () { var logger = this.node.getLogger(); this._target = this.getOption('depsTarget'); if (this._target) { logger.logOptionIsDeprecated(this.node.unmaskTargetName(this._target), 'enb-bem-techs', this.getName(), 'depsTarget', 'target'); } else { this._target = this.getOption('target', this.node.getTargetName('deps.js')); } this._target = this.node.unmaskTargetName(this._target); this._declFile = this.getOption('bemdeclTarget'); if (this._declFile) { logger.logOptionIsDeprecated(this._target, 'enb-bem-techs', this.getName(), 'bemdeclTarget', 'bemdeclFile'); } else { this._declFile = this.getOption('bemdeclFile', this.node.getTargetName('bemdecl.js')); } this._declFile = this.node.unmaskTargetName(this._declFile); this._levelsTarget = this.node.unmaskTargetName( this.getOption('levelsTarget', this.node.getTargetName('levels'))); }, getTargets: function () { return [this._target]; }, build: function () { var node = this.node, target = this._target, targetFilename = node.resolvePath(target), cache = node.getNodeCache(target), declFilename = this.node.resolvePath(this._declFile); return this.node.requireSources([this._levelsTarget, this._declFile]) .spread(function (levels, sourceDeps) { var depFiles = levels.getFilesBySuffix('deps.js').concat(levels.getFilesBySuffix('deps.yaml')); if (cache.needRebuildFile('deps-file', targetFilename) || cache.needRebuildFile('source-deps-file', declFilename) || cache.needRebuildFileList('deps-file-list', depFiles) ) { return requireSourceDeps(sourceDeps, declFilename) .then(function (sourceDeps) { var resolver = new DepsResolver(levels), decls = resolver.normalizeDeps(sourceDeps); return resolver.addDecls(decls) .then(function () { var resolvedDeps = resolver.resolve(), str = 'exports.deps = ' + JSON.stringify(resolvedDeps, null, 4) + ';\n'; return vfs.write(targetFilename, str, 'utf8') .then(function () { cache.cacheFileInfo('deps-file', targetFilename); cache.cacheFileInfo('source-deps-file', declFilename); cache.cacheFileList('deps-file-list', depFiles); node.resolveTarget(target, { deps: resolvedDeps }); }); }); }); } else { node.isValidTarget(target); dropRequireCache(require, targetFilename); return asyncRequire(targetFilename) .then(function (result) { node.resolveTarget(target, result); return null; }); } }); } });
var StaticPage = inherit(Page, { /** * Build static page * * @returns {Promise} promise */ handle: function () { return vow.all([ this._getPages(), this._getTemplate(), this._getI18n() ]) .spread(function (pages, bt, buildI18n) { var i18n = buildI18n(); pages.setI18n(i18n); bt.lib.i18n = i18n; return pages .exec(this._pageName, { query: this._query, options: this._getPageOptions(), lang: this._lang }) .then(function (btJson) { var html = this._applyTemplate(btJson, bt); return this._makeStaticPage(this._id, html); }.bind(this)); }.bind(this)); }, /** * Returns path for css and js files * * @returns {Object} */ _getPageOptions: function () { return { assetsPath: '_' + path.basename(this._id) }; }, /** * Создаёт статическую страницу по переданному пути. * * @param {String} pageName * @param {String} html * @returns {Promise} */ _makeStaticPage: function(pageName, html) { var fileName = path.basename(pageName); var filePath = pageName + '/' + fileName + '.html'; return vowFs .write(filePath, html) .then(function () { return filePath; }); } }, { /** * @param {String} pageName * @param {String} lang */ make: function (pageName, lang) { var req = { query: { lang: lang } }; return new StaticPage('build/' + pageName, pageName + '-page', req, {}) .handle() .then(function (filePath) { console.log('Vow! Generated ' + filePath); }); } });
'use strict'; var INHERIT = require('inherit'), Tech = require('../tech').Tech; exports.Tech = INHERIT(Tech, { getBuildResultChunk: function(relPath, path, suffix) { return '/*borschik:include:' + relPath + '*/;\n'; }, getSuffixes: function() { return ['js']; } });
module.exports = inherit(EventEmitter, { __constructor: function(config, browserLauncher) { this.config = config; this._cancelled = false; this.browserLauncher = browserLauncher || new BrowserLauncher(config); this.coverage = new Coverage(config); }, setGrepPattern: function(pattern) { this._grepPattern = pattern; }, run: function(rootSuite) { var _this = this; this._stats = new Stats(); return q.fcall(function() { _this.emit('begin', { config: _this.config, totalStates: rootSuite.deepStatesCount, browserIds: Object.keys(_this.config.browsers) }); }).then(function() { return _this._prepare(); }) .then(function() { return _this._runBrowsers(rootSuite.children); }) .then(function() { if (_this.config.coverage) { return _this.coverage.processStats(); } }) .then(function() { _this.emit('end'); return _this._stats.data; }); }, _prepare: function() { }, _runBrowsers: function(suites) { var _this = this; return q.all(Object.keys(this.config.browsers).map(function(browserId) { return _this.browserLauncher.launch(browserId) .then(function(browser) { _this.emit('startBrowser', {browserId: browser.id}); return _this._runSuitesInBrowser(suites, browser) .fin(function() { _this.emit('stopBrowser', {browserId: browser.id}); return _this.browserLauncher.stop(browser); }); }) .fail(function(e) { _this._cancelled = true; return q.reject(e); }); })); }, _runSuitesInBrowser: function(suites, browser) { var _this = this; return promiseUtils.seqMap(suites, function(suite) { return _this._runSuiteInBrowser(suite, browser); }); }, _runSuiteInBrowser: function(suite, browser) { if (this._cancelled) { return q.resolve(); } var _this = this, eventData = { browserId: browser.id, suiteName: suite.name, suiteId: suite.id }; this.emit('beginSuite', eventData); return this._runSuiteStateIfMatches(suite, browser) .then(function() { return _this._runSuitesInBrowser(suite.children, browser); }) .then(function() { _this.emit('endSuite', eventData); }); }, _runSuiteStateIfMatches: function(suite, browser) { if (this._grepPattern && !this._grepPattern.test(suite.fullName)) { return q.resolve(); } return this._runSuiteStates(suite, browser); }, _runSuiteStates: function(suite, browser) { if (!suite.hasStates) { return q.resolve(); } var _this = this, session = new CaptureSession(browser); return browser.open(this.config.getAbsoluteUrl(suite.url)) .then(function() { return session.runHook(suite.beforeHook); }) .then(function() { return promiseUtils.seqMap(suite.states, function(state) { return _this._runStateInSession(state, session); }); }) .then(function() { return session.runHook(suite.afterHook); }); }, _runStateInSession: function(state, session) { if (this._cancelled) { return q.resolve(); } var _this = this, suite = state.suite, eventData = { browserId: session.browser.id, suiteName: state.suite.name, suiteId: state.suite.id, stateName: state.name }; _this._stats.add('total'); if (state.shouldSkip(session.browser)) { _this.emit('skipState', eventData); this._stats.add('skipped'); return q(); } _this.emit('beginState', eventData); return session.capture(state, {coverage: this.config.coverage}) .then(function(data) { if (_this.config.coverage) { _this.coverage.addStats(data.coverage); } return q(_this._processCapture({ suite: suite, state: state, browser: session.browser, image: data.image, canHaveCaret: data.canHaveCaret })); }) .fail(function(e) { if (e instanceof StateError) { e.suiteId = state.suite.id; e.suiteName = state.suite.name; e.stateName = state.name; e.browserId = session.browserId; _this._stats.add('errored'); _this.emit('error', e); } else { return q.reject(e); } }) .fin(function() { _this.emit('endState', eventData); }); }, _processCapture: function() { } });
* * @api private */ Scrollbar.Horizontal = function (pane) { pane.el.insertAdjacentHTML('beforeend', '<div class="antiscroll-scrollbar antiscroll-scrollbar-horizontal"/>'); this.el = q('.antiscroll-scrollbar-horizontal', pane.el); Scrollbar.call(this, pane); }; /** * Inherits from Scrollbar. */ inherit(Scrollbar.Horizontal, Scrollbar); /** * Updates size/position of scrollbar. * * @api private */ Scrollbar.Horizontal.prototype.update = function () { var paneWidth = this.pane.el.offsetWidth, trackWidth = paneWidth - this.pane.padding * 2, scrollWidth = this.pane.inner.scrollWidth; css(this.el, { width: Math.floor(trackWidth * paneWidth / scrollWidth), transform: 'translateX(' + Math.floor(trackWidth * this.pane.inner.scrollLeft / scrollWidth) + 'px)'
module.exports = inherit( /** @lends Node.prototype */ { /** * Конструктор. * @param {String} nodePath * @param {MakePlatform} makePlatform * @param {Cache} cache */ __constructor: function(nodePath, makePlatform, cache) { var root = makePlatform.getDir(); /** * Ссылка на платформу. * @type {MakePlatform} * @name Node.prototype._makePlatform * @private */ this._makePlatform = makePlatform; /** * Путь к директории с нодой относительно корня проекта. * @type {String} * @name Node.prototype._path * @private */ this._path = nodePath; /** * Абсолютный путь к корню проекта. * @type {String} * @name Node.prototype._root * @private */ this._root = root; /** * Абсолютный путь к директории с нодой. * @type {String} * @name Node.prototype._dirname * @private */ this._dirname = root + '/' + nodePath; /** * Имя директории с нодой. Например, "index" для ноды "pages/index". * @type {String} * @name Node.prototype._targetName * @private */ this._targetName = path.basename(nodePath); /** * Зарегистрированные технологии. * @type {Tech[]} * @name Node.prototype._techs * @private */ this._techs = []; /** * Ссылка на кэш платформы. * @type {Cache} * @name Node.prototype._cache * @private */ this._cache = cache; /** * Кэш для ноды. * @type {Cache} * @name Node.prototype._nodeCache * @private */ this._nodeCache = cache.subCache(nodePath); /** * Логгер для ноды. * @type {Logger} * @name Node.prototype._logger * @private */ this._logger = null; /** * Зарегистрированные таргеты со ссылками на технологии и с промисами на выполнение таргетов. * Формат: * { 'index.js': { tech: <ссылка на технологию>, started: true|false, promise: <промис на выполнение> } } * @type {Object} * @name Node.prototype._targetNames * @private */ this._targetNames = {}; /** * Список таргетов на сборку. * @type {String[]} * @name Node.prototype._targetNamesToBuild * @private */ this._targetNamesToBuild = []; /** * Список таргетов на удаление (для команды enb make clean). * @type {String[]} * @name Node.prototype._targetNamesToClean * @private */ this._targetNamesToClean = []; // TODO: Удалить this._languages. /** * Список языков для ноды. Уже почти не используется в связи с переходом на новый формат настроек. * Будет удалено в будущих версиях. * @type {String[]} * @name Node.prototype._languages * @deprecated * @private */ this._languages = null; /** * Промис на регистрацию всех таргетов для добавленных технологий. * @type {Promise} * @name Node.prototype._registerTargetsPromise * @private */ this._registerTargetsPromise = null; /** * Построитель графа сборки. * @type {BuildGraph} * @name Node.prototype._graph * @private */ this._graph = null; }, /** * Внутреннее состояние текущей сборки. Используется для обмена данными между нодами. * @param {Object} buildState */ setBuildState: function(buildState) { this.buildState = buildState; }, /** * Устанавливает логгер для ноды (для того, чтобы логгировать ход сборки в консоль). * @param {Logger} logger * @returns {Node} */ setLogger: function(logger) { this._logger = logger; return this; }, /** * Возвращает логгер для ноды. Технологии могут пользоваться этим методов для дополнительного логгирования. * @returns {Logger} */ getLogger: function() { return this._logger; }, /** * Устанавливает языки для ноды. * @param {String[]} languages * @returns {Node} */ setLanguages: function(languages) { this._languages = languages; return this; }, /** * Возвращает языки для текущей ноды. * @returns {String[]} */ getLanguages: function() { return this._languages; }, /** * Возвращает абсолютный путь к директории с нодой. * @returns {String} */ getDir: function() { return this._dirname; }, /** * Возвращает абсолютный путь к директории с проектом. * @returns {String} */ getRootDir: function() { return this._root; }, /** * Возвращает относительный путь к директории с нодой (от корня проекта). * @returns {*} */ getPath: function() { return this._path; }, /** * Возвращает технологии, зарегистрированные для данной ноды. * @returns {Tech[]} */ getTechs: function() { return this._techs; }, /** * Устанавливает технологии для ноды. * @param {Tech[]} techs */ setTechs: function(techs) { this._techs = techs; }, /** * Устанавливает таргеты для сборки. * @param {String[]} targetsToBuild */ setTargetsToBuild: function(targetsToBuild) { this._targetNamesToBuild = targetsToBuild; }, /** * Устанавливает таргеты для удаления. * @param {String[]} targetsToClean */ setTargetsToClean: function(targetsToClean) { this._targetNamesToClean = targetsToClean; }, /** * Устанавливает построитель графа сборки. * @param {BuildGraph} graph * @returns {Node} */ setBuildGraph: function(graph) { this._graph = graph; return this; }, /** * Возвращает абсолютный путь к файлу, лежащему в директории с нодой. * @param {String} filename * @returns {String} */ resolvePath: function(filename) { return this._dirname + '/' + filename; }, /** * Возвращает абсолютный путь к файлу, лежащему в директории с указанной нодой. * @param {String} nodePath Имя ноды (например, "pages/index"). * @param {String} filename * @returns {String} */ resolveNodePath: function(nodePath, filename) { return this._root + '/' + nodePath + '/' + filename; }, /** * Демаскирует имя таргета для указанной ноды. Например, для ноды "pages/index" заменяет "?.js" на "index.js". * @param {String} nodePath Например, "pages/login". * @param {String} targetName * @returns {String} */ unmaskNodeTargetName: function(nodePath, targetName) { return targetName.replace(/\?/g, path.basename(nodePath)); }, /** * Возвращает относительный ноды путь к файлу (заданному абсолютным путем). * @param {String} filename * @returns {String} */ relativePath: function(filename) { var res = path.relative(this._path, filename); if (res.charAt(0) !== '.') { res = './' + res; } return res; }, /** * Возвращает www-путь к файлу (заданному абсолютным путем). * @param {String} filename * @param {String} wwwRoot Адрес соответствующий корню проекта. * @returns {String} */ wwwRootPath: function(filename, wwwRoot) { wwwRoot = wwwRoot || '/'; return wwwRoot + path.relative(this._root, filename); }, /** * Удаляет файл, лежащий в директории ноды. Вспомогательный метод для технологий. * @param {String} target */ cleanTargetFile: function(target) { var targetPath = this.resolvePath(target); if (fs.existsSync(targetPath)) { fs.unlinkSync(targetPath); this.getLogger().logClean(target); } }, /** * Создает временный файл для указанного таргета. * @param {String} targetName * @returns {String} */ createTmpFileForTarget: function(targetName) { var dir = this._dirname; function createTmpFilename() { var prefix = '_tmp_' + (+new Date()) + (Math.random() * 0x1000000000).toString(36) + '_', filename = dir + '/' + prefix + targetName; return vowFs.exists(filename).then(function(exists) { if (exists) { return createTmpFilename(); } else { return vowFs.write(filename, '').then(function() { return filename; }); } }); } return createTmpFilename(); }, /** * Инициализирует технологии, зарегистрированные в рамках ноды. * @returns {Promise} */ loadTechs: function() { var _this = this; return Vow.all(this._techs.map(function(t) { var nodeMirrorClass = function() {}; nodeMirrorClass.prototype = _this; var mirror = new nodeMirrorClass(); return Vow.when(t.init(mirror)).then(function() { return Vow.when(t.getTargets()).then(function(targets) { if (_this._graph) { targets.forEach(function(target) { var targetPath = _this._path + '/' + target; _this._graph.addTarget(targetPath, t.getName()); }); } var origRequireSources = _this.requireSources, origRequireNodeSources = _this.requireNodeSources; mirror.requireSources = function(sources) { if (_this._graph) { targets.forEach(function(target) { var targetPath = _this._path + '/' + target; sources.forEach(function(source) { _this._graph.addDep(targetPath, _this._path + '/' + _this.unmaskTargetName(source)); }); }); } return origRequireSources.apply(_this, arguments); }; mirror.requireNodeSources = function(sources) { if (_this._graph) { Object.keys(sources).forEach(function(nodeName) { targets.forEach(function(target) { var targetPath = _this._path + '/' + target; sources[nodeName].forEach(function(source) { _this._graph.addDep(targetPath, nodeName + '/' + _this.unmaskNodeTargetName(nodeName, source)); }); }); }); } return origRequireNodeSources.apply(_this, arguments); }; }); }); })); }, /** * Возвращает техническую информацию об указанном таргете. * Формат результата: { tech: <ссылка на технологию>, started: true|false, promise: <промис на выполнение> } * @param {String} name Имя таргета. * @returns {Object} * @private */ _getTarget: function(name) { var targets = this._targetNames, target; if (!(target = targets[name])) { target = targets[name] = {started: false}; } if (!target.promise) { target.promise = Vow.promise(); } return target; }, /** * Возвращает true, если таргет под указанным именем может быть собран. В противном случае возвращает false. * @param {String} name * @returns {Boolean} */ hasRegisteredTarget: function(name) { return !!this._targetNames[name]; }, /** * Возвращает базовое имя таргета по умолчанию для ноды. Например, "index" для "pages/index". * Добавляет суффикс если не обходимо. Например, для "pages/index": node.getTargetName("js") -> index.js * @param {String} suffix * @returns {String} */ getTargetName: function(suffix) { return this._targetName + (suffix ? '.' + suffix : ''); }, /** * Демаскирует имя таргета. Например, для ноды "pages/index" заменяет "?.js" на "index.js". * @param {String} targetName * @returns {String} */ unmaskTargetName: function(targetName) { return targetName.replace(/\?/g, this._targetName); }, /** * Регистрирует таргет для указанной технологии. * @param {String} target * @param {Tech} tech * @private */ _registerTarget: function(target, tech) { var targetObj = this._getTarget(target); if (targetObj.tech) { throw Error( 'Concurrent techs for target: ' + target + ', techs: "' + targetObj.tech.getName() + '" vs "' + tech.getName() + '"' ); } targetObj.tech = tech; }, /** * Оповещает ноду о том, что таргет собран. Технологии, которые зависят от этого таргета могут продолжить работу. * @param {String} target * @param {Object} [value] * @returns {Promise} */ resolveTarget: function(target, value) { var targetObj = this._getTarget(target); if (!targetObj.isValid) { this._logger.logAction('rebuild', target, targetObj.tech.getName()); } if (this._graph) { this._graph.resolveTarget(this._path + '/' + target); } return targetObj.promise.fulfill(value); }, /** * Вывод сообщение в лог о том, что таргет не был пересобран, т.к. в этом нет нужды. * @param {String} target */ isValidTarget: function(target) { var targetObj = this._getTarget(target); this._logger.isValid(target, targetObj.tech.getName()); targetObj.isValid = true; }, /** * Оповещает ноду о том, что таргет не удалось собрать. В этот метод следует передать ошибку. * @param {String} target * @param {Error} err * @returns {Promise} */ rejectTarget: function(target, err) { var targetObj = this._getTarget(target); this._logger.logErrorAction('failed', target, targetObj.tech.getName()); return targetObj.promise.reject(err); }, /** * Требует выполнения таргетов для переданных нод. * Требование в формате: { "node/path": [ "target1", "target2", ... ], "another-node/path": ... }. * @param {Object} sourcesByNodes * @returns {Promise} */ requireNodeSources: function(sourcesByNodes) { var _this = this, resultByNodes = {}; return Vow.all(Object.keys(sourcesByNodes).map(function(nodePath) { return _this._makePlatform.requireNodeSources(nodePath, sourcesByNodes[nodePath]).then(function(results) { resultByNodes[nodePath] = results; }); })).then(function() { return resultByNodes; }); }, /** * Требует выполнения таргетов. * Требование в формате: ["target1", "target2", ...]. * Например, node.requireSources(["index.js"]).then(...); * @param {String[]} sources * @returns {Promise} */ requireSources: function(sources) { var _this = this; return this._registerTargets().then(function() { return Vow.all(sources.map(function(source) { source = _this.unmaskTargetName(source); var targetObj = _this._getTarget(source); if (!targetObj.tech) { throw TargetNotFoundEror('There is no tech for target ' + _this._path + '/' + source + '.'); } if (!targetObj.started) { targetObj.started = true; if (!targetObj.tech.__started) { targetObj.tech.__started = true; try { Vow.when(targetObj.tech.build()).fail(function(err) { _this.rejectTarget(source, err); }); } catch (err) { _this.rejectTarget(source, err); } } } return targetObj.promise; })); }); }, /** * Удаляет таргеты с помощью технологий. * @param {String[]} targets * @returns {Promise} */ cleanTargets: function(targets) { var _this = this; return Vow.all(targets.map(function(target) { var targetObj = _this._getTarget(target); if (!targetObj.tech) { throw Error('There is no tech for target ' + target + '.'); } return Vow.when(targetObj.tech.clean()); })); }, /** * Регистрирует таргеты по имеющимся технологиям. * Часть инициализации ноды. * @returns {Promise} * @private */ _registerTargets: function() { var _this = this; if (!this._registerTargetsPromise) { this._registerTargetsPromise = Vow.all(this._techs.map(function (t) { return t.getTargets(); })).then(function (targetLists) { function registerTarget(targetName) { _this._registerTarget(targetName, _this._techs[i]); } for (var i = 0, l = _this._techs.length; i < l; i++) { targetLists[i].forEach(registerTarget); } }); } return this._registerTargetsPromise; }, /** * Вычисляет список имен таргетов по переданным данным. * @param {String[]} targets Список целей (указанный в настройках сборки, например). * @param {String[]} defaultTargetList Полный список целей (для случая, когда указана маска "*"). * @returns {String[]} * @private */ _resolveTargets: function(targets, defaultTargetList) { var targetsToBuild = this._targetNamesToBuild, _this = this; if (targets) { targetsToBuild = targets; targetsToBuild = [].concat.apply([], targetsToBuild.map(function(targetName) { if (targetName == '*') { return (defaultTargetList.length ? defaultTargetList : Object.keys(_this._targetNames)); } else { return [targetName]; } })); } if (!targetsToBuild) { targetsToBuild = Object.keys(this._targetNames); } targetsToBuild = targetsToBuild.filter(function(elem, pos) { return targetsToBuild.indexOf(elem) == pos; }); return targetsToBuild; }, /** * Запускает сборку указанных целей для ноды. * @param {String[]} targets * @returns {Promise} */ build: function(targets) { var _this = this; return this.requireSources(_this._resolveTargets(targets, _this._targetNamesToBuild)); }, // TODO: Удалить параметр buildCache. /** * Запускает удаление указанных целей для ноды. * @param {String[]} targets * @param {Object} buildCache Вроде, лишний параметр, надо удалить. * @returns {Promise} */ clean: function(targets, buildCache) { var _this = this; this.buildState = buildCache || {}; return this._registerTargets().then(function(){ return _this.cleanTargets(_this._resolveTargets(targets, _this._targetNamesToClean)); }); }, /** * Возвращает кэш для таргета ноды. Этим методом могут пользоваться технологии для кэширования. * @param {String} subCacheName * @returns {Cache} */ getNodeCache: function(subCacheName) { return subCacheName ? this._nodeCache.subCache(subCacheName) : this._nodeCache; }, /** * Возвращает схему именования для уровня переопределения. * Схема именования содержит два метода: * ```javascript * // Выполняет построение структуры файлов уровня переопределения, используя методы инстанции класса LevelBuilder. * {Promise} buildLevel( {String} levelPath, {LevelBuilder} levelBuilder ) * // Возвращает путь к файлу на основе пути к уровню переопределения и BEM-описания. * {String} buildFilePath( {String} levelPath, {String} blockName, {String} elemName, {String} modName, {String} modVal ) * ``` * @returns {Object|undefined} */ getLevelNamingScheme: function(levelPath) { return this._makePlatform.getLevelNamingScheme(levelPath); } });
module.exports = inherit(Runner, { __constructor: function(config, options) { this.__base(config); options = options || {}; this._tempDir = options.tempDir || temp.path('gemini'); }, _prepare: function() { return fs.makeTree(this._tempDir); }, _processCapture: function(capture) { var refPath = this.config.getScreenshotPath( capture.suite, capture.state.name, capture.browser.id), tmpPath = this._tempPath(), _this = this; return fs.exists(refPath) .then(function(refExists) { if (!refExists) { return q.reject(new StateError( 'Can not find reference image at ' + refPath + '\n' + 'Run `gemini gather` command to capture all reference images.' )); } }) .then(function() { return capture.image.save(tmpPath); }) .then(function() { return [tmpPath, Image.compare(tmpPath, refPath, { strictComparison: _this.config.strictComparison, canHaveCaret: capture.canHaveCaret })]; }) .spread(function(currentPath, isEqual) { _this._stats.add(isEqual? 'passed' : 'failed'); _this.emit('endTest', { suiteName: capture.suite.name, suiteId: capture.suite.id, stateName: capture.state.name, equal: isEqual, referencePath: refPath, currentPath: currentPath, browserId: capture.browser.id }); }); }, _tempPath: function() { return temp.path({dir: this._tempDir, suffix: '.png'}); } });
*/ exports.Overlay = Overlay; function overlay(options) { return new Overlay(options); } function Overlay(options) { Emitter.call(this); options = options || {}; this.$el = o(require("./template")); this.$el.appendTo("body"); } inherit(Overlay, Emitter); Overlay.prototype.show = function() { this.emit("show"); this.$el.removeClass("hide"); return this; }; Overlay.prototype.hide = function() { this.emit("hide"); return this.remove(); }; Overlay.prototype.remove = function() { var self = this; this.emit('close');
module.exports.OldDeps = (function() { /** * Класс, раскрывающий зависимости. Взят из bem-tools. * * @name OldDeps */ var OldDeps = inherit({ /** * Конструктор. * Принимает блоки из bemdecl. * * @param {Array} deps */ __constructor: function(deps) { this.items = {}; this.itemsByOrder = []; this.uniqExpand = {}; // Force adding of root item to this.items var rootItem = this.rootItem = new OldDepsItem({}); this.items[rootItem.buildKey()] = rootItem; deps && this.parse(deps); }, /** * Добавляет зависимость в коллекцию. * * @param {OldDepsItem} target * @param {String} depsType shouldDeps/mustDeps * @param {OldDepsItem} item */ add: function(target, depsType, item) { var items = this.items, targetKey = target.buildKey(), itemKey = item.buildKey(); if (!items[itemKey]) { items[itemKey] = item; this.itemsByOrder.push(itemKey); } (items[targetKey] || (items[targetKey] = target))[depsType].push(itemKey); }, /** * Удаляет зависимость из коллекции. * * @param {OldDepsItem} target * @param {OldDepsItem} item */ remove: function(target, item) { target = this.items[target.buildKey()]; var itemKey = item.buildKey(); removeFromArray(target.shouldDeps, itemKey); removeFromArray(target.mustDeps, itemKey); }, /** * Клонирует резолвер зависимостей. * * @param {OldDeps} [target] * @returns {OldDeps} */ clone: function(target) { target || (target = new this.__self()); var items = this.items; for (var i in items) { if (!items.hasOwnProperty(i)) continue; target.items[i] = items[i].clone(); } target.itemsByOrder = this.itemsByOrder.concat(); target.tech = this.tech; target.uniqExpand = this.uniqExpand; return target; }, /** * Разбирает bemdecl. * * @param {Array} deps * @param {Object} [ctx] * @param {Function} [fn] * @returns {OldDeps} */ parse: function(deps, ctx, fn) { fn || (fn = function(i) { this.add(this.rootItem, 'shouldDeps', i); }); var _this = this, forEachItem = function(type, items, ctx) { items && !isEmptyObject(items) && (Array.isArray(items) ? items : [items]).forEach(function(item) { if (isSimple(item)) { var i = item; (item = {})[type] = i; } item.name && (item[type] = item.name); var depsItem = new OldDepsItem(item, ctx); fn.call(_this, depsItem); // _this.add(rootItem, 'shouldDeps', depsItem); _this.parse( item.mustDeps, depsItem, function(i) { this.add(depsItem, 'mustDeps', i) }); _this.parse( item.shouldDeps, depsItem, function(i) { this.add(depsItem, 'shouldDeps', i) }); _this.parse( item.noDeps, depsItem, function(i) { this.remove(depsItem, i) }); forEachItem('elem', item.elems, depsItem); var mods = item.mods; if (mods && !Array.isArray(mods)) { // Object var modsArr = []; for (var m in mods) { if (!mods.hasOwnProperty(m)) continue; modsArr.push({ mod: m }); var mod = { mod: m }, v = mods[m]; Array.isArray(v) ? (mod.vals = v) : (mod.val = v); modsArr.push(mod); } mods = modsArr; } forEachItem('mod', mods, depsItem); forEachItem('val', item.vals, depsItem); }); }; forEachItem('block', deps, ctx); return this; }, /** * Раскрывает зависимости, используя deps.js-файлы. * * @param {Object} tech * @returns {Promise} */ expandByFS: function(tech) { this.tech = tech; var _this = this, depsCount1 = this.getCount(), depsCount2; return Vow.when(this.expandOnceByFS()) .then(function again(newDeps) { depsCount2 = newDeps.getCount(); if (depsCount1 !== depsCount2) { depsCount1 = depsCount2; return Vow.when(newDeps.expandOnceByFS(), again); } return newDeps.clone(_this); }); }, /** * Раскрывает зависимости, используя deps.js-файлы без повторений. * * @returns {Promise} */ expandOnceByFS: function() { var newDeps = this.clone(); var items = this.filter(function(item) { return !newDeps.uniqExpand.hasOwnProperty(item.buildKey()); }); function keepWorking(item) { newDeps.uniqExpand[item.buildKey()] = true; return newDeps.expandItemByFS(item).then(function() { if (items.length > 0) { return keepWorking(items.shift()); } else { return null; } }); } if (items.length > 0) { return keepWorking(items.shift()).then(function() { return newDeps; }); } else { return Vow.fulfill(newDeps); } }, /** * Раскрывает одну зависимость, используя deps.js-файлы. * * @param {OldDepsItem} item * @returns {Promise} */ expandItemByFS: function(item) { var _this = this, tech = this.tech; var files = tech.levels.getFilesByDecl(item.item.block, item.item.elem, item.item.mod, item.item.val) .filter(function(file) { return file.suffix === 'deps.js'; }); var promise = Vow.fulfill(); files.forEach(function(file) { promise = promise.then(function() { return vowFs.read(file.fullname, 'utf8').then(function(content) { try { _this.parse(vm.runInThisContext(content, file.fullname), item); } catch (e) { throw new Error('Syntax error in file "' + file.fullname + '": ' + e.message); } }); }); }); return promise; }, /** * Вычитает зависимости из переданного OldDeps. * * @param {OldDeps} deps * @returns {OldDeps} */ subtract: function(deps) { var items1 = this.items, items2 = deps.items; for (var k in items2) if (k && items2.hasOwnProperty(k)) delete items1[k]; return this; }, /** * Сохраняет пересечение с другим OldDeps. * * @param {OldDeps} deps * @returns {OldDeps} */ intersect: function(deps) { var items1 = this.items, items2 = deps.items, newItems = {}; for (var k in items2) { if ((items2.hasOwnProperty(k) && items1.hasOwnProperty(k)) || !k) newItems[k] = items1[k]; } this.items = newItems; return this; }, /** * Возвращает количество зависимостей. * * @returns {Number} */ getCount: function() { var res = 0, items = this.items; for (var k in items) items.hasOwnProperty(k) && res++; return res; }, /** * Итерирует по набору зависимостей. * * @param {Function} fn * @param {Object} [uniq] * @param {Array} [itemsByOrder] * @param {Object} [ctx] */ forEach: function(fn, uniq, itemsByOrder, ctx) { uniq || (uniq = {}); var _this = this; (itemsByOrder || this.items[''].shouldDeps).forEach(function(i) { if (i = _this.items[i]) { var key = i.buildKey(); if (!uniq.hasOwnProperty(key)) { uniq[key] = true; var newCtx = ctx || i; _this.forEach(fn, uniq, i.mustDeps, newCtx); fn.call(_this, i, newCtx); _this.forEach(fn, uniq, i.shouldDeps, newCtx); } } }) }, /** * Вызывает map для набора зависимостей. * * @param {Function} fn * @returns {Array} */ map: function(fn) { var res = []; this.forEach(function(item) { res.push(fn.call(this, item)); }); return res; }, /** * Фильтрует зависимости, возвращает результат. * @param {Function} fn * @returns {Array} */ filter: function(fn) { var res = []; this.forEach(function(item) { if (fn.call(this, item)) res.push(item); }); return res; }, /** * Возвращает результат резолвинга. * * @returns {Object} */ serialize: function() { var byTech = {}; this.forEach(function(item, ctx) { var t1 = ctx.item.tech || '', t2 = item.item.tech || '', techsByTech = byTech[t1] || (byTech[t1] = {}), i = item.serialize(); i && (techsByTech[t2] || (techsByTech[t2] = [])).push(i); }); return byTech; }, /** * Сериализует в строку. * * @returns {String} */ stringify: function() { var res = [], deps = this.serialize(); if (deps['']) { res.push('exports.deps = ' + JSON.stringify(deps[''][''], null, 4) + ';\n'); delete deps['']['']; } else { res.push('exports.deps = [];\n'); } isEmptyObject(deps) || res.push('exports.depsByTechs = ' + JSON.stringify(deps, null, 4) + ';\n'); return res.join(''); }, /** * Возвращает результат раскрытия зависимостей. * * @returns {Object|*|*|Array} */ getDeps: function() { var serializedData = this.serialize(); return (serializedData && serializedData[''] && serializedData['']['']) || []; } }); /** * Элемент зависимостей. * * @name OldDepsItem */ var OldDepsItem = inherit({ __constructor: function(item, ctx) { this.shouldDeps = []; this.mustDeps = []; this.item = {}; this.extendByCtx({ item: item }); this.extendByCtx(ctx); }, /** * Раскрывает зависимости. * * @param {Object} ctx * @returns {OldDepsItem} */ extendByCtx: function(ctx) { if (ctx && (ctx = ctx.item)) { var ks = ['tech', 'block', 'elem', 'mod', 'val'], k; while (k = ks.shift()) { if (this.item[k]) { break; } else { ctx[k] && (this.item[k] = ctx[k]); } } } return this; }, /** * Возвращает копию. * * @returns {OldDepsItem} */ clone: function() { var res = new this.__self({}, this); res.shouldDeps = this.shouldDeps.concat(); res.mustDeps = this.mustDeps.concat(); this.hasOwnProperty('key') && (res.key = this.key); return res; }, /** * Расширяет зависимость. * * @param {OldDepsItem} item * @returns {OldDepsItem} */ extend: function(item) { if (!item) return this; var ds = ['mustDeps', 'shouldDeps'], d, thisDeps, itemDeps; while (d = ds.shift()) { itemDeps = item[d] || (item[d] = {}); if (thisDeps = this.item[d]) { for (var k in thisDeps) if (thisDeps.hasOwnProperty(k)) { if (!thisDeps[k].extend) throw 'bla'; (itemDeps[k] = thisDeps[k].extend(itemDeps[k])); } } } return item; }, /** * Записывает зависимость в кэш по ключу. * * @param {Object} cache * @returns {OldDepsItem} */ cache: function(cache) { var key = this.buildKey(); return cache[key] = this.extend(cache[key]); }, /** * Строит ключ для зависимости. * * @returns {String} */ buildKey: function() { if ('key' in this) return this.key; var i = this.item, k = ''; if (i.block) { k += i.block; i.elem && (k += '__' + i.elem); if (i.mod) { k += '_' + i.mod; i.val && (k += '_' + i.val); } } i.tech && (k += '.' + i.tech); return this.key = k; }, /** * Сериализует зависимость в объект. * * @returns {Object} */ serialize: function() { var res = {}, ks = ['tech', 'block', 'elem', 'mod', 'val'], k; while (k = ks.shift()) this.item[k] && (res[k] = this.item[k]); if (res.block) return res; } }); exports.DepsItem = OldDepsItem; /** * Возвращает true при String/Number. * @param {*} value * @returns {Boolean} */ function isSimple(value) { var t = typeof value; return t === 'string' || t === 'number'; } /** * Хэлпер для удаления значений из массива. * Возвращает true в случае успеха. * * @param {Array} arr * @param {*} value * @returns {Boolean} */ function removeFromArray(arr, value) { var i = arr.indexOf(value); if (i >= 0) { arr.splice(i, 1); return true; } else { return false; } } /** * Возвращает true, если переданный объект пуст. * @param {Object} obj * @returns {Boolean} */ function isEmptyObject(obj) { for (var i in obj) return false; return true; } return OldDeps; })();
var NodesSerializer = inherit({ /** * @param {BaseNode[]} nodes * @param {String|Boolean} [options.inputSourceMap=false] One of the 'file', 'inline', false. * @param {String|Boolean} [options.sourceMap=false] One of the 'file', 'inline', false. * @param {String} [options.sourceMapRoot] Relative root for sources (i.e. all source paths will be relative to this path). * @param {String} [options.sourceMapFilename] `file` property of the resulting source map. * @param {String} [options.sourceMapSourceRoot] `sourceRoot` property of the resulting source map. */ __constructor: function(nodes, options) { this.nodes = nodes; options = (options || {}); options.sourceMapFilename = options.sourceMapFilename || ''; options.sourceMapRoot = options.sourceMapRoot || path.dirname(options.sourceMapFilename); this.options = options; this.builder = new Builder({ generateSourceMap: !!options.sourceMap, sourceMapFilename: options.sourceMapFilename, sourceMapSourceRoot: options.sourceMapSourceRoot }); if (options.sourceMap) { this.sourceMapRegistry = new SourceMapRegistry({ sourceMapLocation: options.inputSourceMap }); } /** * @type {String[]} */ this.passedDirectives = []; }, /** * @returns {Builder} */ serialize: function() { var self = this; var options = this.options; var builder = this.builder; var inputSourceMap = options.sourceMap && options.inputSourceMap; this.nodes.forEach(function append(node, i) { if (node.hasChildren()) { node.eachChild(append); } else { var resetFile = i === 0; var mappings = inputSourceMap ? self.getMappingsForNode(node) : null; builder.append(path.relative(options.sourceMapRoot, node.path), node, mappings, resetFile); } if (node.directive) { // Mark the directive as passed in order to correct offsets // of the source mappings for the following node. self.passedDirectives.push(node.directive); } }); return builder; }, /** * @param {BaseNode} node * @returns {Mapping[]} */ getMappingsForNode: function(node) { // Always extract passed directive, since the presence of the source map // is not guaranteed for all source files. var passedDirective = this.passedDirectives.pop(); var mappingsIterator = this.sourceMapRegistry.getMappingsIterator(node.path); if (!mappingsIterator) { return null; } var lines = node.getLines(); var linesCount = lines.length; // Lines are 1-based. var firstLine = mappingsIterator.lastLine; var lastLine = firstLine + linesCount - 1; // First column starts after borschik's directive. var firstColumn = mappingsIterator.lastColumn + (passedDirective ? passedDirective.length : 0); // One-liners should add the start offset to the end offset. -1 since columns are 0-based; var lastColumn = (linesCount === 1 ? firstColumn : 0) + lines[linesCount - 1].length - 1; mappingsIterator.skipWhile(function(m) { return m.generatedLine === firstLine && m.generatedColumn < firstColumn; }); var mappings = mappingsIterator.readWhile(function(m) { return m.generatedLine < lastLine || m.generatedLine === lastLine && m.generatedColumn <= lastColumn; }); // Correct offsets. mappings.forEach(function(m) { m.generatedColumn = m.generatedColumn - (m.generatedLine === firstLine ? firstColumn : 0); m.generatedLine = m.generatedLine - (firstLine - 1); }); mappingsIterator.lastLine = lastLine; mappingsIterator.lastColumn = node.ignoreMappings() ? firstColumn : (lastColumn + 1); return mappings; }, }, { inherit: _inherit });
module.exports = inherit(Base, { __constructor: function(id, iframeContainer) { this.__base(id); this.iframeContainer = iframeContainer; }, initialize: function() { var self = this; self.getIframe().addEventListener('load', function iframeLoadHandler(event) { var lazyLoadInit = self.__self.getLazyLoad(); lazyLoadInit(this); self.getIframe().removeEventListener('load', iframeLoadHandler); }); } }, { instances: {}, createInstance: function(id, iframeContainer) { var instance = new this(id, iframeContainer); instance.initialize(); this.instances[id] = instance; return instance; }, sourceLazyLoad: null, getLazyLoad: function() { if (!this.sourceLazyLoad) { //Initialize the lazy load this.sourceLazyLoad = lazyLoad({ container: doc.body, offset: 1000, src: 'data-src' }); } return this.sourceLazyLoad; } });
function _inherit(d, s) { return inherit(this, d, s); }
module.exports = inherit(require('../lib/tech/base-tech'), { getName: function() { return 'borschik'; }, configure: function() { this._source = this.node.unmaskTargetName(this.getRequiredOption('sourceTarget')); this._target = this.node.unmaskTargetName(this.getRequiredOption('destTarget')); this._freeze = this.getOption('freeze', false); this._minify = this.getOption('minify', true); }, getTargets: function() { return [this._target]; }, build: function() { var target = this._target, targetPath = this.node.resolvePath(target), source = this._source, sourcePath = this.node.resolvePath(source), _this = this, cache = this.node.getNodeCache(target); return this.node.requireSources([source]).then(function() { if (cache.needRebuildFile('source-file', sourcePath) || cache.needRebuildFile('target-file', targetPath)) { var borschikProcessor = BorschikProcessorSibling.fork(); return Vow.when(borschikProcessor.process(sourcePath, targetPath, _this._freeze, _this._minify)).then(function() { cache.cacheFileInfo('source-file', sourcePath); cache.cacheFileInfo('target-file', targetPath); _this.node.resolveTarget(target); borschikProcessor.dispose(); }); } else { _this.node.isValidTarget(target); _this.node.resolveTarget(target); return null; } }); } });
var Track = inherit(/** @lends Track.prototype */{ /** * @private * @memberOf {Track} * @method * * @constructs * * @returns void * */ __constructor: function (agent) { /** * @public * @memberOf {Track} * @property * @type {Tracker} * */ this.agent = agent; /** * @public * @memberOf {Track} * @property * @type {String} * */ this.id = uniqueId(); /** * @private * @memberOf {Track} * @property * @type {Object} * */ this.__tasks = {}; }, /** * @public * @memberOf {Track} * @method * * @param {String} path * @param {Object} [locals] * * @returns {vow.Promise} * */ invoke: function (path, locals) { var result; if ( !_.has(this.__tasks, path) ) { this.__tasks[path] = new TaskCache(); } result = this.__tasks[path].get(locals); if ( result ) { return result; } result = this.__executeUnit(path, locals); this.__tasks[path].set(locals, result); return result; }, /** * @protected * @memberOf {Track} * @method * * @param {String} path * @param {Object} [params] * * @returns {Deps} * */ _createContext: function (path, params) { return new Deps(this, path, params); }, /** * @private * @memberOf {Track} * @method * * @param {String} path * @param {Object} [params] * * @returns {vow.Promise} * */ __executeUnit: function (path, params) { return this._createContext(path, params).execute(); } });
// unique ID this.id = window.ZeroClipboard.nextId++; this.movieId = 'ZeroClipboardMovie_' + this.id; // register client with ZeroClipboard global to receive flash events window.ZeroClipboard.clients[this.id] = this; // create movie if (node) this.render(node, parent); } /** * Inherits from `Emitter.prototype`. */ inherit(Client, Emitter); /** * Render the SWF movie on top of the "elem" DOM element. * * @param elem DOMNode The DOM node that will be "converted" * @param appendElem DOMNode (optional) The DOM node that the SWF will be inserted into * @api public */ Client.prototype.glue = Client.prototype.render = function(elem, appendElem) { this.domElement = elem; // float just above object, or default zIndex if dom element isn't set if (this.domElement.style.zIndex) {
module.exports = inherit({ /** * Конструктор. * @param {Level[]} levels */ __constructor: function(levels) { this.levels = levels; this.declarations = []; this.resolved = {}; this.declarationIndex = {}; }, /** * Раскрывает шорткаты deps'а. * @param {String|Object} dep * @param {String} blockName * @param {String} elemName * @returns {Array} */ normalizeDep: function(dep, blockName, elemName) { var levels = this.levels; if (typeof dep === 'string') { return [{ name: dep }]; } else { var res = []; if (dep.view) { (dep.mods || (dep.mods = {})).view = dep.view; } if (dep.skin) { (dep.mods || (dep.mods = {})).skin = dep.skin; } if (dep.elem) { if (dep.mods) { Object.keys(dep.mods).forEach(function(modName) { var modVals = dep.mods[modName]; if (!Array.isArray(modVals)) { modVals = [modVals]; } res = res.concat(modVals.map(function(modVal) { return { name: dep.block || blockName, elem: dep.elem, modName: modName, modVal: modVal }; })); }); } else if (dep.mod) { res.push({ name: dep.block || blockName, elem: dep.elem, modName: dep.mod, modVal: dep.val }); } else { res.push({ name: dep.block || blockName, elem: dep.elem }); } } else if (dep.mod || dep.mods || dep.elems) { if (dep.mod) { res.push({ name: dep.block || blockName, modName: dep.mod, modVal: dep.val }); } Object.keys(dep.mods || {}).forEach(function(modName) { var modVals = dep.mods[modName]; if (modVals === '*') { modVals = levels.getModValues(dep.block || blockName, modName); } if (!Array.isArray(modVals)) { modVals = [modVals]; } res = res.concat(modVals.map(function(modVal) { if (elemName && !dep.block && !dep.elem) { return { name: dep.block || blockName, elem: elemName, modName: modName, modVal: modVal }; } else { return { name: dep.block || blockName, modName: modName, modVal: modVal }; } })); }); if (dep.elems) { res.push({ name: dep.block || blockName }); var elems = dep.elems || []; if (!Array.isArray(elems)) { elems = [elems]; } elems.forEach(function(elem) { if (typeof elem === 'object') { res.push({ name: dep.block || blockName, elem: elem.elem }); Object.keys(elem.mods || {}).forEach(function(modName) { var modVals = elem.mods[modName]; if (!Array.isArray(modVals)) { modVals = [modVals]; } res = res.concat(modVals.map(function(modVal) { return { name: dep.block || blockName, elem: elem.elem, modName: modName, modVal: modVal }; })); }); } else { res.push({ name: dep.block || blockName, elem: elem }); } }); } } else { res = [{ name: dep.block || blockName }]; } if (dep.required) { res.forEach(function (subDep) { subDep.required = true; }); } return res; } }, /** * Раскрывает шорткаты для списка deps'ов. * @param {String|Object|Array} deps * @param {String} [blockName] * @param {String} [elemName] * @returns {Array} */ normalizeDeps: function(deps, blockName, elemName) { if (Array.isArray(deps)) { var result = []; for (var i = 0, l = deps.length; i < l; i++) { result = result.concat(this.normalizeDep(deps[i], blockName, elemName)); } return result; } else { return this.normalizeDep(deps, blockName, elemName); } }, /** * Возвращает deps'ы для декларации (с помощью levels). * @param {Object} decl * @returns {{mustDeps: Array, shouldDeps: Array}} */ getDeps: function(decl) { var _this = this, files, key, mustDecls, mustDepIndex, mustDeps, shouldDepIndex, shouldDeps; if (decl.elem) { files = this.levels.getElemFiles(decl.name, decl.elem, decl.modName, decl.modVal); } else { files = this.levels.getBlockFiles(decl.name, decl.modName, decl.modVal); } files = files.filter(function(file) { return file.suffix === 'deps.js' || file.suffix === 'deps.yaml'; }); mustDepIndex = {}; shouldDepIndex = {}; mustDepIndex[declKey(decl)] = true; mustDeps = []; if (decl.modName) { if (decl.elem) { mustDecls = [ { name: decl.name, elem: decl.elem } ]; if (decl.modVal) { mustDecls.push({ name: decl.name, elem: decl.elem, modName: decl.modName }); } } else { mustDecls = [ { name: decl.name } ]; if (decl.modVal) { mustDecls.push({ name: decl.name, modName: decl.modName }); } } mustDecls.forEach(function(mustDecl) { mustDecl.key = declKey(mustDecl); mustDepIndex[mustDecl.key] = true; mustDeps.push(mustDecl); }); } shouldDeps = []; function keepWorking(file) { return vowFs.read(file.fullname, 'utf8').then(function(depContent) { if (file.suffix === 'deps.js') { var depData; try { depData = vm.runInThisContext(depContent); } catch (e) { throw new Error('Syntax error in file "' + file.fullname + '": ' + e.message); } depData = Array.isArray(depData) ? depData : [depData]; depData.forEach(function(dep) { if (!dep.tech) { if (dep.mustDeps) { _this.normalizeDeps(dep.mustDeps, decl.name, decl.elem).forEach(function(nd) { key = declKey(nd); if (!mustDepIndex[key]) { mustDepIndex[key] = true; nd.key = key; mustDeps.push(nd); } }); } if (dep.shouldDeps) { _this.normalizeDeps(dep.shouldDeps, decl.name, decl.elem).forEach(function(nd) { key = declKey(nd); if (!shouldDepIndex[key]) { shouldDepIndex[key] = true; nd.key = key; shouldDeps.push(nd); } }); } if (dep.noDeps) { _this.normalizeDeps(dep.noDeps, decl.name, decl.elem).forEach(function(nd) { key = declKey(nd); nd.key = key; removeFromDeps(nd, mustDepIndex, mustDeps); removeFromDeps(nd, shouldDepIndex, shouldDeps); }); } } }); } else if (file.suffix === 'deps.yaml') { var depYamlStructure = yaml.safeLoad(depContent, { filename: file.fullname, strict: true }); if (!Array.isArray(depYamlStructure)) { throw new Error('Invalid yaml deps structure at: ' + file.fullname); } _this.normalizeDeps(depYamlStructure, decl.name, decl.elem).forEach(function(nd) { key = declKey(nd); var index; var depList; if (nd.required) { index = mustDepIndex; depList = mustDeps; } else { index = shouldDepIndex; depList = shouldDeps; } if (!index[key]) { index[key] = true; nd.key = key; depList.push(nd); } }); } if (files.length > 0) { return keepWorking(files.shift()); } else { return null; } }); } function removeFromDeps(decl, index, list) { if (index[decl.key]) { for (var i = 0, l = list.length; i < l; i++) { if (list[i].key === decl.key) { return list.splice(i, 1); } } } else { index[decl.key] = true; } return null; } var result = { mustDeps: mustDeps, shouldDeps: shouldDeps }; if (files.length > 0) { return keepWorking(files.shift()).then(function() { return result; }); } else { return Vow.fulfill(result); } }, /** * Добавляет декларацию блока в резолвер. * @param {String} blockName * @param {String} modName * @param {String} modVal * @returns {Promise} */ addBlock: function(blockName, modName, modVal) { if (modName) { this.addDecl({ name: blockName, modName: modName, modVal: modVal }); } else { this.addDecl({ name: blockName }); } }, /** * Добавляет декларацию элемента в резолвер. * @param {String} blockName * @param {String} elemName * @param {String} modName * @param {String} modVal * @returns {Promise} */ addElem: function(blockName, elemName, modName, modVal) { if (modName) { return this.addDecl({ name: blockName, elem: elemName, modName: modName, modVal: modVal }); } else { return this.addDecl({ name: blockName, elem: elemName }); } }, /** * Добавляет декларацию в резолвер. * @param {Object} decl * @returns {Promise} */ addDecl: function(decl) { var _this = this, key = declKey(decl); if (this.declarationIndex[key]) { return null; } this.declarations.push(decl); this.declarationIndex[key] = decl; return this.getDeps(decl).then(function(deps) { decl.key = key; decl.deps = {}; decl.depCount = 0; return _this.addDecls(deps.mustDeps, function(dep) { decl.deps[dep.key] = true; decl.depCount++; }).then(function() { return _this.addDecls(deps.shouldDeps); }); }); }, /** * Добавляет набор деклараций. * @param {Array} decls * @returns {Promise} * @param {Function} [preCallback] */ addDecls: function(decls, preCallback) { var promise = Vow.fulfill(); var _this = this; decls.forEach(function(decl) { promise = promise.then(function() { if (preCallback) { preCallback(decl); } return _this.addDecl(decl); }); }); return promise; }, /** * Упорядочивает deps'ы, возвращает в порядке зависимостей. * @returns {Array} */ resolve: function() { var decl, hasChanges, item, items, newItems, result, subDecl, i, j, l; items = this.declarations.slice(0); result = []; hasChanges = true; while (hasChanges) { newItems = []; hasChanges = false; for (i = 0, l = items.length; i < l; i++) { decl = items[i]; if (decl.depCount === 0) { hasChanges = true; for (j = 0; j < l; j++) { subDecl = items[j]; if (subDecl.deps[decl.key]) { delete subDecl.deps[decl.key]; subDecl.depCount--; } } item = { block: decl.name }; if (decl.elem) { item.elem = decl.elem; } if (decl.modName) { item.mod = decl.modName; if (decl.hasOwnProperty('modVal')) { item.val = decl.modVal; } } result.push(item); } else { newItems.push(decl); } } items = newItems; } if (items.length) { var errorMessage = items.map(function(item) { return item.key + ' <- ' + Object.keys(item.deps).join(', '); }); throw Error('Unresolved deps: \n' + errorMessage.join('\n')); } return result; } });