<commit_before>/**
 * Loading
 *
 * @type {{config, count, getSpinner, start, stop, startInline}}
 */
var pmUtilLoading = function () {
    "use strict";

    return {
        config: {
            spinner: {
                id: "pm-util-loading-modal",
                color: "#fff",
                icon: "fa fa-spinner fa-spin fa-4x",
                style: "display:block;overflow: visible!important;"
            },
            showModal: true,
            inline: {
                color: "#3bc9d7",
                init: false
            }
        },
        count: 0,
        /**
         * Get Spinner
         * @returns {*|Mixed|jQuery|HTMLElement}
         */
        getSpinner: function () {
            return $('#' + this.config.spinner.id);
        },
        /**
         * Start
         */
        start: function () {
            var _self = this;

            if (0 === _self.count) {
                var body = $('body');

                var spinner = $('

');
                var icon = $('').attr('class', _self.config.spinner.icon).css('color', _self.config.spinner.color);

                spinner.css("top", ($(window).height() / 2) + "px");

                spinner.append(icon);

                body.append(spinner);

                if (true === _self.config.showModal) {
                    body.append($('

'));
                }
            }

            _self.count++;
        },
        /**
         * Stop
         */
        stop: function () {
            var _self = this;

            if (0 < _self.count) {
                _self.count--;
            }

            if (0 === _self.count) {
                if (true === _self.config.showModal) {
                    $("#" + _self.config.spinner.id + "-modal").remove();
                }

                _self.getSpinner().remove();
            }
        },
        /**
         * Start inline
         * @param target
         * @param size
         */
        startInline: function (target, size) {
            if (false === this.config.inline.init) {
                this.config.inline.init = true;

                $('head').append($('<link rel="stylesheet" type="text/css" />').attr('href', '/bundles/pmtool/module/pm-util/css/loading.css?v=1.0.1'));
            }

            var divClass = 'pm-util-loading-inline';
            var text = 'Lädt...';
            var style = 'width:80px;margin: 60px auto;';

            if ('small' === size) {
                text = '';
                divClass += ' small';
                style = 'width:40px;margin: 5px auto;';
            }

            target.html('
<svg class="circular" viewBox="25 25 50 50"><circle class="path" cx="50" cy="50" r="20" fill="none" stroke="' + this.config.inline.color + '" stroke-width="2" stroke-miterlimit="10" /></svg>' + text + '
');
        }
    }
}();

/**
 * pmUtil
 *
 * @type {{config, debug, initBootbox, init}}
 */
var pmUtil = function () {
    "use strict";

    return {
        config: {
            debugging: false
        },
        /**
         * Debug
         */
        debug: function () {
            if (true === this.config.debugging) {
                for (var i = 0; i < arguments.length; i++) {
                    console.log(arguments[i]);
                }
            }
        },
        /**
         * Init Bootbox
         */
        initBootbox: function () {
            var _self = this;

            $('a.delete').on('click', function () {
                var elem = $(this);

                var title = "Den Eintrag wirklich löschen?";
                if (elem.attr('title')) {
                    title = elem.attr('title');
                }

                bootbox.confirm(title, function (result) {
                    if (true === result) {
                        pmUtilLoading.start();
                        window.location.href = elem.attr('href');
                    }
                });

                return false;
            });
        },
        /**
         * Init
         */
        init: function () {
            this.initBootbox();
        }
    };
}();

$(document).ready(function () {
    pmUtil.init();
});<commit_msg>pmUtilLoading with bootbox<commit_after>/**
 * Loading
 *
 * @type {{config, count, getSpinner, start, stop, startInline}}
 */
var pmUtilLoading = function () {
    "use strict";

    return {
        config: {
            spinner: {
                id: "pm-util-loading-modal",
                color: "#fff",
                icon: "fa fa-spinner fa-spin fa-4x",
                style: "display:block;overflow: visible!important;"
            },
            showModal: true,
            inline: {
                color: "#3bc9d7",
                init: false
            }
        },
        count: 0,
        /**
         * Get Spinner
         * @returns {*|Mixed|jQuery|HTMLElement}
         */
        getSpinner: function () {
            return $('#' + this.config.spinner.id);
        },
        /**
         * Start
         */
        start: function () {
            var _self = this;

            if (0 === _self.count) {
                var body = $('body');

                var spinner = $('

');
                var icon = $('').attr('class', _self.config.spinner.icon).css('color', _self.config.spinner.color);

                spinner.css("top", ($(window).height() / 2) + "px");

                spinner.append(icon);

                body.append(spinner);

                if (true === _self.config.showModal) {
                    body.append($('

'));
                }
            }

            _self.count++;
        },
        /**
         * Stop
         */
        stop: function () {
            var _self = this;

            if (0 < _self.count) {
                _self.count--;
            }

            if (0 === _self.count) {
                if (true === _self.config.showModal) {
                    $("#" + _self.config.spinner.id + "-modal").remove();
                }

                _self.getSpinner().remove();
            }
        },
        /**
         * Start inline
         * @param target
         * @param size
         */
        startInline: function (target, size) {
            if (false === this.config.inline.init) {
                this.config.inline.init = true;

                $('head').append($('<link rel="stylesheet" type="text/css" />').attr('href', '/bundles/pmtool/module/pm-util/css/loading.css?v=1.0.1'));
            }

            var divClass = 'pm-util-loading-inline';
            var text = 'Lädt...';
            var style = 'width:80px;margin: 60px auto;';

            if ('small' === size) {
                text = '';
                divClass += ' small';
                style = 'width:40px;margin: 5px auto;';
            }

            target.html('
<svg class="circular" viewBox="25 25 50 50"><circle class="path" cx="50" cy="50" r="20" fill="none" stroke="' + this.config.inline.color + '" stroke-width="2" stroke-miterlimit="10" /></svg>' + text + '
');
        },
        /**
         * Start Dialog
         */
        startDialog: function(){
            var element = $('

');

            bootbox.dialog({
                message: element
            });

            this.startInline(element);
        }
    }
}();

/**
 * pmUtil
 *
 * @type {{config, debug, initBootbox, init}}
 */
var pmUtil = function () {
    "use strict";

    return {
        config: {
            debugging: false
        },
        /**
         * Debug
         */
        debug: function () {
            if (true === this.config.debugging) {
                for (var i = 0; i < arguments.length; i++) {
                    console.log(arguments[i]);
                }
            }
        },
        /**
         * Init Bootbox
         */
        initBootbox: function () {
            var _self = this;

            $('a.delete').on('click', function () {
                var elem = $(this);

                var title = "Den Eintrag wirklich löschen?";
                if (elem.attr('title')) {
                    title = elem.attr('title');
                }

                bootbox.confirm(title, function (result) {
                    if (true === result) {
                        pmUtilLoading.start();
                        window.location.href = elem.attr('href');
                    }
                });

                return false;
            });
        },
        /**
         * Init
         */
        init: function () {
            this.initBootbox();
        }
    };
}();

$(document).ready(function () {
    pmUtil.init();
});<|endoftext|>


<commit_before>App.TagValuesGrid = Ext.extend(Ext.grid.EditorGridPanel, {
	border: false
	,closable: true
	,title: 'Tag values'
	,loadMask: true
	,oid: null
	,initComponent: function(){
		this.sm = new Ext.grid.RowSelectionModel({singleSelect:true});
		this.store = gridstore = new Ext.data.GroupingStore({
			proxy: new Ext.data.DirectProxy({
				directFn: direct.TagValuesFetch
				,paramsAsHash: false
				,paramOrder: 'oid'
			})
			,autoDestroy: true
			,groupField: 'ns'
			,sortInfo: {field: 'tag', direction: 'ASC'}
			,remoteSort: false
			,reader: new Ext.data.JsonReader({
				root: 'tags'
				,idProperty: 'tag'
				,fields: ['ns', 'tag', 'value', 'readonly', 'type']
			})
		});
		this.view = new Ext.grid.GroupingView({
			forceFit: true
			,groupTextTpl: '{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})'
			,emptyText: 'Nothing to display'
		});
		this.tbar = [
			{text: 'Refresh', iconCls: 'icon-refresh', handler: this.onRefresh, scope: this}
			,{text: 'Load all tag values', iconCls: 'icon-fetch-all', handler: this.onLoadAllTags, scope: this}
			,{text: 'Add a tag', iconCls: 'icon-tag-add', handler: this.onAddTag, scope: this}
			,'-'
			,{text: 'View visual representation', iconCls: 'icon-eye', handler: function(){ window.open("http://abouttag.appspot.com/id/butterfly/"+this.oid);}, scope: this}
		];
		this.action = new Ext.ux.grid.RowActions({
			actions: [
				{iconCls: 'icon-refresh', tooltip: 'Load tag value'}
				,{iconCls: 'icon-tag-remove', tooltip: 'Remove tag'}
			]
			,callbacks:{
				'icon-refresh': this.onRefreshRow.createDelegate(this)
				,'icon-tag-remove': this.onDeleteTag.createDelegate(this)
			}
		});
		this.colModel = new Ext.grid.ColumnModel({
			columns: [
				this.action
				,{header:'Namespace', dataIndex: 'ns', hidden: true}
				,{id:'tag', header:'Tag', width: 230, dataIndex: 'tag', sortable: true}
				,{header: 'Value', width: 600, dataIndex: 'value', sortable: true, renderer: {fn: this.valueRenderer, scope: this}, editor: {xtype: 'textfield'}}
			]
			,isCellEditable: function(col, row) {
				var record = gridstore.getAt(row);
				if (record.get('readonly')) {
					return false;
				}
				return Ext.grid.ColumnModel.prototype.isCellEditable.call(this, col, row);
			}
		});

		this.plugins = [this.action];
		App.TagValuesGrid.superclass.initComponent.call(this);

		// TODO: See why cellclick can't be used (like App.ResultsGrid)
		this.on('cellmousedown', this.onCellClick, this);
	}
	,afterRender: function(){
		App.TagValuesGrid.superclass.afterRender.apply(this, arguments);

		if (this.oid) {
			this.store.load({params: {oid: this.oid}});
		}

		this.on('afteredit', this.onAfterEdit, this);
		this.body.on('click', this.onClick, this);
	}
	,onAfterEdit: function(e){
		var tag = e.record.data.tag;
		var value = e.value;

		direct.TagObject(this.oid, tag, value, function(){e.record.commit();});
	}
	,onAddTag: function(){
		store = this.store;
		oid = this.oid;

		Ext.Msg.prompt('Tag', 'Please enter full tag path', function(btn, tag){
			if (btn == 'ok') {
				Ext.Msg.prompt('Value', 'Value', function(btn, value){
					if (btn == 'ok') {
						direct.TagObject(oid, tag,  value, function(){store.reload();});
					}
				});
			}
		});

	}
	,onLoadAllTags: function(a){
		this.store.each(function(r){
			if (r.data.type == 'notfetch') {
				this.setTag(r);
			}
		}, this);
	}
	,onRefresh: function(){
		this.store.reload();
	}
	,onRefreshRow: function(g, r, action, row, col){
		this.setTag(r);
	}
	,onDeleteTag: function(g, r, action, row, col){
		r.set('value', 'removing tag...');
		direct.DeleteTagValue(this.oid, r.data.tag, function(a,b){if (b.status) g.store.remove(r); else r.commit();});
	}
	,setTag: function(r){
		r.set('type', 'primitive');
		r.set('value', 'loading...');
		oid = this.oid;
		direct.GetTagValue(oid, r.data.tag, function(json){
			type = json.type;

			if (type == 'primitive') {
				value = json.value;
			}
			else if (type == 'empty') {
				// TODO: Add color and/or icon
				value = 'empty';
			}
			else if (type == 'opaque') {
				value = 'Opaque value with content-type: "' + json.value + '" open';
			}
			else {
				value = 'Unsupported type: "' + type + '"';
			}
			r.set('value', value);
			r.set('type', type);
			r.set('readonly', json.readonly);
			r.commit();
		});
	}
	,valueRenderer: function(value, metaData, rec) {
		type = rec.data.type;
		if (type == 'notfetch') {
			metaData.css = 'gridclicktofetch';
			return 'Click to load tag value';
		}

		if (value.match(/^https?:\/\//)) {
			return '' + value + '';
		}

		if (!value.match(/^Opaque value with content-type/)) {
			value = value.replace(/(([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12})/g, '$1');
		}

		return value;
	}
	,onCellClick: function(grid, rowIndex, columnIndex, e) {
		if (columnIndex != 3) {
			return;
		}

		var rec = grid.getStore().getAt(rowIndex);
		if (rec.data.type == 'notfetch') {
			this.setTag(rec);
		}
	}
	,onClick: function(e, target) {
		if (target = e.getTarget('.openobject', 2)) {
			e.stopEvent();
			objid = Ext.fly(target).getAttributeNS('expl', 'objectid');
			Ext.getCmp('mainpanel').openObject(objid);
		}
	}
});

Ext.reg('app.tagvaluesgrid', App.TagValuesGrid);

<commit_msg>Tagvalues grid: get rid of 'Namespace' text and rename items to tags<commit_after>App.TagValuesGrid = Ext.extend(Ext.grid.EditorGridPanel, {
	border: false
	,closable: true
	,title: 'Tag values'
	,loadMask: true
	,oid: null
	,initComponent: function(){
		this.sm = new Ext.grid.RowSelectionModel({singleSelect:true});
		this.store = gridstore = new Ext.data.GroupingStore({
			proxy: new Ext.data.DirectProxy({
				directFn: direct.TagValuesFetch
				,paramsAsHash: false
				,paramOrder: 'oid'
			})
			,autoDestroy: true
			,groupField: 'ns'
			,sortInfo: {field: 'tag', direction: 'ASC'}
			,remoteSort: false
			,reader: new Ext.data.JsonReader({
				root: 'tags'
				,idProperty: 'tag'
				,fields: ['ns', 'tag', 'value', 'readonly', 'type']
			})
		});
		this.view = new Ext.grid.GroupingView({
			forceFit: true
			,groupTextTpl: '{group} ({[values.rs.length]} {[values.rs.length > 1 ? "tags" : "tag"]})'
			,emptyText: 'Nothing to display'
		});
		this.tbar = [
			{text: 'Refresh', iconCls: 'icon-refresh', handler: this.onRefresh, scope: this}
			,{text: 'Load all tag values', iconCls: 'icon-fetch-all', handler: this.onLoadAllTags, scope: this}
			,{text: 'Add a tag', iconCls: 'icon-tag-add', handler: this.onAddTag, scope: this}
			,'-'
			,{text: 'View visual representation', iconCls: 'icon-eye', handler: function(){ window.open("http://abouttag.appspot.com/id/butterfly/"+this.oid);}, scope: this}
		];
		this.action = new Ext.ux.grid.RowActions({
			actions: [
				{iconCls: 'icon-refresh', tooltip: 'Load tag value'}
				,{iconCls: 'icon-tag-remove', tooltip: 'Remove tag'}
			]
			,callbacks:{
				'icon-refresh': this.onRefreshRow.createDelegate(this)
				,'icon-tag-remove': this.onDeleteTag.createDelegate(this)
			}
		});
		this.colModel = new Ext.grid.ColumnModel({
			columns: [
				this.action
				,{header:'Namespace', dataIndex: 'ns', hidden: true}
				,{id:'tag', header:'Tag', width: 230, dataIndex: 'tag', sortable: true}
				,{header: 'Value', width: 600, dataIndex: 'value', sortable: true, renderer: {fn: this.valueRenderer, scope: this}, editor: {xtype: 'textfield'}}
			]
			,isCellEditable: function(col, row) {
				var record = gridstore.getAt(row);
				if (record.get('readonly')) {
					return false;
				}
				return Ext.grid.ColumnModel.prototype.isCellEditable.call(this, col, row);
			}
		});

		this.plugins = [this.action];
		App.TagValuesGrid.superclass.initComponent.call(this);

		// TODO: See why cellclick can't be used (like App.ResultsGrid)
		this.on('cellmousedown', this.onCellClick, this);
	}
	,afterRender: function(){
		App.TagValuesGrid.superclass.afterRender.apply(this, arguments);

		if (this.oid) {
			this.store.load({params: {oid: this.oid}});
		}

		this.on('afteredit', this.onAfterEdit, this);
		this.body.on('click', this.onClick, this);
	}
	,onAfterEdit: function(e){
		var tag = e.record.data.tag;
		var value = e.value;

		direct.TagObject(this.oid, tag, value, function(){e.record.commit();});
	}
	,onAddTag: function(){
		store = this.store;
		oid = this.oid;

		Ext.Msg.prompt('Tag', 'Please enter full tag path', function(btn, tag){
			if (btn == 'ok') {
				Ext.Msg.prompt('Value', 'Value', function(btn, value){
					if (btn == 'ok') {
						direct.TagObject(oid, tag,  value, function(){store.reload();});
					}
				});
			}
		});

	}
	,onLoadAllTags: function(a){
		this.store.each(function(r){
			if (r.data.type == 'notfetch') {
				this.setTag(r);
			}
		}, this);
	}
	,onRefresh: function(){
		this.store.reload();
	}
	,onRefreshRow: function(g, r, action, row, col){
		this.setTag(r);
	}
	,onDeleteTag: function(g, r, action, row, col){
		r.set('value', 'removing tag...');
		direct.DeleteTagValue(this.oid, r.data.tag, function(a,b){if (b.status) g.store.remove(r); else r.commit();});
	}
	,setTag: function(r){
		r.set('type', 'primitive');
		r.set('value', 'loading...');
		oid = this.oid;
		direct.GetTagValue(oid, r.data.tag, function(json){
			type = json.type;

			if (type == 'primitive') {
				value = json.value;
			}
			else if (type == 'empty') {
				// TODO: Add color and/or icon
				value = 'empty';
			}
			else if (type == 'opaque') {
				value = 'Opaque value with content-type: "' + json.value + '" open';
			}
			else {
				value = 'Unsupported type: "' + type + '"';
			}
			r.set('value', value);
			r.set('type', type);
			r.set('readonly', json.readonly);
			r.commit();
		});
	}
	,valueRenderer: function(value, metaData, rec) {
		type = rec.data.type;
		if (type == 'notfetch') {
			metaData.css = 'gridclicktofetch';
			return 'Click to load tag value';
		}

		if (value.match(/^https?:\/\//)) {
			return '' + value + '';
		}

		if (!value.match(/^Opaque value with content-type/)) {
			value = value.replace(/(([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12})/g, '$1');
		}

		return value;
	}
	,onCellClick: function(grid, rowIndex, columnIndex, e) {
		if (columnIndex != 3) {
			return;
		}

		var rec = grid.getStore().getAt(rowIndex);
		if (rec.data.type == 'notfetch') {
			this.setTag(rec);
		}
	}
	,onClick: function(e, target) {
		if (target = e.getTarget('.openobject', 2)) {
			e.stopEvent();
			objid = Ext.fly(target).getAttributeNS('expl', 'objectid');
			Ext.getCmp('mainpanel').openObject(objid);
		}
	}
});

Ext.reg('app.tagvaluesgrid', App.TagValuesGrid);

<|endoftext|>


<commit_before>/* eslint no-console: 0 */

import domassist from '..';

import { test } from 'tape';

const init = () => {
  const container = document.createElement('div');
  container.id = 'domassist';
  document.body.appendChild(container);
};

init();

const page = window.phantom.page;

test('find, findOne', assert => {
  const el = domassist.findOne('#domassist');

  el.innerHTML = `

Test1

Test2

p1

p2

  `;

  // Test default find
  const found1 = domassist.find('p');
  assert.ok(found1 instanceof NodeList, 'Default - Elements found');
  assert.equal(found1.length, 2, 'Default - Correct number of elements');

  // Test scoped find
  const list = domassist.findOne('ul');
  const found2 = domassist.find('li', list);
  assert.ok(found2 instanceof NodeList, 'Scoped - Elements found');
  assert.equal(found2.length, 2, 'Scoped - Correct number of elements');
  assert.end();
});

test('addClass, hasClass, removeClass', assert => {
  const el = domassist.findOne('#domassist');

  assert.ok(!domassist.hasClass(el, 'testClass'), 'Does not have test class');
  domassist.addClass(el, 'testClass');
  assert.ok(domassist.hasClass(el, 'testClass'), 'Class added');
  domassist.removeClass(el, 'testClass');
  assert.ok(!domassist.hasClass(el, 'testClass'), 'test class removed');
  assert.end();
});

test('toggleClass', assert => {
  const el = domassist.findOne('#domassist');
  el.innerHTML = '

';
  domassist.toggleClass(el.firstChild, 'new-class');
  assert.ok(/new-class/.test(el.firstChild.classList), 'Class list should contain "new-class"');
  domassist.toggleClass(el.firstChild, 'new-class');
  assert.notOk(/new-class/.test(el.firstChild.classList), 'Class list should not contain "new-class"');
  // teardown
  while (el.firstChild) {
    el.removeChild(el.firstChild);
  }
  assert.end();
});

test('show, hide', assert => {
  const el = domassist.findOne('#domassist');
  el.style.display = 'inline';

  domassist.hide(el);
  assert.equal(el.style.display, 'none', 'Element hidden');
  domassist.show(el);
  assert.equal(el.style.display, 'inline', 'Element shown');

  // reset
  el.style.display = 'block';
  assert.end();
});

test('matches', assert => {
  const el = domassist.findOne('#domassist');

  el.innerHTML = `

  `;

  const matcher = domassist.findOne('div', el);
  assert.ok(domassist.matches(matcher, '.test'), 'Matches');
  assert.end();
});

test('Events - on', assert => {
  const el = domassist.findOne('#domassist');

  el.innerHTML = `
    Click
  `;

  const link = domassist.findOne('a', el);
  const pos = link.getBoundingClientRect();

  domassist.on(link, 'click', e => {
    assert.ok(e instanceof MouseEvent, 'Event fired');
    assert.end();
  });

  page.sendEvent('click', pos.left + pos.width / 2, pos.top + pos.height / 2);
});

test('Events - off', assert => {
  const el = domassist.findOne('#domassist');

  el.innerHTML = `
    Click
  `;

  const link = domassist.findOne('a', el);
  const pos = link.getBoundingClientRect();

  let clicked = false;

  domassist.on(link, 'click', e => {
    clicked = true;
  });

  domassist.off(link, 'click');

  page.sendEvent('click', pos.left + pos.width / 2, pos.top + pos.height / 2);

  setTimeout(() => {
    assert.ok(!clicked, 'Event not fired');
    assert.end();
  }, 500);
});

test('Events - delegate', assert => {
  const el = domassist.findOne('#domassist');

  domassist.delegate(el, 'click', 'button', e => {
    assert.ok(e instanceof MouseEvent, 'Event fired');
    assert.end();
  });

  el.innerHTML = `
    <button type="button">Click</button>
  `;

  const button = domassist.findOne('button', el);
  const pos = button.getBoundingClientRect();

  page.sendEvent('click', pos.left + pos.width / 2, pos.top + pos.height / 2);
});

test('Events - once', assert => {
  const el = domassist.findOne('#domassist');

  el.innerHTML = `
    Click
  `;

  const link = domassist.findOne('a', el);
  const pos = link.getBoundingClientRect();

  let clicks = 0;

  domassist.once(link, 'click', e => {
    clicks++;
  });

  page.sendEvent('click', pos.left + pos.width / 2, pos.top + pos.height / 2);
  page.sendEvent('click', pos.left + pos.width / 2, pos.top + pos.height / 2);
  page.sendEvent('click', pos.left + pos.width / 2, pos.top + pos.height / 2);

  setTimeout(() => {
    assert.equal(clicks, 1, 'Only fired once');
    assert.end();
  }, 500);
});

test('Events - hover', assert => {
  const el = domassist.findOne('#domassist');

  el.innerHTML = `

  `;

  const box = domassist.findOne('div', el);
  const pos = box.getBoundingClientRect();

  domassist.hover(box, e => {
    assert.ok(e instanceof MouseEvent, 'Enter fired');
    assert.equal(e.type, 'mouseenter', 'Correct event');
  }, e => {
    assert.ok(e instanceof MouseEvent, 'Leave fired');
    assert.equal(e.type, 'mouseleave', 'Correct event');
    assert.end();
  });

  page.sendEvent('mousemove', pos.left + pos.width / 2, pos.top + pos.height / 2);
  page.sendEvent('mousemove', pos.left + pos.width + 100, pos.top + pos.height + 100);
});
<commit_msg>Update toggleClass test per code review<commit_after>/* eslint no-console: 0 */

import domassist from '..';

import { test } from 'tape';

const init = () => {
  const container = document.createElement('div');
  container.id = 'domassist';
  document.body.appendChild(container);
};

init();

const page = window.phantom.page;

test('find, findOne', assert => {
  const el = domassist.findOne('#domassist');

  el.innerHTML = `

Test1

Test2

p1

p2

  `;

  // Test default find
  const found1 = domassist.find('p');
  assert.ok(found1 instanceof NodeList, 'Default - Elements found');
  assert.equal(found1.length, 2, 'Default - Correct number of elements');

  // Test scoped find
  const list = domassist.findOne('ul');
  const found2 = domassist.find('li', list);
  assert.ok(found2 instanceof NodeList, 'Scoped - Elements found');
  assert.equal(found2.length, 2, 'Scoped - Correct number of elements');
  assert.end();
});

test('addClass, hasClass, removeClass, toggleClass', assert => {
  const el = domassist.findOne('#domassist');

  assert.ok(!domassist.hasClass(el, 'testClass'), 'Does not have test class');
  domassist.addClass(el, 'testClass');
  assert.ok(domassist.hasClass(el, 'testClass'), 'Class added');
  domassist.removeClass(el, 'testClass');
  assert.ok(!domassist.hasClass(el, 'testClass'), 'test class removed');

  el.innerHTML = '

';
  domassist.toggleClass(el.firstChild, 'new-class');
  assert.ok(domassist.hasClass(el.firstChild, 'new-class'), 'toggleClass - class list should contain "new-class"');
  domassist.toggleClass(el.firstChild, 'new-class');
  assert.notOk(domassist.hasClass(el.firstChild, 'new-class'), 'toggleClass - class list should not contain "new-class"');
  // teardown
  while (el.firstChild) {
    el.removeChild(el.firstChild);
  }
  assert.end();
});

test('show, hide', assert => {
  const el = domassist.findOne('#domassist');
  el.style.display = 'inline';

  domassist.hide(el);
  assert.equal(el.style.display, 'none', 'Element hidden');
  domassist.show(el);
  assert.equal(el.style.display, 'inline', 'Element shown');

  // reset
  el.style.display = 'block';
  assert.end();
});

test('matches', assert => {
  const el = domassist.findOne('#domassist');

  el.innerHTML = `

  `;

  const matcher = domassist.findOne('div', el);
  assert.ok(domassist.matches(matcher, '.test'), 'Matches');
  assert.end();
});

test('Events - on', assert => {
  const el = domassist.findOne('#domassist');

  el.innerHTML = `
    Click
  `;

  const link = domassist.findOne('a', el);
  const pos = link.getBoundingClientRect();

  domassist.on(link, 'click', e => {
    assert.ok(e instanceof MouseEvent, 'Event fired');
    assert.end();
  });

  page.sendEvent('click', pos.left + pos.width / 2, pos.top + pos.height / 2);
});

test('Events - off', assert => {
  const el = domassist.findOne('#domassist');

  el.innerHTML = `
    Click
  `;

  const link = domassist.findOne('a', el);
  const pos = link.getBoundingClientRect();

  let clicked = false;

  domassist.on(link, 'click', e => {
    clicked = true;
  });

  domassist.off(link, 'click');

  page.sendEvent('click', pos.left + pos.width / 2, pos.top + pos.height / 2);

  setTimeout(() => {
    assert.ok(!clicked, 'Event not fired');
    assert.end();
  }, 500);
});

test('Events - delegate', assert => {
  const el = domassist.findOne('#domassist');

  domassist.delegate(el, 'click', 'button', e => {
    assert.ok(e instanceof MouseEvent, 'Event fired');
    assert.end();
  });

  el.innerHTML = `
    <button type="button">Click</button>
  `;

  const button = domassist.findOne('button', el);
  const pos = button.getBoundingClientRect();

  page.sendEvent('click', pos.left + pos.width / 2, pos.top + pos.height / 2);
});

test('Events - once', assert => {
  const el = domassist.findOne('#domassist');

  el.innerHTML = `
    Click
  `;

  const link = domassist.findOne('a', el);
  const pos = link.getBoundingClientRect();

  let clicks = 0;

  domassist.once(link, 'click', e => {
    clicks++;
  });

  page.sendEvent('click', pos.left + pos.width / 2, pos.top + pos.height / 2);
  page.sendEvent('click', pos.left + pos.width / 2, pos.top + pos.height / 2);
  page.sendEvent('click', pos.left + pos.width / 2, pos.top + pos.height / 2);

  setTimeout(() => {
    assert.equal(clicks, 1, 'Only fired once');
    assert.end();
  }, 500);
});

test('Events - hover', assert => {
  const el = domassist.findOne('#domassist');

  el.innerHTML = `

  `;

  const box = domassist.findOne('div', el);
  const pos = box.getBoundingClientRect();

  domassist.hover(box, e => {
    assert.ok(e instanceof MouseEvent, 'Enter fired');
    assert.equal(e.type, 'mouseenter', 'Correct event');
  }, e => {
    assert.ok(e instanceof MouseEvent, 'Leave fired');
    assert.equal(e.type, 'mouseleave', 'Correct event');
    assert.end();
  });

  page.sendEvent('mousemove', pos.left + pos.width / 2, pos.top + pos.height / 2);
  page.sendEvent('mousemove', pos.left + pos.width + 100, pos.top + pos.height + 100);
});
<|endoftext|>


<commit_before>Tabula = window.Tabula || {};

Tabula.UploadedFile = Backbone.Model.extend({
  size: null,
  page_count: null,
  initialize: function(){
    this.set('size', this.get('size') || null);
    this.set('page_count', this.get('page_count') || null)
  }

});

Tabula.FilesCollection = Backbone.Collection.extend({
    model: Tabula.UploadedFile,
    url: '/pdfs/workspace.json',
});

Tabula.File = Backbone.View.extend({
  tagName: 'tr',
  events: {
    'click .delete-pdf': 'deletePDF'
  },
  template: _.template( $('#uploaded-file-template').html().replace(/nestedscript/g, 'script')),
  initialize: function(){
    _.bindAll(this, 'render', 'deletePDF');
  },
  render: function(){
    this.$el.append(this.template(this.model.attributes));
    return this;
  },
  deletePDF: function(e) {
    var btn = $(e.currentTarget);
    var tr = btn.parents('tr');

    if (!confirm('Delete file "'+btn.data('filename')+'"?')) return;
    var pdf_id = btn.data('pdfid');

    $.post('/pdf/' + pdf_id,
          { _method: 'delete' },
          function() {
            tr.fadeOut(200, function() { $(this).remove(); });
          });
    },
})

Tabula.Library = Backbone.View.extend({
    events: {
        "submit form#upload": 'uploadPDF',
    },
    template: _.template( $('#upload-template').html().replace(/nestedscript/g, 'script')),
    initialize: function(){
      _.bindAll(this, 'uploadPDF', 'render', 'renderFileLibrary');
      this.files_collection = new Tabula.FilesCollection([]);
      this.files_collection.fetch({silent: true, success: _.bind(function(){ this.render(); this.renderFileLibrary(); }, this) });
      this.listenTo(this.files_collection, 'add', this.renderFileLibrary);
    },
    uploadPDF: function(e){
      $(e.currentTarget).find('button').attr('disabled', 'disabled');

      var formdata = new FormData($('form#upload')[0]);
      $.ajax({
          url: $('form#upload').attr('action'),
          type: 'POST',
          success: _.bind(function (res) {
              var data = JSON.parse(res);
              this.checker = new Tabula.UploadStatusChecker({
                  el: this.$el.find('#progress-container'),
                  file_id: data.file_id,
                  upload_id: data.upload_id,
              });
              this.checker.checkStatus();
              this.checker.render();
          }, this),
          error: function(a,b,c){ console.log('error', a,b,c)},
          data: formdata,

          cache: false,
          contentType: false,
          processData: false
      });
      e.preventDefault();
      return false; // don't actually submit the form
    },

    renderFileLibrary: function(added_model){
      if(this.files_collection.length > 0){
        this.files_collection.each(function(uploaded_file){
          var file_element = new Tabula.File({model: uploaded_file}).render().el;
          if(added_model == uploaded_file){
            $(file_element).addClass('flash');
          }
          $('#uploaded-files-container').append(file_element);
        })
        $("#fileTable").tablesorter( { headers: { 4: { sorter: false}, 5: {sorter: false} } } );
      }else{
        $('#uploaded-files-container').html( $('
No uploaded files yet.
') );
      }
    },

    render: function(){
      $('#tabula-app').html( this.template({
        TABULA_VERSION: TABULA_VERSION,
        pct_complete: 0,
        importing: false
      }) );
      return this;
    }
})

Tabula.UploadStatusChecker = Backbone.View.extend({
    file_id: null,
    upload_id: null,
    pct_complete: 0,
    message: null,
    initialize: function(stuff){
        _.bindAll(this, 'statusComplete', 'checkStatus', 'render');
        this.file_id = stuff.file_id;
        this.upload_id = stuff.upload_id;
    },
    statusComplete: function(file_id) {
        if (!!this.spinobj) {
            this.spinobj.stop()
        };
        Tabula.library.files_collection.fetch({ success: Tabula.library.renderFileLibrary });
        $('form#upload').find('button').removeAttr('disabled');
        // window.location = '/pdf/' + file_id;
    },

    checkStatus: function() {
        $.ajax({
            dataType: 'json',
            url: '/queue/'+this.upload_id+'/json?file_id=' + this.file_id,
            success: _.bind(function(data, status, xhr) {
                console.log(data);
                this.message = data.message;
                this.pct_complete = data.pct_complete;
                this.render();
                if (data.status == "error" && data.error_type == "unknown") {
                    // window.location.reload(true);
                } else if (data.status == "error" && data.error_type == "no-text") {
                    console.log('no text');
                    window.clearTimeout(this.timer);
                    alert("Sorry, your PDF file is image-based; it does not have any embedded text. It might have been scanned... Tabula can't be able to extract any data from image-based PDFs. (Though you can try OCRing the PDF with a tool like Tesseract and then trying Tabula again.)") //TODO: something prettier.
                } else if (data.pct_complete >= 100) {
                    this.statusComplete(data.file_id);
                } else {
                    this.timer = setTimeout(this.checkStatus, 1000);
                }
            }, this),
            error: function(xhr, status, err) {
                console.log(err);
            }
        });
    },
    render: function(){
        if(this.pct_complete <= 0){
            this.$el.find('h4').text("Upload Progress");
        }else if(this.pct_complete >= 100){
            this.$el.find('h4').text("Upload Finished.");
            this.$el.find('#message').text('');
        }else{
          this.$el.find('h4').text("Importing…");
          var spinpots = {
              lines: 11,
              length: 5,
              width: 2,
              radius: 4,
              hwaccel: true,
              top: '0',
              left: 0
          };
          this.spinobj = new Spinner(spinpots).spin(this.$el.find('#spinner')[0]);
        }
        var msg;
        if (this.message) {
            msg = this.message;
        } else if (this.pct_complete === 0) {
            msg = "waiting to be processed..."
        }
        this.$el.find("#message").text(msg);
        this.$el.find(".progress-bar").css("width", this.pct_complete + "%").attr('aria-valuenow', this.pct_complete);
        this.$el.find("#percent").html(this.pct_complete + "%");
        return this;
    }
});<commit_msg>and fix sorting<commit_after>Tabula = window.Tabula || {};

Tabula.UploadedFile = Backbone.Model.extend({
  size: null,
  page_count: null,
  initialize: function(){
    this.set('size', this.get('size') || null);
    this.set('page_count', this.get('page_count') || null)
  }

});

Tabula.FilesCollection = Backbone.Collection.extend({
    model: Tabula.UploadedFile,
    url: '/pdfs/workspace.json',
    comparator: function(i){ return -i.get('time')}
});

Tabula.File = Backbone.View.extend({
  tagName: 'tr',
  events: {
    'click .delete-pdf': 'deletePDF'
  },
  template: _.template( $('#uploaded-file-template').html().replace(/nestedscript/g, 'script')),
  initialize: function(){
    _.bindAll(this, 'render', 'deletePDF');
  },
  render: function(){
    this.$el.append(this.template(this.model.attributes));
    return this;
  },
  deletePDF: function(e) {
    var btn = $(e.currentTarget);
    var tr = btn.parents('tr');

    if (!confirm('Delete file "'+btn.data('filename')+'"?')) return;
    var pdf_id = btn.data('pdfid');

    $.post('/pdf/' + pdf_id,
          { _method: 'delete' },
          function() {
            tr.fadeOut(200, function() { $(this).remove(); });
          });
    },
})

Tabula.Library = Backbone.View.extend({
    events: {
        "submit form#upload": 'uploadPDF',
    },
    template: _.template( $('#upload-template').html().replace(/nestedscript/g, 'script')),
    initialize: function(){
      _.bindAll(this, 'uploadPDF', 'render', 'renderFileLibrary');
      this.files_collection = new Tabula.FilesCollection([]);
      this.files_collection.fetch({silent: true, success: _.bind(function(){ this.render(); this.renderFileLibrary(); }, this) });
      this.listenTo(this.files_collection, 'add', this.renderFileLibrary);
    },
    uploadPDF: function(e){
      $(e.currentTarget).find('button').attr('disabled', 'disabled');

      var formdata = new FormData($('form#upload')[0]);
      $.ajax({
          url: $('form#upload').attr('action'),
          type: 'POST',
          success: _.bind(function (res) {
              var data = JSON.parse(res);
              this.checker = new Tabula.UploadStatusChecker({
                  el: this.$el.find('#progress-container'),
                  file_id: data.file_id,
                  upload_id: data.upload_id,
              });
              this.checker.checkStatus();
              this.checker.render();
          }, this),
          error: function(a,b,c){ console.log('error', a,b,c)},
          data: formdata,

          cache: false,
          contentType: false,
          processData: false
      });
      e.preventDefault();
      return false; // don't actually submit the form
    },

    renderFileLibrary: function(added_model){
      $('#uploaded-files-container').empty();
      if(this.files_collection.length > 0){
        this.files_collection.each(function(uploaded_file){
          var file_element = new Tabula.File({model: uploaded_file}).render().el;
          if(added_model == uploaded_file){
            $(file_element).addClass('flash');
          }
          $('#uploaded-files-container').append(file_element);
        })
        $("#fileTable").tablesorter( { headers: { 3: { sorter: "usLongDate" },  4: { sorter: false}, 5: {sorter: false} } } );
      }else{
        $('#uploaded-files-container').html( $('
No uploaded files yet.
') );
      }
    },

    render: function(){
      $('#tabula-app').html( this.template({
        TABULA_VERSION: TABULA_VERSION,
        pct_complete: 0,
        importing: false
      }) );
      return this;
    }
})

Tabula.UploadStatusChecker = Backbone.View.extend({
    file_id: null,
    upload_id: null,
    pct_complete: 0,
    message: null,
    initialize: function(stuff){
        _.bindAll(this, 'statusComplete', 'checkStatus', 'render');
        this.file_id = stuff.file_id;
        this.upload_id = stuff.upload_id;
    },
    statusComplete: function(file_id) {
        if (!!this.spinobj) {
            this.spinobj.stop()
        };
        Tabula.library.files_collection.fetch({ success: Tabula.library.renderFileLibrary });
        $('form#upload').find('button').removeAttr('disabled');
        // window.location = '/pdf/' + file_id;
    },

    checkStatus: function() {
        $.ajax({
            dataType: 'json',
            url: '/queue/'+this.upload_id+'/json?file_id=' + this.file_id,
            success: _.bind(function(data, status, xhr) {
                console.log(data);
                this.message = data.message;
                this.pct_complete = data.pct_complete;
                this.render();
                if (data.status == "error" && data.error_type == "unknown") {
                    // window.location.reload(true);
                } else if (data.status == "error" && data.error_type == "no-text") {
                    console.log('no text');
                    window.clearTimeout(this.timer);
                    alert("Sorry, your PDF file is image-based; it does not have any embedded text. It might have been scanned... Tabula can't be able to extract any data from image-based PDFs. (Though you can try OCRing the PDF with a tool like Tesseract and then trying Tabula again.)") //TODO: something prettier.
                } else if (data.pct_complete >= 100) {
                    this.statusComplete(data.file_id);
                } else {
                    this.timer = setTimeout(this.checkStatus, 1000);
                }
            }, this),
            error: function(xhr, status, err) {
                console.log(err);
            }
        });
    },
    render: function(){
        if(this.pct_complete <= 0){
            this.$el.find('h4').text("Upload Progress");
        }else if(this.pct_complete >= 100){
            this.$el.find('h4').text("Upload Finished.");
            this.$el.find('#message').text('');
        }else{
          this.$el.find('h4').text("Importing…");
          var spinpots = {
              lines: 11,
              length: 5,
              width: 2,
              radius: 4,
              hwaccel: true,
              top: '0',
              left: 0
          };
          this.spinobj = new Spinner(spinpots).spin(this.$el.find('#spinner')[0]);
        }
        var msg;
        if (this.message) {
            msg = this.message;
        } else if (this.pct_complete === 0) {
            msg = "waiting to be processed..."
        }
        this.$el.find("#message").text(msg);
        this.$el.find(".progress-bar").css("width", this.pct_complete + "%").attr('aria-valuenow', this.pct_complete);
        this.$el.find("#percent").html(this.pct_complete + "%");
        return this;
    }
});<|endoftext|>


<commit_before>import test from "ava"
import puppeteer from "puppeteer"
import { Nuxt, Builder } from "nuxt"
import { resolve } from "path"

let browser = null
let nuxt = null

test.before("Launch browser", async () => {
  browser = await puppeteer.launch()
})

test.before("Init Nuxt.js", async () => {
  const rootDir = resolve(__dirname, "../..")
  let config = {}
  try {
    config = require(resolve(rootDir, "nuxt.config.js"))
  } catch (e) {
    /* do nothing. */
  }
  config.rootDir = rootDir
  config.dev = false
  config.mode = "universal"
  nuxt = new Nuxt(config)
  await new Builder(nuxt).build()
  nuxt.listen(4000, "localhost")
})

const routeTests = [
  ["/", "不思議のダンジョン 値段識別表"],
  [
    "/shiren/",
    "不思議のダンジョン2 風来のシレン - 不思議のダンジョン 値段識別表"
  ],
  [
    "/shiren2/",
    "風来のシレン2 ～鬼襲来!シレン城!～ - 不思議のダンジョン 値段識別表"
  ],
  [
    "/shiren3/",
    "風来のシレン3 からくり屋敷の眠り姫 - 不思議のダンジョン 値段識別表"
  ],
  [
    "/shiren4/",
    "風来のシレン4 神の眼と悪魔のヘソ - 不思議のダンジョン 値段識別表"
  ],
  [
    "/shiren5",
    "風来のシレン5 フォーチュンタワーと運命のダイス - 不思議のダンジョン 値段識別表"
  ]
]
routeTests.forEach(([path, title]) => {
  test(`Route ${path} exits and render HTML`, async t => {
    const page = await browser.newPage()
    await page.goto("http://localhost:4000" + path)
    const pageTitle = await page.title()
    t.is(pageTitle, title)
  })
})

test("Route /shiren/ exits and render tab header", async t => {
  const page = await browser.newPage()
  await page.goto("http://localhost:4000/shiren5/")
  const tabs = await page.$$eval(".v-tabs__item", ts =>
    ts.map(tab => tab.textContent.trim())
  )

  t.true(tabs.includes("腕輪"))
  t.true(tabs.includes("草"))
  t.true(tabs.includes("巻物"))
  t.true(tabs.includes("壺"))
  t.true(tabs.includes("杖"))
})

test("Test checkbox state changing", async t => {
  const page = await browser.newPage()
  await page.goto("http://localhost:4000/shiren/", { waitUntil: "load" })

  let control = await page.$(".v-input")
  t.is(await control.$eval("input", node => node.checked), false)

  control.click()
  await page.waitFor(500)
  t.is(await control.$eval("input", node => node.checked), true)

  // test localStorage save data
  await page.reload({ waitUntil: "load" })
  control = await page.$(".v-input")
  t.is(await control.$eval("input", node => node.checked), true)

  // test reset method
  await page.click("button.v-toolbar__side-icon")
  await page.waitFor(500)
  await page.click("#tile-delete_sweep")
  await page.waitFor(500)
  await page.click("#button-delete_sweep")
  await page.waitFor(500)
  control = await page.$(".v-input")
  t.is(await control.$eval("input", node => node.checked), false)
})

test("Test router-link", async t => {
  const page = await browser.newPage()
  await page.goto("http://localhost:4000")
  t.is(page.url(), "http://localhost:4000/")

  await page.click("a.v-list__tile--link")
  await page.waitFor(500)
  t.is(page.url(), "http://localhost:4000/shiren/")

  await page.click("button.v-toolbar__side-icon")
  await page.waitFor(500)
  await page.click(".tile-home")
  await page.waitFor(500)
  t.is(page.url(), "http://localhost:4000/")
})

test.after("Close browser", async () => {
  await browser.close()
})

test.after("Closing server", () => {
  nuxt.close()
})
<commit_msg>Fix test/e2e to click '.v-input input' instead of '.v-input'<commit_after>import test from "ava"
import puppeteer from "puppeteer"
import { Nuxt, Builder } from "nuxt"
import { resolve } from "path"

let browser = null
let nuxt = null

test.before("Launch browser", async () => {
  browser = await puppeteer.launch()
})

test.before("Init Nuxt.js", async () => {
  const rootDir = resolve(__dirname, "../..")
  let config = {}
  try {
    config = require(resolve(rootDir, "nuxt.config.js"))
  } catch (e) {
    /* do nothing. */
  }
  config.rootDir = rootDir
  config.dev = false
  config.mode = "universal"
  nuxt = new Nuxt(config)
  await new Builder(nuxt).build()
  nuxt.listen(4000, "localhost")
})

const routeTests = [
  ["/", "不思議のダンジョン 値段識別表"],
  [
    "/shiren/",
    "不思議のダンジョン2 風来のシレン - 不思議のダンジョン 値段識別表"
  ],
  [
    "/shiren2/",
    "風来のシレン2 ～鬼襲来!シレン城!～ - 不思議のダンジョン 値段識別表"
  ],
  [
    "/shiren3/",
    "風来のシレン3 からくり屋敷の眠り姫 - 不思議のダンジョン 値段識別表"
  ],
  [
    "/shiren4/",
    "風来のシレン4 神の眼と悪魔のヘソ - 不思議のダンジョン 値段識別表"
  ],
  [
    "/shiren5",
    "風来のシレン5 フォーチュンタワーと運命のダイス - 不思議のダンジョン 値段識別表"
  ]
]
routeTests.forEach(([path, title]) => {
  test(`Route ${path} exits and render HTML`, async t => {
    const page = await browser.newPage()
    await page.goto("http://localhost:4000" + path)
    const pageTitle = await page.title()
    t.is(pageTitle, title)
  })
})

test("Route /shiren/ exits and render tab header", async t => {
  const page = await browser.newPage()
  await page.goto("http://localhost:4000/shiren5/")
  const tabs = await page.$$eval(".v-tabs__item", ts =>
    ts.map(tab => tab.textContent.trim())
  )

  t.true(tabs.includes("腕輪"))
  t.true(tabs.includes("草"))
  t.true(tabs.includes("巻物"))
  t.true(tabs.includes("壺"))
  t.true(tabs.includes("杖"))
})

test("Test checkbox state changing", async t => {
  const page = await browser.newPage()
  await page.goto("http://localhost:4000/shiren/", { waitUntil: "load" })

  let control = await page.$(".v-input")
  t.is(await control.$eval("input", node => node.checked), false)

  let input = await page.$(".v-input input")
  input.click()
  await page.waitFor(500)
  t.is(await control.$eval("input", node => node.checked), true)

  // test localStorage save data
  await page.reload({ waitUntil: "load" })
  control = await page.$(".v-input")
  t.is(await control.$eval("input", node => node.checked), true)

  // test reset method
  await page.click("button.v-toolbar__side-icon")
  await page.waitFor(500)
  await page.click("#tile-delete_sweep")
  await page.waitFor(500)
  await page.click("#button-delete_sweep")
  await page.waitFor(500)
  control = await page.$(".v-input")
  t.is(await control.$eval("input", node => node.checked), false)
})

test("Test router-link", async t => {
  const page = await browser.newPage()
  await page.goto("http://localhost:4000")
  t.is(page.url(), "http://localhost:4000/")

  await page.click("a.v-list__tile--link")
  await page.waitFor(500)
  t.is(page.url(), "http://localhost:4000/shiren/")

  await page.click("button.v-toolbar__side-icon")
  await page.waitFor(500)
  await page.click(".tile-home")
  await page.waitFor(500)
  t.is(page.url(), "http://localhost:4000/")
})

test.after("Close browser", async () => {
  await browser.close()
})

test.after("Closing server", () => {
  nuxt.close()
})
<|endoftext|>


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

module.exports.addTests = function({testRunner, expect, puppeteer}) {
  const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner;
  const {it, fit, xit, it_fails_ffox} = testRunner;
  const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;

  const iPhone = puppeteer.devices['iPhone 6'];
  const iPhoneLandscape = puppeteer.devices['iPhone 6 landscape'];

  describe('Page.viewport', function() {
    it('should get the proper viewport size', async({page, server}) => {
      expect(page.viewport()).toEqual({width: 800, height: 600});
      await page.setViewport({width: 123, height: 456});
      expect(page.viewport()).toEqual({width: 123, height: 456});
    });
    it('should support mobile emulation', async({page, server}) => {
      await page.goto(server.PREFIX + '/mobile.html');
      expect(await page.evaluate(() => window.innerWidth)).toBe(800);
      await page.setViewport(iPhone.viewport);
      expect(await page.evaluate(() => window.innerWidth)).toBe(375);
      await page.setViewport({width: 400, height: 300});
      expect(await page.evaluate(() => window.innerWidth)).toBe(400);
    });
    it_fails_ffox('should support touch emulation', async({page, server}) => {
      await page.goto(server.PREFIX + '/mobile.html');
      expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(false);
      await page.setViewport(iPhone.viewport);
      expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(true);
      expect(await page.evaluate(dispatchTouch)).toBe('Received touch');
      await page.setViewport({width: 100, height: 100});
      expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(false);

      function dispatchTouch() {
        let fulfill;
        const promise = new Promise(x => fulfill = x);
        window.ontouchstart = function(e) {
          fulfill('Received touch');
        };
        window.dispatchEvent(new Event('touchstart'));

        fulfill('Did not receive touch');

        return promise;
      }
    });
    it_fails_ffox('should be detectable by Modernizr', async({page, server}) => {
      await page.goto(server.PREFIX + '/detect-touch.html');
      expect(await page.evaluate(() => document.body.textContent.trim())).toBe('NO');
      await page.setViewport(iPhone.viewport);
      await page.goto(server.PREFIX + '/detect-touch.html');
      expect(await page.evaluate(() => document.body.textContent.trim())).toBe('YES');
    });
    it_fails_ffox('should detect touch when applying viewport with touches', async({page, server}) => {
      await page.setViewport({ width: 800, height: 600, hasTouch: true });
      await page.addScriptTag({url: server.PREFIX + '/modernizr.js'});
      expect(await page.evaluate(() => Modernizr.touchevents)).toBe(true);
    });
    it_fails_ffox('should support landscape emulation', async({page, server}) => {
      await page.goto(server.PREFIX + '/mobile.html');
      expect(await page.evaluate(() => screen.orientation.type)).toBe('portrait-primary');
      await page.setViewport(iPhoneLandscape.viewport);
      expect(await page.evaluate(() => screen.orientation.type)).toBe('landscape-primary');
      await page.setViewport({width: 100, height: 100});
      expect(await page.evaluate(() => screen.orientation.type)).toBe('portrait-primary');
    });
  });

  describe('Page.emulate', function() {
    it('should work', async({page, server}) => {
      await page.goto(server.PREFIX + '/mobile.html');
      await page.emulate(iPhone);
      expect(await page.evaluate(() => window.innerWidth)).toBe(375);
      expect(await page.evaluate(() => navigator.userAgent)).toContain('iPhone');
    });
    it('should support clicking', async({page, server}) => {
      await page.emulate(iPhone);
      await page.goto(server.PREFIX + '/input/button.html');
      const button = await page.$('button');
      await page.evaluate(button => button.style.marginTop = '200px', button);
      await button.click();
      expect(await page.evaluate(() => result)).toBe('Clicked');
    });
  });

  describe('Page.emulateMedia', function() {
    it('should be an alias for Page.emulateMediaType', async({page, server}) => {
      expect(page.emulateMedia).toEqual(page.emulateMediaType);
    });
  });

  describe('Page.emulateMediaType', function() {
    it_fails_ffox('should work', async({page, server}) => {
      expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(true);
      expect(await page.evaluate(() => matchMedia('print').matches)).toBe(false);
      await page.emulateMediaType('print');
      expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(false);
      expect(await page.evaluate(() => matchMedia('print').matches)).toBe(true);
      await page.emulateMediaType(null);
      expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(true);
      expect(await page.evaluate(() => matchMedia('print').matches)).toBe(false);
    });
    it('should throw in case of bad argument', async({page, server}) => {
      let error = null;
      await page.emulateMediaType('bad').catch(e => error = e);
      expect(error.message).toBe('Unsupported media type: bad');
    });
  });

  describe('Page.emulateMediaFeatures', function() {
    it_fails_ffox('should work', async({page, server}) => {
      await page.emulateMediaFeatures([
        { name: 'prefers-reduced-motion', value: 'reduce' },
      ]);
      expect(await page.evaluate(() => matchMedia('(prefers-reduced-motion: reduce)').matches)).toBe(true);
      expect(await page.evaluate(() => matchMedia('(prefers-reduced-motion: no-preference)').matches)).toBe(false);
      await page.emulateMediaFeatures([
        { name: 'prefers-color-scheme', value: 'light' },
      ]);
      expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(true);
      expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(false);
      expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches)).toBe(false);
      await page.emulateMediaFeatures([
        { name: 'prefers-color-scheme', value: 'dark' },
      ]);
      expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true);
      expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(false);
      expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches)).toBe(false);
      await page.emulateMediaFeatures([
        { name: 'prefers-reduced-motion', value: 'reduce' },
        { name: 'prefers-color-scheme', value: 'light' },
      ]);
      expect(await page.evaluate(() => matchMedia('(prefers-reduced-motion: reduce)').matches)).toBe(true);
      expect(await page.evaluate(() => matchMedia('(prefers-reduced-motion: no-preference)').matches)).toBe(false);
      expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(true);
      expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(false);
      expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches)).toBe(false);
    });
    it('should throw in case of bad argument', async({page, server}) => {
      let error = null;
      await page.emulateMediaFeatures([{ name: 'bad', value: '' }]).catch(e => error = e);
      expect(error.message).toBe('Unsupported media feature: bad');
    });
  });

  describe_fails_ffox('Page.emulateTimezone', function() {
    it('should work', async({page, server}) => {
      page.evaluate(() => {
        globalThis.date = new Date(1479579154987);
      });
      await page.emulateTimezone('America/Jamaica');
      expect(await page.evaluate(() => date.toString())).toBe('Sat Nov 19 2016 13:12:34 GMT-0500 (Eastern Standard Time)');

      await page.emulateTimezone('Pacific/Honolulu');
      expect(await page.evaluate(() => date.toString())).toBe('Sat Nov 19 2016 08:12:34 GMT-1000 (Hawaii-Aleutian Standard Time)');

      await page.emulateTimezone('America/Buenos_Aires');
      expect(await page.evaluate(() => date.toString())).toBe('Sat Nov 19 2016 15:12:34 GMT-0300 (Argentina Standard Time)');

      await page.emulateTimezone('Europe/Berlin');
      expect(await page.evaluate(() => date.toString())).toBe('Sat Nov 19 2016 19:12:34 GMT+0100 (Central European Standard Time)');
    });

    it('should throw for invalid timezone IDs', async({page, server}) => {
      let error = null;
      await page.emulateTimezone('Foo/Bar').catch(e => error = e);
      expect(error.message).toBe('Invalid timezone ID: Foo/Bar');
      await page.emulateTimezone('Baz/Qux').catch(e => error = e);
      expect(error.message).toBe('Invalid timezone ID: Baz/Qux');
    });
  });

};
<commit_msg>chore: fix emulateMedia tests (#5593)<commit_after>/**
 * Copyright 2018 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

module.exports.addTests = function({testRunner, expect, puppeteer}) {
  const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner;
  const {it, fit, xit, it_fails_ffox} = testRunner;
  const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;

  const iPhone = puppeteer.devices['iPhone 6'];
  const iPhoneLandscape = puppeteer.devices['iPhone 6 landscape'];

  describe('Page.viewport', function() {
    it('should get the proper viewport size', async({page, server}) => {
      expect(page.viewport()).toEqual({width: 800, height: 600});
      await page.setViewport({width: 123, height: 456});
      expect(page.viewport()).toEqual({width: 123, height: 456});
    });
    it('should support mobile emulation', async({page, server}) => {
      await page.goto(server.PREFIX + '/mobile.html');
      expect(await page.evaluate(() => window.innerWidth)).toBe(800);
      await page.setViewport(iPhone.viewport);
      expect(await page.evaluate(() => window.innerWidth)).toBe(375);
      await page.setViewport({width: 400, height: 300});
      expect(await page.evaluate(() => window.innerWidth)).toBe(400);
    });
    it_fails_ffox('should support touch emulation', async({page, server}) => {
      await page.goto(server.PREFIX + '/mobile.html');
      expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(false);
      await page.setViewport(iPhone.viewport);
      expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(true);
      expect(await page.evaluate(dispatchTouch)).toBe('Received touch');
      await page.setViewport({width: 100, height: 100});
      expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(false);

      function dispatchTouch() {
        let fulfill;
        const promise = new Promise(x => fulfill = x);
        window.ontouchstart = function(e) {
          fulfill('Received touch');
        };
        window.dispatchEvent(new Event('touchstart'));

        fulfill('Did not receive touch');

        return promise;
      }
    });
    it_fails_ffox('should be detectable by Modernizr', async({page, server}) => {
      await page.goto(server.PREFIX + '/detect-touch.html');
      expect(await page.evaluate(() => document.body.textContent.trim())).toBe('NO');
      await page.setViewport(iPhone.viewport);
      await page.goto(server.PREFIX + '/detect-touch.html');
      expect(await page.evaluate(() => document.body.textContent.trim())).toBe('YES');
    });
    it_fails_ffox('should detect touch when applying viewport with touches', async({page, server}) => {
      await page.setViewport({ width: 800, height: 600, hasTouch: true });
      await page.addScriptTag({url: server.PREFIX + '/modernizr.js'});
      expect(await page.evaluate(() => Modernizr.touchevents)).toBe(true);
    });
    it_fails_ffox('should support landscape emulation', async({page, server}) => {
      await page.goto(server.PREFIX + '/mobile.html');
      expect(await page.evaluate(() => screen.orientation.type)).toBe('portrait-primary');
      await page.setViewport(iPhoneLandscape.viewport);
      expect(await page.evaluate(() => screen.orientation.type)).toBe('landscape-primary');
      await page.setViewport({width: 100, height: 100});
      expect(await page.evaluate(() => screen.orientation.type)).toBe('portrait-primary');
    });
  });

  describe('Page.emulate', function() {
    it('should work', async({page, server}) => {
      await page.goto(server.PREFIX + '/mobile.html');
      await page.emulate(iPhone);
      expect(await page.evaluate(() => window.innerWidth)).toBe(375);
      expect(await page.evaluate(() => navigator.userAgent)).toContain('iPhone');
    });
    it('should support clicking', async({page, server}) => {
      await page.emulate(iPhone);
      await page.goto(server.PREFIX + '/input/button.html');
      const button = await page.$('button');
      await page.evaluate(button => button.style.marginTop = '200px', button);
      await button.click();
      expect(await page.evaluate(() => result)).toBe('Clicked');
    });
  });

  describe('Page.emulateMedia [deprecated]', function() {
    /* emulateMedia is deprecated in favour of emulateMediaType but we
     * don't want to remove it from Puppeteer just yet. We can't check
     * that emulateMedia === emulateMediaType because when running tests
     * with COVERAGE=1 the methods get rewritten. So instead we
     * duplicate the tests for emulateMediaType and ensure they pass
     * when calling the deprecated emulateMedia method.
     *
     * If you update these tests, you should update emulateMediaType's
     * tests, and vice-versa.
     */
    it_fails_ffox('should work', async({page, server}) => {
      expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(true);
      expect(await page.evaluate(() => matchMedia('print').matches)).toBe(false);
      await page.emulateMedia('print');
      expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(false);
      expect(await page.evaluate(() => matchMedia('print').matches)).toBe(true);
      await page.emulateMedia(null);
      expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(true);
      expect(await page.evaluate(() => matchMedia('print').matches)).toBe(false);
    });
    it('should throw in case of bad argument', async({page, server}) => {
      let error = null;
      await page.emulateMedia('bad').catch(e => error = e);
      expect(error.message).toBe('Unsupported media type: bad');
    });
  });

  describe('Page.emulateMediaType', function() {
    /* NOTE! Updating these tests? Update the emulateMedia tests above
     * too (and see the big comment for why we have these duplicated).
     */
    it_fails_ffox('should work', async({page, server}) => {
      expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(true);
      expect(await page.evaluate(() => matchMedia('print').matches)).toBe(false);
      await page.emulateMediaType('print');
      expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(false);
      expect(await page.evaluate(() => matchMedia('print').matches)).toBe(true);
      await page.emulateMediaType(null);
      expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(true);
      expect(await page.evaluate(() => matchMedia('print').matches)).toBe(false);
    });
    it('should throw in case of bad argument', async({page, server}) => {
      let error = null;
      await page.emulateMediaType('bad').catch(e => error = e);
      expect(error.message).toBe('Unsupported media type: bad');
    });
  });

  describe('Page.emulateMediaFeatures', function() {
    it_fails_ffox('should work', async({page, server}) => {
      await page.emulateMediaFeatures([
        { name: 'prefers-reduced-motion', value: 'reduce' },
      ]);
      expect(await page.evaluate(() => matchMedia('(prefers-reduced-motion: reduce)').matches)).toBe(true);
      expect(await page.evaluate(() => matchMedia('(prefers-reduced-motion: no-preference)').matches)).toBe(false);
      await page.emulateMediaFeatures([
        { name: 'prefers-color-scheme', value: 'light' },
      ]);
      expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(true);
      expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(false);
      expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches)).toBe(false);
      await page.emulateMediaFeatures([
        { name: 'prefers-color-scheme', value: 'dark' },
      ]);
      expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true);
      expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(false);
      expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches)).toBe(false);
      await page.emulateMediaFeatures([
        { name: 'prefers-reduced-motion', value: 'reduce' },
        { name: 'prefers-color-scheme', value: 'light' },
      ]);
      expect(await page.evaluate(() => matchMedia('(prefers-reduced-motion: reduce)').matches)).toBe(true);
      expect(await page.evaluate(() => matchMedia('(prefers-reduced-motion: no-preference)').matches)).toBe(false);
      expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(true);
      expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(false);
      expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches)).toBe(false);
    });
    it('should throw in case of bad argument', async({page, server}) => {
      let error = null;
      await page.emulateMediaFeatures([{ name: 'bad', value: '' }]).catch(e => error = e);
      expect(error.message).toBe('Unsupported media feature: bad');
    });
  });

  describe_fails_ffox('Page.emulateTimezone', function() {
    it('should work', async({page, server}) => {
      page.evaluate(() => {
        globalThis.date = new Date(1479579154987);
      });
      await page.emulateTimezone('America/Jamaica');
      expect(await page.evaluate(() => date.toString())).toBe('Sat Nov 19 2016 13:12:34 GMT-0500 (Eastern Standard Time)');

      await page.emulateTimezone('Pacific/Honolulu');
      expect(await page.evaluate(() => date.toString())).toBe('Sat Nov 19 2016 08:12:34 GMT-1000 (Hawaii-Aleutian Standard Time)');

      await page.emulateTimezone('America/Buenos_Aires');
      expect(await page.evaluate(() => date.toString())).toBe('Sat Nov 19 2016 15:12:34 GMT-0300 (Argentina Standard Time)');

      await page.emulateTimezone('Europe/Berlin');
      expect(await page.evaluate(() => date.toString())).toBe('Sat Nov 19 2016 19:12:34 GMT+0100 (Central European Standard Time)');
    });

    it('should throw for invalid timezone IDs', async({page, server}) => {
      let error = null;
      await page.emulateTimezone('Foo/Bar').catch(e => error = e);
      expect(error.message).toBe('Invalid timezone ID: Foo/Bar');
      await page.emulateTimezone('Baz/Qux').catch(e => error = e);
      expect(error.message).toBe('Invalid timezone ID: Baz/Qux');
    });
  });

};
<|endoftext|>


<commit_before>#!/usr/bin/env node

import path from 'path';
import chokidar from 'chokidar';
import glob from 'glob';
import yargs from 'yargs';
import chalk from 'chalk';
import {DtsCreator} from './dtsCreator';

let yarg = yargs.usage('Create .css.d.ts from CSS modules *.css files.\nUsage: $0 [options] <input directory>')
  .example('$0 src/styles')
  .example('$0 src -o dist')
  .example('$0 -p styles/**/*.icss -w')
  .detectLocale(false)
  .demand(['_'])
  .alias('c', 'camelCase').describe('c', 'Convert CSS class tokens to camelcase')
  .alias('o', 'outDir').describe('o', 'Output directory')
  .alias('p', 'pattern').describe('p', 'Glob pattern with css files')
  .alias('w', 'watch').describe('w', 'Watch input directory\'s css files or pattern').boolean('w')
  .alias('d', 'dropExtension').describe('d', 'Drop the input files extension').boolean('d')
  .alias('s', 'silent').describe('s', 'Silent output. Do not show "files written" or warning messages').boolean('s')
  .alias('h', 'help').help('h')
  .version(() => require('../package.json').version)
let argv = yarg.argv;
let creator;

function writeFile(f) {
  creator.create(f, null, !!argv.w)
  .then(content => content.writeFile())
  .then(content => {
    if (!argv.s) {
      console.log('Wrote ' + chalk.green(content.outputFilePath));
      content.messageList.forEach(message => {
        console.warn(chalk.yellow('[Warn] ' + message));
      });
    }
  })
  .catch(reason => console.error(chalk.red('[Error] ' + reason)));
};

let main = () => {
  let rootDir, searchDir;
  if(argv.h) {
    yarg.showHelp();
    return;
  }

  if(argv._ && argv._[0]) {
    searchDir = argv._[0];
  }else if(argv.p) {
    searchDir = './';
  }else{
    yarg.showHelp();
    return;
  }
  let filesPattern = path.join(searchDir, argv.p || '**/*.css');
  rootDir = process.cwd();
  creator = new DtsCreator({
    rootDir,
    searchDir,
    outDir: argv.o,
    camelCase: argv.c,
    dropExtension: argv.d
  });

  if(!argv.w) {
    glob(filesPattern, null, (err, files) => {
      if(err) {
        console.error(err);
        return;
      }
      if(!files || !files.length) return;
      files.forEach(writeFile);
    });
  } else {
    console.log('Watch ' + filesPattern + '...');

    var watcher = chokidar.watch([filesPattern.replace(/\\/g, "/")]);
    watcher.on('add', writeFile);
    watcher.on('change', writeFile);
  }
};

main();
<commit_msg>Remove unecessary TypeError on build<commit_after>#!/usr/bin/env node

import path from 'path';
import chokidar from 'chokidar';
import glob from 'glob';
import yargs from 'yargs';
import chalk from 'chalk';
import {DtsCreator} from './dtsCreator';

let yarg = yargs.usage('Create .css.d.ts from CSS modules *.css files.\nUsage: $0 [options] <input directory>')
  .example('$0 src/styles')
  .example('$0 src -o dist')
  .example('$0 -p styles/**/*.icss -w')
  .detectLocale(false)
  .demand(['_'])
  .alias('c', 'camelCase').describe('c', 'Convert CSS class tokens to camelcase')
  .alias('o', 'outDir').describe('o', 'Output directory')
  .alias('p', 'pattern').describe('p', 'Glob pattern with css files')
  .alias('w', 'watch').describe('w', 'Watch input directory\'s css files or pattern').boolean('w')
  .alias('d', 'dropExtension').describe('d', 'Drop the input files extension').boolean('d')
  .alias('s', 'silent').describe('s', 'Silent output. Do not show "files written" or warning messages').boolean('s')
  .alias('h', 'help').help('h')
  .version(() => require('../package.json').version)
let argv = yarg.argv;
let creator;

function writeFile(f) {
  creator.create(f, null, !!argv.w)
  .then(content => content.writeFile())
  .then(content => {
    if (!argv.s) {
      console.log('Wrote ' + chalk.green(content.outputFilePath));
      if (content.messageList && content.messageList.length) {
        content.messageList.forEach(message => {
          console.warn(chalk.yellow('[Warn] ' + message));
        });
      }
    }
  })
  .catch(reason => console.error(chalk.red('[Error] ' + reason)));
};

let main = () => {
  let rootDir, searchDir;
  if(argv.h) {
    yarg.showHelp();
    return;
  }

  if(argv._ && argv._[0]) {
    searchDir = argv._[0];
  }else if(argv.p) {
    searchDir = './';
  }else{
    yarg.showHelp();
    return;
  }
  let filesPattern = path.join(searchDir, argv.p || '**/*.css');
  rootDir = process.cwd();
  creator = new DtsCreator({
    rootDir,
    searchDir,
    outDir: argv.o,
    camelCase: argv.c,
    dropExtension: argv.d
  });

  if(!argv.w) {
    glob(filesPattern, null, (err, files) => {
      if(err) {
        console.error(err);
        return;
      }
      if(!files || !files.length) return;
      files.forEach(writeFile);
    });
  } else {
    console.log('Watch ' + filesPattern + '...');

    var watcher = chokidar.watch([filesPattern.replace(/\\/g, "/")]);
    watcher.on('add', writeFile);
    watcher.on('change', writeFile);
  }
};

main();
<|endoftext|>


<commit_before>#!/usr/bin/env node

import 'babel-polyfill'

import fs from 'graceful-fs'
import { basename } from 'path'

import log from 'npmlog'
import untildify from 'untildify'
import { promisify } from 'bluebird'
import Bluebird from 'bluebird'

// don't log the config until the log level is set
import config from './config/default.js'
import options from './config/options.js'

import albums from './command/albums.js'
import artists from './command/artists.js'
import audit from './metadata/audit.js'
import makePlaylist from './utils/make-playlist.js'
import optimize from './command/optimize.js'
import saveConfig from './config/save.js'
import scanAlbums from './albums.js'
import scanFLAC from './flac/scan.js'
import unpack from './unpack.js'

const writeFile = promisify(fs.writeFile)

const yargs = require('yargs')
                .usage('Usage: $0 [options] <command>')
                .command('albums', 'print a list of albums in human-readable format')
                .command('artists', 'generate a list of artists from roots')
                .command('audit', 'check metadata for inconsistencies')
                .command('inspect', 'dump all the metadata from a track or album')
                .command('pls', 'print a list of albums as a .pls file, sorted by date')
                .command('unpack', 'unpack a set of zipped files into a staging directory')
                .option('S', options.S)
                .option('loglevel', options.loglevel)
                .help('h')
                .alias('h', 'help')
                .version(() => require('../package').version)
                .demand(1)

log.level = yargs.argv.loglevel
log.verbose('config', config)

let argv, roots
const groups = new Map()
switch (yargs.argv._[0]) {
  case 'albums':
    argv = yargs.reset()
                .usage('Usage: $0 [options] albums [-R dir [file...]]')
                .options({
                  R: options.R
                })
                .check(argv => {
                  if (argv._.length > 1 || (argv.R && argv.R.length)) return true

                  return 'Must pass 1 or more audio files or directory trees.'
                })
                .argv

    log.silly('albums argv', argv)
    albums(argv._.slice(1), argv.R, groups)
    break
  case 'artists':
    options.R.required = '- Must have at least one tree to scan.'
    argv = yargs.reset()
                .usage('Usage: $0 artists [-R dir [-R dir...]]')
                .options({ R: options.R })
                .argv
    roots = argv.R.map(r => untildify(r))
    log.silly('artists', 'argv', argv)

    log.enableProgress()
    artists(roots, groups)
    break
  case 'audit':
    argv = yargs.reset()
                .usage('Usage: $0 audit [file [file...]]')
                .check(argv => {
                  if (argv._.length > 1) return true

                  return 'must pass either 1 or more files containing metadata'
                })
                .argv

    const files = argv._.slice(1)
    log.silly('audit', 'argv', argv)
    log.silly('audit', 'files', files)

    log.enableProgress()
    scanAlbums(files, groups)
      .then(albums => {
        log.disableProgress()
        log.silly('audit', 'albums', albums)
        for (let album of albums) {
          const id = album.artist.name + ': ' + album.name + ' /'
          for (let warning of audit(album)) log.warn('audit', id, warning)
        }
        log.silly('audit', 'tracker debugging', log.tracker.debug())
      })
      .catch(e => {
        log.disableProgress()
        log.error('audit', e.stack)
      })

    break
  case 'inspect':
    argv = yargs.reset()
                .usage('Usage: $0 [options] inspect [file [dir...]]')
                .demand(2)
                .argv

    const things = argv._.slice(1)
    log.silly('inspect', 'argv', argv)
    log.silly('inspect', 'things', things)

    log.enableProgress()
    Bluebird.map(things, f => {
      groups.set(basename(f), log.newGroup(f))
      return scanFLAC(f, groups)
    })
    .then(track => {
      log.disableProgress()
      console.log(JSON.stringify(track, null, 2))
      log.silly('inspect', 'tracker debugging', log.tracker.debug())
    })
    .catch(e => {
      log.disableProgress()
      log.error('inspect', e.stack)
    })

    break
  case 'optimize':
    argv = yargs.reset()
                .usage('Usage: $0 [options] optimize -O blocks [-R dir [file...]]')
                .options({
                  O: options.O,
                  R: options.R,
                  B: options.B
                })
                .check(argv => {
                  if (argv._.length > 1 || (argv.R && argv.R.length)) return true

                  return 'Must pass 1 or more audio files or directory trees.'
                })
                .argv

    log.silly('optimize argv', argv)
    optimize(argv._.slice(1), argv.R, argv.S, argv.O, groups)
    break
  case 'pls':
    options.R.required = '- Must have at least one tree to scan.'
    argv = yargs.reset()
                .usage('Usage: $0 [options] pls [-R dir [-R dir...]]')
                .options({
                  R: options.R
                })
                .argv
    roots = argv.R.map(r => untildify(r))
    log.silly('pls', 'argv', argv)

    log.enableProgress()
    scanAlbums(roots, groups).then(sorted => console.log(makePlaylist(sorted)))
    break
  case 'unpack':
    options.R.describe = 'root directory containing zipped files'
    argv = yargs.reset()
                .usage('Usage: $0 [options] unpack [zipfile [zipfile...]]')
                .options({
                  s: options.s,
                  R: options.R,
                  P: options.P,
                  'archive': options.archive,
                  'archive-root': options.archiveRoot,
                  playlist: options.playlist
                })
                .check(argv => {
                  if (argv._.length > 1 || (argv.R && argv.R.length && argv.P)) return true

                  return 'Must pass either 1 or more zipfiles, or root and glob pattern.'
                })
                .argv

    log.silly('unpack argv', argv)
    const zipfiles = argv._.slice(1)

    let finish = unpack(
      zipfiles,
      argv.s,
      (argv.R || [])[0], argv.P,
      argv.archive, argv.archiveRoot
    )
    if (argv.playlist) {
      finish = finish.then(albums => {
        return writeFile(untildify(argv.playlist), makePlaylist(albums), 'utf-8')
      })
    }
    if (argv.saveConfig) finish = finish.then(() => saveConfig(argv))
    finish.catch(e => log.error('unpack', e.stack))
    break
  default:
    yargs.showHelp()
    process.exit(1)
}
<commit_msg>cli: top-level error trapping<commit_after>#!/usr/bin/env node

import 'babel-polyfill'

import fs from 'graceful-fs'
import { basename } from 'path'

import log from 'npmlog'
import untildify from 'untildify'
import { promisify } from 'bluebird'
import Bluebird from 'bluebird'

// don't log the config until the log level is set
import config from './config/default.js'
import options from './config/options.js'

import albums from './command/albums.js'
import artists from './command/artists.js'
import audit from './metadata/audit.js'
import makePlaylist from './utils/make-playlist.js'
import optimize from './command/optimize.js'
import saveConfig from './config/save.js'
import scanAlbums from './albums.js'
import scanFLAC from './flac/scan.js'
import unpack from './unpack.js'

const writeFile = promisify(fs.writeFile)

const yargs = require('yargs')
                .usage('Usage: $0 [options] <command>')
                .command('albums', 'print a list of albums in human-readable format')
                .command('artists', 'generate a list of artists from roots')
                .command('audit', 'check metadata for inconsistencies')
                .command('inspect', 'dump all the metadata from a track or album')
                .command('pls', 'print a list of albums as a .pls file, sorted by date')
                .command('unpack', 'unpack a set of zipped files into a staging directory')
                .option('S', options.S)
                .option('loglevel', options.loglevel)
                .help('h')
                .alias('h', 'help')
                .version(() => require('../package').version)
                .demand(1)

log.level = yargs.argv.loglevel
log.verbose('config', config)

let argv, command, roots
const groups = new Map()
switch (yargs.argv._[0]) {
  case 'albums':
    argv = yargs.reset()
                .usage('Usage: $0 [options] albums [-R dir [file...]]')
                .options({
                  R: options.R
                })
                .check(argv => {
                  if (argv._.length > 1 || (argv.R && argv.R.length)) return true

                  return 'Must pass 1 or more audio files or directory trees.'
                })
                .argv

    log.silly('albums argv', argv)
    command = albums(argv._.slice(1), argv.R, groups)
    break
  case 'artists':
    options.R.required = '- Must have at least one tree to scan.'
    argv = yargs.reset()
                .usage('Usage: $0 artists [-R dir [-R dir...]]')
                .options({ R: options.R })
                .argv
    roots = argv.R.map(r => untildify(r))
    log.silly('artists', 'argv', argv)

    log.enableProgress()
    command = artists(roots, groups)
    break
  case 'audit':
    argv = yargs.reset()
                .usage('Usage: $0 audit [file [file...]]')
                .check(argv => {
                  if (argv._.length > 1) return true

                  return 'must pass either 1 or more files containing metadata'
                })
                .argv

    const files = argv._.slice(1)
    log.silly('audit', 'argv', argv)
    log.silly('audit', 'files', files)

    log.enableProgress()
    command = scanAlbums(files, groups)
      .then(albums => {
        log.disableProgress()
        log.silly('audit', 'albums', albums)
        for (let album of albums) {
          const id = album.artist.name + ': ' + album.name + ' /'
          for (let warning of audit(album)) log.warn('audit', id, warning)
        }
        log.silly('audit', 'tracker debugging', log.tracker.debug())
      })
      .catch(e => {
        log.disableProgress()
        log.error('audit', e.stack)
      })

    break
  case 'inspect':
    argv = yargs.reset()
                .usage('Usage: $0 [options] inspect [file [dir...]]')
                .demand(2)
                .argv

    const things = argv._.slice(1)
    log.silly('inspect', 'argv', argv)
    log.silly('inspect', 'things', things)

    log.enableProgress()
    command = Bluebird.map(things, f => {
      groups.set(basename(f), log.newGroup(f))
      return scanFLAC(f, groups)
    })
    .then(track => {
      log.disableProgress()
      console.log(JSON.stringify(track, null, 2))
      log.silly('inspect', 'tracker debugging', log.tracker.debug())
    })
    .catch(e => {
      log.disableProgress()
      log.error('inspect', e.stack)
    })

    break
  case 'optimize':
    argv = yargs.reset()
                .usage('Usage: $0 [options] optimize -O blocks [-R dir [file...]]')
                .options({
                  O: options.O,
                  R: options.R,
                  B: options.B
                })
                .check(argv => {
                  if (argv._.length > 1 || (argv.R && argv.R.length)) return true

                  return 'Must pass 1 or more audio files or directory trees.'
                })
                .argv

    log.silly('optimize argv', argv)
    command = optimize(argv._.slice(1), argv.R, argv.S, argv.O, groups)
    break
  case 'pls':
    options.R.required = '- Must have at least one tree to scan.'
    argv = yargs.reset()
                .usage('Usage: $0 [options] pls [-R dir [-R dir...]]')
                .options({
                  R: options.R
                })
                .argv
    roots = argv.R.map(r => untildify(r))
    log.silly('pls', 'argv', argv)

    log.enableProgress()
    command = scanAlbums(roots, groups).then(sorted => console.log(makePlaylist(sorted)))
    break
  case 'unpack':
    options.R.describe = 'root directory containing zipped files'
    argv = yargs.reset()
                .usage('Usage: $0 [options] unpack [zipfile [zipfile...]]')
                .options({
                  s: options.s,
                  R: options.R,
                  P: options.P,
                  archive: options.archive,
                  'archive-root': options.archiveRoot,
                  playlist: options.playlist
                })
                .check(argv => {
                  if (argv._.length > 1 || (argv.R && argv.R.length && argv.P)) return true

                  return 'Must pass either 1 or more zipfiles, or root and glob pattern.'
                })
                .argv

    log.silly('unpack argv', argv)
    const zipfiles = argv._.slice(1)

    command = unpack(
      zipfiles,
      argv.s,
      (argv.R || [])[0], argv.P,
      argv.archive, argv.archiveRoot
    )
    if (argv.playlist) {
      command = command.then(albums => {
        return writeFile(untildify(argv.playlist), makePlaylist(albums), 'utf-8')
      })
    }
    break
  default:
    yargs.showHelp()
    process.exit(1)
}

if (argv.saveConfig) command = command.then(() => saveConfig(argv))
command.catch(e => log.error('packard', e.stack))
<|endoftext|>