<commit_before>module.exports = {

//---------------------------------------------------------------------
// Action Name
//
// This is the name of the action displayed in the editor.
//---------------------------------------------------------------------

name: "Send GIF",

//---------------------------------------------------------------------
// Action Section
//
// This is the section the action will fall into.
//---------------------------------------------------------------------

section: "Image Editing",

//---------------------------------------------------------------------
// Action Subtitle
//
// This function generates the subtitle displayed next to the name.
//---------------------------------------------------------------------

subtitle: function(data) {
	const channels = ['Same Channel', 'Command Author', 'Mentioned User', 'Mentioned Channel', 'Default Channel', 'Temp Variable', 'Server Variable', 'Global Variable'];
	return `${channels[parseInt(data.channel)]} ${data.channel < 5 ? "" : `- ${data.varName2}`}`;
},

//---------------------------------------------------------------------
	 // DBM Mods Manager Variables (Optional but nice to have!)
	 //
	 // These are variables that DBM Mods Manager uses to show information
	 // about the mods for people to see in the list.
	 //---------------------------------------------------------------------

	 // Who made the mod (If not set, defaults to "DBM Mods")
	 author: "MrGold",

	 // The version of the mod (Defaults to 1.0.0)
	 version: "1.9.4", //Added in 1.9.4

	 // A short description to show on the mod line for this mod (Must be on a single line)
	 short_description: "Sends a GIF",

	 // If it depends on any other mods by name, ex: WrexMODS if the mod uses something from WrexMods

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

//---------------------------------------------------------------------
// Action Fields
//
// These are the fields for the action. These fields are customized
// by creating elements with corresponding IDs in the HTML. These
// are also the names of the fields stored in the action's JSON data.
//---------------------------------------------------------------------

fields: ["storage", "varName", "channel", "varName2", "message"],

//---------------------------------------------------------------------
// Command HTML
//
// This function returns a string containing the HTML used for
// editting actions.
//
// The "isEvent" parameter will be true if this action is being used
// for an event. Due to their nature, events lack certain information,
// so edit the HTML to reflect this.
//
// The "data" parameter stores constants for select elements to use.
// Each is an array: index 0 for commands, index 1 for events.
// The names are: sendTargets, members, roles, channels,
//                messages, servers, variables
//---------------------------------------------------------------------

html: function(isEvent, data) {
	return `

        Mod Info:

	    Created by MrGold

		Source GIF:

		<select id="storage" class="round" onchange="glob.refreshVariableList(this)">
			${data.variables[1]}
		</select>

		Variable Name:

		<input id="varName" class="round" type="text" list="variableList">

		Send To:

		<select id="channel" class="round" onchange="glob.sendTargetChange(this, 'varNameContainer2')">
			${data.sendTargets[isEvent ? 1 : 0]}
		</select>

		Variable Name:

		<input id="varName2" class="round" type="text">

	Message:

	<textarea id="message" rows="8" placeholder="Insert message here... (optional)" style="width: 508px; font-family: monospace; white-space: nowrap; resize: none;"></textarea>

`
},

//---------------------------------------------------------------------
// Action Editor Init Code
//
// When the HTML is first applied to the action editor, this code
// is also run. This helps add modifications or setup reactionary
// functions for the DOM elements.
//---------------------------------------------------------------------

init: function() {
	const {glob, document} = this;

    glob.refreshVariableList(document.getElementById('storage'));
	glob.sendTargetChange(document.getElementById('channel'), 'varNameContainer2');
},

//---------------------------------------------------------------------
// Action Bot Function
//
// This is the function for the action within the Bot's Action class.
// Keep in mind event calls won't have access to the "msg" parameter,
// so be sure to provide checks for variable existance.
//---------------------------------------------------------------------

action: function(cache) {
	const data = cache.actions[cache.index];
	const storage = parseInt(data.storage);
	const varName = this.evalMessage(data.varName, cache);
	const image = this.getVariable(storage, varName, cache);

	if(!image) {
		this.callNextAction(cache);
		return;
	}

	const channel = parseInt(data.channel);
	const varName2 = this.evalMessage(data.varName2, cache);
	const target = this.getSendTarget(channel, varName2, cache);

	if(Array.isArray(target)) {
		this.callListFunc(target, 'send', [this.evalMessage(data.message, cache), {
			files: [image]
		}]).then(function() {
			this.callNextAction(cache);
		}.bind(this)).catch(this.displayError.bind(this, data, cache));
	} else if(target && target.send) {
		target.send(this.evalMessage(data.message, cache), {
			files: [image]
		}).then(function() {
			this.callNextAction(cache);
		}.bind(this)).catch(this.displayError.bind(this, data, cache));
	} else {
		this.callNextAction(cache);
	}
},

//---------------------------------------------------------------------
// Action Bot Mod
//
// Upon initialization of the bot, this code is run. Using the bot's
// DBM namespace, one can add/modify existing functions if necessary.
// In order to reduce conflictions between mods, be sure to alias
// functions you wish to overwrite.
//---------------------------------------------------------------------

mod: function(DBM) {
}

}; // End of module
<commit_msg>Update send_gif_MOD.js<commit_after>module.exports = {

//---------------------------------------------------------------------
// Action Name
//
// This is the name of the action displayed in the editor.
//---------------------------------------------------------------------

name: "Send GIF",

//---------------------------------------------------------------------
// Action Section
//
// This is the section the action will fall into.
//---------------------------------------------------------------------

section: "Image Editing",

//---------------------------------------------------------------------
// Action Subtitle
//
// This function generates the subtitle displayed next to the name.
//---------------------------------------------------------------------

subtitle: function(data) {
	const channels = ['Same Channel', 'Command Author', 'Mentioned User', 'Mentioned Channel', 'Default Channel (Top Channel)', 'Temp Variable', 'Server Variable', 'Global Variable'];
	return `${channels[parseInt(data.channel)]} ${data.channel < 5 ? "" : `- ${data.varName2}`}`;
},

//---------------------------------------------------------------------
	 // DBM Mods Manager Variables (Optional but nice to have!)
	 //
	 // These are variables that DBM Mods Manager uses to show information
	 // about the mods for people to see in the list.
	 //---------------------------------------------------------------------

	 // Who made the mod (If not set, defaults to "DBM Mods")
	 author: "MrGold",

	 // The version of the mod (Defaults to 1.0.0)
	 version: "1.9.4", //Added in 1.9.4

	 // A short description to show on the mod line for this mod (Must be on a single line)
	 short_description: "Sends a GIF",

	 // If it depends on any other mods by name, ex: WrexMODS if the mod uses something from WrexMods

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

//---------------------------------------------------------------------
// Action Fields
//
// These are the fields for the action. These fields are customized
// by creating elements with corresponding IDs in the HTML. These
// are also the names of the fields stored in the action's JSON data.
//---------------------------------------------------------------------

fields: ["storage", "varName", "channel", "varName2", "message"],

//---------------------------------------------------------------------
// Command HTML
//
// This function returns a string containing the HTML used for
// editting actions.
//
// The "isEvent" parameter will be true if this action is being used
// for an event. Due to their nature, events lack certain information,
// so edit the HTML to reflect this.
//
// The "data" parameter stores constants for select elements to use.
// Each is an array: index 0 for commands, index 1 for events.
// The names are: sendTargets, members, roles, channels,
//                messages, servers, variables
//---------------------------------------------------------------------

html: function(isEvent, data) {
	return `

        Mod Info:

	    Created by MrGold

		Source GIF:

		<select id="storage" class="round" onchange="glob.refreshVariableList(this)">
			${data.variables[1]}
		</select>

		Variable Name:

		<input id="varName" class="round" type="text" list="variableList">

		Send To:

		<select id="channel" class="round" onchange="glob.sendTargetChange(this, 'varNameContainer2')">
			${data.sendTargets[isEvent ? 1 : 0]}
		</select>

		Variable Name:

		<input id="varName2" class="round" type="text">

	Message:

	<textarea id="message" rows="8" placeholder="Insert message here... (optional)" style="width: 508px; font-family: monospace; white-space: nowrap; resize: none;"></textarea>

`
},

//---------------------------------------------------------------------
// Action Editor Init Code
//
// When the HTML is first applied to the action editor, this code
// is also run. This helps add modifications or setup reactionary
// functions for the DOM elements.
//---------------------------------------------------------------------

init: function() {
	const {glob, document} = this;

    glob.refreshVariableList(document.getElementById('storage'));
	glob.sendTargetChange(document.getElementById('channel'), 'varNameContainer2');
},

//---------------------------------------------------------------------
// Action Bot Function
//
// This is the function for the action within the Bot's Action class.
// Keep in mind event calls won't have access to the "msg" parameter,
// so be sure to provide checks for variable existance.
//---------------------------------------------------------------------

action: function(cache) {
	const data = cache.actions[cache.index];
	const storage = parseInt(data.storage);
	const varName = this.evalMessage(data.varName, cache);
	const image = this.getVariable(storage, varName, cache);

	if(!image) {
		this.callNextAction(cache);
		return;
	}

	const channel = parseInt(data.channel);
	const varName2 = this.evalMessage(data.varName2, cache);
	const target = this.getSendTarget(channel, varName2, cache);

	if(Array.isArray(target)) {
		this.callListFunc(target, 'send', [this.evalMessage(data.message, cache), {
			files: [image]
		}]).then(function() {
			this.callNextAction(cache);
		}.bind(this)).catch(this.displayError.bind(this, data, cache));
	} else if(target && target.send) {
		target.send(this.evalMessage(data.message, cache), {
			files: [image]
		}).then(function() {
			this.callNextAction(cache);
		}.bind(this)).catch(this.displayError.bind(this, data, cache));
	} else {
		this.callNextAction(cache);
	}
},

//---------------------------------------------------------------------
// Action Bot Mod
//
// Upon initialization of the bot, this code is run. Using the bot's
// DBM namespace, one can add/modify existing functions if necessary.
// In order to reduce conflictions between mods, be sure to alias
// functions you wish to overwrite.
//---------------------------------------------------------------------

mod: function(DBM) {
}

}; // End of module
<|endoftext|>


<commit_before>JsGlobal.repeatWithPause = function(block, interval){ setInterval(function(){block.value()}, interval.num$) };
<commit_msg>Fixed / updated additional_functions<commit_after>JsGlobal.repeatWithPause = function(aFunction, interval){
	setInterval(aFunction, interval.num$);
};
<|endoftext|>


<commit_before>import Service from '@ember/service';
import EObject, { computed, setProperties } from '@ember/object';
import { filterBy } from '@ember/object/computed';
import emojiDefs from 'ember-emojione/emoji-defs';
import { assert } from '@ember/debug';
import { A } from '@ember/array';
import { htmlSafe } from '@ember/string';
import {
  EMOJI_CATEGORIES_ARRAY,
  EMOJI_TONES_ARRAY
} from "ember-emojione/-private/utils/constants";

const O = EObject.create.bind(EObject);

function filterEmojiBySkinToneCP(tone) {
  const anotherTone   = tone === 1 ? 2 : 1;
  const regexThisTone = new RegExp(`_tone${tone}$`);
  const regexAnyTone  = /_tone\d$/;

  return computed('emojiDefs', 'emoji.@each.id', function () {
    const emojiDefs = this.get('emojiDefs');

    const emoji =
      this
        .get('emoji')
        .filter(({id}) => {
          if (regexThisTone.test(id)) return true;
          if (regexAnyTone.test(id))  return false;
          return !emojiDefs[`${id}_tone${anotherTone}`];
        });

    return A(emoji);
  });
}

export default Service.extend({
  emojiDefs,

  categories: A([
    O({id: "people",   name: "Smileys & People", icon: htmlSafe(":grinning:")}),
    O({id: "nature",   name: "Animals & Nature", icon: htmlSafe(":deciduous_tree:")}),
    O({id: "food",     name: "Food & Drink",     icon: htmlSafe(":pizza:")}),
    O({id: "activity", name: "Activity",         icon: htmlSafe(":cartwheel:")}),
    O({id: "travel",   name: "Travel & Places",  icon: htmlSafe(":ship:")}),
    O({id: "objects",  name: "Objects",          icon: htmlSafe(":hammer:")}),
    O({id: "symbols",  name: "Symbols",          icon: htmlSafe(":heart:")}),
    O({id: "flags",    name: "Flags",            icon: htmlSafe(":flag_aq:")}),
    // "regional",
    // "modifier",
  ]),

  emojiCategoryIds: EMOJI_CATEGORIES_ARRAY,
  emojiToneIds: EMOJI_TONES_ARRAY,

  _currentSkinTone: 'default',

  currentSkinTone: computed('_currentSkinTone', {
    get() {
      return this.get('_currentSkinTone');
    },
    set(key, value) {
      if (value === 'default') {
        this.set('_currentSkinTone', 'default');
        return 'default';
      }

      assert(`Attempted to set emoji skin tone to an unsupported value: ${value}`, (
        value >= 1 && value <= 5
      ));

      value = value.toString();
      this.set('_currentSkinTone', value);
      return value;
    }
  }),

  emoji: computed('emojiDefs', function () {
    const emojiDefs = this.get('emojiDefs');

    return Object
      .keys(emojiDefs)
      .map(id => this._prepareEmojo(emojiDefs, id));
  }),

  emoji__tone_default: computed('emoji.@each.id', function () {
    const emoji =
      this
        .get('emoji')
        .filter(({id}) => !/_tone\d$/.test(id));

    return A(emoji);
  }),

  emoji__tone_1: filterEmojiBySkinToneCP(1),
  emoji__tone_2: filterEmojiBySkinToneCP(2),
  emoji__tone_3: filterEmojiBySkinToneCP(3),
  emoji__tone_4: filterEmojiBySkinToneCP(4),
  emoji__tone_5: filterEmojiBySkinToneCP(5),

  init() {
    this._defineEmojiComputedProperties();
  },

  // Defines computed properties for each category and tone, for example:
  // * people: filterBy('emoji', 'category', 'people')
  // * people__tone_default: filterBy('emoji__tone_default', 'category', 'people')
  _defineEmojiComputedProperties() {
    this.get('emojiCategoryIds').forEach(category => {
      this.set(category, filterBy('emoji', 'category', category));

      this.get('emojiToneIds').forEach(tone => {
        const propertyName = `${category}__tone_${tone}`;
        const dependentKey = `emoji__tone_${tone}`;

        this.set(propertyName, filterBy(dependentKey, 'category', category));
      });
    });
  },

  _prepareEmojo(emojiDefs, id) {
    const emojo = emojiDefs[id];

    let title = emojo.shortname;

    if (id.replace(/_/g, ' ') !== emojo.name) {
      title += ` (${emojo.name})`;
    }

    // Prepare search field
    const filterable =
      [
        [emojo.name, emojo.code_decimal, emojo.shortname, emojo.unicode],
        emojo.aliases,
        emojo.aliases_ascii,
        emojo.keywords
      ]
        .reduce((result, arr) => result.concat(arr), []) // flatten
        .filter(item => item && item.length) // remove empty
        .join(' ');

    setProperties(emojo, {
      id,
      title,
      filterable
    });

    return emojo;
  }
});
<commit_msg>Fix deprecation id `ember-meta.descriptor-on-object`<commit_after>import Service from '@ember/service';
import EObject, { computed, defineProperty, setProperties } from '@ember/object';
import { filterBy } from '@ember/object/computed';
import emojiDefs from 'ember-emojione/emoji-defs';
import { assert } from '@ember/debug';
import { A } from '@ember/array';
import { htmlSafe } from '@ember/string';
import {
  EMOJI_CATEGORIES_ARRAY,
  EMOJI_TONES_ARRAY
} from "ember-emojione/-private/utils/constants";

const O = EObject.create.bind(EObject);

function filterEmojiBySkinToneCP(tone) {
  const anotherTone   = tone === 1 ? 2 : 1;
  const regexThisTone = new RegExp(`_tone${tone}$`);
  const regexAnyTone  = /_tone\d$/;

  return computed('emojiDefs', 'emoji.@each.id', function () {
    const emojiDefs = this.get('emojiDefs');

    const emoji =
      this
        .get('emoji')
        .filter(({id}) => {
          if (regexThisTone.test(id)) return true;
          if (regexAnyTone.test(id))  return false;
          return !emojiDefs[`${id}_tone${anotherTone}`];
        });

    return A(emoji);
  });
}

export default Service.extend({
  emojiDefs,

  categories: A([
    O({id: "people",   name: "Smileys & People", icon: htmlSafe(":grinning:")}),
    O({id: "nature",   name: "Animals & Nature", icon: htmlSafe(":deciduous_tree:")}),
    O({id: "food",     name: "Food & Drink",     icon: htmlSafe(":pizza:")}),
    O({id: "activity", name: "Activity",         icon: htmlSafe(":cartwheel:")}),
    O({id: "travel",   name: "Travel & Places",  icon: htmlSafe(":ship:")}),
    O({id: "objects",  name: "Objects",          icon: htmlSafe(":hammer:")}),
    O({id: "symbols",  name: "Symbols",          icon: htmlSafe(":heart:")}),
    O({id: "flags",    name: "Flags",            icon: htmlSafe(":flag_aq:")}),
    // "regional",
    // "modifier",
  ]),

  emojiCategoryIds: EMOJI_CATEGORIES_ARRAY,
  emojiToneIds: EMOJI_TONES_ARRAY,

  _currentSkinTone: 'default',

  currentSkinTone: computed('_currentSkinTone', {
    get() {
      return this.get('_currentSkinTone');
    },
    set(key, value) {
      if (value === 'default') {
        this.set('_currentSkinTone', 'default');
        return 'default';
      }

      assert(`Attempted to set emoji skin tone to an unsupported value: ${value}`, (
        value >= 1 && value <= 5
      ));

      value = value.toString();
      this.set('_currentSkinTone', value);
      return value;
    }
  }),

  emoji: computed('emojiDefs', function () {
    const emojiDefs = this.get('emojiDefs');

    return Object
      .keys(emojiDefs)
      .map(id => this._prepareEmojo(emojiDefs, id));
  }),

  emoji__tone_default: computed('emoji.@each.id', function () {
    const emoji =
      this
        .get('emoji')
        .filter(({id}) => !/_tone\d$/.test(id));

    return A(emoji);
  }),

  emoji__tone_1: filterEmojiBySkinToneCP(1),
  emoji__tone_2: filterEmojiBySkinToneCP(2),
  emoji__tone_3: filterEmojiBySkinToneCP(3),
  emoji__tone_4: filterEmojiBySkinToneCP(4),
  emoji__tone_5: filterEmojiBySkinToneCP(5),

  init() {
    this._defineEmojiComputedProperties();
  },

  // Defines computed properties for each category and tone, for example:
  // * people: filterBy('emoji', 'category', 'people')
  // * people__tone_default: filterBy('emoji__tone_default', 'category', 'people')
  _defineEmojiComputedProperties() {
    this.get('emojiCategoryIds').forEach(category => {
      defineProperty(this, category, filterBy('emoji', 'category', category));

      this.get('emojiToneIds').forEach(tone => {
        const propertyName = `${category}__tone_${tone}`;
        const dependentKey = `emoji__tone_${tone}`;

        defineProperty(this, propertyName, filterBy(dependentKey, 'category', category));
      });
    });
  },

  _prepareEmojo(emojiDefs, id) {
    const emojo = emojiDefs[id];

    let title = emojo.shortname;

    if (id.replace(/_/g, ' ') !== emojo.name) {
      title += ` (${emojo.name})`;
    }

    // Prepare search field
    const filterable =
      [
        [emojo.name, emojo.code_decimal, emojo.shortname, emojo.unicode],
        emojo.aliases,
        emojo.aliases_ascii,
        emojo.keywords
      ]
        .reduce((result, arr) => result.concat(arr), []) // flatten
        .filter(item => item && item.length) // remove empty
        .join(' ');

    setProperties(emojo, {
      id,
      title,
      filterable
    });

    return emojo;
  }
});
<|endoftext|>


<commit_before>'use strict';

const trackArtSelector = '.ytmusic-player-bar.image';
const artistSelectors = [
	'.ytmusic-player-bar.byline [href^=channel]',
	'.ytmusic-player-bar.byline [href*="browse/FEmusic_library_privately_owned_artist_detaila_"]',
];

const trackSelector = '.ytmusic-player-bar.title';
const adSelector = '.ytmusic-player-bar.advertisement';

const playButtonSelector =
	'.ytmusic-player-bar.play-pause-button #icon > svg > g > path';
const playingPath = 'M6 19h4V5H6v14zm8-14v14h4V5h-4z';

Connector.playerSelector = 'ytmusic-player-bar';

Connector.getTrackArt = () => {
	const trackArtUrl = Util.extractImageUrlFromSelectors(trackArtSelector);
	if (trackArtUrl) {
		return trackArtUrl.substring(0, trackArtUrl.lastIndexOf('='));
	}
	return null;
};

Connector.albumSelector = [
	'.ytmusic-player-bar .yt-formatted-string.style-scope.yt-simple-endpoint[href*="browse/MPREb_"]',
	'.ytmusic-player-bar .yt-formatted-string.style-scope.yt-simple-endpoint[href*="browse/FEmusic_library_privately_owned_release_detailb_"]',
];

function hasVideoAlbum() {
	return !!Connector.getAlbum();
}

Connector.getArtistTrack = () => {
	let artist;
	let track;

	if (hasVideoAlbum()) {
		artist = getArtists();
		track = Util.getTextFromSelectors(trackSelector);
	} else {
		({ artist, track } = Util.processYtVideoTitle(
			Util.getTextFromSelectors(trackSelector)
		));
		if (!artist) {
			artist = getArtists();
		}
	}
	return { artist, track };
};

Connector.timeInfoSelector = '.ytmusic-player-bar.time-info';

Connector.isPlaying = () => {
	return Util.getAttrFromSelectors(playButtonSelector, 'd') === playingPath;
};

Connector.isScrobblingAllowed = () => !Util.isElementVisible(adSelector);

function getArtists() {
	// FIXME Use Array.from after jQuery support will be removed
	const artistElements = Util.queryElements(artistSelectors);
	return artistElements && Util.joinArtists(artistElements.toArray());
}

function filterYoutubeIfNonAlbum(text) {
	return hasVideoAlbum() ? text : MetadataFilter.youtube(text);
}

const youtubeMusicFilter = MetadataFilter.createFilter({
	track: [
		filterYoutubeIfNonAlbum,
		MetadataFilter.removeRemastered,
		MetadataFilter.removeLive,
	],
	album: [MetadataFilter.removeRemastered, MetadataFilter.removeLive],
});

Connector.applyFilter(youtubeMusicFilter);
<commit_msg>Fix youtube-music connector (#2748)<commit_after>'use strict';

const trackArtSelector = '.ytmusic-player-bar.image';

const artistSelectors = [
	'.ytmusic-player-bar.byline [href*="channel/"]:not([href*="channel/MPREb_"])',
	'.ytmusic-player-bar.byline [href*="feed/music_library_privately_owned_artist_detaila_"]',
];

const trackSelector = '.ytmusic-player-bar.title';
const adSelector = '.ytmusic-player-bar.advertisement';

const playButtonSelector =
	'.ytmusic-player-bar.play-pause-button #icon > svg > g > path';
const playingPath = 'M6 19h4V5H6v14zm8-14v14h4V5h-4z';

Connector.playerSelector = 'ytmusic-player-bar';

Connector.getTrackArt = () => {
	const trackArtUrl = Util.extractImageUrlFromSelectors(trackArtSelector);
	if (trackArtUrl) {
		return trackArtUrl.substring(0, trackArtUrl.lastIndexOf('='));
	}
	return null;
};

Connector.albumSelector = [
	'.ytmusic-player-bar [href*="channel/MPREb_"]',
	'.ytmusic-player-bar [href*="feed/music_library_privately_owned_release_detailb_"]',
];

function hasVideoAlbum() {
	return !!Connector.getAlbum();
}

Connector.getArtistTrack = () => {
	let artist;
	let track;

	if (hasVideoAlbum()) {
		artist = getArtists();
		track = Util.getTextFromSelectors(trackSelector);
	} else {
		({ artist, track } = Util.processYtVideoTitle(
			Util.getTextFromSelectors(trackSelector)
		));
		if (!artist) {
			artist = getArtists();
		}
	}
	return { artist, track };
};

Connector.timeInfoSelector = '.ytmusic-player-bar.time-info';

Connector.isPlaying = () => {
	return Util.getAttrFromSelectors(playButtonSelector, 'd') === playingPath;
};

Connector.isScrobblingAllowed = () => !Util.isElementVisible(adSelector);

function getArtists() {
	// FIXME Use Array.from after jQuery support will be removed
	const artistElements = Util.queryElements(artistSelectors);
	return artistElements && Util.joinArtists(artistElements.toArray());
}

function filterYoutubeIfNonAlbum(text) {
	return hasVideoAlbum() ? text : MetadataFilter.youtube(text);
}

const youtubeMusicFilter = MetadataFilter.createFilter({
	track: [
		filterYoutubeIfNonAlbum,
		MetadataFilter.removeRemastered,
		MetadataFilter.removeLive,
	],
	album: [MetadataFilter.removeRemastered, MetadataFilter.removeLive],
});

Connector.applyFilter(youtubeMusicFilter);
<|endoftext|>


<commit_before>const LANGUAGE_COLORS = {
  Scala: '#BB0017',
  Clojure: '#00AA00',
  JS: '#CCAA00',
  VB: '#663300',
  'C#': '#7600BC',
  Go: '#996633',
  HTML5: '#FF0032',
  Python: '#0000CC',
  'C++': '#FF0099',
  Groovy: '#2196F3',
  Java: '#006600',
  ObjectiveC: '#FF8C00',
  CSS: '#0099FF',
  DEFAULT: '#EEE'
};

export default LANGUAGE_COLORS;
<commit_msg>fix groovy color<commit_after>const LANGUAGE_COLORS = {
  Scala: '#BB0017',
  Clojure: '#00AA00',
  JS: '#CCAA00',
  VB: '#663300',
  'C#': '#7600BC',
  Go: '#996633',
  HTML5: '#FF0032',
  Python: '#0000CC',
  'C++': '#FF0099',
  Groovy: '#00C92',
  Java: '#006600',
  ObjectiveC: '#FF8C00',
  CSS: '#0099FF',
  DEFAULT: '#EEE'
};

export default LANGUAGE_COLORS;
<|endoftext|>


<commit_before>//@flow
import React from 'react';
import {
  View,
  Text,
  Dimensions,
  TouchableWithoutFeedback,
  PanResponder,
  RefreshControl,
} from 'react-native';
import { Actions } from 'react-native-router-flux';
import Spinner from 'react-native-spinkit';
import {
  Container,
  Navbar
} from 'navbar-native';
import { Button } from 'react-native-elements'
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';

import { parseArticleContent } from '../../parser';
import { updateLastRead } from '../../ducks/directory';
import parseContent from '../../utils/parseContent';

//在切换页面的时候,发送通知,切换index
type Props = {
  novel: Novel,
  directory: Array<Article>,
  index: number, // start
};

class Reader extends React.Component {
  realm: Realm;
  props: Props;
  state: {
    index: number,//current
    refetch: number,
    fetching: bool,
    fontSize: number,
    lines:Array<string>,
    page:number,
    showSetting:bool,
  };

  constructor(props: Props) {
    super(props);
    this.state = {
      index: parseInt(props.index),
      refetch: 0,
      fetching: true,
      fontSize: 26,
      lines:[],
      page:1,
      showSetting:false,
    };
    this.realm = realmFactory();
  }

  fetchContent = (index: number, refresh: bool = false) => {
    let article: Article = this.props.directory.get(index);
    if (!article) {
      return;
    }
    this._isMounted && this.setState({
      fetching: true,
      index
    });

    this.realm.write(() => {
      this.props.novel.lastReadIndex = index;
      this.props.novel.lastReadTitle = article.get('title');
    });

    parseArticleContent(this.props.novel.directoryUrl, article.get('url'), refresh).then((content: string) => {
      var {height, width} = Dimensions.get('window');
      lineWidth = Math.floor((width - 40) * 2 / this.state.fontSize);
      let lines = parseContent(content, lineWidth);
      lines.unshift(article.get('title')+"\n");
      this._isMounted && this.setState({
        fetching: false,
        page:1,
        lines,
        index
      });
    }).done(() => {
      //load more data
      for (var i = 1; i <= 5; i++) {
        if (this.props.directory.get(index + i)) {
          parseArticleContent(this.props.novel.directoryUrl, this.props.directory.getIn([index + i, 'url'])).catch(e => {
            console.log(e);
          });
        }
      }
    });

  }

  componentDidMount() {
    this.fetchContent(this.state.index);
  }

  handleGotoArticle = (index: number) => {
    this.props.updateLastRead(index);
    this.fetchContent(index);
  }

  get numbersOfLinesPerPages(){
    let {height, width} = Dimensions.get('window');
    let margin = 20;
    height -= margin*2;
    let lineHeight = this.state.fontSize + 15;
    return Math.floor(height/lineHeight);
  }

  get linesOfCurrentPage(){
    let numbersOfLinesPerPages = this.numbersOfLinesPerPages;
    let linesStart = (this.state.page-1)*numbersOfLinesPerPages;
    let content = this.state.lines.slice(linesStart,linesStart+numbersOfLinesPerPages);
    return content;
  }

  get pageCount(){
    let numbersOfLinesPerPages = this.numbersOfLinesPerPages;
    return Math.ceil(this.state.lines.length*1.0/this.numbersOfLinesPerPages)
  }

  handleContentClick = (e)=>{
    console.log(e.nativeEvent);
    if(this.state.showSetting){
      this.setState({
        showSetting:false
      });
      return ;
    }
    var {pageX,pageY} = e.nativeEvent;
    var {height, width} = Dimensions.get('window');
    if(pageY>height/3 && pageY<height*2/3
    && pageX>width/3 && pageX<width*2/3
    ){
      //show/hide navbar
      this.setState({
        showSetting:true
      });
      return ;
    }else if(pageX<width/2){
      if(this.state.page == 1){
        if(this.state.index-1>0){
          this.handleGotoArticle(this.state.index-1);
        }
      }else{
        this.setState({
          page:this.state.page-1
        });
      }

    }else{
      if(this.pageCount == this.state.page){
        if(this.state.index+1<=this.directory.length){
          this.handleGotoArticle(this.state.index+1);
        }
      }else{
        this.setState({
          page:this.state.page+1
        });
      }
    }

  }
  render() {
    var {height, width} = Dimensions.get('window');
    let current = this.props.directory.get(this.state.index);
    if (current) {

      let content,loading = false;
      if(this.state.fetching){
        loading={
          styleContainer:{
            marginTop:64
          }
        }
      }else{
        let style={
          fontSize: this.state.fontSize,
          lineHeight: Math.ceil(this.state.fontSize +15),
          fontWeight: '300',
        },
        content = (<View style={{
          flex:1,
          flexDirection:'column',
          height,
          width,
          paddingHorizontal:20,
          justifyContent:'space-between'
        }}>
          <View onPress={this.handleContentClick} style={{
            paddingTop:40,
            paddingBottom:0,
            flexGrow:1,
            backgroundColor:'red',
            width,
            height,
          }}>{this.linesOfCurrentPage.map((val,rowID)=>{
            return (<Text key={rowID} style={style}>{val}</Text>);
          })}
          </View>
          <Text style={{
            height:20,
            flexGrow:0,
            lineHeight:20,
            color:'gray'
          }}>{this.state.page}/{this.pageCount}</Text>
        </View>);
      }
      return (
        <Container
        type="plain"
        style={{
          backgroundColor: '#9FB2A1',
          position:'absolute',
          top:0,
          height,
          width
        }}>
          <Navbar
            title={current.get('title')}
            left={{
              icon: "ios-arrow-back",
              label: "返回",
              onPress: Actions.pop
            }}
            right={{
              label: "刷新",
              onPress: e => {
                this.fetchContent(this.state.index, true);
              }
            }}
            style={{
              marginTop:this.state.showSetting?0:-64,
            }}
            />
          {content}
        </Container>
      );
    } else {
      return (
        <Container>
          <Navbar
            title="没有下一章了"
            left={{
              icon: "ios-arrow-back",
              label: "返回",
              onPress: Actions.pop
            }}
            />
          <Text>没有更多内容了</Text>
        </Container>
      );
    }
  }

  _isMounted = true;
  componentWillUnmount() {
    this._isMounted = false;
  }

}
const mapStateToProps = (state, ownProps) => {
  return {
  };
};

const mapDispatchToProps = (dispatch) => {
  return {
    updateLastRead: bindActionCreators(updateLastRead, dispatch),
  };
};

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Reader);<commit_msg>no message<commit_after>//@flow
import React from 'react';
import {
  View,
  Text,
  Dimensions,
  TouchableWithoutFeedback,
  PanResponder,
  RefreshControl,
} from 'react-native';
import { Actions } from 'react-native-router-flux';
import Spinner from 'react-native-spinkit';
import {
  Container,
  Navbar
} from 'navbar-native';
import { Button } from 'react-native-elements'
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';

import { parseArticleContent } from '../../parser';
import { updateLastRead } from '../../ducks/directory';
import parseContent from '../../utils/parseContent';

//在切换页面的时候,发送通知,切换index
type Props = {
  novel: Novel,
  directory: Array<Article>,
  index: number, // start
};

class Reader extends React.Component {
  realm: Realm;
  props: Props;
  state: {
    index: number,//current
    refetch: number,
    fetching: bool,
    fontSize: number,
    lines:Array<string>,
    page:number,
    showSetting:bool,
  };

  constructor(props: Props) {
    super(props);
    this.state = {
      index: parseInt(props.index),
      refetch: 0,
      fetching: true,
      fontSize: 26,
      lines:[],
      page:1,
      showSetting:false,
    };
    this.realm = realmFactory();
  }

  fetchContent = (index: number, refresh: bool = false) => {
    let article: Article = this.props.directory.get(index);
    if (!article) {
      return;
    }
    this._isMounted && this.setState({
      fetching: true,
      index
    });

    this.realm.write(() => {
      this.props.novel.lastReadIndex = index;
      this.props.novel.lastReadTitle = article.get('title');
    });

    parseArticleContent(this.props.novel.directoryUrl, article.get('url'), refresh).then((content: string) => {
      var {height, width} = Dimensions.get('window');
      lineWidth = Math.floor((width - 40) * 2 / this.state.fontSize);
      let lines = parseContent(content, lineWidth);
      lines.unshift(article.get('title')+"\n");
      this._isMounted && this.setState({
        fetching: false,
        page:1,
        lines,
        index
      });
    }).done(() => {
      //load more data
      for (var i = 1; i <= 5; i++) {
        if (this.props.directory.get(index + i)) {
          parseArticleContent(this.props.novel.directoryUrl, this.props.directory.getIn([index + i, 'url'])).catch(e => {
            console.log(e);
          });
        }
      }
    });

  }

  componentDidMount() {
    this.fetchContent(this.state.index);
  }

  handleGotoArticle = (index: number) => {
    this.props.updateLastRead(index);
    this.fetchContent(index);
  }

  get numbersOfLinesPerPages(){
    let {height, width} = Dimensions.get('window');
    let margin = 20;
    height -= margin*2;
    let lineHeight = this.state.fontSize + 15;
    return Math.floor(height/lineHeight);
  }

  get linesOfCurrentPage(){
    let numbersOfLinesPerPages = this.numbersOfLinesPerPages;
    let linesStart = (this.state.page-1)*numbersOfLinesPerPages;
    let content = this.state.lines.slice(linesStart,linesStart+numbersOfLinesPerPages);
    return content;
  }

  get pageCount(){
    let numbersOfLinesPerPages = this.numbersOfLinesPerPages;
    return Math.ceil(this.state.lines.length*1.0/this.numbersOfLinesPerPages)
  }

  handleContentClick = (e)=>{
    if(this.state.showSetting){
      this.setState({
        showSetting:false
      });
      return ;
    }
    var {pageX,pageY} = e.nativeEvent;
    var {height, width} = Dimensions.get('window');
    if(pageY>height/3 && pageY<height*2/3
    && pageX>width/3 && pageX<width*2/3
    ){
      //show/hide navbar
      this.setState({
        showSetting:true
      });
      return ;
    }else if(pageX<width/2){
      if(this.state.page == 1){
        if(this.state.index-1>0){
          this.handleGotoArticle(this.state.index-1);
        }
      }else{
        this.setState({
          page:this.state.page-1
        });
      }

    }else{
      if(this.pageCount == this.state.page){
        console.log(this.state.index+1,this.props.directory.size);
        if(this.state.index+1<=this.props.directory.size){
          this.handleGotoArticle(this.state.index+1);
        }
      }else{
        this.setState({
          page:this.state.page+1
        });
      }
    }

  }
  render() {
    var {height, width} = Dimensions.get('window');
    let current = this.props.directory.get(this.state.index);
    if (current) {

      let content,loading = false;
      if(this.state.fetching){
        loading={
          styleContainer:{
            marginTop:64
          }
        }
      }else{
        let style={
          fontSize: this.state.fontSize,
          lineHeight: Math.ceil(this.state.fontSize +15),
          height: Math.ceil(this.state.fontSize +15),
          fontWeight: '300',
        };
        content = (<TouchableWithoutFeedback
        onPress={this.handleContentClick} >
        <View
         style={{
          flex:1,
          flexDirection:'column',
          padding:20,
          justifyContent:'space-between',
          width,
          height,
        }}
        >
          <View style={{
          }}>{this.linesOfCurrentPage.map((val,rowID)=>{
            return (<Text key={rowID} style={style}>{val}</Text>);
          })}
          </View>
          <Text style={{
            height:20,
            flexGrow:0,
            lineHeight:20,
            color:'gray'
          }}>{this.state.page}/{this.pageCount}</Text>
          </View>
        </TouchableWithoutFeedback>);
      }
      return (
        <Container
        type="plain"
        style={{
          backgroundColor: '#9FB2A1',
          position:'absolute',
          top:0,
          height,
          width
        }}>
          <Navbar
            title={current.get('title')}
            left={{
              icon: "ios-arrow-back",
              label: "返回",
              onPress: Actions.pop
            }}
            right={{
              label: "刷新",
              onPress: e => {
                this.fetchContent(this.state.index, true);
              }
            }}
            style={{
              marginTop:this.state.showSetting?0:-64,
            }}
            />
          {content}
        </Container>
      );
    } else {
      return (
        <Container>
          <Navbar
            title="没有下一章了"
            left={{
              icon: "ios-arrow-back",
              label: "返回",
              onPress: Actions.pop
            }}
            />
          <Text>没有更多内容了</Text>
        </Container>
      );
    }
  }

  _isMounted = true;
  componentWillUnmount() {
    this._isMounted = false;
  }

}
const mapStateToProps = (state, ownProps) => {
  return {
  };
};

const mapDispatchToProps = (dispatch) => {
  return {
    updateLastRead: bindActionCreators(updateLastRead, dispatch),
  };
};

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Reader);<|endoftext|>


<commit_before>import React, {Component, PropTypes} from 'react';
import {connect} from 'react-redux';
import Helmet from 'react-helmet';
import {initialize} from 'redux-form';
import {SurveyForm} from 'components';
import request from 'superagent';
import { asyncConnect } from 'redux-async-connect';

@asyncConnect({
  buttonColors: (params, helpers) => {
    if (!window.initColor) {
      request.get('http://planout.tribemedia.io/parameters?user_guid=anonymous').end(function(err, res) {
        window.initColor = res.body.init_color.value;
        window.saveColor = res.body.save_color.value;
        Promise.resolve({initColor: window.initColor, saveColor: window.saveColor});
      });
    } else {
      Promise.resolve({initColor: window.initColor, saveColor: window.saveColor});
    }
  }
})
@connect(
  () => ({}),
  {initialize})
export default class Survey extends Component {
  static propTypes = {
    initialize: PropTypes.func.isRequired
  }

  handleSubmit = (data) => {
    /*window.optimizely.push(['trackEvent', 'saveSurvey']);*/
    request.get('https://5662157414.log.optimizely.com/event?a=5662157414&d=5662157414&y=false&src=js&x5750250113=5759140016&s5692111775=gc&s5697131616=false&s5688002034=direct&tsent=1462369819.279&n=saveSurvey&u=oeu1462367115866r0.9819769772170281&wxhr=true&time=1462369819.279&f=5750250113&g=5826340299&cx2=255f22b5')
      .end(function(err, res) {
        window.alert('Data submitted! ' + JSON.stringify(data));
        this.props.initialize('survey', {});
      });

    request.get('http://abtesting.tribemedia.io:5000/participate?experiment=button_color_out&alternatives=red&alternatives=blue&client_id=12345678-1234-5678-1234-567812345678&kpi=saveSurvey')
      .end(function(err, res) {
        request.get('http://abtesting.tribemedia.io:5000/convert?experiment=button_color&client_id=12345678-1234-5678-1234-567812345678&kpi=saveSurvey')
          .end(function(err, res) {

          });
      });
  }

  handleInitialize = () => {
    this.props.initialize('survey', {
      name: window.myvar,
      email: window.myemail,
      occupation: 'Redux Wizard',
      currentlyEmployed: true,
      sex: 'male'
    });

    //window.optimizely.push(['trackEvent', 'initSurvey']);
    request.get('https://5662157414.log.optimizely.com/event?a=5662157414&d=5662157414&y=false&src=js&s5692111775=gc&s5697131616=false&s5688002034=direct&tsent=1462367147.538&n=initSurvey&u=oeu1462367115866r0.9819769772170281&wxhr=true&time=1462367147.538&f=5750250113&g=5824140674&cx2=3632d2e0')
      .end(function(err, res) {

      });

    request.get('http://abtesting.tribemedia.io:5000/participate?experiment=button_color_out&alternatives=red&alternatives=blue&client_id=12345678-1234-5678-1234-567812345678&kpi=initSurvey')
      .end(function(err, res) {
        request.get('http://abtesting.tribemedia.io:5000/convert?experiment=button_color&client_id=12345678-1234-5678-1234-567812345678&kpi=initSurvey')
          .end(function(err, res) {

          });
      });
  }

  render() {
    return (

Survey

        <Helmet title="Survey"/>

          This is an example of a form in redux in which all the state is kept within the redux store.
          All the components are pure "dumb" components.

          Things to notice:

No validation errors are shown initially.

Validation errors are only shown onBlur

Validation errors are hidden onChange when the error is rectified

valid, invalid, pristine and dirty flags
            are passed with each change

Except when you submit the form, in which case they are shown for all invalid fields.

If you click the Initialize Form button, the form will be prepopupated with some values and
            the pristine and dirty flags will be based on those values.

          Pardon the use of window.alert(), but I wanted to keep this component stateless.

          <button className="btn btn-primary" color={buttonColors.initColor} onClick={this.handleInitialize}>
             Initialize Form
          </button>

The circles to the left of the inputs correspond to flags provided by redux-form:
          Touched, Visited, Active, and Dirty.

        <SurveyForm onSubmit={this.handleSubmit}/>

    );
  }
}
<commit_msg>get properties<commit_after>import React, {Component, PropTypes} from 'react';
import {connect} from 'react-redux';
import Helmet from 'react-helmet';
import {initialize} from 'redux-form';
import {SurveyForm} from 'components';
import request from 'superagent';
import { asyncConnect } from 'redux-async-connect';

/*@asyncConnect({
  buttonColors: (params, helpers) => {
    if (!window.initColor) {
      request.get('http://planout.tribemedia.io/parameters?user_guid=anonymous').end(function(err, res) {
        window.initColor = res.body.init_color.value;
        window.saveColor = res.body.save_color.value;
        Promise.resolve({initColor: window.initColor, saveColor: window.saveColor});
      });
    } else {
      Promise.resolve({initColor: window.initColor, saveColor: window.saveColor});
    }
  }
})*/
@connect(
  () => ({}),
  {initialize})
export default class Survey extends Component {
  static propTypes = {
    initialize: PropTypes.func.isRequired
  }

  handleSubmit = (data) => {
    /*window.optimizely.push(['trackEvent', 'saveSurvey']);*/
    request.get('https://5662157414.log.optimizely.com/event?a=5662157414&d=5662157414&y=false&src=js&x5750250113=5759140016&s5692111775=gc&s5697131616=false&s5688002034=direct&tsent=1462369819.279&n=saveSurvey&u=oeu1462367115866r0.9819769772170281&wxhr=true&time=1462369819.279&f=5750250113&g=5826340299&cx2=255f22b5')
      .end(function(err, res) {
        window.alert('Data submitted! ' + JSON.stringify(data));
        this.props.initialize('survey', {});
      });

    request.get('http://abtesting.tribemedia.io:5000/participate?experiment=button_color_out&alternatives=red&alternatives=blue&client_id=12345678-1234-5678-1234-567812345678&kpi=saveSurvey')
      .end(function(err, res) {
        request.get('http://abtesting.tribemedia.io:5000/convert?experiment=button_color&client_id=12345678-1234-5678-1234-567812345678&kpi=saveSurvey')
          .end(function(err, res) {

          });
      });
  }

  handleInitialize = () => {
    this.props.initialize('survey', {
      name: window.myvar,
      email: window.myemail,
      occupation: 'Redux Wizard',
      currentlyEmployed: true,
      sex: 'male'
    });

    //window.optimizely.push(['trackEvent', 'initSurvey']);
    request.get('https://5662157414.log.optimizely.com/event?a=5662157414&d=5662157414&y=false&src=js&s5692111775=gc&s5697131616=false&s5688002034=direct&tsent=1462367147.538&n=initSurvey&u=oeu1462367115866r0.9819769772170281&wxhr=true&time=1462367147.538&f=5750250113&g=5824140674&cx2=3632d2e0')
      .end(function(err, res) {

      });

    request.get('http://abtesting.tribemedia.io:5000/participate?experiment=button_color_out&alternatives=red&alternatives=blue&client_id=12345678-1234-5678-1234-567812345678&kpi=initSurvey')
      .end(function(err, res) {
        request.get('http://abtesting.tribemedia.io:5000/convert?experiment=button_color&client_id=12345678-1234-5678-1234-567812345678&kpi=initSurvey')
          .end(function(err, res) {

          });
      });
  }

  render() {
    const buttonColors = {initColor: window.initColor, saveColor: window.saveColor };

    return (

Survey

        <Helmet title="Survey"/>

          This is an example of a form in redux in which all the state is kept within the redux store.
          All the components are pure "dumb" components.

          Things to notice:

No validation errors are shown initially.

Validation errors are only shown onBlur

Validation errors are hidden onChange when the error is rectified

valid, invalid, pristine and dirty flags
            are passed with each change

Except when you submit the form, in which case they are shown for all invalid fields.

If you click the Initialize Form button, the form will be prepopupated with some values and
            the pristine and dirty flags will be based on those values.

          Pardon the use of window.alert(), but I wanted to keep this component stateless.

          <button className="btn btn-primary" color={buttonColors.initColor} onClick={this.handleInitialize}>
             Initialize Form
          </button>

The circles to the left of the inputs correspond to flags provided by redux-form:
          Touched, Visited, Active, and Dirty.

        <SurveyForm onSubmit={this.handleSubmit}/>

    );
  }
}
<|endoftext|>


<commit_before>/*
* extend
* Visit http://createjs.com/ for documentation, updates and examples.
*
* Copyright (c) 2010 gskinner.com, inc.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/

/**
 * @module CreateJS
 */

// namespace:
this.createjs = this.createjs||{};

/**
 * @class Utility Methods
 */

/**
 * Wraps deprecated methods so they still be used, but throw warnings to developers.
 *
 *	obj.deprecatedMethod = createjs.deprecate("Old Method Name", obj._fallbackMethod);
 *
 * The recommended approach for deprecated properties is:
 *
 *	try {
 *		Obj	ect.defineProperties(object, {
 *			readyOnlyProp: { get: createjs.deprecate("readOnlyProp", function() { return this.alternateProp; }) },
 *			readWriteProp: {
 *				get: createjs.deprecate("readOnlyProp", function() { return this.alternateProp; }),
 *				set: createjs.deprecate("readOnlyProp", function(val) { this.alternateProp = val; })
 *		});
 *	} catch (e) {}
 *
 * @method deprecate
 * @param {Function} [fallbackMethod=null] A method to call when the deprecated method is used. See the example for how
 * @param {String} [name=null] The name of the method or property to display in the console warning.
 * to deprecate properties.
 * @return {Function} If a fallbackMethod is supplied, returns a closure that will call the fallback method after
 * logging the warning in the console.
 */
createjs.deprecate = function(fallbackMethod, name) {
	"use strict";
	return function() {
		console && (console.warn || console.log)("Deprecated property or method '"+name+"'. See docs for info.");
		return fallbackMethod && fallbackMethod.apply(this, arguments);
	}
};<commit_msg>Updated deprecate utility to be iOS-safe.<commit_after>/*
* extend
* Visit http://createjs.com/ for documentation, updates and examples.
*
* Copyright (c) 2010 gskinner.com, inc.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/

/**
 * @module CreateJS
 */

// namespace:
this.createjs = this.createjs||{};

/**
 * @class Utility Methods
 */

/**
 * Wraps deprecated methods so they still be used, but throw warnings to developers.
 *
 *	obj.deprecatedMethod = createjs.deprecate("Old Method Name", obj._fallbackMethod);
 *
 * The recommended approach for deprecated properties is:
 *
 *	try {
 *		Obj	ect.defineProperties(object, {
 *			readyOnlyProp: { get: createjs.deprecate("readOnlyProp", function() { return this.alternateProp; }) },
 *			readWriteProp: {
 *				get: createjs.deprecate("readOnlyProp", function() { return this.alternateProp; }),
 *				set: createjs.deprecate("readOnlyProp", function(val) { this.alternateProp = val; })
 *		});
 *	} catch (e) {}
 *
 * @method deprecate
 * @param {Function} [fallbackMethod=null] A method to call when the deprecated method is used. See the example for how
 * @param {String} [name=null] The name of the method or property to display in the console warning.
 * to deprecate properties.
 * @return {Function} If a fallbackMethod is supplied, returns a closure that will call the fallback method after
 * logging the warning in the console.
 */
createjs.deprecate = function(fallbackMethod, name) {
	"use strict";
	return function() {
		var msg = "Deprecated property or method '"+name+"'. See docs for info.";
		console && (console.warn ? console.warn(msg) : console.log(msg));
		return fallbackMethod && fallbackMethod.apply(this, arguments);
	}
};<|endoftext|>


<commit_before>'use strict';

var async = require('async'),
    mkdirp = require('mkdirp'),
    fs = require('fs'),
    spawn = require('child_process').spawn;
var discourse, config, log = [],
    oncomplete = [];
exports.name = 'Backups';
exports.version = '0.0.1';
exports.description = 'Backup Manager';
exports.configuration = {
    'auto_download': true,
    'download_dir': 'backups',
    'remove_old_backups': true,
    'auto_restore': true,
    'auto_backup': true,
    'auto_backup_time': '05:00',
    'auto_size_limit': 20971520,
    'notify_users': 'accalia'
};

exports.begin = function begin(browser, configuration) {
    discourse = browser;
    config = configuration;
    if (config.auto_backup) {
        async.forever(function (next) {
            autoBackup(next);
        });
    }
};

function autoBackup(callback) {
    var hour = parseInt(/(\d+)/.exec(config.auto_backup_time)[1], 10),
        minute = parseInt(/:(\d+)/.exec(config.auto_backup_time)[1], 10),
        now = new Date(),
        utc = new Date();
    utc.setUTCHours(hour);
    utc.setUTCMinutes(minute);
    utc.setUTCSeconds(0);
    utc.setMilliseconds(0);
    now = now.getTime();
    utc = utc.getTime();
    if (now > utc) {
        // add a day if scheduling after 23:40 UTC
        utc += 24 * 60 * 60 * 1000;
    }
    setTimeout(function () {
        discourse.log('Starting Scheduled Backup.');
        startBackup(function (err) {
            if (err) {
                discourse.warn('Backup Failed');
            } else {
                discourse.log('Backup Succeeded');
            }
            callback();
        });
    }, utc - now);
}

exports.backup = function backup(args, callback) {
    var arg = (args || []).shift() || '';
    arg = ''.toLowerCase();
    switch (arg) {
    case '':
    case 'all':
    case 'start':
    case 'download':
    case 'load':
        async.series([
            function (flow) {
                if (arg === 'all' || arg === 'start' || arg === '') {
                    return startBackup(flow);
                }
                flow(null, []);
            },
            function (flow) {
                if (arg === 'all' || arg === 'download') {
                    return downloadLatestBackup(false, flow);
                }
                flow(null, []);
            },
            function (flow) {
                if (arg === 'all' || arg === 'load') {
                    return restoreBackup(flow);
                }
                flow(null, []);
            }
        ], function (err, results) {
            var logs = err ? 'ERROR:\n' : 'SUCCESS:\n';
            results.forEach(function (i) {
                logs += i.join('\n');
            });
            callback(null, logs);
        });
        break;
    default:
        callback(null, 'Usage: !admin download [all|start|download|load]');
        break;
    }
};
exports.backup.command = 'backup';
exports.backup.description = 'Do backup things';
exports.backup.trustLevel = 8;
exports.backup.muteable = false;
exports.backup.prefix = false;

function startBackup(callback) {
    var abort = false;
    oncomplete.push(function (err, log, after) {
        if (abort) {
            return;
        }
        callback(err, log);
        after();
    });
    discourse.postForm('admin/backups', {
        'with_uploads': false
    }, function (err) {
        if (err) {
            abort = true;
            callback(err, 'Backup failed to start');
        }
    });
}

function exec(command, args, pipeTo, callback) {
    function split(data) {
        return ('' + data).split('\n').filter(function (i) {
            return !!i;
        });
    }
    if (!callback) {
        callback = pipeTo;
        pipeTo = null;
    }
    var stdout = [],
        stderr = [],
        process;
    process = spawn(command, args);
    process.stderr.on('data', function (data) {
        stderr = stderr.concat(split(data));
    });
    process.stdout.on('data', function (data) {
        if (pipeTo) {
            pipeTo.stdin.write(data);
        }
        stdout = stdout.concat(split(data));
    });
    process.on('close', function (code) {
        if (pipeTo) {
            pipeTo.stdin.end();
        }
        callback(code, stdout, stderr);
    });
    return process;
}

function afterBackup() {
    var messages = log.slice();
    async.series([
        function (flow) {
            if (!config.auto_download) {
                return flow(true);
            }
            downloadLatestBackup(true, function (err, output) {
                messages = messages.concat(output);
                flow(err);
            });
        },
        function (flow) {
            if (!config.auto_restore) {
                flow(true);
            }
            restoreBackup(function (err, output) {
                messages = messages.concat(output);
                flow(err);
            });
        }
    ], function (err) {
        console.log(err);
        mailLog(messages, function () {});
    });
}

function downloadLatestBackup(isauto, callback) {
    var messages = [];
    async.series([
        function (flow) {
            mkdirp(config.download_dir, function (err) {
                var prefix = 'DOWNLOAD: Ensuring Downloads Directory Exists: ';
                if (err) {
                    messages.push(prefix + 'Failed');
                } else {
                    messages.push(prefix + 'Success');
                }
                flow(err);
            });
        },
        function (flow) {
            if (!config.remove_old_backups) {
                return flow();
            }
            messages.push('DOWNLOAD: Removing Old Backups');
            fs.readdir(config.download_dir, function (err, files) {
                if (err) {
                    messages.push('DOWNLOAD: List Directory Failed');
                    return flow(err);
                }
                async.each(files.filter(function (i) {
                    return /tar[.]gz$/.test(i);
                }).map(function (i) {
                    return config.download_dir + '/' + i;
                }), fs.unlink, function (err) {
                    if (err) {
                        messages.push('DOWNLOAD: Removing Old Backups Failed');
                    }
                    flow(err);
                });
            });
        },
        function (flow) {
            discourse.getForm('admin/backups.json', function (err, b, data) {
                if (err) {
                    messages.push('DOWNLOAD: Error Loading Backups');
                    return flow(err);
                }
                if (isauto && data[0].size > config.auto_size_limit) {
                    messages.push('DOWNLOAD: Aborting Download As Over Limit');
                    flow(true);
                }
                var src = 'http:' + data[0].link;
                var dest = config.download_dir + '/' + data[0].filename;
                discourse.saveFile(src, dest, function (err) {
                    if (err) {
                        messages.push('DOWNLOAD: Error Downloading Backup');
                    } else {
                        messages.push('DOWNLOAD: Downloaded Backup');
                    }
                    flow(err);
                });
            });
        }
    ], function (err) {
        callback(err, messages);
    });
}

function restoreBackup(callback) {
    var src, messages = [];
    async.series([
        function (flow) {
            fs.readdir(config.download_dir, function (err, files) {
                if (err) {
                    messages.push('RESTORE: No Backup Found');
                    return flow(err);
                }
                var file = files.filter(function (i) {
                    return /tar[.]gz$/.test(i);
                });
                file.sort();
                file = file.pop();
                if (!file) {
                    return flow(true);
                }
                messages.push('RESTORE: Restoring ' + file);
                src = config.download_dir + '/' + file;
                flow();
            });
        },
        function (flow) {
            exec('psql', [
                '-d', 'discourse', '-c',
                'DROP SCHEMA backup CASCADE'
            ], function () {
                messages.push('RESTORE: Dropped Old `restore` Schema');
                flow();
            });
        },
        function (flow) {
            var psql = exec('psql', ['-d', 'discourse'], function (err) {
                if (err) {
                    messages.push('RESTORE: Load to `restore` Schema Failed');
                    return flow(err);
                }
                messages.push('RESTORE: Load to `restore` Schema Done');
                flow();
            });
            exec('tar', ['-xOzf', src, 'dump.sql'], psql, function () {});
        },
        function (flow) {
            exec('psql', [
                '-d', 'discourse', '-c',
                'ALTER SCHEMA public RENAME TO backup'
            ], function (err, out, errors) {
                if (err) {
                    messages.push('RESTORE: Rename Current Schema Failed');
                    messages = messages.concat(errors);
                    return flow(err);
                }
                messages.push('RESTORE: Rename Current Schema Succeded');
                flow();
            });
        },
        function (flow) {
            exec('psql', [
                '-d', 'discourse', '-c',
                'ALTER SCHEMA restore RENAME TO public'
            ], function (err, out, errors) {
                if (err) {
                    messages.push('RESTORE: Restore Database Failed');
                    messages = messages.concat(errors);
                    return flow(err);
                }
                messages.push('RESTORE: Restore Database Succeded');
                flow();
            });
        }
    ], function (err) {
        callback(err, messages);
    });
}

function mailLog(lines, callback) {
    var msg = 'Backup completed: \n\n```text\n' + lines.join('\n') + '\n```\n';
    discourse.createPrivateMessage(config.notify_users, 'Backup Completed', msg,
        callback);
}

exports.registerListeners = function registerListeners(callback) {
    callback(null, ['/admin/backups/logs']);
};

exports.onMessage = function onMessage(message, post, callback) {
    if (message.data && message.data.operation === 'backup') {
        if (message.data.message === '[STARTED]') {
            log = [];
        }
        if (!/^pg_dump/.test(message.data.message)) {
            log.push(message.data.message);
        }
        if (message.data.message === '[SUCCESS]' ||
            message.data.message === '[FAILED]') {
            afterBackup();
        }
    }
    callback();
};
<commit_msg> auto backup module!<commit_after>'use strict';

var async = require('async'),
    mkdirp = require('mkdirp'),
    fs = require('fs'),
    spawn = require('child_process').spawn;
var discourse, config, log = [],
    oncomplete = [],
    executing = false;
exports.name = 'Backups';
exports.version = '0.0.1';
exports.description = 'Backup Manager';
exports.configuration = {
    'enabled': false,
    'auto_download': true,
    'download_dir': 'backups',
    'remove_old_backups': true,
    'auto_restore': true,
    'auto_backup': true,
    'auto_backup_time': '05:00',
    'auto_size_limit': 20971520,
    'notify_users': 'accalia'
};

exports.begin = function begin(browser, configuration) {
    discourse = browser;
    config = configuration;
    if (!config.enabled) {
        return;
    }
    if (config.auto_backup) {
        async.forever(function (next) {
            autoBackup(next);
        });
    }
};

function autoBackup(callback) {
    var hour = parseInt(/(\d+)/.exec(config.auto_backup_time)[1], 10),
        minute = parseInt(/:(\d+)/.exec(config.auto_backup_time)[1], 10),
        now = new Date(),
        utc = new Date();
    utc.setUTCHours(hour);
    utc.setUTCMinutes(minute);
    utc.setUTCSeconds(0);
    utc.setMilliseconds(0);
    now = now.getTime();
    utc = utc.getTime();
    if (now > utc) {
        // add a day if scheduling after 23:40 UTC
        utc += 24 * 60 * 60 * 1000;
    }
    setTimeout(function () {
        discourse.log('Starting Scheduled Backup.');
        startBackup(function (err) {
            if (err) {
                discourse.warn('Backup Failed');
            } else {
                discourse.log('Backup Succeeded');
            }
            callback();
        });
    }, utc - now);
}

exports.backup = function backup(args, callback) {
    if (!config.enabled) {
        return callback(true, 'Disabled');
    }
    executing = true;
    var arg = (args || []).shift() || '';
    arg = arg.toLowerCase();
    switch (arg) {
    case '':
    case 'all':
    case 'start':
    case 'download':
    case 'load':
        async.series([
            function (flow) {
                if (arg === 'all' || arg === 'start') {
                    return startBackup(flow);
                }
                flow(null, []);
            },
            function (flow) {
                if (arg === 'all' || arg === 'download') {
                    return downloadLatestBackup(false, flow);
                }
                flow(null, []);
            },
            function (flow) {
                if (arg === 'all' || arg === 'load') {
                    return restoreBackup(flow);
                }
                flow(null, []);
            }
        ], function (err, results) {
            var logs = err ? 'ERROR:\n' : 'SUCCESS:\n';
            results.forEach(function (i) {
                logs += i.join('\n') + '\n';
            });
            executing = false;
            callback(null, logs);
        });
        break;
    default:
        executing = false;
        callback(null, 'Usage: !admin download [all|start|download|load]');
        break;
    }
};
exports.backup.command = 'backup';
exports.backup.description = 'Do backup things';
exports.backup.trustLevel = 8;
exports.backup.muteable = false;
exports.backup.prefix = false;

function startBackup(callback) {
    var abort = false;
    oncomplete.push(function (err, log, after) {
        if (abort) {
            return;
        }
        callback(err, log);
        after();
    });
    discourse.postForm('admin/backups', {
        'with_uploads': false
    }, function (err) {
        if (err) {
            abort = true;
            callback(err, 'Backup failed to start');
        }
    });
}

function exec(command, args, pipeTo, callback) {
    function split(data) {
        return ('' + data).split('\n').filter(function (i) {
            return !!i;
        });
    }
    if (!callback) {
        callback = pipeTo;
        pipeTo = null;
    }
    var stdout = [],
        stderr = [],
        process;
    process = spawn(command, args);
    process.stderr.on('data', function (data) {
        stderr = stderr.concat(split(data));
    });
    process.stdout.on('data', function (data) {
        if (pipeTo) {
            pipeTo.stdin.write(data);
        }
        stdout = stdout.concat(split(data));
    });
    process.on('close', function (code) {
        if (pipeTo) {
            pipeTo.stdin.end();
        }
        callback(code, stdout, stderr);
    });
    return process;
}

function afterBackup(success) {
    var running = executing;
    var messages = log.slice();
    var m = oncomplete;
    oncomplete = [];
    async.each(m, function (func, flow) {
        func(!success, messages, flow);
    });
    if (running) {
        return;
    }
    async.series([
        function (flow) {
            if (!config.auto_download) {
                return flow(true);
            }
            downloadLatestBackup(true, function (err, output) {
                messages = messages.concat(output);
                flow(err);
            });
        },
        function (flow) {
            if (!config.auto_restore) {
                flow(true);
            }
            restoreBackup(function (err, output) {
                messages = messages.concat(output);
                flow(err);
            });
        }
    ], function () {
        mailLog(messages, function () {});
    });
}

function downloadLatestBackup(isauto, callback) {
    var messages = [];
    async.series([
        function (flow) {
            mkdirp(config.download_dir, function (err) {
                var prefix = 'DOWNLOAD: Ensuring Downloads Directory Exists: ';
                if (err) {
                    messages.push(prefix + 'Failed');
                } else {
                    messages.push(prefix + 'Success');
                }
                flow(err);
            });
        },
        function (flow) {
            if (!config.remove_old_backups) {
                return flow();
            }
            messages.push('DOWNLOAD: Removing Old Backups');
            fs.readdir(config.download_dir, function (err, files) {
                if (err) {
                    messages.push('DOWNLOAD: List Directory Failed');
                    return flow(err);
                }
                async.each(files.filter(function (i) {
                    return /tar[.]gz$/.test(i);
                }).map(function (i) {
                    return config.download_dir + '/' + i;
                }), fs.unlink, function (err) {
                    if (err) {
                        messages.push('DOWNLOAD: Removing Old Backups Failed');
                    }
                    flow(err);
                });
            });
        },
        function (flow) {
            discourse.getForm('admin/backups.json', function (err, b, data) {
                if (err) {
                    messages.push('DOWNLOAD: Error Loading Backups');
                    return flow(err);
                }
                if (isauto && data[0].size > config.auto_size_limit) {
                    messages.push('DOWNLOAD: Aborting Download As Over Limit');
                    flow(true);
                }
                var src = 'http:' + data[0].link;
                var dest = config.download_dir + '/' + data[0].filename;
                discourse.saveFile(src, dest, function (err) {
                    if (err) {
                        messages.push('DOWNLOAD: Error Downloading Backup');
                    } else {
                        messages.push('DOWNLOAD: Downloaded Backup');
                    }
                    flow(err);
                });
            });
        }
    ], function (err) {
        callback(err, messages);
    });
}

function restoreBackup(callback) {
    var src, messages = [];
    async.series([
        function (flow) {
            fs.readdir(config.download_dir, function (err, files) {
                if (err) {
                    messages.push('RESTORE: No Backup Found');
                    return flow(err);
                }
                var file = files.filter(function (i) {
                    return /tar[.]gz$/.test(i);
                });
                file.sort();
                file = file.pop();
                if (!file) {
                    return flow(true);
                }
                messages.push('RESTORE: Restoring ' + file);
                src = config.download_dir + '/' + file;
                flow();
            });
        },
        function (flow) {
            exec('psql', [
                '-d', 'discourse', '-c',
                'DROP SCHEMA backup CASCADE'
            ], function () {
                messages.push('RESTORE: Dropped Old `restore` Schema');
                flow();
            });
        },
        function (flow) {
            var psql = exec('psql', ['-d', 'discourse'], function (err) {
                if (err) {
                    messages.push('RESTORE: Load to `restore` Schema Failed');
                    return flow(err);
                }
                messages.push('RESTORE: Load to `restore` Schema Done');
                flow();
            });
            exec('tar', ['-xOzf', src, 'dump.sql'], psql, function () {});
        },
        function (flow) {
            exec('psql', [
                '-d', 'discourse', '-c',
                'ALTER SCHEMA public RENAME TO backup'
            ], function (err, out, errors) {
                if (err) {
                    messages.push('RESTORE: Rename Current Schema Failed');
                    messages = messages.concat(errors);
                    return flow(err);
                }
                messages.push('RESTORE: Rename Current Schema Succeded');
                flow();
            });
        },
        function (flow) {
            exec('psql', [
                '-d', 'discourse', '-c',
                'ALTER SCHEMA restore RENAME TO public'
            ], function (err, out, errors) {
                if (err) {
                    messages.push('RESTORE: Restore Database Failed');
                    messages = messages.concat(errors);
                    return flow(err);
                }
                messages.push('RESTORE: Restore Database Succeded');
                flow();
            });
        }
    ], function (err) {
        callback(err, messages);
    });
}

function mailLog(lines, callback) {
    var msg = 'Backup completed: \n\n```text\n' + lines.join('\n') + '\n```\n';
    discourse.createPrivateMessage(config.notify_users, 'Backup Completed', msg,
        callback);
}

exports.registerListeners = function registerListeners(callback) {
    if (!config.enabled) {
        return callback(null, []);
    }
    callback(null, ['/admin/backups/logs']);
};

exports.onMessage = function onMessage(message, post, callback) {
    if (!config.enabled) {
        return callback();
    }
    if (message.data && message.data.operation === 'backup') {
        if (message.data.message === '[STARTED]') {
            log = [];
        }
        if (!/^pg_dump/.test(message.data.message)) {
            log.push(message.data.message);
        }
        if (message.data.message === '[SUCCESS]' ||
            message.data.message === '[FAILED]') {
            afterBackup(message.data.message === '[SUCCESS]');
        }
    }
    callback();
};
<|endoftext|>


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

var fs = require('fs');
var path = require('path');
var UglifyJS = require('uglify-js');
var CleanCSS = require('clean-css');
var ngAnnotate = require('ng-annotate');
var cssMinifier = new CleanCSS({
    noAdvanced: true, // disable advanced optimizations - selector & property merging, reduction, etc.
    keepSpecialComments: 0 // remove all css comments ('*' to keep all, 1 to keep first comment only)
});

var rootDir = process.argv[2];
var platformPath = path.join(rootDir, 'platforms');
var platform = process.env.CORDOVA_PLATFORMS;
var cliCommand = process.env.CORDOVA_CMDLINE;

// hook configuration
var isRelease = true; // by default this hook is always enabled, see the line below on how to execute it only for release
//var isRelease = (cliCommand.indexOf('--release') > -1);
var recursiveFolderSearch = true; // set this to false to manually indicate the folders to process
var foldersToProcess = [ // add other www folders in here if needed (ex. js/controllers)
    'js',
    'css'
];

if (!isRelease) {
    return;
}

console.log('cordova-uglify will always run by default, uncomment the line checking for the release flag otherwise');

switch (platform) {
    case 'android':
        platformPath = path.join(platformPath, platform, 'assets', 'www');
        break;
    case 'ios':
        platformPath = path.join(platformPath, platform, 'www');
        break;
    default:
        console.log('this hook only supports android and ios currently');
        return;
}

foldersToProcess.forEach(function(folder) {
    processFiles(path.join(platformPath, folder));
});

function processFiles(dir) {
    fs.readdir(dir, function (err, list) {
        if (err) {
            console.log('processFiles err: ' + err);
            return;
        }
        list.forEach(function(file) {
            file = path.join(dir, file);
            fs.stat(file, function(err, stat) {
                if (recursiveFolderSearch && stat.isDirectory()) {
                    processFiles(file);
                } else{
                    compress(file);
                }
            });
        });
    });
}

function compress(file) {
    var ext = path.extname(file);
    switch(ext) {
        case '.js':
            console.log('uglifying js file ' + file);
            var res = ngAnnotate(String(fs.readFileSync(file)), { add: true });
            var result = UglifyJS.minify(res.src, {
                compress: { // pass false here if you only want to minify (no obfuscate)
                    drop_console: true // remove console.* statements (log, warn, etc.)
                },
                fromString: true
            });
            fs.writeFileSync(file, result.code, 'utf8'); // overwrite the original unminified file
            break;
        case '.css':
            console.log('minifying css file ' + file);
            var source = fs.readFileSync(file, 'utf8');
            var result = cssMinifier.minify(source);
            fs.writeFileSync(file, result, 'utf8'); // overwrite the original unminified file
            break;
        default:
            console.log('encountered a ' + ext + ' file, not compressing it');
            break;
    }
}
<commit_msg>added support for 'browser' platform<commit_after>#!/usr/bin/env node

var fs = require('fs');
var path = require('path');
var UglifyJS = require('uglify-js');
var CleanCSS = require('clean-css');
var ngAnnotate = require('ng-annotate');
var cssMinifier = new CleanCSS({
    noAdvanced: true, // disable advanced optimizations - selector & property merging, reduction, etc.
    keepSpecialComments: 0 // remove all css comments ('*' to keep all, 1 to keep first comment only)
});

var rootDir = process.argv[2];
var platformPath = path.join(rootDir, 'platforms');
var platform = process.env.CORDOVA_PLATFORMS;
var cliCommand = process.env.CORDOVA_CMDLINE;

// hook configuration
var isRelease = true; // by default this hook is always enabled, see the line below on how to execute it only for release
//var isRelease = (cliCommand.indexOf('--release') > -1);
var recursiveFolderSearch = true; // set this to false to manually indicate the folders to process
var foldersToProcess = [ // add other www folders in here if needed (ex. js/controllers)
    'js',
    'css'
];

if (!isRelease) {
    return;
}

console.log('cordova-uglify will always run by default, uncomment the line checking for the release flag otherwise');

switch (platform) {
    case 'android':
        platformPath = path.join(platformPath, platform, 'assets', 'www');
        break;
    case 'ios': case 'browser':
        platformPath = path.join(platformPath, platform, 'www');
        break;
    default:
        console.log('this hook only supports android, ios, and browser currently');
        return;
}

foldersToProcess.forEach(function(folder) {
    processFiles(path.join(platformPath, folder));
});

function processFiles(dir) {
    fs.readdir(dir, function (err, list) {
        if (err) {
            console.log('processFiles err: ' + err);
            return;
        }
        list.forEach(function(file) {
            file = path.join(dir, file);
            fs.stat(file, function(err, stat) {
                if (recursiveFolderSearch && stat.isDirectory()) {
                    processFiles(file);
                } else{
                    compress(file);
                }
            });
        });
    });
}

function compress(file) {
    var ext = path.extname(file);
    switch(ext) {
        case '.js':
            console.log('uglifying js file ' + file);
            var res = ngAnnotate(String(fs.readFileSync(file)), { add: true });
            var result = UglifyJS.minify(res.src, {
                compress: { // pass false here if you only want to minify (no obfuscate)
                    drop_console: true // remove console.* statements (log, warn, etc.)
                },
                fromString: true
            });
            fs.writeFileSync(file, result.code, 'utf8'); // overwrite the original unminified file
            break;
        case '.css':
            console.log('minifying css file ' + file);
            var source = fs.readFileSync(file, 'utf8');
            var result = cssMinifier.minify(source);
            fs.writeFileSync(file, result, 'utf8'); // overwrite the original unminified file
            break;
        default:
            console.log('encountered a ' + ext + ' file, not compressing it');
            break;
    }
}
<|endoftext|>


<commit_before>'use strict';

module.exports = function (newDoc, oldDoc) {
    // allow to delete documents
    if (newDoc._deleted) {
        return;
    }
    var validTypes = ['entry', 'group', 'db', 'log'];
    // see http://emailregex.com/
    var validEmail = /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+)*\.(aero|arpa|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org|pro|travel|mobi|[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i;

    function validateOwners(doc) {
        if (!isArray(doc.$owners)) {
            throw({forbidden: 'Missing owners array'});
        }
        if (!validEmail.test(doc.$owners[0])) {
            throw({forbidden: 'First owner must be an email'});
        }
    }

    if (!newDoc.$type || validTypes.indexOf(newDoc.$type) === -1) {
        throw({forbidden: 'Invalid type'});
    }
    if (oldDoc && newDoc.$type !== oldDoc.$type) {
        throw({forbidden: 'Cannot change the type of document'});
    }

    if (newDoc.$type === 'group') {
        if (!newDoc.name) {
            throw({forbidden: 'group must have a name'});
        }
        if (validEmail.test(newDoc.name)) {
            throw({forbidden: 'group cannot be an email'});
        }
        validateOwners(newDoc);
    } else if (newDoc.$type === 'entry') {
        if (!newDoc.$id) {
            throw({forbidden: 'ID is mandatory'});
        }
        if (typeof newDoc.$creationDate !== 'number' || typeof newDoc.$modificationDate !== 'number') {
            throw({forbidden: 'Creation and modification dates are mandatory'});
        }
        if (newDoc.$modificationDate < newDoc.$creationDate) {
            throw({forbidden: 'Modification date cannot be before creation date'});
        }
        validateOwners(newDoc);
        if (oldDoc) {
            if (newDoc.$creationDate !== oldDoc.$creationDate) {
                throw({forbidden: 'Cannot change creation date'});
            }
            if (newDoc.$modificationDate < oldDoc.$modificationDate) {
                throw({forbidden: 'Modification date cannot change to the past'});
            }
            if (newDoc.$id !== oldDoc.$id) {
                throw({forbidden: 'Cannot change the ID'});
            }
        }
    } else if (newDoc.$type === 'log' && oldDoc) {
        throw({forbidden: 'Logs cannot be changed'});
    }
};
<commit_msg>Update validateDocUpdate.js<commit_after>'use strict';

module.exports = function (newDoc, oldDoc) {
    // allow to delete documents
    if (newDoc._deleted) {
        return;
    }
    var validTypes = ['entry', 'group', 'db', 'log'];
    // see http://emailregex.com/
    var validEmail = /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+)*\.(aero|arpa|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org|pro|travel|mobi|[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i;

    function validateOwners(doc) {
        if (!Array.isArray(doc.$owners)) {
            throw({forbidden: 'Missing owners array'});
        }
        if (!validEmail.test(doc.$owners[0])) {
            throw({forbidden: 'First owner must be an email'});
        }
    }

    if (!newDoc.$type || validTypes.indexOf(newDoc.$type) === -1) {
        throw({forbidden: 'Invalid type'});
    }
    if (oldDoc && newDoc.$type !== oldDoc.$type) {
        throw({forbidden: 'Cannot change the type of document'});
    }

    if (newDoc.$type === 'group') {
        if (!newDoc.name) {
            throw({forbidden: 'group must have a name'});
        }
        if (validEmail.test(newDoc.name)) {
            throw({forbidden: 'group cannot be an email'});
        }
        validateOwners(newDoc);
    } else if (newDoc.$type === 'entry') {
        if (!newDoc.$id) {
            throw({forbidden: 'ID is mandatory'});
        }
        if (typeof newDoc.$creationDate !== 'number' || typeof newDoc.$modificationDate !== 'number') {
            throw({forbidden: 'Creation and modification dates are mandatory'});
        }
        if (newDoc.$modificationDate < newDoc.$creationDate) {
            throw({forbidden: 'Modification date cannot be before creation date'});
        }
        validateOwners(newDoc);
        if (oldDoc) {
            if (newDoc.$creationDate !== oldDoc.$creationDate) {
                throw({forbidden: 'Cannot change creation date'});
            }
            if (newDoc.$modificationDate < oldDoc.$modificationDate) {
                throw({forbidden: 'Modification date cannot change to the past'});
            }
            if (newDoc.$id !== oldDoc.$id) {
                throw({forbidden: 'Cannot change the ID'});
            }
        }
    } else if (newDoc.$type === 'log' && oldDoc) {
        throw({forbidden: 'Logs cannot be changed'});
    }
};
<|endoftext|>


<commit_before>import filterObject from './utils/filter-object';
import objectMerge from './utils/object-merge';
import fillShape from './utils/fill-shape';
import _ from 'lodash';

let globalName = '__REDUX_ELECTRON_STORE__';

/**
 * Creates a store enhancer which allows a redux store to synchronize its data
 * with an electronEnhanced store in the browser process.
 * @param {Object} p - The parameters to the creator
 * @param {Object|Function|true} p.filter - Describes what data should be forwarded to this process from the browser
 * @param {Boolean} p.excludeUnfilteredState - Whether to have all data not passing the filter to be undefined (Helpful to avoid bugs at the cost of performance)
 * @param {Boolean} p.synchronous - Whether dispatches from this process should be processed within this process synchronously, or await an update from the browser process
 * @param {Function} p.stateTransformer - A function that takes data from the browser store and returns an object in the proper format of the renderer's data (if you have different reducers between processes)
 * @param {Function} p.postDispatchCallback - A callback to run after a dispatch has occurred.
 * @param {Function} p.preDispatchCallback - A callback to run before an action is dispatched.
*/
export default function electronRendererEnhancer({
  filter: filter = true,
  excludeUnfilteredState: excludeUnfilteredState = false,
  synchronous: synchronous = true,
  postDispatchCallback: postDispatchCallback = (() => null),
  preDispatchCallback: preDispatchCallback = (() => null),
  stateTransformer: stateTransformer = ((state) => state)
}) {
  return (storeCreator) => {
    return (reducer, initialState = {}) => {
      let { ipcRenderer } = require('electron');
      let remote = require('remote');

      // If this process is a webview, it will have a guestInstanceId.  Otherwise it is a window
      let rendererId = process.guestInstanceId || remote.getCurrentWindow().id;

      // Get current data from the electronEnhanced store in the browser throughthe global it creates
      let browserStore = remote.getGlobal(globalName);
      let storeData = browserStore.getState();
      let filteredStoreData = excludeUnfilteredState ? fillShape(storeData, filter) : storeData;
      let preload = stateTransformer(_.cloneDeep(filteredStoreData)); // Clonedeep is used as remote'd objects are handled in a unique way (breaks redux-immutable-state-invariant)
      let newInitialState = objectMerge(initialState, preload);

      let currentSource = process.guestInstanceId ? `webview ${rendererId}` : `window ${rendererId}`;

      // Dispatches from the browser are in the format of {type, data: {updated, deleted}}.
      let parsedReducer = (state = newInitialState, action) => {
        if (!action.source) return state; // If its not from an electronEnhanced store, ignore it
        if (!synchronous || action.source !== currentSource) {
          action.data.deleted = stateTransformer(action.data.deleted);
          action.data.updated = stateTransformer(action.data.updated);
          let filteredState = filterObject(state, action.data.deleted);
          return objectMerge(filteredState, action.data.updated);
        }

        let reduced = reducer(state, action);
        return excludeUnfilteredState ? fillShape(reduced, filter) : reduced;
      };

      let store = storeCreator(parsedReducer, newInitialState);

      // Renderers register themselves to the electronEnhanced store in the browser proecss
      ipcRenderer.send(`${globalName}-register-renderer`, {filter});

      // Dispatches from other processes are forwarded using this ipc message
      ipcRenderer.on(`${globalName}-browser-dispatch`, (event, action) => {
        if (!synchronous || action.source !== currentSource) {
          store.dispatch(action);
        }
      });

      let oldDispatchMethod = store.dispatch;
      store.dispatch = (action) => {
        action.source = action.source || currentSource;

        if (synchronous || action.source !== currentSource) {
          preDispatchCallback(action);
          oldDispatchMethod(action);
          postDispatchCallback(action);
        }

        if (action.source === currentSource) {
          ipcRenderer.send(`${globalName}-renderer-dispatch`, action);
        }
      };

      return store;
    };
  };
}
<commit_msg>Calling store.dispatch in the browser IPC event would create a loop, because the overridden dispatch does more IPC.<commit_after>import filterObject from './utils/filter-object';
import objectMerge from './utils/object-merge';
import fillShape from './utils/fill-shape';
import _ from 'lodash';

let globalName = '__REDUX_ELECTRON_STORE__';

/**
 * Creates a store enhancer which allows a redux store to synchronize its data
 * with an electronEnhanced store in the browser process.
 * @param {Object} p - The parameters to the creator
 * @param {Object|Function|true} p.filter - Describes what data should be forwarded to this process from the browser
 * @param {Boolean} p.excludeUnfilteredState - Whether to have all data not passing the filter to be undefined (Helpful to avoid bugs at the cost of performance)
 * @param {Boolean} p.synchronous - Whether dispatches from this process should be processed within this process synchronously, or await an update from the browser process
 * @param {Function} p.stateTransformer - A function that takes data from the browser store and returns an object in the proper format of the renderer's data (if you have different reducers between processes)
 * @param {Function} p.postDispatchCallback - A callback to run after a dispatch has occurred.
 * @param {Function} p.preDispatchCallback - A callback to run before an action is dispatched.
*/
export default function electronRendererEnhancer({
  filter: filter = true,
  excludeUnfilteredState: excludeUnfilteredState = false,
  synchronous: synchronous = true,
  postDispatchCallback: postDispatchCallback = (() => null),
  preDispatchCallback: preDispatchCallback = (() => null),
  stateTransformer: stateTransformer = ((state) => state)
}) {
  return (storeCreator) => {
    return (reducer, initialState = {}) => {
      let { ipcRenderer } = require('electron');
      let remote = require('remote');

      // If this process is a webview, it will have a guestInstanceId.  Otherwise it is a window
      let rendererId = process.guestInstanceId || remote.getCurrentWindow().id;

      // Get current data from the electronEnhanced store in the browser throughthe global it creates
      let browserStore = remote.getGlobal(globalName);
      let storeData = browserStore.getState();
      let filteredStoreData = excludeUnfilteredState ? fillShape(storeData, filter) : storeData;
      let preload = stateTransformer(_.cloneDeep(filteredStoreData)); // Clonedeep is used as remote'd objects are handled in a unique way (breaks redux-immutable-state-invariant)
      let newInitialState = objectMerge(initialState, preload);

      let currentSource = process.guestInstanceId ? `webview ${rendererId}` : `window ${rendererId}`;

      // Dispatches from the browser are in the format of {type, data: {updated, deleted}}.
      let parsedReducer = (state = newInitialState, action) => {
        if (!action.source) return state; // If its not from an electronEnhanced store, ignore it
        if (!synchronous || action.source !== currentSource) {
          action.data.deleted = stateTransformer(action.data.deleted);
          action.data.updated = stateTransformer(action.data.updated);
          let filteredState = filterObject(state, action.data.deleted);
          return objectMerge(filteredState, action.data.updated);
        }

        let reduced = reducer(state, action);
        return excludeUnfilteredState ? fillShape(reduced, filter) : reduced;
      };

      let store = storeCreator(parsedReducer, newInitialState);

      // Renderers register themselves to the electronEnhanced store in the browser proecss
      ipcRenderer.send(`${globalName}-register-renderer`, {filter});

      let storeDotDispatch = store.dispatch;
      let doDispatch = (action) => {
        preDispatchCallback(action);
        storeDotDispatch(action);
        postDispatchCallback(action);
      };

      // Dispatches from other processes are forwarded using this ipc message
      ipcRenderer.on(`${globalName}-browser-dispatch`, (event, action) => {
        if (!synchronous || action.source !== currentSource) {
          doDispatch(action);
        }
      });

      store.dispatch = (action) => {
        action.source = action.source || currentSource;

        if (synchronous || action.source !== currentSource) {
          doDispatch(action);
        }

        if (action.source === currentSource) {
          ipcRenderer.send(`${globalName}-renderer-dispatch`, action);
        }
      };

      return store;
    };
  };
}
<|endoftext|>