<commit_before>/*

Copyright 2008-2015 Clipperz Srl

This file is part of Clipperz, the online password manager.
For further information about its features and functionalities please
refer to http://www.clipperz.com.

* Clipperz is free software: you can redistribute it and/or modify it
  under the terms of the GNU Affero General Public License as published
  by the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

* Clipperz is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  See the GNU Affero General Public License for more details.

* You should have received a copy of the GNU Affero General Public
  License along with Clipperz. If not, see http://www.gnu.org/licenses/.

*/

"use strict";
Clipperz.Base.module('Clipperz.PM.UI.Components.ExtraFeatures');

Clipperz.PM.UI.Components.ExtraFeatures.PreferencesClass = React.createClass({

	propTypes: {
	},

	//============================================================================

	preference: function (aKeyPath) {
		return this.props['userInfo']['preferences'].getValue(aKeyPath);
	},

	setPreference: function (aKeyPath) {
		return function (anEvent) {
			var	value;
			var	target;

			target = anEvent.target;
			if (target.type == 'checkbox') {
				value = target.checked;
			} else if (target.type == 'text') {
				value = target.value;
			} else if (target.type == 'select-one') {
				value = target.value;
			}

			MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'setPreference', aKeyPath, value);
		}
	},

	//============================================================================

	checkboxClick: function (aRef) {
		return MochiKit.Base.bind(function (anEvent) {
//console.log("CHECKBOX CLICK", this, this.refs, this.refs[aRef]);
			this.refs[aRef].getDOMNode().click();
		}, this);
	},

	//----------------------------------------------------------------------------

	handleKeyDown: function (aKeyPath) {
		return MochiKit.Base.bind(function (anEvent) {
			var	value = anEvent.target.value;

console.log("HANDLE KEY DOWN", anEvent, anEvent.keyCode, value);
			if (anEvent.target.defaultValue != value) {
				switch (anEvent.keyCode) {
					case  9: // tab
					case 13: // enter	- TODO: edit next preference
						MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'setPreference', aKeyPath, value);
						anEvent.target.defaultValue = anEvent.target.value;
						break;
					case 27: // escape
console.log("ESCAPE");
						anEvent.target.value = anEvent.target.defaultValue;
						break;
				}
			}
		}, this);
	},

	//============================================================================

	render: function () {
		var result;
//console.log("PREFERENCES", this.props['userInfo']['preferences']);

		if (! this.props['userInfo']['preferences']) {
			result = React.DOM.p({}, "spinner...");
		} else {
			result = React.DOM.div({'className':'extraFeature preferences'}, [
				React.DOM.div({'className':'header'}, [
					React.DOM.h1({}, "Preferences"),
//					React.DOM.div({'className':'description'}, [
//						React.DOM.p({}, "Insert copy here..."),
//					])
				]),
				React.DOM.div({'className':'content'}, [
					React.DOM.ul({'className':'preferenceList'}, [
						React.DOM.li({'key': 'lock'}, [
							React.DOM.h3({'key':'1'}, "Lock"),
							React.DOM.div({'key':'2', 'className':'row two lockEnabled'}, [
								React.DOM.div({'className':'col one'}, [
									React.DOM.input({'type':'checkbox', 'checked':this.preference('lock.enabled'), 'onChange':this.setPreference('lock.enabled'), 'ref':'lock-enabled'}),
								]),
								React.DOM.div({'className':'col two'}, [
									React.DOM.span({'className':'clickable', 'onClick':this.checkboxClick('lock-enabled')}, "Enable auto-lock"),
								]),
							]),
							React.DOM.div({'key':'3', 'className':'row one lockTimeout'}, [
								React.DOM.p({'className':(this.preference('lock.enabled') ? 'enabled' : 'disabled')}, [
									React.DOM.span({}, "Lock timeout"),
									React.DOM.input({'type':'text', 'defaultValue':this.preference('lock.timeoutInMinutes'), 'onKeyDown':this.handleKeyDown('lock.timeoutInMinutes')}),
									React.DOM.span({'className':'timeUnit'}, "minutes"),
								])
							]),
						]),

						React.DOM.li({'key': 'passwordGenerator'}, [
							React.DOM.h3({'key':'1'}, "Password generator"),
							React.DOM.div({'key':'2', 'className':'row one passwordLength'}, [
								React.DOM.p({}, [
									React.DOM.span({}, "Password length"),
									React.DOM.input({'type':'text', 'defaultValue':this.preference('passwordGenerator.length'), 'onKeyDown':this.handleKeyDown('passwordGenerator.length')}),
									React.DOM.span({'className':'sizeUnit'}, "characters"),
								])
							]),
							React.DOM.div({'key':'3', 'className':'row one passwordCharSets'}, [
								React.DOM.p({'key':'label'}, "Characters"),
								React.DOM.ul({'key':'list'}, [
									React.DOM.li({'key':'A-Z'},   [ React.DOM.span({'className':'clickable', 'onClick':this.checkboxClick('A-Z')},   "A-Z"),   React.DOM.input({'type':'checkbox', 'checked':this.preference('passwordGenerator.characters.A-Z'),   'onChange':this.setPreference('passwordGenerator.characters.A-Z'),   'ref':'A-Z'}) ]),
									React.DOM.li({'key':'a-z'},   [ React.DOM.span({'className':'clickable', 'onClick':this.checkboxClick('a-z')},   "a-z"),   React.DOM.input({'type':'checkbox', 'checked':this.preference('passwordGenerator.characters.a-z'),   'onChange':this.setPreference('passwordGenerator.characters.a-z'),   'ref':'a-z'}) ]),
									React.DOM.li({'key':'0-9'},   [ React.DOM.span({'className':'clickable', 'onClick':this.checkboxClick('0-9')},   "0-9"),   React.DOM.input({'type':'checkbox', 'checked':this.preference('passwordGenerator.characters.0-9'),   'onChange':this.setPreference('passwordGenerator.characters.0-9'),   'ref':'0-9'}) ]),
									React.DOM.li({'key':'space'}, [ React.DOM.span({'className':'clickable', 'onClick':this.checkboxClick('space')}, "space"), React.DOM.input({'type':'checkbox', 'checked':this.preference('passwordGenerator.characters.space'), 'onChange':this.setPreference('passwordGenerator.characters.space'), 'ref':'space'}) ]),
									React.DOM.li({'key':'!#?'},   [ React.DOM.span({'className':'clickable', 'onClick':this.checkboxClick('!#?')},   "!#?"),   React.DOM.input({'type':'checkbox', 'checked':this.preference('passwordGenerator.characters.!#?'),   'onChange':this.setPreference('passwordGenerator.characters.!#?'),   'ref':'!#?'}) ]),
								])
							]),
						]),

						React.DOM.li({'key': 'language'}, [
							React.DOM.h3({'key':'1'}, "Language"),
							React.DOM.div({'key':'2', 'className':'row one language'}, [
								React.DOM.select({'value':this.preference('preferredLanguage'), 'onChange':this.setPreference('preferredLanguage')}, [
									React.DOM.option({'value':'en'}, "English"),
									React.DOM.option({'value':'fr'}, "Français"),
									React.DOM.option({'value':'it'}, "Italiano"),
								])
							]),
						]),

						React.DOM.li({'key': 'donationReminder'}, [
							React.DOM.h3({'key':'1'}, "Donation reminder"),
							React.DOM.div({'key':'2', 'className':'row two donationReminder'}, [
								React.DOM.div({'className':'col one'}, [
									React.DOM.input({'type':'checkbox', 'checked':this.preference('shouldShowDonationPanel'), 'onChange':this.setPreference('shouldShowDonationPanel'), 'ref':'shouldShowDonationPanel'}),
								]),
								React.DOM.div({'className':'col two'}, [
									React.DOM.span({'className':'clickable', 'onClick':this.checkboxClick('shouldShowDonationPanel')}, "Show donation reminder"),
								]),
							]),
						]),

					]),
				])
			]);
		}

		return result;
	},

	//============================================================================
});

Clipperz.PM.UI.Components.ExtraFeatures.Preferences = React.createFactory(Clipperz.PM.UI.Components.ExtraFeatures.PreferencesClass);
<commit_msg>Improved responsiveness using React default values<commit_after>/*

Copyright 2008-2015 Clipperz Srl

This file is part of Clipperz, the online password manager.
For further information about its features and functionalities please
refer to http://www.clipperz.com.

* Clipperz is free software: you can redistribute it and/or modify it
  under the terms of the GNU Affero General Public License as published
  by the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

* Clipperz is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  See the GNU Affero General Public License for more details.

* You should have received a copy of the GNU Affero General Public
  License along with Clipperz. If not, see http://www.gnu.org/licenses/.

*/

"use strict";
Clipperz.Base.module('Clipperz.PM.UI.Components.ExtraFeatures');

Clipperz.PM.UI.Components.ExtraFeatures.PreferencesClass = React.createClass({

	propTypes: {
	},

	//============================================================================

	preference: function (aKeyPath) {
		return this.props['userInfo']['preferences'].getValue(aKeyPath);
	},

	setPreference: function (aKeyPath) {
		return function (anEvent) {
			var	value;
			var	target;

			target = anEvent.target;
			if (target.type == 'checkbox') {
				value = target.checked;
				target.defaultChecked = value;
//			} else if (target.type == 'text') {
//				value = target.value;
//				target.defaultValue = value;
			} else if (target.type == 'select-one') {
				value = target.value;
				target.defaultValue = value;
			}

			MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'setPreference', aKeyPath, value);
		}
	},

	//============================================================================

	checkboxClick: function (aRef) {
		return MochiKit.Base.bind(function (anEvent) {
//console.log("CHECKBOX CLICK", this, this.refs, this.refs[aRef]);
			this.refs[aRef].getDOMNode().click();
		}, this);
	},

	//----------------------------------------------------------------------------

	handleKeyDown: function (aKeyPath) {
		return MochiKit.Base.bind(function (anEvent) {
			var	value = anEvent.target.value;

console.log("HANDLE KEY DOWN", anEvent, anEvent.keyCode, value);
			if (anEvent.target.defaultValue != value) {
				switch (anEvent.keyCode) {
					case  9: // tab
					case 13: // enter	- TODO: edit next preference
						MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'setPreference', aKeyPath, value);
						anEvent.target.defaultValue = anEvent.target.value;
						break;
					case 27: // escape
console.log("ESCAPE");
						anEvent.target.value = anEvent.target.defaultValue;
						break;
				}
			}
		}, this);
	},

	//============================================================================

	render: function () {
		var result;
//console.log("PREFERENCES", this.props['userInfo']['preferences']);

		if (! this.props['userInfo']['preferences']) {
			result = React.DOM.p({}, "spinner...");
		} else {
			result = React.DOM.div({'className':'extraFeature preferences'}, [
				React.DOM.div({'className':'header'}, [
					React.DOM.h1({}, "Preferences"),
//					React.DOM.div({'className':'description'}, [
//						React.DOM.p({}, "Insert copy here..."),
//					])
				]),
				React.DOM.div({'className':'content'}, [
					React.DOM.ul({'className':'preferenceList'}, [
						React.DOM.li({'key': 'lock'}, [
							React.DOM.h3({'key':'1'}, "Lock"),
							React.DOM.div({'key':'2', 'className':'row two lockEnabled'}, [
								React.DOM.div({'className':'col one'}, [
									React.DOM.input({'type':'checkbox', 'defaultChecked':this.preference('lock.enabled'), 'onChange':this.setPreference('lock.enabled'), 'ref':'lock-enabled'}),
								]),
								React.DOM.div({'className':'col two'}, [
									React.DOM.span({'className':'clickable', 'onClick':this.checkboxClick('lock-enabled')}, "Enable auto-lock"),
								]),
							]),
							React.DOM.div({'key':'3', 'className':'row one lockTimeout'}, [
								React.DOM.p({'className':(this.preference('lock.enabled') ? 'enabled' : 'disabled')}, [
									React.DOM.span({}, "Lock timeout"),
									React.DOM.input({'type':'text', 'defaultValue':this.preference('lock.timeoutInMinutes'), 'onKeyDown':this.handleKeyDown('lock.timeoutInMinutes')}),
									React.DOM.span({'className':'timeUnit'}, "minutes"),
								])
							]),
						]),

						React.DOM.li({'key': 'passwordGenerator'}, [
							React.DOM.h3({'key':'1'}, "Password generator"),
							React.DOM.div({'key':'2', 'className':'row one passwordLength'}, [
								React.DOM.p({}, [
									React.DOM.span({}, "Password length"),
									React.DOM.input({'type':'text', 'defaultValue':this.preference('passwordGenerator.length'), 'onKeyDown':this.handleKeyDown('passwordGenerator.length')}),
									React.DOM.span({'className':'sizeUnit'}, "characters"),
								])
							]),
							React.DOM.div({'key':'3', 'className':'row one passwordCharSets'}, [
								React.DOM.p({'key':'label'}, "Characters"),
								React.DOM.ul({'key':'list'}, [
									React.DOM.li({'key':'A-Z'},   [ React.DOM.span({'className':'clickable', 'onClick':this.checkboxClick('A-Z')},   "A-Z"),   React.DOM.input({'type':'checkbox', 'defaultChecked':this.preference('passwordGenerator.characters.A-Z'),   'onChange':this.setPreference('passwordGenerator.characters.A-Z'),   'ref':'A-Z'}) ]),
									React.DOM.li({'key':'a-z'},   [ React.DOM.span({'className':'clickable', 'onClick':this.checkboxClick('a-z')},   "a-z"),   React.DOM.input({'type':'checkbox', 'defaultChecked':this.preference('passwordGenerator.characters.a-z'),   'onChange':this.setPreference('passwordGenerator.characters.a-z'),   'ref':'a-z'}) ]),
									React.DOM.li({'key':'0-9'},   [ React.DOM.span({'className':'clickable', 'onClick':this.checkboxClick('0-9')},   "0-9"),   React.DOM.input({'type':'checkbox', 'defaultChecked':this.preference('passwordGenerator.characters.0-9'),   'onChange':this.setPreference('passwordGenerator.characters.0-9'),   'ref':'0-9'}) ]),
									React.DOM.li({'key':'space'}, [ React.DOM.span({'className':'clickable', 'onClick':this.checkboxClick('space')}, "space"), React.DOM.input({'type':'checkbox', 'defaultChecked':this.preference('passwordGenerator.characters.space'), 'onChange':this.setPreference('passwordGenerator.characters.space'), 'ref':'space'}) ]),
									React.DOM.li({'key':'!#?'},   [ React.DOM.span({'className':'clickable', 'onClick':this.checkboxClick('!#?')},   "!#?"),   React.DOM.input({'type':'checkbox', 'defaultChecked':this.preference('passwordGenerator.characters.!#?'),   'onChange':this.setPreference('passwordGenerator.characters.!#?'),   'ref':'!#?'}) ]),
								])
							]),
						]),

						React.DOM.li({'key': 'language'}, [
							React.DOM.h3({'key':'1'}, "Language"),
							React.DOM.div({'key':'2', 'className':'row one language'}, [
								React.DOM.select({'defaultValue':this.preference('preferredLanguage'), 'onChange':this.setPreference('preferredLanguage')}, [
									React.DOM.option({'value':'en'}, "English"),
									React.DOM.option({'value':'fr'}, "Français"),
									React.DOM.option({'value':'it'}, "Italiano"),
								])
							]),
						]),

						React.DOM.li({'key': 'donationReminder'}, [
							React.DOM.h3({'key':'1'}, "Donation reminder"),
							React.DOM.div({'key':'2', 'className':'row two donationReminder'}, [
								React.DOM.div({'className':'col one'}, [
									React.DOM.input({'type':'checkbox', 'defaultChecked':this.preference('shouldShowDonationPanel'), 'onChange':this.setPreference('shouldShowDonationPanel'), 'ref':'shouldShowDonationPanel'}),
								]),
								React.DOM.div({'className':'col two'}, [
									React.DOM.span({'className':'clickable', 'onClick':this.checkboxClick('shouldShowDonationPanel')}, "Show donation reminder"),
								]),
							]),
						]),

					]),
				])
			]);
		}

		return result;
	},

	//============================================================================
});

Clipperz.PM.UI.Components.ExtraFeatures.Preferences = React.createFactory(Clipperz.PM.UI.Components.ExtraFeatures.PreferencesClass);
<|endoftext|>


<commit_before>/*
Copyright 2016 Mozilla

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

import { createTab, setPageOrder } from '../../actions/main-actions';
import { getBestDropItem, getWebView } from '../../browser-util';

/* This is a dirty hack around Blink not supporting relatedTarget in dragleave
   events (https://bugs.chromium.org/p/chromium/issues/detail?id=159534).
   currentDragTarget should be correct for any dragleave handlers deeper in the
   DOM. */
let currentDragTarget;
window.addEventListener('dragenter', ev => currentDragTarget = ev.target, true);
window.addEventListener('dragleave', () => currentDragTarget = null);
window.addEventListener('dragend', () => currentDragTarget = null);

/**
 *
 */
export class TabDragDrop {
  constructor(page, pageOrder, pageIndex, ipcRenderer) {
    this.page = page;
    this.pageIndex = pageIndex;
    this.pageOrder = pageOrder;
    this.ipcRenderer = ipcRenderer;

    this.handlers = {
      onDragStart: this.onDragStart.bind(this),
      onDragEnd: this.onDragEnd.bind(this),
      onDragEnter: this.onDragEnter.bind(this),
      onDrop: this.onDrop.bind(this),
    };
  }

  onDragStart(e) {
    const { pageIndex, pageOrder } = this;

    const tabIndex = pageOrder.indexOf(pageIndex);
    // e.dataTransfer.setData('text/uri-list', page.location);
    // e.dataTransfer.setData('text/plain', page.location);
    e.dataTransfer.setData('application/vnd.mozilla.bh.page',
                           JSON.stringify({ pageIndex, tabIndex }));
    e.dataTransfer.effectAllowed = 'copyMove';
  }

  onDragEnd(e) {
    const { page, pageIndex } = this;

    if (e.dataTransfer.dropEffect === 'none') {
      const webview = getWebView(e.target.ownerDocument, pageIndex);
      const guestInstanceId = webview.guestinstance;
      this.ipcRenderer.send('tab-detach', { page, guestInstanceId });
    }
  }

  onDragEnter(e) {
    const item = getBestDropItem(e.dataTransfer);
    if (item.type === 'text/uri-list' || item.type === 'text/plain') {
      e.dataTransfer.dropEffect = 'copy';
      e.preventDefault();
    }
  }

  onDrop(e) {
    const { pageIndex } = this;

    const item = getBestDropItem(e.dataTransfer);
    if (item.type === 'text/uri-list' || item.type === 'text/plain') {
      const url = e.dataTransfer.getData(item.type);

      const webview = getWebView(e.target.ownerDocument, pageIndex);
      webview.setAttribute('src', url);

      e.dataTransfer.dropEffect = 'copy';
      e.preventDefault();
    }
  }
}

/**
 *
 */
export class TabBarDragDrop {
  constructor(pages, pageOrder, dispatch) {
    this.pages = pages;
    this.pageOrder = pageOrder;
    this.dispatch = dispatch;

    this.handlers = {
      onDragEnter: this.onDragEnter.bind(this),
      onDragOver: this.onDragOver.bind(this),
      onDragLeave: this.onDragLeave.bind(this),
      onDrop: this.onDrop.bind(this),
    };
  }

  onDragEnter(e) {
    const { pages, pageOrder } = this;

    if (e.defaultPrevented) {
      return;
    }

    const item = getBestDropItem(e.dataTransfer);
    if (!item) {
      return;
    }

    if (item.type === 'text/uri-list' || item.type === 'text/plain') {
      // A tab would have cancelled this event already so this is dragging to
      // a new tab
      e.dataTransfer.dropEffect = 'copy';
      e.preventDefault();
      return;
    }

    // Dropping a tab
    const dragData = JSON.parse(e.dataTransfer.getData('application/vnd.mozilla.bh.page'));

    // Find the tab target or default to the last tab
    const document = e.target.ownerDocument;
    let tabIndex = pageOrder.size - 1;
    for (let i = 0; i < pages.size ; i++) {
      const element = document.querySelector(`.browser-tab-${i}`);
      if (element.contains(e.target)) {
        tabIndex = pageOrder.indexOf(i);
        break;
      }
    }

    if (pageOrder[tabIndex] !== dragData.pageIndex) {
      // Mutate the tab order so the dragged page appears at tabIndex
      const newPageOrder = pageOrder.filter(p => p !== dragData.pageIndex);
      newPageOrder.splice(tabIndex, 0, dragData.pageIndex);
      this.dispatch(setPageOrder(newPageOrder));
    }

    e.dataTransfer.dropEffect = 'move';
    e.preventDefault();
  }

  onDragOver(e) {
    const item = getBestDropItem(e.dataTransfer);
    if (item) {
      e.preventDefault();
    }
  }

  onDragLeave(e) {
    const { pageOrder } = this;

    // If we've just entered a descendent then ignore the event
    if (e.currentTarget.contains(currentDragTarget)) {
      return;
    }

    const data = e.dataTransfer.getData('application/vnd.mozilla.bh.page');
    if (data) {
      const dragData = JSON.parse(data);
      // Move the tab back to its original index
      const newPageOrder = pageOrder.filter(p => p !== dragData.pageIndex);
      newPageOrder.splice(dragData.tabIndex, 0, dragData.pageIndex);
      this.dispatch(setPageOrder(newPageOrder));
    }
  }

  onDrop(e) {
    if (e.defaultPrevented) {
      return;
    }

    const item = getBestDropItem(e.dataTransfer);
    if (!item) {
      return;
    }
    e.preventDefault();

    if (item.type === 'text/uri-list' || item.type === 'text/plain') {
      // A tab would have cancelled this event already so this is dragging to
      // a new tab
      const url = e.dataTransfer.getData(item.type);
      createTab(url);
      e.dataTransfer.dropEffect = 'copy';
      return;
    }

    // Dropping a tab. The mutation will have already been taken care of in
    // onDragEnterTabBar
    e.dataTransfer.dropEffect = 'move';
  }
}
<commit_msg>No bug - eslint fix for semi-spacing<commit_after>/*
Copyright 2016 Mozilla

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

import { createTab, setPageOrder } from '../../actions/main-actions';
import { getBestDropItem, getWebView } from '../../browser-util';

/* This is a dirty hack around Blink not supporting relatedTarget in dragleave
   events (https://bugs.chromium.org/p/chromium/issues/detail?id=159534).
   currentDragTarget should be correct for any dragleave handlers deeper in the
   DOM. */
let currentDragTarget;
window.addEventListener('dragenter', ev => currentDragTarget = ev.target, true);
window.addEventListener('dragleave', () => currentDragTarget = null);
window.addEventListener('dragend', () => currentDragTarget = null);

/**
 *
 */
export class TabDragDrop {
  constructor(page, pageOrder, pageIndex, ipcRenderer) {
    this.page = page;
    this.pageIndex = pageIndex;
    this.pageOrder = pageOrder;
    this.ipcRenderer = ipcRenderer;

    this.handlers = {
      onDragStart: this.onDragStart.bind(this),
      onDragEnd: this.onDragEnd.bind(this),
      onDragEnter: this.onDragEnter.bind(this),
      onDrop: this.onDrop.bind(this),
    };
  }

  onDragStart(e) {
    const { pageIndex, pageOrder } = this;

    const tabIndex = pageOrder.indexOf(pageIndex);
    // e.dataTransfer.setData('text/uri-list', page.location);
    // e.dataTransfer.setData('text/plain', page.location);
    e.dataTransfer.setData('application/vnd.mozilla.bh.page',
                           JSON.stringify({ pageIndex, tabIndex }));
    e.dataTransfer.effectAllowed = 'copyMove';
  }

  onDragEnd(e) {
    const { page, pageIndex } = this;

    if (e.dataTransfer.dropEffect === 'none') {
      const webview = getWebView(e.target.ownerDocument, pageIndex);
      const guestInstanceId = webview.guestinstance;
      this.ipcRenderer.send('tab-detach', { page, guestInstanceId });
    }
  }

  onDragEnter(e) {
    const item = getBestDropItem(e.dataTransfer);
    if (item.type === 'text/uri-list' || item.type === 'text/plain') {
      e.dataTransfer.dropEffect = 'copy';
      e.preventDefault();
    }
  }

  onDrop(e) {
    const { pageIndex } = this;

    const item = getBestDropItem(e.dataTransfer);
    if (item.type === 'text/uri-list' || item.type === 'text/plain') {
      const url = e.dataTransfer.getData(item.type);

      const webview = getWebView(e.target.ownerDocument, pageIndex);
      webview.setAttribute('src', url);

      e.dataTransfer.dropEffect = 'copy';
      e.preventDefault();
    }
  }
}

/**
 *
 */
export class TabBarDragDrop {
  constructor(pages, pageOrder, dispatch) {
    this.pages = pages;
    this.pageOrder = pageOrder;
    this.dispatch = dispatch;

    this.handlers = {
      onDragEnter: this.onDragEnter.bind(this),
      onDragOver: this.onDragOver.bind(this),
      onDragLeave: this.onDragLeave.bind(this),
      onDrop: this.onDrop.bind(this),
    };
  }

  onDragEnter(e) {
    const { pages, pageOrder } = this;

    if (e.defaultPrevented) {
      return;
    }

    const item = getBestDropItem(e.dataTransfer);
    if (!item) {
      return;
    }

    if (item.type === 'text/uri-list' || item.type === 'text/plain') {
      // A tab would have cancelled this event already so this is dragging to
      // a new tab
      e.dataTransfer.dropEffect = 'copy';
      e.preventDefault();
      return;
    }

    // Dropping a tab
    const dragData = JSON.parse(e.dataTransfer.getData('application/vnd.mozilla.bh.page'));

    // Find the tab target or default to the last tab
    const document = e.target.ownerDocument;
    let tabIndex = pageOrder.size - 1;
    for (let i = 0; i < pages.size; i++) {
      const element = document.querySelector(`.browser-tab-${i}`);
      if (element.contains(e.target)) {
        tabIndex = pageOrder.indexOf(i);
        break;
      }
    }

    if (pageOrder[tabIndex] !== dragData.pageIndex) {
      // Mutate the tab order so the dragged page appears at tabIndex
      const newPageOrder = pageOrder.filter(p => p !== dragData.pageIndex);
      newPageOrder.splice(tabIndex, 0, dragData.pageIndex);
      this.dispatch(setPageOrder(newPageOrder));
    }

    e.dataTransfer.dropEffect = 'move';
    e.preventDefault();
  }

  onDragOver(e) {
    const item = getBestDropItem(e.dataTransfer);
    if (item) {
      e.preventDefault();
    }
  }

  onDragLeave(e) {
    const { pageOrder } = this;

    // If we've just entered a descendent then ignore the event
    if (e.currentTarget.contains(currentDragTarget)) {
      return;
    }

    const data = e.dataTransfer.getData('application/vnd.mozilla.bh.page');
    if (data) {
      const dragData = JSON.parse(data);
      // Move the tab back to its original index
      const newPageOrder = pageOrder.filter(p => p !== dragData.pageIndex);
      newPageOrder.splice(dragData.tabIndex, 0, dragData.pageIndex);
      this.dispatch(setPageOrder(newPageOrder));
    }
  }

  onDrop(e) {
    if (e.defaultPrevented) {
      return;
    }

    const item = getBestDropItem(e.dataTransfer);
    if (!item) {
      return;
    }
    e.preventDefault();

    if (item.type === 'text/uri-list' || item.type === 'text/plain') {
      // A tab would have cancelled this event already so this is dragging to
      // a new tab
      const url = e.dataTransfer.getData(item.type);
      createTab(url);
      e.dataTransfer.dropEffect = 'copy';
      return;
    }

    // Dropping a tab. The mutation will have already been taken care of in
    // onDragEnterTabBar
    e.dataTransfer.dropEffect = 'move';
  }
}
<|endoftext|>


<commit_before>import { cloneDeep, forEach, includes, set, unset, replace } from 'lodash';

/* eslint-disable consistent-return */

export default function setParallelAttribute(newAttribute) {
  if (newAttribute.params.target === this.props.modelName) {
    const parallelAttribute = cloneDeep(newAttribute);
    parallelAttribute.name = newAttribute.params.key;
    parallelAttribute.params.key = newAttribute.name;
    parallelAttribute.params.columnName = newAttribute.params.targetColumnName;
    parallelAttribute.params.targetColumnName = newAttribute.params.columnName;
    parallelAttribute.params.dominant = false;
    return parallelAttribute;
  }
  return;
}

export function setTempAttribute() {
  const newAttribute = this.props.modifiedDataAttribute;

  forEach(newAttribute.params, (value, key) => {
    if (includes(key, 'Value')) {
      set(newAttribute.params, replace(key, 'Value', ''), value);
      unset(newAttribute.params, key);
    }
  });

  return newAttribute;
}
<commit_msg>Fix edit temp attribute<commit_after>import { cloneDeep, forEach, includes, set, unset, replace } from 'lodash';

/* eslint-disable consistent-return */

export default function setParallelAttribute(newAttribute) {
  if (newAttribute.params.target === this.props.modelName) {
    const parallelAttribute = cloneDeep(newAttribute);
    parallelAttribute.name = newAttribute.params.key;
    parallelAttribute.params.key = newAttribute.name;
    parallelAttribute.params.columnName = newAttribute.params.targetColumnName;
    parallelAttribute.params.targetColumnName = newAttribute.params.columnName;
    parallelAttribute.params.dominant = false;
    return parallelAttribute;
  }
  return;
}

export function setTempAttribute() {
  const newAttribute = cloneDeep(this.props.modifiedDataAttribute);

  forEach(newAttribute.params, (value, key) => {
    if (includes(key, 'Value')) {
      set(newAttribute.params, replace(key, 'Value', ''), value);
      unset(newAttribute.params, key);
    }
  });

  return newAttribute;
}
<|endoftext|>


<commit_before>'use strict';

const fs = require('fs');
const path = require('path');
const marked = require('marked');

let pages = [];

const files = ['quickstart', 'events'];
for (let fileName of files) {
    const rawData = fs.readFileSync(path.resolve(__dirname, 'source', fileName + '.md'), {
        encoding: 'utf8'
    });
    const tokens = marked.lexer(rawData);

    let pageTitle = '';
    for (let token of tokens) {
        // heading 1
        if (token.type === 'heading' && token.depth === 1) {
            pageTitle = token.text;
        }
    }

    pages.push({
        title: pageTitle,
        id: fileName,
        file: fileName + '.html'
    });

    if (!fs.existsSync(path.resolve(__dirname, 'build'))) {
        fs.mkdirSync(path.resolve(__dirname, 'build'));
    }

    let html = '<template>\n' + marked(rawData) + '</template>';

    fs.writeFileSync(path.resolve(__dirname, 'build', fileName + '.html'), html);
}

fs.writeFileSync(path.resolve(__dirname, 'build', 'sponge-client-docs.json'), JSON.stringify(pages, null, 4), 'utf-8');
<commit_msg>Custom codeblock rendering<commit_after>'use strict';

const fs = require('fs');
const path = require('path');
const marked = require('marked');
const renderer = new marked.Renderer();

renderer.code = function(code, lang) {
    code = code.replace(/"/g, '"'); // replace double quotes with "

    return `<codeblock content.one-way="'${code}'" type.one-way="'${lang}'"></codeblock>`;
}

let pages = [];

const files = ['quickstart', 'events'];
for (let fileName of files) {
    const rawData = fs.readFileSync(path.resolve(__dirname, 'source', fileName + '.md'), {
        encoding: 'utf8'
    });
    const tokens = marked.lexer(rawData);

    let pageTitle = '';
    for (let token of tokens) {
        // heading 1
        if (token.type === 'heading' && token.depth === 1) {
            pageTitle = token.text;
        }
    }

    pages.push({
        title: pageTitle,
        id: fileName,
        file: fileName + '.html'
    });

    if (!fs.existsSync(path.resolve(__dirname, 'build'))) {
        fs.mkdirSync(path.resolve(__dirname, 'build'));
    }

    let html = `<template>\n<require from="../../codeblock"></require>\n${marked(rawData, {renderer})}</template>`;

    fs.writeFileSync(path.resolve(__dirname, 'build', fileName + '.html'), html);
}

fs.writeFileSync(path.resolve(__dirname, 'build', 'sponge-client-docs.json'), JSON.stringify(pages, null, 4), 'utf-8');
<|endoftext|>


<commit_before>angular.module("umbraco.directives")
    .directive('umbAutoFocus', function($timeout) {

        return function (scope, element, attrs) {

            var update = function() {
                //if it uses its default naming
                if (element.val() === "" || attrs.focusOnFilled) {
                    element.trigger("focus");
                }
            };

            var enabled = true;
            //check if there's a value for the attribute, if there is and it's false then we conditionally don't
            //use auto focus.
            if (attrs.umbAutoFocus) {
                attrs.$observe("umbAutoFocus", function (newVal) {
                    enabled = (newVal === "false" || newVal === 0 || newVal === false) ? false : true;
                });
            }

            if (enabled) {
                $timeout(function() {
                    update();
                });
            }
        };
});
<commit_msg>Fix broken auto-focus (#9004)<commit_after>angular.module("umbraco.directives")
    .directive('umbAutoFocus', function($timeout) {

        return function (scope, element, attrs) {

            var update = function() {
                //if it uses its default naming
                if (element.val() === "" || attrs.focusOnFilled) {
                    element.trigger("focus");
                }
            };

            //check if there's a value for the attribute, if there is and it's false then we conditionally don't
            //use auto focus.
            if (attrs.umbAutoFocus) {
                attrs.$observe("umbAutoFocus", function (newVal) {
                    var enabled = (newVal === "false" || newVal === 0 || newVal === false) ? false : true;
                    if (enabled) {
                        $timeout(function() {
                            update();
                        });
                    }
                });
            }

        };
});
<|endoftext|>


<commit_before>var express = require('express'),
    bodyParser = require('body-parser'),
    request = require('request'),
    app = express(),
    router = express.Router(),
    cors = require('cors'),
    cheerio = require('cheerio'),
    port = process.env.LATE_BUS_API_PORT || 8888,
    LATE_BUS_SOURCE_URL = 'http://transportation2.sandi.net/latebus.htm';

router.route('/latebuses').get(
    function(req, res) {
        var pos = -1,
            endPos = -1;

        request(LATE_BUS_SOURCE_URL, function (error, response, body) {
            var updateDate = undefined,
                updateTime = undefined,
                lateBusTable = undefined,
                delayedBuses = [],
                delayedBus = {},
                resp = {},
                count = 1;

            if (!error && response.statusCode == 200) {
                pos = body.indexOf('Late Buses for');
                if (pos === -1) {
                    // Fail could not find today
                    // TODO
                    return;
                }

                endPos = body.indexOf('', pos);
                if (endPos === -1) {
                    // Fail could not find end today marker
                    // TODO
                    return;
                }

                // Date: November 19, 2015
                updateDate = body.substring(pos + 15, endPos);
                updateDate = updateDate.trim();

                pos = body.indexOf('as of');
                if (pos === -1) {
                    // Fail could not find updated time
                    // TODO
                    return;
                }

                endPos = body.indexOf('', pos);
                updateTime = body.substring(pos + 6, endPos);
                updateTime = updateTime.trim();

                pos = body.indexOf('');
                if (pos === -1) {
                    // Fail could not find list start
                    // TODO
                    return;
                }

                endPos = body.indexOf('');
                if (endPos === -1) {
                    // Fail could not find list start
                    // TODO
                    return;
                }

                // Table with bus details in
                lateBusTable = body.substring(pos + 19, endPos);

                console.log(updateDate);
                console.log(updateTime);

                $ = cheerio.load(lateBusTable);

                $('TR').children('TD').each(function(idx, elem) {
                    var textValue = $(elem).text().trim();

                    if (textValue.length > 0) {
                        switch(count) {
                            case 1:
                                delayedBus.school = textValue;
                                break;
                            case 2:
                                delayedBus.route = textValue;
                                break;
                            case 3:
                                delayedBus.delay = parseInt(textValue);
                                break;
                            case 4:
                                delayedBus.scheduledStart = textValue;
                                break;
                            case 5:
                                delayedBus.scheduledEnd = textValue;
                        }

                        if (count === 5) {
                            delayedBuses.push(delayedBus);
                            delayedBus = {};
                            count = 1;
                        } else {
                            count++;
                        }
                    }
                });

                resp.delayedBuses = delayedBuses;
                resp.updateDate = updateDate;
                resp.updateTime = updateTime;
                // TODO updateTimestamp
                resp.updateTimestamp = 0;
                resp.source = LATE_BUS_SOURCE_URL;
                resp.phone = "(858) 496-8460";

                res.send(resp);
            }
        });
    }
);

app.use(cors());
app.use('/', router);
app.listen(port);
console.log('San Diego Late School Bus API Server listening on port ' + port);<commit_msg>Adds updated timestamp.<commit_after>var express = require('express'),
    bodyParser = require('body-parser'),
    request = require('request'),
    app = express(),
    router = express.Router(),
    cors = require('cors'),
    cheerio = require('cheerio'),
    port = process.env.LATE_BUS_API_PORT || 8888,
    LATE_BUS_SOURCE_URL = 'http://transportation2.sandi.net/latebus.htm';

router.route('/latebuses').get(
    function(req, res) {
        var pos = -1,
            endPos = -1;

        request(LATE_BUS_SOURCE_URL, function (error, response, body) {
            var updateDate = undefined,
                updateTime = undefined,
                updateTimestamp = 0,
                lateBusTable = undefined,
                delayedBuses = [],
                delayedBus = {},
                resp = {},
                count = 1;

            if (!error && response.statusCode == 200) {
                pos = body.indexOf('Late Buses for');
                if (pos === -1) {
                    // Fail could not find today
                    // TODO
                    return;
                }

                endPos = body.indexOf('', pos);
                if (endPos === -1) {
                    // Fail could not find end today marker
                    // TODO
                    return;
                }

                // Date: November 19, 2015
                updateDate = body.substring(pos + 15, endPos);
                updateDate = updateDate.trim();

                pos = body.indexOf('as of');
                if (pos === -1) {
                    // Fail could not find updated time
                    // TODO
                    return;
                }

                endPos = body.indexOf('', pos);
                updateTime = body.substring(pos + 6, endPos).trim();
                updateTimestamp = Date.parse(updateDate + ' ' + updateTime + ' -0800');

                pos = body.indexOf('');
                if (pos === -1) {
                    // Fail could not find list start
                    // TODO
                    return;
                }

                endPos = body.indexOf('');
                if (endPos === -1) {
                    // Fail could not find list start
                    // TODO
                    return;
                }

                // Table with bus details in
                lateBusTable = body.substring(pos + 19, endPos);

                $ = cheerio.load(lateBusTable);

                $('TR').children('TD').each(function(idx, elem) {
                    var textValue = $(elem).text().trim();

                    if (textValue.length > 0) {
                        switch(count) {
                            case 1:
                                delayedBus.school = textValue;
                                break;
                            case 2:
                                delayedBus.route = textValue;
                                break;
                            case 3:
                                delayedBus.delay = parseInt(textValue);
                                break;
                            case 4:
                                delayedBus.scheduledStart = textValue;
                                break;
                            case 5:
                                delayedBus.scheduledEnd = textValue;
                        }

                        if (count === 5) {
                            delayedBuses.push(delayedBus);
                            delayedBus = {};
                            count = 1;
                        } else {
                            count++;
                        }
                    }
                });

                resp.delayedBuses = delayedBuses;
                resp.updateDate = updateDate;
                resp.updateTime = updateTime;
                // TODO updateTimestamp
                resp.updateTimestamp = updateTimestamp;
                resp.source = LATE_BUS_SOURCE_URL;
                resp.phone = "(858) 496-8460";

                res.send(resp);
            }
        });
    }
);

app.use(cors());
app.use('/', router);
app.listen(port);
console.log('San Diego Late School Bus API Server listening on port ' + port);<|endoftext|>


<commit_before>var coldstart = true;
var startupTime = new Date();
var startupThreshold = 1000; // ms

var _atStartup = function() {
  // If startup time is less than startupThreshold ago then lets say this is
  // at startup.
  return (new Date() - startupTime < startupThreshold);
};

Push.setBadge = function(count) {
  document.addEventListener('deviceready', function() {
    // Helper
    var pushNotification = window.plugins.pushNotification;

    // If the set application badge is available
    if (typeof pushNotification.setApplicationIconBadgeNumber == 'function') {
      // Set the badge
      pushNotification.setApplicationIconBadgeNumber(function(result) {
        // Success callback
      }, function(err) {
        // Error callback
        Push.emit('error', { type: 'badge', error: err });
      }, count);

    }
  });
};

_parsePayload = function(value) {
  // Android actually parses payload into an object - this is not the case with
  // iOS (here is it just a string)
  if (value !== ''+value) value = JSON.stringify(value);

  // Run the string through ejson
  try {
    return EJSON.parse(value);
  } catch(err) {
    return { error: err };
  }
};

var onNotification = function(notification) {
  // alert('onNotification' + JSON.stringify(notification));

  // Emit alert event - this requires the app to be in forground
  if (notification.message && notification.foreground)
    Push.emit('alert', notification);

  // Emit sound event
  if (notification.sound) Push.emit('sound', notification);

  // Emit badge event
  if (notification.badge) Push.emit('badge', notification);

  // If within thres
  if (notification.open) {
    Push.emit('startup', notification);
  } else {
    Push.emit('message', notification);
  }
};

// handle APNS notifications for iOS
onNotificationAPN = function(e) {
  // alert('onNotificationAPN ' + JSON.stringify(e));

  var sound = e.sound;

  // Only prefix sound if actual text found
  if (sound && sound.length) sound = 'application/' + sound;

  // XXX: Investigate if we need more defaults
  var unifiedMessage = {
    message: e.alert,
    sound: sound,
    badge: e.badge,
    coldstart: coldstart,
    background: !e.foreground,
    foreground: !!e.foreground,
    open: _atStartup(),
    type: 'apn.cordova'
  };

  // E.ejson should be a string - we send it directly to payload
  if (e.ejson) unifiedMessage.payload = _parsePayload(e.ejson);

  // Trigger notification
  onNotification(unifiedMessage);
};

// handle GCM notifications for Android and Fire OS
onNotificationGCM = function(e) {
  // alert('onNotificationGCM ' + JSON.stringify(e));

  switch( e.event ) {
    case 'registered':
      if ( e.regid.length > 0 ) {
        Push.emit('token', { gcm: ''+e.regid } );
      }
    break;

    case 'message':

      var sound = e.soundname || e.payload.sound;

      // Only prefix sound if actual text found
      if (sound && sound.length) sound = '/android_asset/www/application/' + sound;

      // XXX: Investigate if we need more defaults
      var unifiedMessage = {
        message: e.payload.message || e.msg || '',
        sound: sound,
        badge: e.payload.msgcnt,
        // Coldstart on android is a bit inconsistent - its only set when the
        // notification opens the app
        coldstart: (e.coldstart === !!e.coldstart) ? e.coldstart : coldstart,
        background: !e.foreground,
        foreground: !!e.foreground,
        // open: _atStartup(),  // This is the iOS implementation
        open: (e.coldstart === !!e.coldstart), // If set true / false its an open event
        type: 'gcm.cordova'
      };

      // If payload.ejson this is an object - we hand it over to parsePayload,
      // parsePayload will do the convertion for us
      if (e.payload.ejson) unifiedMessage.payload = _parsePayload(e.payload.ejson);

      // Trigger notification
      onNotification(unifiedMessage);
    break;

    case 'error':
      // e.msg
      Push.emit('error', { type: 'gcm.cordova', error: e });
    break;
  }
};

var rigDefaultEventListeners = function(options) {
  // alert('RIG: ' + JSON.stringify(options));

  // Set default badge behaviour
  if (options.badge === true) Push.addListener('badge', function(notification) {
    Push.setBadge(notification.badge);
  });

  // Set default sound behaviour
  if (options.sound === true) Push.addListener('sound', function(notification) {
    if (notification.sound && notification.sound.length) {
      var snd = new Media(notification.sound);
      snd.play();
    }
  });

  // Set default alert behaviour
  if (options.alert === true) Push.addListener('alert', function(notification) {

    // if (navigator && navigator.notification && navigator.notification.alert) {
    //   // If native notifications installed
    //   navigator.notification.alert(notification.message);
    // } else {
    //   // Use regular notification
    //   alert(notification.message);
    // }
  });

  // We add a vibrate listener
  if (options.vibrate === true) Push.addListener('message', function(notification) {
    // Vibrate if possible
    if (navigator && navigator.notification && navigator.notification.vibrate) {
      // If vibrate installed
      navigator.notification.vibrate(500);
    }
  });
};

var isConfigured = false;

Push.Configure = function(options) {
  var self = this;

  // Block multiple calls
  if (isConfigured)
    throw new Error('Push.Configure should not be called more than once!');

  isConfigured = true;

  // Clean up options, make sure the projectNumber
  if (options.gcm && !options.gcm.projectNumber)
    throw new Error('Push.Configure gcm got no projectNumber');

  // Client-side security warnings
  checkClientSecurity(options);

  // Set default options - these are needed for apn?
  options = _.extend({
    badge: (options.badge === true),
    sound: (options.sound === true),
    alert: (options.alert === true),
    vibrate: (options.vibrate === true)
  }, options);

  // Rig default event listeners for badge/sound/alert/vibrate
  rigDefaultEventListeners(options);

  // Add debug info
  if (Push.debug) console.log('Push.Configure', options);

  // Start token updates
  initPushUpdates(options.appName);

    // Initialize on ready
  Meteor.startup(function() {

    document.addEventListener('deviceready', function() {

      // At initial startup set startup time
      startupTime = new Date();

      // Update flag if app coldstart
      document.addEventListener("pause", function() {
        coldstart = false;
      }, false);

      document.addEventListener('resume', function() {
        // Reset startup time at resume
        startupTime = new Date();
      });

      var pushNotification = window.plugins.pushNotification;

      if (device.platform == 'android' || device.platform == 'Android') {
        // alert('Rig GCM');
        try {

          if (options.gcm && options.gcm.projectNumber) {
            pushNotification.register(function(result) {
              // Emit registered
              self.emit('register', result);
            }, function(error) {
              // Emit error
              self.emit('error', { type: 'gcm.cordova', error: error });
            }, {
              'senderID': ''+options.gcm.projectNumber,
              'ecb': 'onNotificationGCM'
            });
          } else {
            // throw new Error('senderID not set in options, required on android');
            console.warn('WARNING: Push.init, No gcm.projectNumber set on android');
          }

        } catch(err) {
          // console.log('There was an error starting up push');
          // console.log('Error description: ' + err.message);
          self.emit('error', { type: 'gcm.cordova', error: err.message });
        }
      } else {
        // alert('Rig APN');

        try {

          pushNotification.register(function(token) {
            // Got apn / ios token
            self.emit('token', { apn: token });
          }, function(error) {
            // Emit error
            self.emit('error', { type: 'apn.cordova', error: error });
          }, {
            'badge': ''+options.badge,
            'sound': ''+options.sound,
            'alert': ''+options.alert,
            'ecb': 'onNotificationAPN'
          }); // required!
        } catch(err) {
          // console.log('There was an error starting up push');
          // console.log('Error description: ' + err.message);
          self.emit('error', { type: 'apn.cordova', error: err.message });
        }
      }

    }, false);

  });

}; // EO Push
<commit_msg>make startup event stable<commit_after>var coldstart = true;
var startupTime = new Date();
var startupThreshold = 1000; // ms
var paused = false;

var _atStartup = function() {
  // If we were triggered before resume then its a startup
  if (paused) return true;

  // If startup time is less than startupThreshold ago then lets say this is
  // at startup.
  return (new Date() - startupTime < startupThreshold);
};

Push.setBadge = function(count) {
  document.addEventListener('deviceready', function() {
    // Helper
    var pushNotification = window.plugins.pushNotification;

    // If the set application badge is available
    if (typeof pushNotification.setApplicationIconBadgeNumber == 'function') {
      // Set the badge
      pushNotification.setApplicationIconBadgeNumber(function(result) {
        // Success callback
      }, function(err) {
        // Error callback
        Push.emit('error', { type: 'badge', error: err });
      }, count);

    }
  });
};

_parsePayload = function(value) {
  // Android actually parses payload into an object - this is not the case with
  // iOS (here is it just a string)
  if (value !== ''+value) value = JSON.stringify(value);

  // Run the string through ejson
  try {
    return EJSON.parse(value);
  } catch(err) {
    return { error: err };
  }
};

var onNotification = function(notification) {
  // alert('onNotification' + JSON.stringify(notification));

  // Emit alert event - this requires the app to be in forground
  if (notification.message && notification.foreground)
    Push.emit('alert', notification);

  // Emit sound event
  if (notification.sound) Push.emit('sound', notification);

  // Emit badge event
  if (notification.badge) Push.emit('badge', notification);

  // If within thres
  if (notification.open) {
    Push.emit('startup', notification);
  } else {
    Push.emit('message', notification);
  }
};

// handle APNS notifications for iOS
onNotificationAPN = function(e) {
  // alert('onNotificationAPN ' + JSON.stringify(e));

  var sound = e.sound;

  // Only prefix sound if actual text found
  if (sound && sound.length) sound = 'application/' + sound;

  // XXX: Investigate if we need more defaults
  var unifiedMessage = {
    message: e.alert,
    sound: sound,
    badge: e.badge,
    coldstart: coldstart,
    background: !e.foreground,
    foreground: !!e.foreground,
    open: _atStartup(),
    type: 'apn.cordova'
  };

  // E.ejson should be a string - we send it directly to payload
  if (e.ejson) unifiedMessage.payload = _parsePayload(e.ejson);

  // Trigger notification
  onNotification(unifiedMessage);
};

// handle GCM notifications for Android and Fire OS
onNotificationGCM = function(e) {
  // alert('onNotificationGCM ' + JSON.stringify(e));

  switch( e.event ) {
    case 'registered':
      if ( e.regid.length > 0 ) {
        Push.emit('token', { gcm: ''+e.regid } );
      }
    break;

    case 'message':

      var sound = e.soundname || e.payload.sound;

      // Only prefix sound if actual text found
      if (sound && sound.length) sound = '/android_asset/www/application/' + sound;

      // XXX: Investigate if we need more defaults
      var unifiedMessage = {
        message: e.payload.message || e.msg || '',
        sound: sound,
        badge: e.payload.msgcnt,
        // Coldstart on android is a bit inconsistent - its only set when the
        // notification opens the app
        coldstart: (e.coldstart === !!e.coldstart) ? e.coldstart : coldstart,
        background: !e.foreground,
        foreground: !!e.foreground,
        // open: _atStartup(),  // This is the iOS implementation
        open: (e.coldstart === !!e.coldstart), // If set true / false its an open event
        type: 'gcm.cordova'
      };

      // If payload.ejson this is an object - we hand it over to parsePayload,
      // parsePayload will do the convertion for us
      if (e.payload.ejson) unifiedMessage.payload = _parsePayload(e.payload.ejson);

      // Trigger notification
      onNotification(unifiedMessage);
    break;

    case 'error':
      // e.msg
      Push.emit('error', { type: 'gcm.cordova', error: e });
    break;
  }
};

var rigDefaultEventListeners = function(options) {
  // alert('RIG: ' + JSON.stringify(options));

  // Set default badge behaviour
  if (options.badge === true) Push.addListener('badge', function(notification) {
    Push.setBadge(notification.badge);
  });

  // Set default sound behaviour
  if (options.sound === true) Push.addListener('sound', function(notification) {
    if (notification.sound && notification.sound.length) {
      var snd = new Media(notification.sound);
      snd.play();
    }
  });

  // Set default alert behaviour
  if (options.alert === true) Push.addListener('alert', function(notification) {

    // if (navigator && navigator.notification && navigator.notification.alert) {
    //   // If native notifications installed
    //   navigator.notification.alert(notification.message);
    // } else {
    //   // Use regular notification
    //   alert(notification.message);
    // }
  });

  // We add a vibrate listener
  if (options.vibrate === true) Push.addListener('message', function(notification) {
    // Vibrate if possible
    if (navigator && navigator.notification && navigator.notification.vibrate) {
      // If vibrate installed
      navigator.notification.vibrate(500);
    }
  });
};

var isConfigured = false;

Push.Configure = function(options) {
  var self = this;

  // Block multiple calls
  if (isConfigured)
    throw new Error('Push.Configure should not be called more than once!');

  isConfigured = true;

  // Clean up options, make sure the projectNumber
  if (options.gcm && !options.gcm.projectNumber)
    throw new Error('Push.Configure gcm got no projectNumber');

  // Client-side security warnings
  checkClientSecurity(options);

  // Set default options - these are needed for apn?
  options = _.extend({
    badge: (options.badge === true),
    sound: (options.sound === true),
    alert: (options.alert === true),
    vibrate: (options.vibrate === true)
  }, options);

  // Rig default event listeners for badge/sound/alert/vibrate
  rigDefaultEventListeners(options);

  // Add debug info
  if (Push.debug) console.log('Push.Configure', options);

  // Start token updates
  initPushUpdates(options.appName);

    // Initialize on ready
  Meteor.startup(function() {

    document.addEventListener('deviceready', function() {

      // At initial startup set startup time
      startupTime = new Date();

      // Update flag if app coldstart
      document.addEventListener("pause", function() {
        paused = true;
        coldstart = false;
      }, false);

      document.addEventListener('resume', function() {
        paused = false;
        // Reset startup time at resume
        startupTime = new Date();
      });

      var pushNotification = window.plugins.pushNotification;

      if (device.platform == 'android' || device.platform == 'Android') {
        // alert('Rig GCM');
        try {

          if (options.gcm && options.gcm.projectNumber) {
            pushNotification.register(function(result) {
              // Emit registered
              self.emit('register', result);
            }, function(error) {
              // Emit error
              self.emit('error', { type: 'gcm.cordova', error: error });
            }, {
              'senderID': ''+options.gcm.projectNumber,
              'ecb': 'onNotificationGCM'
            });
          } else {
            // throw new Error('senderID not set in options, required on android');
            console.warn('WARNING: Push.init, No gcm.projectNumber set on android');
          }

        } catch(err) {
          // console.log('There was an error starting up push');
          // console.log('Error description: ' + err.message);
          self.emit('error', { type: 'gcm.cordova', error: err.message });
        }
      } else {
        // alert('Rig APN');

        try {

          pushNotification.register(function(token) {
            // Got apn / ios token
            self.emit('token', { apn: token });
          }, function(error) {
            // Emit error
            self.emit('error', { type: 'apn.cordova', error: error });
          }, {
            'badge': ''+options.badge,
            'sound': ''+options.sound,
            'alert': ''+options.alert,
            'ecb': 'onNotificationAPN'
          }); // required!
        } catch(err) {
          // console.log('There was an error starting up push');
          // console.log('Error description: ' + err.message);
          self.emit('error', { type: 'apn.cordova', error: err.message });
        }
      }

    }, false);

  });

}; // EO Push
<|endoftext|>


<commit_before>// Bot config
var config = require('./lua_bot_config');

// Node libraries
var nodelua = require('nodelua');
var fs = require('fs');

// Print method that overrides the default Lua print method
var output;
function pseudoprint() {
	var args = Array.prototype.slice.call(arguments);
	for(var i = 0; i < args.length; i++) {
		output += (args[i] + " ");
	};
	output += "\n"
}

// Table containing all currently running instances
var luaInstances = {};

// Starts a Lua instance
function setupLuaInstance(name) {
	luaInstances[name] = new nodelua.LuaState(name);
	luaInstances[name].registerFunction('print', pseudoprint);
}

// Captures the output after running some Lua code
function runAndCaptureOutput(name, string) {
	try {
		output = "";
		luaInstances[name].doStringSync(string);
		if(output.length > config.max_length) {
			output = output.substr(0, config.max_length);
		}
		return output;
	} catch(e) {
		return e.message;
	}
}

// Saves the global table of a Lua instance to a file
function saveGlobalTable(name) {
	var gt = luaInstances[name].doFileSync(__dirname + "/encode_global_table.lua");
	luaInstances[name].setGlobal("GLOBAL_TABLE_ENCODER", null);

	var str = "module.exports = " + gt;

	fs.writeFile(__dirname + "/global-tables/" + name + "_global_table.js", str, function(err) {
		if(err) {
			return console.log(err);
		}

		console.log("Lua bot global table file saved.");
	});
}

// Saves the config object to the config file
function saveConfig() {
	var str = "module.exports = " + JSON.stringify(config);

	fs.writeFile(__dirname + "/lua_bot_config.js", str, function(err) {
		if(err) {
			return console.log(err);
		}

		console.log("Lua bot config file saved.");
	});
}

// Simple table contains element method
function contains(a, obj) {
	var i = a.length;
	while (i--) {
		if (a[i] === obj) {
			return true;
		}
	}
	return false;
}

// Simple append to array method
function appendToArray(array, element) {
	if(!contains(array, element)) {
		array.push(element);
		return true;
	}
	return false;
}

// Simple remove from array method
function removeFromArray(array, element) {
	var index = array.indexOf(element);
	if(index > -1) {
		array.splice(index, 1);
		return true;
	}
	return false;
}

// Handler function
module.exports = function(formData) {
	console.log("Lua bot handling request.");

	if(formData.user_name == "slackbot") {
		return null;
	}

	if(luaInstances[formData.team_domain] == null) {
		setupLuaInstance(formData.team_domain);
	}

	var ret;
	if(formData.keyword == "lua" && !contains(config.user_blacklist, formData.user_name)) {
		var allowed = true;
		var illegalword = "";
		for (var i = 0; i < config.lua_blacklist.length; i++) {
			if(formData.message.indexOf(config.lua_blacklist[i]) > -1) {
				allowed = false;
				illegalword = config.lua_blacklist[i];
				break;
			}
		};

		if(allowed) {
			ret = runAndCaptureOutput(formData.team_domain, formData.message);
		} else {
			ret = "You aren't allowed to use '" + illegalword + "'' in your code!";
		}
	} else if(formData.keyword == "luaadmin") {
		var commands = formData.message.split(' ');

		if(contains(config.user_admins, formData.user_name) || contains(config.user_limited_admins, formData.user_name)) {
			if(commands[0] == "reset") {
				setupLuaInstance(formData.team_domain);
				ret = "Lua instance reset."
			} else if(commands[0] == "setlength") {
				config.max_length = Number(commands[1]);
				saveConfig();
				ret = "Max length of output set to: " + config.max_length;
			} else if(commands[0] == "list") {
				var listName = commands[1] == "admins" ? "user_admins" : commands[1] == "limitedadmins" ? "user_limited_admins" : commands[1] == "banned" ?
					"user_blacklist" : commands[1] == "blacklist" ? "lua_blacklist" : null;
				console.log(commands[1]);
				console.log(listName);
				if(listName != null) {
					ret = "";
					for (var i = 0; i < config[listName].length; i++) {
						ret += config[listName][i] + (i < config[listName].length ? ", " : "");
					};
				}
			} else if(commands[0] == "getlength") {
				console.log('hi');
				ret = config.max_length.toString();
			}
		}
		if(contains(config.user_admins, formData.user_name)) {
			if(commands[0] == "ban") {
				if(appendToArray(config.user_blacklist, commands[1])) {
					ret = commands[1] + " was added to ban list."
					saveConfig();
				} else {
					ret = commands[1] + " is already on the ban list."
				}
			} else if(commands[0] == "unban") {
				if(removeFromArray(config.user_blacklist, commands[1])) {
					ret = commands[1] + " was removed from the ban list."
					saveConfig();
				} else {
					ret = commands[1] + " is not on the ban list."
				}
			} else if(commands[0] == "promote") {
				if(appendToArray(config.user_limited_admins, commands[1])) {
					ret = commands[1] + " was promoted to a limited admin."
					saveConfig();
				} else {
					ret = commands[1] + " is already a limited admin."
				}
			} else if(commands[0] == "demote") {
				if(removeFromArray(config.user_limited_admins, commands[1])) {
					ret = commands[1] + " was demoted from a limited admin."
					saveConfig();
				} else {
					ret = commands[1] + " wasn't a limited admin to start with."
				}
			} else if(commands[0] == "blacklist") {
				if(appendToArray(config.lua_blacklist, commands[1])) {
					ret = commands[1] + " was added to the blacklist."
					saveConfig();
				} else {
					ret = commands[1] + " is already on the blacklist."
				}
			} else if(commands[0] == "whitelist") {
				if(removeFromArray(config.lua_blacklist, commands[1])) {
					ret = commands[1] + " was removed from the blacklist."
					saveConfig();
				} else {
					ret = commands[1] + " is already on the whitelist."
				}
			}
		}
	}

	return {
		text: ret
	}
}<commit_msg>worked on Lua bot<commit_after>// Bot config
var config = require('./lua_bot_config');

// Node libraries
var nodelua = require('nodelua');
var fs = require('fs');

// Print method that overrides the default Lua print method
var output;
function pseudoprint() {
	var args = Array.prototype.slice.call(arguments);
	for(var i = 0; i < args.length; i++) {
		output += (args[i] + " ");
	};
	output += "\n"
}

// Table containing all currently running instances
var luaInstances = {};

// Starts a Lua instance
function setupLuaInstance(name) {
	luaInstances[name] = new nodelua.LuaState(name);
	luaInstances[name].registerFunction('print', pseudoprint);
}

// Captures the output after running some Lua code
function runAndCaptureOutput(name, string) {
	try {
		output = "";
		luaInstances[name].doStringSync(string);
		if(output.length > config.max_length) {
			output = output.substr(0, config.max_length);
		}
		return output;
	} catch(e) {
		return e.message;
	}
}

// Saves the global table of a Lua instance to a file
function saveGlobalTable(name) {
	var gt = luaInstances[name].doFileSync(__dirname + "/encode_global_table.lua");
	luaInstances[name].setGlobal("GLOBAL_TABLE_ENCODER", null);

	var str = "module.exports = " + gt;

	fs.writeFile(__dirname + "/global-tables/" + name + "_global_table.js", str, function(err) {
		if(err) {
			return console.log(err);
		}

		console.log("Lua bot global table file saved.");
	});
}

// Saves the config object to the config file
function saveConfig() {
	var str = "module.exports = " + JSON.stringify(config);

	fs.writeFile(__dirname + "/lua_bot_config.js", str, function(err) {
		if(err) {
			return console.log(err);
		}

		console.log("Lua bot config file saved.");
	});
}

// Simple table contains element method
function contains(a, obj) {
	var i = a.length;
	while (i--) {
		if (a[i] === obj) {
			return true;
		}
	}
	return false;
}

// Simple append to array method
function appendToArray(array, element) {
	if(!contains(array, element)) {
		array.push(element);
		return true;
	}
	return false;
}

// Simple remove from array method
function removeFromArray(array, element) {
	var index = array.indexOf(element);
	if(index > -1) {
		array.splice(index, 1);
		return true;
	}
	return false;
}

// Handler function
module.exports = function(formData) {
	console.log("Lua bot handling request.");

	if(formData.user_name == "slackbot") {
		return null;
	}

	if(luaInstances[formData.team_domain] == null) {
		setupLuaInstance(formData.team_domain);
	}

	var ret;
	if(formData.keyword == "lua" && !contains(config.user_blacklist, formData.user_name)) {
		var allowed = true;
		var illegalword = "";
		for (var i = 0; i < config.lua_blacklist.length; i++) {
			if(formData.message.indexOf(config.lua_blacklist[i]) > -1) {
				allowed = false;
				illegalword = config.lua_blacklist[i];
				break;
			}
		};

		if(allowed) {
			ret = runAndCaptureOutput(formData.team_domain, formData.message);
		} else {
			ret = "You aren't allowed to use '" + illegalword + "'' in your code!";
		}
	} else if(formData.keyword == "luaadmin") {
		var commands = formData.message.split(' ');

		if(contains(config.user_admins, formData.user_name) || contains(config.user_limited_admins, formData.user_name)) {
			if(commands[0] == "reset") {
				setupLuaInstance(formData.team_domain);
				ret = "Lua instance reset."
			} else if(commands[0] == "setlength") {
				config.max_length = Number(commands[1]);
				saveConfig();
				ret = "Max length of output set to: " + config.max_length;
			} else if(commands[0] == "list") {
				var listName = commands[1] == "admins" ? "user_admins" : commands[1] == "limitedadmins" ? "user_limited_admins" : commands[1] == "banned" ?
					"user_blacklist" : commands[1] == "blacklist" ? "lua_blacklist" : null;
				if(listName != null) {
					ret = "";
					for (var i = 0; i < config[listName].length; i++) {
						ret += config[listName][i] + (i < config[listName].length - 1 ? ", " : "");
					};
				}
			} else if(commands[0] == "getlength") {
				console.log('hi');
				ret = config.max_length.toString();
			}
		}
		if(contains(config.user_admins, formData.user_name)) {
			if(commands[0] == "ban") {
				if(appendToArray(config.user_blacklist, commands[1])) {
					ret = commands[1] + " was added to ban list."
					saveConfig();
				} else {
					ret = commands[1] + " is already on the ban list."
				}
			} else if(commands[0] == "unban") {
				if(removeFromArray(config.user_blacklist, commands[1])) {
					ret = commands[1] + " was removed from the ban list."
					saveConfig();
				} else {
					ret = commands[1] + " is not on the ban list."
				}
			} else if(commands[0] == "promote") {
				if(appendToArray(config.user_limited_admins, commands[1])) {
					ret = commands[1] + " was promoted to a limited admin."
					saveConfig();
				} else {
					ret = commands[1] + " is already a limited admin."
				}
			} else if(commands[0] == "demote") {
				if(removeFromArray(config.user_limited_admins, commands[1])) {
					ret = commands[1] + " was demoted from a limited admin."
					saveConfig();
				} else {
					ret = commands[1] + " wasn't a limited admin to start with."
				}
			} else if(commands[0] == "blacklist") {
				if(appendToArray(config.lua_blacklist, commands[1])) {
					ret = commands[1] + " was added to the blacklist."
					saveConfig();
				} else {
					ret = commands[1] + " is already on the blacklist."
				}
			} else if(commands[0] == "whitelist") {
				if(removeFromArray(config.lua_blacklist, commands[1])) {
					ret = commands[1] + " was removed from the blacklist."
					saveConfig();
				} else {
					ret = commands[1] + " is already on the whitelist."
				}
			}
		}
	}

	return {
		text: ret
	}
}<|endoftext|>


<commit_before>/**
 * Board-offline.js
 *
 * @description :: TODO: You might write a short summary of how this model works and what it represents here.
 * @docs        :: http://sailsjs.org/documentation/concepts/models-and-orm/models
 */

var Q = require('q');
var json2csv = require('json2csv');

var util = require( 'util' ),
  actionUtil = require( '../blueprints/_util/actionUtil' );

module.exports = {
  migrate: 'drop',

  attributes: {
  	number: 'string',
  	totalVotes: 'string',
  	blankVotes: 'string',
  	recurredVotes: 'string',
  	inpugnedVotes: 'string',
  	nullVotes: 'string',

  	town: {
  		model: 'town'
  	},

    instance: {
      model: 'instance'
    },

  	config: {
  		model: 'config'
  	},

  	votes: 'json'
  },

  afterCreate: function (args, next) {
    var self = this;
    Board.findOne({name: args.number, town: args.town}).populate('school').exec(function (err, board) {
        console.log(board);
        var votes = JSON.parse(args.votes);
        _.each(votes, function (candidate) {
          var p = {
            school: board.school.id,
            borough: board.borough,
            candidate: candidate.id,
            instance: args.instance,
            board: board,
            config: arg.config
          };
          candivotes.push(p);
        });

        next();
    });
  },
};

<commit_msg>offline<commit_after>/**
 * Board-offline.js
 *
 * @description :: TODO: You might write a short summary of how this model works and what it represents here.
 * @docs        :: http://sailsjs.org/documentation/concepts/models-and-orm/models
 */

var Q = require('q');
var json2csv = require('json2csv');

var util = require( 'util' ),
  actionUtil = require( '../blueprints/_util/actionUtil' );

module.exports = {
  migrate: 'drop',

  attributes: {
  	number: 'string',
  	totalVotes: 'string',
  	blankVotes: 'string',
  	recurredVotes: 'string',
  	inpugnedVotes: 'string',
  	nullVotes: 'string',

  	town: {
  		model: 'town'
  	},

    instance: {
      model: 'instance'
    },

  	config: {
  		model: 'config'
  	},

  	votes: 'json'
  },

  afterCreate: function (args, next) {
    var self = this;
    Board.findOne({name: args.number, town: args.town}).populate('school').exec(function (err, board) {
        console.log(board);
        var votes = JSON.parse(args.votes);
        var candivotes = [];

        _.each(votes, function (candidate) {
          var p = {
            school: board.school.id,
            borough: board.borough,
            candidate: candidate.id,
            instance: args.instance,
            board: board,
            config: arg.config
          };
          candivotes.push(p);
        });

        console.log(candivotes);

        next();
    });
  },
};

<|endoftext|>


<commit_before>"use strict";
import TagAPI from '../../api/tag';

const
    FETCHING_TAGS = "FETCHING_TAGS",
    FETCHING_TAGS_SUCCESS = "FETCHING_TAGS_SUCCESS",
    FETCHING_TAGS_ERROR = "FETCHING_TAGS_ERROR",
    CREATE_TAG = "CREATE_TAG",
    CREATE_TAG_SUCCESS = "CREATE_TAG_SUCCESS",
    CREATE_TAG_ERROR = "CREATE_TAG_ERROR"
;

const state = {
    tags: [],
    error: null,
    createTagError: null,
    isLoading: false,
    createTagIsLoading: false,
};

const getters = {
    tags(state) {
        return state.tags.slice(0).sort((a, b) => a.name > b.name ? 1 : -1);
    },
    getTagById(state) {
        return tagId => {
            let foundTag = false;
            state.tags.forEach(
                (tag) => {
                    if (String(tag.id) === String(tagId)) {
                        foundTag = tag;
                    }
                },
            );

            return foundTag;
        };
    },
    getTagByIri(state, getters) {
        return iri => getters.getTagById(iri.replace('/api/tags/', ''));
    },
    hasTags(state) {
        return state.tags.length > 0;
    },
    error(state) {
        return state.error;
    },
    createTagError(state) {
        return state.createTagError;
    },
    isLoading(state) {
        return state.isLoading;
    },
    createTagIsLoading(state) {
        return state.createTagIsLoading;
    },
};

const mutations = {
    [FETCHING_TAGS](state) {
        state.isLoading = true;
        state.error = null;
    },
    [FETCHING_TAGS_SUCCESS](state, tags) {
        state.error = null;
        state.isLoading = false;
        state.tags = tags;
    },
    [FETCHING_TAGS_ERROR](state, error) {
        state.error = error;
        state.isLoading = false;
    },
    [CREATE_TAG](state) {
        state.createTagIsLoading = true;
        state.createTagError = null;
    },
    [CREATE_TAG_SUCCESS](state, createdTag) {
        state.createTagError = null;
        state.createTagIsLoading = false;
        state.tags = [ ...state.tags, createdTag ];
    },
    [CREATE_TAG_ERROR](state, error) {
        state.createTagError = error;
        state.createTagIsLoading = false;
    },
};

const actions = {
    async findAll({commit}) {
        commit(FETCHING_TAGS);
        try {
            let response = await TagAPI.findAll();
            commit(FETCHING_TAGS_SUCCESS, response.data['hydra:member']);
        } catch (error) {
            commit(FETCHING_TAGS_ERROR, error);
        }
    },
    async create({commit}, payload) {
        commit(CREATE_TAG);
        try {
            let response = await TagAPI.create(payload);
            commit(CREATE_TAG_SUCCESS, response.data);

            return response.data;
        } catch (error) {
            commit(CREATE_TAG_ERROR, error);
        }
    },
};

export default {
    namespaced: true,
    state,
    getters,
    actions,
    mutations
}
<commit_msg>Sort tags case insensitiv by default<commit_after>"use strict";
import TagAPI from '../../api/tag';

const
    FETCHING_TAGS = "FETCHING_TAGS",
    FETCHING_TAGS_SUCCESS = "FETCHING_TAGS_SUCCESS",
    FETCHING_TAGS_ERROR = "FETCHING_TAGS_ERROR",
    CREATE_TAG = "CREATE_TAG",
    CREATE_TAG_SUCCESS = "CREATE_TAG_SUCCESS",
    CREATE_TAG_ERROR = "CREATE_TAG_ERROR"
;

const state = {
    tags: [],
    error: null,
    createTagError: null,
    isLoading: false,
    createTagIsLoading: false,
};

const getters = {
    tags(state) {
        return state.tags.slice(0).sort((a, b) => a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1);
    },
    getTagById(state) {
        return tagId => {
            let foundTag = false;
            state.tags.forEach(
                (tag) => {
                    if (String(tag.id) === String(tagId)) {
                        foundTag = tag;
                    }
                },
            );

            return foundTag;
        };
    },
    getTagByIri(state, getters) {
        return iri => getters.getTagById(iri.replace('/api/tags/', ''));
    },
    hasTags(state) {
        return state.tags.length > 0;
    },
    error(state) {
        return state.error;
    },
    createTagError(state) {
        return state.createTagError;
    },
    isLoading(state) {
        return state.isLoading;
    },
    createTagIsLoading(state) {
        return state.createTagIsLoading;
    },
};

const mutations = {
    [FETCHING_TAGS](state) {
        state.isLoading = true;
        state.error = null;
    },
    [FETCHING_TAGS_SUCCESS](state, tags) {
        state.error = null;
        state.isLoading = false;
        state.tags = tags;
    },
    [FETCHING_TAGS_ERROR](state, error) {
        state.error = error;
        state.isLoading = false;
    },
    [CREATE_TAG](state) {
        state.createTagIsLoading = true;
        state.createTagError = null;
    },
    [CREATE_TAG_SUCCESS](state, createdTag) {
        state.createTagError = null;
        state.createTagIsLoading = false;
        state.tags = [ ...state.tags, createdTag ];
    },
    [CREATE_TAG_ERROR](state, error) {
        state.createTagError = error;
        state.createTagIsLoading = false;
    },
};

const actions = {
    async findAll({commit}) {
        commit(FETCHING_TAGS);
        try {
            let response = await TagAPI.findAll();
            commit(FETCHING_TAGS_SUCCESS, response.data['hydra:member']);
        } catch (error) {
            commit(FETCHING_TAGS_ERROR, error);
        }
    },
    async create({commit}, payload) {
        commit(CREATE_TAG);
        try {
            let response = await TagAPI.create(payload);
            commit(CREATE_TAG_SUCCESS, response.data);

            return response.data;
        } catch (error) {
            commit(CREATE_TAG_ERROR, error);
        }
    },
};

export default {
    namespaced: true,
    state,
    getters,
    actions,
    mutations
}
<|endoftext|>


<commit_before>"use strict";

const defaultOptions = {
    enableHeadingId: true, //SA what to do if TOC is detected?
    includeLevel: [1, 2, 3, 4, 5, 6],
    tocContainerClass: "toc",
    tocRegex: "^\\[\\]\\(toc\\)",
    excludeFromTocRegex: "\\{\\}\\(notoc\\)",
    defaultListElement: "ul",
    listElements: ["ul", "ul", "ul", "ul", "ul", "ul"],
    defaultListElementAttributeSet: { style: "list-style-type: none;" },
    listElementAttributeSets: [],
    itemPrefixes: [], // array of strings: prefix depending on level
    idPrefix: "headings.",
    format: undefined
}; //defaultOptions
defaultOptions.bulletedListType = defaultOptions.defaultListElement;

module.exports = function (md, userOptions) {

    const util = require("util");

    const options = defaultOptions;
    if (userOptions)
        for (let index in userOptions)
            options[index] = userOptions[index];

    const slugify = function (s, used) {
        let slug = options.idPrefix +
            s.replace(' ', '-')
                .replace(/[^A-Za-z0-9\-\.\_]/g, function (match) {
                    return match.codePointAt().toString(16);
                }).toLowerCase();
        while (used[slug])
            slug += '.';
        used[slug] = slug;
        return slug;
    } // idHeadersSlugify

    // function nextNumber(number) { // number is a letter of a number as string or numeric
    //     let tryNumeric = parseInt(number);
    //     if (isNaN(tryNumeric)) {
    //         let codePoint = number.codePointAt();
    //         return String.fromCodePoint(++codePoint);
    //     } else
    //         return (++tryNumeric).toString();
    // } //nextNumber

    let tocRegexp = options.tocRegex;
    if (tocRegexp.constructor != RegExp)
        tocRegexp = new RegExp(options.tocRegex, "m");
    let excludeFromTocRegex = options.excludeFromTocRegex;
    if (excludeFromTocRegex.constructor != RegExp)
        excludeFromTocRegex = new RegExp(options.excludeFromTocRegex, "m");

    const usedIDs = { headings: {}, toc: {} };
    const idCounts = { headings: 0, toc: 0 };
    const idSet = [];
    const originalHeadingOpen = md.renderer.rules.heading_open;
    let fullTokenSet;
    // no magic function names:
    const tocFunctionNames = { open: "tocOpen", close: "tocClose", body: "tocBody" };
    const ruleName = "toc"; // works with null, but let's care about other plug-ins

    md.renderer.rules.heading_open = function (tokens, index, userOptions, object, renderer) {
        tokens[index].attrs = tokens[index].attrs || [];
        let title = tokens[index + 1].children.reduce(function (accumulator, child) {
            return accumulator + child.content;
        }, "");
        const headingSlug = idSet[idCounts.headings];
        tokens[index].attrs.push(["id", headingSlug]);
        ++idCounts.headings;
        if (originalHeadingOpen)
            return originalHeadingOpen.apply(this, arguments);
        else
            return renderer.renderToken.apply(renderer, arguments);
        //SA!!! APPEND text to return to add prefix to heading content
    }; //md.renderer.rules.heading_open

    let r = md.renderer.rules[tocFunctionNames.open] = function (tokens, index) {
        usedIDs.toc = {};
        return util.format("
", options.tocContainerClass);
    }; // open
    md.renderer.rules[tocFunctionNames.body] = function (tokens, index) {
        return createTocTree(0, fullTokenSet)[1];
    }; //body
    md.renderer.rules[tocFunctionNames.close] = function (tokens, index) {
        return "
";
        usedIDs.toc = {};
    }; //close

    function createTocTree(pos, tokens) {
        let headings = [],
            buffer = "",
            currentLevel,
            subHeadings,
            size = tokens.length,
            currentPos = pos;
        while (currentPos < size) {
            let token = tokens[currentPos];
            let heading = tokens[currentPos - 1];
            let level = token.tag && parseInt(token.tag.substr(1, 1));
            if (token.type !== "heading_close"
                || options.includeLevel.indexOf(level) == -1 || heading.type !== "inline"
                || excludeFromTocRegex.exec(heading.content)) {
                currentPos++;
                continue;
            } //if
            if (currentLevel) {
                if (level > currentLevel) {
                    subHeadings = createTocTree(currentPos, tokens);
                    buffer += subHeadings[1];
                    currentPos = subHeadings[0];
                    continue;
                } //if
                if (level < currentLevel) {
                    buffer += "
";
                    headings.push(buffer);
                    return listElement(currentLevel, currentPos, options, headings);
                } //if
                if (level == currentLevel) {
                    buffer += "
";
                    headings.push(buffer);
                } //if
            } else
                currentLevel = level; // We init with the first found level
            //SA!!! currentLevel is the level of the TOC item, number of '#' in '#', '##', '###'...
            // APPEND text after "
" to make PREFIX to title
            const tocSlug = idSet[idCounts.toc];
            ++idCounts.toc;
            buffer = util.format("
", tocSlug);
            let headingContent = heading.content;
            if (options.itemPrefixes)
                if (options.itemPrefixes[currentLevel - 1])
                    headingContent = options.itemPrefixes[currentLevel - 1] + headingContent;
            buffer += typeof options.format === "function" ? options.format(headingContent) : headingContent;
            buffer += "";
            currentPos++;
        } //loop
        buffer += "
";
        headings.push(buffer);
        return listElement(currentLevel, currentPos, options, headings);
    } //createTocTree

    function listElement(level, currentPos, options, headings) {
        let listTag = options.defaultListElement;
        if (options.listElements)
            if (options.listElements[level - 1])
                listTag = options.listElements[level - 1];
        let elementAttributes = "";
        if (options.listElementAttributeSets)
            if (options.listElementAttributeSets[level - 1])
                for (let index in options.listElementAttributeSets[level - 1])
                    elementAttributes += util.format(" %s=\"%s\"", index, options.listElementAttributeSets[level - 1][index]);
        if (options.listElementAttributeSets)
            if (options.listElementAttributeSets.length < 1)
                for (let index in options.defaultListElementAttributeSet)
                    elementAttributes += util.format(" %s=\"%s\"", index, options.defaultListElementAttributeSet[index]);
        return [currentPos,
            util.format("<%s%s>%s</%s>",
                listTag, elementAttributes, headings.join(""), listTag)];
    } //listElement

    function buildIdSet(idSet, tokens) {
        for (let index = 1; index < tokens.length; ++index) {
            const token = tokens[index];
            const previousToken = tokens[index - 1];
            if (token.type !== "heading_close" || previousToken.type !== "inline") continue;
            idSet.push(slugify(previousToken.content, usedIDs));
        } //loop
    } //buildIdSet

    md.core.ruler.before("inline", "buildToc", function (state) {
        usedIDs.headings = [];
        if (!tocRegexp) return;
        // extra global check saves time if there is no match:
        const match = tocRegexp.exec(state.src);
        if (!match) return;
        if (match.length < 1) return;
        usedIDs.headings = {};
        usedIDs.toc = {};
        buildIdSet(idSet, state.tokens);
        idCounts.headings = 0;
        idCounts.top = 0;
        md.inline.ruler.before("text", ruleName, function toc(state, silent) {
            if (silent) return false;
            const match = tocRegexp.exec(state.src);
            if (!match) return false;
            if (match.length < 1) return false;
            state.push(tocFunctionNames.open, ruleName, 1);
            state.push(tocFunctionNames.body, ruleName, 0);
            state.push(tocFunctionNames.close, ruleName, -1);
            let newline = state.src.indexOf("\n");
            if (newline !== -1)
                state.pos = state.pos + newline;
            else
                state.pos = state.pos + state.posMax + 1;
            return true;
        });
        fullTokenSet = state.tokens;
    }); //md.core.ruler.before

}; //module.exports
<commit_msg>Multiple improvement and corrections to the embedded plug-in id.toc.js<commit_after>"use strict";

const defaultOptions = {
    enableHeadingId: true, //SA what to do if TOC is detected?
    includeLevel: [1, 2, 3, 4, 5, 6],
    tocContainerClass: "toc",
    tocRegex: "^\\[\\]\\(toc\\)",
    excludeFromTocRegex: "\\[\\]\\(notoc\\)",
    defaultListElement: "ul",
    listElements: ["ul", "ul", "ul", "ul", "ul", "ul"],
    defaultListElementAttributeSet: { style: "list-style-type: none;" },
    listElementAttributeSets: [],
    itemPrefixes: [], // array of strings: prefix depending on level
    idPrefix: "headings.",
    format: undefined
}; //defaultOptions
defaultOptions.bulletedListType = defaultOptions.defaultListElement;

module.exports = function (md, userOptions) {

    const util = require("util");

    const options = defaultOptions;
    if (userOptions)
        for (let index in userOptions)
            options[index] = userOptions[index];

    const slugify = function (s, used) {
        let slug = options.idPrefix +
            s.replace(' ', '-')
                .replace(/[^A-Za-z0-9\-\.\_]/g, function (match) {
                    return match.codePointAt().toString(16);
                }).toLowerCase();
        while (used[slug])
            slug += '.';
        used[slug] = slug;
        return slug;
    } // idHeadersSlugify

    // function nextNumber(number) { // number is a letter of a number as string or numeric
    //     let tryNumeric = parseInt(number);
    //     if (isNaN(tryNumeric)) {
    //         let codePoint = number.codePointAt();
    //         return String.fromCodePoint(++codePoint);
    //     } else
    //         return (++tryNumeric).toString();
    // } //nextNumber

    // no magic function names:
    const tocFunctionNames = { open: "tocOpen", close: "tocClose", body: "tocBody" };
    const ruleName = "toc"; // works with null, but let's care about other plug-ins

    function createTocTree(pos, tokens, usedIds, idCounts, idSet, excludeFromTocRegex) {
        let headings = [],
            buffer = "",
            currentLevel,
            subHeadings,
            size = tokens.length,
            currentPos = pos;
        while (currentPos < size) {
            let token = tokens[currentPos];
            let heading = tokens[currentPos - 1];
            if (!heading) { currentPos++; continue; }
            let level = token.tag && parseInt(token.tag.substr(1, 1));
            let excludeFromToc = false;
            if (excludeFromTocRegex)
                excludeFromToc = excludeFromTocRegex.exec(heading.content) != null;
            if (token.type !== "heading_close"
                || options.includeLevel.indexOf(level) == -1 || heading.type !== "inline"
                || excludeFromToc) {
                currentPos++;
                continue;
            } //if
            if (currentLevel) {
                if (level > currentLevel) {
                    subHeadings = createTocTree(currentPos, tokens, usedIds, idCounts, idSet, excludeFromTocRegex);
                    buffer += subHeadings[1];
                    currentPos = subHeadings[0];
                    continue;
                } //if
                if (level < currentLevel) {
                    buffer += "
";
                    headings.push(buffer);
                    return [currentPos, listElement(currentLevel, options, headings)];
                } //if
                if (level == currentLevel) {
                    buffer += "
";
                    headings.push(buffer);
                } //if
            } else
                currentLevel = level; // We init with the first found level
            //SA!!! currentLevel is the level of the TOC item, number of '#' in '#', '##', '###'...
            // APPEND text after "
" to make PREFIX to title
            const tocSlug = idSet[idCounts.toc];
            ++idCounts.toc;
            buffer = util.format("
", tocSlug);
            let headingContent = heading.content;
            if (options.itemPrefixes)
                if (options.itemPrefixes[currentLevel - 1])
                    headingContent = options.itemPrefixes[currentLevel - 1] + headingContent;
            buffer += typeof options.format === "function" ? options.format(headingContent) : headingContent;
            buffer += "";
            currentPos++;
        } //loop
        buffer += "
";
        headings.push(buffer);
        return [tokens.length, listElement(currentLevel, options, headings)];
    } //createTocTree

    function listElement(level, options, headings) {
        let listTag = options.defaultListElement;
        if (options.listElements)
            if (options.listElements[level - 1])
                listTag = options.listElements[level - 1];
        let elementAttributes = "";
        if (options.listElementAttributeSets)
            if (options.listElementAttributeSets[level - 1])
                for (let index in options.listElementAttributeSets[level - 1])
                    elementAttributes += util.format(" %s=\"%s\"", index, options.listElementAttributeSets[level - 1][index]);
        if (options.listElementAttributeSets)
            if (options.listElementAttributeSets.length < 1)
                for (let index in options.defaultListElementAttributeSet)
                    elementAttributes += util.format(" %s=\"%s\"",
                        index,
                        options.defaultListElementAttributeSet[index]);
        return util.format("<%s%s>%s</%s>", listTag, elementAttributes, headings.join(""), listTag);
    } //listElement

    function buildIdSet(idSet, tokens, usedIds) {
        for (let index = 1; index < tokens.length; ++index) {
            const token = tokens[index];
            const previousToken = tokens[index - 1];
            if (token.type !== "heading_close" || previousToken.type !== "inline") continue;
            idSet.push(slugify(previousToken.content, usedIds));
        } //loop
    } //buildIdSet

    md.core.ruler.before("inline", "buildToc", function (state) {
        let tocRegexp = options.tocRegex;
        if (tocRegexp.constructor != RegExp)
            tocRegexp = new RegExp(options.tocRegex, "m");
        // extra global check saves time if there is no match:
        let doToc = false;
        if (tocRegexp) {
            const match = tocRegexp.exec(state.src);
            doToc = match != null;
        } //if
        let excludeFromTocRegex = options.excludeFromTocRegex;
        if (excludeFromTocRegex.constructor != RegExp)
            excludeFromTocRegex = new RegExp(options.excludeFromTocRegex, "m");
        //
        const usedIds = { headings: {}, toc: {} };
        const idCounts = { headings: 0, toc: 0 };
        const idSet = [];
        buildIdSet(idSet, state.tokens, usedIds);
        // create TOC:
        if (doToc) {
            md.renderer.rules[tocFunctionNames.open] = function (tokens, index) {
                return util.format("
", options.tocContainerClass);
            }; // open
            md.renderer.rules[tocFunctionNames.body] = function (tokens, index) {
                return createTocTree(0, state.tokens, usedIds, idCounts, idSet, excludeFromTocRegex)[1];
            }; //body
            md.renderer.rules[tocFunctionNames.close] = function (tokens, index) {
                return "
";
            }; //close
        } //if doToc
        // add id attributes:
        const headingOpenPrevious = md.renderer.rules.heading_open;
        md.renderer.rules.heading_open = function (tokens, index, userOptions, object, renderer) {
            tokens[index].attrs = tokens[index].attrs || [];
            let title = tokens[index + 1].children.reduce(function (accumulator, child) {
                return accumulator + child.content;
            }, "");
            const headingSlug = idSet[idCounts.headings];
            tokens[index].attrs.push(["id", headingSlug]);
            ++idCounts.headings;
            if (headingOpenPrevious)
                return headingOpenPrevious.apply(this, arguments);
            else
                return renderer.renderToken.apply(renderer, arguments);
            //SA!!! APPEND text to return to add prefix to heading content
        }; //md.renderer.rules.heading_open
        // detect TOC location:
        if (doToc)
            md.inline.ruler.before("text", ruleName, function toc(state, silent) {
                if (silent) return false;
                const match = tocRegexp.exec(state.src);
                if (!match) return false;
                if (match.length < 1) return false;
                state.push(tocFunctionNames.open, ruleName, 1);
                state.push(tocFunctionNames.body, ruleName, 0);
                state.push(tocFunctionNames.close, ruleName, -1);
                let newline = state.src.indexOf("\n");
                if (newline !== -1)
                    state.pos = state.pos + newline;
                else
                    state.pos = state.pos + state.posMax + 1;
                return true;
            });
    }); //md.core.ruler.before

}; //module.exports
<|endoftext|>