<commit_before>/*
	Чтобы работать Деодару нужен файл со шрифтом, здесь ряд функций пытаются найти его
	1) пробуется "fc-list"
	2) если ничего (на некоторых дистрибах fc-list не выдаёт пути к шрифтам), то пробуется "fc-list -v"
	3) если опять ничего пробуется более железный вариант "find /usr/share | grep ttf"
*/

var knownGoodFonts = [ // можно пометить предпочитаемый фонт звёздочкой, например '*consola.ttf'
	'PTM75F_W.ttf',
	'FiraMono-Regular.otf',
	'DejaVuSansMono.ttf',
	'DejaVuSansMono-Oblique.ttf',
	'LiberationMono-Italic.ttf',
	'LiberationMono-BoldItalic.ttf',
	'LiberationMono-Bold.ttf',
	'LiberationMono-Regular.ttf',
	'FreeMono.ttf',
	'FreeMonoBold.ttf',
	'UbuntuMono-R.ttf',
	'UbuntuMono-B.ttf',
	'UbuntuMono-BI.ttf',
	'UbuntuMono-RI.ttf',
	'Inconsolata.otf',
	'DroidSansMono.ttf',
	'Share-TechMono.ttf',
	'Share-TechMono.otf',
	'fixedU1.ttf',
	'fixed7.ttf',
	'consolai.ttf',
	'consola.ttf',
	'consolab.ttf',
	'consolaz.ttf',
	'Inconsolata.otf',
	'*FreeSerif.ttf',
], fontPattern = ['mono', 'consol', 'fix']
var favouriteFont = '', fontPath

for (var t = 0; t < knownGoodFonts.length; t++)
	if (knownGoodFonts[t].indexOf('*') == 0) {
		var s = knownGoodFonts[t]; s = s.substr(1, s.length)
		favouriteFont = s
		knownGoodFonts[t] = s
	}

function parseFontFindData(L) {
	var R = [], found
	if (fs.existsSync(L[0]) == false) return
	for (var i = 0; i < L.length; i++) if (L[i]) {
		var match = false
		for (var j = 0; j < fontPattern.length; j++) {
			if (L[i].toLowerCase().indexOf(fontPattern[j]) >= 0) { match = true; break }
		}
		if (match) {
			var s = L[i]
			for (var f = 0; f < knownGoodFonts.length; f++) {
				if (s.indexOf(knownGoodFonts[f]) >= 0)
				if (R.indexOf(s) < 0) R.push(s)
				if (s.indexOf(favouriteFont) >= 0) found = s
			}
		}
	}
	if (found) return found
	return R[0]
}

function taskFontFind() {
	var me = this
	require('child_process').exec('find /usr/share/fonts | grep \\.ttf', function(err, data) {
		foundFont = parseFontFindData(data.split('\n'))
		me.chain.fontPath = foundFont
		me.state = 'done'
		me.chain.tick()
	})
}

function taskFontSelect2() {
	var me = this
	require('child_process').exec('fc-list -v | grep "file:"', function(err, data) {
		data = data.split('\n')
		for (var i = 0; i < data.length; i++) data[i] = data[i].split('"')[1]
		var foundFont = parseFontFindData(data)
		if (foundFont) {
			me.chain.fontPath = foundFont
			me.state = 'done'
		} else me.task = taskFontFind
		me.chain.tick()
	})
}

function taskFontSelect() {
	this.chain.fontPath =
// '/home/ya/.local/share/fonts/PTM55F_W.ttf'
//'/usr/share/fonts/truetype/freefont/FreeSans.ttf'
//'/x/fonts/freefont-20120503/sfd/GnuMono.ttf'
'elfu.ttf'
//'/x/fonts/freefont-20120503/sfd/PTMono-Sym.ttf'
//'/usr/share/fonts/truetype/unifont/unifont.ttf'
	this.state = 'done'
	this.chain.tick()
	return
	var me = this
	require('child_process').exec('fc-list', function(err, data) {
		data = data.split('\n')
		for (var i = 0; i < data.length; i++) data[i] = data[i].split(':')[0]
		var foundFont = parseFontFindData(data)
		if (foundFont) {
			me.chain.fontPath = foundFont
			me.state = 'done'
		} else me.task = taskFontSelect2
		me.chain.tick()
	})
}

exports.taskFontSelect = taskFontSelect

<commit_msg>fontfind:__dirname.elfu<commit_after>/*
	Чтобы работать Деодару нужен файл со шрифтом, здесь ряд функций пытаются найти его
	1) пробуется "fc-list"
	2) если ничего (на некоторых дистрибах fc-list не выдаёт пути к шрифтам), то пробуется "fc-list -v"
	3) если опять ничего пробуется более железный вариант "find /usr/share | grep ttf"
*/

var knownGoodFonts = [ // можно пометить предпочитаемый фонт звёздочкой, например '*consola.ttf'
	'PTM75F_W.ttf',
	'FiraMono-Regular.otf',
	'DejaVuSansMono.ttf',
	'DejaVuSansMono-Oblique.ttf',
	'LiberationMono-Italic.ttf',
	'LiberationMono-BoldItalic.ttf',
	'LiberationMono-Bold.ttf',
	'LiberationMono-Regular.ttf',
	'FreeMono.ttf',
	'FreeMonoBold.ttf',
	'UbuntuMono-R.ttf',
	'UbuntuMono-B.ttf',
	'UbuntuMono-BI.ttf',
	'UbuntuMono-RI.ttf',
	'Inconsolata.otf',
	'DroidSansMono.ttf',
	'Share-TechMono.ttf',
	'Share-TechMono.otf',
	'fixedU1.ttf',
	'fixed7.ttf',
	'consolai.ttf',
	'consola.ttf',
	'consolab.ttf',
	'consolaz.ttf',
	'Inconsolata.otf',
	'*FreeSerif.ttf',
], fontPattern = ['mono', 'consol', 'fix']
var favouriteFont = '', fontPath

for (var t = 0; t < knownGoodFonts.length; t++)
	if (knownGoodFonts[t].indexOf('*') == 0) {
		var s = knownGoodFonts[t]; s = s.substr(1, s.length)
		favouriteFont = s
		knownGoodFonts[t] = s
	}

function parseFontFindData(L) {
	var R = [], found
	if (fs.existsSync(L[0]) == false) return
	for (var i = 0; i < L.length; i++) if (L[i]) {
		var match = false
		for (var j = 0; j < fontPattern.length; j++) {
			if (L[i].toLowerCase().indexOf(fontPattern[j]) >= 0) { match = true; break }
		}
		if (match) {
			var s = L[i]
			for (var f = 0; f < knownGoodFonts.length; f++) {
				if (s.indexOf(knownGoodFonts[f]) >= 0)
				if (R.indexOf(s) < 0) R.push(s)
				if (s.indexOf(favouriteFont) >= 0) found = s
			}
		}
	}
	if (found) return found
	return R[0]
}

function taskFontFind() {
	var me = this
	require('child_process').exec('find /usr/share/fonts | grep \\.ttf', function(err, data) {
		foundFont = parseFontFindData(data.split('\n'))
		me.chain.fontPath = foundFont
		me.state = 'done'
		me.chain.tick()
	})
}

function taskFontSelect2() {
	var me = this
	require('child_process').exec('fc-list -v | grep "file:"', function(err, data) {
		data = data.split('\n')
		for (var i = 0; i < data.length; i++) data[i] = data[i].split('"')[1]
		var foundFont = parseFontFindData(data)
		if (foundFont) {
			me.chain.fontPath = foundFont
			me.state = 'done'
		} else me.task = taskFontFind
		me.chain.tick()
	})
}

function taskFontSelect() {
	this.chain.fontPath =
// '/home/ya/.local/share/fonts/PTM55F_W.ttf'
//'/usr/share/fonts/truetype/freefont/FreeSans.ttf'
//'/x/fonts/freefont-20120503/sfd/GnuMono.ttf'
__dirname + '/elfu.ttf'
//'/x/fonts/freefont-20120503/sfd/PTMono-Sym.ttf'
//'/usr/share/fonts/truetype/unifont/unifont.ttf'
	this.state = 'done'
	this.chain.tick()
	return
	var me = this
	require('child_process').exec('fc-list', function(err, data) {
		data = data.split('\n')
		for (var i = 0; i < data.length; i++) data[i] = data[i].split(':')[0]
		var foundFont = parseFontFindData(data)
		if (foundFont) {
			me.chain.fontPath = foundFont
			me.state = 'done'
		} else me.task = taskFontSelect2
		me.chain.tick()
	})
}

exports.taskFontSelect = taskFontSelect

<|endoftext|>


<commit_before>define(["Tone/core/Tone", "Tone/source/Player", "Tone/component/Envelope", "Tone/component/Filter", "Tone/source/Source"],
function(Tone){

	"use strict";

	/**
	 *  @class A simple sampler instrument which plays an audio buffer
	 *         through an amplitude envelope and optionally a filter
	 *         envelope.
	 *
	 *  @constructor
	 *  @extends {Tone}
	 *  @param {string|object} url the url of the audio file
	 *  @param {function} load called when the sample has been loaded
	 */
	Tone.Sampler = function(){

		var options = this.optionsObject(arguments, ["url", "load"], Tone.Sampler.defaults);

		/**
		 *  @type {GainNode}
		 */
		this.output = this.context.createGain();

		/**
		 *  the sample player
		 *  @type {Tone.Player}
		 */
		this.player = new Tone.Player(options.url, options.load);
		this.player.retrigger = true;

		/**
		 *  the amplitude envelope
		 *  @type {Tone.Envelope}
		 */
		this.envelope = new Tone.Envelope(options.envelope);

		/**
		 *  the amplitude
		 *  @type {GainNode}
		 *  @private
		 */
		this._amplitude = this.context.createGain();

		/**
		 *  the filter envelope
		 *  @type {Tone.Envelope}
		 */
		this.filterEnvelope = new Tone.Envelope(options.filterEnvelope);

		/**
		 *  the filter
		 *  @type {BiquadFilterNode}
		 */
		this.filter = new Tone.Filter(options.filter);

		//connections
		this.chain(this.player, this.filter, this._amplitude, this.output);
		this.envelope.connect(this._amplitude.gain);
		this.filterEnvelope.connect(this.filter.frequency);
	};

	Tone.extend(Tone.Sampler);

	/**
	 *  the default parameters
	 *
	 *  @static
	 */
	Tone.Sampler.defaults = {
		"url" : null,
		"load" : function(){},
		"envelope" : {
			"attack" : 0.001,
			"decay" : 0,
			"sustain" : 1,
			"release" : 0.1
		},
		"filterEnvelope" : {
			"attack" : 0.001,
			"decay" : 0.001,
			"sustain" : 1,
			"release" : 0.5,
			"min" : 20,
			"max" : 20000
		},
		"filter" : {
			"type" : "lowpass"
		}
	};

	/**
	 *  set the parameters in bulk
	 *  @param {Object} param
	 */
	 Tone.Sampler.prototype.set = function(params){
	 	if (!this.isUndef(params.filterEnvelope)) this.filterEnvelope.set(params.filterEnvelope);
	 	if (!this.isUndef(params.envelope)) this.envelope.set(params.envelope);
	 	if (!this.isUndef(params.filter)) this.filter.set(params.filter);
	 };

	/**
	 *  start the sample
	 *
	 *  @param {Tone.Time=} [time=now] the time when the note should start
	 *  @param {number=} velocity the velocity of the note
	 */
	Tone.Sampler.prototype.triggerAttack = function(time, velocity){
		this.player.start(time);
		this.envelope.triggerAttack(time, velocity);
		this.filterEnvelope.triggerAttack(time);
	};

	/**
	 *  start the release portion of the sample
	 *
	 *  @param {Tone.Time=} [time=now] the time when the note should release
	 */
	Tone.Sampler.prototype.triggerRelease = function(time){
		this.filterEnvelope.triggerRelease(time);
		this.envelope.triggerRelease(time);
	};

	/**
	 *  trigger the attack and release after the specified duration
	 *
	 *  @param  {number|string} note     the note as a number or a string note name
	 *  @param  {Tone.Time} duration the duration of the note
	 *  @param  {Tone.Time=} time     if no time is given, defaults to now
	 *  @param  {number=} velocity the velocity of the attack (0-1)
	 */
	Tone.Sampler.prototype.triggerAttackRelease = function(note, duration, time, velocity) {
		time = this.toSeconds(time);
		this.triggerAttack(note, time, velocity);
		this.triggerRelease(time + this.toSeconds(duration));
	};

	/**
	 *  set volume method borrowed form {@link Tone.Source}
	 *  @function
	 */
	Tone.Sampler.prototype.setVolume = Tone.Source.prototype.setVolume;

	/**
	 *  clean up
	 */
	Tone.Sampler.prototype.dispose = function(){
		Tone.prototype.dispose.call(this);
		this.player.dispose();
		this.filterEnvelope.dispose();
		this.envelope.dispose();
		this.filter.dispose();
		this._amplitude.disconnect();
		this.player = null;
		this.filterEnvelope = null;
		this.envelope = null;
		this.filter = null;
		this._amplitude = null;
	};

	return Tone.Sampler;
});
<commit_msg>Sampler attack accepts a note value in semitones<commit_after>define(["Tone/core/Tone", "Tone/source/Player", "Tone/component/AmplitudeEnvelope", "Tone/component/Filter", "Tone/source/Source"],
function(Tone){

	"use strict";

	/**
	 *  @class A simple sampler instrument which plays an audio buffer
	 *         through an amplitude envelope and optionally a filter
	 *         envelope.
	 *
	 *  @constructor
	 *  @extends {Tone}
	 *  @param {string|object} url the url of the audio file
	 *  @param {function} load called when the sample has been loaded
	 */
	Tone.Sampler = function(){

		var options = this.optionsObject(arguments, ["url", "load"], Tone.Sampler.defaults);

		/**
		 *  @type {GainNode}
		 */
		this.output = this.context.createGain();

		/**
		 *  the sample player
		 *  @type {Tone.Player}
		 */
		this.player = new Tone.Player(options.url, options.load);
		this.player.retrigger = true;

		/**
		 *  the amplitude envelope
		 *  @type {Tone.Envelope}
		 */
		this.envelope = new Tone.AmplitudeEnvelope(options.envelope);

		/**
		 *  the filter envelope
		 *  @type {Tone.Envelope}
		 */
		this.filterEnvelope = new Tone.Envelope(options.filterEnvelope);

		/**
		 *  the filter
		 *  @type {BiquadFilterNode}
		 */
		this.filter = new Tone.Filter(options.filter);

		//connections
		this.chain(this.player, this.filter, this.envelope, this.output);
		this.filterEnvelope.connect(this.filter.frequency);
	};

	Tone.extend(Tone.Sampler);

	/**
	 *  the default parameters
	 *
	 *  @static
	 */
	Tone.Sampler.defaults = {
		"url" : null,
		"load" : function(){},
		"envelope" : {
			"attack" : 0.001,
			"decay" : 0,
			"sustain" : 1,
			"release" : 0.1
		},
		"filterEnvelope" : {
			"attack" : 0.001,
			"decay" : 0.001,
			"sustain" : 1,
			"release" : 0.5,
			"min" : 20,
			"max" : 20000
		},
		"filter" : {
			"type" : "lowpass"
		}
	};

	/**
	 *  set the parameters in bulk
	 *  @param {Object} param
	 */
	 Tone.Sampler.prototype.set = function(params){
	 	if (!this.isUndef(params.filterEnvelope)) this.filterEnvelope.set(params.filterEnvelope);
	 	if (!this.isUndef(params.envelope)) this.envelope.set(params.envelope);
	 	if (!this.isUndef(params.filter)) this.filter.set(params.filter);
	 };

	/**
	 *  start the sample
	 *
	 *  @param {number} [note=0] the interval in the sample should be played at 0 = no change
	 *  @param {Tone.Time} [time=now] the time when the note should start
	 *  @param {number} [velocity=1] the velocity of the note
	 */
	Tone.Sampler.prototype.triggerAttack = function(note, time, velocity){
		time = this.toSeconds(time);
		note = this.defaultArg(note, 0);
		this.player.setPlaybackRate(this.intervalToFrequencyRatio(note), time);
		this.player.start(time);
		this.envelope.triggerAttack(time, velocity);
		this.filterEnvelope.triggerAttack(time);
	};

	/**
	 *  start the release portion of the sample
	 *
	 *  @param {Tone.Time} [time=now] the time when the note should release
	 */
	Tone.Sampler.prototype.triggerRelease = function(time){
		time = this.toSeconds(time);
		this.filterEnvelope.triggerRelease(time);
		this.envelope.triggerRelease(time);
	};

	/**
	 *  trigger the attack and release after the specified duration
	 *
	 *  @param  {number|string} note     the note as a number or a string note name
	 *  @param  {Tone.Time} duration the duration of the note
	 *  @param  {Tone.Time=} time     if no time is given, defaults to now
	 *  @param  {number=} velocity the velocity of the attack (0-1)
	 */
	Tone.Sampler.prototype.triggerAttackRelease = function(duration, time, velocity) {
		time = this.toSeconds(time);
		this.triggerAttack(time, velocity);
		this.triggerRelease(time + this.toSeconds(duration));
	};

	/**
	 *  set volume method borrowed form {@link Tone.Source}
	 *  @function
	 */
	Tone.Sampler.prototype.setVolume = Tone.Source.prototype.setVolume;

	/**
	 *  clean up
	 */
	Tone.Sampler.prototype.dispose = function(){
		Tone.prototype.dispose.call(this);
		this.player.dispose();
		this.filterEnvelope.dispose();
		this.envelope.dispose();
		this.filter.dispose();
		this.player = null;
		this.filterEnvelope = null;
		this.envelope = null;
		this.filter = null;
	};

	return Tone.Sampler;
});
<|endoftext|>


<commit_before>// ==UserScript==
// @name         Asana
// @namespace    http://tampermonkey.net/
// @version      0.4
// @description  Custom Code formatting for Asana
// @author       Lukas Siemon
// @match        https://app.asana.com/*
// @grant        none
// @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.3/highlight.min.js
// ==/UserScript==

$('head').append('<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.3/styles/default.min.css" type="text/css" />');

(function() {
    'use strict';
    function highlight() {
        $('pre:not(.syntaxhighlighter *), code:not(.syntaxhighlighter *)').each(function(i, e) {
            hljs.highlightBlock(e);
        });
    }
    function rewrite(input) {
        return input.replace(
            //g,
            ''
        ).replace(
            /(<[^>]+?>)?```([a-z]*?)(?![a-z])(<br>)?([\s\S]*?)```(<[^>]+?>)?/g,
            '
$1$4$5
'
        ).replace(
            /`([^`]*?)`/g,
            "$1"
        );
    }

    setInterval(function() {
        if (!document.hidden) {
            // format description
            let desc = $("#TaskDescription-textEditor, #property_sheet\\:details_property_sheet_field\\:description");
            if (desc.length == 1) {
                let display = desc.next("p");
                if (display.length === 0) {
                    display = $("
");
                    display.click(function(e) {
                        desc.show();
                        desc.focus();
                        display.hide();
                    });
                    desc.blur(function() {
                        display.show();
                        display[0].innerHTML = rewrite(desc[0].innerHTML);
                        highlight();
                        desc.hide();
                    });
                    $(display).insertAfter(desc);
                    display[0].innerHTML = rewrite(desc[0].innerHTML);
                    highlight();
                    desc.hide();
                }
            }
            // format comments
            $("div.truncatedRichText-richText, div.commentStoryView-content").each(function(idx, e) {
                if (e.innerText.indexOf('`') != e.innerText.lastIndexOf('`')) {
                    e.innerHTML = rewrite(e.innerHTML);
                    highlight();
                }
            });
        }
    }, 1000);
})();
<commit_msg>Requires at least Tampermonkey v4.4.5549<commit_after>// ==UserScript==
// @name         Asana
// @namespace    http://tampermonkey.net/
// @version      0.5
// @description  Custom Code formatting for Asana
// @author       Lukas Siemon
// @match        https://app.asana.com/*
// @grant        none
// @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js
// ==/UserScript==

$('head').append('<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/default.min.css" type="text/css" />');

(function() {
    'use strict';
    function highlight() {
        $('pre:not(.syntaxhighlighter *), code:not(.syntaxhighlighter *)').each(function(i, e) {
            hljs.highlightBlock(e);
        });
    }
    function rewrite(input) {
        return input.replace(
            //g,
            ''
        ).replace(
            /(<[^>]+?>)?```([a-z]*?)(?![a-z])(<br>)?([\s\S]*?)```(<[^>]+?>)?/g,
            '
$1$4$5
'
        ).replace(
            /`([^`]*?)`/g,
            "$1"
        );
    }

    setInterval(function() {
        if (!document.hidden) {
            // format description
            let desc = $("#TaskDescription-textEditor, #property_sheet\\:details_property_sheet_field\\:description");
            if (desc.length == 1) {
                let display = desc.next("p");
                if (display.length === 0) {
                    display = $("
");
                    display.click(function(e) {
                        desc.show();
                        desc.focus();
                        display.hide();
                    });
                    desc.blur(function() {
                        display.show();
                        display[0].innerHTML = rewrite(desc[0].innerHTML);
                        highlight();
                        desc.hide();
                    });
                    $(display).insertAfter(desc);
                    display[0].innerHTML = rewrite(desc[0].innerHTML);
                    highlight();
                    desc.hide();
                }
            }
            // format comments
            $("div.truncatedRichText-richText, div.commentStoryView-content").each(function(idx, e) {
                if (e.innerText.indexOf('`') != e.innerText.lastIndexOf('`')) {
                    e.innerHTML = rewrite(e.innerHTML);
                    highlight();
                }
            });
        }
    }, 1000);
})();
<|endoftext|>


<commit_before>const COLLECTION_MODE = 'collection';
const SINGLE_MODE = 'single';
const RELATION_MODE = 'relation';
const RELATED_MODE = 'related';

import _ from 'lodash';
import Kapow from 'kapow';

import throwIfModel from './lib/throw_if_model';
import throwIfNoModel from './lib/throw_if_no_model';
import verifyAccept from './lib/verify_accept';
import verifyContentType from './lib/verify_content_type';
import verifyDataObject from './lib/verify_data_object';
import splitStringProps from './lib/split_string_props';
import verifyClientGeneratedId from './lib/verify_client_generated_id';
import verifyFullReplacement from './lib/verify_full_replacement';

/**
  Provides methods for pulling out json-api relevant data from
  express or hapi request instances. Also provides route level
  validation.
*/
class RequestHandler {

  /**
    The constructor.

    @constructs RequestHandler
    @param {Endpoints.Adapter} adapter
  */
  constructor (adapter, config={}) {
    this.config = config;
    this.adapter = adapter;
    this.method = config.method;

    // this used to happen in the configureController step
    // TODO: is this even needed? i believe we're only using
    // it to generate the location header response for creation
    // which is brittle and invalid anyway.
    config.typeName = adapter.typeName();
  }

  /**
    A function that, given a request, validates the request.

    @returns {Object} An object containing errors, if any.
  */
  validate (request) {

    var err;
    var validators = [verifyAccept];

    if (request.body && request.body.data) {
      var clientIdCheck =
        request.method === 'POST' &&
        // posting to a relation endpoint is for appending
        // relationships and and such is allowed (must have, really)
        // ids
        this.mode(request) !== RELATION_MODE &&
        !this.config.allowClientGeneratedIds;

      // this applies for both "base" and relation endpoints
      var fullReplacementCheck =
        request.method === 'PATCH' &&
        !this.config.allowToManyFullReplacement;

      if (clientIdCheck) {
        validators.push(verifyClientGeneratedId);
      }
      if (fullReplacementCheck) {
        validators.push(verifyFullReplacement);
      }
      validators = validators.concat([
        verifyContentType,
        verifyDataObject
      ]);
    }

    // does this.validators needs a better name? controllerValidator, userValidators?
    validators = validators.concat(this.config.validators);

    for (var validate in validators) {
      err = validators[validate](request, this);
      if (err) {
        break;
      }
    }
    return err;
  }

  /**
    Builds a query object to be passed to Endpoints.Adapter#read.

    @returns {Object} The query object on a request.
   */
  query (request) {
    // bits down the chain can mutate this config
    // on a per-request basis, so we need to clone
    var config = _.cloneDeep(this.config);

    var query = request.query;
    var include = query.include;
    var filter = query.filter;
    var fields = query.fields;
    var sort = query.sort;
    return {
      include: include ? include.split(',') : config.include,
      filter: filter ? splitStringProps(filter) : config.filter,
      fields: fields ? splitStringProps(fields) : config.fields,
      sort: sort ? sort.split(',') : config.sort
    };
  }

  /**
    Determines mode based on what request.params are available.

    @returns {String} the read mode
  */
  mode (request) {
    var hasIdParam = !!request.params.id;
    var hasRelationParam = !!request.params.relation;
    var hasRelatedParam = !!request.params.related;

    if (!hasIdParam) {
      return COLLECTION_MODE;
    }

    if (!hasRelationParam && !hasRelatedParam) {
      return SINGLE_MODE;
    }

    if (hasRelationParam) {
      return RELATION_MODE;
    }

    if (hasRelatedParam) {
      return RELATED_MODE;
    }

    throw Kapow(400, 'Unable to determine mode based on `request.params` keys.');
  }

  /**
    Creates a new instance of a model.

    @returns {Promise(Bookshelf.Model)} Newly created instance of the Model.
  */
  create (request) {
    var adapter = this.adapter;
    var method = this.method;
    var data = request.body.data;

    if (data && data.id) {
      return adapter.byId(data.id)
        .then(throwIfModel)
        .then(function() {
          return adapter.create(method, data);
        }
      );
    } else {
      return adapter.create(method, data);
    }
  }

  /**
    Queries the adapter for matching models.

    @returns {Promise(Bookshelf.Model)|Promise(Bookshelf.Collection)}
  */
  read (request) {
    var adapter = this.adapter;
    var query = this.query(request);
    var mode = this.mode(request);

    var params = request.params;
    var id = params.id;

    var related, findRelated;

    if (mode === RELATED_MODE || mode === RELATION_MODE) {
      related = params.related || params.relation;
      findRelated = adapter.related.bind(adapter, query, related, mode);
      return adapter.byId(id, related).then(throwIfNoModel).then(findRelated);
    }

    if (id) {
      // FIXME: this could collide with filter[id]=#
      query.filter.id = id;
    }
    return adapter.read(query, mode);
  }

  /**
    Edits a model.

    @returns {Promise(Bookshelf.Model)}
  */
  update (request) {
    var adapter = this.adapter;
    var method = this.method;
    var id = request.params.id;
    var relation = request.params.relation;
    var data = request.body.data;

    if (relation) {
      this.config.relationOnly = true;
      data = {
        id: id,
        type: adapter.typeName(),
        links: {}
      };
      data.links[relation] = {linkage: request.body.data};
    }

    return adapter.byId(id, [relation]).
      then(throwIfNoModel).
      then(function (model) {
        if (request.method !== 'PATCH') {
          // FIXME: This will break heterogeneous relations
          var relationType = data.links[relation].linkage[0].type;
          var existingRels = model.toJSON()[relation].map(function(rel) {
            return {
              id: String(rel.id),
              type: relationType
            };
          });

          if (request.method === 'POST') {
            data.links[relation].linkage = _.uniq(data.links[relation].linkage.concat(existingRels));
          }

          if (request.method === 'DELETE') {
            data.links[relation].linkage = _.reject(existingRels, function(rel) {
              return _.findWhere(data.links[relation].linkage, rel);
            });
          }
        }

        return adapter.update(model, method, data);
      }).catch(function(e) {
        // FIXME: This may only work for SQLITE3, but tries to be general
        if (e.message.toLowerCase().indexOf('null') !== -1) {
          Kapow.wrap(e, 409);
        }
        throw e;
      });
  }

  /**
    Deletes a model.

    @returns {Promise(Bookshelf.Model)}
  */
  destroy (request) {
    var method = this.method;
    var adapter = this.adapter;
    var id = request.params.id;

    return adapter.byId(id).then(function (model) {
      if (model) {
        return adapter.destroy(model, method);
      }
    });
  }

}

export default RequestHandler;
<commit_msg>refactor request-handler<commit_after>const COLLECTION_MODE = 'collection';
const SINGLE_MODE = 'single';
const RELATION_MODE = 'relation';
const RELATED_MODE = 'related';

import _ from 'lodash';
import Kapow from 'kapow';

import throwIfModel from './lib/throw_if_model';
import throwIfNoModel from './lib/throw_if_no_model';
import verifyAccept from './lib/verify_accept';
import verifyContentType from './lib/verify_content_type';
import verifyDataObject from './lib/verify_data_object';
import splitStringProps from './lib/split_string_props';
import verifyClientGeneratedId from './lib/verify_client_generated_id';
import verifyFullReplacement from './lib/verify_full_replacement';

/**
  Provides methods for pulling out json-api relevant data from
  express or hapi request instances. Also provides route level
  validation.
*/
class RequestHandler {

  /**
    The constructor.

    @constructs RequestHandler
    @param {Endpoints.Store.*} store
  */
  constructor (config={}) {
    this.config = config;
  }

  get method() {
    return this.config.method;
  }

  get store() {
    return this.config.store;
  }

  get model() {
    return this.config.model;
  }

  /**
    A function that, given a request, validates the request.

    @returns {Object} An object containing errors, if any.
  */
  validate (request) {

    var err;
    var validators = [verifyAccept];

    if (request.body && request.body.data) {
      var clientIdCheck =
        request.method === 'POST' &&
        // posting to a relation endpoint is for appending
        // relationships and and such is allowed (must have, really)
        // ids
        this.mode(request) !== RELATION_MODE &&
        !this.config.allowClientGeneratedIds;

      // this applies for both "base" and relation endpoints
      var fullReplacementCheck =
        request.method === 'PATCH' &&
        !this.config.allowToManyFullReplacement;

      if (clientIdCheck) {
        validators.push(verifyClientGeneratedId);
      }
      if (fullReplacementCheck) {
        validators.push(verifyFullReplacement);
      }
      validators = validators.concat([
        verifyContentType,
        verifyDataObject
      ]);
    }

    // does this.validators needs a better name? controllerValidator, userValidators?
    validators = validators.concat(this.config.validators);

    for (var validate in validators) {
      err = validators[validate](request, this);
      if (err) {
        break;
      }
    }
    return err;
  }

  /**
    Builds a query object to be passed to Endpoints.Adapter#read.

    @returns {Object} The query object on a request.
   */
  query (request) {
    // bits down the chain can mutate this config
    // on a per-request basis, so we need to clone
    const config = _.cloneDeep(_.omit(this.config, ['store', 'model']));
    const {include, filter, fields, sort} = request.query;
    return {
      include: include ? include.split(',') : config.include,
      filter: filter ? splitStringProps(filter) : config.filter,
      fields: fields ? splitStringProps(fields) : config.fields,
      sort: sort ? sort.split(',') : config.sort
    };
  }

  /**
    Determines mode based on what request.params are available.

    @returns {String} the read mode
  */
  mode (request) {
    const hasIdParam = !!request.params.id;
    const hasRelationParam = !!request.params.relation;
    const hasRelatedParam = !!request.params.related;

    if (!hasIdParam) {
      return COLLECTION_MODE;
    }

    if (!hasRelationParam && !hasRelatedParam) {
      return SINGLE_MODE;
    }

    if (hasRelationParam) {
      return RELATION_MODE;
    }

    if (hasRelatedParam) {
      return RELATED_MODE;
    }

    throw Kapow(400, 'Unable to determine mode based on `request.params` keys.');
  }

  /**
    Creates a new instance of a model.

    @returns {Promise(Bookshelf.Model)} Newly created instance of the Model.
  */
  create (request) {
    const {store, method, model} = this;
    const data = request.body.data;
    if (data && data.id) {
      return store.byId(model, data.id)
        .then(throwIfModel)
        .then(function() {
          return store.create(model, method, data);
        }
      );
    } else {
      return store.create(model, method, data);
    }
  }

  /**
    Queries the store for matching models.

    @returns {Promise(Bookshelf.Model)|Promise(Bookshelf.Collection)}
  */
  read (request) {
    const {store, model} = this;
    const query = this.query(request);
    const params = request.params;
    const id = params.id;
    if (id) {
      // FIXME: this could collide with filter[id]=#
      query.filter.id = id;
      query.singleResult = true;
    }
    return store.read(model, query);
  }

  readRelated (request) {
    const {store, model} = this;
    const id = request.params.id;
    const relation = request.params.related;
    const query = this.query(request);
    return store.readRelated(model, id, relation, query);
  }

  readRelation (request) {
    const {store, model} = this;
    const id = request.params.id;
    const relation = request.params.relation;
    const query = this.query(request);
    return store.readRelation(model, id, relation, query);
  }

  /**
    Edits a model.

    @returns {Promise(Bookshelf.Model)}
  */
  update (request) {
    const {store, method, model} = this;
    const id = request.params.id;
    const relation = request.params.relation;

    var data = request.body.data;

    if (relation) {
      this.config.relationOnly = true;
      data = {
        id: id,
        type: store.type(model),
        links: {}
      };
      data.links[relation] = {linkage: request.body.data};
    }

    return store.byId(model, id, [relation]).
      then(throwIfNoModel).
      then(function (model) {
        if (request.method !== 'PATCH') {
          // FIXME: This will break heterogeneous relations
          var relationType = data.links[relation].linkage[0].type;
          var existingRels = model.toJSON()[relation].map(function(rel) {
            return {
              id: String(rel.id),
              type: relationType
            };
          });

          if (request.method === 'POST') {
            data.links[relation].linkage = _.uniq(data.links[relation].linkage.concat(existingRels));
          }

          if (request.method === 'DELETE') {
            data.links[relation].linkage = _.reject(existingRels, function(rel) {
              return _.findWhere(data.links[relation].linkage, rel);
            });
          }
        }

        return store.update(model, method, data);
      }).catch(function(e) {
        // FIXME: This may only work for SQLITE3, but tries to be general
        if (e.message.toLowerCase().indexOf('null') !== -1) {
          Kapow.wrap(e, 409);
        }
        throw e;
      });
  }

  /**
    Deletes a model.

    @returns {Promise(Bookshelf.Model)}
  */
  destroy (request) {
    const {method, store, model} = this;
    const id = request.params.id;

    return store.byId(model, id).then(function (model) {
      if (model) {
        return store.destroy(model, method);
      }
    });
  }

}

export default RequestHandler;
<|endoftext|>


<commit_before>/**
 * VIE - Vienna IKS Editables
 * (c) 2011 Henri Bergius, IKS Consortium
 */
(function() {
    // Export the VIE namespace for both CommonJS and the browser
    var VIE;
    if (typeof exports !== 'undefined') {
        VIE = exports;
    } else {
        VIE = this.VIE = {};
    }

    // Require underscore using CommonJS require if it isn't yet loaded
    var _ = this._;
    if (!_ && (typeof require !== 'undefined')) { _ = require('underscore')._; }
    if (!_) {
        throw 'VIE requires underscore.js to be available';
    }

    // Require Backbone using CommonJS require if it isn't yet loaded
    var Backbone = this.Backbone;
    if (!Backbone && (typeof require !== 'undefined')) { Backbone = require('backbone'); }
    if (!Backbone) {
        throw 'VIE requires Backbone.js to be available';
    }

    // Require jQuery using CommonJS require if it isn't yet loaded
    var jQuery = this.jQuery;
    if (!jQuery && (typeof require !== 'undefined')) { jQuery = require('jquery'); }
    if (!jQuery) {
        throw 'VIE requires jQuery to be available';
    }

    // Backbone Model for RDF entities
    VIE.RDFEntity = Backbone.Model.extend({
        namespaces: {},
        type: '',

        toJSONLD: function() {
            var instance = this;
            var instanceLD = {};
            var property;

            if (typeof instance.id !== 'undefined') {
                instanceLD['@'] = '<' + instance.id + '>';
            } else {
                instanceLD['@'] = instance.cid.replace('c', '_:bnode');
            }

            if (instance.namespaces.length > 0) {
                instanceLD['#'] = instance.namespaces;
            }

            if (instance.type) {
                instanceLD.a = instance.type;
            }

            for (property in instance.attributes) {
                if (instance.attributes.hasOwnProperty(property)) { //  && typeof instance.attributes[property] != "function"
                    if (['id'].indexOf(property) === -1) {
                        instanceLD[property] = instance.attributes[property];
                    }
                }
            }
            return instanceLD;
        }
    });

    // Backbone View for RDF entities represented in RDFa
    VIE.RDFaView = Backbone.View.extend({
        // Ensure view gets updated when properties of the entity change
        initialize: function() {
            _.bindAll(this, 'render');
            this.model.bind('change', this.render);
        },

        render: function() {
            VIE.RDFa.writeEntity(this.el, this.model.toJSONLD());
            return this;
        }
    });

    // Entity Manager keeps track of all RDFa entities loaded via VIE
    VIE.EntityManager = {
        Entities: {},
        allEntities: [],

        Types: {},

        getBySubject: function(id) {
            if (typeof VIE.EntityManager.Entities[id] === 'undefined') {
                return null;
            }
            return VIE.EntityManager.Entities[id];
        },

        getByJSONLD: function(jsonld) {
            var entityInstance;
            var properties;

            properties = VIE.EntityManager._JSONtoProperties(jsonld);

            if (typeof jsonld['@'] !== 'undefined') {
                entityInstance = VIE.EntityManager.getBySubject(jsonld['@']);
                if (entityInstance) {
                    entityInstance.set(properties);
                    return entityInstance;
                }
            }

            entityInstance = new VIE.RDFEntity(properties);

            if (typeof jsonld['#'] !== 'undefined') {
                entityInstance.namespaces = jsonld['#'];
            }

            if (typeof jsonld.a !== 'undefined') {
                entityInstance.type = jsonld.a;
            }

            if (typeof jsonld['@'] !== 'undefined') {
                entityInstance.id = jsonld['@'];
                VIE.EntityManager.Entities[entityInstance.id] = entityInstance;
            }

            VIE.EntityManager.allEntities.push(entityInstance);

            return entityInstance;
        },

        // Clean up JSON-LD so that it can be used as properties in a Backbone Model
        _JSONtoProperties: function(jsonld) {
            var properties;
            properties = jQuery.extend({}, jsonld);
            delete properties['@'];
            delete properties.a;
            delete properties['#'];
            return properties;
        }
    };

    // Mapping between RDFa and Backbone models
    VIE.RDFaEntities = {
        Views: [],

        // Create a Backbone model instance for a RDFa-marked element
        getInstance: function(element) {
            element = jQuery(element);
            var entityInstance;
            var viewInstance;
            var jsonld;

            jsonld = VIE.RDFa.readEntity(element);
            if (!jsonld) {
                return null;
            }

            entityInstance = VIE.EntityManager.getByJSONLD(jsonld);

            jQuery.each(VIE.RDFaEntities.Views, function() {
                // Check whether we already have this view instantiated for the element
                if (this.el.get(0) === element.get(0)) {
                    viewInstance = this;
                    return false;
                }
            });

            if (!viewInstance) {
                // Create a view for the RDFa
                viewInstance = new VIE.RDFaView({
                    model: entityInstance,
                    el: element,
                    tagName: element.get(0).nodeName
                });
                VIE.RDFaEntities.Views.push(viewInstance);
            }

            return entityInstance;
        },

        // Get a list of Backbone model instances for all RDFa-marked content in an element
        getInstances: function(element) {
            var entities = [];
            var entity;

            if (typeof element === 'undefined') {
                // We're working with the full document scope
                element = jQuery(document);
            }

            jQuery(VIE.RDFa.subjectSelector, element).add(jQuery(element).filter(VIE.RDFa.subjectSelector)).each(function() {
                entity = VIE.RDFaEntities.getInstance(this);
                if (entity) {
                    entities.push(entity);
                }
            });

            return entities;
        }
    };

    // RDFa reading and writing utilities
    VIE.RDFa = {
        // Resolved prefix->namespace pairs
        Namespaces: {},

        subjectSelector: '[about],[typeof],[src],html',
        predicateSelector: '[property],[rel]',

        // Get a JSON-LD en
        readEntity: function(element) {
            var entity;
            var subject;
            var namespaces = {};
            var namespace;
            var type;
            var propertyName;

            subject = VIE.RDFa.getSubject(element);

            // Read properties from element
            entity = VIE.RDFa._getElementProperties(subject, element, false);
            if (jQuery.isEmptyObject(entity)) {
                // No properties, skip creating entity
                return null;
            }

            // Resolve namespaces
            for (propertyName in entity) {
                if (entity.hasOwnProperty(propertyName)) {
                    var propertyParts = propertyName.split(':');
                    if (propertyParts.length === 2) {
                        namespace = VIE.RDFa._resolveNamespace(propertyParts[0], element);
                        if (namespace) {
                            namespaces[propertyParts[0]] = namespace;
                        }
                    }
                }
            }
            if (!jQuery.isEmptyObject(namespaces)) {
                entity['#'] = namespaces;
            }

            // Read typeof from element
            type = VIE.RDFa._getElementValue(element, 'typeof');
            if (type) {
                entity.a = type;
            }

            if (typeof subject === 'string') {
                entity['@'] = subject;
            }
            return entity;
        },

        readEntities: function(element) {
            var entities = [];
            var entity;

            if (typeof element === 'undefined') {
                // We're working with the full document scope
                element = jQuery(document);
            }

            jQuery(VIE.RDFa.subjectSelector, element).add(jQuery(element).filter(VIE.RDFa.subjectSelector)).each(function() {
                entity = VIE.RDFa.readEntity(this);
                if (entity) {
                    entities.push(entity);
                }
            });

            return entities;
        },

        writeEntity: function(element, jsonld) {
            VIE.RDFa.findElementProperties(VIE.RDFa.getSubject(element), element, true).each(function() {
                var propertyElement = jQuery(this);
                var propertyName = propertyElement.attr('property');

                if (typeof jsonld[propertyName] === 'undefined') {
                    // Entity doesn't contain this property
                    return true;
                }

                if (jsonld[propertyName] instanceof Array) {
                    // For now we don't deal with multivalued properties in Views
                    return true;
                }

                if (VIE.RDFa._readPropertyValue(propertyElement) !== jsonld[propertyName]) {
                    VIE.RDFa._writePropertyValue(propertyElement, jsonld[propertyName]);
                }
            });
            return this;
        },

        _readPropertyValue: function(element) {
            // Property has machine-readable content value
            var content = element.attr('content');
            if (content) {
                return content;
            }
            var resource = element.attr('resource');
            if (resource) {
                return '<' + resource + '>';
            }
            var href = element.attr('href');
            if (href) {
                return '<' + href + '>';
            }

            if (element.attr('rel')) {
                // Relation, we should look for identified child objects
                var value = [];
                jQuery(element).children(VIE.RDFa.subjectSelector).each(function() {
                    var subject = VIE.RDFa.getSubject(this);
                    if (typeof subject === 'string') {
                        value.push('<' + subject + '>');
                    }
                });
                return value;
            }

            // Property has inline value
            return element.html();
        },

        _writePropertyValue: function(element, value) {
            // Property has machine-readable content value
            var content = element.attr('content');
            if (content) {
                element.attr('content', value);
                return;
            }
            var resource = element.attr('resource');
            if (resource) {
                element.attr('resource', value);
            }

            // Property has inline value
            element.html(value);
        },

        getSubject: function(element) {
            if (typeof document !== 'undefined') {
                if (element === document) {
                    return document.baseURI;
                }
            }
            var subject;
            jQuery(element).closest(VIE.RDFa.subjectSelector).each(function() {
                if (jQuery(this).attr('about')) {
                    subject = jQuery(this).attr('about');
                    return true;
                }
                if (jQuery(this).attr('src')) {
                    subject = jQuery(this).attr('src');
                    return true;
                }
                if (jQuery(this).attr('typeof')) {
                    subject = this;
                    return true;
                }

                // Handle baseURL also outside browser context
                if (jQuery(this).get(0).nodeName === 'HTML') {
                    jQuery(this).find('base').each(function() {
                        subject = jQuery(this).attr('href');
                    });
                }
            });

            return subject;
        },

        _resolveNamespace: function(prefix, element) {
            if (typeof VIE.RDFa.Namespaces[prefix] !== 'undefined') {
                return VIE.RDFa.Namespaces[prefix];
            }

            jQuery('[xmlns\\:' + prefix + ']').each(function() {
                VIE.RDFa.Namespaces[prefix] = jQuery(this).attr('xmlns:' + prefix);
            });

            return VIE.RDFa.Namespaces[prefix];
        },

        // Get the value of attribute from the element or from one of its children
        _getElementValue: function(element, propertyName) {
            element = jQuery(element);
            if (typeof element.attr(propertyName) !== 'undefined')
            {
                // Direct match with container
                return element.attr(propertyName);
            }
            return element.children('[' + propertyName + ']').attr(propertyName);
        },

        findElementProperties: function(subject, element, allowPropertiesInProperties) {
            return jQuery(element).find(VIE.RDFa.predicateSelector).add(jQuery(element).filter(VIE.RDFa.predicateSelector)).filter(function() {
                if (VIE.RDFa.getSubject(this) !== subject) {
                    // The property is under another entity, skip
                    return false;
                }

                if (!allowPropertiesInProperties) {
                    if (!jQuery(this).parents('[property]').length) {
                        return true;
                    }
                    // This property is under another property, skip
                    return false;
                }

                return true;
            });
        },

        _getElementProperties: function(subject, element, emptyValues) {
            var containerProperties = {};

            VIE.RDFa.findElementProperties(subject, element, true).each(function() {
                var propertyName;
                var propertyValue;
                var objectProperty = jQuery(this);

                propertyName = objectProperty.attr('property');
                if (!propertyName) {
                    propertyName = objectProperty.attr('rel');
                }

                propertyValue = VIE.RDFa._readPropertyValue(objectProperty);

                if (typeof containerProperties[propertyName] !== 'undefined') {
                    if (containerProperties[propertyName] instanceof Array) {
                        if (emptyValues) {
                            return;
                        }
                        containerProperties[propertyName].push(propertyValue);
                        return;
                    }
                    // Multivalued property, convert to Array
                    var previousValue = containerProperties[propertyName];
                    containerProperties[propertyName] = [];

                    if (emptyValues) {
                        return;
                    }

                    containerProperties[propertyName].push(previousValue);
                    containerProperties[propertyName].push(propertyValue);
                    return;
                }

                if (emptyValues) {
                    containerProperties[propertyName] = '';
                    return;
                }

                containerProperties[propertyName] = propertyValue;
            });

            return containerProperties;
        }
    };

    VIE.cleanup = function() {
        VIE.EntityManager.Entities = {};
        VIE.EntityManager.allEntities = [];
        VIE.EntityManager.Types = {};
        VIE.RDFaEntities.Views = [];
        VIE.RDFa.Namespaces = {};
    };

}).call(this);
<commit_msg>Started documenting VIE in literate style<commit_after>//     VIE - Vienna IKS Editables
//     (c) 2011 Henri Bergius, IKS Consortium
//     VIE may be freely distributed under the MIT license.
//     For all details and documentation:
//     http://wiki.iks-project.eu/index.php/VIE
//
//  [VIE](http://wiki.iks-project.eu/index.php/VIE) enables you to make
//  [RDFa](http://en.wikipedia.org/wiki/RDFa) -annotated content on your
//  web pages editable.
//
//  For example, if your page contains the following mark-up:
//
//

//       In his latest book
//       Wikinomics,
//       Don Tapscott
//       explains deep changes in technology,
//       demographics and business.
//       The book is due to be published in
//       October 2006.
//

//
// Then with VIE you can get a proper Backbone Model object for that. First
// scan your page for RDFa entities:
//
//     VIE.RDFaEntities.getInstances();
//
// And then just access the entity by subject:
//
//     var myBook = VIE.EntityManager.getBySubject('http://www.example.com/books/wikinomics');
//     alert(objectInstance.get('dc:title')); // "Wikinomics"
//
// Properties of the entity may also be modified, and these changes will
// also happen on the page itself:
//
//     myBook.set({'dc:title':'Wikinomics, Second Edition'});
//
// You can also access the entities via the `VIE.EntityManager.allEntities` array.
(function() {
    // Initial setup
    // -------------
    //
    // The VIE library is fully contained inside a `VIE` namespace. The
    // namespace is available in both CommonJS and the browser.
    var VIE;
    if (typeof exports !== 'undefined') {
        VIE = exports;
    } else {
        VIE = this.VIE = {};
    }

    // ### Handle dependencies
    //
    // VIE tries to load its dependencies automatically.
    // Please note that this autoloading functionality only works on the server.
    // On browser Backbone needs to be included manually.

    // Require [underscore.js](http://documentcloud.github.com/underscore/)
    // using CommonJS require if it isn't yet loaded.
    //
    // On node.js underscore.js can be installed via:
    //
    //     npm install underscore
    var _ = this._;
    if (!_ && (typeof require !== 'undefined')) { _ = require('underscore')._; }
    if (!_) {
        throw 'VIE requires underscore.js to be available';
    }

    // Require [Backbone.js](http://documentcloud.github.com/backbone/)
    // using CommonJS require if it isn't yet loaded.
    //
    // On node.js Backbone.js can be installed via:
    //
    //     npm install backbone
    var Backbone = this.Backbone;
    if (!Backbone && (typeof require !== 'undefined')) { Backbone = require('backbone'); }
    if (!Backbone) {
        throw 'VIE requires Backbone.js to be available';
    }

    // Require [jQuery](http://jquery.com/) using CommonJS require if it
    // isn't yet loaded.
    //
    // On node.js jQuery can be installed via:
    //
    //     npm install jquery
    var jQuery = this.jQuery;
    if (!jQuery && (typeof require !== 'undefined')) { jQuery = require('jquery'); }
    if (!jQuery) {
        throw 'VIE requires jQuery to be available';
    }

    // VIE.RDFEntity
    // -------------
    //
    // VIE.RDFEntity defines a common [Backbone Model](http://documentcloud.github.com/backbone/#Model)
    // for RDF entities handled in VIE.
    VIE.RDFEntity = Backbone.Model.extend({
        namespaces: {},
        type: '',

        // VIE's entities have a method for generating [JSON-LD](http://json-ld.org/)
        // representations of themselves. JSON-LD is a lightweight format for handling
        // Linked Data (RDF) information.
        //
        // Using the book example from above, we could call:
        //
        //     myBook.toJSONLD();
        //
        // And we would get a JSON object looking like the following:
        //
        //     {
        //         '@': '<http://www.example.com/books/wikinomics>',
        //         'dc:title': 'Wikinomics',
        //         'dc:creator': 'Don Tapscott',
        //         'dc:date': '2006-10-01'
        //     }
        //
        // Calling `JSON.stringify()` for this object would produce:
        //
        //
        //     {
        //         "@": "<http://www.example.com/books/wikinomics>",
        //         "dc:title": "Wikinomics",
        //         "dc:creator": "Don Tapscott",
        //         "dc:date": "2006-10-01"
        //     }
        toJSONLD: function() {
            var instance = this;
            var instanceLD = {};
            var property;

            if (typeof instance.id !== 'undefined') {
                instanceLD['@'] = '<' + instance.id + '>';
            } else {
                instanceLD['@'] = instance.cid.replace('c', '_:bnode');
            }

            if (instance.namespaces.length > 0) {
                instanceLD['#'] = instance.namespaces;
            }

            if (instance.type) {
                instanceLD.a = instance.type;
            }

            for (property in instance.attributes) {
                if (instance.attributes.hasOwnProperty(property)) {
                    if (['id'].indexOf(property) === -1) {
                        instanceLD[property] = instance.attributes[property];
                    }
                }
            }
            return instanceLD;
        }
    });

    // VIE.RDFaView
    // -------------
    //
    // VIE.RDFEntity defines a common [Backbone View](http://documentcloud.github.com/backbone/#View)
    // for all RDFa -annotated elements on a page that have been loaded as
    // `VIE.RDFEntity` objects.
    VIE.RDFaView = Backbone.View.extend({

        // We ensure view gets updated when properties of the Entity change.
        initialize: function() {
            _.bindAll(this, 'render');
            this.model.bind('change', this.render);
        },

        // Rendering a view means writing the properties of the Entity back to
        // the element containing our RDFa annotations.
        render: function() {
            VIE.RDFa.writeEntity(this.el, this.model.toJSONLD());
            return this;
        }
    });

    // VIE.EntityManager
    // -------------
    //
    // VIE.EntityManager keeps track of all RDFa entities loaded via VIE. This
    // means that entities matched by a common subject can be treated as singletons.
    //
    // It is possible to access all loaded entities via the
    // `VIE.EntityManager.allEntities` array.
    VIE.EntityManager = {
        Entities: {},
        allEntities: [],

        Types: {},

        // ### VIE.EntityManager.getBySubject
        //
        // It is possible to get an entity that has been loaded from the page
        // via the `getBySubject` method. If the entity cannot be found this method
        // will return `null`.
        //
        // The entities accessed this way are singletons, so multiple calls to same
        // subject will all return the same `VIE.RDFEntity` instance.
        //
        // Example:
        //
        //     var myBook = VIE.EntityManager.getBySubject('http://www.example.com/books/wikinomics');
        getBySubject: function(id) {
            if (typeof VIE.EntityManager.Entities[id] === 'undefined') {
                return null;
            }
            return VIE.EntityManager.Entities[id];
        },

        // ### VIE.EntityManager.getByJSONLD
        //
        // Another way to get or load entities is by passing EntityManager a valid
        // JSON-LD object.
        //
        // This can be either called with a JavaScript object representing JSON-LD,
        // or with a JSON-LD string.
        //
        // Example:
        //
        //     var json = '{"@": "<http://www.example.com/books/wikinomics>","dc:title": "Wikinomics","dc:creator": "Don Tapscott","dc:date": "2006-10-01"}';
        //     var objectInstance = VIE.EntityManager.getByJSONLD(json);
        //
        // The entities accessed this way are singletons, so multiple calls to same
        // subject (`@` in JSON-LD) will all return the same `VIE.RDFEntity` instance.
        getByJSONLD: function(jsonld) {
            var entityInstance;
            var properties;

            if (typeof jsonld !== 'object') {
                try {
                    jsonld = jQuery.parseJSON(jsonld);
                } catch (e) {
                    return null;
                }
            }

            properties = VIE.EntityManager._JSONtoProperties(jsonld);

            if (typeof jsonld['@'] !== 'undefined') {
                entityInstance = VIE.EntityManager.getBySubject(jsonld['@']);
                if (entityInstance) {
                    entityInstance.set(properties);
                    return entityInstance;
                }
            }

            entityInstance = new VIE.RDFEntity(properties);

            if (typeof jsonld['#'] !== 'undefined') {
                entityInstance.namespaces = jsonld['#'];
            }

            if (typeof jsonld.a !== 'undefined') {
                entityInstance.type = jsonld.a;
            }

            if (typeof jsonld['@'] !== 'undefined') {
                entityInstance.id = jsonld['@'];
                VIE.EntityManager.Entities[entityInstance.id] = entityInstance;
            }

            VIE.EntityManager.allEntities.push(entityInstance);

            return entityInstance;
        },

        // Helper for cleaning up JSON-LD so that it can be used as properties
        // of a Backbone Model
        _JSONtoProperties: function(jsonld) {
            var properties;
            properties = jQuery.extend({}, jsonld);
            delete properties['@'];
            delete properties.a;
            delete properties['#'];
            return properties;
        }
    };

    // Mapping between RDFa and Backbone models
    VIE.RDFaEntities = {
        Views: [],

        // Create a Backbone model instance for a RDFa-marked element
        getInstance: function(element) {
            element = jQuery(element);
            var entityInstance;
            var viewInstance;
            var jsonld;

            jsonld = VIE.RDFa.readEntity(element);
            if (!jsonld) {
                return null;
            }

            entityInstance = VIE.EntityManager.getByJSONLD(jsonld);

            jQuery.each(VIE.RDFaEntities.Views, function() {
                // Check whether we already have this view instantiated for the element
                if (this.el.get(0) === element.get(0)) {
                    viewInstance = this;
                    return false;
                }
            });

            if (!viewInstance) {
                // Create a view for the RDFa
                viewInstance = new VIE.RDFaView({
                    model: entityInstance,
                    el: element,
                    tagName: element.get(0).nodeName
                });
                VIE.RDFaEntities.Views.push(viewInstance);
            }

            return entityInstance;
        },

        // Get a list of Backbone model instances for all RDFa-marked content in an element
        getInstances: function(element) {
            var entities = [];
            var entity;

            if (typeof element === 'undefined') {
                // We're working with the full document scope
                element = jQuery(document);
            }

            jQuery(VIE.RDFa.subjectSelector, element).add(jQuery(element).filter(VIE.RDFa.subjectSelector)).each(function() {
                entity = VIE.RDFaEntities.getInstance(this);
                if (entity) {
                    entities.push(entity);
                }
            });

            return entities;
        }
    };

    // RDFa reading and writing utilities
    VIE.RDFa = {
        // Resolved prefix->namespace pairs
        Namespaces: {},

        subjectSelector: '[about],[typeof],[src],html',
        predicateSelector: '[property],[rel]',

        // Get a JSON-LD en
        readEntity: function(element) {
            var entity;
            var subject;
            var namespaces = {};
            var namespace;
            var type;
            var propertyName;

            subject = VIE.RDFa.getSubject(element);

            // Read properties from element
            entity = VIE.RDFa._getElementProperties(subject, element, false);
            if (jQuery.isEmptyObject(entity)) {
                // No properties, skip creating entity
                return null;
            }

            // Resolve namespaces
            for (propertyName in entity) {
                if (entity.hasOwnProperty(propertyName)) {
                    var propertyParts = propertyName.split(':');
                    if (propertyParts.length === 2) {
                        namespace = VIE.RDFa._resolveNamespace(propertyParts[0], element);
                        if (namespace) {
                            namespaces[propertyParts[0]] = namespace;
                        }
                    }
                }
            }
            if (!jQuery.isEmptyObject(namespaces)) {
                entity['#'] = namespaces;
            }

            // Read typeof from element
            type = VIE.RDFa._getElementValue(element, 'typeof');
            if (type) {
                entity.a = type;
            }

            if (typeof subject === 'string') {
                entity['@'] = subject;
            }
            return entity;
        },

        readEntities: function(element) {
            var entities = [];
            var entity;

            if (typeof element === 'undefined') {
                // We're working with the full document scope
                element = jQuery(document);
            }

            jQuery(VIE.RDFa.subjectSelector, element).add(jQuery(element).filter(VIE.RDFa.subjectSelector)).each(function() {
                entity = VIE.RDFa.readEntity(this);
                if (entity) {
                    entities.push(entity);
                }
            });

            return entities;
        },

        writeEntity: function(element, jsonld) {
            VIE.RDFa.findElementProperties(VIE.RDFa.getSubject(element), element, true).each(function() {
                var propertyElement = jQuery(this);
                var propertyName = propertyElement.attr('property');

                if (typeof jsonld[propertyName] === 'undefined') {
                    // Entity doesn't contain this property
                    return true;
                }

                if (jsonld[propertyName] instanceof Array) {
                    // For now we don't deal with multivalued properties in Views
                    return true;
                }

                if (VIE.RDFa._readPropertyValue(propertyElement) !== jsonld[propertyName]) {
                    VIE.RDFa._writePropertyValue(propertyElement, jsonld[propertyName]);
                }
            });
            return this;
        },

        _readPropertyValue: function(element) {
            // Property has machine-readable content value
            var content = element.attr('content');
            if (content) {
                return content;
            }
            var resource = element.attr('resource');
            if (resource) {
                return '<' + resource + '>';
            }
            var href = element.attr('href');
            if (href) {
                return '<' + href + '>';
            }

            if (element.attr('rel')) {
                // Relation, we should look for identified child objects
                var value = [];
                jQuery(element).children(VIE.RDFa.subjectSelector).each(function() {
                    var subject = VIE.RDFa.getSubject(this);
                    if (typeof subject === 'string') {
                        value.push('<' + subject + '>');
                    }
                });
                return value;
            }

            // Property has inline value
            return element.html();
        },

        _writePropertyValue: function(element, value) {
            // Property has machine-readable content value
            var content = element.attr('content');
            if (content) {
                element.attr('content', value);
                return;
            }
            var resource = element.attr('resource');
            if (resource) {
                element.attr('resource', value);
            }

            // Property has inline value
            element.html(value);
        },

        getSubject: function(element) {
            if (typeof document !== 'undefined') {
                if (element === document) {
                    return document.baseURI;
                }
            }
            var subject;
            jQuery(element).closest(VIE.RDFa.subjectSelector).each(function() {
                if (jQuery(this).attr('about')) {
                    subject = jQuery(this).attr('about');
                    return true;
                }
                if (jQuery(this).attr('src')) {
                    subject = jQuery(this).attr('src');
                    return true;
                }
                if (jQuery(this).attr('typeof')) {
                    subject = this;
                    return true;
                }

                // Handle baseURL also outside browser context
                if (jQuery(this).get(0).nodeName === 'HTML') {
                    jQuery(this).find('base').each(function() {
                        subject = jQuery(this).attr('href');
                    });
                }
            });

            return subject;
        },

        _resolveNamespace: function(prefix, element) {
            if (typeof VIE.RDFa.Namespaces[prefix] !== 'undefined') {
                return VIE.RDFa.Namespaces[prefix];
            }

            jQuery('[xmlns\\:' + prefix + ']').each(function() {
                VIE.RDFa.Namespaces[prefix] = jQuery(this).attr('xmlns:' + prefix);
            });

            return VIE.RDFa.Namespaces[prefix];
        },

        // Get the value of attribute from the element or from one of its children
        _getElementValue: function(element, propertyName) {
            element = jQuery(element);
            if (typeof element.attr(propertyName) !== 'undefined')
            {
                // Direct match with container
                return element.attr(propertyName);
            }
            return element.children('[' + propertyName + ']').attr(propertyName);
        },

        findElementProperties: function(subject, element, allowPropertiesInProperties) {
            return jQuery(element).find(VIE.RDFa.predicateSelector).add(jQuery(element).filter(VIE.RDFa.predicateSelector)).filter(function() {
                if (VIE.RDFa.getSubject(this) !== subject) {
                    // The property is under another entity, skip
                    return false;
                }

                if (!allowPropertiesInProperties) {
                    if (!jQuery(this).parents('[property]').length) {
                        return true;
                    }
                    // This property is under another property, skip
                    return false;
                }

                return true;
            });
        },

        _getElementProperties: function(subject, element, emptyValues) {
            var containerProperties = {};

            VIE.RDFa.findElementProperties(subject, element, true).each(function() {
                var propertyName;
                var propertyValue;
                var objectProperty = jQuery(this);

                propertyName = objectProperty.attr('property');
                if (!propertyName) {
                    propertyName = objectProperty.attr('rel');
                }

                propertyValue = VIE.RDFa._readPropertyValue(objectProperty);

                if (typeof containerProperties[propertyName] !== 'undefined') {
                    if (containerProperties[propertyName] instanceof Array) {
                        if (emptyValues) {
                            return;
                        }
                        containerProperties[propertyName].push(propertyValue);
                        return;
                    }
                    // Multivalued property, convert to Array
                    var previousValue = containerProperties[propertyName];
                    containerProperties[propertyName] = [];

                    if (emptyValues) {
                        return;
                    }

                    containerProperties[propertyName].push(previousValue);
                    containerProperties[propertyName].push(propertyValue);
                    return;
                }

                if (emptyValues) {
                    containerProperties[propertyName] = '';
                    return;
                }

                containerProperties[propertyName] = propertyValue;
            });

            return containerProperties;
        }
    };

    VIE.cleanup = function() {
        VIE.EntityManager.Entities = {};
        VIE.EntityManager.allEntities = [];
        VIE.EntityManager.Types = {};
        VIE.RDFaEntities.Views = [];
        VIE.RDFa.Namespaces = {};
    };

}).call(this);
<|endoftext|>


<commit_before>var fs = require('fs');
var path = require('path');
var handlebars = require('handlebars');
import moment from 'moment';

const escape = {
  '&': '&',
  '<': '<',
  '>': '>'
};

const badChars = /[&<>]/g;
const possible = /[&<>]/;

function escapeChar(chr) {
  return escape[chr];
}

function customEscapeExpression(string) {
  if (typeof string !== 'string') {
    if (string && string.toHTML) {
      return string.toHTML();
    } else if (string === null) {
      return '';
    } else if (!string) {
      return String(string);
    }
    string = String(string);
  }
  if (!possible.test(string)) {
    return string;
  }
  return string.replace(badChars, escapeChar);
}

handlebars.Utils.escapeExpression = customEscapeExpression;

function renderPodcastFile(directory, podcastState, filename) {
  var hbsPath = './app/podcast-template.hbs';
  var hbsTemplate = handlebars.compile(fs.readFileSync(hbsPath, "utf8"));
  var DATE_RFC2822 = "ddd, DD MMM YYYY HH:mm:ss ZZ";
  podcastState.buildDate = moment().format(DATE_RFC2822);
  var outPath = path.join(directory, filename);
  fs.writeFile(outPath, hbsTemplate(podcastState), function(err) {
    if (err !== null) {
      console.error(err);
    }
  });
}

function renderLocal(directory, podcastState) {
  renderPodcastFile(directory, podcastState, '.podcast-local.xml');
}

function renderRemote(directory, podcastState, prefixUrl) {
  if (!prefixUrl.endsWith('/')) {
    prefixUrl += '/';
  }
  var podcastCopy = Object.assign({}, podcastState);
  podcastCopy.image = prefixUrl + path.basename(podcastCopy.image);
  renderPodcastFile(directory, podcastCopy, 'podcast.xml');
}

export default function podcastRender(directory, podcastState, prefixUrl) {
  renderLocal(directory, podcastState);
  renderRemote(directory, podcastState, prefixUrl);
}
<commit_msg>fix path for handlebars template - fixes windows<commit_after>var fs = require('fs');
var path = require('path');
var handlebars = require('handlebars');
import moment from 'moment';

const escape = {
  '&': '&',
  '<': '<',
  '>': '>'
};

const badChars = /[&<>]/g;
const possible = /[&<>]/;

function escapeChar(chr) {
  return escape[chr];
}

function customEscapeExpression(string) {
  if (typeof string !== 'string') {
    if (string && string.toHTML) {
      return string.toHTML();
    } else if (string === null) {
      return '';
    } else if (!string) {
      return String(string);
    }
    string = String(string);
  }
  if (!possible.test(string)) {
    return string;
  }
  return string.replace(badChars, escapeChar);
}

handlebars.Utils.escapeExpression = customEscapeExpression;

function renderPodcastFile(directory, podcastState, filename) {
  var hbsPath = path.join(__dirname, 'podcast-template.hbs');
  var hbsTemplate = handlebars.compile(fs.readFileSync(hbsPath, "utf8"));
  var DATE_RFC2822 = "ddd, DD MMM YYYY HH:mm:ss ZZ";
  podcastState.buildDate = moment().format(DATE_RFC2822);
  var outPath = path.join(directory, filename);
  fs.writeFile(outPath, hbsTemplate(podcastState), function(err) {
    if (err !== null) {
      console.error(err);
    }
  });
}

function renderLocal(directory, podcastState) {
  renderPodcastFile(directory, podcastState, '.podcast-local.xml');
}

function renderRemote(directory, podcastState, prefixUrl) {
  if (!prefixUrl.endsWith('/')) {
    prefixUrl += '/';
  }
  var podcastCopy = Object.assign({}, podcastState);
  podcastCopy.image = prefixUrl + path.basename(podcastCopy.image);
  renderPodcastFile(directory, podcastCopy, 'podcast.xml');
}

export default function podcastRender(directory, podcastState, prefixUrl) {
  renderLocal(directory, podcastState);
  renderRemote(directory, podcastState, prefixUrl);
}
<|endoftext|>


<commit_before>/* global TrelloPowerUp */

var WHITE_ICON = './images/icon-white.svg';
var GRAY_ICON = './images/icon-gray.svg';

var parkMap = {
  acad: 'Acadia National Park',
  arch: 'Arches National Park',
  badl: 'Badlands National Park',
  brca: 'Bryce Canyon National Park',
  crla: 'Crater Lake National Park',
  dena: 'Denali National Park',
  glac: 'Glacier National Park',
  grca: 'Grand Canyon National Park',
  grte: 'Grand Teton National Park',
  olym: 'Olympic National Park',
  yell: 'Yellowstone National Park',
  yose: 'Yosemite National Park',
  zion: 'Zion National Park'
};

var getBadges = function(t){
  return t.card('name')
  .get('name')
  .then(function(cardName){
    var badgeColor;
    var icon = GRAY_ICON;
    var lowercaseName = cardName.toLowerCase();
    if(lowercaseName.indexOf('green') > -1){
      badgeColor = 'green';
      icon = WHITE_ICON;
    } else if(lowercaseName.indexOf('yellow') > -1){
      badgeColor = 'yellow';
      icon = WHITE_ICON;
    } else if(lowercaseName.indexOf('red') > -1){
      badgeColor = 'red';
      icon = WHITE_ICON;
    }

    if(lowercaseName.indexOf('dynamic') > -1){
      // dynamic badges can have their function rerun after a set number
      // of seconds defined by refresh. Minimum of 10 seconds.
      return [{
        dynamic: function(){
          return {
            title: 'Detail Badge', // for detail badges only
            text: 'Dynamic ' + (Math.random() * 100).toFixed(0).toString(),
            icon: icon, // for card front badges only
            color: badgeColor,
            refresh: 10
          }
        }
      }]
    }

    if(lowercaseName.indexOf('static') > -1){
      // return an array of badge objects
      return [{
        title: 'Detail Badge', // for detail badges only
        text: 'Static',
        icon: icon, // for card front badges only
        color: badgeColor
      }];
    } else {
      return [];
    }
  })
};

var formatNPSUrl = function(t, url){
  if(!/^https?:\/\/www\.nps\.gov\/[a-z]{4}\//.test(url)){
    return null;
  }
  var parkShort = /^https?:\/\/www\.nps\.gov\/([a-z]{4})\//.exec(url)[1];
  if(parkShort && parkMap[parkShort]){
    return parkMap[parkShort];
  } else{
    return null;
  }
};

var boardButtonCallback = function(t){
  return t.popup({
    title: 'Popup List Example',
    items: [
      {
        text: 'Open Overlay',
        callback: function(t){
          return t.overlay({
            url: './overlay.html',
            args: { rand: (Math.random() * 100).toFixed(0) }
          })
          .then(function(){
            return t.closePopup();
          });
        }
      },
      {
        text: 'Open Board Bar',
        callback: function(t){
          return t.boardBar({
            url: './board-bar.html',
            height: 200
          })
          .then(function(){
            return t.closePopup();
          });
        }
      },
      {
        text: 'Hello',
        callback: function(t){
          return alert("hello world");
      }
    ]
  });
};

var cardButtonCallback = function(t){
  var items = Object.keys(parkMap).map(function(parkCode){
    var urlForCode = 'http://www.nps.gov/' + parkCode + '/';
    return {
      text: parkMap[parkCode],
      url: urlForCode,
      callback: function(t){
        return t.attach({ url: urlForCode, name: parkMap[parkCode] })
        .then(function(){
          return t.closePopup();
        })
      }
    };
  });

  return t.popup({
    title: 'Popup Search Example',
    items: items,
    search: {
      count: 5,
      placeholder: 'Search National Parks',
      empty: 'No parks found'
    }
  });
};

TrelloPowerUp.initialize({
  'attachment-sections': function(t, options){
    // options.entries is a list of the attachments for this card
    // you can look through them and 'claim' any that you want to
    // include in your section.

    // we will just claim urls for Yellowstone
    var claimed = options.entries.filter(function(attachment){
      return attachment.url.indexOf('http://www.nps.gov/yell/') == 0;
    });

    // you can have more than one attachment section on a card
    // you can group items together into one section, have a section
    // per attachment, or anything in between.
    if(claimed && claimed.length > 0){
      // if the title for your section requires a network call or other
      // potentially length operation you can provide a function for the title
      // that returns the section title. If you do so, provide a unique id for
      // your section
      return [{
        id: 'Yellowstone', // optional if you aren't using a function for the title
        claimed: claimed,
        icon: GRAY_ICON,
        title: 'Example Attachment Section: Yellowstone',
        content: {
          type: 'iframe',
          url: t.signUrl('./section.html', { arg: 'you can pass your section args here' }),
          height: 230
        }
      }];
    } else {
      return [];
    }
  },
  'attachment-thumbnail': function(t, options){
    var parkName = formatNPSUrl(t, options.url);
    if(parkName){
      // return an object with some or all of these properties:
      // url, title, image, openText, modified (Date), created (Date), createdBy, modifiedBy
      return {
        url: options.url,
        title: parkName,
        image: {
          url: './images/nps.svg',
          logo: true // false if you are using a thumbnail of the content
        },
        openText: 'Open in NPS'
      };
    } else {
      throw t.NotHandled();
    }
  },
  'board-buttons': function(t, options){
    return [{
      icon: WHITE_ICON,
      text: 'Template',
      callback: boardButtonCallback
    }];
  },
  'card-badges': function(t, options){
    return getBadges(t);
  },
  'card-buttons': function(t, options) {
    return [{
      icon: GRAY_ICON,
      text: 'Template',
      callback: cardButtonCallback
    }];
  },
  'card-detail-badges': function(t, options) {
    return getBadges(t);
  },
  'card-from-url': function(t, options) {
    var parkName = formatNPSUrl(t, options.url);
    if(parkName){
      return {
        name: parkName,
        desc: 'An awesome park: ' + options.url
      };
    } else {
      throw t.NotHandled();
    }
  },
  'format-url': function(t, options) {
    var parkName = formatNPSUrl(t, options.url);
    if(parkName){
      return {
        icon: GRAY_ICON,
        text: parkName
      };
    } else {
      throw t.NotHandled();
    }
  },
  'show-settings': function(t, options){
    return t.popup({
      title: 'Settings',
      url: './settings.html',
      height: 184
    });
  }
});
<commit_msg>hello world 1<commit_after>/* global TrelloPowerUp */

var WHITE_ICON = './images/icon-white.svg';
var GRAY_ICON = './images/icon-gray.svg';

var parkMap = {
  acad: 'Acadia National Park',
  arch: 'Arches National Park',
  badl: 'Badlands National Park',
  brca: 'Bryce Canyon National Park',
  crla: 'Crater Lake National Park',
  dena: 'Denali National Park',
  glac: 'Glacier National Park',
  grca: 'Grand Canyon National Park',
  grte: 'Grand Teton National Park',
  olym: 'Olympic National Park',
  yell: 'Yellowstone National Park',
  yose: 'Yosemite National Park',
  zion: 'Zion National Park'
};

var getBadges = function(t){
  return t.card('name')
  .get('name')
  .then(function(cardName){
    var badgeColor;
    var icon = GRAY_ICON;
    var lowercaseName = cardName.toLowerCase();
    if(lowercaseName.indexOf('green') > -1){
      badgeColor = 'green';
      icon = WHITE_ICON;
    } else if(lowercaseName.indexOf('yellow') > -1){
      badgeColor = 'yellow';
      icon = WHITE_ICON;
    } else if(lowercaseName.indexOf('red') > -1){
      badgeColor = 'red';
      icon = WHITE_ICON;
    }

    if(lowercaseName.indexOf('dynamic') > -1){
      // dynamic badges can have their function rerun after a set number
      // of seconds defined by refresh. Minimum of 10 seconds.
      return [{
        dynamic: function(){
          return {
            title: 'Detail Badge', // for detail badges only
            text: 'Dynamic ' + (Math.random() * 100).toFixed(0).toString(),
            icon: icon, // for card front badges only
            color: badgeColor,
            refresh: 10
          }
        }
      }]
    }

    if(lowercaseName.indexOf('static') > -1){
      // return an array of badge objects
      return [{
        title: 'Detail Badge', // for detail badges only
        text: 'Static',
        icon: icon, // for card front badges only
        color: badgeColor
      }];
    } else {
      return [];
    }
  })
};

var formatNPSUrl = function(t, url){
  if(!/^https?:\/\/www\.nps\.gov\/[a-z]{4}\//.test(url)){
    return null;
  }
  var parkShort = /^https?:\/\/www\.nps\.gov\/([a-z]{4})\//.exec(url)[1];
  if(parkShort && parkMap[parkShort]){
    return parkMap[parkShort];
  } else{
    return null;
  }
};

var boardButtonCallback = function(t){
  return t.popup({
    title: 'Popup List Example',
    items: [
      {
        text: 'Open Overlay',
        callback: function(t){
          return t.overlay({
            url: './overlay.html',
            args: { rand: (Math.random() * 100).toFixed(0) }
          })
          .then(function(){
            return t.closePopup();
          });
        }
      },
      {
        text: 'Open Board Bar',
        callback: function(t){
          return t.boardBar({
            url: './board-bar.html',
            height: 200
          })
          .then(function(){
            return t.closePopup();
          });
        }
      },
      {
        text: 'Hello',
        callback: function(t){
          return alert("hello world");
        }
      }
    ]
  });
};

var cardButtonCallback = function(t){
  var items = Object.keys(parkMap).map(function(parkCode){
    var urlForCode = 'http://www.nps.gov/' + parkCode + '/';
    return {
      text: parkMap[parkCode],
      url: urlForCode,
      callback: function(t){
        return t.attach({ url: urlForCode, name: parkMap[parkCode] })
        .then(function(){
          return t.closePopup();
        })
      }
    };
  });

  return t.popup({
    title: 'Popup Search Example',
    items: items,
    search: {
      count: 5,
      placeholder: 'Search National Parks',
      empty: 'No parks found'
    }
  });
};

TrelloPowerUp.initialize({
  'attachment-sections': function(t, options){
    // options.entries is a list of the attachments for this card
    // you can look through them and 'claim' any that you want to
    // include in your section.

    // we will just claim urls for Yellowstone
    var claimed = options.entries.filter(function(attachment){
      return attachment.url.indexOf('http://www.nps.gov/yell/') == 0;
    });

    // you can have more than one attachment section on a card
    // you can group items together into one section, have a section
    // per attachment, or anything in between.
    if(claimed && claimed.length > 0){
      // if the title for your section requires a network call or other
      // potentially length operation you can provide a function for the title
      // that returns the section title. If you do so, provide a unique id for
      // your section
      return [{
        id: 'Yellowstone', // optional if you aren't using a function for the title
        claimed: claimed,
        icon: GRAY_ICON,
        title: 'Example Attachment Section: Yellowstone',
        content: {
          type: 'iframe',
          url: t.signUrl('./section.html', { arg: 'you can pass your section args here' }),
          height: 230
        }
      }];
    } else {
      return [];
    }
  },
  'attachment-thumbnail': function(t, options){
    var parkName = formatNPSUrl(t, options.url);
    if(parkName){
      // return an object with some or all of these properties:
      // url, title, image, openText, modified (Date), created (Date), createdBy, modifiedBy
      return {
        url: options.url,
        title: parkName,
        image: {
          url: './images/nps.svg',
          logo: true // false if you are using a thumbnail of the content
        },
        openText: 'Open in NPS'
      };
    } else {
      throw t.NotHandled();
    }
  },
  'board-buttons': function(t, options){
    return [{
      icon: WHITE_ICON,
      text: 'Template',
      callback: boardButtonCallback
    }];
  },
  'card-badges': function(t, options){
    return getBadges(t);
  },
  'card-buttons': function(t, options) {
    return [{
      icon: GRAY_ICON,
      text: 'Template',
      callback: cardButtonCallback
    }];
  },
  'card-detail-badges': function(t, options) {
    return getBadges(t);
  },
  'card-from-url': function(t, options) {
    var parkName = formatNPSUrl(t, options.url);
    if(parkName){
      return {
        name: parkName,
        desc: 'An awesome park: ' + options.url
      };
    } else {
      throw t.NotHandled();
    }
  },
  'format-url': function(t, options) {
    var parkName = formatNPSUrl(t, options.url);
    if(parkName){
      return {
        icon: GRAY_ICON,
        text: parkName
      };
    } else {
      throw t.NotHandled();
    }
  },
  'show-settings': function(t, options){
    return t.popup({
      title: 'Settings',
      url: './settings.html',
      height: 184
    });
  }
});
<|endoftext|>


<commit_before>(function($) {

	$.fn.tagit = function(options) {

		var el = this;

		const BACKSPACE		= 8;
		const ENTER			= 13;
		const SPACE			= 32;
		const COMMA			= 44;
		const TAB			= 9;

		// add the tagit CSS class.
		el.addClass("tagit");

		// create the input field.
		var html_input_field = "
<input class=\"tagit-input\" type=\"text\" />
\n";
		el.html (html_input_field);

		tag_input		= el.children(".tagit-new").children(".tagit-input");

		$(this).click(function(e){
			if (e.target.tagName == 'A') {
				// Removes a tag when the little 'x' is clicked.
				// Event is binded to the UL, otherwise a new tag (LI > A) wouldn't have this event attached to it.
				$(e.target).parent().remove();
			}
			else {
				// Sets the focus() to the input field, if the user clicks anywhere inside the UL.
				// This is needed because the input field needs to be of a small size.
				tag_input.focus();
			}
		});

		tag_input.keypress(function(event){
			keyCode = event.keyCode || event.which
			if (keyCode == BACKSPACE) {
				if (tag_input.val() == "") {
					// When backspace is pressed, the last tag is deleted.
					$(el).children(".tagit-choice:last").remove();
				}
			}
			// Comma/Space/Enter are all valid delimiters for new tags. except when there is an open quote
			else if (
					keyCode == COMMA ||
					keyCode == ENTER ||
					keyCode == TAB ||
					(
						keyCode == SPACE &&
						(
							(tag_input.val().trim().replace( /^s*/, "" ).charAt(0) != '"') ||
							(
								tag_input.val().trim().charAt(0) == '"' &&
								tag_input.val().trim().charAt(tag_input.val().trim().length - 1) == '"' &&
								tag_input.val().trim().length - 1 != 0
							)
						)
					)
				) {

				event.preventDefault();

				var typed = tag_input.val();
				typed = typed.replace(/,+$/,"").replace(/^\s*"(.*)"\s*$/, "$1");
				typed = typed.trim();

				if (typed != "") {
					if (is_new (typed)) {
						create_choice (typed);
					}
					// Cleaning the input.
					tag_input.val("");
				}
			}
		});

		tag_input.autocomplete({
			source: options.availableTags,
			select: function(event,ui){
				if (is_new (ui.item.value)) {
					create_choice (ui.item.value);
				}
				// Cleaning the input.
				tag_input.val("");

				// Preventing the tag input to be update with the chosen value.
				return false;
			}
		});

		function is_new (value){
			var is_new = true;
			this.tag_input.parents("ul").children(".tagit-choice").each(function(i){
				n = $(this).children("input").val();
				if (value == n) {
					is_new = false;
				}
			})
			return is_new;
		}
		function create_choice (value){
			var el = "";
			el  = "
\n";
			el += value + "\n";
			el += "x\n";
			el += "<input type=\"hidden\" style=\"display:none;\" value=\""+value+"\" name=\"item[tags][]\">\n";
			el += "
\n";
			var li_search_tags = this.tag_input.parent();
			$(el).insertBefore (li_search_tags);
			this.tag_input.val("");
		}
	};

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

})(jQuery);
<commit_msg>cleaned up quote removal<commit_after>(function($) {

	$.fn.tagit = function(options) {

		var el = this;

		const BACKSPACE		= 8;
		const ENTER			= 13;
		const SPACE			= 32;
		const COMMA			= 44;
		const TAB			= 9;

		// add the tagit CSS class.
		el.addClass("tagit");

		// create the input field.
		var html_input_field = "
<input class=\"tagit-input\" type=\"text\" />
\n";
		el.html (html_input_field);

		tag_input		= el.children(".tagit-new").children(".tagit-input");

		$(this).click(function(e){
			if (e.target.tagName == 'A') {
				// Removes a tag when the little 'x' is clicked.
				// Event is binded to the UL, otherwise a new tag (LI > A) wouldn't have this event attached to it.
				$(e.target).parent().remove();
			}
			else {
				// Sets the focus() to the input field, if the user clicks anywhere inside the UL.
				// This is needed because the input field needs to be of a small size.
				tag_input.focus();
			}
		});

		tag_input.keypress(function(event){
			keyCode = event.keyCode || event.which
			if (keyCode == BACKSPACE) {
				if (tag_input.val() == "") {
					// When backspace is pressed, the last tag is deleted.
					$(el).children(".tagit-choice:last").remove();
				}
			}
			// Comma/Space/Enter are all valid delimiters for new tags. except when there is an open quote
			else if (
					keyCode == COMMA ||
					keyCode == ENTER ||
					keyCode == TAB ||
					(
						keyCode == SPACE &&
						(
							(tag_input.val().trim().replace( /^s*/, "" ).charAt(0) != '"') ||
							(
								tag_input.val().trim().charAt(0) == '"' &&
								tag_input.val().trim().charAt(tag_input.val().trim().length - 1) == '"' &&
								tag_input.val().trim().length - 1 != 0
							)
						)
					)
				) {

				event.preventDefault();

				var typed = tag_input.val();
				typed = typed.replace(/,+$/,"").trim().replace(/^"/, "").replace(/"$/, "");
				typed = typed.trim();

				if (typed != "") {
					if (is_new (typed)) {
						create_choice (typed);
					}
					// Cleaning the input.
					tag_input.val("");
				}
			}
		});

		tag_input.autocomplete({
			source: options.availableTags,
			select: function(event,ui){
				if (is_new (ui.item.value)) {
					create_choice (ui.item.value);
				}
				// Cleaning the input.
				tag_input.val("");

				// Preventing the tag input to be update with the chosen value.
				return false;
			}
		});

		function is_new (value){
			var is_new = true;
			this.tag_input.parents("ul").children(".tagit-choice").each(function(i){
				n = $(this).children("input").val();
				if (value == n) {
					is_new = false;
				}
			})
			return is_new;
		}
		function create_choice (value){
			var el = "";
			el  = "
\n";
			el += value + "\n";
			el += "x\n";
			el += "<input type=\"hidden\" style=\"display:none;\" value=\""+value+"\" name=\"item[tags][]\">\n";
			el += "
\n";
			var li_search_tags = this.tag_input.parent();
			$(el).insertBefore (li_search_tags);
			this.tag_input.val("");
		}
	};

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

})(jQuery);
<|endoftext|>