<commit_before>define(['marionette',
    'modules/dc/views/samplechanger',
    'collections/datacollections',
    'modules/dc/datacollections',
    'tpl!templates/dc/samplechangerfull.html'
    ], function(Marionette, SampleChanger, DCs, DCList, template) {

    return SampleChanger.extend({
        template: template,
        className: 'content',
        fullScreen: true,

        regions: {
            dcl: '.dcl',
        },

        onRender: function() {
            if (!app.mobile()) {
                this.$el.find('.left').css('width', '25%')
                this.$el.find('.right').css('width', '73%')
            }
            this.$el.find('.key').show().css('float', 'none')

            this.dcs = new DCs(null, { running: false, state: { pageSize: 5 } })
            this.dcl.show(new DCList({ collection: this.dcs, params: { visit: null}, }))

            this.$el.find('.filter, .srch, .dcl .content > h1').hide()
        },

    })

})<commit_msg>make full page sample changer more flexible for larger dewars<commit_after>define(['marionette',
    'modules/dc/views/samplechanger',
    'collections/datacollections',
    'modules/dc/datacollections',
    'tpl!templates/dc/samplechangerfull.html'
    ], function(Marionette, SampleChanger, DCs, DCList, template) {

    return SampleChanger.extend({
        template: template,
        className: 'content',
        fullScreen: true,

        regions: {
            dcl: '.dcl',
        },

        templateHelpers: function() {
            return {
                BL: this.getOption('bl')
            }
        },

        onRender: function() {
            var bl = this.getOption('bl')
            var large = bl in app.config.pucks && app.config.pucks[bl] > 10
            console.log('sc large', large)
            if (!app.mobile() && !large) {
                this.$el.find('.left').css('width', '25%')
                this.$el.find('.right').css('width', '73%')
            }

            if (large) {
                this.$el.find('.left').removeClass('left')
                this.$el.find('.right').removeClass('right')
            }

            this.$el.find('.key').show().css('float', 'none')

            this.dcs = new DCs(null, { running: false, state: { pageSize: 5 } })
            this.dcl.show(new DCList({ collection: this.dcs, params: { visit: null}, }))

            this.$el.find('.filter, .srch, .dcl .content > h1').hide()
        },

    })

})<|endoftext|>


<commit_before>/**!
 * cnpmjs.org - controllers/registry/sync_module_worker.js
 *
 * Copyright(c) cnpmjs.org and other contributors.
 * MIT Licensed
 *
 * Authors:
 *  fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
 */

'use strict';

/**
 * Module dependencies.
 */

var debug = require('debug')('cnpm:controllers:registry:sync_module_worker');
var EventEmitter = require('events').EventEmitter;
var util = require('util');
var fs = require('fs');
var path = require('path');
var crypto = require('crypto');
var eventproxy = require('eventproxy');
var urllib = require('urllib');
var utility = require('utility');
var nfs = require('../../common/nfs');
var npm = require('../../proxy/npm');
var common = require('./common');
var Module = require('../../proxy/module');
var Log = require('../../proxy/module_log');

function SyncModuleWorker(options) {
  EventEmitter.call(this);
  this._logId = options.logId;
  this.names = [options.name];
  this.username = options.username;
  this.nameMap = {};
  this.successes = [];
  this.fails = [];
}

util.inherits(SyncModuleWorker, EventEmitter);

module.exports = SyncModuleWorker;

SyncModuleWorker.prototype.finish = function () {
  this.log('Finished, %d success, %d fail\nSuccess: [ %s ]\nFail: [ %s ]',
    this.successes.length, this.fails.length,
    this.successes.join(', '), this.fails.join(', '));
  this.emit('end');
};

SyncModuleWorker.prototype.log = function (format, arg1, arg2) {
  var str = util.format.apply(util, arguments);
  debug(str);
  Log.append(this._logId, '[' + utility.YYYYMMDDHHmmss() + '] ' + str, utility.noop);
};

SyncModuleWorker.prototype.start = function () {
  this.log('user: %s, sync %s worker start.', this.username, this.names[0]);
  this.next();
};

SyncModuleWorker.prototype.add = function (name) {
  if (this.nameMap[name]) {
    return;
  }
  this.nameMap[name] = true;
  this.names.push(name);
  this.log('    add dependencies: %s', name);
};

SyncModuleWorker.prototype.next = function () {
  var name = this.names.shift();
  if (!name) {
    return this.finish();
  }

  var that = this;
  npm.get(name, function (err, pkg, response) {
    if (!err && !pkg) {
      err = new Error('Module ' + name + ' not exists, http status ' + response.statusCode);
      err.name = 'NpmModuleNotExsitsError';
    }
    if (err) {
      that.fails.push(name);
      that.log('[error] [%s] get package error: %s', name, err.stack);
      return that.next();
    }

    that.log('Start syncing %s', pkg.name);
    that._sync(pkg, function (err, versions) {
      if (err) {
        that.fails.push(pkg.name);
        that.log('[error] [%s] sync error: %s', name, err.stack);
        return that.next();
      }
      that.log('[%s] synced success, %d versions: %s',
        name, versions.length, versions.join(', '));
      that.successes.push(name);
      that.next();
    });
  });
};

SyncModuleWorker.prototype._sync = function (pkg, callback) {
  var username = this.name;
  var name = pkg.name;
  var that = this;
  var ep = eventproxy.create();
  ep.fail(callback);

  Module.listByName(name, ep.done(function (rows) {
    var map = {};
    for (var i = 0; i < rows.length; i++) {
      var r = rows[i];
      if (r.version === 'next') {
        continue;
      }
      map[r.version] = r;
    }
    ep.emit('existsMap', map);
  }));

  var missingVersions = [];
  ep.on('existsMap', function (map) {
    var times = pkg.time || {};
    var versions = [];
    for (var v in times) {
      var exists = map[v];
      var version = pkg.versions[v];
      if (!version || !version.dist) {
        continue;
      }
      if (exists && exists.package.dist.shasum === version.dist.shasum) {
        continue;
      }
      version.gmt_modified = Date.parse(times[v]);
      versions.push(version);
    }

    if (versions.length === 0) {
      that.log('  [%s] all versions are exists', pkg.name);
      return ep.emit('done');
    }

    versions.sort(function (a, b) {
      return a.gmt_modified - b.gmt_modified;
    });
    missingVersions = versions;
    that.log('  [%s] %d versions', pkg.name, versions.length);
    ep.emit('syncModule', missingVersions.shift());
  });

  var versionNames = [];
  ep.on('syncModule', function (syncModule) {
    versionNames.push(syncModule.version);
    that._syncOneVersion(versionNames.length, syncModule, ep.done(function (result) {
      var nextVersion = missingVersions.shift();
      if (!nextVersion) {
        return ep.emit('done', result);
      }
      ep.emit('syncModule', nextVersion);
    }));
  });

  ep.on('done', function () {
    // TODO: set latest version
    callback(null, versionNames);
  });
};

SyncModuleWorker.prototype._syncOneVersion = function (versionIndex, sourcePackage, callback) {
  var that = this;
  var username = this.username;
  var downurl = sourcePackage.dist.tarball;
  var filename = path.basename(downurl);
  var filepath = common.getTarballFilepath(filename);
  var ws = fs.createWriteStream(filepath);
  var options = {
    writeStream: ws,
  };
  var ep = eventproxy.create();
  ep.fail(callback);

  that.log('    [%s:%d] syncing, version: %s, dist: %j',
    sourcePackage.name, versionIndex, sourcePackage.version, sourcePackage.dist);

  for (var k in sourcePackage.dependencies) {
    that.add(k);
  }

  for (var k in sourcePackage.devDependencies) {
    that.add(k);
  }

  var shasum = crypto.createHash('sha1');
  var dataSize = 0;
  urllib.request(downurl, options, ep.done(function (_, response) {
    var statusCode = response && response.statusCode || -1;
    if (statusCode !== 200) {
      fs.unlink(filepath, utility.noop);
      var err = new Error('Download ' + downurl + ' fail, status: ' + statusCode);
      err.name = 'DownloadTarballError';
      err.data = sourcePackage;
      return ep.emit('error', err);
    }

    var rs = fs.createReadStream(filepath);
    rs.once('error', function (err) {
      ep.emit('error', err);
    });
    rs.on('data', function (data) {
      shasum.update(data);
      dataSize += data.length;
    });
    rs.on('end', function () {
      shasum = shasum.digest('hex');
      if (shasum !== sourcePackage.dist.shasum) {
        fs.unlink(filepath, utility.noop);
        var err = new Error('Download ' + downurl + ' shasum:' + shasum + ' not match ' + sourcePackage.dist.shasum);
        err.name = 'DownloadTarballShasumError';
        err.data = sourcePackage;
        return ep.emit('error', err);
      }

      var key = common.getCDNKey(sourcePackage.name, filename);
      nfs.upload(filepath, {key: key, size: dataSize}, ep.done('uploadResult'));
    });
  }));

  ep.on('uploadResult', function (result) {
    // remove tmp file whatever
    fs.unlink(filepath, utility.noop);
    var mod = {
      version: sourcePackage.version,
      name: sourcePackage.name,
      package: sourcePackage,
      author: username,
    };
    var dist = {
      tarball: result.url,
      shasum: shasum,
      size: dataSize
    };
    mod.package.dist = dist;

    that.log('    [%s:%s] done, version: %s, size: %d',
      sourcePackage.name, versionIndex, mod.version, dataSize);
    Module.add(mod, ep.done(function (result) {
      callback(null, result);
    }));
  });
};
<commit_msg>add time for debug str<commit_after>/**!
 * cnpmjs.org - controllers/registry/sync_module_worker.js
 *
 * Copyright(c) cnpmjs.org and other contributors.
 * MIT Licensed
 *
 * Authors:
 *  fengmk2 <fengmk2@gmail.com> (http://fengmk2.github.com)
 */

'use strict';

/**
 * Module dependencies.
 */

var debug = require('debug')('cnpm:controllers:registry:sync_module_worker');
var EventEmitter = require('events').EventEmitter;
var util = require('util');
var fs = require('fs');
var path = require('path');
var crypto = require('crypto');
var eventproxy = require('eventproxy');
var urllib = require('urllib');
var utility = require('utility');
var nfs = require('../../common/nfs');
var npm = require('../../proxy/npm');
var common = require('./common');
var Module = require('../../proxy/module');
var Log = require('../../proxy/module_log');

function SyncModuleWorker(options) {
  EventEmitter.call(this);
  this._logId = options.logId;
  this.names = [options.name];
  this.username = options.username;
  this.nameMap = {};
  this.successes = [];
  this.fails = [];
}

util.inherits(SyncModuleWorker, EventEmitter);

module.exports = SyncModuleWorker;

SyncModuleWorker.prototype.finish = function () {
  this.log('Finished, %d success, %d fail\nSuccess: [ %s ]\nFail: [ %s ]',
    this.successes.length, this.fails.length,
    this.successes.join(', '), this.fails.join(', '));
  this.emit('end');
};

SyncModuleWorker.prototype.log = function (format, arg1, arg2) {
  var str = '[' + utility.YYYYMMDDHHmmss() + '] ' + util.format.apply(util, arguments);
  debug(str);
  Log.append(this._logId, str, utility.noop);
};

SyncModuleWorker.prototype.start = function () {
  this.log('user: %s, sync %s worker start.', this.username, this.names[0]);
  this.next();
};

SyncModuleWorker.prototype.add = function (name) {
  if (this.nameMap[name]) {
    return;
  }
  this.nameMap[name] = true;
  this.names.push(name);
  this.log('    add dependencies: %s', name);
};

SyncModuleWorker.prototype.next = function () {
  var name = this.names.shift();
  if (!name) {
    return this.finish();
  }

  var that = this;
  npm.get(name, function (err, pkg, response) {
    if (!err && !pkg) {
      err = new Error('Module ' + name + ' not exists, http status ' + response.statusCode);
      err.name = 'NpmModuleNotExsitsError';
    }
    if (err) {
      that.fails.push(name);
      that.log('[error] [%s] get package error: %s', name, err.stack);
      return that.next();
    }

    that.log('Start syncing %s', pkg.name);
    that._sync(pkg, function (err, versions) {
      if (err) {
        that.fails.push(pkg.name);
        that.log('[error] [%s] sync error: %s', name, err.stack);
        return that.next();
      }
      that.log('[%s] synced success, %d versions: %s',
        name, versions.length, versions.join(', '));
      that.successes.push(name);
      that.next();
    });
  });
};

SyncModuleWorker.prototype._sync = function (pkg, callback) {
  var username = this.name;
  var name = pkg.name;
  var that = this;
  var ep = eventproxy.create();
  ep.fail(callback);

  Module.listByName(name, ep.done(function (rows) {
    var map = {};
    for (var i = 0; i < rows.length; i++) {
      var r = rows[i];
      if (r.version === 'next') {
        continue;
      }
      map[r.version] = r;
    }
    ep.emit('existsMap', map);
  }));

  var missingVersions = [];
  ep.on('existsMap', function (map) {
    var times = pkg.time || {};
    var versions = [];
    for (var v in times) {
      var exists = map[v];
      var version = pkg.versions[v];
      if (!version || !version.dist) {
        continue;
      }
      if (exists && exists.package.dist.shasum === version.dist.shasum) {
        continue;
      }
      version.gmt_modified = Date.parse(times[v]);
      versions.push(version);
    }

    if (versions.length === 0) {
      that.log('  [%s] all versions are exists', pkg.name);
      return ep.emit('done');
    }

    versions.sort(function (a, b) {
      return a.gmt_modified - b.gmt_modified;
    });
    missingVersions = versions;
    that.log('  [%s] %d versions', pkg.name, versions.length);
    ep.emit('syncModule', missingVersions.shift());
  });

  var versionNames = [];
  ep.on('syncModule', function (syncModule) {
    versionNames.push(syncModule.version);
    that._syncOneVersion(versionNames.length, syncModule, ep.done(function (result) {
      var nextVersion = missingVersions.shift();
      if (!nextVersion) {
        return ep.emit('done', result);
      }
      ep.emit('syncModule', nextVersion);
    }));
  });

  ep.on('done', function () {
    // TODO: set latest version
    callback(null, versionNames);
  });
};

SyncModuleWorker.prototype._syncOneVersion = function (versionIndex, sourcePackage, callback) {
  var that = this;
  var username = this.username;
  var downurl = sourcePackage.dist.tarball;
  var filename = path.basename(downurl);
  var filepath = common.getTarballFilepath(filename);
  var ws = fs.createWriteStream(filepath);
  var options = {
    writeStream: ws,
  };
  var ep = eventproxy.create();
  ep.fail(callback);

  that.log('    [%s:%d] syncing, version: %s, dist: %j',
    sourcePackage.name, versionIndex, sourcePackage.version, sourcePackage.dist);

  for (var k in sourcePackage.dependencies) {
    that.add(k);
  }

  for (var k in sourcePackage.devDependencies) {
    that.add(k);
  }

  var shasum = crypto.createHash('sha1');
  var dataSize = 0;
  urllib.request(downurl, options, ep.done(function (_, response) {
    var statusCode = response && response.statusCode || -1;
    if (statusCode !== 200) {
      fs.unlink(filepath, utility.noop);
      var err = new Error('Download ' + downurl + ' fail, status: ' + statusCode);
      err.name = 'DownloadTarballError';
      err.data = sourcePackage;
      return ep.emit('error', err);
    }

    var rs = fs.createReadStream(filepath);
    rs.once('error', function (err) {
      ep.emit('error', err);
    });
    rs.on('data', function (data) {
      shasum.update(data);
      dataSize += data.length;
    });
    rs.on('end', function () {
      shasum = shasum.digest('hex');
      if (shasum !== sourcePackage.dist.shasum) {
        fs.unlink(filepath, utility.noop);
        var err = new Error('Download ' + downurl + ' shasum:' + shasum + ' not match ' + sourcePackage.dist.shasum);
        err.name = 'DownloadTarballShasumError';
        err.data = sourcePackage;
        return ep.emit('error', err);
      }

      var key = common.getCDNKey(sourcePackage.name, filename);
      nfs.upload(filepath, {key: key, size: dataSize}, ep.done('uploadResult'));
    });
  }));

  ep.on('uploadResult', function (result) {
    // remove tmp file whatever
    fs.unlink(filepath, utility.noop);
    var mod = {
      version: sourcePackage.version,
      name: sourcePackage.name,
      package: sourcePackage,
      author: username,
    };
    var dist = {
      tarball: result.url,
      shasum: shasum,
      size: dataSize
    };
    mod.package.dist = dist;

    that.log('    [%s:%s] done, version: %s, size: %d',
      sourcePackage.name, versionIndex, mod.version, dataSize);
    Module.add(mod, ep.done(function (result) {
      callback(null, result);
    }));
  });
};
<|endoftext|>


<commit_before>'use strict';

// Declare app level module which depends on views, and components
var app = angular.module('poolui', [
	'pool.globals',
	'ngRoute',
	'ngMaterial',
	'md.data.table',
	'angularMoment',
	'ngStorage',
	'ngAudio',
	'utils.strings',
	'utils.services',
	'utils.xhr',
	'n3-line-chart',
	'angular-page-visibility'
	]).config(['$locationProvider', '$routeProvider', '$mdThemingProvider', function($locationProvider, $routeProvider, $mdThemingProvider) {
		$locationProvider.hashPrefix('')
		;
		$mdThemingProvider.theme('default')
		.primaryPalette('grey')
		.accentPalette('light-blue');

		$routeProvider
		.when('/home', {
			templateUrl: 'user/home/home.html',
			controller: 'HomeCtrl',
			activetab: 'home'
		})
		.when('/dashboard', {
			templateUrl: 'user/dashboard/dashboard.html',
			controller: 'DashboardCtrl',
			activetab: 'dashboard'
		})
		.when('/blocks', {
			templateUrl: 'user/blocks/blocks.html',
			controller: 'BlocksCtrl',
			activetab: 'blocks'
		})
		.when('/payments', {
			templateUrl: 'user/payments/payments.html',
			controller: 'PaymentsCtrl',
			activetab: 'payments'
		})
		.when('/network', {
			templateUrl: 'user/network/network.html',
			controller: 'NetworkCtrl',
			activetab: 'network'
		})
		.when('/ports', {
			templateUrl: 'user/ports/ports.html',
			controller: 'PortsCtrl',
			activetab: 'ports'
		})
		.when('/help/chat', {
			templateUrl: 'user/help/chat.html',
			controller: 'ChatCtrl',
			activetab: 'support'
		})
		.when('/help/getting_started', {
			templateUrl: 'user/help/getting_started.html',
			controller: 'GettingStartedCtrl',
			activetab: 'help'
		})
		.when('/help/faq', {
			templateUrl: 'user/help/faq.html',
			controller: 'FAQCtrl',
			activetab: 'help'
		});

	}]);

	app.controller('AppCtrl', function($scope, $rootScope, $location, $route, $routeParams, $anchorScroll, $window, $interval, $mdDialog, dataService, timerService, addressService, $mdSidenav, $mdMedia, $localStorage, ngAudio, GLOBALS){
		$scope.GLOBALS = GLOBALS;
		var appCache = window.applicationCache;
		$scope.$storage = $localStorage;

		$scope.poolList = ["pplns", "pps", "solo"];
	$scope.poolStats = {}; // All Pool stats
	$scope.addrStats = {}; // All tracked addresses
	$scope.lastBlock = {};

	// for miner tracking
	$scope.yourTotalHashRate = 0;

	// Hashrate Alarm
	$scope.globalSiren = false;
	$scope.sirenAudio = ngAudio.load("assets/ding.wav");

	// Update global hashrate and set off alarm if any of the tracked addresses fall below the threshold
	var updateHashRate = function (addrStats){
		var totalHashRate = 0;
		var siren = false;

		_.each(addrStats, function(addr,index) {
			totalHashRate += addr.hash;
			if (addr.alarm && addr.hash < addr.alarmLimit) {
				siren=true;
			}
		});

		$scope.globalSiren=siren;
		$scope.yourTotalHashRate = totalHashRate;
	}

	var playSiren = function (){
		($scope.globalSiren) ? $scope.sirenAudio.play() : $scope.sirenAudio.stop();
	}

	// ------- UI HELPERS

	$scope.menuOpen = $mdMedia('gt-md');
	$scope.$watch(function() { return $mdMedia('gt-md'); }, function(big) {
		$scope.menuOpen = $mdMedia('gt-md');
	});

	$scope.toggleSidenav = function (){
		if (!$mdMedia('gt-md')) {
			$mdSidenav('left').toggle();
		} else {
			// toggle boolean
			$scope.menuOpen = !$scope.menuOpen;
		}
	}

	// ------- Miner Login and auth
	$scope.minerLogin = function (ev) {
		$mdDialog.show({
			controller: "LoginCtrl",
			templateUrl: 'user/home/login.html',
			parent: angular.element(document.body),
			targetEvent: ev,
			clickOutsideToClose:true,
		  fullscreen: !$scope.menuOpen // Only for -xs, -sm breakpoints.
		})
		.then(function(answer) {
		  // success callback
		}, function(error) {
		  // error callback
		});
	}

	$scope.minerConsole = function (ev) {
		$mdDialog.show({
			locals: $scope.config,
			controller: "ConsoleCtrl",
			templateUrl: 'user/home/console.html',
			parent: angular.element(document.body),
			targetEvent: ev,
			clickOutsideToClose:true,
		  fullscreen: !$scope.menuOpen // Only for -xs, -sm breakpoints.
		})
		.then(function(answer){
			if(answer=='logout'){
				dataService.logout();
			}
		}, function(reason){
			// console.log(reason);
		});
	}

	$scope.isLoggedIn = function() {
		return dataService.isLoggedIn();
	}

	// ------- App Update
	var update = function() {
		if (appCache.status == window.applicationCache.UPDATEREADY) {
			$window.location.reload();
		}
	}

	appCache.addEventListener("updateready", function(event) {
		update();
	}, false);

	var updateCache = function () {
		var appCache = window.applicationCache;
		appCache.update();
	}

	// API Requests
	var loadData = function () {
		dataService.getData("/pool/stats", function(data){
			$scope.poolList = data.pool_list;
			$scope.poolStats.global = data.pool_statistics;
		});

		dataService.getData("/network/stats", function(data){
			$scope.network = data;
		});
	}

	var loadOnce = function () {
		dataService.getData("/config", function(data){
			$scope.config = data;
		});
	}

	// For FAQ
	$rootScope.$on('$routeChangeSuccess', function(newRoute, oldRoute) {
		$location.hash($routeParams.scrollTo);
		$anchorScroll();
	});

	// Start doing things
	loadOnce();
	loadData();
	updateCache();

	// Start the timer and register global requests
	timerService.startTimer(GLOBALS.api_refresh_interval);
	timerService.register(loadData, 'global');
	$interval(updateCache, GLOBALS.app_update_interval); // check for app updates every 5 mins

	// Start address tracking servuce after starting timer, only one callback supported at a time
	addressService.start(function(addrStats) {
		$scope.addrStats = addrStats;
		updateHashRate(addrStats);
		playSiren();
	}
	);

	// Sponsor
	$scope.sponsor_open = false

});<commit_msg>revert default route<commit_after>'use strict';

// Declare app level module which depends on views, and components
var app = angular.module('poolui', [
	'pool.globals',
	'ngRoute',
	'ngMaterial',
	'md.data.table',
	'angularMoment',
	'ngStorage',
	'ngAudio',
	'utils.strings',
	'utils.services',
	'utils.xhr',
	'n3-line-chart',
	'angular-page-visibility'
	]).config(['$locationProvider', '$routeProvider', '$mdThemingProvider', function($locationProvider, $routeProvider, $mdThemingProvider) {
		$locationProvider.hashPrefix('');
		$mdThemingProvider.theme('default')
		.primaryPalette('grey')
		.accentPalette('light-blue');

		$routeProvider
		.when('/home', {
			templateUrl: 'user/home/home.html',
			controller: 'HomeCtrl',
			activetab: 'home'
		})
		.when('/dashboard', {
			templateUrl: 'user/dashboard/dashboard.html',
			controller: 'DashboardCtrl',
			activetab: 'dashboard'
		})
		.when('/blocks', {
			templateUrl: 'user/blocks/blocks.html',
			controller: 'BlocksCtrl',
			activetab: 'blocks'
		})
		.when('/payments', {
			templateUrl: 'user/payments/payments.html',
			controller: 'PaymentsCtrl',
			activetab: 'payments'
		})
		.when('/network', {
			templateUrl: 'user/network/network.html',
			controller: 'NetworkCtrl',
			activetab: 'network'
		})
		.when('/ports', {
			templateUrl: 'user/ports/ports.html',
			controller: 'PortsCtrl',
			activetab: 'ports'
		})
		.when('/help/chat', {
			templateUrl: 'user/help/chat.html',
			controller: 'ChatCtrl',
			activetab: 'support'
		})
		.when('/help/getting_started', {
			templateUrl: 'user/help/getting_started.html',
			controller: 'GettingStartedCtrl',
			activetab: 'help'
		})
		.when('/help/faq', {
			templateUrl: 'user/help/faq.html',
			controller: 'FAQCtrl',
			activetab: 'help'
		});

		$routeProvider.otherwise({redirectTo: '/home'});

	}]);

	app.controller('AppCtrl', function($scope, $rootScope, $location, $route, $routeParams, $anchorScroll, $window, $interval, $mdDialog, dataService, timerService, addressService, $mdSidenav, $mdMedia, $localStorage, ngAudio, GLOBALS){
		$scope.GLOBALS = GLOBALS;
		var appCache = window.applicationCache;
		$scope.$storage = $localStorage;

		$scope.poolList = ["pplns", "pps", "solo"];
	$scope.poolStats = {}; // All Pool stats
	$scope.addrStats = {}; // All tracked addresses
	$scope.lastBlock = {};

	// for miner tracking
	$scope.yourTotalHashRate = 0;

	// Hashrate Alarm
	$scope.globalSiren = false;
	$scope.sirenAudio = ngAudio.load("assets/ding.wav");

	// Update global hashrate and set off alarm if any of the tracked addresses fall below the threshold
	var updateHashRate = function (addrStats){
		var totalHashRate = 0;
		var siren = false;

		_.each(addrStats, function(addr,index) {
			totalHashRate += addr.hash;
			if (addr.alarm && addr.hash < addr.alarmLimit) {
				siren=true;
			}
		});

		$scope.globalSiren=siren;
		$scope.yourTotalHashRate = totalHashRate;
	}

	var playSiren = function (){
		($scope.globalSiren) ? $scope.sirenAudio.play() : $scope.sirenAudio.stop();
	}

	// ------- UI HELPERS

	$scope.menuOpen = $mdMedia('gt-md');
	$scope.$watch(function() { return $mdMedia('gt-md'); }, function(big) {
		$scope.menuOpen = $mdMedia('gt-md');
	});

	$scope.toggleSidenav = function (){
		if (!$mdMedia('gt-md')) {
			$mdSidenav('left').toggle();
		} else {
			// toggle boolean
			$scope.menuOpen = !$scope.menuOpen;
		}
	}

	// ------- Miner Login and auth
	$scope.minerLogin = function (ev) {
		$mdDialog.show({
			controller: "LoginCtrl",
			templateUrl: 'user/home/login.html',
			parent: angular.element(document.body),
			targetEvent: ev,
			clickOutsideToClose:true,
		  fullscreen: !$scope.menuOpen // Only for -xs, -sm breakpoints.
		})
		.then(function(answer) {
		  // success callback
		}, function(error) {
		  // error callback
		});
	}

	$scope.minerConsole = function (ev) {
		$mdDialog.show({
			locals: $scope.config,
			controller: "ConsoleCtrl",
			templateUrl: 'user/home/console.html',
			parent: angular.element(document.body),
			targetEvent: ev,
			clickOutsideToClose:true,
		  fullscreen: !$scope.menuOpen // Only for -xs, -sm breakpoints.
		})
		.then(function(answer){
			if(answer=='logout'){
				dataService.logout();
			}
		}, function(reason){
			// console.log(reason);
		});
	}

	$scope.isLoggedIn = function() {
		return dataService.isLoggedIn();
	}

	// ------- App Update
	var update = function() {
		if (appCache.status == window.applicationCache.UPDATEREADY) {
			$window.location.reload();
		}
	}

	appCache.addEventListener("updateready", function(event) {
		update();
	}, false);

	var updateCache = function () {
		var appCache = window.applicationCache;
		appCache.update();
	}

	// API Requests
	var loadData = function () {
		dataService.getData("/pool/stats", function(data){
			$scope.poolList = data.pool_list;
			$scope.poolStats.global = data.pool_statistics;
		});

		dataService.getData("/network/stats", function(data){
			$scope.network = data;
		});
	}

	var loadOnce = function () {
		dataService.getData("/config", function(data){
			$scope.config = data;
		});
	}

	// For FAQ
	$rootScope.$on('$routeChangeSuccess', function(newRoute, oldRoute) {
		$location.hash($routeParams.scrollTo);
		$anchorScroll();
	});

	// Start doing things
	loadOnce();
	loadData();
	updateCache();

	// Start the timer and register global requests
	timerService.startTimer(GLOBALS.api_refresh_interval);
	timerService.register(loadData, 'global');
	$interval(updateCache, GLOBALS.app_update_interval); // check for app updates every 5 mins

	// Start address tracking servuce after starting timer, only one callback supported at a time
	addressService.start(function(addrStats) {
		$scope.addrStats = addrStats;
		updateHashRate(addrStats);
		playSiren();
	}
	);

	// Sponsor
	$scope.sponsor_open = false

});<|endoftext|>


<commit_before>var request = require("request");

function YoutubePlugin(bot) {
	var self = this;
	self.name = "youtube";
	self.help = "Youtube plugin";
	self.depend = ["auth"];

	self.vidre = new RegExp('(?:youtube(?:-nocookie)?\.com/(?:[^/]+/.+/|(?:v|e(?:mbed)?)/|.*[?&]v=)|youtu\.be/)([^"&?/ ]{11})', "i");

	self.channels = [];
	self.ignores = [];

	self.load = function(data) {
		self.channels = data.channels;
		self.ignores = data.ignores;
	};

	self.save = function() {
		return {channels: self.channels, ignores: self.ignores};
	};

	// http://www.mredkj.com/javascript/numberFormat.html#addcommas
	self.thseps = function(nStr) {
		nStr += '';
		x = nStr.split('.');
		x1 = x[0];
		x2 = x.length > 1 ? '.' + x[1] : '';
		var rgx = /(\d+)(\d{3})/;
		while (rgx.test(x1)) {
			x1 = x1.replace(rgx, '$1' + ',' + '$2');
		}
		return x1 + x2;
	};

	self.lookup = function(id, callback) {
		request("https://gdata.youtube.com/feeds/api/videos/" + id + "?v=2&alt=json", function(err, res, body) {
			if (!err && res.statusCode == 200) {
				var data = JSON.parse(body).entry;
				var str = "\x1Fhttp://youtu.be/" + id + "\x1F : \x02" + data.title["$t"] + "\x02 by " + data.author[0].name["$t"] + "; " + self.thseps(data["yt$statistics"].viewCount.toString()) + " views";
				if (data["yt$rating"] !== undefined) {
					var likes = parseFloat(data["yt$rating"].numLikes);
					var dislikes = parseFloat(data["yt$rating"].numDislikes);
					var bar = "\x033" + new Array(Math.round(likes / (likes + dislikes) * 10) + 1).join("+") + "\x034" + new Array(Math.round(dislikes / (likes + dislikes) * 10) + 1).join("-") + "\x03";
					str += "; " + bar;
				}
				(callback || function(){})(str);
			}
		});
	};

	self.events = {
		"message": function(nick, to, text, message) {
			if ((self.channels.indexOf(to) != -1) &&
				!self.ignores.some(function (elem, i, arr) {
					return bot.plugins.auth.match(message.nick + "!" + message.user + "@" + message.host, elem);
				})) {
				var match = text.match(self.vidre);
				if (match) {
					self.lookup(match[1], function(str) {
						bot.say(to, str);
					});
				}
			}
		},

		"pm": function(nick, text, message) {
			var match = text.match(self.vidre);
			if (match) {
				self.lookup(match[1], function(str) {
					bot.say(nick, str);
				});
			}
		},
	}
}

module.exports = YoutubePlugin;
<commit_msg>Log youtube lookups<commit_after>var request = require("request");

function YoutubePlugin(bot) {
	var self = this;
	self.name = "youtube";
	self.help = "Youtube plugin";
	self.depend = ["auth"];

	self.vidre = new RegExp('(?:youtube(?:-nocookie)?\.com/(?:[^/]+/.+/|(?:v|e(?:mbed)?)/|.*[?&]v=)|youtu\.be/)([^"&?/ ]{11})', "i");

	self.channels = [];
	self.ignores = [];

	self.load = function(data) {
		self.channels = data.channels;
		self.ignores = data.ignores;
	};

	self.save = function() {
		return {channels: self.channels, ignores: self.ignores};
	};

	// http://www.mredkj.com/javascript/numberFormat.html#addcommas
	self.thseps = function(nStr) {
		nStr += '';
		x = nStr.split('.');
		x1 = x[0];
		x2 = x.length > 1 ? '.' + x[1] : '';
		var rgx = /(\d+)(\d{3})/;
		while (rgx.test(x1)) {
			x1 = x1.replace(rgx, '$1' + ',' + '$2');
		}
		return x1 + x2;
	};

	self.lookup = function(id, callback) {
		request("https://gdata.youtube.com/feeds/api/videos/" + id + "?v=2&alt=json", function(err, res, body) {
			if (!err && res.statusCode == 200) {
				var data = JSON.parse(body).entry;
				var str = "\x1Fhttp://youtu.be/" + id + "\x1F : \x02" + data.title["$t"] + "\x02 by " + data.author[0].name["$t"] + "; " + self.thseps(data["yt$statistics"].viewCount.toString()) + " views";
				if (data["yt$rating"] !== undefined) {
					var likes = parseFloat(data["yt$rating"].numLikes);
					var dislikes = parseFloat(data["yt$rating"].numDislikes);
					var bar = "\x033" + new Array(Math.round(likes / (likes + dislikes) * 10) + 1).join("+") + "\x034" + new Array(Math.round(dislikes / (likes + dislikes) * 10) + 1).join("-") + "\x03";
					str += "; " + bar;
				}
				(callback || function(){})(str);
			}
		});
	};

	self.events = {
		"message": function(nick, to, text, message) {
			if ((self.channels.indexOf(to) != -1) &&
				!self.ignores.some(function (elem, i, arr) {
					return bot.plugins.auth.match(message.nick + "!" + message.user + "@" + message.host, elem);
				})) {
				var match = text.match(self.vidre);
				if (match) {
					bot.out.log("youtube", nick + " in " + to + ": " + match[0]);
					self.lookup(match[1], function(str) {
						bot.say(to, str);
					});
				}
			}
		},

		"pm": function(nick, text, message) {
			var match = text.match(self.vidre);
			if (match) {
				bot.out.log("youtube", nick + " in PM: " + match[0]);
				self.lookup(match[1], function(str) {
					bot.say(nick, str);
				});
			}
		},
	}
}

module.exports = YoutubePlugin;
<|endoftext|>


<commit_before>const async = require('async');
const db = require('../store/db');
const queries = require('../store/queries');
const utility = require('../util/utility');

const { invokeInterval, generateJob, getData } = utility;

function doProPlayers(cb) {
  const container = generateJob('api_notable', {});
  getData(container.url, (err, body) => {
    if (err) {
      return cb(err);
    }
    return async.each(body.player_infos, (p, cb) => {
      // Corrections
      if (p.account_id === 116525052 && p.locked_until < 1535785200) {
        // Duster's locked until value is incorrect
        p.locked_until = 1535785200;
      }
      if (p.account_id === 87382579 && p.team_id === 39) {
        // Misery no longer on EG
        p.team_id = 0;
      }
      if (p.account_id === 88271237 && p.locked_until < 1535785200) {
        p.locked_until = 1535785200;
      }
      if (p.account_id === 311360822 && p.team_id === 5216274) {
        // Ana is now on OG
        p.team_id = 2586976;
        p.locked_until = 1535785200;
      }
      if (p.account_id === 94054712 && p.team_id === 5154470) {
        // Topson is now on OG
        p.team_id = 2586976;
        p.locked_until = 1535785200;
      }
      if (p.account_id === 124936122) {
        // Zyd now on Team Serenity
        p.team_id = 5066616;
        p.locked_until = 1535785200;
      }
      if (p.account_id === 94155156) {
        // Fly now on EG
        p.team_id = 39;
        p.locked_until = 1535785200;
      }
      if (p.account_id === 41231571) {
        // s4 now on EG
        p.team_id = 39;
        p.locked_until = 1535785200;
      }
      if (p.account_id === 86725175) {
        // Reso now on VGJ.Storm
        p.team_id = 5228654;
        p.locked_until = 1535785200;
      }
      queries.upsert(db, 'notable_players', p, {
        account_id: p.account_id,
      }, cb);
    }, cb);
  });
}
invokeInterval(doProPlayers, 30 * 60 * 1000);
<commit_msg>Update proplayers.js<commit_after>const async = require('async');
const db = require('../store/db');
const queries = require('../store/queries');
const utility = require('../util/utility');

const { invokeInterval, generateJob, getData } = utility;

function doProPlayers(cb) {
  const container = generateJob('api_notable', {});
  getData(container.url, (err, body) => {
    if (err) {
      return cb(err);
    }
    return async.each(body.player_infos, (p, cb) => {
      // Corrections
      if (p.account_id === 116525052 && p.locked_until < 1535785200) {
        // Duster
        p.locked_until = 1535785200;
      }
      if (p.account_id === 87382579 && p.team_id === 39) {
        // Misery no longer on EG
        p.team_id = 0;
      }
      if (p.account_id === 88271237 && p.locked_until < 1535785200) {
        // Ceb
        p.locked_until = 1535785200;
      }
      queries.upsert(db, 'notable_players', p, {
        account_id: p.account_id,
      }, cb);
    }, cb);
  });
}
invokeInterval(doProPlayers, 30 * 60 * 1000);
<|endoftext|>


<commit_before>import { test, moduleForComponent } from 'ember-qunit';
import Ember from 'ember';
import { openDatepicker } from 'ember-pikaday/helpers/pikaday';

moduleForComponent('pikaday-input', 'PikadayInputComponent', {});

test('is an input tag', function(assert) {
  assert.equal('INPUT', this.$().prop('tagName'));
});

test('the input tag has the readonly attribute if it has been set on the component', function(assert) {
  this.subject({ 'readonly': true });

  assert.ok(this.$().is('[readonly]'));
});

test('clicking the input opens the pikaday dialog', function(assert) {
  this.render();

  assert.ok($('.pika-single').hasClass('is-hidden'));
  openDatepicker(this.$());
  assert.ok(!$('.pika-single').hasClass('is-hidden'));
});

test('selecting a date should update the value attribute', function(assert) {
  var interactor = openDatepicker(this.$());

  interactor.selectDate(new Date(2013, 3, 28));

  var date = this.subject().get('value');

  assert.equal(date.getFullYear(), 2013);
  assert.equal(date.getMonth(), 3);
  assert.equal(date.getDate(), 28);
});

test('setting the value attribute should select the correct date', function(assert) {
  this.subject({ value: new Date(2010, 7, 10) });

  var interactor = openDatepicker(this.$());

  assert.equal(interactor.selectedYear(), 2010);
  assert.equal(interactor.selectedMonth(), 7);
  assert.equal(interactor.selectedDay(), 10);
});

test('DD.MM.YYYY should be the default format for the input', function(assert) {
  this.subject({ value: new Date(2010, 7, 10) });

  assert.equal(this.$().val(), '10.08.2010');
});

test('format of the input is changeable', function(assert) {
  this.subject({ format: 'YYYY.DD.MM', value: new Date(2010, 7, 10) });

  assert.equal(this.$().val(), '2010.10.08');
});

test('yearRange of the input defaults to 10', function(assert) {
  var interactor = openDatepicker(this.$());
  var currentYear = new Date().getFullYear();

  assert.equal(interactor.minimumYear(), currentYear - 10);
  assert.equal(interactor.maximumYear(), currentYear + 10);
});

test('yearRange of the input can be set with a range', function(assert) {
  this.subject({ yearRange: '4' });

  var interactor = openDatepicker(this.$());
  var currentYear = new Date().getFullYear();

  assert.equal(interactor.minimumYear(), currentYear - 4);
  assert.equal(interactor.maximumYear(), currentYear + 4);
});

test('yearRange of the input can be set with comma separated years', function(assert) {
  this.subject({ yearRange: '1900,2006' });

  var interactor = openDatepicker(this.$());

  assert.equal(interactor.minimumYear(), 1900);
  assert.equal(interactor.maximumYear(), 2006);
});

test('yearRange of the input with comma separated years supports currentYear as max', function(assert) {
  this.subject({ yearRange: '1900,currentYear' });

  var interactor = openDatepicker(this.$());
  var currentYear = new Date().getFullYear();

  assert.equal(interactor.minimumYear(), 1900);
  assert.equal(interactor.maximumYear(), currentYear);
});

test('default i18n configuration of Pikaday can be changed', function(assert) {
  this.subject({
    i18n: {
      previousMonth: 'Vorheriger Monat',
      nextMonth: 'Nächster Monat',
      months: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
      weekdays: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'],
      weekdaysShort: ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa']
    },
    value: new Date(2014, 2, 10)
  });

  openDatepicker(this.$());

  assert.equal($('.pika-select-month option:selected').text(), 'März');
});

test('if utc is set the date returned from pikaday should be in UTC format', function(assert) {
  this.subject({ useUTC: true });

  var interactor = openDatepicker(this.$());

  interactor.selectDate(new Date(2013, 3, 28));

  var date = this.subject().get('value');

  assert.equal(date.getUTCFullYear(), 2013);
  assert.equal(date.getUTCMonth(), 3);
  assert.equal(date.getUTCDate(), 28);
});
<commit_msg>add tests for placeholder binding<commit_after>import { test, moduleForComponent } from 'ember-qunit';
import Ember from 'ember';
import { openDatepicker } from 'ember-pikaday/helpers/pikaday';

moduleForComponent('pikaday-input', 'PikadayInputComponent', {});

test('is an input tag', function(assert) {
  assert.equal('INPUT', this.$().prop('tagName'));
});

test('the input tag has the readonly attribute if it has been set on the component', function(assert) {
  this.subject({ 'readonly': true });

  assert.ok(this.$().is('[readonly]'));
});

test('clicking the input opens the pikaday dialog', function(assert) {
  this.render();

  assert.ok($('.pika-single').hasClass('is-hidden'));
  openDatepicker(this.$());
  assert.ok(!$('.pika-single').hasClass('is-hidden'));
});

test('selecting a date should update the value attribute', function(assert) {
  var interactor = openDatepicker(this.$());

  interactor.selectDate(new Date(2013, 3, 28));

  var date = this.subject().get('value');

  assert.equal(date.getFullYear(), 2013);
  assert.equal(date.getMonth(), 3);
  assert.equal(date.getDate(), 28);
});

test('setting the value attribute should select the correct date', function(assert) {
  this.subject({ value: new Date(2010, 7, 10) });

  var interactor = openDatepicker(this.$());

  assert.equal(interactor.selectedYear(), 2010);
  assert.equal(interactor.selectedMonth(), 7);
  assert.equal(interactor.selectedDay(), 10);
});

test('DD.MM.YYYY should be the default format for the input', function(assert) {
  this.subject({ value: new Date(2010, 7, 10) });

  assert.equal(this.$().val(), '10.08.2010');
});

test('format of the input is changeable', function(assert) {
  this.subject({ format: 'YYYY.DD.MM', value: new Date(2010, 7, 10) });

  assert.equal(this.$().val(), '2010.10.08');
});

test('yearRange of the input defaults to 10', function(assert) {
  var interactor = openDatepicker(this.$());
  var currentYear = new Date().getFullYear();

  assert.equal(interactor.minimumYear(), currentYear - 10);
  assert.equal(interactor.maximumYear(), currentYear + 10);
});

test('yearRange of the input can be set with a range', function(assert) {
  this.subject({ yearRange: '4' });

  var interactor = openDatepicker(this.$());
  var currentYear = new Date().getFullYear();

  assert.equal(interactor.minimumYear(), currentYear - 4);
  assert.equal(interactor.maximumYear(), currentYear + 4);
});

test('yearRange of the input can be set with comma separated years', function(assert) {
  this.subject({ yearRange: '1900,2006' });

  var interactor = openDatepicker(this.$());

  assert.equal(interactor.minimumYear(), 1900);
  assert.equal(interactor.maximumYear(), 2006);
});

test('yearRange of the input with comma separated years supports currentYear as max', function(assert) {
  this.subject({ yearRange: '1900,currentYear' });

  var interactor = openDatepicker(this.$());
  var currentYear = new Date().getFullYear();

  assert.equal(interactor.minimumYear(), 1900);
  assert.equal(interactor.maximumYear(), currentYear);
});

test('default i18n configuration of Pikaday can be changed', function(assert) {
  this.subject({
    i18n: {
      previousMonth: 'Vorheriger Monat',
      nextMonth: 'Nächster Monat',
      months: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
      weekdays: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'],
      weekdaysShort: ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa']
    },
    value: new Date(2014, 2, 10)
  });

  openDatepicker(this.$());

  assert.equal($('.pika-select-month option:selected').text(), 'März');
});

test('if utc is set the date returned from pikaday should be in UTC format', function(assert) {
  this.subject({ useUTC: true });

  var interactor = openDatepicker(this.$());

  interactor.selectDate(new Date(2013, 3, 28));

  var date = this.subject().get('value');

  assert.equal(date.getUTCFullYear(), 2013);
  assert.equal(date.getUTCMonth(), 3);
  assert.equal(date.getUTCDate(), 28);
});

test('placeholder is linked to the HTML element', function (assert) {
  this.subject({ placeholder: 'I am the placeholder'});
  this.render();
  assert.equal(this.$().attr('placeholder'), 'I am the placeholder');
});

test('no placeholder exists is it isn\'t linked on the component', function (assert) {
  this.subject({});
  this.render();
  assert.equal(this.$().attr('placeholder'), undefined);
});
<|endoftext|>


<commit_before>const {render, rerender} = require('preact');
const isEqual = require('lodash.isequal');

const {isWhere} = require('./is-where');
const {selToWhere} = require('./sel-to-where');

const config = {
  SPY_PRIVATE_KEY: 'SPY_PRIVATE_KEY',
};

const spyWalk = (context, vdom, depth) => {
  if (!vdom) {
    return vdom;
  }
  if (typeof vdom.nodeName === 'function' && !vdom.nodeName.isSpy) {
    vdom = Object.assign({}, vdom, {
      nodeName: (depth > context.renderedDepth ? createNoopSpy : createSpy)(
        context, vdom.nodeName,
      ),
      attributes: Object.assign({}, vdom.attributes, {
        [config.SPY_PRIVATE_KEY]: {vdom, depth},
      }),
    });
  }
  else if (vdom.children) {
    vdom = Object.assign({}, vdom, {
      children: vdom.children.map(child => spyWalk(context, child, depth)),
    });
  }
  return vdom;
};

const popSpyKey = _props => {
  const {vdom: spyKey, depth: spyDepth} = _props[config.SPY_PRIVATE_KEY];
  delete _props[config.SPY_PRIVATE_KEY];
  return [spyKey, spyDepth, _props];
};

const setVDom = (context, spyKey, vdom) => {
  context.vdomMap.set(spyKey, vdom);
  return vdom;
};

const NoopSpy = ()=>{};

const _createNoopSpy = (context, Component) => {
  return function(_props) {
    const [,, props] = popSpyKey(_props);
    const vdom = {
      nodeName: NoopSpy,
      attributes: Object.assign({
        component: Component,
      }, props),
      children: props.children,
    };
    delete vdom.attributes.children;
    return vdom;
  };
};

const createFuncSpy = (context, Component) => {
  return function(_props, ...args) {
    const [spyKey, depth, props] = popSpyKey(_props);
    const output = Component.call(this, props, ...args);
    return spyWalk(context, setVDom(context, spyKey, output), depth + 1);
  };
};

const createClassSpy = (context, Component) => {
  class Spy extends Component {
    constructor(_props, ...args) {
      const [spyKey, spyDepth, props] = popSpyKey(_props);
      super(props, ...args);
      context.keyMap.set(this, spyKey);
      context.depthMap.set(this, spyDepth);
    }

    componentWillReceiveProps(_props, ...args) {
      const [spyKey, spyDepth, props] = popSpyKey(_props);
      context.keyMap.set(this, spyKey);
      context.depthMap.set(this, spyDepth);
      if (super.componentWillReceiveProps) {
        super.componentWillReceiveProps(props, ...args);
      }
    }

    render(...args) {
      const spyKey = context.keyMap.get(this);
      const spyDepth = context.depthMap.get(this);
      return spyWalk(context, setVDom(context, spyKey, super.render(...args)), spyDepth + 1);
    }
  }
  return Spy;
};

const createNoopSpy = (context, Component) => {
  if (context.componentNoopMap.get(Component)) {return context.componentNoopMap.get(Component);}

  const Spy = _createNoopSpy(context, Component);
  Spy.isSpy = true;

  context.componentNoopMap.set(Component, Spy);

  return Spy;
};

const createSpy = (context, Component) => {
  if (context.componentMap.get(Component)) {return context.componentMap.get(Component);}

  let Spy;
  if (!Component.prototype.render) {
    Spy = createFuncSpy(context, Component);
  }
  else {
    Spy = createClassSpy(context, Component);
  }

  Spy.isSpy = true;

  context.componentMap.set(Component, Spy);

  return Spy;
};

class RenderContext {
  constructor({depth}) {
    this.renderedDepth = (depth || Infinity) - 1;

    this.keyMap = new Map();
    this.depthMap = new Map();
    this.componentMap = new Map();
    this.componentNoopMap = new Map();
    this.vdomMap = new Map();
    this.fragment = document.createDocumentFragment();
  }

  render(vdom) {
    this.component = render(
      spyWalk(this, setVDom(this, 'root', vdom), 0),
      this.fragment
    );
    return this;
  }

  find(selector) {
    return new FindWrapper(this, [this.vdomMap.get('root')], selector);
  }

  filter(selector) {
    return new FindWrapper(this, [this.vdomMap.get('root')]).filter(selector);
  }

  attr(name) {
    return new FindWrapper(this, [this.vdomMap.get('root')]).attr(name);
  }

  attrs() {
    return new FindWrapper(this, [this.vdomMap.get('root')]).attrs();
  }

  at(index) {
    return new FindWrapper(this, [this.vdomMap.get('root')]).at(index);
  }

  text() {
    return new FindWrapper(this, [this.vdomMap.get('root')]).text();
  }

  contains(vdom) {
    return new FindWrapper(this, [this.vdomMap.get('root')]).contains(vdom);
  }

  simulate(event, ...args) {
    return new FindWrapper(this, [this.vdomMap.get('root')]).simulate(event, ...args);
  }

  output() {
    return new FindWrapper(this, [this.vdomMap.get('root')]).output();
  }
}

const vdomIter = function* (vdomMap, vdom) {
  if (!vdom) {
    return;
  }
  yield vdom;
  if (typeof vdom.nodeName === 'function') {
    yield* vdomIter(vdomMap, vdomMap.get(vdom));
  }
  else {
    for (const child of (vdom.children || [])) {
      yield* vdomIter(vdomMap, child);
    }
  }
};

const vdomWalk = function* (vdomMap, iter) {
  for (const vdom of iter) {
    yield* vdomIter(vdomMap, vdom);
  }
};

const vdomFilter = (pred, vdomMap, vdom) => {
  return Array.from(vdomIter(vdomMap, vdom)).filter(pred);
};

class FindWrapper {
  constructor(context, _iter, selector) {
    this.context = context;
    this.length = 0;
    let iter = _iter;
    if (selector) {
      this.selector = selector;
      iter = [].concat(...Array.from(iter, root => (
        Array.from(vdomFilter(isWhere(selToWhere(selector)), context.vdomMap, root))
      )));
    }
    iter
      .forEach((element, index) => {
        this[index] = element;
        this.length = index + 1;
      });
  }

  at(index) {
    if (index >= this.length) {
      throw new Error(`preact-render-spy: Must have enough results for .at(${index}).`);
    }

    return new FindWrapper(this.context, [this[index]]);
  }

  attr(name) {
    if (this.length > 1 || this.length === 0) {
      throw new Error(`preact-render-spy: Must have only 1 result for .attr(${name})`);
    }

    const item = this[0];
    if (
      typeof item === 'object' &&
      item.attributes &&
      item.attributes[name]
    ) {
      return item.attributes[name];
    }
  }

  /**
   * Return an object copy of the attributes from the first node that matched.
   */
  attrs() {
    if (this.length > 1 || this.length === 0) {
      throw new Error('preact-render-spy: Must have only 1 result for .attrs().');
    }

    return Object.assign({}, this[0].attributes);
  }

  /**
   * Return the text of all nested children concatenated together.
   */
  text() {
    return Array.from(vdomWalk(this.context.vdomMap, Array.from(this)))
      // Filter for strings (text nodes)
      .filter(value => typeof value === 'string')
      // Concatenate all strings together
      .join('');
  }

  contains(vdom) {
    return Array.from(vdomWalk(this.context.vdomMap, Array.from(this)))
      .filter(value => isEqual(vdom, value))
      .length > 0;
  }

  simulate(event, ...args) {
    for (let i = 0; i < this.length; i++) {
      const vdom = this[i];
      const eventlc = event.toLowerCase();
      const eventKeys = new Set([`on${eventlc}`, `on${eventlc}capture`]);

      for (const key in vdom.attributes) {
        if (eventKeys.has(key.toLowerCase())) {
          vdom.attributes[key](...args);
          break;
        }
      }
    }
    rerender();
  }

  find(selector) {
    return new FindWrapper(this.context, Array.from(this), selector);
  }

  filter(selector) {
    return new FindWrapper(
      this.context,
      Array.from(this).filter(isWhere(selToWhere(selector)))
    );
  }

  output() {
    if (this.length > 1 || this.length === 0) {
      throw new Error('preact-render-spy: Must have only 1 result for .output().');
    }

    if (typeof this[0].nodeName !== 'function') {
      throw new Error('preact-render-spy: Must have a result of a preact class or function component for .output()');
    }

    return this.context.vdomMap.get(this[0]);
  }
}

const deep = (vdom, {depth = Infinity} = {}) => new RenderContext({depth}).render(vdom);
const shallow = vdom => deep(vdom, {depth: 1});

exports = module.exports = deep;

exports.config = config;
exports.deep = deep;
exports.default = deep;
exports.render = deep;
exports.shallow = shallow;
<commit_msg>defineProperty for spy and selector on FindWrapper<commit_after>const {render, rerender} = require('preact');
const isEqual = require('lodash.isequal');

const {isWhere} = require('./is-where');
const {selToWhere} = require('./sel-to-where');

const config = {
  SPY_PRIVATE_KEY: 'SPY_PRIVATE_KEY',
};

const spyWalk = (context, vdom, depth) => {
  if (!vdom) {
    return vdom;
  }
  if (typeof vdom.nodeName === 'function' && !vdom.nodeName.isSpy) {
    vdom = Object.assign({}, vdom, {
      nodeName: (depth > context.renderedDepth ? createNoopSpy : createSpy)(
        context, vdom.nodeName,
      ),
      attributes: Object.assign({}, vdom.attributes, {
        [config.SPY_PRIVATE_KEY]: {vdom, depth},
      }),
    });
  }
  else if (vdom.children) {
    vdom = Object.assign({}, vdom, {
      children: vdom.children.map(child => spyWalk(context, child, depth)),
    });
  }
  return vdom;
};

const popSpyKey = _props => {
  const {vdom: spyKey, depth: spyDepth} = _props[config.SPY_PRIVATE_KEY];
  delete _props[config.SPY_PRIVATE_KEY];
  return [spyKey, spyDepth, _props];
};

const setVDom = (context, spyKey, vdom) => {
  context.vdomMap.set(spyKey, vdom);
  return vdom;
};

const NoopSpy = ()=>{};

const _createNoopSpy = (context, Component) => {
  return function(_props) {
    const [,, props] = popSpyKey(_props);
    const vdom = {
      nodeName: NoopSpy,
      attributes: Object.assign({
        component: Component,
      }, props),
      children: props.children,
    };
    delete vdom.attributes.children;
    return vdom;
  };
};

const createFuncSpy = (context, Component) => {
  return function(_props, ...args) {
    const [spyKey, depth, props] = popSpyKey(_props);
    const output = Component.call(this, props, ...args);
    return spyWalk(context, setVDom(context, spyKey, output), depth + 1);
  };
};

const createClassSpy = (context, Component) => {
  class Spy extends Component {
    constructor(_props, ...args) {
      const [spyKey, spyDepth, props] = popSpyKey(_props);
      super(props, ...args);
      context.keyMap.set(this, spyKey);
      context.depthMap.set(this, spyDepth);
    }

    componentWillReceiveProps(_props, ...args) {
      const [spyKey, spyDepth, props] = popSpyKey(_props);
      context.keyMap.set(this, spyKey);
      context.depthMap.set(this, spyDepth);
      if (super.componentWillReceiveProps) {
        super.componentWillReceiveProps(props, ...args);
      }
    }

    render(...args) {
      const spyKey = context.keyMap.get(this);
      const spyDepth = context.depthMap.get(this);
      return spyWalk(context, setVDom(context, spyKey, super.render(...args)), spyDepth + 1);
    }
  }
  return Spy;
};

const createNoopSpy = (context, Component) => {
  if (context.componentNoopMap.get(Component)) {return context.componentNoopMap.get(Component);}

  const Spy = _createNoopSpy(context, Component);
  Spy.isSpy = true;

  context.componentNoopMap.set(Component, Spy);

  return Spy;
};

const createSpy = (context, Component) => {
  if (context.componentMap.get(Component)) {return context.componentMap.get(Component);}

  let Spy;
  if (!Component.prototype.render) {
    Spy = createFuncSpy(context, Component);
  }
  else {
    Spy = createClassSpy(context, Component);
  }

  Spy.isSpy = true;

  context.componentMap.set(Component, Spy);

  return Spy;
};

class RenderContext {
  constructor({depth}) {
    this.renderedDepth = (depth || Infinity) - 1;

    this.keyMap = new Map();
    this.depthMap = new Map();
    this.componentMap = new Map();
    this.componentNoopMap = new Map();
    this.vdomMap = new Map();
    this.fragment = document.createDocumentFragment();
  }

  render(vdom) {
    this.component = render(
      spyWalk(this, setVDom(this, 'root', vdom), 0),
      this.fragment
    );
    return this;
  }

  find(selector) {
    return new FindWrapper(this, [this.vdomMap.get('root')], selector);
  }

  filter(selector) {
    return new FindWrapper(this, [this.vdomMap.get('root')]).filter(selector);
  }

  attr(name) {
    return new FindWrapper(this, [this.vdomMap.get('root')]).attr(name);
  }

  attrs() {
    return new FindWrapper(this, [this.vdomMap.get('root')]).attrs();
  }

  at(index) {
    return new FindWrapper(this, [this.vdomMap.get('root')]).at(index);
  }

  text() {
    return new FindWrapper(this, [this.vdomMap.get('root')]).text();
  }

  contains(vdom) {
    return new FindWrapper(this, [this.vdomMap.get('root')]).contains(vdom);
  }

  simulate(event, ...args) {
    return new FindWrapper(this, [this.vdomMap.get('root')]).simulate(event, ...args);
  }

  output() {
    return new FindWrapper(this, [this.vdomMap.get('root')]).output();
  }
}

const vdomIter = function* (vdomMap, vdom) {
  if (!vdom) {
    return;
  }
  yield vdom;
  if (typeof vdom.nodeName === 'function') {
    yield* vdomIter(vdomMap, vdomMap.get(vdom));
  }
  else {
    for (const child of (vdom.children || [])) {
      yield* vdomIter(vdomMap, child);
    }
  }
};

const vdomWalk = function* (vdomMap, iter) {
  for (const vdom of iter) {
    yield* vdomIter(vdomMap, vdom);
  }
};

const vdomFilter = (pred, vdomMap, vdom) => {
  return Array.from(vdomIter(vdomMap, vdom)).filter(pred);
};

class FindWrapper {
  constructor(context, _iter, selector) {
    // Set a non-enumerable property for context. In case a user does an deep
    // equal comparison this removes the chance for recursive comparisons.
    Object.defineProperty(this, 'context', {enumerable: false, value: context});
    this.length = 0;
    let iter = _iter;
    if (selector) {
      Object.defineProperty(this, 'selector', {enumerable: false, value: selector});
      iter = [].concat(...Array.from(iter, root => (
        Array.from(vdomFilter(isWhere(selToWhere(selector)), context.vdomMap, root))
      )));
    }
    iter
      .forEach((element, index) => {
        this[index] = element;
        this.length = index + 1;
      });
  }

  at(index) {
    if (index >= this.length) {
      throw new Error(`preact-render-spy: Must have enough results for .at(${index}).`);
    }

    return new FindWrapper(this.context, [this[index]]);
  }

  attr(name) {
    if (this.length > 1 || this.length === 0) {
      throw new Error(`preact-render-spy: Must have only 1 result for .attr(${name})`);
    }

    const item = this[0];
    if (
      typeof item === 'object' &&
      item.attributes &&
      item.attributes[name]
    ) {
      return item.attributes[name];
    }
  }

  /**
   * Return an object copy of the attributes from the first node that matched.
   */
  attrs() {
    if (this.length > 1 || this.length === 0) {
      throw new Error('preact-render-spy: Must have only 1 result for .attrs().');
    }

    return Object.assign({}, this[0].attributes);
  }

  /**
   * Return the text of all nested children concatenated together.
   */
  text() {
    return Array.from(vdomWalk(this.context.vdomMap, Array.from(this)))
      // Filter for strings (text nodes)
      .filter(value => typeof value === 'string')
      // Concatenate all strings together
      .join('');
  }

  contains(vdom) {
    return Array.from(vdomWalk(this.context.vdomMap, Array.from(this)))
      .filter(value => isEqual(vdom, value))
      .length > 0;
  }

  simulate(event, ...args) {
    for (let i = 0; i < this.length; i++) {
      const vdom = this[i];
      const eventlc = event.toLowerCase();
      const eventKeys = new Set([`on${eventlc}`, `on${eventlc}capture`]);

      for (const key in vdom.attributes) {
        if (eventKeys.has(key.toLowerCase())) {
          vdom.attributes[key](...args);
          break;
        }
      }
    }
    rerender();
  }

  find(selector) {
    return new FindWrapper(this.context, Array.from(this), selector);
  }

  filter(selector) {
    return new FindWrapper(
      this.context,
      Array.from(this).filter(isWhere(selToWhere(selector)))
    );
  }

  output() {
    if (this.length > 1 || this.length === 0) {
      throw new Error('preact-render-spy: Must have only 1 result for .output().');
    }

    if (typeof this[0].nodeName !== 'function') {
      throw new Error('preact-render-spy: Must have a result of a preact class or function component for .output()');
    }

    return this.context.vdomMap.get(this[0]);
  }
}

const deep = (vdom, {depth = Infinity} = {}) => new RenderContext({depth}).render(vdom);
const shallow = vdom => deep(vdom, {depth: 1});

exports = module.exports = deep;

exports.config = config;
exports.deep = deep;
exports.default = deep;
exports.render = deep;
exports.shallow = shallow;
<|endoftext|>


<commit_before>/**
 * Serializer.js
 *
 * Copyright 2009, Moxiecode Systems AB
 * Released under LGPL License.
 *
 * License: http://tinymce.moxiecode.com/license
 * Contributing: http://tinymce.moxiecode.com/contributing
 */

(function(tinymce) {
	tinymce.dom.Serializer = function(settings, dom, schema) {
		var onPreProcess, onPostProcess, isIE = tinymce.isIE, each = tinymce.each, htmlParser;

		// Default DOM and Schema if they are undefined
		dom = dom || tinymce.DOM;
		schema = schema || new tinymce.html.Schema(settings);
		settings.entity_encoding = settings.entity_encoding || 'named';

		onPreProcess = new tinymce.util.Dispatcher(self);
		onPostProcess = new tinymce.util.Dispatcher(self);
		htmlParser = new tinymce.html.DomParser(settings, schema);

		// Convert move data-mce_src, data-mce_href and data-mce_style into nodes or process them if needed
		htmlParser.addAttributeFilter('src,href,style', function(nodes, name) {
			var i = nodes.length, node, value, internalName = 'data-mce_' + name, urlConverter = settings.url_converter, urlConverterScope = settings.url_converter_scope, undef;

			while (i--) {
				node = nodes[i];

				value = node.attributes.map[internalName];
				if (value !== undef) {
					// Set external name to internal value and remove internal
					node.attr(name, value.length > 0 ? value : null);
					node.attr(internalName, null);
				} else {
					// No internal attribute found then convert the value we have in the DOM
					value = node.attributes.map[name];

					if (name === "style")
						value = dom.serializeStyle(dom.parseStyle(value), node.name);
					else if (urlConverter)
						value = urlConverter.call(urlConverterScope, value, name, node.name);

					node.attr(name, value.length > 0 ? value : null);
				}
			}
		});

		// Remove internal classes mceItem<..>
		htmlParser.addAttributeFilter('class', function(nodes, name) {
			var i = nodes.length, node, value;

			while (i--) {
				node = nodes[i];
				value = node.attr('class').replace(/\s*mceItem\w+\s*/g, '');
				node.attr('class', value.length > 0 ? value : null);
			}
		});

		// Remove bookmark elements
		htmlParser.addAttributeFilter('data-mce_type', function(nodes, name) {
			var i = nodes.length, node;

			while (i--) {
				node = nodes[i];

				if (node.attributes.map['data-mce_type'] === 'bookmark')
					node.remove();
			}
		});

		// Force script into CDATA sections and remove the mce- prefix also add comments around styles
		htmlParser.addNodeFilter('script,style', function(nodes, name) {
			var i = nodes.length, node, value;

			function trim(value) {
				return value.replace(/()/g, '\n')
						.replace(/^[\r\n]*|[\r\n]*$/g, '')
						.replace(/^\s*(\/\/\s*|\]\]>|-->|\]\]-->)\s*$/g, '');
			};

			while (i--) {
				node = nodes[i];
				value = node.firstChild ? node.firstChild.value : '';

				if (name === "script") {
					// Remove mce- prefix from script elements
					node.attr('type', (node.attr('type') || 'text/javascript').replace(/^mce\-/, ''));

					if (value.length > 0)
						node.firstChild.value = '// <![CDATA[\n' + trim(value) + '\n// ]]>';
				} else {
					if (value.length > 0)
						node.firstChild.value = '';
				}
			}
		});

		// Remove
 at end of block elements
		htmlParser.addNodeFilter('br', function(nodes, name) {
			var i = nodes.length, node, blockElements = schema.getBlockElements(), emptyElements = schema.getEmptyElements(), parent;

			while (i--) {
				node = nodes[i];
				parent = node.parent;

				if (blockElements[node.parent.name] && node === parent.lastChild) {
					node.remove();

					if (parent.isEmpty(emptyElements))
						parent.empty().append(new tinymce.html.Node('#text', 3)).value = '\u00a0';
				}
			}
		});

		// Convert comments to cdata and handle protected comments
		htmlParser.addNodeFilter('#comment', function(nodes, name) {
			var i = nodes.length, node;

			while (i--) {
				node = nodes[i];

				if (node.value.indexOf('[CDATA[') === 0) {
					node.name = '#cdata';
					node.type = 4;
					node.value = node.value.replace(/^\[CDATA\[|\]\]$/g, '');
				} else if (node.value.indexOf('mce:protected ') === 0) {
					node.name = "#text";
					node.type = 3;
					node.raw = true;
					node.value = unescape(node.value).substr(14);
				}
			}
		});

		htmlParser.addNodeFilter('#pi,input', function(nodes, name) {
			var i = nodes.length, node;

			while (i--) {
				node = nodes[i];
				if (node.type === 7 && node.value.indexOf(':namespace ') === 0)
					node.remove();
				else if (node.type === 1) {
					if (name === "input" && !("type" in node.attributes.map))
						node.attr('type', 'text');
				}
			}
		});

		// Fix list elements, TODO: Replace this later
		if (settings.fix_list_elements) {
			htmlParser.addNodeFilter('ul,ol', function(nodes, name) {
				var i = nodes.length, node, parentNode;

				while (i--) {
					node = nodes[i];
					parentNode = node.parent;

					if (parentNode.name === 'ul' || parentNode.name === 'ol') {
						if (node.prev && node.prev.name === 'li') {
							node.prev.append(node);
						}
					}
				}
			});
		}

		// Return public methods
		return {
			schema : schema,

			addNodeFilter : htmlParser.addNodeFilter,

			addAttributeFilter : htmlParser.addAttributeFilter,

			onPreProcess : onPreProcess,

			onPostProcess : onPostProcess,

			serialize : function(node, args) {
				var impl, doc, oldDoc, htmlSerializer, content;

				if (isIE) {
					content = node.innerHTML;
					node = node.cloneNode(false);
					dom.setHTML(node, content);
				} else
					node = node.cloneNode(true);

				// Nodes needs to be attached to something in WebKit/Opera
				// Older builds of Opera crashes if you attach the node to an document created dynamically
				// and since we can't feature detect a crash we need to sniff the acutal build number
				// This fix will make DOM ranges and make Sizzle happy!
				impl = node.ownerDocument.implementation;
				if (impl.createHTMLDocument && (tinymce.isOpera && opera.buildNumber() >= 1767)) {
					// Create an empty HTML document
					doc = impl.createHTMLDocument("");

					// Add the element or it's children if it's a body element to the new document
					each(node.nodeName == 'BODY' ? node.childNodes : [node], function(node) {
						doc.body.appendChild(doc.importNode(node, true));
					});

					// Grab first child or body element for serialization
					if (node.nodeName != 'BODY')
						node = doc.body.firstChild;
					else
						node = doc.body;

					// set the new document in DOMUtils so createElement etc works
					oldDoc = dom.doc;
					dom.doc = doc;
				}

				args = args || {};
				args.format = args.format || 'html';

				// Pre process
				if (!args.no_events) {
					args.node = node;
					onPreProcess.dispatch(self, args);
				}

				// Setup serializer
				htmlSerializer = new tinymce.html.Serializer(settings, schema);

				// Parse and serialize HTML
				args.content = htmlSerializer.serialize(
					htmlParser.parse(args.getInner ? node.innerHTML : tinymce.trim(dom.getOuterHTML(node)), args)
				);

				// Post process
				if (!args.no_events)
					onPostProcess.dispatch(self, args);

				// Restore the old document if it was changed
				if (oldDoc)
					dom.doc = oldDoc;

				args.node = null;

				return args.content;
			},

			addRules : function(rules) {
				schema.addValidElements(rules);
			},

			/**
			 * Sets the valid elements rules of the serializer this enables you to specify things like what elements should be
			 * outputted and what attributes specific elements might have.
			 * Consult the Wiki for more details on this format.
			 *
			 * @method setRules
			 * @param {String} rules Valid elements rules string.
			 */
			setRules : function(rules) {
				schema.setValidElements(rules);
			}
		};
	};
})(tinymce);<commit_msg>Fixed so bookmark elements are retained when doing a cleanup.<commit_after>/**
 * Serializer.js
 *
 * Copyright 2009, Moxiecode Systems AB
 * Released under LGPL License.
 *
 * License: http://tinymce.moxiecode.com/license
 * Contributing: http://tinymce.moxiecode.com/contributing
 */

(function(tinymce) {
	tinymce.dom.Serializer = function(settings, dom, schema) {
		var onPreProcess, onPostProcess, isIE = tinymce.isIE, each = tinymce.each, htmlParser;

		// Default DOM and Schema if they are undefined
		dom = dom || tinymce.DOM;
		schema = schema || new tinymce.html.Schema(settings);
		settings.entity_encoding = settings.entity_encoding || 'named';

		onPreProcess = new tinymce.util.Dispatcher(self);
		onPostProcess = new tinymce.util.Dispatcher(self);
		htmlParser = new tinymce.html.DomParser(settings, schema);

		// Convert move data-mce_src, data-mce_href and data-mce_style into nodes or process them if needed
		htmlParser.addAttributeFilter('src,href,style', function(nodes, name) {
			var i = nodes.length, node, value, internalName = 'data-mce_' + name, urlConverter = settings.url_converter, urlConverterScope = settings.url_converter_scope, undef;

			while (i--) {
				node = nodes[i];

				value = node.attributes.map[internalName];
				if (value !== undef) {
					// Set external name to internal value and remove internal
					node.attr(name, value.length > 0 ? value : null);
					node.attr(internalName, null);
				} else {
					// No internal attribute found then convert the value we have in the DOM
					value = node.attributes.map[name];

					if (name === "style")
						value = dom.serializeStyle(dom.parseStyle(value), node.name);
					else if (urlConverter)
						value = urlConverter.call(urlConverterScope, value, name, node.name);

					node.attr(name, value.length > 0 ? value : null);
				}
			}
		});

		// Remove internal classes mceItem<..>
		htmlParser.addAttributeFilter('class', function(nodes, name) {
			var i = nodes.length, node, value;

			while (i--) {
				node = nodes[i];
				value = node.attr('class').replace(/\s*mceItem\w+\s*/g, '');
				node.attr('class', value.length > 0 ? value : null);
			}
		});

		// Remove bookmark elements
		htmlParser.addAttributeFilter('data-mce_type', function(nodes, name, args) {
			var i = nodes.length, node;

			while (i--) {
				node = nodes[i];

				if (node.attributes.map['data-mce_type'] === 'bookmark' && !args.cleanup)
					node.remove();
			}
		});

		// Force script into CDATA sections and remove the mce- prefix also add comments around styles
		htmlParser.addNodeFilter('script,style', function(nodes, name) {
			var i = nodes.length, node, value;

			function trim(value) {
				return value.replace(/()/g, '\n')
						.replace(/^[\r\n]*|[\r\n]*$/g, '')
						.replace(/^\s*(\/\/\s*|\]\]>|-->|\]\]-->)\s*$/g, '');
			};

			while (i--) {
				node = nodes[i];
				value = node.firstChild ? node.firstChild.value : '';

				if (name === "script") {
					// Remove mce- prefix from script elements
					node.attr('type', (node.attr('type') || 'text/javascript').replace(/^mce\-/, ''));

					if (value.length > 0)
						node.firstChild.value = '// <![CDATA[\n' + trim(value) + '\n// ]]>';
				} else {
					if (value.length > 0)
						node.firstChild.value = '';
				}
			}
		});

		// Remove
 at end of block elements
		htmlParser.addNodeFilter('br', function(nodes, name) {
			var i = nodes.length, node, blockElements = schema.getBlockElements(), emptyElements = schema.getEmptyElements(), parent;

			while (i--) {
				node = nodes[i];
				parent = node.parent;

				if (blockElements[node.parent.name] && node === parent.lastChild) {
					node.remove();

					if (parent.isEmpty(emptyElements))
						parent.empty().append(new tinymce.html.Node('#text', 3)).value = '\u00a0';
				}
			}
		});

		// Convert comments to cdata and handle protected comments
		htmlParser.addNodeFilter('#comment', function(nodes, name) {
			var i = nodes.length, node;

			while (i--) {
				node = nodes[i];

				if (node.value.indexOf('[CDATA[') === 0) {
					node.name = '#cdata';
					node.type = 4;
					node.value = node.value.replace(/^\[CDATA\[|\]\]$/g, '');
				} else if (node.value.indexOf('mce:protected ') === 0) {
					node.name = "#text";
					node.type = 3;
					node.raw = true;
					node.value = unescape(node.value).substr(14);
				}
			}
		});

		htmlParser.addNodeFilter('#pi,input', function(nodes, name) {
			var i = nodes.length, node;

			while (i--) {
				node = nodes[i];
				if (node.type === 7 && node.value.indexOf(':namespace ') === 0)
					node.remove();
				else if (node.type === 1) {
					if (name === "input" && !("type" in node.attributes.map))
						node.attr('type', 'text');
				}
			}
		});

		// Fix list elements, TODO: Replace this later
		if (settings.fix_list_elements) {
			htmlParser.addNodeFilter('ul,ol', function(nodes, name) {
				var i = nodes.length, node, parentNode;

				while (i--) {
					node = nodes[i];
					parentNode = node.parent;

					if (parentNode.name === 'ul' || parentNode.name === 'ol') {
						if (node.prev && node.prev.name === 'li') {
							node.prev.append(node);
						}
					}
				}
			});
		}

		// Return public methods
		return {
			schema : schema,

			addNodeFilter : htmlParser.addNodeFilter,

			addAttributeFilter : htmlParser.addAttributeFilter,

			onPreProcess : onPreProcess,

			onPostProcess : onPostProcess,

			serialize : function(node, args) {
				var impl, doc, oldDoc, htmlSerializer, content;

				if (isIE) {
					content = node.innerHTML;
					node = node.cloneNode(false);
					dom.setHTML(node, content);
				} else
					node = node.cloneNode(true);

				// Nodes needs to be attached to something in WebKit/Opera
				// Older builds of Opera crashes if you attach the node to an document created dynamically
				// and since we can't feature detect a crash we need to sniff the acutal build number
				// This fix will make DOM ranges and make Sizzle happy!
				impl = node.ownerDocument.implementation;
				if (impl.createHTMLDocument && (tinymce.isOpera && opera.buildNumber() >= 1767)) {
					// Create an empty HTML document
					doc = impl.createHTMLDocument("");

					// Add the element or it's children if it's a body element to the new document
					each(node.nodeName == 'BODY' ? node.childNodes : [node], function(node) {
						doc.body.appendChild(doc.importNode(node, true));
					});

					// Grab first child or body element for serialization
					if (node.nodeName != 'BODY')
						node = doc.body.firstChild;
					else
						node = doc.body;

					// set the new document in DOMUtils so createElement etc works
					oldDoc = dom.doc;
					dom.doc = doc;
				}

				args = args || {};
				args.format = args.format || 'html';

				// Pre process
				if (!args.no_events) {
					args.node = node;
					onPreProcess.dispatch(self, args);
				}

				// Setup serializer
				htmlSerializer = new tinymce.html.Serializer(settings, schema);

				// Parse and serialize HTML
				args.content = htmlSerializer.serialize(
					htmlParser.parse(args.getInner ? node.innerHTML : tinymce.trim(dom.getOuterHTML(node), args), args)
				);

				// Post process
				if (!args.no_events)
					onPostProcess.dispatch(self, args);

				// Restore the old document if it was changed
				if (oldDoc)
					dom.doc = oldDoc;

				args.node = null;

				return args.content;
			},

			addRules : function(rules) {
				schema.addValidElements(rules);
			},

			/**
			 * Sets the valid elements rules of the serializer this enables you to specify things like what elements should be
			 * outputted and what attributes specific elements might have.
			 * Consult the Wiki for more details on this format.
			 *
			 * @method setRules
			 * @param {String} rules Valid elements rules string.
			 */
			setRules : function(rules) {
				schema.setValidElements(rules);
			}
		};
	};
})(tinymce);<|endoftext|>


<commit_before>var fs = require('fs'),
    path = require('path'),
    url = require('url'),
    async = require('async');

// TODO: Switch over to a static file creator and file watching (don't forget about creation of new files -- directory watching? or async setTimeout that watches dir)

// TODO: Implement RSS

/**
 * jojo - 10 second blog engine for hackers (in javascript)
 * @param {Object<ExpressServer>} [app] App to write to
 * @param {Object} [options] Various options corresponding to jojo
 * @param {String} [options.baseRoute='/'] Route where the base file will be rendered
 * @param {String} [options.templateEngine='ejs'] Route where the base file will be rendered
 * @param {String} [options.templateDir='templates'] Directory to retrieve templates from
 * @param {String} [options.articleDir='articles'] Directory to retrieve articles from
 * @param {Function} [options.formatter='showdown'] Processor used for articles // TODO: name this proeprly
 * @returns {Object<ExpressServer>} Express server initiall passed or created with the proper routes
 */
function jojo(req, res, next) {
  // Grab the url, jojoBase
  // TODO: Figure out how to use express route testing
  var url = req.url,
      app = req.app,
      settings = app.settings,
      basepath = settings['jojo basepath'] || '/',
      cwd = process.cwd(),
      articleDir = settings['jojo articles'] || path.join(cwd, 'articles'),
      dataEngine = settings['jojo data parser'] || 'json',
      formatEngine = settings['jojo formatter'] || 'showdown',
      indexView = settings['jojo index view'],
      articleView = settings['jojo article view'],
      baseIndex;

  // If we are in the base route
  if (url === basepath) {
    // and there is an index view
    if (indexView) {
      // TODO: Index page with articles
      return res.render(indexView, 'Post summary');
    }
  } else {
    baseIndex = url.indexOf(basepath);
    if (baseIndex !== -1) {
    // TODO: Abstract this into a jojo.readArticles (and readArticlesSync?) function
    // Otherwise, attempt to find a matching article
      return readArticles(articleDir, function (err, fileNames) {
        // If there is an error, log it and run the next function
        if (err) {
          console.error(err);
          return next();
        }

        var articleUrl = url.slice(baseIndex + 1);

        // Iterate the files
        var i = 0,
            len = fileNames.length,
            fileName,
            fileParts,
            articleName,
            fileFound = false;
        for (; i < len; i++) {
          fileName = fileNames[i];

          // Remove the extension from the file
          fileParts = fileName.split('.');
          if (fileParts.length > 1) {
            fileParts.pop();
          }

          // If it matches our article url, save it
          articleName = fileParts.join('.');
          if (articleUrl.indexOf(articleName) !== -1) {
            fileFound = true;
            break;
          }
        }

        // If the file was found, interpret it
        if (fileFound) {
          fs.readFile(path.join(articleDir, fileName), 'utf8', function (err, file) {
            // If there was an error, log it and call the next method
            if (err) {
              console.error('Article could not be read: ', fileName);
              console.error(err);
              return next();
            }

            // Grab the formatter
            var formatter = require(formatEngine);

            // If the engine is showdown, get the proper formatter
            if (formatEngine === 'showdown') {
              // This is so wrong... on so many levels... =_=
              var tempThis = {};
              formatter.Showdown.converter.call(tempThis);
              formatter = tempThis.makeHtml;
            }

            // Grab the data parser
            var dataParser = dataEngine === 'json' ? JSON : require(dataEngine);
            dataParser = dataParser.parse || dataParser;

            // TODO: This is what cloudhead uses but I am not too fond of it
            // Find where the JSON ends (denoted by a double line break)
            var dblLineBreakIndex = file.search(/\n\r?\n/g);

            // Fallback the dblLineBreakIndex
            if (dblLineBreakIndex === -1) {
              dblLineBreakIndex = file.length;
            }

            // Break up the properties and content
            var propsStr = file.slice(0, dblLineBreakIndex),
                props = new Function('return ' + propsStr + ';')(),
                rawContent = file.slice(dblLineBreakIndex);

            // Render the content via the formatter
            var content = formatter(rawContent),
                renderObj = dataParser(propsStr);

            // Save the content to the renderObj
            renderObj.content = content;

            // TODO: Proper config item
            renderObj.config = app.settings;

            // If there is an articleView, render through it
            if (articleView !== undefined) {
              // TODO: Allow for alternative jojo render engine?
              res.render(articleView, renderObj);
            } else {
            // Otherwise, use res.send
              res.send(content);
            }
          });
        } else {
          // Otherwise, call the next method
          next();
        }
      });
    }
  }
  // Otherwise, call the next method
  next();
}

// Sugar method for creating a server
jojo.createServer = function () {
  // Create a server an express server
  var express = require('express'),
      server = express.createServer.apply(express, arguments);

  // Bind jojo to the server
  server.use(jojo);

  // Return the bound server
  return server;
};

// TODO: jojo.static which preloads views and does not dynamically fetch during each request

// Helper method for reading articles
function readArticles(articleDir, callback) {
  fs.readdir(articleDir, function (err, articles) {
    // If there is an error, log and callback with it
    if (err) {
      console.error('Article directory could not be read: ', articleDir);
      return callback(err);
    }

    // Otherwise, read in all of the articles
    async.map(articles, function (article, callback) {
      var articlePath = path.join(articleDir, article);
      readArticle(articlePath, 'utf8', callback);
    }, callback);
  });
}
jojo.readArticles = readArticles;

// Reader for a single article
function readArticle(articlePath, callback) {
  fs.readFile(articlePath, 'utf8', function (err, article) {
    // If there was an error, log it
    if (err) {
      console.error('An article could not be read: ', articlePath);
      return callback(err);
    }

    // Otherwise, parse the article
    var parsedArticle = parseArticle(article);

    // Callback with the article
    callback(null, parsedArticle);
  });
}
jojo.readArticle = readArticle;

// Parse a single article
function parseArticle(article) {
  // TODO: Need settings
  return article;
}
jojo.parseArticle = parseArticle;

// Export jojo
module.exports = jojo;<commit_msg>Rearranged for OO stateful-ness<commit_after>var fs = require('fs'),
    path = require('path'),
    url = require('url'),
    async = require('async');

// TODO: Switch over to a static file creator and file watching (don't forget about creation of new files -- directory watching? or async setTimeout that watches dir)

// TODO: Implement RSS

/**
 * jojo - 10 second blog engine for hackers (in javascript)
 * @param {Object<ExpressServer>} [app] App to write to
 * @param {Object} [options] Various options corresponding to jojo
 * @param {String} [options.baseRoute='/'] Route where the base file will be rendered
 * @param {String} [options.templateEngine='ejs'] Route where the base file will be rendered
 * @param {String} [options.templateDir='templates'] Directory to retrieve templates from
 * @param {String} [options.articleDir='articles'] Directory to retrieve articles from
 * @param {Function} [options.formatter='showdown'] Processor used for articles // TODO: name this proeprly
 * @returns {Object<ExpressServer>} Express server initiall passed or created with the proper routes
 */
function jojo(req, res, next) {
  // Grab the url, jojoBase
  // TODO: Figure out how to use express route testing
  var url = req.url,
      app = req.app,
      settings = app.settings,
      basepath = settings['jojo basepath'] || '/',
      cwd = process.cwd(),
      articleDir = settings['jojo articles'] || path.join(cwd, 'articles'),
      yoyo = new Yoyo(app),
      indexView = settings['jojo index view'],
      articleView = settings['jojo article view'],
      baseIndex;

  // If we are in the base route
  if (url === basepath) {
    // and there is an index view
    if (indexView) {
      // TODO: Index page with articles
      return res.render(indexView, 'Post summary');
    }
  } else {
    baseIndex = url.indexOf(basepath);
    if (baseIndex !== -1) {
    // TODO: Abstract this into a jojo.readArticles (and readArticlesSync?) function
    // Otherwise, attempt to find a matching article
      return yoyo.readArticles(articleDir, function (err, fileNames) {
        // If there is an error, log it and run the next function
        if (err) {
          console.error(err);
          return next();
        }

        var articleUrl = url.slice(baseIndex + 1);

        // Iterate the files
        var i = 0,
            len = fileNames.length,
            fileName,
            fileParts,
            articleName,
            fileFound = false;
        for (; i < len; i++) {
          fileName = fileNames[i];

          // Remove the extension from the file
          fileParts = fileName.split('.');
          if (fileParts.length > 1) {
            fileParts.pop();
          }

          // If it matches our article url, save it
          articleName = fileParts.join('.');
          if (articleUrl.indexOf(articleName) !== -1) {
            fileFound = true;
            break;
          }
        }

        // If the file was found, interpret it
        if (fileFound) {
          fs.readFile(path.join(articleDir, fileName), 'utf8', function (err, file) {
            // If there was an error, log it and call the next method
            if (err) {
              console.error('Article could not be read: ', fileName);
              console.error(err);
              return next();
            }

            // Grab the formatter
            var formatter = require(formatEngine);

            // If the engine is showdown, get the proper formatter
            if (formatEngine === 'showdown') {
              // This is so wrong... on so many levels... =_=
              var tempThis = {};
              formatter.Showdown.converter.call(tempThis);
              formatter = tempThis.makeHtml;
            }

            // Grab the data parser
            var dataParser = dataEngine === 'json' ? JSON : require(dataEngine);
            dataParser = dataParser.parse || dataParser;

            // TODO: This is what cloudhead uses but I am not too fond of it
            // Find where the JSON ends (denoted by a double line break)
            var dblLineBreakIndex = file.search(/\n\r?\n/g);

            // Fallback the dblLineBreakIndex
            if (dblLineBreakIndex === -1) {
              dblLineBreakIndex = file.length;
            }

            // Break up the properties and content
            var propsStr = file.slice(0, dblLineBreakIndex),
                props = new Function('return ' + propsStr + ';')(),
                rawContent = file.slice(dblLineBreakIndex);

            // Render the content via the formatter
            var content = formatter(rawContent),
                renderObj = dataParser(propsStr);

            // Save the content to the renderObj
            renderObj.content = content;

            // TODO: Proper config item
            renderObj.config = app.settings;

            // If there is an articleView, render through it
            if (articleView !== undefined) {
              // TODO: Allow for alternative jojo render engine?
              res.render(articleView, renderObj);
            } else {
            // Otherwise, use res.send
              res.send(content);
            }
          });
        } else {
          // Otherwise, call the next method
          next();
        }
      });
    }
  }
  // Otherwise, call the next method
  next();
}

// Sugar method for creating a server
jojo.createServer = function () {
  // Create a server an express server
  var express = require('express'),
      server = express.createServer.apply(express, arguments);

  // Bind jojo to the server
  server.use(jojo);

  // Return the bound server
  return server;
};

// TODO: jojo.static which preloads views and does not dynamically fetch during each request

// State (and sanity) preserver for jojo
function Yoyo(app) {
  // Fallback app
  app = app || {'settings': {}};

  // Save app to this
  this.app = app;
}
Yoyo.prototype = {
  'readArticles': function (articleDir, callback) {
    var that = this;
    fs.readdir(articleDir, function (err, articles) {
      // If there is an error, log and callback with it
      if (err) {
        console.error('Article directory could not be read: ', articleDir);
        return callback(err);
      }

      // Otherwise, read in all of the articles
      async.map(articles, function (article, callback) {
        var articlePath = path.join(articleDir, article);
        that.readArticle(articlePath, 'utf8', callback);
      }, callback);
    });
  },
  'readArticle': function (articlePath, callback) {
    var that = this;
    fs.readFile(articlePath, 'utf8', function (err, article) {
      // If there was an error, log it
      if (err) {
        console.error('An article could not be read: ', articlePath);
        return callback(err);
      }

      // Otherwise, parse the article
      var parsedArticle = that.parseArticle(article);

      // Callback with the article
      callback(null, parsedArticle);
    });
  },
  'parseArticle': function (article) {
    var app = this.app,
        settings = app.settings,
        dataEngine = settings['jojo data parser'] || 'json',
        formatEngine = settings['jojo formatter'] || 'showdown';

    // It feels so good to have preserved state of 'app' (phew)
    return article;
  }
};

// Expose Yoyo via jojo
jojo.Yoyo = Yoyo;

// Create helper methods for reading and parsing articles
var emptyYoyo = new Yoyo();
jojo.readArticles = function (articleDir, callback) {
  emptyYoyo.readArticles(articleDir, callback);
};
jojo.readArticle = function (articlePath, callback) {
  emptyYoyo.readArticle(articlePath, callback);
};
jojo.parseArticle = function (article) {
  emptyYoyo.parseArticle(article);
};

// Export jojo
module.exports = jojo;<|endoftext|>