<commit_before>(function (Q, $, window, undefined) {

/**
 * @module Streams-tools
 */

/**
 * Renders a preview for a Streams/file stream
 * @class Streams file preview
 * @constructor
 * @param {Object} [options] options to pass to this tool, besides the ones passed to preview
 *   @param {String} [options.windowName='file'] the name of the window in which to open files. Set it to blank to open in the current window.
 *   @param {Object} [options.inplace] Any options to pass to the Q/inplace tool -- see its options.
 *   @uses Q inplace
 *   @param {Object} [options.templates] Under the keys "views", "edit" and "create" you can override options for Q.Template.render .
 *     @param {Object} [options.templates.view]
 *       @param {String} [options.templates.view.name='Streams/file/preview/view']
 *       @param {Object} [options.templates.view.fields]
 *         @param {String} [options.templates.view.fields.alt]
 *         @param {String} [options.templates.view.fields.titleClass]
 *         @param {String} [options.templates.view.fields.titleTag]
 *     @param {Object} [options.templates.edit]
 *       @param {String} [options.templates.edit.name='Streams/file/preview/edit']
 *       @param {Object} [options.templates.edit.fields]
 *         @param {String} [options.templates.edit.fields.alt]
 *         @param {String} [options.templates.edit.fields.titleClass]
 *         @param {String} [options.templates.edit.fields.titleTag]
 * @param {Object} [preview] this is the preview tool that's been instantiated
 */
Q.Tool.define("Streams/file/preview", "Streams/preview",
function _Streams_file_preview(options, preview) {
	var tool = this;
	tool.preview = preview;
	var $te = $(tool.element);
	var state = this.state;
	var ps = preview.state;
	var src = Q.getObject('templates.create.fields.src', state);
	if (src) {
		state.templates.create.fields.src = Q.url(src);
	}
	ps.templates.create = Q.extend(
		ps.templates.create,
		10,
		state.templates.create
	);
	if (ps.creatable) {
		ps.creatable.streamType = ps.creatable.streamType || 'Streams/file';
		ps.creatable.title = ps.creatable.title || 'Upload File';
		if (ps.creatable.clickable) {
			ps.creatable.clickable.preventDefault = false;
		}
	}
	ps.onRefresh.add(tool.refresh.bind(tool), tool);
	ps.onComposer.add(tool.composer.bind(tool), tool);
	preview.loading = function () {
		var $form = $te.find('form').detach();
		var $img = $te.find('img.Streams_preview_add').detach();
		$te.empty().append($img, $form).addClass('Q_uploading');
	};

	// edit action
	if (ps.editable) {
		Q.setObject(["actions", "actions", "edit"], true, ps);
	}
},

{
	inplace: {},
	windowName: 'file',
	templates: {
		view: {
			name: 'Streams/file/preview/view',
			fields: { alt: 'icon', titleClass: '', titleTag: 'h3' }
		},
		edit: {
			name: 'Streams/file/preview/edit',
			fields: { alt: 'icon', titleClass: '', titleTag: 'h3' }
		},
		create: {
			name: 'Streams/file/preview/create',
			fields: {
				src: '{{Q}}/img/actions/upload.png'
			}
		}
	}
},

{
	refresh: function (stream, callback) {
		var tool = this;
		var state = tool.state;
		var ps = tool.preview.state;
		var $te = $(tool.element);
		// set up a pipe to know when the icon has loaded
		var p = Q.pipe(['inplace', 'icon'], function () {
			Q.handle(callback, tool);
		});
		$te.removeClass('Q_uploading');
		// set up the inplace options
		var inplace = null;
		if (state.inplace) {
			var inplaceOptions = Q.extend({
				publisherId: ps.publisherId,
				streamName: ps.streamName,
				field: 'title',
				inplaceType: 'text'
			}, state.inplace);
			var se = ps.editable;
			if (!se || (se !== true && se.indexOf('title') < 0)) {
				inplaceOptions.editable = false;
			} else {
				$te.addClass('Streams_editable_title');
			}
			inplace = tool.setUpElementHTML('div', 'Streams/inplace', inplaceOptions);
		}
		// render a template
		var f = state.template && state.template.fields;
		var size = stream.getAttribute('Q.file.size') || stream.getAttribute('file.size');
		var fields = Q.extend({}, state.templates.edit.fields, f, {
			alt: stream.fields.title,
			title: stream.fields.title,
			inplace: inplace,
			size: _formatSize(size),
			streamName: ps.streamName,
			streamType: stream.fields.type
		});
		var tpl = (ps.editable !== false && stream.testWriteLevel('suggest'))
			? 'edit'
			: 'view';
		Q.Template.render(
			'Streams/file/preview/'+tpl,
			fields,
			function (err, html) {
				if (err) return;
				tool.element.innerHTML = html;
				Q.activate(tool, function () {
					// load the icon
					var jq = tool.$('img.Streams_preview_icon');
					tool.preview.icon(jq[0], p.fill('icon'));
					var $pc = tool.$('.Streams_preview_contents');
					$pc.width(0).width($pc[0].remainingWidth());
					Q.onLayout(tool.element).set(function () {
						var $pc = tool.$('.Streams_preview_contents');
						$pc.width($pc[0].remainingWidth());
					}, tool);
					var inplace = tool.child('Streams_inplace');
					if (!inplace) {
						return p.fill('inplace').apply(this, arguments);
					}
					inplace.state.onLoad.add(function () {
						p.fill('inplace').apply(this, arguments);
					});
				});
				$(tool.element).on(Q.Pointer.fastclick, function () {
					var url = stream.fileUrl();
					if (!url) {
						return tool.selectAndUploadFile();
					}
					if (state.windowName) {
						window.open(url, state.windowName);
					} else {
						window.location = url;
					}
				});

				// apply edit action
				if (Q.getObject(["actions", "actions", "edit"], ps)) {
					ps.actions.actions.edit = tool.selectAndUploadFile.bind(tool);
					tool.preview.actions();
				}
			},
			state.templates[tpl]
		);
	},
	composer: function () {
		var tool = this;
		var $file = tool.$('.Streams_file_input');

		var $container = this.$('.Streams_preview_container');

		// remove default click handler
		$container.off([Q.Pointer.fastclick, ".Streams_preview"]);

		// attach custom handler
		// As we styled file input to fill whole tool element no need to trigger click, because click happens naturally
		// on file element and hence on desktop and android click happens twice, but on ios it happens once.
		/*$container.on([Q.Pointer.fastclick, ".Streams_file_preview"], function () {
			$file.trigger("click");
		});*/

		$file.on("click", function (event) {
			event.stopPropagation();
		}).change(function (event) {
			if (!this.value) {
				return; // it was canceled
			}
			var $this = $(this);
			var form = $this.closest('form').get(0);
			tool.preview.state.creatable.options.form = form;
			tool.preview.state.creatable.options.resultFunction = 'result';
			tool.preview.create(event, function (data) {
				var fem = Q.firstErrorMessage(data && data.error);
				if (fem) {
					Q.alert(fem);
				}

				// after file uploaded, Q/actions plugin must do this
				$container.off([Q.Pointer.fastclick, ".Streams_file_preview"]);
			});
			form.reset();
		});
	},
	selectAndUploadFile: function () {
		var tool = this;
		var state = tool.state;
		var $te = $(tool.element);
		tool.$('.Streams_file_input')
		.off('click')
		.on("click", function (event) {
			event.stopPropagation();
		}).off('change')
		.on("change", function (event) {
			if (!this.value) {
				return; // it was canceled
			}

			$te.addClass("Q_working");

			var form = $(this).closest('form')[0];

			// send request to replace file
			Q.req("Streams/file", function(err, response){
				$te.removeClass("Q_working");
				var msg = Q.firstErrorMessage(err, response && response.errors);
				if (msg) {
					return Q.alert(msg);
				}

				// refresh tool
				var ps = tool.preview.state;
				Q.Streams.Stream.refresh(
					ps.publisherId, ps.streamName, function () {
						tool.preview.preview();
					}
				);

			}, {
				method: 'POST',
				form: form
			});
		}).trigger("click");

		return false;
	}
}

);

function _formatSize(bytes)
{
	if (isNaN(bytes)) return '';
	if (bytes >= Math.pow(2, 30)) {
		return Math.ceil(bytes / Math.pow(2, 30)) + ' GB';
	} else if (bytes >= Math.pow(2, 20)) {
		return Math.ceil(bytes / Math.pow(2, 20)) + ' MB';
	} else if (bytes >= Math.pow(2, 10)) {
		return Math.ceil(bytes / Math.pow(2, 10)) + ' KB';
	} else {
		return bytes + ' bytes';
	}
}

Q.Template.set('Streams/file/preview/view',
	'
'
	+ ''
	+ '
'
	+ '<{{titleTag}} class="Streams_preview_title">{{title}}</{{titleTag}}>'
	+ '
{{size}}
'
	+ '

'
);

Q.Template.set('Streams/file/preview/edit',
	'
'
	+ ''
	+ '
'
	+ '<{{titleTag}} class="Streams_preview_title">{{& inplace}}</{{titleTag}}>'
	+ '
{{size}}
'
	+ '<form enctype="multipart/form-data" class="Streams_file_form">'
	+ '	<input name="file" type="file" class="Streams_file_input">'
	+ ' <input name="streamName" type="hidden" value="{{streamName}}">'
	+ ' <input name="streamType" type="hidden" value="{{streamType}}">'
	+ '</form>'
	+ '

'
);

Q.Template.set('Streams/file/preview/create',
	'
'
	+ ''
	+ '
'
	+ '<{{titleTag}} class="Streams_preview_title">{{title}}</{{titleTag}}>'
	+ '
'
	+ '<form enctype="multipart/form-data" class="Streams_file_form"><input name="file" type="file" class="Streams_file_input"></form>'
	+ '
'
);

})(Q, Q.$, window);<commit_msg>Modified Streams/file/preview tool. Added option skipImagePicker=false. If true skip applying Q/imagepicker to icon.<commit_after>(function (Q, $, window, undefined) {

/**
 * @module Streams-tools
 */

/**
 * Renders a preview for a Streams/file stream
 * @class Streams file preview
 * @constructor
 * @param {Object} [options] options to pass to this tool, besides the ones passed to preview
 *   @param {String} [options.windowName='file'] the name of the window in which to open files. Set it to blank to open in the current window.
 *   @param {Object} [options.inplace] Any options to pass to the Q/inplace tool -- see its options.
 *   @param {Boolean} [options.skipImagePicker=false] If true skip apply Q/imagepicker plugin on icon
 *   @uses Q inplace
 *   @param {Object} [options.templates] Under the keys "views", "edit" and "create" you can override options for Q.Template.render .
 *     @param {Object} [options.templates.view]
 *       @param {String} [options.templates.view.name='Streams/file/preview/view']
 *       @param {Object} [options.templates.view.fields]
 *         @param {String} [options.templates.view.fields.alt]
 *         @param {String} [options.templates.view.fields.titleClass]
 *         @param {String} [options.templates.view.fields.titleTag]
 *     @param {Object} [options.templates.edit]
 *       @param {String} [options.templates.edit.name='Streams/file/preview/edit']
 *       @param {Object} [options.templates.edit.fields]
 *         @param {String} [options.templates.edit.fields.alt]
 *         @param {String} [options.templates.edit.fields.titleClass]
 *         @param {String} [options.templates.edit.fields.titleTag]
 * @param {Object} [preview] this is the preview tool that's been instantiated
 */
Q.Tool.define("Streams/file/preview", "Streams/preview",
function _Streams_file_preview(options, preview) {
	var tool = this;
	tool.preview = preview;
	var $te = $(tool.element);
	var state = this.state;
	var ps = preview.state;
	var src = Q.getObject('templates.create.fields.src', state);
	if (src) {
		state.templates.create.fields.src = Q.url(src);
	}
	ps.templates.create = Q.extend(
		ps.templates.create,
		10,
		state.templates.create
	);
	if (ps.creatable) {
		ps.creatable.streamType = ps.creatable.streamType || 'Streams/file';
		ps.creatable.title = ps.creatable.title || 'Upload File';
		if (ps.creatable.clickable) {
			ps.creatable.clickable.preventDefault = false;
		}
	}
	ps.onRefresh.add(tool.refresh.bind(tool), tool);
	ps.onComposer.add(tool.composer.bind(tool), tool);
	preview.loading = function () {
		var $form = $te.find('form').detach();
		var $img = $te.find('img.Streams_preview_add').detach();
		$te.empty().append($img, $form).addClass('Q_uploading');
	};

	// edit action
	if (ps.editable) {
		Q.setObject(["actions", "actions", "edit"], true, ps);
	}
},

{
	skipImagePicker: false,
	inplace: {},
	windowName: 'file',
	templates: {
		view: {
			name: 'Streams/file/preview/view',
			fields: { alt: 'icon', titleClass: '', titleTag: 'h3' }
		},
		edit: {
			name: 'Streams/file/preview/edit',
			fields: { alt: 'icon', titleClass: '', titleTag: 'h3' }
		},
		create: {
			name: 'Streams/file/preview/create',
			fields: {
				src: '{{Q}}/img/actions/upload.png'
			}
		}
	}
},

{
	refresh: function (stream, callback) {
		var tool = this;
		var state = tool.state;
		var ps = tool.preview.state;
		var $te = $(tool.element);
		// set up a pipe to know when the icon has loaded
		var p = Q.pipe(['inplace', 'icon'], function () {
			Q.handle(callback, tool);
		});
		$te.removeClass('Q_uploading');
		// set up the inplace options
		var inplace = null;
		if (state.inplace) {
			var inplaceOptions = Q.extend({
				publisherId: ps.publisherId,
				streamName: ps.streamName,
				field: 'title',
				inplaceType: 'text'
			}, state.inplace);
			var se = ps.editable;
			if (!se || (se !== true && se.indexOf('title') < 0)) {
				inplaceOptions.editable = false;
			} else {
				$te.addClass('Streams_editable_title');
			}
			inplace = tool.setUpElementHTML('div', 'Streams/inplace', inplaceOptions);
		}
		// render a template
		var f = state.template && state.template.fields;
		var size = stream.getAttribute('Q.file.size') || stream.getAttribute('file.size');
		var fields = Q.extend({}, state.templates.edit.fields, f, {
			alt: stream.fields.title,
			title: stream.fields.title,
			inplace: inplace,
			size: _formatSize(size),
			streamName: ps.streamName,
			streamType: stream.fields.type
		});
		var tpl = (ps.editable !== false && stream.testWriteLevel('suggest'))
			? 'edit'
			: 'view';
		Q.Template.render(
			'Streams/file/preview/'+tpl,
			fields,
			function (err, html) {
				if (err) return;
				tool.element.innerHTML = html;
				Q.activate(tool, function () {
					// load the icon
					var jq = tool.$('img.Streams_preview_icon');
					if (state.skipImagePicker) {
						p.fill('icon')();
					} else {
						tool.preview.icon(jq[0], p.fill('icon'));
					}

					var $pc = tool.$('.Streams_preview_contents');
					$pc.width(0).width($pc[0].remainingWidth());
					Q.onLayout(tool.element).set(function () {
						var $pc = tool.$('.Streams_preview_contents');
						$pc.width($pc[0].remainingWidth());
					}, tool);
					var inplace = tool.child('Streams_inplace');
					if (!inplace) {
						return p.fill('inplace').apply(this, arguments);
					}
					inplace.state.onLoad.add(function () {
						p.fill('inplace').apply(this, arguments);
					});
				});
				$(tool.element).on(Q.Pointer.fastclick, function () {
					var url = stream.fileUrl();
					if (!url) {
						return tool.selectAndUploadFile();
					}
					if (state.windowName) {
						window.open(url, state.windowName);
					} else {
						window.location = url;
					}
				});

				// apply edit action
				if (Q.getObject(["actions", "actions", "edit"], ps)) {
					ps.actions.actions.edit = tool.selectAndUploadFile.bind(tool);
					tool.preview.actions();
				}
			},
			state.templates[tpl]
		);
	},
	composer: function () {
		var tool = this;
		var $file = tool.$('.Streams_file_input');

		var $container = this.$('.Streams_preview_container');

		// remove default click handler
		$container.off([Q.Pointer.fastclick, ".Streams_preview"]);

		// attach custom handler
		// As we styled file input to fill whole tool element no need to trigger click, because click happens naturally
		// on file element and hence on desktop and android click happens twice, but on ios it happens once.
		/*$container.on([Q.Pointer.fastclick, ".Streams_file_preview"], function () {
			$file.trigger("click");
		});*/

		var $icon = tool.$("img.Q_imagepicker");
		if ($icon.length) {
			$icon.plugin('Q/imagepicker', 'remove');
		}

		$file.on("click", function (event) {
			event.stopPropagation();
		}).change(function (event) {
			if (!this.value) {
				return; // it was canceled
			}
			var $this = $(this);
			var form = $this.closest('form').get(0);
			tool.preview.state.creatable.options.form = form;
			tool.preview.state.creatable.options.resultFunction = 'result';
			tool.preview.create(event, function (data) {
				var fem = Q.firstErrorMessage(data && data.error);
				if (fem) {
					Q.alert(fem);
				}

				// after file uploaded, Q/actions plugin must do this
				$container.off([Q.Pointer.fastclick, ".Streams_file_preview"]);
			});
			form.reset();
		});
	},
	selectAndUploadFile: function () {
		var tool = this;
		var state = tool.state;
		var $te = $(tool.element);
		tool.$('.Streams_file_input')
		.off('click')
		.on("click", function (event) {
			event.stopPropagation();
		}).off('change')
		.on("change", function (event) {
			if (!this.value) {
				return; // it was canceled
			}

			$te.addClass("Q_working");

			var form = $(this).closest('form')[0];

			// send request to replace file
			Q.req("Streams/file", function(err, response){
				$te.removeClass("Q_working");
				var msg = Q.firstErrorMessage(err, response && response.errors);
				if (msg) {
					return Q.alert(msg);
				}

				// refresh tool
				var ps = tool.preview.state;
				Q.Streams.Stream.refresh(
					ps.publisherId, ps.streamName, function () {
						tool.preview.preview();
					}
				);

			}, {
				method: 'POST',
				form: form
			});
		}).trigger("click");

		return false;
	}
}

);

function _formatSize(bytes)
{
	if (isNaN(bytes)) return '';
	if (bytes >= Math.pow(2, 30)) {
		return Math.ceil(bytes / Math.pow(2, 30)) + ' GB';
	} else if (bytes >= Math.pow(2, 20)) {
		return Math.ceil(bytes / Math.pow(2, 20)) + ' MB';
	} else if (bytes >= Math.pow(2, 10)) {
		return Math.ceil(bytes / Math.pow(2, 10)) + ' KB';
	} else {
		return bytes + ' bytes';
	}
}

Q.Template.set('Streams/file/preview/view',
	'
'
	+ ''
	+ '
'
	+ '<{{titleTag}} class="Streams_preview_title">{{title}}</{{titleTag}}>'
	+ '
{{size}}
'
	+ '

'
);

Q.Template.set('Streams/file/preview/edit',
	'
'
	+ ''
	+ '
'
	+ '<{{titleTag}} class="Streams_preview_title">{{& inplace}}</{{titleTag}}>'
	+ '
{{size}}
'
	+ '<form enctype="multipart/form-data" class="Streams_file_form">'
	+ '	<input name="file" type="file" class="Streams_file_input">'
	+ ' <input name="streamName" type="hidden" value="{{streamName}}">'
	+ ' <input name="streamType" type="hidden" value="{{streamType}}">'
	+ '</form>'
	+ '

'
);

Q.Template.set('Streams/file/preview/create',
	'
'
	+ ''
	+ '
'
	+ '<{{titleTag}} class="Streams_preview_title">{{title}}</{{titleTag}}>'
	+ '
'
	+ '<form enctype="multipart/form-data" class="Streams_file_form"><input name="file" type="file" class="Streams_file_input"></form>'
	+ '
'
);

})(Q, Q.$, window);<|endoftext|>


<commit_before>/* global window */
import Component from '@ember/component';
import { task } from 'ember-concurrency';
import { computed } from '@ember/object';
import { isBlank } from '@ember/utils';
import { inject as service } from '@ember/service';
import { getOwner } from '@ember/application';

export default Component.extend({
  store: service(),
  config: computed(function(){
    return getOwner(this).resolveRegistration('config:environment');
  }),
  apiEndpoint: computed(function(){
    const config = this.get('config');
    return config.apiEndpoint;
  }),
  template: computed('value', function(){
    const store = this.get('store');
    return store.peekRecord('pdf', this.get('value'));
  }),
  showModal(){
    this.$('.modal').modal('show');
    this.get('fetchTemplates').perform();
  },
  generatePdf: task(function * (){
    const template = this.get('template');
    const model = this.get('model');
    let digest = null;

    yield template.generateDigest({ id: model.get('id')})
    .then((results) => {
      digest = results.digest;
    });

    window.open(`${this.get('apiEndpoint')}template/pdf-generate/${this.get('template.key')}?id=${this.get('model.id')}&digest=${digest}${!isBlank(this.get('language')) ? `&lang=${this.get('language')}` : ''}`, '_blank');
  }),
  fetchTemplates: task(function * (){
    const grouping = this.get('grouping');

    let pdfResults = [];
    yield this.get('store').query('pdf', {
      filter: {
        1: {
          field: 'grouping',
          value: grouping
        }
      }
    }).then((results) => {
      pdfResults = results;
    });

    let selectOptions = [];
    let value = null;
    pdfResults.forEach((pdfResult) => {
      if(isBlank(value)){
        value = pdfResult.get('id');
      }

      let selectOption = {};
      selectOption.value = pdfResult.get('id');
      selectOption.label = pdfResult.get('name');
      selectOptions.push(selectOption);
    });

    this.set('value', value);
    this.set('selectOptions', selectOptions);
  }),
  actions: {
    showModal() {
      this.showModal();
      this.set('modalVisible', true);
    },
    closeModal(){
      this.$('.modal').modal('hide');
      this.set('modalVisible', false);
    },
    templateValueChanged(value){
      this.set('value', value);
    }
  }
});
<commit_msg>print endpoint fix<commit_after>/* global window */
import Component from '@ember/component';
import { task } from 'ember-concurrency';
import { computed } from '@ember/object';
import { isBlank } from '@ember/utils';
import { inject as service } from '@ember/service';
import { getOwner } from '@ember/application';

export default Component.extend({
  store: service(),
  config: computed(function(){
    return getOwner(this).resolveRegistration('config:environment');
  }),
  apiEndpoint: computed(function(){
    const config = this.get('config');
    return config.apiEndpoint;
  }),
  template: computed('value', function(){
    const store = this.get('store');
    return store.peekRecord('pdf', this.get('value'));
  }),
  showModal(){
    this.$('.modal').modal('show');
    this.get('fetchTemplates').perform();
  },
  generatePdf: task(function * (){
    const template = this.get('template');
    const model = this.get('model');
    let digest = null;

    yield template.generateDigest({ id: model.get('id')})
    .then((results) => {
      digest = results.digest;
    });

    window.open(`${this.get('apiEndpoint')}template/generate/${this.get('template.key')}?id=${this.get('model.id')}&digest=${digest}${!isBlank(this.get('language')) ? `&lang=${this.get('language')}` : ''}`, '_blank');
  }),
  fetchTemplates: task(function * (){
    const grouping = this.get('grouping');

    let pdfResults = [];
    yield this.get('store').query('pdf', {
      filter: {
        1: {
          field: 'grouping',
          value: grouping
        }
      }
    }).then((results) => {
      pdfResults = results;
    });

    let selectOptions = [];
    let value = null;
    pdfResults.forEach((pdfResult) => {
      if(isBlank(value)){
        value = pdfResult.get('id');
      }

      let selectOption = {};
      selectOption.value = pdfResult.get('id');
      selectOption.label = pdfResult.get('name');
      selectOptions.push(selectOption);
    });

    this.set('value', value);
    this.set('selectOptions', selectOptions);
  }),
  actions: {
    showModal() {
      this.showModal();
      this.set('modalVisible', true);
    },
    closeModal(){
      this.$('.modal').modal('hide');
      this.set('modalVisible', false);
    },
    templateValueChanged(value){
      this.set('value', value);
    }
  }
});
<|endoftext|>


<commit_before>/*
*  $Id$
*  $HeadURL$
*
*  DOM-related stuff and CSS manipulation class
*
*  @author Ilya Lebedev
*  @author $Author$
*  @modified $Date$
*  @version $Rev$
*  @license LGPL
*  @depends helpers.js
*  @depends arrayextensions.js
*/

if (isUndefined(DOM)) var DOM = {};
/**
 *  Performs parent lookup by
 *   - node object: actually it's "is child of" check
 *   - tagname: getParent(el, 'li') == getParent(el, 'tagName', 'LI')
 *   - any node attribute
 *
 *  @param {HTMLElement} el source element
 *  @param {HTMLElement, String} cp DOMNode or string tagname or string attribute name
 *  @param {String} vl optional attribute value
 *  @return {HTMLElement, Null}
 *  @scope public
 */
DOM.getParent = function (el /* : HTMLElement */, cp /* :String, HTMLElement */, vl /* :String */) /* :HTMLElement */ {
  if (el == null) return null;
  else if (el.nodeType == 1 &&
      ((!isUndefined(vl) && el[cp] == vl) ||
       ('string' == typeof cp && DOM.hasTagName(el, cp)) ||
       el == cp)) return el;
  else return arguments.callee(el.parentNode, cp, vl);
};
/**
 *  Calculates the offset for the DOM node from top left corner
 *
 *  @author Matt Kruse
 *  @see http://javascripttoolbox.com/lib/objectposition/index.php
 *  @param {HTMLElement} el
 *  @return {Object} x: horizontal offset, y: vertical offset
 *  @scope public
 */
DOM.getOffset = function (el /* :HTMLElement */) /* :Object */ {
    var fixBrowserQuirks = true
       ,o = el
       ,left = 0
       ,top = 0
       ,width = 0
       ,height = 0
       ,parentNode = null
       ,offsetParent = null;

    if (o==null) return null;

    offsetParent = o.offsetParent;
    var originalObject = o
       ,el = o; // "el" will be nodes as we walk up, "o" will be saved for offsetParent references
    while (el.parentNode!=null) {
      el = el.parentNode;
      if (el.offsetParent==null) {
      }
      else {
        var considerScroll = true;
        /*
        In Opera, if parentNode of the first object is scrollable, then offsetLeft/offsetTop already
        take its scroll position into account. If elements further up the chain are scrollable, their
        scroll offsets still need to be added in. And for some reason, TR nodes have a scrolltop value
        which must be ignored.
        */
        if (fixBrowserQuirks && window.opera) {
          if (el==originalObject.parentNode || el.nodeName=="TR") {
            considerScroll = false;
          }
        }
        if (considerScroll) {
          if (el.scrollTop && el.scrollTop>0) {
            top -= el.scrollTop;
          }
          if (el.scrollLeft && el.scrollLeft>0) {
            left -= el.scrollLeft;
          }
        }
      }
      // If this node is also the offsetParent, add on the offsets and reset to the new offsetParent
      if (el == offsetParent) {
        left += o.offsetLeft;
        if (el.clientLeft && el.nodeName!="TABLE") {
          left += el.clientLeft;
        }
        top += o.offsetTop;
        if (el.clientTop && el.nodeName!="TABLE") {
          top += el.clientTop;
        }
        o = el;
        if (o.offsetParent==null) {
          if (o.offsetLeft) {
            left += o.offsetLeft;
          }
          if (o.offsetTop) {
            top += o.offsetTop;
          }
        }
        offsetParent = o.offsetParent;
      }
    }

    if (originalObject.offsetWidth) {
      width = originalObject.offsetWidth;
    }
    if (originalObject.offsetHeight) {
      height = originalObject.offsetHeight;
    }

    return {'x':left, 'y':top, 'width':width, 'height':height};
};

//DOM.getOffset = function (el /* :HTMLElement */) /* :Object */ {
/*
    var xy = {'x' : el.offsetLeft , 'y' : el.offsetTop};
    if (el.offsetParent) {
        var xy1 = arguments.callee(el.offsetParent);
        xy.x += xy1.x;
        xy.y += xy1.y;
    }
    return xy;
}
*/
/**
 *  Returns the width of the window canvas
 *
 *  @return {Number}
 *  @scope public
 */
DOM.getClientWidth = function () /* :Number */{
    var w=0;
    if (self.innerHeight) w = self.innerWidth;
    else if (document.documentElement && document.documentElement.clientWidth) w = document.documentElement.clientWidth;
    else if (document.body) w = document.body.clientWidth;
    return w;
};
/**
 *  Returns the height of the window canvas
 *
 *  @return {Number}
 *  @scope public
 */
DOM.getClientHeight = function () /* :Number */{
    var h=0;
    if (self.innerHeight) h = self.innerHeight;
    else if (document.documentElement && document.documentElement.clientHeight) h = document.documentElement.clientHeight;
    else if (document.body) h = document.body.clientHeight;
    return h;
};
/**
 *  Returns the height of the scrolled area for the body
 *
 *  @return {Number}
 *  @scope public
 */
DOM.getBodyScrollTop = function () /* :Number */{
    return self.pageYOffset || (document.documentElement && document.documentElement.scrollTop) || (document.body && document.body.scrollTop);
};
/**
 *  Returns the height of the scrolled area for the body
 *
 *  @return {Number}
 *  @scope public
 */
DOM.getBodyScrollLeft = function () /* :Number */{
    return self.pageXOffset || (document.documentElement && document.documentElement.scrollLeft) || (document.body && document.body.scrollLeft);
};
/**
 *  Calculates cursor position properly
 *
 *  @param {Event} e event object to get cursor positions from
 *  @return {Object} object with x and y cursor positions
 *  @scope protected
 *  @see http://hartshorne.ca/2006/01/23/javascript_cursor_position/
 *  @author Beau Hartshorne
 */
DOM.getCursorPosition = function (e) {
    if (e.pageX || e.pageY) return {'x': e.pageX, 'y': e.pageY};

    var de = document.documentElement || document.body;
    return {'x': e.clientX + de.scrollLeft - (de.clientLeft || 0)
           ,'y': e.clientY + de.scrollTop - (de.clientTop || 0)};
};
/**
 *  Checks, if property matches a tagname(s)
 *
 *  @param {HTMLElement} prop
 *  @param {String, Array} tags
 *  @return {Boolean}
 *  @scope public
 */
DOM.hasTagName = function (prop /* :HTMLElement */, tags /* :String, Array */) {
    if (isString(tags)) tags = [tags];
    if (!isArray(tags) || isEmpty(tags) || isUndefined(prop) || isEmpty(prop.tagName)) return false;
    var t = prop.tagName.toLowerCase();
    for (var i=0, tL=tags.length; i<tL; i++) {
        if (tags[i].toLowerCase() == t) return true;
    }
    return false;
};

/**
 *  DOM.CSS is the CSS processing class, allowing to easy mangle class names
 *
 *  @param {HTMLElement} el element to provide interface for
 *  @scope public
 *  @constructor
 *  @class DOM.CSS
 *  @exception on invalid parameter
 *  @depends arrayextensions.js
 *  @depends helpers.js
 */
DOM.CSS = function (el) {
  var keys = {
      'singleton' : 'DOM_CSS_singletonObject'
  };
  /**
   *  Interface itself
   *
   *  @param {HTMLElement} el element to provide interface for
   *  @return DOM.CSS
   */
  var instance = function(el) {
      var self = this
      /**
       *  Array of the css classes
       *
       *  @type Array
       *  @scope private
       */
         ,css = el.className.split(/\s+/)
         ,csstext = el.className;
      /**
       *  Adds the class name, unlimited number of arguments is supported
       *
       *  @param {String} class classname to apply to the element
       *  @return {String} new class name
       *  @scope public
       */
      self.addClass = function() {
          if (csstext != el.className) css = el.className.split(/\s+/);
          for (var i=arguments.length, ar, f=false; i>=0; i--) {
              ar=arguments[i];
              if (isString(ar) && css.indexOf(ar)==-1) {css[css.length] = ar; f=true};
          }
          if (f) el.className = css.join(" ");
          return csstext = el.className;
      };
      /**
       *  Removes the class name, unlimited number of arguments is supported
       *
       *  @param {String} class classname to apply to the element
       *  @return {String} new class name
       *  @scope public
       */
      self.removeClass = function() {
          if (csstext != el.className) css = el.className.split(/\s+/);
          for (var i=arguments.length, ar, f=false; i>=0; i--) {
              ar=arguments[i];
              if (isString(ar) && css.indexOf(ar)>-1) css.splice(css.indexOf(ar),1);
          }
          el.className = css.join(" ");
          return csstext = el.className;
      };
      /**
       *  Checks classname for the certain class
       *
       *  @param {String} c class name to check for
       *  @return {Boolean} class name existence
       *  @scope public
       */
      self.hasClass = function(c) {
          if (csstext != el.className) {
              csstext = el.className;
              css = csstext.split(/\s+/);
          }
          return css.indexOf(c)>-1;
      }
  };
  if (isUndefined(el.className)) { throw new Error('Invalid element supplied, no className attribute detected');};

  /*
  *  this is used for the IE, because sometimes it looses the singleton somehow
  */
  try {el[keys.singleton].t=true; return el[keys.singleton]} catch(e) { return el[keys.singleton] = new instance(el) }

};
<commit_msg>+ DOM.color2rgb method, used to convert CSS color definition to the valid RGB array + DOM.CSS.getComputedStyle method, returning effective styles, applied to the element + String.prototype.entityDecode method, used to decode HTML entities in the string<commit_after>/*
*  $Id$
*  $HeadURL$
*
*  DOM-related stuff and CSS manipulation class
*
*  @author Ilya Lebedev
*  @author $Author$
*  @modified $Date$
*  @version $Rev$
*  @license LGPL
*  @depends helpers.js
*  @depends arrayextensions.js
*/

if (isUndefined(DOM)) var DOM = {};
/**
 *  Performs parent lookup by
 *   - node object: actually it's "is child of" check
 *   - tagname: getParent(el, 'li') == getParent(el, 'tagName', 'LI')
 *   - any node attribute
 *
 *  @param {HTMLElement} el source element
 *  @param {HTMLElement, String} cp DOMNode or string tagname or string attribute name
 *  @param {String} vl optional attribute value
 *  @return {HTMLElement, Null}
 *  @scope public
 */
DOM.getParent = function (el /* : HTMLElement */, cp /* :String, HTMLElement */, vl /* :String */) /* :HTMLElement */ {
  if (el == null) return null;
  else if (el.nodeType == 1 &&
      ((!isUndefined(vl) && el[cp] == vl) ||
       ('string' == typeof cp && DOM.hasTagName(el, cp)) ||
       el == cp)) return el;
  else return arguments.callee(el.parentNode, cp, vl);
};
/**
 *  Calculates the offset for the DOM node from top left corner
 *
 *  @author Matt Kruse
 *  @see http://javascripttoolbox.com/lib/objectposition/index.php
 *  @param {HTMLElement} el
 *  @return {Object} x: horizontal offset, y: vertical offset
 *  @scope public
 */
DOM.getOffset = function (el /* :HTMLElement */) /* :Object */ {
    var fixBrowserQuirks = true
       ,o = el
       ,left = 0
       ,top = 0
       ,width = 0
       ,height = 0
       ,parentNode = null
       ,offsetParent = null;

    if (o==null) return null;

    offsetParent = o.offsetParent;
    var originalObject = o
       ,el = o; // "el" will be nodes as we walk up, "o" will be saved for offsetParent references
    while (el.parentNode!=null) {
      el = el.parentNode;
      if (el.offsetParent==null) {
      }
      else {
        var considerScroll = true;
        /*
        In Opera, if parentNode of the first object is scrollable, then offsetLeft/offsetTop already
        take its scroll position into account. If elements further up the chain are scrollable, their
        scroll offsets still need to be added in. And for some reason, TR nodes have a scrolltop value
        which must be ignored.
        */
        if (fixBrowserQuirks && window.opera) {
          if (el==originalObject.parentNode || el.nodeName=="TR") {
            considerScroll = false;
          }
        }
        if (considerScroll) {
          if (el.scrollTop && el.scrollTop>0) {
            top -= el.scrollTop;
          }
          if (el.scrollLeft && el.scrollLeft>0) {
            left -= el.scrollLeft;
          }
        }
      }
      // If this node is also the offsetParent, add on the offsets and reset to the new offsetParent
      if (el == offsetParent) {
        left += o.offsetLeft;
        if (el.clientLeft && el.nodeName!="TABLE") {
          left += el.clientLeft;
        }
        top += o.offsetTop;
        if (el.clientTop && el.nodeName!="TABLE") {
          top += el.clientTop;
        }
        o = el;
        if (o.offsetParent==null) {
          if (o.offsetLeft) {
            left += o.offsetLeft;
          }
          if (o.offsetTop) {
            top += o.offsetTop;
          }
        }
        offsetParent = o.offsetParent;
      }
    }

    if (originalObject.offsetWidth) {
      width = originalObject.offsetWidth;
    }
    if (originalObject.offsetHeight) {
      height = originalObject.offsetHeight;
    }

    return {'x':left, 'y':top, 'width':width, 'height':height};
};

//DOM.getOffset = function (el /* :HTMLElement */) /* :Object */ {
/*
    var xy = {'x' : el.offsetLeft , 'y' : el.offsetTop};
    if (el.offsetParent) {
        var xy1 = arguments.callee(el.offsetParent);
        xy.x += xy1.x;
        xy.y += xy1.y;
    }
    return xy;
}
*/
/**
 *  Returns the width of the window canvas
 *
 *  @return {Number}
 *  @scope public
 */
DOM.getClientWidth = function () /* :Number */{
    var w=0;
    if (self.innerHeight) w = self.innerWidth;
    else if (document.documentElement && document.documentElement.clientWidth) w = document.documentElement.clientWidth;
    else if (document.body) w = document.body.clientWidth;
    return w;
};
/**
 *  Returns the height of the window canvas
 *
 *  @return {Number}
 *  @scope public
 */
DOM.getClientHeight = function () /* :Number */{
    var h=0;
    if (self.innerHeight) h = self.innerHeight;
    else if (document.documentElement && document.documentElement.clientHeight) h = document.documentElement.clientHeight;
    else if (document.body) h = document.body.clientHeight;
    return h;
};
/**
 *  Returns the height of the scrolled area for the body
 *
 *  @return {Number}
 *  @scope public
 */
DOM.getBodyScrollTop = function () /* :Number */{
    return self.pageYOffset || (document.documentElement && document.documentElement.scrollTop) || (document.body && document.body.scrollTop);
};
/**
 *  Returns the height of the scrolled area for the body
 *
 *  @return {Number}
 *  @scope public
 */
DOM.getBodyScrollLeft = function () /* :Number */{
    return self.pageXOffset || (document.documentElement && document.documentElement.scrollLeft) || (document.body && document.body.scrollLeft);
};
/**
 *  Calculates cursor position properly
 *
 *  @param {Event} e event object to get cursor positions from
 *  @return {Object} object with x and y cursor positions
 *  @scope protected
 *  @see http://hartshorne.ca/2006/01/23/javascript_cursor_position/
 *  @author Beau Hartshorne
 */
DOM.getCursorPosition = function (e) {
    if (e.pageX || e.pageY) return {'x': e.pageX, 'y': e.pageY};

    var de = document.documentElement || document.body;
    return {'x': e.clientX + de.scrollLeft - (de.clientLeft || 0)
           ,'y': e.clientY + de.scrollTop - (de.clientTop || 0)};
};
/**
 *  Checks, if property matches a tagname(s)
 *
 *  @param {HTMLElement} prop
 *  @param {String, Array} tags
 *  @return {Boolean}
 *  @scope public
 */
DOM.hasTagName = function (prop /* :HTMLElement */, tags /* :String, Array */) {
    if (isString(tags)) tags = [tags];
    if (!isArray(tags) || isEmpty(tags) || isUndefined(prop) || isEmpty(prop.tagName)) return false;
    var t = prop.tagName.toLowerCase();
    for (var i=0, tL=tags.length; i<tL; i++) {
        if (tags[i].toLowerCase() == t) return true;
    }
    return false;
};
/**
 *  Returns the actual rgb color value from the following formats
 *  #xxxxxx
 *  rgb (0..255, 0..255,0..255)
 *  rgb (0..100%, 0..100%,0..100%)
 *  <color_name>
 *
 *  @param {String} from attr name
 *  @return {Array} rgb values
 *  @scope public
 */
DOM.color2rgb = function (prop) {
     var e;
     if (/^[a-z]+$/i.test(prop)) {
         var d = document.body, ov = d.vLink;
         d.vLink = prop;
         prop = d.vLink;
         d.vLink = ov;
     }
     if (/^#[\da-f]{6}$/i.test(prop))
         return e=parseInt(prop.replace(/[^\da-f]/,''),16),[(e&0xff0000)>>16,(e&0xff00)>>8,(e&0xff)]
     else {
         return (prop.split(/^|[^\d%]+/).splice(1,2).map(function(a){ return /%/.test(a)?(parseInt(a)*2.55).toFixed(0):parseInt(a)}))
     }
}
/**
 *  DOM.CSS is the CSS processing class, allowing to easy mangle class names
 *
 *  @param {HTMLElement} el element to provide interface for
 *  @scope public
 *  @constructor
 *  @class DOM.CSS
 *  @exception on invalid parameter
 *  @depends arrayextensions.js
 *  @depends helpers.js
 */
DOM.CSS = function (el) {
  var keys = {
      'singleton' : 'DOM_CSS_singletonObject'
  };
  /**
   *  Interface itself
   *
   *  @param {HTMLElement} el element to provide interface for
   *  @return DOM.CSS
   */
  var instance = function(el) {
      var self = this
      /**
       *  Array of the css classes
       *
       *  @type Array
       *  @scope private
       */
         ,css = el.className.split(/\s+/)
         ,csstext = el.className;
      /**
       *  Adds the class name, unlimited number of arguments is supported
       *
       *  @param {String} class classname to apply to the element
       *  @return {String} new class name
       *  @scope public
       */
      self.addClass = function() {
          if (csstext != el.className) css = el.className.split(/\s+/);
          for (var i=arguments.length, ar, f=false; i>=0; i--) {
              ar=arguments[i];
              if (isString(ar) && css.indexOf(ar)==-1) {css[css.length] = ar; f=true};
          }
          if (f) el.className = css.join(" ");
          return csstext = el.className;
      };
      /**
       *  Removes the class name, unlimited number of arguments is supported
       *
       *  @param {String} class classname to apply to the element
       *  @return {String} new class name
       *  @scope public
       */
      self.removeClass = function() {
          if (csstext != el.className) css = el.className.split(/\s+/);
          for (var i=arguments.length, ar, f=false; i>=0; i--) {
              ar=arguments[i];
              if (isString(ar) && css.indexOf(ar)>-1) css.splice(css.indexOf(ar),1);
          }
          el.className = css.join(" ");
          return csstext = el.className;
      };
      /**
       *  Checks classname for the certain class
       *
       *  @param {String} c class name to check for
       *  @return {Boolean} class name existence
       *  @scope public
       */
      self.hasClass = function(c) {
          if (csstext != el.className) {
              csstext = el.className;
              css = csstext.split(/\s+/);
          }
          return css.indexOf(c)>-1;
      };
      /**
       *  Returns actual style for the element, computed from CSS and inline styles
       *
       *  @param {String} prop optional style property to fetch
       *  @return {Object} computed style or property value
       *  @scope public
       */
      self.getComputedStyle = function(prop) {
          var y;
          if (el.currentStyle)
              y = prop?el.currentStyle[prop]:x.currentStyle;
          else if (window.getComputedStyle) {
              y = document.defaultView.getComputedStyle(el,null);
              if (prop) y=y.getPropertyValue(prop)
          } else {
              y = null;
          }
          return y;
      }
  };
  if (isUndefined(el.className)) { throw new Error('Invalid element supplied, no className attribute detected');};

  /*
  *  this is used for the IE, because sometimes it looses the singleton somehow
  */
  try {el[keys.singleton].t=true; return el[keys.singleton]} catch(e) { return el[keys.singleton] = new instance(el) }

};
<|endoftext|>


<commit_before>'use strict';

var toPositiveInteger = require('es5-ext/number/to-pos-integer')
  , primitiveSet      = require('es5-ext/object/primitive-set')
  , memoize           = require('memoizee/plain')
  , getNormalizer     = require('memoizee/normalizers/get-1')

  , forEach = Array.prototype.forEach, keys = Object.keys, round = Math.round
  , byNum = function (a, b) { return a - b; }
  , actions = primitiveSet('activate', 'deactivate', 'insert', 'remove', 'mark', 'class')
  , getSubsteps;

getSubsteps = memoize(function (element) {
	var map = {}, defaultOrder = 0;
	forEach.call(element.querySelectorAll('.substep'), function (el) {
		var order = Number(el.dataset.order), action, data, names, classOrder;
		if (isNaN(order)) order = (defaultOrder += 0.01);
		else defaultOrder = order;
		if (!map[order]) map[order] = {};
		action = el.dataset.action;
		if (!actions[action]) action = null;
		if (action == null) {
			if (el.nodeName.toLowerCase() === 'mark') action = 'mark';
			else action = 'activate';
		}
		if (action === 'class') {
			if (!el.dataset.names) throw new TypeError('Missing names for class configuration');
			names = el.dataset.names.split(' ');
			classOrder = order;
			names.forEach(function (name) {
				data = { el: el, name: name };
				if (!map[classOrder]) map[classOrder] = {};
				if (!map[classOrder].class) map[classOrder].class = [data];
				else map[order][action].push(data);
				classOrder += 0.001;
				classOrder = round(classOrder * 1000) / 1000;
			});
			return;
		}
		if (!map[order][action]) map[order][action] = [el];
		else map[order][action].push(el);
	});
	return keys(map).sort(byNum).map(function (order) { return map[order]; });
}, { normalizer: getNormalizer() });

module.exports = function (deck/*, options*/) {
	var activeSubstep = 0;

	deck.on('activate', function (e) {
		var substep = toPositiveInteger(e.substep);
		getSubsteps(e.slide).forEach(function (els, index) {
			var current = substep === index + 1
			  , isAfter = substep > index;
			if (els.activate) {
				els.activate.forEach(function (el) {
					el.classList[isAfter ? 'add' : 'remove']('active');
					el.classList[isAfter ? 'remove' : 'add']('inactive');
				});
			}
			if (els.deactivate) {
				els.deactivate.forEach(function (el) {
					el.classList[isAfter ? 'remove' : 'add']('active');
					el.classList[isAfter ? 'add' : 'remove']('inactive');
				});
			}
			if (els.insert) {
				els.insert.forEach(function (el) {
					el.classList[isAfter ? 'add' : 'remove']('inserted');
					el.classList[isAfter ? 'remove' : 'add']('removed');
				});
			}
			if (els.remove) {
				els.remove.forEach(function (el) {
					el.classList[isAfter ? 'remove' : 'add']('inserted');
					el.classList[isAfter ? 'add' : 'remove']('removed');
				});
			}
			if (els.mark) {
				els.mark.forEach(function (el) {
					el.classList[current ? 'add' : 'remove']('marked');
					el.classList[current ? 'remove' : 'add']('unmarked');
				});
			}
			if (els.class) {
				els.class.forEach(function (data) {
					data.el.classList[current ? 'add' : 'remove'](data.name);
				});
			}
		});
		activeSubstep = substep;
	});

	deck.on('next', function () {
		var activeSlide = deck.slide();
		if (!getSubsteps(deck.slides[activeSlide])[activeSubstep]) return;
		deck.slide(activeSlide, { substep: activeSubstep + 1 });
		return false;
	});

	deck.on('prev', function () {
		var activeSlide = deck.slide();
		if (!activeSlide) return;
		if (!activeSubstep) {
			--activeSlide;
			deck.slide(activeSlide, {
				substep: getSubsteps(deck.slides[activeSlide]).length
			});
		} else {
			deck.slide(activeSlide, { substep: activeSubstep - 1 });
		}
		return false;
	});
};
<commit_msg>Update for Bespoke 1.0<commit_after>'use strict';

var toPositiveInteger = require('es5-ext/number/to-pos-integer')
  , primitiveSet      = require('es5-ext/object/primitive-set')
  , memoize           = require('memoizee/plain')
  , getNormalizer     = require('memoizee/normalizers/get-1')

  , forEach = Array.prototype.forEach, keys = Object.keys, round = Math.round
  , byNum = function (a, b) { return a - b; }
  , actions = primitiveSet('activate', 'deactivate', 'insert', 'remove', 'mark', 'class')
  , getSubsteps;

getSubsteps = memoize(function (element) {
	var map = {}, defaultOrder = 0;
	forEach.call(element.querySelectorAll('.substep'), function (el) {
		var order = Number(el.dataset.order), action, data, names, classOrder;
		if (isNaN(order)) order = (defaultOrder += 0.01);
		else defaultOrder = order;
		if (!map[order]) map[order] = {};
		action = el.dataset.action;
		if (!actions[action]) action = null;
		if (action == null) {
			if (el.nodeName.toLowerCase() === 'mark') action = 'mark';
			else action = 'activate';
		}
		if (action === 'class') {
			if (!el.dataset.names) throw new TypeError('Missing names for class configuration');
			names = el.dataset.names.split(' ');
			classOrder = order;
			names.forEach(function (name) {
				data = { el: el, name: name };
				if (!map[classOrder]) map[classOrder] = {};
				if (!map[classOrder].class) map[classOrder].class = [data];
				else map[order][action].push(data);
				classOrder += 0.001;
				classOrder = round(classOrder * 1000) / 1000;
			});
			return;
		}
		if (!map[order][action]) map[order][action] = [el];
		else map[order][action].push(el);
	});
	return keys(map).sort(byNum).map(function (order) { return map[order]; });
}, { normalizer: getNormalizer() });

module.exports = function (/*options*/) {
	return function (deck) {
		var activeSubstep = 0;

		deck.on('activate', function (e) {
			var substep = toPositiveInteger(e.substep);
			getSubsteps(e.slide).forEach(function (els, index) {
				var current = substep === index + 1
				  , isAfter = substep > index;
				if (els.activate) {
					els.activate.forEach(function (el) {
						el.classList[isAfter ? 'add' : 'remove']('active');
						el.classList[isAfter ? 'remove' : 'add']('inactive');
					});
				}
				if (els.deactivate) {
					els.deactivate.forEach(function (el) {
						el.classList[isAfter ? 'remove' : 'add']('active');
						el.classList[isAfter ? 'add' : 'remove']('inactive');
					});
				}
				if (els.insert) {
					els.insert.forEach(function (el) {
						el.classList[isAfter ? 'add' : 'remove']('inserted');
						el.classList[isAfter ? 'remove' : 'add']('removed');
					});
				}
				if (els.remove) {
					els.remove.forEach(function (el) {
						el.classList[isAfter ? 'remove' : 'add']('inserted');
						el.classList[isAfter ? 'add' : 'remove']('removed');
					});
				}
				if (els.mark) {
					els.mark.forEach(function (el) {
						el.classList[current ? 'add' : 'remove']('marked');
						el.classList[current ? 'remove' : 'add']('unmarked');
					});
				}
				if (els.class) {
					els.class.forEach(function (data) {
						data.el.classList[current ? 'add' : 'remove'](data.name);
					});
				}
			});
			activeSubstep = substep;
		});

		deck.on('next', function () {
			var activeSlide = deck.slide();
			if (!getSubsteps(deck.slides[activeSlide])[activeSubstep]) return;
			deck.slide(activeSlide, { substep: activeSubstep + 1 });
			return false;
		});

		deck.on('prev', function () {
			var activeSlide = deck.slide();
			if (!activeSlide) return;
			if (!activeSubstep) {
				--activeSlide;
				deck.slide(activeSlide, {
					substep: getSubsteps(deck.slides[activeSlide]).length
				});
			} else {
				deck.slide(activeSlide, { substep: activeSubstep - 1 });
			}
			return false;
		});
	};
};
<|endoftext|>


<commit_before>'use strict';

function EventPubSub() {
    this._events_={};
    this.publish=this.trigger=this.emit=emit;
    this.subscribe=this.on=on;
    this.unSubscribe=this.off=off;
    this.emit$=emit$;

    function on(type,handler,once){
        if(!handler){
            throw new ReferenceError('handler not defined.');
        }

        if(!this._events_[type]){
            this._events_[type]=[];
        }

        if(once){
            handler._once_ = once;
        }
        this._events_[type].push(handler);
        return this;
    }

    function once(type,handler){
        return this.on(type, handler, true);
    }

    function off(type,handler){
        if(!this._events_[type]){
            return this;
        }

        if(!handler){
            throw new ReferenceError('handler not defined. if you wish to remove all handlers from the event please pass "*" as the handler');
        }

        if(handler=='*'){
            delete this._events_[type];
            return this;
        }

        const handlers=this._events_[type];

        while(handlers.includes(handler)){
            handlers.splice(
                handlers.indexOf(handler),
                1
            );
        }

        if(handlers.length<1){
            delete this._events_[type];
        }

        return this;
    }

    function emit(type){
        this.emit$.apply(this, arguments);
        if(!this._events_[type]){
            return this;
        }
        arguments.splice=Array.prototype.splice;
        arguments.splice(0,1);

        const handlers=this._events_[type];

        for(let handler in handlers){
            const handler=handlers[handler];
            handler.apply(this, arguments);
            if(handler._once_){
                handlers.splice(
                    handlers.indexOf(handler),
                    1
                );
            }
        }

        return this;
    }

    function emit$(type, args){
        if(!this._events_['*']){
            return this;
        }

        const catchAll=this._events_['*'];

        args.shift=Array.prototype.shift;
        args.shift(type);

        for(let handler of catchAll){
            handler.apply(this, args);
        }

        return this;
    }

    return this;
}

if (!Array.prototype.includes) {
  Array.prototype.includes = function(searchElement /*, fromIndex*/) {
    'use strict';
    if (this == null) {
      throw new TypeError('Array.prototype.includes called on null or undefined');
    }

    var O = Object(this);
    var len = parseInt(O.length, 10) || 0;
    if (len === 0) {
      return false;
    }
    var n = parseInt(arguments[1], 10) || 0;
    var k;
    if (n >= 0) {
      k = n;
    } else {
      k = len + n;
      if (k < 0) {k = 0;}
    }
    var currentElement;
    while (k < len) {
      currentElement = O[k];
      if (searchElement === currentElement ||
         (searchElement !== searchElement && currentElement !== currentElement)) { // NaN !== NaN
        return true;
      }
      k++;
    }
    return false;
  };
}

module.exports=EventPubSub;
<commit_msg>cleanup and some es5 fixes<commit_after>'use strict';

function EventPubSub() {
    this._events_={};
    this.publish=this.trigger=this.emit=emit;
    this.subscribe=this.on=on;
    this.unSubscribe=this.off=off;
    this.emit$=emit$;

    function on(type,handler,once){
        if(!handler){
            throw new ReferenceError('handler not defined.');
        }

        if(!this._events_[type]){
            this._events_[type]=[];
        }

        if(once){
            handler._once_ = once;
        }
        this._events_[type].push(handler);
        return this;
    }

    function once(type,handler){
        return this.on(type, handler, true);
    }

    function off(type,handler){
        if(!this._events_[type]){
            return this;
        }

        if(!handler){
            throw new ReferenceError('handler not defined. if you wish to remove all handlers from the event please pass "*" as the handler');
        }

        if(handler=='*'){
            delete this._events_[type];
            return this;
        }

        var handlers=this._events_[type];

        while(handlers.includes(handler)){
            handlers.splice(
                handlers.indexOf(handler),
                1
            );
        }

        if(handlers.length<1){
            delete this._events_[type];
        }

        return this;
    }

    function emit(type){
        this.emit$.apply(this, arguments);
        if(!this._events_[type]){
            return this;
        }
        arguments.splice=Array.prototype.splice;
        arguments.splice(0,1);

        var handlers=this._events_[type];

        for(let handler in handlers){
            const handler=handlers[handler];
            handler.apply(this, arguments);
            this.off(type,handler);
        }

        return this;
    }

    function emit$(type, args){
        if(!this._events_['*']){
            return this;
        }

        var catchAll=this._events_['*'];

        args.shift=Array.prototype.shift;
        args.shift(type);

        for(let handler of catchAll){
            handler.apply(this, args);
        }

        return this;
    }

    return this;
}

if (!Array.prototype.includes) {
  Array.prototype.includes = function(searchElement /*, fromIndex*/) {
    'use strict';
    if (this == null) {
      throw new TypeError('Array.prototype.includes called on null or undefined');
    }

    var O = Object(this);
    var len = parseInt(O.length, 10) || 0;
    if (len === 0) {
      return false;
    }
    var n = parseInt(arguments[1], 10) || 0;
    var k;
    if (n >= 0) {
      k = n;
    } else {
      k = len + n;
      if (k < 0) {k = 0;}
    }
    var currentElement;
    while (k < len) {
      currentElement = O[k];
      if (searchElement === currentElement ||
         (searchElement !== searchElement && currentElement !== currentElement)) { // NaN !== NaN
        return true;
      }
      k++;
    }
    return false;
  };
}

module.exports=EventPubSub;
<|endoftext|>


<commit_before>'use strict';

var $isNaN = Number.isNaN || function (a) { return a !== a; };
var $isFinite = require('./helpers/isFinite');

var sign = require('./helpers/sign');
var mod = require('./helpers/mod');

var IsCallable = require('is-callable');
var toPrimitive = require('es-to-primitive/es5');

// https://es5.github.io/#x9
var ES5 = {
	ToPrimitive: toPrimitive,

	ToBoolean: function ToBoolean(value) {
		return Boolean(value);
	},
	ToNumber: function ToNumber(value) {
		return Number(value);
	},
	ToInteger: function ToInteger(value) {
		var number = this.ToNumber(value);
		if ($isNaN(number)) { return 0; }
		if (number === 0 || !$isFinite(number)) { return number; }
		return sign(number) * Math.floor(Math.abs(number));
	},
	ToInt32: function ToInt32(x) {
		return this.ToNumber(x) >> 0;
	},
	ToUint32: function ToUint32(x) {
		return this.ToNumber(x) >>> 0;
	},
	ToUint16: function ToUint16(value) {
		var number = this.ToNumber(value);
		if ($isNaN(number) || number === 0 || !$isFinite(number)) { return 0; }
		var posInt = sign(number) * Math.floor(Math.abs(number));
		return mod(posInt, 0x10000);
	},
	ToString: function ToString(value) {
		return String(value);
	},
	ToObject: function ToObject(value) {
		this.CheckObjectCoercible(value);
		return Object(value);
	},
	CheckObjectCoercible: function CheckObjectCoercible(value, optMessage) {
		/* jshint eqnull:true */
		if (value == null) {
			throw new TypeError(optMessage || 'Cannot call method on ' + value);
		}
	},
	IsCallable: IsCallable,
	SameValue: function SameValue(x, y) {
		if (x === y) { // 0 === -0, but they are not identical.
			if (x === 0) { return 1 / x === 1 / y; }
			return true;
		}
        return $isNaN(x) && $isNaN(y);
	}
};

module.exports = ES5;
<commit_msg>Both `ES5.CheckObjectCoercible` and `ES6.RequireObjectCoercible` return the value if they don't throw.<commit_after>'use strict';

var $isNaN = Number.isNaN || function (a) { return a !== a; };
var $isFinite = require('./helpers/isFinite');

var sign = require('./helpers/sign');
var mod = require('./helpers/mod');

var IsCallable = require('is-callable');
var toPrimitive = require('es-to-primitive/es5');

// https://es5.github.io/#x9
var ES5 = {
	ToPrimitive: toPrimitive,

	ToBoolean: function ToBoolean(value) {
		return Boolean(value);
	},
	ToNumber: function ToNumber(value) {
		return Number(value);
	},
	ToInteger: function ToInteger(value) {
		var number = this.ToNumber(value);
		if ($isNaN(number)) { return 0; }
		if (number === 0 || !$isFinite(number)) { return number; }
		return sign(number) * Math.floor(Math.abs(number));
	},
	ToInt32: function ToInt32(x) {
		return this.ToNumber(x) >> 0;
	},
	ToUint32: function ToUint32(x) {
		return this.ToNumber(x) >>> 0;
	},
	ToUint16: function ToUint16(value) {
		var number = this.ToNumber(value);
		if ($isNaN(number) || number === 0 || !$isFinite(number)) { return 0; }
		var posInt = sign(number) * Math.floor(Math.abs(number));
		return mod(posInt, 0x10000);
	},
	ToString: function ToString(value) {
		return String(value);
	},
	ToObject: function ToObject(value) {
		this.CheckObjectCoercible(value);
		return Object(value);
	},
	CheckObjectCoercible: function CheckObjectCoercible(value, optMessage) {
		/* jshint eqnull:true */
		if (value == null) {
			throw new TypeError(optMessage || 'Cannot call method on ' + value);
		}
		return value;
	},
	IsCallable: IsCallable,
	SameValue: function SameValue(x, y) {
		if (x === y) { // 0 === -0, but they are not identical.
			if (x === 0) { return 1 / x === 1 / y; }
			return true;
		}
        return $isNaN(x) && $isNaN(y);
	}
};

module.exports = ES5;
<|endoftext|>


<commit_before>require([
	'jquery',
	'jsx/course_wizard/ListItems',
], function($, ListItems) {

	/**
	 * SYNOPSIS:
	 *
	 * This module modifies the checklist items that populate the "Setup Checklist" for
	 * course instructors.
	 *
	 * DESCRIPTION:
	 *
	 * The "Setup Checklist", or CourseWizard, as it is called in Canvas, is a component built
	 * using ReactJS and a syntax extension called JSX. The CourseWizard is composed of several
	 * sub-components, each of which is contained in a separate JSX file, which compiles down
	 * to native JS (the compilation step happens on the server).
	 *
	 * The components are passed environment values from the Courses Controller, and these values
	 * are accessed in the global ENV namespace. To better understand how the CourseWizard works,
	 * refer to these source files:
	 *
	 * 1) https://github.com/instructure/canvas-lms/blob/master/app/jsx/course_wizard/ListItems.jsx
	 * 2) https://github.com/instructure/canvas-lms/blob/master/app/jsx/course_wizard/ChecklistItem.jsx
	 * 3) https://github.com/instructure/canvas-lms/blob/master/app/jsx/course_wizard/CourseWizard.jsx
	 * 4) https://github.com/instructure/canvas-lms/blob/master/app/controllers/courses_controller.rb
     *
	 * To customize the list of items that appear in the CourseWizard, we load the ListItems
	 * module and then modify the desired items. ListItems is a reference to an array of objects,
	 * and CourseWizard uses this same reference at render time, so any changes we make here are
	 * visible to the CourseWizard component.
	 *
	 * CHANGE LIST:
	 *
	 * 1. Change text of "Import Content" to be more iSites-specific.
	 * 2. Remove "Add Students" from the checklist.
	 * 3. Change "Add TAs" nomenclature and link to the "Manage People" tool.
	 * 4. Insert item for the "Academic Integrity Policy" tool.
	 *
	 * NOTES:
	 *
	 * The external tool links have the tool ID hard coded for the "Harvard College/GSAS"
	 * account (account_id=39), since it would be too cumbersome to obtain the tool ID
	 * using the Canvas API. Ideally, these would be provided to the JS as environment
	 * variables, but since we don't have the ability to modify the server-side controller,
	 * that's not an option.
	 */

	var BASE_COURSE_URL = window.location.pathname; // i.e. /courses/12345
	var POLICY_WIZARD_TOOL_ID = 1513; // Tool ID for account_id=39
	var MANAGE_PEOPLE_TOOL_ID = 3958; // Tool ID for account_id=39

	//----- CHANGE #1 -----
	// REMOVE: Add Students item
	ListItems.splice(2, 1);

	//----- CHANGE #2 -----
	// CHANGE: Add TAs item
	var add_tas = ListItems[6];
	$.each(['text', 'title'], function(idx, prop) {
		add_tas[prop] = add_tas[prop].replace(/TA(s)?/g, "TF$1");
	});
	add_tas.url = BASE_COURSE_URL + "/external_tools/" + MANAGE_PEOPLE_TOOL_ID;

	//----- CHANGE #3 -----
	// INSERT: Academic Integrity Policy tool
	ListItems.splice(7, 0, {
		key:'policy_wizard',
		complete: false,
		title: "Customize academic integrity policy",
		text: "Customize the academic integrity policy for your course.",
		url: BASE_COURSE_URL + "/external_tools/" + POLICY_WIZARD_TOOL_ID,
		iconClass: 'icon-educators'
	});

	console.log("Customized Setup Checklist: ", ListItems);
});
<commit_msg>Fixed whitespace<commit_after>require([
	'jquery',
	'jsx/course_wizard/ListItems',
], function($, ListItems) {

	/**
	 * SYNOPSIS:
	 *
	 * This module modifies the checklist items that populate the "Setup Checklist" for
	 * course instructors.
	 *
	 * DESCRIPTION:
	 *
	 * The "Setup Checklist", or CourseWizard, as it is called in Canvas, is a component built
	 * using ReactJS and a syntax extension called JSX. The CourseWizard is composed of several
	 * sub-components, each of which is contained in a separate JSX file, which compiles down
	 * to native JS (the compilation step happens on the server).
	 *
	 * The components are passed environment values from the Courses Controller, and these values
	 * are accessed in the global ENV namespace. To better understand how the CourseWizard works,
	 * refer to these source files:
	 *
	 * 1) https://github.com/instructure/canvas-lms/blob/master/app/jsx/course_wizard/ListItems.jsx
	 * 2) https://github.com/instructure/canvas-lms/blob/master/app/jsx/course_wizard/ChecklistItem.jsx
	 * 3) https://github.com/instructure/canvas-lms/blob/master/app/jsx/course_wizard/CourseWizard.jsx
	 * 4) https://github.com/instructure/canvas-lms/blob/master/app/controllers/courses_controller.rb
	 *
	 * To customize the list of items that appear in the CourseWizard, we load the ListItems
	 * module and then modify the desired items. ListItems is a reference to an array of objects,
	 * and CourseWizard uses this same reference at render time, so any changes we make here are
	 * visible to the CourseWizard component.
	 *
	 * CHANGE LIST:
	 *
	 * 1. Change text of "Import Content" to be more iSites-specific.
	 * 2. Remove "Add Students" from the checklist.
	 * 3. Change "Add TAs" nomenclature and link to the "Manage People" tool.
	 * 4. Insert item for the "Academic Integrity Policy" tool.
	 *
	 * NOTES:
	 *
	 * The external tool links have the tool ID hard coded for the "Harvard College/GSAS"
	 * account (account_id=39), since it would be too cumbersome to obtain the tool ID
	 * using the Canvas API. Ideally, these would be provided to the JS as environment
	 * variables, but since we don't have the ability to modify the server-side controller,
	 * that's not an option.
	 */

	var BASE_COURSE_URL = window.location.pathname; // i.e. /courses/12345
	var POLICY_WIZARD_TOOL_ID = 1513; // Tool ID for account_id=39
	var MANAGE_PEOPLE_TOOL_ID = 3958; // Tool ID for account_id=39

	//----- CHANGE #1 -----
	// REMOVE: Add Students item
	ListItems.splice(2, 1);

	//----- CHANGE #2 -----
	// CHANGE: Add TAs item
	var add_tas = ListItems[6];
	$.each(['text', 'title'], function(idx, prop) {
		add_tas[prop] = add_tas[prop].replace(/TA(s)?/g, "TF$1");
	});
	add_tas.url = BASE_COURSE_URL + "/external_tools/" + MANAGE_PEOPLE_TOOL_ID;

	//----- CHANGE #3 -----
	// INSERT: Academic Integrity Policy tool
	ListItems.splice(7, 0, {
		key:'policy_wizard',
		complete: false,
		title: "Customize academic integrity policy",
		text: "Customize the academic integrity policy for your course.",
		url: BASE_COURSE_URL + "/external_tools/" + POLICY_WIZARD_TOOL_ID,
		iconClass: 'icon-educators'
	});

	console.log("Customized Setup Checklist: ", ListItems);
});
<|endoftext|>


<commit_before>/* Globals */
var kfRemoteDomain = 'http://localhost:8888';

/* onReady */
(function() {
  configure();
  onReady(function() { init() });

  function configure() {
    window['fig'] = window['fig'] || function() {(window['fig'].e = window['fig'].e || []).push(arguments)};
    window['fig'].reset = function(injectedConfig) {
      var config = getConfig();
      for (var attrname in injectedConfig) {
        config[attrname] = injectedConfig[attrname];
      }
      fig(config);
      var pill = document.getElementById('kfPillContainer');
      if (pill) {
        pill.parentNode.removeChild(pill);
      }
      var modal = document.getElementById('kfModal');
      if (modal) {
        modal.parentNode.removeChild(modal);
      }
      init();
    };
  }

  function init() {
    var config = getConfig();
    if (!config.email) {
      console.error('Error: KissFeedback requires an \'email\' to be provided.');
      return;
    }
    injectStylesheets();
    injectModal();
    injectPill();
  }

  function injectPill(button) {
    button = button || getDefaultFeedbackButton();
    var canary = document.getElementById('kfCanary');
    var display = canary.currentStyle
      ? canary.currentStyle['display']
      : window.getComputedStyle
        ? window.getComputedStyle(canary, null).getPropertyValue('display')
        : null;
    if (display == 'none') {
      document.body.appendChild(button);
      setOnClickListener(button.children[0], openFeedbackForm);
    } else {
      setTimeout(function() { injectPill(button) }, 250);
    }
  }

  function setOnClickListener(element, fn) {
    _attachListener('click', element, fn);
  }

  function setOnSubmitListener(element, fn) {
    _attachListener('submit', element, fn);
  }

  function setOnBlurListener(element, fn) {
    _attachListener('blur', element, fn);
  }

  function setOnKeyupListener(element, fn) {
    _attachListener('keyup', element, function(e) { fn(e || window.event) });
  }

  function _attachListener(event, element, fn) {
    if (element.addEventListener) {
      element.addEventListener(event, fn);
    } else if (element.attachEvent) {
      element.attachEvent("on" + event, fn);
    } else {
      element["on" + event] = fn;
    }
  }

  function getDefaultFeedbackButton() {
    var color = getPrimaryColor();
    var buttonContainer = document.createElement("div");
    buttonContainer.id = 'kfPillContainer';

    var button = document.createElement("div");
    button.innerHTML = getConfig().text || "Feedback";
    button.id = 'kfPill';
    button.className = "kfButton";
    button.style.background = color;
    buttonContainer.appendChild(button);

    var pillPosition = getConfig().position;
    var animation = getConfig().animationSpeed || '0.5s';
    if (pillPosition == 'topLeft') {
      buttonContainer.style.top = 0;
      buttonContainer.style.left = 0;
      button.style.animation = animation + ' slide-down';
    } else if (pillPosition == 'topRight') {
      buttonContainer.style.top = 0;
      buttonContainer.style.right = 0;
      button.style.animation = animation + ' slide-down';
    } else if (pillPosition == 'bottomLeft') {
      buttonContainer.style.bottom = 0;
      buttonContainer.style.left = 0;
      button.style.animation = animation + ' slide-up';
    } else {
      buttonContainer.style.bottom = 0;
      buttonContainer.style.right = 0;
      button.style.animation = animation + ' slide-up';
    }

    var css =
      '.kfButton:hover { background-color:' +
      shadeColor(color, -0.3) +
      ' !important; }';
    css +=
      '.kfButton:active { background-color:' +
      shadeColor(color, -0.4) +
      ' !important; }';
    css +=
      '.kfButton.kfLoading { background-color:' +
      shadeColor(color, 0.3) +
      ' !important; }';
    style = document.createElement('style');
    if (style.styleSheet) {
      style.styleSheet.cssText = css;
    } else {
      style.appendChild(document.createTextNode(css));
    }
    document.getElementsByTagName('head')[0].appendChild(style);
    return buttonContainer;
  }

  /* onClickListener. Open up feedback form for user. */
  function openFeedbackForm(event) {
    var modal = document.getElementById('kfModal') || injectModal();
    modal.style.display = "block";
    document.getElementById("kfEmail").focus();
  }

  function injectModal() {
    var modal = document.createElement("div");
    modal.id = 'kfModal';
    modal.style.display = 'none';
    document.body.appendChild(modal);
    setOnClickListener(modal, function(e) {
      if (e.target == modal) modal.style.display = "none";
    });

    var modalContent = document.createElement("div");
    modalContent.id = "kfModalContent";
    modal.appendChild(modalContent);

    var form = document.createElement('form');
    form.method = "post";
    form.action = kfRemoteDomain + "/feedback";
    form.id = "kfForm";
    modalContent.appendChild(form);
    setOnSubmitListener(
      form,
      function(e) {
        e.preventDefault();
        if (validateForm()) {
          ajaxSubmit(form, onSuccess, onFailure);
          formIsSending(true);
        }
        return false;
      });

    var header = document.createElement("header");
    header.innerHTML = getConfig().prompt || "How can we help?";
    form.appendChild(header);

    var fieldset = document.createElement("fieldset");
    form.appendChild(fieldset);

    var email = document.createElement("input");
    email.type = "email";
    email.name = "from_email";
    email.placeholder = "Your Email";
    email.className = 'kfField';
    email.autofocus = true;
    email.id = "kfEmail";
    var emailSection = document.createElement("section");
    emailSection.appendChild(email);
    fieldset.appendChild(emailSection);
    email.focus();
    setOnBlurListener(email, validateEmail);
    var email_check = document.createElement("div");
    email_check.className = "kfRequirements";
    email_check.innerHTML = "Must be a valid email address.";
    emailSection.appendChild(email_check);

    var textarea = document.createElement("textarea");
    textarea.rows = "4";
    textarea.placeholder = "Message";
    textarea.className = 'kfField';
    textarea.name = "message";
    textarea.id = "kfTextArea";
    var taSection = document.createElement("section");
    taSection.appendChild(textarea);
    setOnKeyupListener(textarea, clearMessageErrorIfValid);
    fieldset.appendChild(taSection);
    var text_check = document.createElement("div");
    text_check.className = "kfRequirements";
    text_check.innerHTML = "Must not be empty.";
    taSection.appendChild(text_check);

    var footer = document.createElement("footer");
    footer.className = "kfFooter";
    form.appendChild(footer);

    var account = document.createElement("input");
    account.type = "hidden";
    account.name = "to_email";
    account.value = getConfig().email;
    footer.appendChild(account);

    var logo = document.createElement("a");
    logo.id = "kfLogo";
    logo.href = kfRemoteDomain;
    logo.target = "_blank";
    footer.appendChild(logo);

    var logoImg = document.createElement("img");
    logoImg.src = kfRemoteDomain + "/static/img/fig.png";
    logoImg.width = "25";
    logoImg.alt = "Fig logo";
    logo.appendChild(logoImg);

    var logoText = document.createElement("div");
    logoText.className = "kfLogoText";
    logoText.innerHTML = "Fig";
    logo.appendChild(logoText);

    var error = document.createElement("div");
    error.id = 'kfError';
    error.innerHTML = 'Unexpected error occurred. Please try again later.';
    footer.appendChild(error);

    var button = document.createElement("button");
    button.type = "submit";
    button.innerHTML = "Send";
    button.className = 'kfButton';
    button.id = "kfSubmitButton";
    button.style.background = getPrimaryColor();
    footer.appendChild(button);
    setOnClickListener(button, function(e) { validateForm() });

    var canary = document.createElement("div");
    canary.id = 'kfCanary';
    modal.appendChild(canary);

    return modal;
  }

  function resetForm() {
    var modal = document.getElementById('kfModal');
    modal.className = "kfSuccess";
    setTimeout(function() { modal.parentNode.removeChild(modal) }, 500);
  }

  function validateForm() {
    document.getElementById("kfError").className = "";
    var valid = validateEmail();
    return validateMessage() && valid;
  }

  function validateEmail(blur = false) {
    var email = document.getElementById("kfEmail");
    var re = /\S+@\S+\.\S+/;
    var value = email.value && email.value.trim();
    if (value) {
      email.value = value;
    }
    if ((value || !blur) && !re.test(email.value)) {
      email.className += " kfInvalid";
      return false;
    }
    email.className = "kfField";
    return true;
  }

  function validateMessage() {
    var message = document.getElementById("kfTextArea");
    if (message.value.trim() == '') {
      message.className += " kfInvalid"
      return false;
    }
    message.className = "kfField";
    return true;
  }

  function clearMessageErrorIfValid() {
    var message = document.getElementById("kfTextArea");
    if (message.className.includes("kfInvalid")) {
      validateMessage();
    }
  }

  function formIsSending(disable) {
    var email = document.getElementById("kfEmail");
    var message = document.getElementById("kfTextArea");
    var button = document.getElementById("kfSubmitButton");
    email.disabled = disable;
    message.disabled = disable;
    button.disabled = disable;
    button.innerHTML = disable ? 'Sending...' : 'Send';
    if (disable) {
      button.className += " kfLoading";
    } else {
      button.className = "kfButton";
    }
  }

  function onSuccess(xhr) {
    if (xhr.currentTarget.responseText != "1") {
      onFailure(xhr);
      return;
    }
    var button = document.getElementById('kfSubmitButton');
    button.innerHTML = "Sent!";
    button.className += " kfDone";
    setTimeout(resetForm, 500);
  }

  function onFailure(xhr) {
    formIsSending(false);
    document.getElementById('kfError').className = "kfShow";
  }

  /* $.ready x-browser substitute. see http://stackoverflow.com/a/30319853 */
  function onReady(callback) {
    (!isConfigured() || /in/.test(document.readyState))
      ? setTimeout(function() { onReady(callback); }, 250)
      : callback();
  }

  function stringToNodes(html_string) {
    var div = document.createElement('div');
    div.innerHTML = html_string;
    return div;
  }

  /* class selector. falling back to dustin diaz method on older browsers */
  function getElementsByClassName(classname) {
    if (document.getElementsByClassName) {
      return document.getElementsByClassName(classname);
    }
    var matches = [];
    var candidates = document.getElementsByTagName("*");
    var pattern = new RegExp("(^|\\s)" + classname + "(\\s|$)");
    for (var i = 0; i < candidates.length; i++) {
      if (pattern.test(candidates[i].className) ) {
        matches.push(candidates[i]);
      }
    }
    return matches;
  }

  /* Calculate darker shade of color for hover. See http://stackoverflow.com/a/13542669 */
  function shadeColor(color, percent) {
    var f=parseInt(color.slice(1),16), t=percent<0?0:255,p=percent<0?percent*-1:percent,R=f>>16,G=f>>8&0x00FF,B=f&0x0000FF;
    return "#"+(0x1000000+(Math.round((t-R)*p)+R)*0x10000+(Math.round((t-G)*p)+G)*0x100+(Math.round((t-B)*p)+B)).toString(16).slice(1);
  }

  function isConfigured() {
    return window['fig'] !== undefined;
  }

  function getConfig() {
    var args = (window['fig'] && window['fig'].e) || [];
    return args.length > 0 ? args[0][0] : {};
  }

  function getPrimaryColor() {
    return getConfig().color || '#0064cd';
  }

  function injectStylesheets() {
    injectStyle('kfTextStyle', "https://fonts.googleapis.com/css?family=Pacifico");
    injectStyle('kfFigStyle', kfRemoteDomain + '/static/lib/fig.css');
  }

  function injectStyle(id, href) {
    if (document.getElementById(id)) {
      return;
    }
    var link = document.createElement('link');
    link.id = id;
    link.rel = 'stylesheet';
    link.href = href;
    document.getElementsByTagName('head')[0].appendChild(link);
  }

  /* See http://stackoverflow.com/a/34296482 */
  function ajaxSubmit(form, success_callback, failure_callback) {
    var xhr = new XMLHttpRequest();
    var params = [].filter.call(form.elements, function (el) {
        return !(el.type in ['checkbox', 'radio']) || el.checked;
        })
      .filter(function(el) { return !!el.name; }) //Nameless elements die.
      .filter(function(el) { return !el.disabled; }) //Disabled elements die.
      .map(function(el) {
        return encodeURIComponent(el.name) + '=' + encodeURIComponent(el.value);
        })
      .join('&'); //Then join all the strings by &
    xhr.open("POST", form.action);
    xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    xhr.onload = success_callback.bind(xhr);
    xhr.onerror = failure_callback && failure_callback.bind(xhr);
    xhr.send(params);
  }

  String.prototype.trim = function() {
    return this.replace(/^\s+|\s+$/g,"");
  }
})();
<commit_msg>Set to proper prod url<commit_after>/* Globals */
var kfRemoteDomain = 'https://www.usefig.com';

/* onReady */
(function() {
  configure();
  onReady(function() { init() });

  function configure() {
    window['fig'] = window['fig'] || function() {(window['fig'].e = window['fig'].e || []).push(arguments)};
    window['fig'].reset = function(injectedConfig) {
      var config = getConfig();
      for (var attrname in injectedConfig) {
        config[attrname] = injectedConfig[attrname];
      }
      fig(config);
      var pill = document.getElementById('kfPillContainer');
      if (pill) {
        pill.parentNode.removeChild(pill);
      }
      var modal = document.getElementById('kfModal');
      if (modal) {
        modal.parentNode.removeChild(modal);
      }
      init();
    };
  }

  function init() {
    var config = getConfig();
    if (!config.email) {
      console.error('Error: KissFeedback requires an \'email\' to be provided.');
      return;
    }
    injectStylesheets();
    injectModal();
    injectPill();
  }

  function injectPill(button) {
    button = button || getDefaultFeedbackButton();
    var canary = document.getElementById('kfCanary');
    var display = canary.currentStyle
      ? canary.currentStyle['display']
      : window.getComputedStyle
        ? window.getComputedStyle(canary, null).getPropertyValue('display')
        : null;
    if (display == 'none') {
      document.body.appendChild(button);
      setOnClickListener(button.children[0], openFeedbackForm);
    } else {
      setTimeout(function() { injectPill(button) }, 250);
    }
  }

  function setOnClickListener(element, fn) {
    _attachListener('click', element, fn);
  }

  function setOnSubmitListener(element, fn) {
    _attachListener('submit', element, fn);
  }

  function setOnBlurListener(element, fn) {
    _attachListener('blur', element, fn);
  }

  function setOnKeyupListener(element, fn) {
    _attachListener('keyup', element, function(e) { fn(e || window.event) });
  }

  function _attachListener(event, element, fn) {
    if (element.addEventListener) {
      element.addEventListener(event, fn);
    } else if (element.attachEvent) {
      element.attachEvent("on" + event, fn);
    } else {
      element["on" + event] = fn;
    }
  }

  function getDefaultFeedbackButton() {
    var color = getPrimaryColor();
    var buttonContainer = document.createElement("div");
    buttonContainer.id = 'kfPillContainer';

    var button = document.createElement("div");
    button.innerHTML = getConfig().text || "Feedback";
    button.id = 'kfPill';
    button.className = "kfButton";
    button.style.background = color;
    buttonContainer.appendChild(button);

    var pillPosition = getConfig().position;
    var animation = getConfig().animationSpeed || '0.5s';
    if (pillPosition == 'topLeft') {
      buttonContainer.style.top = 0;
      buttonContainer.style.left = 0;
      button.style.animation = animation + ' slide-down';
    } else if (pillPosition == 'topRight') {
      buttonContainer.style.top = 0;
      buttonContainer.style.right = 0;
      button.style.animation = animation + ' slide-down';
    } else if (pillPosition == 'bottomLeft') {
      buttonContainer.style.bottom = 0;
      buttonContainer.style.left = 0;
      button.style.animation = animation + ' slide-up';
    } else {
      buttonContainer.style.bottom = 0;
      buttonContainer.style.right = 0;
      button.style.animation = animation + ' slide-up';
    }

    var css =
      '.kfButton:hover { background-color:' +
      shadeColor(color, -0.3) +
      ' !important; }';
    css +=
      '.kfButton:active { background-color:' +
      shadeColor(color, -0.4) +
      ' !important; }';
    css +=
      '.kfButton.kfLoading { background-color:' +
      shadeColor(color, 0.3) +
      ' !important; }';
    style = document.createElement('style');
    if (style.styleSheet) {
      style.styleSheet.cssText = css;
    } else {
      style.appendChild(document.createTextNode(css));
    }
    document.getElementsByTagName('head')[0].appendChild(style);
    return buttonContainer;
  }

  /* onClickListener. Open up feedback form for user. */
  function openFeedbackForm(event) {
    var modal = document.getElementById('kfModal') || injectModal();
    modal.style.display = "block";
    document.getElementById("kfEmail").focus();
  }

  function injectModal() {
    var modal = document.createElement("div");
    modal.id = 'kfModal';
    modal.style.display = 'none';
    document.body.appendChild(modal);
    setOnClickListener(modal, function(e) {
      if (e.target == modal) modal.style.display = "none";
    });

    var modalContent = document.createElement("div");
    modalContent.id = "kfModalContent";
    modal.appendChild(modalContent);

    var form = document.createElement('form');
    form.method = "post";
    form.action = kfRemoteDomain + "/feedback";
    form.id = "kfForm";
    modalContent.appendChild(form);
    setOnSubmitListener(
      form,
      function(e) {
        e.preventDefault();
        if (validateForm()) {
          ajaxSubmit(form, onSuccess, onFailure);
          formIsSending(true);
        }
        return false;
      });

    var header = document.createElement("header");
    header.innerHTML = getConfig().prompt || "How can we help?";
    form.appendChild(header);

    var fieldset = document.createElement("fieldset");
    form.appendChild(fieldset);

    var email = document.createElement("input");
    email.type = "email";
    email.name = "from_email";
    email.placeholder = "Your Email";
    email.className = 'kfField';
    email.autofocus = true;
    email.id = "kfEmail";
    var emailSection = document.createElement("section");
    emailSection.appendChild(email);
    fieldset.appendChild(emailSection);
    email.focus();
    setOnBlurListener(email, validateEmail);
    var email_check = document.createElement("div");
    email_check.className = "kfRequirements";
    email_check.innerHTML = "Must be a valid email address.";
    emailSection.appendChild(email_check);

    var textarea = document.createElement("textarea");
    textarea.rows = "4";
    textarea.placeholder = "Message";
    textarea.className = 'kfField';
    textarea.name = "message";
    textarea.id = "kfTextArea";
    var taSection = document.createElement("section");
    taSection.appendChild(textarea);
    setOnKeyupListener(textarea, clearMessageErrorIfValid);
    fieldset.appendChild(taSection);
    var text_check = document.createElement("div");
    text_check.className = "kfRequirements";
    text_check.innerHTML = "Must not be empty.";
    taSection.appendChild(text_check);

    var footer = document.createElement("footer");
    footer.className = "kfFooter";
    form.appendChild(footer);

    var account = document.createElement("input");
    account.type = "hidden";
    account.name = "to_email";
    account.value = getConfig().email;
    footer.appendChild(account);

    var logo = document.createElement("a");
    logo.id = "kfLogo";
    logo.href = kfRemoteDomain;
    logo.target = "_blank";
    footer.appendChild(logo);

    var logoImg = document.createElement("img");
    logoImg.src = kfRemoteDomain + "/static/img/fig.png";
    logoImg.width = "25";
    logoImg.alt = "Fig logo";
    logo.appendChild(logoImg);

    var logoText = document.createElement("div");
    logoText.className = "kfLogoText";
    logoText.innerHTML = "Fig";
    logo.appendChild(logoText);

    var error = document.createElement("div");
    error.id = 'kfError';
    error.innerHTML = 'Unexpected error occurred. Please try again later.';
    footer.appendChild(error);

    var button = document.createElement("button");
    button.type = "submit";
    button.innerHTML = "Send";
    button.className = 'kfButton';
    button.id = "kfSubmitButton";
    button.style.background = getPrimaryColor();
    footer.appendChild(button);
    setOnClickListener(button, function(e) { validateForm() });

    var canary = document.createElement("div");
    canary.id = 'kfCanary';
    modal.appendChild(canary);

    return modal;
  }

  function resetForm() {
    var modal = document.getElementById('kfModal');
    modal.className = "kfSuccess";
    setTimeout(function() { modal.parentNode.removeChild(modal) }, 500);
  }

  function validateForm() {
    document.getElementById("kfError").className = "";
    var valid = validateEmail();
    return validateMessage() && valid;
  }

  function validateEmail(blur = false) {
    var email = document.getElementById("kfEmail");
    var re = /\S+@\S+\.\S+/;
    var value = email.value && email.value.trim();
    if (value) {
      email.value = value;
    }
    if ((value || !blur) && !re.test(email.value)) {
      email.className += " kfInvalid";
      return false;
    }
    email.className = "kfField";
    return true;
  }

  function validateMessage() {
    var message = document.getElementById("kfTextArea");
    if (message.value.trim() == '') {
      message.className += " kfInvalid"
      return false;
    }
    message.className = "kfField";
    return true;
  }

  function clearMessageErrorIfValid() {
    var message = document.getElementById("kfTextArea");
    if (message.className.includes("kfInvalid")) {
      validateMessage();
    }
  }

  function formIsSending(disable) {
    var email = document.getElementById("kfEmail");
    var message = document.getElementById("kfTextArea");
    var button = document.getElementById("kfSubmitButton");
    email.disabled = disable;
    message.disabled = disable;
    button.disabled = disable;
    button.innerHTML = disable ? 'Sending...' : 'Send';
    if (disable) {
      button.className += " kfLoading";
    } else {
      button.className = "kfButton";
    }
  }

  function onSuccess(xhr) {
    if (xhr.currentTarget.responseText != "1") {
      onFailure(xhr);
      return;
    }
    var button = document.getElementById('kfSubmitButton');
    button.innerHTML = "Sent!";
    button.className += " kfDone";
    setTimeout(resetForm, 500);
  }

  function onFailure(xhr) {
    formIsSending(false);
    document.getElementById('kfError').className = "kfShow";
  }

  /* $.ready x-browser substitute. see http://stackoverflow.com/a/30319853 */
  function onReady(callback) {
    (!isConfigured() || /in/.test(document.readyState))
      ? setTimeout(function() { onReady(callback); }, 250)
      : callback();
  }

  function stringToNodes(html_string) {
    var div = document.createElement('div');
    div.innerHTML = html_string;
    return div;
  }

  /* class selector. falling back to dustin diaz method on older browsers */
  function getElementsByClassName(classname) {
    if (document.getElementsByClassName) {
      return document.getElementsByClassName(classname);
    }
    var matches = [];
    var candidates = document.getElementsByTagName("*");
    var pattern = new RegExp("(^|\\s)" + classname + "(\\s|$)");
    for (var i = 0; i < candidates.length; i++) {
      if (pattern.test(candidates[i].className) ) {
        matches.push(candidates[i]);
      }
    }
    return matches;
  }

  /* Calculate darker shade of color for hover. See http://stackoverflow.com/a/13542669 */
  function shadeColor(color, percent) {
    var f=parseInt(color.slice(1),16), t=percent<0?0:255,p=percent<0?percent*-1:percent,R=f>>16,G=f>>8&0x00FF,B=f&0x0000FF;
    return "#"+(0x1000000+(Math.round((t-R)*p)+R)*0x10000+(Math.round((t-G)*p)+G)*0x100+(Math.round((t-B)*p)+B)).toString(16).slice(1);
  }

  function isConfigured() {
    return window['fig'] !== undefined;
  }

  function getConfig() {
    var args = (window['fig'] && window['fig'].e) || [];
    return args.length > 0 ? args[0][0] : {};
  }

  function getPrimaryColor() {
    return getConfig().color || '#0064cd';
  }

  function injectStylesheets() {
    injectStyle('kfTextStyle', "https://fonts.googleapis.com/css?family=Pacifico");
    injectStyle('kfFigStyle', kfRemoteDomain + '/static/lib/fig.css');
  }

  function injectStyle(id, href) {
    if (document.getElementById(id)) {
      return;
    }
    var link = document.createElement('link');
    link.id = id;
    link.rel = 'stylesheet';
    link.href = href;
    document.getElementsByTagName('head')[0].appendChild(link);
  }

  /* See http://stackoverflow.com/a/34296482 */
  function ajaxSubmit(form, success_callback, failure_callback) {
    var xhr = new XMLHttpRequest();
    var params = [].filter.call(form.elements, function (el) {
        return !(el.type in ['checkbox', 'radio']) || el.checked;
        })
      .filter(function(el) { return !!el.name; }) //Nameless elements die.
      .filter(function(el) { return !el.disabled; }) //Disabled elements die.
      .map(function(el) {
        return encodeURIComponent(el.name) + '=' + encodeURIComponent(el.value);
        })
      .join('&'); //Then join all the strings by &
    xhr.open("POST", form.action);
    xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    xhr.onload = success_callback.bind(xhr);
    xhr.onerror = failure_callback && failure_callback.bind(xhr);
    xhr.send(params);
  }

  String.prototype.trim = function() {
    return this.replace(/^\s+|\s+$/g,"");
  }
})();
<|endoftext|>


<commit_before>// 'use strict';
var argv = require('minimist')(process.argv.slice(2), {
  alias: {
    v: 'verbose',
    d: 'debug'
  }
});

var utils = require('./lib/utils');
if (argv.hasOwnProperty('verbose') && utils.isFalsey(argv.verbose)) {
  argv.verbose = false;
}
if (argv.hasOwnProperty('debug') && utils.isFalsey(argv.debug)) {
  argv.debug = false;
}

var Verbalize = require('./');
var colors = require('./plugins/colors');
var styles = require('./plugins/styles');
var isEnabled = require('./plugins/is-enabled');

var logger = new Verbalize(utils.extend({}, argv));
logger.use(colors());
logger.use(styles());
logger.use(isEnabled());

logger.define('process', function(stats) {
  var modes = stats.modes.map(function(mode) {
    return mode.name;
  });

  if (this.isEnabled(modes)) {
    var res = stats.modes.reduce(function(acc, mode) {
      return mode.fn(acc);
    }, stats.args);
    res = stats.modifiers.reduce(function(acc, modifier) {
      return this.stylize(modifier, acc);
    }.bind(this), res);
    this.writeln.apply(this, res);
  }
});

/**
 * Logger modes
 */

// just an option setting
logger.mode('verbose');

// use this as a negative value
logger.mode('not', {type: 'negative'});

// option setting but allows modifying the content
logger.mode('debug', function(msg) {
  return '[debug]: ' + msg;
});

logger.on('*', function(stats) {
  this.process(stats);
});

logger
  .info('this is a normal info message') // always logged
  .verbose.info('this is a verbose message') // logged when `options.verbose === true`
  .not.verbose.info('this is a not.verbose message') // logged when `options.verbose === false`
  .not.not.verbose.info('this is a not.not.verbose message') // logged when `options.verbose === true`
  .not.verbose.not.info('this is a not.verbose.not message') // logged when `options.verbose === true`
  .writeln();

logger
  .verbose.error('--- VERBOSE INFO---').not.verbose.subhead('--- IMPORTANT INFO ---')
  .verbose.inform('inform')
  .verbose.info('info')
  .verbose.warn('warn')
  .verbose.error('error').not.verbose.error('error')
  .verbose.success('success').not.verbose.success('success')
  .writeln();

logger
  .verbose.red.subhead('--- VERBOSE INFO---').not.verbose.subhead('--- IMPORTANT INFO ---')
  .verbose.yellow.inform.info.warn.error.success('some verbose information')
  .not.verbose.error.success('some not verbose information')
  .writeln();

logger.green('use a style directly');
logger.debug.yellow('this is a debug option');

logger.verbose('this is directly in verbose')
  .not.verbose('this is directly in not verbose')
  .debug('this is directly in debug')
  .not.debug('this is directly in not debug');
<commit_msg>add rainbow plugin to example<commit_after>// 'use strict';
var argv = require('minimist')(process.argv.slice(2), {
  alias: {
    v: 'verbose',
    d: 'debug'
  }
});

var utils = require('./lib/utils');
if (argv.hasOwnProperty('verbose') && utils.isFalsey(argv.verbose)) {
  argv.verbose = false;
}
if (argv.hasOwnProperty('debug') && utils.isFalsey(argv.debug)) {
  argv.debug = false;
}

var Verbalize = require('./');
var colors = require('./plugins/colors');
var styles = require('./plugins/styles');
var rainbow = require('./plugins/rainbow');
var isEnabled = require('./plugins/is-enabled');

var logger = new Verbalize(utils.extend({}, argv));
logger.use(colors());
logger.use(styles());
logger.use(rainbow());
logger.use(isEnabled());

logger.define('process', function(stats) {
  var modes = stats.modes.map(function(mode) {
    return mode.name;
  });

  if (this.isEnabled(modes)) {
    var res = stats.modes.reduce(function(acc, mode) {
      return mode.fn(acc);
    }, stats.args);
    res = stats.modifiers.reduce(function(acc, modifier) {
      return this.stylize(modifier, acc);
    }.bind(this), res);
    this.writeln.apply(this, res);
  }
});

/**
 * Logger modes
 */

// just an option setting
logger.mode('verbose');

// use this as a negative value
logger.mode('not', {type: 'negative'});
logger.mode('or', {type: 'negative'});

// option setting but allows modifying the content
logger.mode('debug', function(msg) {
  return '[debug]: ' + msg;
});

logger.on('*', function(stats) {
  // console.log(stats);
  // if (stats.hasMode('debug')) {
  //   debug('base:' + stats.name).apply(null, stats.args);
  //   return;
  // }

  this.process(stats);
});

logger
  .info('this is a normal info message') // always logged
  .verbose.info('this is a verbose message') // logged when `options.verbose === true`
  .not.verbose.info('this is a not.verbose message') // logged when `options.verbose === false`
  .not.not.verbose.info('this is a not.not.verbose message') // logged when `options.verbose === true`
  .not.verbose.not.info('this is a not.verbose.not message') // logged when `options.verbose === true`
  .writeln();

logger
  .verbose.error('--- VERBOSE INFO---').not.verbose.subhead('--- IMPORTANT INFO ---')
  .verbose.inform('inform')
  .verbose.info('info')
  .verbose.warn('warn')
  .verbose.error('error').not.verbose.error('error')
  .verbose.success('success').not.verbose.success('success')
  .writeln();

logger
  .verbose.red.subhead('--- VERBOSE INFO---').not.verbose.subhead('--- IMPORTANT INFO ---')
  .verbose.yellow.inform.info.warn.error.success('some verbose information')
  .not.verbose.error.success('some not verbose information')
  .writeln();

logger.green('use a style directly');
logger.debug.yellow('this is a debug option');

logger.verbose('this is directly in verbose')
  .not.verbose('this is directly in not verbose')
  .debug('this is directly in debug')
  .not.debug('this is directly in not debug');

logger.rainbow('this is a rainbow!!!!');
<|endoftext|>