<commit_before>'use strict';
/* global snoowrap */
var REDDIT_APP_ID = '_s9Icw2TEXpmaw';
var REDIRECT_URI = 'https://pkmnbumba.github.io/reddit-thread-unnuke/';

var USER_AGENT = 'reddit thread un-nuke by /u/bumbalicious || https://github.com/pkmnbumba/reddit-thread-unnuke';
var REQUIRED_SCOPES = ['modposts', 'read'];
var cachedRequester;
var accessTokenPromise;
var removedCount;

var query = parseQueryString(location.search);
var cookies = parseCookieString(document.cookie);

function parseQueryString (str) {
  if (!str) {
    return {};
  }
  var obj = {};
  var pieces = str.slice(1).split('&');
  for (var i = 0; i < pieces.length; i++) {
    var pair = pieces[i].split('=');
    obj[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
  }
  return obj;
}

function parseCookieString (cookieString) {
  var obj = {};
  var splitCookies = cookieString.split('; ');
  splitCookies.forEach(function (cookie) {
    var pair = cookie.split('=');
    obj[pair[0]] = pair[1];
  });
  return obj;
}

var getAuthRedirect = function (state) {
  return `https://reddit.com/api/v1/authorize?client_id=${REDDIT_APP_ID}&response_type=code&state=${encodeURIComponent(state)}&redirect_uri=${encodeURIComponent(REDIRECT_URI)}&duration=temporary&scope=${REQUIRED_SCOPES.join('%2C')}`;
};

function parseUrl (url) {
  var matches = url.match(/^(?:http(?:s?):\/\/)?(?:\w*\.)?reddit\.com\/(?:r\/\w{1,21}\/)?comments\/(\w{1,10})(?:\/[^\/\?]{1,100})?(?:\/(\w{1,10}))?\/?(?:\?.*)?$/);
  if (!matches) {
    throw new TypeError('Invalid URL. Please enter the URL of a reddit Submission or Comment.');
  }
  return matches;
}

function getExpandedContent (requester, urlMatches) {
  return (urlMatches[2] ? requester.get_comment(urlMatches[2]) : requester.get_submission(urlMatches[1])).expand_replies();
}

function deepRemove (content, preserveDistinguished) {
  var replies = content.comments || content.replies;
  var removeCurrentItem = content.distinguished && preserveDistinguished || content.banned_by !== null
    ? Promise.resolve()
    : content.remove().tap(incrementCounter);
  return Promise.all(Array.from(replies).map(function (reply) {
    return deepRemove(reply, preserveDistinguished);
  }).concat([removeCurrentItem]));
}

function deepApprove (content, preserveRemoved) {
  var replies = content.comments || content.replies;
  console.log(content.banned_by);
  console.log(typeof content.banned_by);
  var determineRemoved = preserveRemoved;
  var approveCurrentItem = content.removed || content.banned_by === null
    ? Promise.resolve()
    : content.approve().tap(incrementCounter);
  return Promise.all(Array.from(replies).map(function (reply) {
    return deepApprove(reply, preserveRemoved);
  }).concat([approveCurrentItem]));
}

function getAccessToken (code) {
  if (accessTokenPromise) {
    return accessTokenPromise;
  }
  accessTokenPromise = cookies.access_token && !code
    ? Promise.resolve(cookies.access_token)
    : snoowrap.prototype.credentialed_client_request.call({
      user_agent: USER_AGENT,
      client_id: REDDIT_APP_ID,
      client_secret: ''
    }, {
      method: 'post',
      url: 'https://www.reddit.com/api/v1/access_token',
      form: {grant_type: 'authorization_code', code, redirect_uri: REDIRECT_URI}
    }).then(function (response) {
      if (!response.access_token) {
        throw new Error('Authentication failed');
      }
      document.cookie = `access_token=${response.access_token}; max-age=3600; secure`;
      cookies.access_token = response.access_token;
      return response.access_token;
    });
  return accessTokenPromise;
}

function incrementCounter () {
  document.getElementById('removed-count').innerHTML = ++removedCount;
}

function getRequester (access_token) {
  if (cachedRequester) {
    return cachedRequester;
  }
  cachedRequester = new snoowrap({user_agent: USER_AGENT, access_token});
  cachedRequester.config({debug: true, continue_after_ratelimit_error: true});
  return cachedRequester;
}

function nukeThread (url, toNuke) {
  var parsedUrl;
  removedCount = 0;
  try {
    parsedUrl = parseUrl(url);
  } catch (err) {
    document.getElementById('url-error-message').innerHTML = err.message;
    document.getElementById('url-error-message').style.display = 'block';
    throw err;
  }
  document.getElementById('url-error-message').style.display = 'none';
  document.getElementById('error-output').style.display = 'none';
  document.getElementById('loading-message').style.display = 'block';
  document.getElementById('done-message').style.display = 'none';
  return getAccessToken(query.code)
    .then(getRequester)
    .then(function (r) {
      return getExpandedContent(r, parsedUrl);
    }).then(function (content) {
      if (toNuke == "nuke") {
        return deepRemove(content, document.getElementById('preserve-distinguished-checkbox').checked);
      } else {
        return deepApprove(content, document.getElementById('preserve-removed-checkbox').checked);
      }

    }).then(function () {
      document.getElementById('done-message').style.display = 'block';
    })
    .catch(function (err) {
      document.getElementById('error-output').style.display = 'block';
      document.getElementById('loading-message').style.display = 'none';
      document.getElementById('done-message').style.display = 'none';
      throw err;
    });
}

function onSubmitClicked () { // eslint-disable-line no-unused-vars
  var url = document.getElementById('thread-url-box').value;
  var preserveDistinguished = document.getElementById('preserve-distinguished-checkbox').checked;
  var e = document.getElementById("to-nuke");
  var toNuke = document.getElementById("to-nuke").value;
  if (cookies.access_token || query.code) {
    return nukeThread(url, toNuke);
  }
  location = getAuthRedirect(JSON.stringify({url, preserveDistinguished}));
}

document.addEventListener('DOMContentLoaded', function () {
  if (query.state && query.code) {
    var parsedState = JSON.parse(decodeURIComponent(query.state));
    var url = decodeURIComponent(parsedState.url);
    document.getElementById('thread-url-box').value = url;
    document.getElementById('preserve-distinguished-checkbox').checked = parsedState.preserveDistinguished;
    document.getElementById('preserve-removed-checkbox').checked = parsedState.preserveRemoved;
    var toNuke = document.getElementById("to-nuke").value;
    nukeThread(url, toNuke);
  }
});<commit_msg>test username pull<commit_after>'use strict';
/* global snoowrap */
var REDDIT_APP_ID = '_s9Icw2TEXpmaw';
var REDIRECT_URI = 'https://pkmnbumba.github.io/reddit-thread-unnuke/';

var USER_AGENT = 'reddit thread un-nuke by /u/bumbalicious || https://github.com/pkmnbumba/reddit-thread-unnuke';
var REQUIRED_SCOPES = ['modposts', 'read'];
var cachedRequester;
var accessTokenPromise;
var removedCount;

var query = parseQueryString(location.search);
var cookies = parseCookieString(document.cookie);

function parseQueryString (str) {
  if (!str) {
    return {};
  }
  var obj = {};
  var pieces = str.slice(1).split('&');
  for (var i = 0; i < pieces.length; i++) {
    var pair = pieces[i].split('=');
    obj[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
  }
  return obj;
}

function parseCookieString (cookieString) {
  var obj = {};
  var splitCookies = cookieString.split('; ');
  splitCookies.forEach(function (cookie) {
    var pair = cookie.split('=');
    obj[pair[0]] = pair[1];
  });
  return obj;
}

var getAuthRedirect = function (state) {
  return `https://reddit.com/api/v1/authorize?client_id=${REDDIT_APP_ID}&response_type=code&state=${encodeURIComponent(state)}&redirect_uri=${encodeURIComponent(REDIRECT_URI)}&duration=temporary&scope=${REQUIRED_SCOPES.join('%2C')}`;
};

function parseUrl (url) {
  var matches = url.match(/^(?:http(?:s?):\/\/)?(?:\w*\.)?reddit\.com\/(?:r\/\w{1,21}\/)?comments\/(\w{1,10})(?:\/[^\/\?]{1,100})?(?:\/(\w{1,10}))?\/?(?:\?.*)?$/);
  if (!matches) {
    throw new TypeError('Invalid URL. Please enter the URL of a reddit Submission or Comment.');
  }
  return matches;
}

function getExpandedContent (requester, urlMatches) {
  return (urlMatches[2] ? requester.get_comment(urlMatches[2]) : requester.get_submission(urlMatches[1])).expand_replies();
}

function deepRemove (content, preserveDistinguished) {
  var replies = content.comments || content.replies;
  var removeCurrentItem = content.distinguished && preserveDistinguished || content.banned_by !== null
    ? Promise.resolve()
    : content.remove().tap(incrementCounter);
  return Promise.all(Array.from(replies).map(function (reply) {
    return deepRemove(reply, preserveDistinguished);
  }).concat([removeCurrentItem]));
}

function deepApprove (content, preserveRemoved) {
  var replies = content.comments || content.replies;
  console.log(content.banned_by['name']);
  console.log(cachedRequester.username);
  var determineRemoved = preserveRemoved;
  var approveCurrentItem = content.removed || content.banned_by === null
    ? Promise.resolve()
    : content.approve().tap(incrementCounter);
  return Promise.all(Array.from(replies).map(function (reply) {
    return deepApprove(reply, preserveRemoved);
  }).concat([approveCurrentItem]));
}

function getAccessToken (code) {
  if (accessTokenPromise) {
    return accessTokenPromise;
  }
  accessTokenPromise = cookies.access_token && !code
    ? Promise.resolve(cookies.access_token)
    : snoowrap.prototype.credentialed_client_request.call({
      user_agent: USER_AGENT,
      client_id: REDDIT_APP_ID,
      client_secret: ''
    }, {
      method: 'post',
      url: 'https://www.reddit.com/api/v1/access_token',
      form: {grant_type: 'authorization_code', code, redirect_uri: REDIRECT_URI}
    }).then(function (response) {
      if (!response.access_token) {
        throw new Error('Authentication failed');
      }
      document.cookie = `access_token=${response.access_token}; max-age=3600; secure`;
      cookies.access_token = response.access_token;
      return response.access_token;
    });
  return accessTokenPromise;
}

function incrementCounter () {
  document.getElementById('removed-count').innerHTML = ++removedCount;
}

function getRequester (access_token) {
  if (cachedRequester) {
    return cachedRequester;
  }
  cachedRequester = new snoowrap({user_agent: USER_AGENT, access_token});
  cachedRequester.config({debug: true, continue_after_ratelimit_error: true});
  return cachedRequester;
}

function nukeThread (url, toNuke) {
  var parsedUrl;
  removedCount = 0;
  try {
    parsedUrl = parseUrl(url);
  } catch (err) {
    document.getElementById('url-error-message').innerHTML = err.message;
    document.getElementById('url-error-message').style.display = 'block';
    throw err;
  }
  document.getElementById('url-error-message').style.display = 'none';
  document.getElementById('error-output').style.display = 'none';
  document.getElementById('loading-message').style.display = 'block';
  document.getElementById('done-message').style.display = 'none';
  return getAccessToken(query.code)
    .then(getRequester)
    .then(function (r) {
      return getExpandedContent(r, parsedUrl);
    }).then(function (content) {
      if (toNuke == "nuke") {
        return deepRemove(content, document.getElementById('preserve-distinguished-checkbox').checked);
      } else {
        return deepApprove(content, document.getElementById('preserve-removed-checkbox').checked);
      }

    }).then(function () {
      document.getElementById('done-message').style.display = 'block';
    })
    .catch(function (err) {
      document.getElementById('error-output').style.display = 'block';
      document.getElementById('loading-message').style.display = 'none';
      document.getElementById('done-message').style.display = 'none';
      throw err;
    });
}

function onSubmitClicked () { // eslint-disable-line no-unused-vars
  var url = document.getElementById('thread-url-box').value;
  var preserveDistinguished = document.getElementById('preserve-distinguished-checkbox').checked;
  var e = document.getElementById("to-nuke");
  var toNuke = document.getElementById("to-nuke").value;
  if (cookies.access_token || query.code) {
    return nukeThread(url, toNuke);
  }
  location = getAuthRedirect(JSON.stringify({url, preserveDistinguished}));
}

document.addEventListener('DOMContentLoaded', function () {
  if (query.state && query.code) {
    var parsedState = JSON.parse(decodeURIComponent(query.state));
    var url = decodeURIComponent(parsedState.url);
    document.getElementById('thread-url-box').value = url;
    document.getElementById('preserve-distinguished-checkbox').checked = parsedState.preserveDistinguished;
    document.getElementById('preserve-removed-checkbox').checked = parsedState.preserveRemoved;
    var toNuke = document.getElementById("to-nuke").value;
    nukeThread(url, toNuke);
  }
});<|endoftext|>


<commit_before>/**
 * @constructor
 */
function FormProcessor() {
  'use strict';
  if ('undefined' === typeof jQuery) {
    throw new Error('Presto-Calc\'s JavaScript requires jQuery');
  }
  if ('undefined' === typeof Ladda) {
    throw new Error('Presto-Calc\'s JavaScript requires Ladda');
  }
}

FormProcessor.prototype = {
  constructor: FormProcessor,

  /**
   * @param $form
   * @param button
   */
  process: function ($form, button) {
    'use strict';

    jQuery('#results').remove();
    jQuery('#errors').remove();

    var l = Ladda.create(button);
    l.start();

    var startDateVal = $form.find('#startDate').val().split('-');
    var endDateVal = $form.find('#endDate').val().split('-');

    // In JS, 00 = January & 11 = December
    var data = {
      startDate:       new Date(startDateVal[0], startDateVal[1] - 1, startDateVal[2]),
      endDate:         new Date(endDateVal[0], endDateVal[1] - 1, endDateVal[2]),
      tripsWorkDay:    parseInt($form.find('#tripsWorkDay').val(), 10),
      tripsWeekendDay: parseInt($form.find('#tripsWeekendDay').val(), 10),
      costSingleTrip:  parseFloat($form.find('#prestoSingleTrip').val()),
      costMonthlyPass: parseFloat($form.find('#prestoMonthlyPass').val())
    };

    if (data.endDate - data.startDate < 1) {
      jQuery('
Your end date must be after your start date.
').insertBefore($form);
      l.stop();
      return false;
    }

    var curDate = data.startDate;
    var totalTrips = 0;
    while (curDate <= data.endDate) {
      if (curDate.getDay() === 0 || curDate.getDay() === 6) {
        totalTrips += data.tripsWeekendDay;
      } else {
        totalTrips += data.tripsWorkDay;
      }
      curDate.setDate(curDate.getDate() + 1);
    }

    var $resultsDiv = jQuery('

Results

')
      .append('By our calculations, you will make ' + totalTrips + ' payable trips during the given period.
');
    if (totalTrips * data.costSingleTrip > data.costMonthlyPass) {
      $resultsDiv.append(jQuery('You should buy your monthly pass!'));
    } else {
      $resultsDiv.append(jQuery('You should not buy your monthly pass!'));
    }
    $resultsDiv.append('
It would cost you ' + totalTrips * data.costSingleTrip + ' to pay for each trip individually.');

    $resultsDiv.insertBefore($form.parents('#config-form'));

    console.log(data);
    l.stop();
  }
};
<commit_msg>Added days counter<commit_after>/**
 * @constructor
 */
function FormProcessor() {
  'use strict';
  if ('undefined' === typeof jQuery) {
    throw new Error('Presto-Calc\'s JavaScript requires jQuery');
  }
  if ('undefined' === typeof Ladda) {
    throw new Error('Presto-Calc\'s JavaScript requires Ladda');
  }
}

FormProcessor.prototype = {
  constructor: FormProcessor,

  /**
   * @param $form
   * @param button
   */
  process: function ($form, button) {
    'use strict';

    jQuery('#results').remove();
    jQuery('#errors').remove();

    var l = Ladda.create(button);
    l.start();

    var startDateVal = $form.find('#startDate').val().split('-');
    var endDateVal = $form.find('#endDate').val().split('-');

    // In JS, 00 = January & 11 = December
    var data = {
      startDate:       new Date(startDateVal[0], startDateVal[1] - 1, startDateVal[2]),
      endDate:         new Date(endDateVal[0], endDateVal[1] - 1, endDateVal[2]),
      tripsWorkDay:    parseInt($form.find('#tripsWorkDay').val(), 10),
      tripsWeekendDay: parseInt($form.find('#tripsWeekendDay').val(), 10),
      costSingleTrip:  parseFloat($form.find('#prestoSingleTrip').val()),
      costMonthlyPass: parseFloat($form.find('#prestoMonthlyPass').val())
    };

    if (data.endDate - data.startDate < 1) {
      jQuery('
Your end date must be after your start date.
').insertBefore($form);
      l.stop();
      return false;
    }

    var curDate = data.startDate;
    var totalTrips = 0;
    var days = 0;
    while (curDate <= data.endDate) {
      days++;
      if (curDate.getDay() === 0 || curDate.getDay() === 6) {
        totalTrips += data.tripsWeekendDay;
      } else {
        totalTrips += data.tripsWorkDay;
      }
      curDate.setDate(curDate.getDate() + 1);
    }

    var $resultsDiv = jQuery('

Results

')
      .append('By our calculations, you will make ' + totalTrips + ' payable trips during the given period ('+days+' days).
');
    if (totalTrips * data.costSingleTrip > data.costMonthlyPass) {
      $resultsDiv.append(jQuery('You should buy your monthly pass!'));
    } else {
      $resultsDiv.append(jQuery('You should not buy your monthly pass!'));
    }
    $resultsDiv.append('
It would cost you ' + totalTrips * data.costSingleTrip + ' to pay for each trip individually.');

    $resultsDiv.insertBefore($form.parents('#config-form'));

    console.log(data);
    l.stop();
  }
};
<|endoftext|>


<commit_before>/** @license MIT License (c) copyright B Cavalier & J Hann */

/**
 * wire/dojo/events plugin
 * wire plugin that can connect event handlers after an object is
 * initialized, and disconnect them when an object is destroyed.
 * This implementation uses dojo.connect and dojo.disconnect to do
 * the work of connecting and disconnecting event handlers.
 *
 * wire is part of the cujo.js family of libraries (http://cujojs.com/)
 *
 * Licensed under the MIT License at:
 * http://www.opensource.org/licenses/mit-license.php
 */

define(['dojo', 'dojo/_base/event'], function(events) {

	return {
		wire$plugin: function eventsPlugin(ready, destroyed, options) {

			var connectHandles = [];

			function connect(target, ref, options, wire) {
				var eventName;
				// If ref is a method on target, connect it to another object's method, i.e. calling a method on target
				// causes a method on the other object to be called.
				// If ref is a reference to another object, connect that object's method to a method on target, i.e.
				// calling a method on the other object causes a method on target to be called.
				if(typeof target[ref] == 'function') {
					eventName = ref;
					for(ref in options) {
						wire.resolveRef(ref).then(function(resolved) {
							connectHandles.push(events.connect(target, eventName, resolved, options[ref]));
						});
					}
				} else {
					wire.resolveRef(ref).then(function(resolved) {
						for(eventName in options) {
							connectHandles.push(events.connect(resolved, eventName, target, options[eventName]));
						}
					});
				}

			}

			/*
				Function: connectFacet
				Setup connections for each specified in the connects param.  Each key
				in connects is a reference, and the corresponding value is an object
				whose keys are event names, and whose values are methods of object to
				invoke.  For example:
				connect: {
					"refToOtherThing": {
						"eventOrMethodOfOtherThing": "myOwnMethod"
					},
					"dom!myButton": {
						"onclick": "_handleButtonClick"
					},
					"dijit!myWidget": {
						"onChange": "_handleValueChange"
					}

					"myOwnEventOrMethod": {
						"refToOtherThing": "methodOfOtherThing"
					}
				}

				Parameters:
					factory - wiring factory
					object - object being wired, will be the target of connected events
					connects - specification of events to connect, see examples above.
			*/
			function connectFacet(wire, target, connects) {
				for(var ref in connects) {
					connect(target, ref, connects[ref], wire);
				}
			}

			destroyed.then(function onContextDestroy() {
				for (var i = connectHandles.length - 1; i >= 0; i--){
					events.disconnect(connectHandles[i]);
				}
			});

			return {
				facets: {
					connect: {
						ready: function(promise, facet, wire) {
							connectFacet(wire, facet.target, facet.options);
							promise.resolve();
						}
					}
				}
			};
		}
	};
});<commit_msg>Initial fix for dojo events plugin not waiting for ref resolution promise before indicating that it is done<commit_after>/** @license MIT License (c) copyright B Cavalier & J Hann */

/**
 * wire/dojo/events plugin
 * wire plugin that can connect event handlers after an object is
 * initialized, and disconnect them when an object is destroyed.
 * This implementation uses dojo.connect and dojo.disconnect to do
 * the work of connecting and disconnecting event handlers.
 *
 * wire is part of the cujo.js family of libraries (http://cujojs.com/)
 *
 * Licensed under the MIT License at:
 * http://www.opensource.org/licenses/mit-license.php
 */

define(['when', 'dojo', 'dojo/_base/event'], function(when, events) {

	return {
		wire$plugin: function eventsPlugin(ready, destroyed, options) {

			var connectHandles = [];

			function connect(target, ref, options, wire) {
				var eventName, promise, promises;

                promises = [];

				// If ref is a method on target, connect it to another object's method, i.e. calling a method on target
				// causes a method on the other object to be called.
				// If ref is a reference to another object, connect that object's method to a method on target, i.e.
				// calling a method on the other object causes a method on target to be called.
				if(typeof target[ref] == 'function') {
					eventName = ref;
					for(ref in options) {
						promise = wire.resolveRef(ref).then(function(resolved) {
							connectHandles.push(events.connect(target, eventName, resolved, options[ref]));
						});

                        promises.push(promise);
					}

                    promise = when.all(promises);

				} else {
					promise = wire.resolveRef(ref).then(function(resolved) {
						for(eventName in options) {
							connectHandles.push(events.connect(resolved, eventName, target, options[eventName]));
						}
					});
				}

                return promise;
			}

			/*
				Function: connectFacet
				Setup connections for each specified in the connects param.  Each key
				in connects is a reference, and the corresponding value is an object
				whose keys are event names, and whose values are methods of object to
				invoke.  For example:
				connect: {
					"refToOtherThing": {
						"eventOrMethodOfOtherThing": "myOwnMethod"
					},
					"dom!myButton": {
						"onclick": "_handleButtonClick"
					},
					"dijit!myWidget": {
						"onChange": "_handleValueChange"
					}

					"myOwnEventOrMethod": {
						"refToOtherThing": "methodOfOtherThing"
					}
				}

				Parameters:
					factory - wiring factory
					object - object being wired, will be the target of connected events
					connects - specification of events to connect, see examples above.
			*/
			function connectFacet(wire, target, connects) {
                var promises = [];

				for(var ref in connects) {
					promises.push(connect(target, ref, connects[ref], wire));
				}

                return when.all(promises);
			}

			destroyed.then(function onContextDestroy() {
				for (var i = connectHandles.length - 1; i >= 0; i--){
					events.disconnect(connectHandles[i]);
				}
			});

			return {
				facets: {
					connect: {
						ready: function(resolver, facet, wire) {
                            when(connectFacet(wire, facet.target, facet.options),
                                resolver.resolve,
                                resolver.reject);
						}
					}
				}
			};
		}
	};
});<|endoftext|>


<commit_before>function Chords( $, functions, share, toolbar, resizer )
{
  'use strict';
  if ( Chords.prototype._instance )
  {
    return Chords.prototype._instance;
  }
  Chords.prototype._instance = this;

  var PLUGIN_ID = "01", DEFAULT_FORMAT = 0;

  var DEFAULT_TIME_SIGNATURE = 4;
  var CHORDS_COUNT_LENGTH = 2;
  var CHORDITEMS_COUNT_LENGTH = 2;
  var TIME_SIGNATURE_LENGTH = 1;
  var CHORD_SIZE = 1;
  var BEAT_SIZE = 1;

  var $SINGLE_BARLINE = $( '

' );
  var $PARENT = $( '#items' );
  var $TIME_SIGNATURE = $( '#time-signature' );
  var $LI = $( '
' );
  var $HANDLE = $( '

' );
  var $INPUT = $( '<input class="chord-text resize-trigger" type="text" title="Add a chord" placeholder="Chord…" />' );
  var $CHORD = $( '
' );

  var beatsHandler = new Beats( $TIME_SIGNATURE, share );

  var postRenderers = [];
  var contentExtractors = [];

  var format = DEFAULT_FORMAT;
  var data = null;

  registerContentExtractor( extract );

  toolbar.registerChordsModule( {
    "getExtracts" : getExtracts,
    "createFromExtracts" : createFromExtracts
  } );

  functions.bindButton( "#add-chord", createItem );

  share.addStructureChangeListener( handleStructureChange );

  share.addTextChangeListener( function( event )
  {
    if ( event.data && event.data.item )
    {
      resizer.performResize( $( event.data.item ) );
    }
  } );

  function setData( inputFormat, inputData )
  {
    format = inputFormat;
    data = inputData;
  }

  function addPostRenderer( renderer )
  {
    postRenderers.push( renderer );
  }

  function render()
  {
    $PARENT.empty();
    if ( !data )
    {
      return;
    }
    if ( format !== DEFAULT_FORMAT )
    {
      throw "Unknown chords data format.";
    }
    var deserializedData = deserialize( data );
    var chordItems = deserializedData.chordItems;
    var timeSignature = deserializedData.timeSignature;
    var hasText = chordItems && chordItems[0] && chordItems[0].lyrics !== undefined;
    if ( hasText )
    {
      $PARENT.addClass( 'has-text' );
    }
    var beatsSum = 0;
    $.each( chordItems, function()
    {
      createItem( this );
      beatsSum += this.beats;
      if ( beatsSum % timeSignature === 0 )
      {
        $SINGLE_BARLINE.clone().appendTo( $PARENT );
      }
    } );
    $TIME_SIGNATURE.val( "" + deserializedData.timeSignature );
    $.each( postRenderers, function()
    {
      this();
    } );
    $PARENT.children( 'li.item' ).each( function()
    {
      resizer.performResize( $( this ) );
    } );
  }

  function deserialize( input )
  {
    var chords = [];
    var chordItems = [];
    var timeSignature = DEFAULT_TIME_SIGNATURE;
    var currentPos = 0;
    try
    {
      timeSignature = functions.getNumber( input.substr( currentPos++, TIME_SIGNATURE_LENGTH ) );

      var read = functions.readStringArray( {
        'data' : input,
        'currentPos' : currentPos,
        'countSize' : CHORDS_COUNT_LENGTH
      } );
      chords = read.array;
      currentPos = read.position;

      var chordBeatChunks = functions.readChunkArray( {
        'data' : input,
        'currentPos' : currentPos,
        'countSize' : CHORDITEMS_COUNT_LENGTH,
        'chunkSize' : CHORD_SIZE + BEAT_SIZE
      } );
      $.each( chordBeatChunks, function()
      {
        var chordText = chords[functions.getNumber( this.charAt( 0 ) )];
        var chordBeat = functions.getNumber( this.charAt( 1 ) );
        chordItems.push( new ChordBeat( chordText, chordBeat ) );
      } );
    }
    catch ( err )
    {
      console.log( err );
    }
    return {
      "chordItems" : chordItems,
      "timeSignature" : timeSignature
    };
  }

  function ChordBeat( chord, beats )
  {
    this.chord = chord;
    this.beats = beats;
  }

  function serialize()
  {
    var result = PLUGIN_ID + DEFAULT_FORMAT;
    var state = getData();
    var chords = state.chords;
    var chordItems = state.chordItems;

    result += functions.getCharacters( state.timeSignature, TIME_SIGNATURE_LENGTH );

    result += functions.getCharacters( chords.length, CHORDS_COUNT_LENGTH );
    for ( var i = 0; i < chords.length; i++ )
    {
      var serializedChord = functions.encode( chords[i] );
      result += functions.getCharacters( serializedChord.length, 1 );
      result += serializedChord;
    }

    result += functions.getCharacters( chordItems.length, CHORDITEMS_COUNT_LENGTH );
    result += chordItems.join( '' );

    return result.length > 7 ? result : '';
  }

  function getData()
  {
    var chords = {}, chordNo = 0;
    var chordValues = [];
    var chordItems = [];
    var timeSignature = $TIME_SIGNATURE.val();
    $PARENT.children( 'li.item' ).each( function( index )
    {
      var chordData = getChordData( this );
      var val = chordData.chord;
      if ( !( val in chords ) )
      {
        chords[val] = functions.getCharacters( chordNo, CHORD_SIZE );
        chordNo++;
        chordValues.push( val );
      }
      chordItems.push( chords[val] + functions.getCharacters( chordData.beats, BEAT_SIZE ) );
    } );
    return {
      'chords' : chordValues,
      'chordItems' : chordItems,
      'timeSignature' : timeSignature
    };
  }

  function handleStructureChange( event )
  {
    updateBarlines();
  }

  function updateBarlines()
  {
    $PARENT.children( 'li.item-barline' ).remove();
    var timeSignature = $TIME_SIGNATURE.val();
    var beatsSum = 0;
    $PARENT.children( 'li.item' ).each( function( index )
    {
      beatsSum += getBeats( this ).length;
      if ( beatsSum % timeSignature === 0 )
      {
        $SINGLE_BARLINE.clone().insertAfter( this );
      }
    } );
  }

  function registerContentExtractor( extractor )
  {
    contentExtractors.push( extractor );
  }

  function extract( li )
  {
    var chord = getChord( li );
    var beats = getBeats( li );
    return function( theItem )
    {
      $( 'input.chord-text', theItem ).val( chord );
      $( 'div.duration > a', theItem ).text( beats );
    };
  }

  function getExtracts( li )
  {
    var extracts = [];
    for ( var i = 0; i < contentExtractors.length; i++ )
    {
      extracts.push( contentExtractors[i]( li ) );
    }
    return extracts;
  }

  function createFromExtracts( extracts )
  {
    var li = createItem();
    for ( var i = 0; i < extracts.length; i++ )
    {
      extracts[i]( li );
    }
  }

  function getBeats( li )
  {
    return $( 'div.duration > a', li ).text();
  }

  function getChord( li )
  {
    return $( 'input.chord-text', li ).val();
  }

  function getChordData( li )
  {
    var chordText = getChord( li );
    var beatCount = getBeats( li ).length;
    return new ChordBeat( chordText, beatCount );
  }

  function createItem( chordData )
  {
    var chordText = undefined;
    var beats = undefined;
    if ( chordData !== undefined )
    {
      chordText = chordData.chord;
      beats = chordData.beats;
    }
    var wrapper = $LI.clone().append( $HANDLE.clone() );
    var input = $INPUT.clone();
    if ( chordText )
    {
      input.val( chordText );
    }
    var div = $CHORD.clone();
    input.appendTo( div );
    wrapper.append( div );
    // wrapper.append( more );
    wrapper.appendTo( $PARENT );

    beatsHandler.createBeats( beats, wrapper );

    $( input ).keydown( functions.handleInputKeyEvent ).blur( {
      'item' : wrapper
    }, function( event )
    {
      input.val( transformChordString( input.val() ) );
      share.changedText( event );
    } );

    addPinEvents( wrapper );

    resizer.prepareResize( wrapper );
    if ( chordData === undefined )
    {
      // create a blank item
      input.focus();
      share.changedStructure( 'chords/new' );
    }

    return wrapper;
  }

  function setCharAt( str, index, chr )
  {
    if ( index > str.length - 1 )
    {
      return str;
    }
    return str.substr( 0, index ) + chr + str.substr( index + 1 );
  }

  function transformChordString( string )
  {
    var inputContent = $.trim( string );
    if ( inputContent.length > 1 )
    {
      var secondChar = inputContent.charAt( 1 );
      if ( secondChar === 'b' )
      {
        inputContent = setCharAt( inputContent, 1, '♭' );
      }
      else if ( secondChar === '#' )
      {
        inputContent = setCharAt( inputContent, 1, '♯' );
      }
    }
    return inputContent;
  }

  function checkAbsentKey( key )
  {
    return key !== undefined && key == false;
  }

  return {
    "render" : render,
    "serialize" : serialize,
    "setData" : setData,
    "registerContentExtractor" : registerContentExtractor,
    "addPostRenderer" : addPostRenderer
  };
}

function Beats( $TIME_SIGNATURE, share )
{
  'use strict';
  var MAX_BULLETS = 16;
  var $BEATS_WRAPPER = $( '
' );
  var $BEATS_LINK = $( '' );
  var $BEATS_LIST = $( '
' );
  var BULLET_STRING = '••••••••••••••••';
  var BULLETS = [];
  for ( var len = 0; len <= MAX_BULLETS; len++ )
  {
    BULLETS.push( BULLET_STRING.substr( 0, len ) );
  }

  function createBeats( beats, wrapper )
  {

    var defaultBeats = parseInt( $TIME_SIGNATURE.val() );
    var num = defaultBeats;
    if ( beats !== undefined )
    {
      num = beats;
    }
    var beatsWrapper = $BEATS_WRAPPER.clone();
    var currentBeats = $BEATS_LINK.clone();
    currentBeats.appendTo( beatsWrapper );
    currentBeats.text( BULLETS[num] );
    currentBeats.dropdown();
    var list = $BEATS_LIST.clone();
    list.appendTo( beatsWrapper );

    for ( var len = defaultBeats; len > 0; len-- )
    {
      var beatString = BULLETS[len];
      var option = $( '
' + beatString + '
' );
      option.appendTo( list );
      option.click( {
        'beatString' : beatString
      }, function( event )
      {
        currentBeats.dropdown( 'toggle' );
        if ( currentBeats.text() !== event.data.beatString )
        {
          currentBeats.text( event.data.beatString );
          currentBeats.blur();
          share.changedStructure( "chords/beats" );
        }
        return false;
      } );
    }

    beatsWrapper.appendTo( wrapper );
  }

  return {
    'createBeats' : createBeats
  };
}

function addPinEvents( wrapper )
{
  'use strict';
  $( '.icon-pushpin', wrapper ).mousedown( function( event )
  {
    event.stopImmediatePropagation();
    if ( wrapper.hasClass( 'ui-selected' ) )
    {
      wrapper.removeClass( 'ui-selected' );
    }
    else
    {
      wrapper.addClass( 'ui-selected' );
    }
    return false;
  } );
}

define( "chords", [ "plugins", "jquery", "functions", "share", "toolbar", "resizer" ], function( plugins, $, functions,
    share, toolbar, resizer )
{
  'use strict';
  plugins.register( {
    "name" : "chords",
    "instance" : new Chords( $, functions, share, toolbar, resizer ),
    "render" : true,
    "serialize" : true
  } );
} );
<commit_msg>Fixed bug in previous commit - empty content should create empty hash.<commit_after>function Chords( $, functions, share, toolbar, resizer )
{
  'use strict';
  if ( Chords.prototype._instance )
  {
    return Chords.prototype._instance;
  }
  Chords.prototype._instance = this;

  var PLUGIN_ID = "01", DEFAULT_FORMAT = 0;

  var DEFAULT_TIME_SIGNATURE = 4;
  var CHORDS_COUNT_LENGTH = 2;
  var CHORDITEMS_COUNT_LENGTH = 2;
  var TIME_SIGNATURE_LENGTH = 1;
  var CHORD_SIZE = 1;
  var BEAT_SIZE = 1;

  var $SINGLE_BARLINE = $( '

' );
  var $PARENT = $( '#items' );
  var $TIME_SIGNATURE = $( '#time-signature' );
  var $LI = $( '
' );
  var $HANDLE = $( '

' );
  var $INPUT = $( '<input class="chord-text resize-trigger" type="text" title="Add a chord" placeholder="Chord…" />' );
  var $CHORD = $( '
' );

  var beatsHandler = new Beats( $TIME_SIGNATURE, share );

  var postRenderers = [];
  var contentExtractors = [];

  var format = DEFAULT_FORMAT;
  var data = null;

  registerContentExtractor( extract );

  toolbar.registerChordsModule( {
    "getExtracts" : getExtracts,
    "createFromExtracts" : createFromExtracts
  } );

  functions.bindButton( "#add-chord", createItem );

  share.addStructureChangeListener( handleStructureChange );

  share.addTextChangeListener( function( event )
  {
    if ( event.data && event.data.item )
    {
      resizer.performResize( $( event.data.item ) );
    }
  } );

  function setData( inputFormat, inputData )
  {
    format = inputFormat;
    data = inputData;
  }

  function addPostRenderer( renderer )
  {
    postRenderers.push( renderer );
  }

  function render()
  {
    $PARENT.empty();
    if ( !data )
    {
      return;
    }
    if ( format !== DEFAULT_FORMAT )
    {
      throw "Unknown chords data format.";
    }
    var deserializedData = deserialize( data );
    var chordItems = deserializedData.chordItems;
    var timeSignature = deserializedData.timeSignature;
    var hasText = chordItems && chordItems[0] && chordItems[0].lyrics !== undefined;
    if ( hasText )
    {
      $PARENT.addClass( 'has-text' );
    }
    var beatsSum = 0;
    $.each( chordItems, function()
    {
      createItem( this );
      beatsSum += this.beats;
      if ( beatsSum % timeSignature === 0 )
      {
        $SINGLE_BARLINE.clone().appendTo( $PARENT );
      }
    } );
    $TIME_SIGNATURE.val( "" + deserializedData.timeSignature );
    $.each( postRenderers, function()
    {
      this();
    } );
    $PARENT.children( 'li.item' ).each( function()
    {
      resizer.performResize( $( this ) );
    } );
  }

  function deserialize( input )
  {
    var chords = [];
    var chordItems = [];
    var timeSignature = DEFAULT_TIME_SIGNATURE;
    var currentPos = 0;
    try
    {
      timeSignature = functions.getNumber( input.substr( currentPos++, TIME_SIGNATURE_LENGTH ) );

      var read = functions.readStringArray( {
        'data' : input,
        'currentPos' : currentPos,
        'countSize' : CHORDS_COUNT_LENGTH
      } );
      chords = read.array;
      currentPos = read.position;

      var chordBeatChunks = functions.readChunkArray( {
        'data' : input,
        'currentPos' : currentPos,
        'countSize' : CHORDITEMS_COUNT_LENGTH,
        'chunkSize' : CHORD_SIZE + BEAT_SIZE
      } );
      $.each( chordBeatChunks, function()
      {
        var chordText = chords[functions.getNumber( this.charAt( 0 ) )];
        var chordBeat = functions.getNumber( this.charAt( 1 ) );
        chordItems.push( new ChordBeat( chordText, chordBeat ) );
      } );
    }
    catch ( err )
    {
      console.log( err );
    }
    return {
      "chordItems" : chordItems,
      "timeSignature" : timeSignature
    };
  }

  function ChordBeat( chord, beats )
  {
    this.chord = chord;
    this.beats = beats;
  }

  function serialize()
  {
    var result = PLUGIN_ID + DEFAULT_FORMAT;
    var state = getData();
    var chords = state.chords;
    var chordItems = state.chordItems;

    result += functions.getCharacters( state.timeSignature, TIME_SIGNATURE_LENGTH );

    result += functions.getCharacters( chords.length, CHORDS_COUNT_LENGTH );
    for ( var i = 0; i < chords.length; i++ )
    {
      var serializedChord = functions.encode( chords[i] );
      result += functions.getCharacters( serializedChord.length, 1 );
      result += serializedChord;
    }

    result += functions.getCharacters( chordItems.length, CHORDITEMS_COUNT_LENGTH );
    result += chordItems.join( '' );

    return result.length > 8 ? result : '';
  }

  function getData()
  {
    var chords = {}, chordNo = 0;
    var chordValues = [];
    var chordItems = [];
    var timeSignature = $TIME_SIGNATURE.val();
    $PARENT.children( 'li.item' ).each( function( index )
    {
      var chordData = getChordData( this );
      var val = chordData.chord;
      if ( !( val in chords ) )
      {
        chords[val] = functions.getCharacters( chordNo, CHORD_SIZE );
        chordNo++;
        chordValues.push( val );
      }
      chordItems.push( chords[val] + functions.getCharacters( chordData.beats, BEAT_SIZE ) );
    } );
    return {
      'chords' : chordValues,
      'chordItems' : chordItems,
      'timeSignature' : timeSignature
    };
  }

  function handleStructureChange( event )
  {
    updateBarlines();
  }

  function updateBarlines()
  {
    $PARENT.children( 'li.item-barline' ).remove();
    var timeSignature = $TIME_SIGNATURE.val();
    var beatsSum = 0;
    $PARENT.children( 'li.item' ).each( function( index )
    {
      beatsSum += getBeats( this ).length;
      if ( beatsSum % timeSignature === 0 )
      {
        $SINGLE_BARLINE.clone().insertAfter( this );
      }
    } );
  }

  function registerContentExtractor( extractor )
  {
    contentExtractors.push( extractor );
  }

  function extract( li )
  {
    var chord = getChord( li );
    var beats = getBeats( li );
    return function( theItem )
    {
      $( 'input.chord-text', theItem ).val( chord );
      $( 'div.duration > a', theItem ).text( beats );
    };
  }

  function getExtracts( li )
  {
    var extracts = [];
    for ( var i = 0; i < contentExtractors.length; i++ )
    {
      extracts.push( contentExtractors[i]( li ) );
    }
    return extracts;
  }

  function createFromExtracts( extracts )
  {
    var li = createItem();
    for ( var i = 0; i < extracts.length; i++ )
    {
      extracts[i]( li );
    }
  }

  function getBeats( li )
  {
    return $( 'div.duration > a', li ).text();
  }

  function getChord( li )
  {
    return $( 'input.chord-text', li ).val();
  }

  function getChordData( li )
  {
    var chordText = getChord( li );
    var beatCount = getBeats( li ).length;
    return new ChordBeat( chordText, beatCount );
  }

  function createItem( chordData )
  {
    var chordText = undefined;
    var beats = undefined;
    if ( chordData !== undefined )
    {
      chordText = chordData.chord;
      beats = chordData.beats;
    }
    var wrapper = $LI.clone().append( $HANDLE.clone() );
    var input = $INPUT.clone();
    if ( chordText )
    {
      input.val( chordText );
    }
    var div = $CHORD.clone();
    input.appendTo( div );
    wrapper.append( div );
    // wrapper.append( more );
    wrapper.appendTo( $PARENT );

    beatsHandler.createBeats( beats, wrapper );

    $( input ).keydown( functions.handleInputKeyEvent ).blur( {
      'item' : wrapper
    }, function( event )
    {
      input.val( transformChordString( input.val() ) );
      share.changedText( event );
    } );

    addPinEvents( wrapper );

    resizer.prepareResize( wrapper );
    if ( chordData === undefined )
    {
      // create a blank item
      input.focus();
      share.changedStructure( 'chords/new' );
    }

    return wrapper;
  }

  function setCharAt( str, index, chr )
  {
    if ( index > str.length - 1 )
    {
      return str;
    }
    return str.substr( 0, index ) + chr + str.substr( index + 1 );
  }

  function transformChordString( string )
  {
    var inputContent = $.trim( string );
    if ( inputContent.length > 1 )
    {
      var secondChar = inputContent.charAt( 1 );
      if ( secondChar === 'b' )
      {
        inputContent = setCharAt( inputContent, 1, '♭' );
      }
      else if ( secondChar === '#' )
      {
        inputContent = setCharAt( inputContent, 1, '♯' );
      }
    }
    return inputContent;
  }

  function checkAbsentKey( key )
  {
    return key !== undefined && key == false;
  }

  return {
    "render" : render,
    "serialize" : serialize,
    "setData" : setData,
    "registerContentExtractor" : registerContentExtractor,
    "addPostRenderer" : addPostRenderer
  };
}

function Beats( $TIME_SIGNATURE, share )
{
  'use strict';
  var MAX_BULLETS = 16;
  var $BEATS_WRAPPER = $( '
' );
  var $BEATS_LINK = $( '' );
  var $BEATS_LIST = $( '
' );
  var BULLET_STRING = '••••••••••••••••';
  var BULLETS = [];
  for ( var len = 0; len <= MAX_BULLETS; len++ )
  {
    BULLETS.push( BULLET_STRING.substr( 0, len ) );
  }

  function createBeats( beats, wrapper )
  {

    var defaultBeats = parseInt( $TIME_SIGNATURE.val() );
    var num = defaultBeats;
    if ( beats !== undefined )
    {
      num = beats;
    }
    var beatsWrapper = $BEATS_WRAPPER.clone();
    var currentBeats = $BEATS_LINK.clone();
    currentBeats.appendTo( beatsWrapper );
    currentBeats.text( BULLETS[num] );
    currentBeats.dropdown();
    var list = $BEATS_LIST.clone();
    list.appendTo( beatsWrapper );

    for ( var len = defaultBeats; len > 0; len-- )
    {
      var beatString = BULLETS[len];
      var option = $( '
' + beatString + '
' );
      option.appendTo( list );
      option.click( {
        'beatString' : beatString
      }, function( event )
      {
        currentBeats.dropdown( 'toggle' );
        if ( currentBeats.text() !== event.data.beatString )
        {
          currentBeats.text( event.data.beatString );
          currentBeats.blur();
          share.changedStructure( "chords/beats" );
        }
        return false;
      } );
    }

    beatsWrapper.appendTo( wrapper );
  }

  return {
    'createBeats' : createBeats
  };
}

function addPinEvents( wrapper )
{
  'use strict';
  $( '.icon-pushpin', wrapper ).mousedown( function( event )
  {
    event.stopImmediatePropagation();
    if ( wrapper.hasClass( 'ui-selected' ) )
    {
      wrapper.removeClass( 'ui-selected' );
    }
    else
    {
      wrapper.addClass( 'ui-selected' );
    }
    return false;
  } );
}

define( "chords", [ "plugins", "jquery", "functions", "share", "toolbar", "resizer" ], function( plugins, $, functions,
    share, toolbar, resizer )
{
  'use strict';
  plugins.register( {
    "name" : "chords",
    "instance" : new Chords( $, functions, share, toolbar, resizer ),
    "render" : true,
    "serialize" : true
  } );
} );
<|endoftext|>


<commit_before>// http://jsperf.com/string-concat-vs-array-join-10000/8
// http://jsperf.com/advance-templates-benchmark/3
// http://jsperf.com/js-template-engines-performance/26

// COMPILATION FLAGS
// ------------------------------------------------------------------------------------------------
/** @define {boolean} */	var DEBUG = true;
/** @define {string} */		var WHACK_NAME = "Whack";

// SYNTAX OPTS
// ------------------------------------------------------------------------------------------------
/** @define {boolean} */	var USE_WITH = false;
/** @define {string} */		var OPEN_TAG = "{{";
/** @define {string} */		var CLOSE_TAG = "}}";
/** @define {string} */		var OPERATOR_ECHO = "=";
/** @define {string} */		var OPERATOR_ECHO_ESCAPE = "~";
/** @define {string} */		var OPERATOR_COMMENT = "!";
/** @define {string} */		var OUTPUT_VAR = "_o";

// SUPPORT OPTS
// ------------------------------------------------------------------------------------------------
/** @define {boolean} */	var SUPPORT_OLD_BROWSERS = false;
/** @define {boolean} */	var SUPPORT_SHORTCODES = true;
/** @define {boolean} */	var SUPPORT_EXTENDS = true;
/** @define {boolean} */	var SUPPORT_PARTIALS = true;
/** @define {boolean} */	var SUPPORT_FILTERS = true;

+function(_window){

	var tplHash = 0;

	// regexp.test is the fastest way
	// E.g. var x = /(\w+)/.test( string ) && RegExp.$1
	// http://jsperf.com/js-exec-vs-match/2
	var _RegExp = _window['RegExp'];

	// Operators codes
	var KEY_JS = 1,
		KEY_FOR = 2,
		KEY_BLOCK = 3;

	// Cache indexes
	var _cacheCompiled = {},
		_cacheCompiledByTpl = {},
		_cachePartials = {},
		_cacheParsing = {};

	// Code defaults
	var CODE_FIRST, CODE_LAST;

	// It is possible to use Whack in underscore's style
	// E.g.  {a: 2} => a
	if( USE_WITH ) {
		CODE_FIRST = "var "+OUTPUT_VAR+"='';with(data){",
		CODE_LAST  = "}return " + OUTPUT_VAR;
	} else {
		CODE_FIRST = "var "+OUTPUT_VAR+"='';",
		CODE_LAST  = "return " + OUTPUT_VAR;
	}

	// Code parse shortcuts
	var parsemap;

	if( SUPPORT_SHORTCODES ) {
		parsemap = {
			"end" : function(){return {operator: KEY_JS, _code: '}'}},
			"else" : function(){return {operator: KEY_JS, _code: '} else {'}},
			"if" : function(code){
				var p = code.lastIndexOf(':');
				var notTernearyExp = code.split(':').slice(-1)[0].trim()==='';
				if( p >-1 && notTernearyExp){
					code = code.substr(0, p) + '{';
				} else if( p === -1 ){
					if(code.split(')').slice(-1)[0].trim()==='') code+= '{';
				}
				return {
					operator : KEY_JS,
					_code : code
				};
			},
			"each" : function(code){
				var vals = regexFOR.test(code) && _RegExp;
				if( DEBUG ) {
					console.log("Parsing <for>: ", code, vals);
				}
				return {
					operator : KEY_FOR,
					_code : [vals.$1, vals.$2]
				}
			}
		}
	} else {
		parsemap = {};
	}

	if( SUPPORT_PARTIALS ) {
		parsemap["include"] = function(code) {
			var r = regexOP.test(code) && _RegExp.$1;
			return {
				operator : KEY_JS,
				_code : _cachePartials[r] || (_cachePartials[r] = _cacheCompiled[r].replace(CODE_FIRST, "").replace(CODE_LAST, ""))
			}
		}
	}

	if( SUPPORT_EXTENDS ) {

		parsemap["block"] = function( code, name )  {
			var blockname = regexOP.test(code) && _RegExp.$1,
				key = name + ":" + blockname

			if( DEBUG ) {
				console.log("Parsing <block>", key, code);
			}

			return {
				operator : KEY_BLOCK,
				_code : key
			}
		}

		parsemap["endblock"] = function( code, name, blockname ) {
			return {
				operator : OPERATOR_ECHO,
				_code: "''"
			}
		}

	}

	if(SUPPORT_OLD_BROWSERS && !Object.keys) {
		Object.keys = function( obj ){
			var keys = [];
			for(var k in obj) obj.hasOwnProperty(k) && keys.push(k);
			return keys;
		}
	}

	if(SUPPORT_FILTERS) {

		var _filters = {},
			isChrome = _window['chrome'],
			doc = _window.document;

		if( isChrome ) {
			var DOMtext 	= doc.createTextNode(""),
				DOMelement 	= doc.createElement("span");
			DOMelement.appendChild( DOMtext );
		}

		// http://jsperf.com/htmlencoderegex/40
		_filters["escapeHTML"] = function( code, name ) {
			return isChrome
				? code
					.replace('&', '&', "g")
					.replace('"', '"', "g")
					.replace("'", ''', "g")
					.replace('<', '<', "g")
					.replace('>', '>', "g")
				: (DOMtext.nodeValue = code) && DOMelement.innerHTML;
		}
	}

	// Precompiled regex
	// http://jsperf.com/regexp-test-search-vs-indexof/27
	var regexFOR	= /\(([\w\.]+),?\s*(\w+)?\)/,
		regexOP		= /(\w+)\s*$/,
		regexEXTEND	= /extends\s+(\w+)/,
		regexPARSE 	= new _RegExp("^\\s*(" + Object.keys(parsemap).join("|") + ")\\b");

	//
	//
	//
	function parse( templateString, name){

		var parsedLinesIndex;
		var parsedLines;
		var tokens =  templateString.split( CLOSE_TAG );

		var extended;
		var parentID;

		if( SUPPORT_EXTENDS && (parentID = regexEXTEND.test(tokens[0]) && _RegExp.$1)) {
			console.warn( _cacheParsing[parentID] );
			_cacheParsing[name] = _cacheParsing[parentID];
			parsedLines = _cacheParsing[name];
			parsedLinesIndex = parsedLines.len;
			tokens.shift();
			extended = 1;
			if( DEBUG ) {
				console.log("Extending: ", name, 'extends', parentID);
			}
		} else {
			parsedLinesIndex = 1;
			parsedLines = {
				0 : {
					operator: KEY_JS,
					_code : CODE_FIRST
				}
			}
		}

		// parse string tpl
		for(var tokenIndex = 0, tokensLength = tokens.length; tokenIndex < tokensLength; tokenIndex++){

			var line = tokens[tokenIndex].replace(/\\/g,'\\\\');
			var l = line.split( OPEN_TAG );

			if( l[0] !== ''){
				parsedLines[parsedLinesIndex++] = {
					operator: OPERATOR_ECHO,
					_code :'\''+l[0].replace(/'/g,'\\\'').replace(/\n/g,'\\n')+'\''
				};
			}

			var code = l[1];

			if( code ){

				var operator = regexPARSE.test(code) && _RegExp.$1;

				if( operator ) {

					code = parsemap[operator]( code, name );

					if( SUPPORT_EXTENDS && code && code.operator === KEY_BLOCK ) {

						var key = code._code;
						var tempLine = tokens[++tokenIndex];
						var partial = [tempLine];
						// console.warn( tempLine )

						if(tempLine.indexOf("endblock") < 0)
							while((tempLine = tokens[++tokenIndex]) !== undefined && tempLine.indexOf("endblock") < 0 ) {
								// console.warn( tempLine )
								partial.push(tempLine);
							}

						// partial.pop();
						// --tokenIndex;

						partial = partial.join( CLOSE_TAG ) // + CLOSE_TAG
						;

						var render = compileTemplateString(partial, key, true).replace(CODE_FIRST, "").replace(CODE_LAST,"");
						_cachePartials[key] = render;
						console.log(">>>>>>", partial, " >>>>> ", render, " >>>>> ", key );

						if( extended ){

							for(var j = -1, l2 = parsedLinesIndex; ++j < l2;) {
								if(parsedLines[j].operator === KEY_BLOCK){
									console.warn(parsedLines[j], _cacheParsing[parentID][j], parentID, name)
									var testkey = key.replace(parentID, name);
									if( testkey === key ) {
										parsedLines[j]  = {
											operator : KEY_BLOCK,
											_code :  key
										}
									}
								}
							}

						} else {
							parsedLines[parsedLinesIndex++] = {
								operator : KEY_BLOCK,
								_code :  key
							};
						}

					} else {
						parsedLines[parsedLinesIndex++] = code;
					}

				} else {

					var symbol = code.charAt(0);
					var operator;

					if(symbol ==' '){
						operator = KEY_JS;

					} else if(symbol=='!'){ // comments

						continue;

					} else if(symbol=='~'){ //OPERATOR_ECHO var escaped
						operator = "-";
						code = '('+code.substring(1)+'.replace(/&/g, "&").replace(/\'/g,"'").replace(/>/g, ">").replace(/</g, "<").replace(/"/g, """).replace(/\\//g,"/"))';
					} else {

						if(symbol === OPERATOR_ECHO ) {
							code = code.substring(1);
						}

						operator = OPERATOR_ECHO;
					}

					operator && (parsedLines[parsedLinesIndex++] = {
						operator : operator,
						_code : code
					});
				}
			}

		}

		if( SUPPORT_EXTENDS && !extended ) {
			parsedLines[parsedLinesIndex++] = {
				operator : KEY_JS,
				_code : CODE_LAST
			}
			parsedLines.len = parsedLinesIndex;
		}

		if( DEBUG ) {
			console.log("Parsed lines: ", parsedLines)
		}

		return (_cacheParsing[name] = parsedLines);
	}

	function compileTemplateString(str, name ){

		var parsedLines = parse(str, name);
		var parsedLinesIndex = parsedLines.len;
		var compiledLines = Array(parsedLinesIndex);
		var compiledLinesIndex = 0;
		var lastOperator,line, operator;

		for(var i=0,l=parsedLinesIndex;i<l;i++){

			line = parsedLines[i];
			operator = line.operator;

			if( lastOperator !== operator || operator === KEY_JS)
				compiledLines[compiledLinesIndex++] = ';';

			var v = line._code;

			if(operator === KEY_JS){

				compiledLines[compiledLinesIndex++] = v;

			} else if(operator === KEY_FOR){

				var iterator = v[2] || "_i",
					item = v[1] || "item";

				compiledLines[compiledLinesIndex++] = (
					'for(var ' + iterator + '=0,' + item + ',_l=' + v[0] + ',_c=_l.length;' +
						iterator + '<_c;' + iterator + '++){' +
							item + '=_l[' + iterator + ']'
				);

			} else if( SUPPORT_EXTENDS && operator === KEY_BLOCK ){

				compiledLines[compiledLinesIndex++] = _cachePartials[v];

			} else {
				if(lastOperator !== OPERATOR_ECHO){
					compiledLines[compiledLinesIndex++] = (OUTPUT_VAR + '+=') + v;
				} else {
					compiledLines[compiledLinesIndex++] = '+' + v
				}
			}
			lastOperator = operator;
		}

		return compiledLines.join('');
	}

	//
	//
	//
	function buildTemplate(templateString, templateID){

		templateID = templateID || ++tplHash;

		if( DEBUG ) {
			console.group( "tpl: " + templateID );
		}

		// if(templateID && _cacheCompiled[templateID])
		// 	return _cacheCompiled[templateID];

		if(_cacheCompiledByTpl[templateString])
			return _cacheCompiledByTpl[templateString];

		var compiled = compileTemplateString(templateString, templateID);

		if( DEBUG ) {
			console.log("Compiled function: ", compiled);
			console.groupEnd("tpl: " + templateID)
		}

		var fn = new Function('data', compiled);

		_cacheCompiledByTpl[templateString] = fn;

		if(templateID && !_cacheCompiled[templateID]){
			// _cacheCompiled[templateID] = fn;
			_cacheCompiled[templateID] = compiled;
		}

		return fn;
    }

    _window[WHACK_NAME] = buildTemplate;

}( this );
<commit_msg>filters added<commit_after>// http://jsperf.com/string-concat-vs-array-join-10000/8
// http://jsperf.com/advance-templates-benchmark/3
// http://jsperf.com/js-template-engines-performance/26

// COMPILATION FLAGS
// ------------------------------------------------------------------------------------------------
/** @define {boolean} */	var DEBUG = true;
/** @define {string} */		var WHACK_NAME = "Whack";

// SYNTAX OPTS
// ------------------------------------------------------------------------------------------------
/** @define {boolean} */	var USE_WITH = false;
/** @define {string} */		var OPEN_TAG = "{{";
/** @define {string} */		var CLOSE_TAG = "}}";
/** @define {string} */		var OPERATOR_ECHO = "=";
/** @define {string} */		var OPERATOR_ECHO_ESCAPE = "~";
/** @define {string} */		var OPERATOR_COMMENT = "!";
/** @define {string} */		var OUTPUT_VAR = "_o";

// SUPPORT OPTS
// ------------------------------------------------------------------------------------------------
/** @define {boolean} */	var SUPPORT_OLD_BROWSERS = false;
/** @define {boolean} */	var SUPPORT_SHORTCODES = true;
/** @define {boolean} */	var SUPPORT_EXTENDS = true;
/** @define {boolean} */	var SUPPORT_PARTIALS = true;
/** @define {boolean} */	var SUPPORT_FILTERS = true;

+function(_window){

	var tplHash = 0;

	// regexp.test is the fastest way
	// E.g. var x = /(\w+)/.test( string ) && RegExp.$1
	// http://jsperf.com/js-exec-vs-match/2
	var _RegExp = _window['RegExp'];

	// Operators codes
	var KEY_JS = 1,
		KEY_FOR = 2,
		KEY_BLOCK = 3,
		KEY_FILTER = 4;

	// Cache indexes
	var _cacheCompiled = {},
		_cacheCompiledByTpl = {},
		_cachePartials = {},
		_cacheParsing = {};

	// Code defaults
	var CODE_FIRST, CODE_LAST;

	// It is possible to use Whack in underscore's style
	// E.g.  {a: 2} => a
	if( USE_WITH ) {
		CODE_FIRST = "var "+OUTPUT_VAR+"='';with(data){",
		CODE_LAST  = "}return " + OUTPUT_VAR;
	} else {
		CODE_FIRST = "var "+OUTPUT_VAR+"='';",
		CODE_LAST  = "return " + OUTPUT_VAR;
	}

	// Code parse shortcuts
	var parsemap;

	if( SUPPORT_SHORTCODES ) {
		parsemap = {

			"end" : function(){return {operator: KEY_JS, _code: '}'}},

			"else" : function(){return {operator: KEY_JS, _code: '} else {'}},

			"if" : function(code){
				var p = code.lastIndexOf(':');
				var notTernearyExp = code.split(':').slice(-1)[0].trim()==='';
				if( p >-1 && notTernearyExp){
					code = code.substr(0, p) + '{';
				} else if( p === -1 ){
					if(code.split(')').slice(-1)[0].trim()==='') code+= '{';
				}
				return {
					operator : KEY_JS,
					_code : code
				};
			},
			"each" : function(code){
				var vals = regexFOR.test(code) && _RegExp;
				if( DEBUG ) {
					console.log("Parsing <for>: ", code, vals);
				}
				return {
					operator : KEY_FOR,
					_code : [vals.$1, vals.$2]
				}
			}
		}
	} else {
		parsemap = {};
	}

	if( SUPPORT_PARTIALS ) {
		parsemap["include"] = function(code) {
			var r = regexOP.test(code) && _RegExp.$1;
			return {
				operator : KEY_JS,
				_code : _cachePartials[r] || (_cachePartials[r] = _cacheCompiled[r].replace(CODE_FIRST, "").replace(CODE_LAST, ""))
			}
		}
	}

	if( SUPPORT_EXTENDS ) {

		parsemap["block"] = function( code, name )  {
			var blockname = regexOP.test(code) && _RegExp.$1,
				key = name + ":" + blockname

			if( DEBUG ) {
				console.log("Parsing <block>", key, code);
			}

			return {
				operator : KEY_BLOCK,
				_code : key
			}
		}

		parsemap["endblock"] = function( code, name, blockname ) {
			return {
				operator : OPERATOR_ECHO,
				_code: "''"
			}
		}

	}

	if(SUPPORT_OLD_BROWSERS && !Object.keys) {
		Object.keys = function( obj ){
			var keys = [];
			for(var k in obj) obj.hasOwnProperty(k) && keys.push(k);
			return keys;
		}
	}

	if(SUPPORT_FILTERS) {

		var _filters = {},
			isChrome = _window['chrome'],
			doc = _window.document;

		if( isChrome ) {
			var DOMtext 	= doc.createTextNode(""),
				DOMelement 	= doc.createElement("span");
			DOMelement.appendChild( DOMtext );
		}

		var
			amp = /&/g,
			q1 	= /"/g,
			q2	= /'/g,
			t1	= />/g,
			t2	= /</g,
			sl 	= /\//g;

		_filters["escapeHTML"] = function( code, name ) {
			return isChrome
				? code
					.replace(amp, '&')
					.replace(t2, '<')
					.replace(t1, '>')
					.replace(q1, '"')
					.replace(q2, ''')
					.replace(sl,'/')
				: (DOMtext.nodeValue = code) && DOMelement.innerHTML;
		}

		_filters["trim"] = function( code ) {
			return code.replace(/^\s+|\s+$/g, '');
		}

		_filters["abs"] = function( code ){
			return Math.abs(Number(code));
		}

		_filters["capitalize"] = function( code ){
			return code.charAt(0).toUpperCase()+code.substr(1);
		}

	}

	// Precompiled regex
	// http://jsperf.com/regexp-test-search-vs-indexof/27
	var regexFOR	= /\(([\w\.]+),?\s*(\w+)?\)/,
		regexOP		= /(\w+)\s*$/,
		regexEXTEND	= /extends\s+(\w+)/,
		regexPARSE 	= new _RegExp("^\\s*(" + Object.keys(parsemap).join("|") + ")\\b");

	//
	//
	//
	function parse( templateString, name){

		var parsedLinesIndex;
		var parsedLines;
		var tokens =  templateString.split( CLOSE_TAG );

		var extended;
		var parentID;

		if( SUPPORT_EXTENDS && (parentID = regexEXTEND.test(tokens[0]) && _RegExp.$1)) {
			console.warn( _cacheParsing[parentID] );
			_cacheParsing[name] = _cacheParsing[parentID];
			parsedLines = _cacheParsing[name];
			parsedLinesIndex = parsedLines.len;
			tokens.shift();
			extended = 1;
			if( DEBUG ) {
				console.log("Extending: ", name, 'extends', parentID);
			}
		} else {
			parsedLinesIndex = 1;
			parsedLines = {
				0 : {
					operator: KEY_JS,
					_code : CODE_FIRST
				}
			}
		}

		// parse string tpl
		for(var tokenIndex = 0, tokensLength = tokens.length; tokenIndex < tokensLength; tokenIndex++){

			var line = tokens[tokenIndex].replace(/\\/g,'\\\\');
			var l = line.split( OPEN_TAG );

			if( l[0] !== ''){
				parsedLines[parsedLinesIndex++] = {
					operator: OPERATOR_ECHO,
					_code :'\''+l[0].replace(/'/g,'\\\'').replace(/\n/g,'\\n')+'\''
				};
			}

			var code = l[1];

			if( code ){

				var operator = regexPARSE.test(code) && _RegExp.$1;

				if( operator ) {

					code = parsemap[operator]( code, name );

					if( SUPPORT_EXTENDS && code && code.operator === KEY_BLOCK ) {

						var key = code._code;
						var tempLine = tokens[++tokenIndex];
						var partial = [tempLine];
						// console.warn( tempLine )

						if(tempLine.indexOf("endblock") < 0)
							while((tempLine = tokens[++tokenIndex]) !== undefined && tempLine.indexOf("endblock") < 0 ) {
								// console.warn( tempLine )
								partial.push(tempLine);
							}

						// partial.pop();
						// --tokenIndex;

						partial = partial.join( CLOSE_TAG ) // + CLOSE_TAG
						;

						var render = compileTemplateString(partial, key, true).replace(CODE_FIRST, "").replace(CODE_LAST,"");
						_cachePartials[key] = render;
						console.log(">>>>>>", partial, " >>>>> ", render, " >>>>> ", key );

						if( extended ){

							for(var j = -1, l2 = parsedLinesIndex; ++j < l2;) {
								if(parsedLines[j].operator === KEY_BLOCK){
									console.warn(parsedLines[j], _cacheParsing[parentID][j], parentID, name)
									var testkey = key.replace(parentID, name);
									if( testkey === key ) {
										parsedLines[j]  = {
											operator : KEY_BLOCK,
											_code :  key
										}
									}
								}
							}

						} else {
							parsedLines[parsedLinesIndex++] = {
								operator : KEY_BLOCK,
								_code :  key
							};
						}

					} else {
						parsedLines[parsedLinesIndex++] = code;
					}

				} else {

					var symbol = code.charAt(0);
					var operator;

					if(symbol ==' '){
						operator = KEY_JS;

					} else if(symbol=='!'){ // comments

						continue;

					} else if(symbol=='~'){ //OPERATOR_ECHO var escaped
						operator = "-";
						code = '('+code.substring(1)+'.replace(/&/g, "&").replace(/\'/g,"'").replace(/>/g, ">").replace(/</g, "<").replace(/"/g, """).replace(/\\//g,"/"))';
					} else {

						if(symbol === OPERATOR_ECHO ) {
							code = code.substring(1);
						}

						if( SUPPORT_FILTERS && /\|\s(\w+)/.test(code)){
							var f = _RegExp.$1;
							code = code.replace("| " + f, "");
							code = WHACK_NAME + ".f." + f + "(" + code + ")";

						}

						operator = OPERATOR_ECHO;
					}

					operator && (parsedLines[parsedLinesIndex++] = {
						operator : operator,
						_code : code
					});
				}
			}

		}

		if( SUPPORT_EXTENDS && !extended ) {
			parsedLines[parsedLinesIndex++] = {
				operator : KEY_JS,
				_code : CODE_LAST
			}
			parsedLines.len = parsedLinesIndex;
		}

		if( DEBUG ) {
			console.log("Parsed lines: ", parsedLines)
		}

		return (_cacheParsing[name] = parsedLines);
	}

	function compileTemplateString(str, name ){

		var parsedLines = parse(str, name);
		var parsedLinesIndex = parsedLines.len;
		var compiledLines = Array(parsedLinesIndex);
		var compiledLinesIndex = 0;
		var lastOperator,line, operator;

		for(var i=0,l=parsedLinesIndex;i<l;i++){

			line = parsedLines[i];
			operator = line.operator;

			if( lastOperator !== operator || operator === KEY_JS)
				compiledLines[compiledLinesIndex++] = ';';

			var v = line._code;

			if(operator === KEY_JS){

				compiledLines[compiledLinesIndex++] = v;

			} else if(operator === KEY_FOR){

				var iterator = v[2] || "_i",
					item = v[1] || "item";

				compiledLines[compiledLinesIndex++] = (
					'for(var ' + iterator + '=0,' + item + ',_l=' + v[0] + ',_c=_l.length;' +
						iterator + '<_c;' + iterator + '++){' +
							item + '=_l[' + iterator + ']'
				);

			} else if( SUPPORT_EXTENDS && operator === KEY_BLOCK ){

				compiledLines[compiledLinesIndex++] = _cachePartials[v];

			} else {
				if(lastOperator !== OPERATOR_ECHO){
					compiledLines[compiledLinesIndex++] = (OUTPUT_VAR + '+=') + v;
				} else {
					compiledLines[compiledLinesIndex++] = '+' + v
				}
			}
			lastOperator = operator;
		}

		return compiledLines.join('');
	}

	//
	//
	//
	function buildTemplate(templateString, templateID){

		templateID = templateID || ++tplHash;

		if( DEBUG ) {
			console.group( "tpl: " + templateID );
		}

		// if(templateID && _cacheCompiled[templateID])
		// 	return _cacheCompiled[templateID];

		if(_cacheCompiledByTpl[templateString])
			return _cacheCompiledByTpl[templateString];

		var compiled = compileTemplateString(templateString, templateID);

		if( DEBUG ) {
			console.log("Compiled function: ", compiled);
			console.groupEnd("tpl: " + templateID)
		}

		var fn = new Function('data', compiled);

		_cacheCompiledByTpl[templateString] = fn;

		if(templateID && !_cacheCompiled[templateID]){
			// _cacheCompiled[templateID] = fn;
			_cacheCompiled[templateID] = compiled;
		}

		return fn;
    }

    if(SUPPORT_FILTERS) {
    	buildTemplate['f'] = _filters;
    }

    _window[WHACK_NAME] = buildTemplate;

}( this );
<|endoftext|>


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

var path = require('path');
var fs = require('fs');
/* eslint no-sync:0 */
var usage = fs.readFileSync(path.resolve(__dirname, '../usage.txt')).toString();
var args = require('minimist')(process.argv.slice(2), {
  boolean: ['debug']
});

if (args.debug) {
  process.env.DEBUG = 'mongodb-runner';
}
var run = require('../');
var pkg = require('../package.json');
var clui = require('clui');
var debug = require('debug')('mongodb-runner:bin');

args.action = args.action || args._[0] || 'start';

if (args.help || args.h) {
  console.error(usage);
  process.exit(1);
}
if (args.version) {
  console.error(pkg.version);
  process.exit(1);
}

debug('running action `%s`', args.action);

if (args.action === 'start') {
  new clui.Spinner('Starting a MongoDB deployment to test against...').start();
}
run(args, function(err) {
  if (err) {
    console.error(err);
    process.exit(1);
    return;
  }
  debug('ran action `%s` successfully', args.action);
  process.exit(0);
});
<commit_msg>:zap: Don't start spinner in CI or we make peoples logs stupidly long<commit_after>#!/usr/bin/env node

var path = require('path');
var fs = require('fs');
/* eslint no-sync:0 */
var usage = fs.readFileSync(path.resolve(__dirname, '../usage.txt')).toString();
var args = require('minimist')(process.argv.slice(2), {
  boolean: ['debug']
});

if (args.debug) {
  process.env.DEBUG = 'mongodb-runner';
}
var run = require('../');
var pkg = require('../package.json');
var clui = require('clui');
var debug = require('debug')('mongodb-runner:bin');

args.action = args.action || args._[0] || 'start';

if (args.help || args.h) {
  console.error(usage);
  process.exit(1);
}
if (args.version) {
  console.error(pkg.version);
  process.exit(1);
}

debug('running action `%s`', args.action);

if (args.action === 'start' && !process.env.CI){
  new clui.Spinner('Starting a MongoDB deployment to test against...').start();
}
run(args, function(err) {
  if (err) {
    console.error(err);
    process.exit(1);
    return;
  }
  debug('ran action `%s` successfully', args.action);
  process.exit(0);
});
<|endoftext|>


<commit_before>const { relative, resolve, dirname } = require('path')

function isUrl (url) {
    return url && /^(https?|webpack(-[^:]+)?):\/\//.test(url)
}

function isDataUrl (url) {
    return url && /^(data:.*;.*,)/.test(url)
}

/**
 * rebase url
 * @param  {String} url     asset's url (asset is in current file)
 * @param  {String} base    current file's base
 * @param  {String} path    current file's path
 * @param  {String} destDir new file's dest dir
 * @return {String}         rebased url
 */
function rebase (url, { base, path, destDir } = {}) {
    if (isUrl(url) || isDataUrl(url) || url[0] === '/' || url[0] === '#') {
        return url
    }

    let resourceAbsUrl = relative(base, resolve(dirname(path), url))
    resourceAbsUrl = relative(destDir, resourceAbsUrl)
    return resourceAbsUrl
}

module.exports = rebase
<commit_msg>ensure rebased url is of unix style, fix #5<commit_after>const { relative, resolve, dirname, sep } = require('path')

function isUrl (url) {
    return url && /^(https?|webpack(-[^:]+)?):\/\//.test(url)
}

function isDataUrl (url) {
    return url && /^(data:.*;.*,)/.test(url)
}

/**
 * rebase url
 * @param  {String} url     asset's url (asset is in current file)
 * @param  {String} base    current file's base
 * @param  {String} path    current file's path
 * @param  {String} destDir new file's dest dir
 * @return {String}         rebased url
 */
function rebase (url, { base, path, destDir } = {}) {
    if (isUrl(url) || isDataUrl(url) || url[0] === '/' || url[0] === '#') {
        return url
    }

    let resourceAbsUrl = relative(base, resolve(dirname(path), url))
    resourceAbsUrl = relative(destDir, resourceAbsUrl)
    // convert to unix style path
    return resourceAbsUrl.split(sep).join('/')
}

module.exports = rebase
<|endoftext|>


<commit_before>var fields = refilm`
文件
`;

function main() {
    var page = div();
    page.innerHTML = root;
    page.onback = function () {
        var $scope = this.$scope;
        if (!$scope.pathlist.length) {
            return;
        }
        $scope.pathlist.pop();
        $scope.open();
        return false;
    }
    page.setAttribute('ng-mousedown', 'setActive')

    renderWithDefaults(page, {
        lattice,
        pathlist: [],
        active: null,
        open(p) {
            if (p) p = String(p.name || '').replace(/\/$/, '');
            if (p) this.pathlist.push(p);
            this.data = data.from("folder", { opt: 'list', path: "/" + this.pathlist.join('/') }, files => {
                if (files) return files.map(f => {
                    return {
                        name: f,
                        type: /\/$/.test(f) ? 'folder' : 'file'
                    }
                });
            });
        },
        setActive(e) {
            this.active = getActive(e);
        },
        data: [],
    });
    page.$scope.open();
    var getActive = e => {
        var p = page.querySelector('lattice');
        var t = getTargetIn(e => e.parentNode && e.parentNode.parentNode === p, e.target);
        return t;
    };
    var when = e => !!getActive(e);
    var popupEdit = function (e) {
        zimoli.prepare('/wow/edit', function () {
            var p = popup("#/wow/edit", {
                path: page.$scope.pathlist,
                name: e || ''
            });
            on('submited')(p, function () {
                page.$scope.open();
                remove(p);
            });
        })

    };
    contextmenu(page, [
        {
            name: "新建文件夹",
            when: e => !getActive(e),
            do() {
                popupEdit();
            }
        },
        {
            name: '编辑',
            when,
            do(e) {
                popupEdit(e.$scope.d.name);
            }
        },
        {
            get name() {
                return this.confirm ? "确认删除" : "删除";
            },
            confirm: false,
            when(e) {
                this.confirm = false;
                return when(e);
            },
            type: "danger",
            async do(e) {
                if (!this.confirm) {
                    this.confirm = true;
                    setTimeout(_ => {
                        this.confirm = false;
                        render.refresh();
                    }, 2000);
                    return false;
                }
                await data.from("folder", { opt: 'del', path: "/" + page.$scope.pathlist.concat(e.$scope.d.name).join("/") }).loading_promise;
                page.$scope.open();
            }
        }
    ]);
    return page;
}<commit_msg>文件路径按名称排序<commit_after>var fields = refilm`
文件
`;

function main() {
    var page = div();
    page.innerHTML = root;
    page.onback = function () {
        var $scope = this.$scope;
        if (!$scope.pathlist.length) {
            return;
        }
        $scope.pathlist.pop();
        $scope.open();
        return false;
    }
    page.setAttribute('ng-mousedown', 'setActive')

    renderWithDefaults(page, {
        lattice,
        pathlist: [],
        active: null,
        open(p) {
            if (p) p = String(p.name || '').replace(/\/$/, '');
            if (p) this.pathlist.push(p);
            this.data = data.from("folder", { opt: 'list', path: "/" + this.pathlist.join('/') }, files => {
                if (files) return sortname(files).map(f => {
                    return {
                        name: f,
                        type: /\/$/.test(f) ? 'folder' : 'file'
                    }
                });
            });
        },
        setActive(e) {
            this.active = getActive(e);
        },
        data: [],
    });
    page.$scope.open();
    var getActive = e => {
        var p = page.querySelector('lattice');
        var t = getTargetIn(e => e.parentNode && e.parentNode.parentNode === p, e.target);
        return t;
    };
    var when = e => !!getActive(e);
    var popupEdit = function (e) {
        zimoli.prepare('/wow/edit', function () {
            var p = popup("#/wow/edit", {
                path: page.$scope.pathlist,
                name: e || ''
            });
            on('submited')(p, function () {
                page.$scope.open();
                remove(p);
            });
        })

    };
    contextmenu(page, [
        {
            name: "新建文件夹",
            when: e => !getActive(e),
            do() {
                popupEdit();
            }
        },
        {
            name: '编辑',
            when,
            do(e) {
                popupEdit(e.$scope.d.name);
            }
        },
        {
            get name() {
                return this.confirm ? "确认删除" : "删除";
            },
            confirm: false,
            when(e) {
                this.confirm = false;
                return when(e);
            },
            type: "danger",
            async do(e) {
                if (!this.confirm) {
                    this.confirm = true;
                    setTimeout(_ => {
                        this.confirm = false;
                        render.refresh();
                    }, 2000);
                    return false;
                }
                await data.from("folder", { opt: 'del', path: "/" + page.$scope.pathlist.concat(e.$scope.d.name).join("/") }).loading_promise;
                page.$scope.open();
            }
        }
    ]);
    return page;
}<|endoftext|>


<commit_before>/**
 * @fileoverview CommonJS-compatible mustache.js module.
 *
 * This version of mustache.js adds filters. If a tag or section name consists
 * of several space-separated items, the items are evaluated one at a time,
 * starting with the right-most item. If an item evaluates to a function, the
 * result of the previous item is passed to it as argument.
 *
 * @example
 * var template = 'Hello {{upper world}}!';
 * var data = {
 *     upper: function(str) { return str.toUpperCase() },
 *     world: 'world'
 * };
 * mustache.to_html(template, data);
 * // -> 'Hello WORLD!'
 *
 * @see http://github.com/janl/mustache.js
 * @see http://github.com/hns/mustache.js
 */

/*
  mustache.js — Logic-less templates in JavaScript

  See http://mustache.github.com/ for more info.
*/

var Mustache = function() {
  var Renderer = function() {};

  Renderer.prototype = {
    otag: "{{",
    ctag: "}}",
    pragmas: {},
    buffer: [],
    pragmas_implemented: {
      "IMPLICIT-ITERATOR": true
    },
    context: {},

    render: function(template, context, partials, in_recursion) {
      // reset buffer & set context
      if(!in_recursion) {
        this.context = context;
        this.buffer = []; // TODO: make this non-lazy
      }

      // fail fast
      if(!this.includes("", template)) {
        if(in_recursion) {
          return template;
        } else {
          this.send(template);
          return;
        }
      }

      template = this.render_pragmas(template);
      var html = this.render_section(template, context, partials);
      if(in_recursion) {
        return this.render_tags(html, context, partials, in_recursion);
      }

      this.render_tags(html, context, partials, in_recursion);
    },

    /*
      Sends parsed lines
    */
    send: function(line) {
      if(line != "") {
        this.buffer.push(line);
      }
    },

    /*
      Looks for %PRAGMAS
    */
    render_pragmas: function(template) {
      // no pragmas
      if(!this.includes("%", template)) {
        return template;
      }

      var that = this;
      var regex = new RegExp(this.otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" +
            this.ctag);
      return template.replace(regex, function(match, pragma, options) {
        if(!that.pragmas_implemented[pragma]) {
          throw({message:
            "This implementation of mustache doesn't understand the '" +
            pragma + "' pragma"});
        }
        that.pragmas[pragma] = {};
        if(options) {
          var opts = options.split("=");
          that.pragmas[pragma][opts[0]] = opts[1];
        }
        return "";
        // ignore unknown pragmas silently
      });
    },

    /*
      Tries to find a partial in the curent scope and render it
    */
    render_partial: function(name, context, partials) {
      name = this.trim(name);
      if(!partials || partials[name] === undefined) {
        throw({message: "unknown_partial '" + name + "'"});
      }
      if(typeof(context[name]) != "object") {
        return this.render(partials[name], context, partials, true);
      }
      return this.render(partials[name], context[name], partials, true);
    },

    /*
      Renders inverted (^) and normal (#) sections
    */
    render_section: function(template, context, partials) {
      if(!this.includes("#", template) && !this.includes("^", template)) {
        return template;
      }

      var that = this;
      // CSW - Added "+?" so it finds the tighest bound, not the widest
      var regex = new RegExp(this.otag + "(\\^|\\#)\\s*(.+)\\s*" + this.ctag +
              "\n*([\\s\\S]+?)" + this.otag + "\\/\\s*\\2\\s*" + this.ctag +
              "\\s*", "mg");

      // for each {{#foo}}{{/foo}} section do...
      return template.replace(regex, function(match, type, name, content) {
        var names = name.split(/\s+/);
        var value = context;
        // loop through names in reverse order: {{foo bar}} -> foo(bar)
        for (var i = names.length - 1; i >= 0; i--) {
          value = that.find(names[i], context, value);
        }
        if(type == "^") { // inverted section
          if(!value || that.is_array(value) && value.length === 0) {
            // false or empty list, render it
            return that.render(content, context, partials, true);
          } else {
            return "";
          }
        } else if(type == "#") { // normal section
          if(that.is_array(value)) { // Enumerable, Let's loop!
            return that.map(value, function(row) {
              return that.render(content, that.create_context(row),
                partials, true);
            }).join("");
          } else if(that.is_object(value)) { // Object, Use it as subcontext!
            return that.render(content, that.create_context(value),
              partials, true);
          } else if(typeof value === "function") {
            // higher order section
            return value.call(context, content, function(text) {
              return that.render(text, context, partials, true);
            });
          } else if(value) { // boolean section
            return that.render(content, context, partials, true);
          } else {
            return "";
          }
        }
      });
    },

    /*
      Replace {{foo}} and friends with values from our view
    */
    render_tags: function(template, context, partials, in_recursion) {
      // tit for tat
      var that = this;

      var new_regex = function() {
        return new RegExp(that.otag + "(=|!|>|\\{|%)?([^\\/#\\^]+?)\\1?" +
          that.ctag + "+", "g");
      };

      var regex = new_regex();
      var tag_replace_callback = function(match, operator, name) {
        switch(operator) {
        case "!": // ignore comments
          return "";
        case "=": // set new delimiters, rebuild the replace regexp
          that.set_delimiters(name);
          regex = new_regex();
          return "";
        case ">": // render partial
          return that.render_partial(name, context, partials);
        default:
          var names = name.split(/\s+/);
          var value = context;
          // loop through names in reverse order: {{foo bar}} -> foo(bar)
          for (var i = names.length - 1; i >= 0; i--) {
              value = that.find(names[i], context, value);
          }
          // escape value unless triple mustache
          return operator === "{" ? value : that.escape(value);
        }
      };
      var lines = template.split("\n");
      for(var i = 0; i < lines.length; i++) {
        lines[i] = lines[i].replace(regex, tag_replace_callback, this);
        if(!in_recursion) {
          this.send(lines[i]);
        }
      }

      if(in_recursion) {
        return lines.join("\n");
      }
    },

    set_delimiters: function(delimiters) {
      var dels = delimiters.split(" ");
      this.otag = this.escape_regex(dels[0]);
      this.ctag = this.escape_regex(dels[1]);
    },

    escape_regex: function(text) {
      // thank you Simon Willison
      if(!arguments.callee.sRE) {
        var specials = [
          '/', '.', '*', '+', '?', '|',
          '(', ')', '[', ']', '{', '}', '\\'
        ];
        arguments.callee.sRE = new RegExp(
          '(\\' + specials.join('|\\') + ')', 'g'
        );
      }
      return text.replace(arguments.callee.sRE, '\\$1');
    },

    /*
      find `name` in current `context`. That is find me a value
      from the view object
    */
    find: function(name, context, previousValue) {
      name = this.trim(name);

      // Checks whether a value is thruthy or false or 0
      function is_kinda_truthy(bool) {
        return bool === false || bool === 0 || bool;
      }

      var value;
      if(is_kinda_truthy(context[name])) {
        value = context[name];
      } else if(is_kinda_truthy(this.context[name])) {
        value = this.context[name];
      }

      if(typeof value === "function") {
        return value.call(context, previousValue);
      }
      if(value !== undefined) {
        return value;
      }
      // silently ignore unkown variables
      return "";
    },

    // Utility methods

    /* includes tag */
    includes: function(needle, haystack) {
      return haystack.indexOf(this.otag + needle) != -1;
    },

    /*
      Does away with nasty characters
    */
    escape: function(s) {
      s = String(s === null ? "" : s);
      return s.replace(/&(?!\w+;)|["'<>\\]/g, function(s) {
        switch(s) {
        case "&": return "&";
        case "\\": return "\\\\";
        case '"': return '"';
        case "'": return ''';
        case "<": return "<";
        case ">": return ">";
        default: return s;
        }
      });
    },

    // by @langalex, support for arrays of strings
    create_context: function(_context) {
      if(this.is_object(_context)) {
        return _context;
      } else {
        var iterator = ".";
        if(this.pragmas["IMPLICIT-ITERATOR"]) {
          iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator;
        }
        var ctx = {};
        ctx[iterator] = _context;
        return ctx;
      }
    },

    is_object: function(a) {
      return a && typeof a == "object";
    },

    is_array: function(a) {
      return Object.prototype.toString.call(a) === '[object Array]';
    },

    /*
      Gets rid of leading and trailing whitespace
    */
    trim: function(s) {
      return s.replace(/^\s*|\s*$/g, "");
    },

    /*
      Why, why, why? Because IE. Cry, cry cry.
    */
    map: function(array, fn) {
      if (typeof array.map == "function") {
        return array.map(fn);
      } else {
        var r = [];
        var l = array.length;
        for(var i = 0; i < l; i++) {
          r.push(fn(array[i]));
        }
        return r;
      }
    }
  };

  return({
    name: "mustache.js",
    version: "0.3.1-dev",

    /*
      Turns a template and view into HTML
    */
    to_html: function(template, view, partials, send_fun) {
      var renderer = new Renderer();
      if(send_fun) {
        renderer.send = send_fun;
      }
      renderer.render(template, view, partials);
      if(!send_fun) {
        return renderer.buffer.join("\n");
      }
    }
  });
}();

/**
 * The name of this module.
 */
exports.name = Mustache.name;
/**
 * The version of this module.
 */
exports.version = Mustache.version;

/**
 * Renders `template` using `data` as context object.
 * @param {String} template the template.
 * @param {Object} data the data object.
 * @returns {String} the formatted text.
 */
exports.to_html = function() {
  return Mustache.to_html.apply(this, arguments);
};
<commit_msg>Update ringo/mustache to latest https://github.com/hns/mustache.js<commit_after>/**
 * @fileoverview CommonJS-compatible mustache.js module.
 *
 * This version of mustache.js adds filters. If a tag or section name consists
 * of several space-separated items, the items are evaluated one at a time,
 * starting with the right-most item. If an item evaluates to a function, the
 * result of the previous item is passed to it as argument.
 *
 * @example
 * var template = 'Hello {{upper world}}!';
 * var data = {
 *     upper: function(str) { return str.toUpperCase() },
 *     world: 'world'
 * };
 * mustache.to_html(template, data);
 * // -> 'Hello WORLD!'
 *
 * @see http://github.com/janl/mustache.js
 * @see http://github.com/hns/mustache.js
 */

/*
  mustache.js — Logic-less templates in JavaScript

  See http://mustache.github.com/ for more info.
*/

var Mustache = function() {
  var Renderer = function() {};

  Renderer.prototype = {
    otag: "{{",
    ctag: "}}",
    pragmas: {},
    buffer: [],
    pragmas_implemented: {
      "IMPLICIT-ITERATOR": true
    },
    context: {},

    render: function(template, context, partials, in_recursion) {
      // reset buffer & set context
      if(!in_recursion) {
        this.context = context;
        this.buffer = []; // TODO: make this non-lazy
      }

      // fail fast
      if(!this.includes("", template)) {
        if(in_recursion) {
          return template;
        } else {
          this.send(template);
          return;
        }
      }

      template = this.render_pragmas(template);
      var html = this.render_section(template, context, partials);
      if(in_recursion) {
        return this.render_tags(html, context, partials, in_recursion);
      }

      this.render_tags(html, context, partials, in_recursion);
    },

    /*
      Sends parsed lines
    */
    send: function(line) {
      if(line != "") {
        this.buffer.push(line);
      }
    },

    /*
      Looks for %PRAGMAS
    */
    render_pragmas: function(template) {
      // no pragmas
      if(!this.includes("%", template)) {
        return template;
      }

      var that = this;
      var regex = new RegExp(this.otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" +
            this.ctag);
      return template.replace(regex, function(match, pragma, options) {
        if(!that.pragmas_implemented[pragma]) {
          throw({message:
            "This implementation of mustache doesn't understand the '" +
            pragma + "' pragma"});
        }
        that.pragmas[pragma] = {};
        if(options) {
          var opts = options.split("=");
          that.pragmas[pragma][opts[0]] = opts[1];
        }
        return "";
        // ignore unknown pragmas silently
      });
    },

    /*
      Tries to find a partial in the curent scope and render it
    */
    render_partial: function(name, context, partials) {
      name = this.trim(name);
      if(!partials || partials[name] === undefined) {
        throw({message: "unknown_partial '" + name + "'"});
      }
      if(typeof(context[name]) != "object") {
        return this.render(partials[name], context, partials, true);
      }
      return this.render(partials[name], context[name], partials, true);
    },

    /*
      Renders inverted (^) and normal (#) sections
    */
    render_section: function(template, context, partials) {
      if(!this.includes("#", template) && !this.includes("^", template)) {
        return template;
      }

      var that = this;
      // CSW - Added "+?" so it finds the tighest bound, not the widest
      var regex = new RegExp(this.otag + "(\\^|\\#)\\s*(.+)\\s*" + this.ctag +
              "\n*([\\s\\S]+?)" + this.otag + "\\/\\s*\\2\\s*" + this.ctag +
              "\\s*", "mg");

      // for each {{#foo}}{{/foo}} section do...
      return template.replace(regex, function(match, type, name, content) {
        var names = name.split(/\s+/);
        var value;
        // loop through names in reverse order: {{foo bar}} -> foo(bar)
        for (var i = names.length - 1; i >= 0; i--) {
          value = that.find(names[i], context, value);
        }
        if(type == "^") { // inverted section
          if(!value || that.is_array(value) && value.length === 0) {
            // false or empty list, render it
            return that.render(content, context, partials, true);
          } else {
            return "";
          }
        } else if(type == "#") { // normal section
          if(that.is_array(value)) { // Enumerable, Let's loop!
            return that.map(value, function(row) {
              return that.render(content, that.create_context(row),
                partials, true);
            }).join("");
          } else if(that.is_object(value)) { // Object, Use it as subcontext!
            return that.render(content, that.create_context(value),
              partials, true);
          } else if(typeof value === "function") {
            // higher order section
            return value.call(context, content, function(text) {
              return that.render(text, context, partials, true);
            });
          } else if(value) { // boolean section
            return that.render(content, context, partials, true);
          } else {
            return "";
          }
        }
      });
    },

    /*
      Replace {{foo}} and friends with values from our view
    */
    render_tags: function(template, context, partials, in_recursion) {
      // tit for tat
      var that = this;

      var new_regex = function() {
        return new RegExp(that.otag + "(=|!|>|\\{|%)?([^\\/#\\^]+?)\\1?" +
          that.ctag + "+", "g");
      };

      var regex = new_regex();
      var tag_replace_callback = function(match, operator, name) {
        switch(operator) {
        case "!": // ignore comments
          return "";
        case "=": // set new delimiters, rebuild the replace regexp
          that.set_delimiters(name);
          regex = new_regex();
          return "";
        case ">": // render partial
          return that.render_partial(name, context, partials);
        default:
          var names = name.split(/\s+/);
          var value;
          // loop through names in reverse order: {{foo bar}} -> foo(bar)
          for (var i = names.length - 1; i >= 0; i--) {
              value = that.find(names[i], context, value);
          }
          // escape value unless triple mustache
          return operator === "{" ? value : that.escape(value);
        }
      };
      var lines = template.split("\n");
      for(var i = 0; i < lines.length; i++) {
        lines[i] = lines[i].replace(regex, tag_replace_callback, this);
        if(!in_recursion) {
          this.send(lines[i]);
        }
      }

      if(in_recursion) {
        return lines.join("\n");
      }
    },

    set_delimiters: function(delimiters) {
      var dels = delimiters.split(" ");
      this.otag = this.escape_regex(dels[0]);
      this.ctag = this.escape_regex(dels[1]);
    },

    escape_regex: function(text) {
      // thank you Simon Willison
      if(!arguments.callee.sRE) {
        var specials = [
          '/', '.', '*', '+', '?', '|',
          '(', ')', '[', ']', '{', '}', '\\'
        ];
        arguments.callee.sRE = new RegExp(
          '(\\' + specials.join('|\\') + ')', 'g'
        );
      }
      return text.replace(arguments.callee.sRE, '\\$1');
    },

    /*
      find `name` in current `context`. That is find me a value
      from the view object
    */
    find: function(name, context, previousValue) {
      name = this.trim(name);

      // Checks whether a value is thruthy or false or 0
      function is_kinda_truthy(bool) {
        return bool === false || bool === 0 || bool;
      }

      var value;
      if(is_kinda_truthy(context[name])) {
        value = context[name];
      } else if(is_kinda_truthy(this.context[name])) {
        value = this.context[name];
      }

      if(typeof value === "function") {
        return previousValue === undefined ?
            value.call(context) : value.call(context, previousValue);
      }
      if(value !== undefined) {
        return value;
      }
      // silently ignore unkown variables
      return "";
    },

    // Utility methods

    /* includes tag */
    includes: function(needle, haystack) {
      return haystack.indexOf(this.otag + needle) != -1;
    },

    /*
      Does away with nasty characters
    */
    escape: function(s) {
      s = String(s === null ? "" : s);
      return s.replace(/&(?!\w+;)|["'<>\\]/g, function(s) {
        switch(s) {
        case "&": return "&";
        case "\\": return "\\\\";
        case '"': return '"';
        case "'": return ''';
        case "<": return "<";
        case ">": return ">";
        default: return s;
        }
      });
    },

    // by @langalex, support for arrays of strings
    create_context: function(_context) {
      if(this.is_object(_context)) {
        return _context;
      } else {
        var iterator = ".";
        if(this.pragmas["IMPLICIT-ITERATOR"]) {
          iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator;
        }
        var ctx = {};
        ctx[iterator] = _context;
        return ctx;
      }
    },

    is_object: function(a) {
      return a && typeof a == "object";
    },

    is_array: function(a) {
      return Object.prototype.toString.call(a) === '[object Array]';
    },

    /*
      Gets rid of leading and trailing whitespace
    */
    trim: function(s) {
      return s.replace(/^\s*|\s*$/g, "");
    },

    /*
      Why, why, why? Because IE. Cry, cry cry.
    */
    map: function(array, fn) {
      if (typeof array.map == "function") {
        return array.map(fn);
      } else {
        var r = [];
        var l = array.length;
        for(var i = 0; i < l; i++) {
          r.push(fn(array[i]));
        }
        return r;
      }
    }
  };

  return({
    name: "mustache.js",
    version: "0.3.1-dev",

    /*
      Turns a template and view into HTML
    */
    to_html: function(template, view, partials, send_fun) {
      var renderer = new Renderer();
      if(send_fun) {
        renderer.send = send_fun;
      }
      renderer.render(template, view, partials);
      if(!send_fun) {
        return renderer.buffer.join("\n");
      }
    }
  });
}();

/**
 * The name of this module.
 */
exports.name = Mustache.name;
/**
 * The version of this module.
 */
exports.version = Mustache.version;

/**
 * Renders `template` using `data` as context object.
 * @param {String} template the template.
 * @param {Object} data the data object.
 * @returns {String} the formatted text.
 */
exports.to_html = function() {
  return Mustache.to_html.apply(this, arguments);
};
<|endoftext|>