<commit_before>import map from 'lodash/fp/map'
import reduce from 'lodash/fp/reduce'
import flatten from 'lodash/fp/flatten'
import compose from 'lodash/fp/compose'
import partition from 'lodash/fp/partition'

import db from 'src/pouchdb'
import { pageKeyPrefix } from 'src/page-storage'
import { convertMetaDocId, visitKeyPrefix } from 'src/activity-logger'
import QueryBuilder from './query-builder'
import * as index from './search-index'

// Gets all the "ok" docs from Pouch bulk result, returning them as an array
// TODO: Try and make this nicer
const bulkResultsToArray = ({ results }) => results
    .map(res => res.docs)
    .map(list => list.filter(doc => doc.ok))
    .filter(list => list.length)
    .map(list => list[0].ok)

// Grabs the timestamp from a metadoc, formatting it to a Number
const getTimestampFromId = id => +convertMetaDocId(id).timestamp

/**
 * Given an augmented page doc (returned from `mapResultsToPouchDocs`), returns the ID of the
 * latest associated meta (either bookmark or visit doc).
 *
 * TODO: there may be cases of not-properly formatted data, which will result in null pointers;
 *  need to handle this.
 */
const grabLatestAssocMetaId = ({ assoc }) => {
    if (assoc.visit && assoc.bookmark) {
        // If both visit and bookmark exist, return the latest of each
        return assoc.visit > assoc.bookmark ? assoc.visit._id : assoc.bookmark._id
    } else if (assoc.visit) {
        return assoc.visit._id
    }
    return assoc.bookmark._id
}

/**
 * @param {Array<any>} augmentedPageDocs Array of augmented page docs (returned from `mapResultsToPouchDocs`)
 *  to sort by their latest associated meta doc timestamps.
 */
const sortByMetas = (augmentedPageDocs = []) =>
    augmentedPageDocs.sort((docA, docB) => {
        const timeA = getTimestampFromId(grabLatestAssocMetaId(docA))
        const timeB = getTimestampFromId(grabLatestAssocMetaId(docB))

        return timeB - timeA
    })

/**
 * Returns the latest bookmark or visit doc ID from index results.
 * @param {Array<string>} metaIds Array of IDs to sort and extract latest from.
 * @returns {any} Object containing `latest` meta ID and ordered `rest` array.
 *  `latest` should be `undefined` if input array length is 0.
 */
function getLatestMeta(metaIds = []) {
    const sorted = metaIds.sort((idA, idB) => getTimestampFromId(idB) - getTimestampFromId(idA))

    if (!sorted.length) {
        return { latest: undefined, rest: [] }
    }

    const [latest, ...rest] = sorted
    return { latest, rest }
}

/**
 * Creates a quick-lookup dictionary of page doc IDs to the associated meta doc IDs
 * from our search-index results.
 * @param {Array<any>} results Array of search-idnex results.
 * @returns {any} Object with page ID keys and values containing either bookmark or visit or both docs.
 */
const createResultIdsDict = reduce((acc, result) => ({
    ...acc,
    [result.document.id]: {
        pageId: result.document.id,
        visitIds: getLatestMeta(result.document.visitTimestamps),
        bookmarkIds: getLatestMeta(result.document.bookmarkTimestamps),
        score: result.score,
    },
}), {})

/**
 * Formats result IDs dict to format needed for injestion into `PouchDB.bulkGet`.
 */
const formatBulkGetInput = compose(
    flatten,
    map(idBatch => [
        { id: idBatch.pageId },
        { id: idBatch.visitIds.latest },
        { id: idBatch.bookmarkIds.latest },
    ].filter(doc => doc.id != null)) // Filter out missing metas
)

/**
 * Creates a quick-lookup dictionary of page doc IDs to the associated meta doc contents
 * from an array of meta doc inputs.
 * @param {Array<any>} metaDocs Array of meta docs to map page IDs to.
 * @returns {any} Object with page ID keys and values containing either bookmark or visit or both docs.
 */
const createMetaDocsDict = reduce((acc, metaDoc) => {
    const key = metaDoc._id.startsWith(visitKeyPrefix) ? 'visit' : 'bookmark'

    // Merge with existing bookmark/visit if present
    if (acc[metaDoc.page._id]) {
        acc[metaDoc.page._id] = {
            ...acc[metaDoc.page._id],
            [key]: metaDoc,
        }
    } else {
        acc[metaDoc.page._id] = {
            [key]: metaDoc,
        }
    }

    return acc
}, {})

/**
 * Performs all the messy logic needed to resolve search-index results against our PouchDB model.
 * Given n results, should produce n page docs with associated meta docs available under `assoc` key.
 * For the sake of differentiation, call these "augmented page docs".
 * The latest visit/bookmark doc should be available on the page doc (assuming they exist), while
 * all later metas will only be as _ids to avoid large fetches in the cases of pages with large
 * number of assoc. meta docs (they can be lazy fetched later on user actions).
 *
 * TODO: All this resolution is messy as hell; is there a better way?
 *
 * @param {Array<any>} results Array of results gotten from our search-index query.
 * @param {boolean} [sortByRelevance=false] Whether or not to maintain relevance to search sort order.
 * @returns {Array<any>} Array of augmented page docs containing linked meta docs.
 */
async function mapResultsToPouchDocs(results, sortByRelevance = false) {
    // Convert results to dictionary of page IDs to related meta IDs
    const resultIdsDict = createResultIdsDict(results)

    // Format IDs of docs needed to be immediately fetched from Pouch
    const bulkGetInput = formatBulkGetInput(resultIdsDict)

    // Perform bulk fetch for needed docs from pouch
    const bulkRes = await db.bulkGet({ docs: bulkGetInput })
    const docs = bulkResultsToArray(bulkRes)

    // Parition into meta and page docs
    const [pageDocs, metaDocs] = partition(doc => doc._id.startsWith(pageKeyPrefix))(docs)

    // Insert meta docs into the relevant page docs,
    //  including later meta doc IDs, within the `pageDoc.assoc` key
    const metaDocsDict = createMetaDocsDict(metaDocs)
    const augmentedPageDocs = pageDocs.map(doc => ({
        ...doc,
        assoc: {
            ...metaDocsDict[doc._id],
            laterVisits: resultIdsDict[doc._id].visitIds.rest,
            laterBookmarks: resultIdsDict[doc._id].bookmarkIds.rest,
        },
    }))

    // Ensure the original results order is maintained, if specified
    return sortByRelevance
        ? augmentedPageDocs.sort((pageA, pageB) =>
            resultIdsDict[pageA._id].score - resultIdsDict[pageB._id].score)
        : augmentedPageDocs
}

export default async function indexSearch({
    query,
    startDate,
    endDate,
    skip,
    limit = 10,
}) {
    const indexQuery = new QueryBuilder()
        .searchTerm(query || '*') // Search by wildcard by default
        .startDate(startDate)
        .endDate(endDate)
        .skipUntil(skip || undefined)
        .limit(limit || 10)
        .get()
    console.log(indexQuery) // DEBUG

    // Using index results, fetch matching pouch docs
    const results = await index.find(indexQuery)

    // Short-circuit if no results
    if (!results.length) {
        return { docs: [], resultsExhausted: true }
    }

    let docs = await mapResultsToPouchDocs(results, query !== '')

    // Perform time-based sort if default query (based on greatest meta doc time)
    if (!query) {
        docs = sortByMetas(docs)
    }

    return {
        docs,
        resultsExhausted: results.length < limit,
    }
}
<commit_msg>Fix post-search bug messing up time filters<commit_after>import map from 'lodash/fp/map'
import reduce from 'lodash/fp/reduce'
import flatten from 'lodash/fp/flatten'
import compose from 'lodash/fp/compose'
import partition from 'lodash/fp/partition'

import db from 'src/pouchdb'
import { pageKeyPrefix } from 'src/page-storage'
import { convertMetaDocId, visitKeyPrefix } from 'src/activity-logger'
import QueryBuilder from './query-builder'
import * as index from './search-index'

// Gets all the "ok" docs from Pouch bulk result, returning them as an array
// TODO: Try and make this nicer
const bulkResultsToArray = ({ results }) => results
    .map(res => res.docs)
    .map(list => list.filter(doc => doc.ok))
    .filter(list => list.length)
    .map(list => list[0].ok)

// Grabs the timestamp from a metadoc, formatting it to a Number
const getTimestampFromId = id => +convertMetaDocId(id).timestamp

/**
 * Given an augmented page doc (returned from `mapResultsToPouchDocs`), returns the ID of the
 * latest associated meta (either bookmark or visit doc).
 *
 * TODO: there may be cases of not-properly formatted data, which will result in null pointers;
 *  need to handle this.
 */
const grabLatestAssocMetaId = ({ assoc }) => {
    if (assoc.visit && assoc.bookmark) {
        // If both visit and bookmark exist, return the latest of each
        return assoc.visit > assoc.bookmark ? assoc.visit._id : assoc.bookmark._id
    } else if (assoc.visit) {
        return assoc.visit._id
    }
    return assoc.bookmark._id
}

/**
 * @param {Array<any>} augmentedPageDocs Array of augmented page docs (returned from `mapResultsToPouchDocs`)
 *  to sort by their latest associated meta doc timestamps.
 */
const sortByMetas = (augmentedPageDocs = []) =>
    augmentedPageDocs.sort((docA, docB) => {
        const timeA = getTimestampFromId(grabLatestAssocMetaId(docA))
        const timeB = getTimestampFromId(grabLatestAssocMetaId(docB))

        return timeB - timeA
    })

/**
 * Returns the latest bookmark or visit doc from index results + IDs of the later metas.
 * @param {Array<string>} metaIds Array of IDs to sort and extract latest from.
 * @param {any} timeFilters Time filters of the search query, if any.
 * @returns {any} Object containing `latest` meta ID and ordered `rest` array.
 *  `latest` should be `undefined` if input array length is 0.
 */
function getLatestMeta(metaIds = [], { startDate, endDate }) {
    const now = Date.now()
    // Filter out out-of-range metas then sort them by time
    const sorted = metaIds
        .filter(id => getTimestampFromId(id) >= (startDate || 0) && getTimestampFromId(id) <= (endDate || now))
        .sort((idA, idB) => getTimestampFromId(idB) - getTimestampFromId(idA))

    if (!sorted.length) {
        return { latest: undefined, rest: [] }
    }

    const [latest, ...rest] = sorted
    return { latest, rest }
}

/**
 * Creates a quick-lookup dictionary of page doc IDs to the associated meta doc IDs
 * from our search-index results.
 * @param {Array<any>} results Array of search-idnex results.
 * @returns {any} Object with page ID keys and values containing either bookmark or visit or both docs.
 */
const createResultIdsDict = timeFilters => reduce((acc, result) => ({
    ...acc,
    [result.document.id]: {
        pageId: result.document.id,
        visitIds: getLatestMeta(result.document.visitTimestamps, timeFilters),
        bookmarkIds: getLatestMeta(result.document.bookmarkTimestamps, timeFilters),
        score: result.score,
    },
}), {})

/**
 * Formats result IDs dict to format needed for injestion into `PouchDB.bulkGet`.
 */
const formatBulkGetInput = compose(
    flatten,
    map(idBatch => [
        { id: idBatch.pageId },
        { id: idBatch.visitIds.latest },
        { id: idBatch.bookmarkIds.latest },
    ].filter(doc => doc.id != null)) // Filter out missing metas
)

/**
 * Creates a quick-lookup dictionary of page doc IDs to the associated meta doc contents
 * from an array of meta doc inputs.
 * @param {Array<any>} metaDocs Array of meta docs to map page IDs to.
 * @returns {any} Object with page ID keys and values containing either bookmark or visit or both docs.
 */
const createMetaDocsDict = reduce((acc, metaDoc) => {
    const key = metaDoc._id.startsWith(visitKeyPrefix) ? 'visit' : 'bookmark'

    // Merge with existing bookmark/visit if present
    if (acc[metaDoc.page._id]) {
        acc[metaDoc.page._id] = {
            ...acc[metaDoc.page._id],
            [key]: metaDoc,
        }
    } else {
        acc[metaDoc.page._id] = {
            [key]: metaDoc,
        }
    }

    return acc
}, {})

/**
 * Performs all the messy logic needed to resolve search-index results against our PouchDB model.
 * Given n results, should produce n page docs with associated meta docs available under `assoc` key.
 * For the sake of differentiation, call these "augmented page docs".
 * The latest visit/bookmark doc should be available on the page doc (assuming they exist), while
 * all later metas will only be as _ids to avoid large fetches in the cases of pages with large
 * number of assoc. meta docs (they can be lazy fetched later on user actions).
 *
 * TODO: All this resolution is messy as hell; is there a better way?
 *
 * @param {Array<any>} results Array of results gotten from our search-index query.
 * @param {any} timeFilters Time filters of the search query, if any.
 * @param {boolean} [sortByRelevance=false] Whether or not to maintain relevance to search sort order.
 * @returns {Array<any>} Array of augmented page docs containing linked meta docs.
 */
async function mapResultsToPouchDocs(results, timeFilters, sortByRelevance = false) {
    // Convert results to dictionary of page IDs to related meta IDs
    const resultIdsDict = createResultIdsDict(timeFilters)(results)

    // Format IDs of docs needed to be immediately fetched from Pouch
    const bulkGetInput = formatBulkGetInput(resultIdsDict)

    // Perform bulk fetch for needed docs from pouch
    const bulkRes = await db.bulkGet({ docs: bulkGetInput })
    const docs = bulkResultsToArray(bulkRes)

    // Parition into meta and page docs
    const [pageDocs, metaDocs] = partition(doc => doc._id.startsWith(pageKeyPrefix))(docs)

    // Insert meta docs into the relevant page docs,
    //  including later meta doc IDs, within the `pageDoc.assoc` key
    const metaDocsDict = createMetaDocsDict(metaDocs)
    const augmentedPageDocs = pageDocs.map(doc => ({
        ...doc,
        assoc: {
            ...metaDocsDict[doc._id],
            laterVisits: resultIdsDict[doc._id].visitIds.rest,
            laterBookmarks: resultIdsDict[doc._id].bookmarkIds.rest,
        },
    }))

    // Ensure the original results order is maintained, if specified
    return sortByRelevance
        ? augmentedPageDocs.sort((pageA, pageB) =>
            resultIdsDict[pageA._id].score - resultIdsDict[pageB._id].score)
        : augmentedPageDocs
}

export default async function indexSearch({
    query,
    startDate,
    endDate,
    skip,
    limit = 10,
}) {
    const indexQuery = new QueryBuilder()
        .searchTerm(query || '*') // Search by wildcard by default
        .startDate(startDate)
        .endDate(endDate)
        .skipUntil(skip || undefined)
        .limit(limit || 10)
        .get()
    console.log(indexQuery) // DEBUG

    // Using index results, fetch matching pouch docs
    const results = await index.find(indexQuery)

    // Short-circuit if no results
    if (!results.length) {
        return { docs: [], resultsExhausted: true }
    }

    let docs = await mapResultsToPouchDocs(results, { startDate, endDate }, query !== '')

    // Perform time-based sort if default query (based on greatest meta doc time)
    if (!query) {
        docs = sortByMetas(docs)
    }

    return {
        docs,
        resultsExhausted: results.length < limit,
    }
}
<|endoftext|>


<commit_before>const selToWhere = sel => {
  if (/^\./.test(sel)) {
    return {attributes: {class: sel.substring(1)}};
  }
  else if (/^#/.test(sel)) {
    return {attributes: {id: sel.substring(1)}};
  }
  else if (/^\[/.test(sel)) {
    return {attributes: {[sel.substring(1, sel.length - 1)]: null}};
  }

  return {nodeName: sel};

};

module.exports = {
  selToWhere,
};
<commit_msg>fixup! Only allow JSX in .test.js<commit_after>const selToWhere = sel => {
  if (/^\./.test(sel)) {
    return {attributes: {class: sel.substring(1)}};
  }
  else if (/^#/.test(sel)) {
    return {attributes: {id: sel.substring(1)}};
  }
  else if (/^\[/.test(sel)) {
    return {attributes: {[sel.substring(1, sel.length - 1)]: null}};
  }

  return {nodeName: sel};

};

module.exports = {
  selToWhere,
};
<|endoftext|>


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

var Chat = {
	assets: "assets/chat/",
	doPush: function() {
		var elements = document.getElementsByTagName("x-chat-msg");
		var i = 0;
		while(i < elements.length) {
			var top = parseInt(elements[i].style.top.replace("px", ""))-30;

			if(top < -100) {
				elements[i].parentElement.removeChild(elements[i]);

			} else {
				elements[i].style.top = top;
			}
			i++;
		}
	},
	timeInitiated: false,
	create: function(name, message, left) {
		this.doPush();
		var chatElement = document.createElement("x-chat-msg");
		chatElement.id = "chat-msg-"+Math.random();

		chatElement.style.fontFamily = "Verdana";
		chatElement.style.fontSize = "10px";
		chatElement.style.position = "absolute";
		chatElement.style.left = left;
		chatElement.style.top = 200;
		chatElement.style.transitionProperty = "top";
		chatElement.style.transitionDuration = "1s, 1s";
		chatElement.style.transitionDelay = "0s, 1s";

		var chatLeft = document.createElement("chat-name");
		var chatRight = document.createElement("chat-msg");

		chatLeft.style.float = "left";
		chatLeft.style.height = "23px";
		chatLeft.style.lineHeight = "23px";
		chatLeft.style.paddingLeft ="10px";
		chatLeft.style.paddingRight ="5px";
		chatLeft.style.backgroundImage = "url('"+this.assets+"name-container.png')";
		chatLeft.innerHTML = ""+name+"";

		chatRight.style.float = "left";
		chatRight.style.height = "23px";
		chatRight.style.lineHeight = "23px";
		chatRight.style.paddingLeft ="10px";
		chatRight.style.paddingRight ="15px";
		chatRight.style.backgroundImage = "url('"+this.assets+"msg-container.png')";
		chatRight.style.backgroundPosition  = "right bottom";
		chatRight.innerHTML = message;

		chatElement.appendChild(chatLeft);
		chatElement.appendChild(chatRight);

		if(!this.timeInitiated) {
			this.timeInitiated = true;
			setInterval(function() { Chat.doPush(); }, 2000);
		}

		return chatElement;
	}

};

document.registerElement("x-chat-msg");<commit_msg>Escape HTML Entities to stop any security issues<commit_after>/**
 * CokeStudios Chat
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Mark Hughes
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

var Chat = {
	assets: "assets/chat/",
	doPush: function() {
		var elements = document.getElementsByTagName("x-chat-msg");
		var i = 0;
		while(i < elements.length) {
			var top = parseInt(elements[i].style.top.replace("px", ""))-30;

			if(top < -100) {
				elements[i].parentElement.removeChild(elements[i]);

			} else {
				elements[i].style.top = top;
			}
			i++;
		}
	},
	timeInitiated: false,
	create: function(name, message, left) {
		this.doPush();

		message = escapeHtmlEntities(message);

		var chatElement = document.createElement("x-chat-msg");
		chatElement.id = "chat-msg-"+Math.random();

		chatElement.style.fontFamily = "Verdana";
		chatElement.style.fontSize = "10px";
		chatElement.style.position = "absolute";
		chatElement.style.left = left;
		chatElement.style.top = 200;
		chatElement.style.transitionProperty = "top";
		chatElement.style.transitionDuration = "1s, 1s";
		chatElement.style.transitionDelay = "0s, 1s";

		var chatLeft = document.createElement("chat-name");
		var chatRight = document.createElement("chat-msg");

		chatLeft.style.float = "left";
		chatLeft.style.height = "23px";
		chatLeft.style.lineHeight = "23px";
		chatLeft.style.paddingLeft ="10px";
		chatLeft.style.paddingRight ="5px";
		chatLeft.style.backgroundImage = "url('"+this.assets+"name-container.png')";
		chatLeft.innerHTML = ""+name+"";

		chatRight.style.float = "left";
		chatRight.style.height = "23px";
		chatRight.style.lineHeight = "23px";
		chatRight.style.paddingLeft ="10px";
		chatRight.style.paddingRight ="15px";
		chatRight.style.backgroundImage = "url('"+this.assets+"msg-container.png')";
		chatRight.style.backgroundPosition  = "right bottom";
		chatRight.innerHTML = message;

		chatElement.appendChild(chatLeft);
		chatElement.appendChild(chatRight);

		if(!this.timeInitiated) {
			this.timeInitiated = true;
			setInterval(function() { Chat.doPush(); }, 2000);
		}

		return chatElement;
	}

};

document.registerElement("x-chat-msg");

// from here: http://stackoverflow.com/a/1354715/2376468
if(typeof escapeHtmlEntities == 'undefined') {
	escapeHtmlEntities = function(text) {
		return text.replace(/[\u00A0-\u2666<>\&]/g, function(c) {
			return '&' +
				(escapeHtmlEntities.entityTable[c.charCodeAt(0)] || '#' + c.charCodeAt(0)) + ';';
		});
	};
	escapeHtmlEntities.entityTable = {
		34: 'quot',
		38: 'amp',
		39: 'apos',
		60: 'lt',
		62: 'gt',
		160: 'nbsp',
		161: 'iexcl',
		162: 'cent',
		163: 'pound',
		164: 'curren',
		165: 'yen',
		166: 'brvbar',
		167: 'sect',
		168: 'uml',
		169: 'copy',
		170: 'ordf',
		171: 'laquo',
		172: 'not',
		173: 'shy',
		174: 'reg',
		175: 'macr',
		176: 'deg',
		177: 'plusmn',
		178: 'sup2',
		179: 'sup3',
		180: 'acute',
		181: 'micro',
		182: 'para',
		183: 'middot',
		184: 'cedil',
		185: 'sup1',
		186: 'ordm',
		187: 'raquo',
		188: 'frac14',
		189: 'frac12',
		190: 'frac34',
		191: 'iquest',
		192: 'Agrave',
		193: 'Aacute',
		194: 'Acirc',
		195: 'Atilde',
		196: 'Auml',
		197: 'Aring',
		198: 'AElig',
		199: 'Ccedil',
		200: 'Egrave',
		201: 'Eacute',
		202: 'Ecirc',
		203: 'Euml',
		204: 'Igrave',
		205: 'Iacute',
		206: 'Icirc',
		207: 'Iuml',
		208: 'ETH',
		209: 'Ntilde',
		210: 'Ograve',
		211: 'Oacute',
		212: 'Ocirc',
		213: 'Otilde',
		214: 'Ouml',
		215: 'times',
		216: 'Oslash',
		217: 'Ugrave',
		218: 'Uacute',
		219: 'Ucirc',
		220: 'Uuml',
		221: 'Yacute',
		222: 'THORN',
		223: 'szlig',
		224: 'agrave',
		225: 'aacute',
		226: 'acirc',
		227: 'atilde',
		228: 'auml',
		229: 'aring',
		230: 'aelig',
		231: 'ccedil',
		232: 'egrave',
		233: 'eacute',
		234: 'ecirc',
		235: 'euml',
		236: 'igrave',
		237: 'iacute',
		238: 'icirc',
		239: 'iuml',
		240: 'eth',
		241: 'ntilde',
		242: 'ograve',
		243: 'oacute',
		244: 'ocirc',
		245: 'otilde',
		246: 'ouml',
		247: 'divide',
		248: 'oslash',
		249: 'ugrave',
		250: 'uacute',
		251: 'ucirc',
		252: 'uuml',
		253: 'yacute',
		254: 'thorn',
		255: 'yuml',
		402: 'fnof',
		913: 'Alpha',
		914: 'Beta',
		915: 'Gamma',
		916: 'Delta',
		917: 'Epsilon',
		918: 'Zeta',
		919: 'Eta',
		920: 'Theta',
		921: 'Iota',
		922: 'Kappa',
		923: 'Lambda',
		924: 'Mu',
		925: 'Nu',
		926: 'Xi',
		927: 'Omicron',
		928: 'Pi',
		929: 'Rho',
		931: 'Sigma',
		932: 'Tau',
		933: 'Upsilon',
		934: 'Phi',
		935: 'Chi',
		936: 'Psi',
		937: 'Omega',
		945: 'alpha',
		946: 'beta',
		947: 'gamma',
		948: 'delta',
		949: 'epsilon',
		950: 'zeta',
		951: 'eta',
		952: 'theta',
		953: 'iota',
		954: 'kappa',
		955: 'lambda',
		956: 'mu',
		957: 'nu',
		958: 'xi',
		959: 'omicron',
		960: 'pi',
		961: 'rho',
		962: 'sigmaf',
		963: 'sigma',
		964: 'tau',
		965: 'upsilon',
		966: 'phi',
		967: 'chi',
		968: 'psi',
		969: 'omega',
		977: 'thetasym',
		978: 'upsih',
		982: 'piv',
		8226: 'bull',
		8230: 'hellip',
		8242: 'prime',
		8243: 'Prime',
		8254: 'oline',
		8260: 'frasl',
		8472: 'weierp',
		8465: 'image',
		8476: 'real',
		8482: 'trade',
		8501: 'alefsym',
		8592: 'larr',
		8593: 'uarr',
		8594: 'rarr',
		8595: 'darr',
		8596: 'harr',
		8629: 'crarr',
		8656: 'lArr',
		8657: 'uArr',
		8658: 'rArr',
		8659: 'dArr',
		8660: 'hArr',
		8704: 'forall',
		8706: 'part',
		8707: 'exist',
		8709: 'empty',
		8711: 'nabla',
		8712: 'isin',
		8713: 'notin',
		8715: 'ni',
		8719: 'prod',
		8721: 'sum',
		8722: 'minus',
		8727: 'lowast',
		8730: 'radic',
		8733: 'prop',
		8734: 'infin',
		8736: 'ang',
		8743: 'and',
		8744: 'or',
		8745: 'cap',
		8746: 'cup',
		8747: 'int',
		8756: 'there4',
		8764: 'sim',
		8773: 'cong',
		8776: 'asymp',
		8800: 'ne',
		8801: 'equiv',
		8804: 'le',
		8805: 'ge',
		8834: 'sub',
		8835: 'sup',
		8836: 'nsub',
		8838: 'sube',
		8839: 'supe',
		8853: 'oplus',
		8855: 'otimes',
		8869: 'perp',
		8901: 'sdot',
		8968: 'lceil',
		8969: 'rceil',
		8970: 'lfloor',
		8971: 'rfloor',
		9001: 'lang',
		9002: 'rang',
		9674: 'loz',
		9824: 'spades',
		9827: 'clubs',
		9829: 'hearts',
		9830: 'diams',
		338: 'OElig',
		339: 'oelig',
		352: 'Scaron',
		353: 'scaron',
		376: 'Yuml',
		710: 'circ',
		732: 'tilde',
		8194: 'ensp',
		8195: 'emsp',
		8201: 'thinsp',
		8204: 'zwnj',
		8205: 'zwj',
		8206: 'lrm',
		8207: 'rlm',
		8211: 'ndash',
		8212: 'mdash',
		8216: 'lsquo',
		8217: 'rsquo',
		8218: 'sbquo',
		8220: 'ldquo',
		8221: 'rdquo',
		8222: 'bdquo',
		8224: 'dagger',
		8225: 'Dagger',
		8240: 'permil',
		8249: 'lsaquo',
		8250: 'rsaquo',
		8364: 'euro'
	};
};<|endoftext|>


<commit_before>/*
 * Copyright (c) 2016, Two Sigma Open Source
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * * Neither the name of git-meta nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
"use strict";

/**
 * This module contains methods for pushing.
 */

const assert       = require("chai").assert;
const ChildProcess = require("child-process-promise");
const co           = require("co");
const colors       = require("colors");
const NodeGit      = require("nodegit");

const DoWorkQueue         = require("./do_work_queue");
const GitUtil             = require("./git_util");
const SubmoduleUtil       = require("./submodule_util");
const SubmoduleConfigUtil = require("./submodule_config_util");
const SyntheticBranchUtil = require("./synthetic_branch_util");
const UserError           = require("./user_error");

// This magic SHA represents an empty tree in Git.

const EMPTY_TREE = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";

/**
 * For a given commit, determine a reasonably close oid that has
 * already been pushed. This does *not* do a fetch, but rather just
 * compares against all available remote refs.
 *
 * This works by building a revwalk and removing all ancestors of remote refs
 * for the given remote. It then reverses the result, and returns the first
 * parent of the oldest, unpushed commit. This is not an absolute last pushed
 * oid, but is a very good proxy to determine what to push.
 *
 * Returns a commit that exists in a remote ref, or null if no such commit
 * exists. If the given commit exists in a remote ref, will return itself.
 *
 * @async
 * @param {NodeGit.Repository} repo
 * @param {NodeGit.Commit}     commit
 */
exports.getClosePushedCommit = co.wrap(function*(repo, commit) {
    assert.instanceOf(repo, NodeGit.Repository);
    assert.instanceOf(commit, NodeGit.Commit);

    // Search for the first commit that is not an ancestor of a remote ref, ie.
    // the first commit that is unpushed. NodeGit Revwalk is too slow, so
    // we shell out.

    // First, get the list of remote refs to exclude
    const excludeRefs = [];
    for (const ref of (yield NodeGit.Reference.list(repo))) {
        if (ref.startsWith("refs/remotes/")) {
            excludeRefs.push("^" + ref);
        }
    }
    if (excludeRefs.length === 0) {
        return null;
    }

    const execString = `\
git -C '${repo.workdir()}' rev-list --reverse --topo-order ${commit} \
${excludeRefs.join(" ")}`;
    let result = yield ChildProcess.exec(execString);
    if (result.error) {
        throw new Error(
            `Unexpected error figuring out what to push:
stderr:
{result.stderr}
stdout:
{result.stdout}`);
    }
    const out = result.stdout;
    if (!out) {
        // Nothing new to push
        return commit;
    }

    const firstNewOid = out.substring(0, out.indexOf("\n"));

    const firstNewCommit = yield repo.getCommit(firstNewOid);

    // If the first unpushed commit has no parents, then the entire set of
    // commits to push is new.

    if (0 === firstNewCommit.parentcount()) {
        return null;
    }
    return yield repo.getCommit(firstNewCommit.parentId(0));
});

/**
 * For a given proposed push, return a map from submodule to sha,
 * excluding any submodules that the server likely already has.
 *
 * Return in the map only those submodules that (1) exist locally, in
 * `.git/modules` and (2) are changed between `commit` and the merge base of
 * `commit` and each relevant branch.
 *
 * @async
 * @param {NodeGit.Repository} repo
 * @param {String}             source
 * @param {NodeGit.Commit}     commit
 */
exports.getPushMap = co.wrap(function*(repo,  source, commit) {
    assert.instanceOf(repo, NodeGit.Repository);
    assert.isString(source);
    assert.instanceOf(commit, NodeGit.Commit);

    const baseCommit = yield exports.getClosePushedCommit(repo, commit);
    let baseTree;
    if (null !== baseCommit) {
        baseTree = yield baseCommit.getTree();
    } else {
        baseTree = EMPTY_TREE;
    }

    const tree = yield commit.getTree();
    const diff = yield NodeGit.Diff.treeToTree(repo, baseTree, tree, null);
    const changes = SubmoduleUtil.getSubmoduleChangesFromDiff(diff, true);

    const pushMap = {};
    for (const path of Object.keys(changes)) {
        const change = changes[path];
        if (!change.deleted) {
            pushMap[path] = change.newSha;
        }
    }

    const openSubmodules = yield SubmoduleUtil.listOpenSubmodules(repo);
    const absorbedSubmodules =
        yield SubmoduleUtil.listAbsorbedSubmodules(repo);

    const availableSubmodules = new Set([...openSubmodules,
                                        ...absorbedSubmodules]);

    // Make sure we have the repositories and commits we want to push.
    for (const sub of Object.keys(pushMap)) {
        if (!availableSubmodules.has(sub)) {
            delete pushMap[sub];
            continue;
        }

        const subRepo = yield SubmoduleUtil.getBareRepo(repo, sub);
        try {
            yield subRepo.getCommit(pushMap[sub]);
        } catch (e) {
            delete pushMap[sub];
        }
    }

    return pushMap;
});

/**
 * For each open submodule that exists in the commit indicated by the specified
 * `source`, push a synthetic-meta-ref for the `source` commit.
 * If all sub-repo pushes succeed, push `source` to
 * to the specified `target` branch in `remoteName`.  If any pushes fail, throw
 * a `UserError` object.
 *
 * Note that this strategy is naive: it does not handle the following
 * situations:
 *
 * - closed submodules with commits that need to be pushed
 * - submodules that do not exist in the `source` commit, but did previously
 *   and need synthetic-meta-refs
 * - submodules with divergent histories, i.e., the commit we create the
 *   synthetic-meta-ref for doesn't contain one or more commits that need to be
 *   pushed in its history
 *
 * Addressing these situations would have a performance impact, requiring
 * calculation and traversal of all meta-repo commits being pushed.  We should
 * probably add a way to do an "exhaustive" push.
 *
 * @async
 * @param {NodeGit.Repository} repo
 * @param {String}             remoteName
 * @param {String}             source
 * @param {String}             target
 * @param {Boolean}            force
 */
exports.push = co.wrap(function *(repo, remoteName, source, target, force) {
    assert.instanceOf(repo, NodeGit.Repository);
    assert.isString(remoteName);
    assert.isString(source);
    assert.isString(target);
    assert.isBoolean(force);

    let remoteUrl = yield GitUtil.getUrlFromRemoteName(repo, remoteName);

    const annotatedCommit = yield GitUtil.resolveCommitish(repo, source);
    if (annotatedCommit === null) {
        throw new UserError(`No such ref: ${source}`);
    }
    const sha = annotatedCommit.id();
    const commit = yield repo.getCommit(sha);

    // First, push the submodules.
    const pushMap = yield exports.getPushMap(repo, source, commit);

    let errorMessage = "";

    const urls = yield SubmoduleConfigUtil.getSubmodulesFromCommit(repo,
                                                                   commit);

    const pushSub = co.wrap(function *(subName) {
        // Push to a synthetic branch; first, calculate name.

        const sha = pushMap[subName];
        const syntheticName =
                          SyntheticBranchUtil.getSyntheticBranchForCommit(sha);
        const subRepo = yield SubmoduleUtil.getBareRepo(repo, subName);

        // Resolve the submodule's URL against the URL of the meta-repo,
        // ignoring the remote that is configured in the open submodule.

        const subUrl = SubmoduleConfigUtil.resolveSubmoduleUrl(remoteUrl,
                                                               urls[subName]);

        // Always force push synthetic refs.  It should not be necessary, but
        // if something does go wrong forcing will allow us to auto-correct.
        // If they succeed, no need to print the output inside the submodules.

        const pushResult = yield GitUtil.push(subRepo,
                                              subUrl,
                                              sha,
                                              syntheticName,
                                              true,
                                              true);
        if (null !== pushResult) {
            errorMessage +=
           `Failed to push submodule ${colors.yellow(subName)}: ${pushResult}`;
        }
    });
    yield DoWorkQueue.doInParallel(Object.keys(pushMap), pushSub);

    // Throw an error if there were any problems pushing submodules; don't push
    // the meta-repo.

    if ("" !== errorMessage) {
        throw new UserError(errorMessage);
    }

    // Finally, push the meta-repo and throw on failure.

    const result = yield GitUtil.push(repo, remoteName, source, target, force);
    if (null !== result) {
        throw new UserError(`Failed to push meta-repo: ${result}`);
    }
});
<commit_msg>do not shell out unnecessarily (which should eliminate E2BIG)<commit_after>/*
 * Copyright (c) 2016, Two Sigma Open Source
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * * Neither the name of git-meta nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
"use strict";

/**
 * This module contains methods for pushing.
 */

const assert       = require("chai").assert;
const ChildProcess = require("child-process-promise");
const co           = require("co");
const colors       = require("colors");
const NodeGit      = require("nodegit");

const DoWorkQueue         = require("./do_work_queue");
const GitUtil             = require("./git_util");
const SubmoduleUtil       = require("./submodule_util");
const SubmoduleConfigUtil = require("./submodule_config_util");
const SyntheticBranchUtil = require("./synthetic_branch_util");
const UserError           = require("./user_error");

// This magic SHA represents an empty tree in Git.

const EMPTY_TREE = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";

/**
 * For a given commit, determine a reasonably close oid that has
 * already been pushed. This does *not* do a fetch, but rather just
 * compares against all available remote refs.
 *
 * This works by building a revwalk and removing all ancestors of remote refs
 * for the given remote. It then reverses the result, and returns the first
 * parent of the oldest, unpushed commit. This is not an absolute last pushed
 * oid, but is a very good proxy to determine what to push.
 *
 * Returns a commit that exists in a remote ref, or null if no such commit
 * exists. If the given commit exists in a remote ref, will return itself.
 *
 * @async
 * @param {NodeGit.Repository} repo
 * @param {NodeGit.Commit}     commit
 */
exports.getClosePushedCommit = co.wrap(function*(repo, commit) {
    assert.instanceOf(repo, NodeGit.Repository);
    assert.instanceOf(commit, NodeGit.Commit);

    // Search for the first commit that is not an ancestor of a remote ref, ie.
    // the first commit that is unpushed. NodeGit Revwalk is too slow, so
    // we shell out.

    // First, get the list of remote refs to exclude
    const excludeRefs = [];
    for (const ref of (yield NodeGit.Reference.list(repo))) {
        if (ref.startsWith("refs/remotes/")) {
            excludeRefs.push("^" + ref);
        }
    }
    if (excludeRefs.length === 0) {
        return null;
    }

    const args = ["-C", repo.workdir(), "rev-list", "--reverse",
                  "--topo-order", commit, ...excludeRefs];
    let result = yield ChildProcess.execFile("git", args);
    if (result.error) {
        throw new Error(
            `Unexpected error figuring out what to push:
stderr:
{result.stderr}
stdout:
{result.stdout}`);
    }
    const out = result.stdout;
    if (!out) {
        // Nothing new to push
        return commit;
    }

    const firstNewOid = out.substring(0, out.indexOf("\n"));

    const firstNewCommit = yield repo.getCommit(firstNewOid);

    // If the first unpushed commit has no parents, then the entire set of
    // commits to push is new.

    if (0 === firstNewCommit.parentcount()) {
        return null;
    }
    return yield repo.getCommit(firstNewCommit.parentId(0));
});

/**
 * For a given proposed push, return a map from submodule to sha,
 * excluding any submodules that the server likely already has.
 *
 * Return in the map only those submodules that (1) exist locally, in
 * `.git/modules` and (2) are changed between `commit` and the merge base of
 * `commit` and each relevant branch.
 *
 * @async
 * @param {NodeGit.Repository} repo
 * @param {String}             source
 * @param {NodeGit.Commit}     commit
 */
exports.getPushMap = co.wrap(function*(repo,  source, commit) {
    assert.instanceOf(repo, NodeGit.Repository);
    assert.isString(source);
    assert.instanceOf(commit, NodeGit.Commit);

    const baseCommit = yield exports.getClosePushedCommit(repo, commit);
    let baseTree;
    if (null !== baseCommit) {
        baseTree = yield baseCommit.getTree();
    } else {
        baseTree = EMPTY_TREE;
    }

    const tree = yield commit.getTree();
    const diff = yield NodeGit.Diff.treeToTree(repo, baseTree, tree, null);
    const changes = SubmoduleUtil.getSubmoduleChangesFromDiff(diff, true);

    const pushMap = {};
    for (const path of Object.keys(changes)) {
        const change = changes[path];
        if (!change.deleted) {
            pushMap[path] = change.newSha;
        }
    }

    const openSubmodules = yield SubmoduleUtil.listOpenSubmodules(repo);
    const absorbedSubmodules =
        yield SubmoduleUtil.listAbsorbedSubmodules(repo);

    const availableSubmodules = new Set([...openSubmodules,
                                        ...absorbedSubmodules]);

    // Make sure we have the repositories and commits we want to push.
    for (const sub of Object.keys(pushMap)) {
        if (!availableSubmodules.has(sub)) {
            delete pushMap[sub];
            continue;
        }

        const subRepo = yield SubmoduleUtil.getBareRepo(repo, sub);
        try {
            yield subRepo.getCommit(pushMap[sub]);
        } catch (e) {
            delete pushMap[sub];
        }
    }

    return pushMap;
});

/**
 * For each open submodule that exists in the commit indicated by the specified
 * `source`, push a synthetic-meta-ref for the `source` commit.
 * If all sub-repo pushes succeed, push `source` to
 * to the specified `target` branch in `remoteName`.  If any pushes fail, throw
 * a `UserError` object.
 *
 * Note that this strategy is naive: it does not handle the following
 * situations:
 *
 * - closed submodules with commits that need to be pushed
 * - submodules that do not exist in the `source` commit, but did previously
 *   and need synthetic-meta-refs
 * - submodules with divergent histories, i.e., the commit we create the
 *   synthetic-meta-ref for doesn't contain one or more commits that need to be
 *   pushed in its history
 *
 * Addressing these situations would have a performance impact, requiring
 * calculation and traversal of all meta-repo commits being pushed.  We should
 * probably add a way to do an "exhaustive" push.
 *
 * @async
 * @param {NodeGit.Repository} repo
 * @param {String}             remoteName
 * @param {String}             source
 * @param {String}             target
 * @param {Boolean}            force
 */
exports.push = co.wrap(function *(repo, remoteName, source, target, force) {
    assert.instanceOf(repo, NodeGit.Repository);
    assert.isString(remoteName);
    assert.isString(source);
    assert.isString(target);
    assert.isBoolean(force);

    let remoteUrl = yield GitUtil.getUrlFromRemoteName(repo, remoteName);

    const annotatedCommit = yield GitUtil.resolveCommitish(repo, source);
    if (annotatedCommit === null) {
        throw new UserError(`No such ref: ${source}`);
    }
    const sha = annotatedCommit.id();
    const commit = yield repo.getCommit(sha);

    // First, push the submodules.
    const pushMap = yield exports.getPushMap(repo, source, commit);

    let errorMessage = "";

    const urls = yield SubmoduleConfigUtil.getSubmodulesFromCommit(repo,
                                                                   commit);

    const pushSub = co.wrap(function *(subName) {
        // Push to a synthetic branch; first, calculate name.

        const sha = pushMap[subName];
        const syntheticName =
                          SyntheticBranchUtil.getSyntheticBranchForCommit(sha);
        const subRepo = yield SubmoduleUtil.getBareRepo(repo, subName);

        // Resolve the submodule's URL against the URL of the meta-repo,
        // ignoring the remote that is configured in the open submodule.

        const subUrl = SubmoduleConfigUtil.resolveSubmoduleUrl(remoteUrl,
                                                               urls[subName]);

        // Always force push synthetic refs.  It should not be necessary, but
        // if something does go wrong forcing will allow us to auto-correct.
        // If they succeed, no need to print the output inside the submodules.

        const pushResult = yield GitUtil.push(subRepo,
                                              subUrl,
                                              sha,
                                              syntheticName,
                                              true,
                                              true);
        if (null !== pushResult) {
            errorMessage +=
           `Failed to push submodule ${colors.yellow(subName)}: ${pushResult}`;
        }
    });
    yield DoWorkQueue.doInParallel(Object.keys(pushMap), pushSub);

    // Throw an error if there were any problems pushing submodules; don't push
    // the meta-repo.

    if ("" !== errorMessage) {
        throw new UserError(errorMessage);
    }

    // Finally, push the meta-repo and throw on failure.

    const result = yield GitUtil.push(repo, remoteName, source, target, force);
    if (null !== result) {
        throw new UserError(`Failed to push meta-repo: ${result}`);
    }
});
<|endoftext|>


<commit_before>/*
This file is part of the Juju GUI, which lets users view and manage Juju
environments within a graphical interface (https://launchpad.net/juju-gui).
Copyright (C) 2012-2013 Canonical Ltd.

This program is free software: you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3, as published by
the Free Software Foundation.

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

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

'use strict';

describe('deployer bar view', function() {
  var Y, container, ECS, ecs, dbObj, envObj, mockEvent, testUtils, utils, views,
      view, View;

  before(function(done) {
    Y = YUI(GlobalConfig).use(['deployer-bar',
                               'juju-views',
                               'juju-tests-utils',
                               'environment-change-set',
                               'event-simulate',
                               'node-event-simulate',
                               'node'], function(Y) {

      utils = Y.namespace('juju-tests.utils');
      views = Y.namespace('juju.views');
      ECS = Y.namespace('juju').EnvironmentChangeSet;
      View = views.DeployerBarView;
      mockEvent = { halt: function() {} };
      done();
    });
  });

  beforeEach(function() {
    ecs = new ECS({
      db: dbObj
    });
    container = utils.makeContainer(this, 'deployer-bar');
    view = new View({container: container, ecs: ecs}).render();
  });

  afterEach(function() {
    ecs.destroy();
    container.remove(true);
    view.destroy();
  });

  it('should exist in the views namespace', function() {
    assert(views.DeployerBarView);
  });

  it('should apply the wrapping class to the container', function() {
    assert.equal(container.hasClass('deployer-bar'), true);
  });

  // Test currently has cleanup issues
  it.skip('should increase changes when a service is added', function() {
    ecs.changeSet.abc123 = { foo: 'foo' };
    assert.equal(view._getChangeCount(ecs), 1);
  });

  it('should confirm ECS changes when deploy is clicked', function() {
    var changesStub = utils.makeStubMethod(view, '_getChangeCount', 0),
        deployStub = utils.makeStubMethod(view, '_getDeployedServices', []),
        relationsStub = utils.makeStubMethod(view, '_getAddRelations', []);
    this._cleanups.push(changesStub.reset);
    this._cleanups.push(deployStub.reset);
    this._cleanups.push(relationsStub.reset);
    view.deploy(mockEvent);
    assert.equal(container.hasClass('summary-open'), true,
                 'summary is not open');
    assert.notEqual(container.one('.summary-panel'), null,
                    'summary panel HTML is not present');
  });

  it('should commit on confirmation', function() {
    var stubCommit = utils.makeStubMethod(ecs, 'commit');
    this._cleanups.push(stubCommit.reset);
    view.confirm(mockEvent);
    assert.equal(stubCommit.calledOnce(), true,
                 'ECS commit not called');
    assert.equal(container.hasClass('summary-open'), false,
                 'summary-open class still present');
  });

  it('should close', function() {
    view.summaryClose(mockEvent);
    assert.equal(container.hasClass('summary-open'), false);
  });

  it('provides a way to retrieve the service icon', function() {
    var url = view._getServiceIconUrl('django');
    assert.strictEqual(
        url,
        'https://manage.jujucharms.com' +
        '/api/3/charm/precise/django/file/icon.svg'
    );
  });

  // XXX frankban 2014-05-12: it seems all this suite is not well isolated,
  // or the view does not clean up correctly.
  it.skip('retrieves all the unit changes', function() {
    ecs.lazyAddUnits(['django', 1]);
    ecs.lazyAddUnits(['rails', 2]);
    var results = view._getAddUnits(ecs);
    assert.lengthOf(results, 2);
    assert.deepEqual(results[0], {
      icon: 'https://manage.jujucharms.com' +
          '/api/3/charm/precise/django/file/icon.svg',
      numUnits: 1,
      serviceName: 'django'
    });
    assert.deepEqual(results[1], {
      icon: 'https://manage.jujucharms.com' +
          '/api/3/charm/precise/rails/file/icon.svg',
      numUnits: 2,
      serviceName: 'rails'
    });
  });

});
<commit_msg>Added test<commit_after>/*
This file is part of the Juju GUI, which lets users view and manage Juju
environments within a graphical interface (https://launchpad.net/juju-gui).
Copyright (C) 2012-2013 Canonical Ltd.

This program is free software: you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3, as published by
the Free Software Foundation.

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

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

'use strict';

describe('deployer bar view', function() {
  var Y, container, ECS, ecs, dbObj, envObj, mockEvent, testUtils, utils, views,
      view, View;

  before(function(done) {
    Y = YUI(GlobalConfig).use(['deployer-bar',
                               'juju-views',
                               'juju-tests-utils',
                               'environment-change-set',
                               'event-simulate',
                               'node-event-simulate',
                               'node'], function(Y) {

      utils = Y.namespace('juju-tests.utils');
      views = Y.namespace('juju.views');
      ECS = Y.namespace('juju').EnvironmentChangeSet;
      View = views.DeployerBarView;
      mockEvent = { halt: function() {} };
      done();
    });
  });

  beforeEach(function() {
    ecs = new ECS({
      db: dbObj
    });
    container = utils.makeContainer(this, 'deployer-bar');
    view = new View({container: container, ecs: ecs}).render();
  });

  afterEach(function() {
    ecs.destroy();
    container.remove(true);
    view.destroy();
  });

  it('should exist in the views namespace', function() {
    assert(views.DeployerBarView);
  });

  it('should apply the wrapping class to the container', function() {
    assert.equal(container.hasClass('deployer-bar'), true);
  });

  // Test currently has cleanup issues
  it.skip('should increase changes when a service is added', function() {
    ecs.changeSet.abc123 = { foo: 'foo' };
    assert.equal(view._getChangeCount(ecs), 1);
  });

  it('should confirm ECS changes when deploy is clicked', function() {
    var changesStub = utils.makeStubMethod(view, '_getChangeCount', 0),
        deployStub = utils.makeStubMethod(view, '_getDeployedServices', []),
        relationsStub = utils.makeStubMethod(view, '_getAddRelations', []);
    this._cleanups.push(changesStub.reset);
    this._cleanups.push(deployStub.reset);
    this._cleanups.push(relationsStub.reset);
    view.deploy(mockEvent);
    assert.equal(container.hasClass('summary-open'), true,
                 'summary is not open');
    assert.notEqual(container.one('.summary-panel'), null,
                    'summary panel HTML is not present');
  });

  it('should commit on confirmation', function() {
    var stubCommit = utils.makeStubMethod(ecs, 'commit');
    this._cleanups.push(stubCommit.reset);
    view.confirm(mockEvent);
    assert.equal(stubCommit.calledOnce(), true,
                 'ECS commit not called');
    assert.equal(container.hasClass('summary-open'), false,
                 'summary-open class still present');
  });

  it('should close', function() {
    view.summaryClose(mockEvent);
    assert.equal(container.hasClass('summary-open'), false);
  });

  it('provides a way to retrieve the service icon', function() {
    var url = view._getServiceIconUrl('django');
    assert.strictEqual(
        url,
        'https://manage.jujucharms.com' +
        '/api/3/charm/precise/django/file/icon.svg'
    );
  });

  // XXX frankban 2014-05-12: it seems all this suite is not well isolated,
  // or the view does not clean up correctly.
  it.skip('retrieves all the unit changes', function() {
    ecs.lazyAddUnits(['django', 1]);
    ecs.lazyAddUnits(['rails', 2]);
    var results = view._getAddUnits(ecs);
    assert.lengthOf(results, 2);
    assert.deepEqual(results[0], {
      icon: 'https://manage.jujucharms.com' +
          '/api/3/charm/precise/django/file/icon.svg',
      numUnits: 1,
      serviceName: 'django'
    });
    assert.deepEqual(results[1], {
      icon: 'https://manage.jujucharms.com' +
          '/api/3/charm/precise/rails/file/icon.svg',
      numUnits: 2,
      serviceName: 'rails'
    });
  });

  // XXX see above
  it.skip('retrieves all the machine changes', function() {
    var machine = {};
    var container = {
      parentId: ecs.lazyAddMachines([[machine]]), containerType: 'lxc'
    };
    // Add a second machine and a container on the first.
    ecs.lazyAddMachines([[machine, container]]);
    var results = view._getAddMachines(ecs);
    assert.lengthOf(results, 3);
    assert.deepEqual(results[0], machine);
    assert.deepEqual(results[1], machine);
    assert.deepEqual(results[2], container);
  });

});
<|endoftext|>


<commit_before>/**
 * grunt-webfont: Node.js engine
 *
 * @requires ttfautohint 1.00+ (optional)
 * @author Artem Sapegin (http://sapegin.me)
 */

module.exports = function(o, allDone) {
	'use strict';

	var fs = require('fs');
	var path = require('path');
	var async = require('async');
	var temp = require('temp');
	var exec = require('exec');
	var _ = require('lodash');
	var StringDecoder = require('string_decoder').StringDecoder;
	var svgicons2svgfont = require('svgicons2svgfont');
	var svg2ttf = require('svg2ttf');
	var ttf2woff = require('ttf2woff');
	var ttf2eot = require('ttf2eot');
	var SVGO = require('svgo');
	var MemoryStream = require('memorystream');
	var logger = o.logger || require('winston');
	var wf = require('../util/util');

	// @todo Ligatures

	var fonts = {};

	var generators = {
		svg: function(done) {
			var font = '';
			var decoder = new StringDecoder('utf8');
			svgFilesToStreams(o.files, function(streams) {
				var stream = svgicons2svgfont(streams, {
					fontName: o.fontName,
					fontHeight: o.fontHeight,
					descent: o.descent,
					normalize: o.normalize,
					round: o.round,
					log: logger.verbose.bind(logger),
					error: logger.error.bind(logger)
				});
				stream.on('data', function(chunk) {
					font += decoder.write(chunk);
				});
				stream.on('end', function() {
					fonts.svg = font;
					done(font);
				});
			});
		},

		ttf: function(done) {
			getFont('svg', function(svgFont) {
				var font = svg2ttf(svgFont, {});
				font = new Buffer(font.buffer);
				autohintTtfFont(font, function(hintedFont) {
					// ttfautohint is optional
					if (hintedFont) {
						font = hintedFont;
					}
					fonts.ttf = font;
					done(font);
				});
			});
		},

		woff: function(done) {
			getFont('ttf', function(ttfFont) {
				var font = ttf2woff(new Uint8Array(ttfFont), {});
				font = new Buffer(font.buffer);
				fonts.woff = font;
				done(font);
			});
		},

		woff2: function(done) {
			// Will be converted from TTF later
			done();
		},

		eot: function(done) {
			getFont('ttf', function(ttfFont) {
				var font = ttf2eot(new Uint8Array(ttfFont));
				font = new Buffer(font.buffer);
				fonts.eot = font;
				done(font);
			});
		}
	};

	var steps = [];

	// Font types
	var typesToGenerate = o.types.slice();
	if (o.types.indexOf('woff2') !== -1 && o.types.indexOf('ttf'  === -1)) typesToGenerate.push('ttf');
	typesToGenerate.forEach(function(type) {
		steps.push(createFontWriter(type));
	});

	steps.push(allDone);

	// Run!
	async.waterfall(steps);

	function getFont(type, done) {
		if (fonts[type]) {
			done(fonts[type]);
		}
		else {
			generators[type](done);
		}
	}

	function createFontWriter(type) {
		return function(done) {
			getFont(type, function(font) {
				fs.writeFileSync(wf.getFontPath(o, type), font);
				done();
			});
		};
	}

	function svgFilesToStreams(files, done) {
		async.map(files, function(file, fileDone) {
			var svg = fs.readFileSync(file, 'utf8');
			var svgo = new SVGO();
			try {
				svgo.optimize(svg, function(res) {
					var idx = files.indexOf(file);
					var name = o.glyphs[idx];
					var stream = new MemoryStream(res.data, {
						writable: false
					});
					fileDone(null, {
						codepoint: o.codepoints[name],
						name: name,
						stream: stream
					});
				});
			}
			catch(err) {
				fileDone(err);
			}
		}, function(err, streams) {
			if (err) {
				logger.error('Can’t simplify SVG file with SVGO.\n\n' + err);
				allDone(false);
			}
			else {
				done(streams);
			}
		});
	}

	function autohintTtfFont(font, done) {
		var tempDir = temp.mkdirSync();
		var originalFilepath = path.join(tempDir, 'font.ttf');
		var hintedFilepath = path.join(tempDir, 'hinted.ttf');

		if (!o.autoHint){
			done(false);
			return;
		}
		// Save original font to temporary directory
		fs.writeFileSync(originalFilepath, font);

		// Run ttfautohint
		var args = [
			'ttfautohint',
			'--symbol',
			'--fallback-script=latn',
			'--windows-compatibility',
			'--no-info',
			originalFilepath,
			hintedFilepath
		];

		exec(args, function(err, out, code) {
			if (err) {
				if (err instanceof Error) {
					if (err.code === 'ENOENT') {
						logger.verbose('Hinting skipped, ttfautohint not found.');
						done(false);
						return;
					}
					err = err.message;
				}
				logger.error('Can’t run ttfautohint.\n\n' + err);
				done(false);
				return;
			}

			// Read hinted font back
			var hintedFont = fs.readFileSync(hintedFilepath);
			done(hintedFont);
		});
	}

};
<commit_msg>Tweak async callback.<commit_after>/**
 * grunt-webfont: Node.js engine
 *
 * @requires ttfautohint 1.00+ (optional)
 * @author Artem Sapegin (http://sapegin.me)
 */

module.exports = function(o, allDone) {
	'use strict';

	var fs = require('fs');
	var path = require('path');
	var async = require('async');
	var temp = require('temp');
	var exec = require('exec');
	var _ = require('lodash');
	var StringDecoder = require('string_decoder').StringDecoder;
	var svgicons2svgfont = require('svgicons2svgfont');
	var svg2ttf = require('svg2ttf');
	var ttf2woff = require('ttf2woff');
	var ttf2eot = require('ttf2eot');
	var SVGO = require('svgo');
	var MemoryStream = require('memorystream');
	var logger = o.logger || require('winston');
	var wf = require('../util/util');

	// @todo Ligatures

	var fonts = {};

	var generators = {
		svg: function(done) {
			var font = '';
			var decoder = new StringDecoder('utf8');
			svgFilesToStreams(o.files, function(streams) {
				var stream = svgicons2svgfont(streams, {
					fontName: o.fontName,
					fontHeight: o.fontHeight,
					descent: o.descent,
					normalize: o.normalize,
					round: o.round,
					log: logger.verbose.bind(logger),
					error: logger.error.bind(logger)
				});
				stream.on('data', function(chunk) {
					font += decoder.write(chunk);
				});
				stream.on('end', function() {
					fonts.svg = font;
					done(font);
				});
			});
		},

		ttf: function(done) {
			getFont('svg', function(svgFont) {
				var font = svg2ttf(svgFont, {});
				font = new Buffer(font.buffer);
				autohintTtfFont(font, function(hintedFont) {
					// ttfautohint is optional
					if (hintedFont) {
						font = hintedFont;
					}
					fonts.ttf = font;
					done(font);
				});
			});
		},

		woff: function(done) {
			getFont('ttf', function(ttfFont) {
				var font = ttf2woff(new Uint8Array(ttfFont), {});
				font = new Buffer(font.buffer);
				fonts.woff = font;
				done(font);
			});
		},

		woff2: function(done) {
			// Will be converted from TTF later
			done();
		},

		eot: function(done) {
			getFont('ttf', function(ttfFont) {
				var font = ttf2eot(new Uint8Array(ttfFont));
				font = new Buffer(font.buffer);
				fonts.eot = font;
				done(font);
			});
		}
	};

	var steps = [];

	// Font types
	var typesToGenerate = o.types.slice();
	if (o.types.indexOf('woff2') !== -1 && o.types.indexOf('ttf'  === -1)) typesToGenerate.push('ttf');
	typesToGenerate.forEach(function(type) {
		steps.push(createFontWriter(type));
	});

	// Run!
	async.waterfall(steps, allDone);

	function getFont(type, done) {
		if (fonts[type]) {
			done(fonts[type]);
		}
		else {
			generators[type](done);
		}
	}

	function createFontWriter(type) {
		return function(done) {
			getFont(type, function(font) {
				fs.writeFileSync(wf.getFontPath(o, type), font);
				done();
			});
		};
	}

	function svgFilesToStreams(files, done) {
		async.map(files, function(file, fileDone) {
			var svg = fs.readFileSync(file, 'utf8');
			var svgo = new SVGO();
			try {
				svgo.optimize(svg, function(res) {
					var idx = files.indexOf(file);
					var name = o.glyphs[idx];
					var stream = new MemoryStream(res.data, {
						writable: false
					});
					fileDone(null, {
						codepoint: o.codepoints[name],
						name: name,
						stream: stream
					});
				});
			}
			catch(err) {
				fileDone(err);
			}
		}, function(err, streams) {
			if (err) {
				logger.error('Can’t simplify SVG file with SVGO.\n\n' + err);
				allDone(false);
			}
			else {
				done(streams);
			}
		});
	}

	function autohintTtfFont(font, done) {
		var tempDir = temp.mkdirSync();
		var originalFilepath = path.join(tempDir, 'font.ttf');
		var hintedFilepath = path.join(tempDir, 'hinted.ttf');

		if (!o.autoHint){
			done(false);
			return;
		}
		// Save original font to temporary directory
		fs.writeFileSync(originalFilepath, font);

		// Run ttfautohint
		var args = [
			'ttfautohint',
			'--symbol',
			'--fallback-script=latn',
			'--windows-compatibility',
			'--no-info',
			originalFilepath,
			hintedFilepath
		];

		exec(args, function(err, out, code) {
			if (err) {
				if (err instanceof Error) {
					if (err.code === 'ENOENT') {
						logger.verbose('Hinting skipped, ttfautohint not found.');
						done(false);
						return;
					}
					err = err.message;
				}
				logger.error('Can’t run ttfautohint.\n\n' + err);
				done(false);
				return;
			}

			// Read hinted font back
			var hintedFont = fs.readFileSync(hintedFilepath);
			done(hintedFont);
		});
	}

};
<|endoftext|>


<commit_before>var marked = require("marked")
var renderer = new marked.Renderer()
var accents = {
  "à" : "a",
  "á" : "a",
  "â" : "a",
  "ã" : "a",
  "ä" : "a",
  "å" : "a",
  "æ" : "ae",
  "ç" : "c",
  "è" : "e",
  "é" : "e",
  "ê" : "e",
  "ë" : "e",
  "ì" : "i",
  "í" : "i",
  "î" : "i",
  "ï" : "i",
  "ñ" : "n",
  "ò" : "o",
  "ó" : "o",
  "ô" : "o",
  "õ" : "o",
  "ö" : "o",
  "ø" : "o",
  "ù" : "u",
  "ú" : "u",
  "û" : "u",
  "ü" : "u",
  "ý" : "y"
}
var accentsRE = RegExp("(" + Object.keys(accents).join("|") + ")", "g")

marked.setOptions({
  highlight : function(code){
    return require("highlight.js").highlightAuto(code).value;
  },
  renderer : renderer,
  smartypants : true
})

renderer.heading = function(text, level){
  var escaped = text.toLowerCase()
    .replace(accentsRE, function(i){
      return accents[i]
    })
    .replace(/\W+/g, "-")

  return "<h" + level + " id=\"" + escaped + "\">" +
    "#" +
    text + "</h" + level + ">"
}

module.exports = marked
<commit_msg>Removing html links in anchors generation<commit_after>var marked = require("marked")
var renderer = new marked.Renderer()
var accents = {
  "à" : "a",
  "á" : "a",
  "â" : "a",
  "ã" : "a",
  "ä" : "a",
  "å" : "a",
  "æ" : "ae",
  "ç" : "c",
  "è" : "e",
  "é" : "e",
  "ê" : "e",
  "ë" : "e",
  "ì" : "i",
  "í" : "i",
  "î" : "i",
  "ï" : "i",
  "ñ" : "n",
  "ò" : "o",
  "ó" : "o",
  "ô" : "o",
  "õ" : "o",
  "ö" : "o",
  "ø" : "o",
  "ù" : "u",
  "ú" : "u",
  "û" : "u",
  "ü" : "u",
  "ý" : "y"
}
var accentsRE = RegExp("(" + Object.keys(accents).join("|") + ")", "g")

marked.setOptions({
  highlight : function(code){
    return require("highlight.js").highlightAuto(code).value;
  },
  renderer : renderer,
  smartypants : true
})

renderer.heading = function(text, level){
  var escaped = text.toLowerCase()
    .replace(accentsRE, function(i){
      return accents[i]
    })
    .replace(/(<\/?a[^>]*>)/ig, "") // strip html links in anchors
    .replace(/\W+/g, "-")

  return "<h" + level + " id=\"" + escaped + "\">" +
    "#" +
    text + "</h" + level + ">"
}

module.exports = marked
<|endoftext|>


<commit_before>import test from 'ava'
import spbus from '../../src'

test('spbus should has the auth() method', t => {
  t.is(typeof spbus.auth, 'function')
})

test('auth() should return an error when not receive a token', async t => {
  const error = await t.throws(spbus.auth)
  t.is(error.message, 'O token é obrigatório para autenticação.')
})
<commit_msg>Improve auth test<commit_after>import test from 'ava'
import spbus from '../../src'

test('spbus should has the auth method', t => {
  t.is(typeof spbus.auth, 'function')
})

test('should return error when receive no token', t => {
  t.throws(spbus.auth)
})
<|endoftext|>


<commit_before>/*
 * grunt-install-init
 * https://github.com/twolfson/grunt-install-init
 *
 * Copyright (c) 2012 Todd Wolfson
 * Licensed under the MIT license.
 */

var win32 = process.platform === 'win32',
    path = require('path'),
    gruntRetro = require('grunt-retro');
module.exports = function(grunt) {
  // Load and bind grunt-retro
  grunt = gruntRetro(grunt);

  // TODO: Should this be included in grunt-retro?
  // Fallback userDir manually
  grunt.file.userDir = grunt.file.userDir || function () {
    var dirpath = path.join.apply(path, arguments);
    var homepath = process.env[win32 ? 'USERPROFILE' : 'HOME'];
    return path.resolve(homepath, '.grunt-init', dirpath);
  };

  // Please see the grunt documentation for more information regarding task and
  // helper creation: https://github.com/gruntjs/grunt/blob/master/docs/toc.md

  // ==========================================================================
  // TASKS
  // ==========================================================================

  var path = require('path'),
      rimraf = require('rimraf');
  grunt.registerMultiTask('install-init', 'Installs a grunt init template to the grunt user directory', function() {
    var file = this.file,
        baseDir = file.src,
        pluginName = file.dest,
        srcJS = baseDir + '/' + pluginName + '.js',
        src = baseDir + '/' + pluginName + '/**',
        dest = grunt.file.userDir('tasks/init');

    // Grab the srcDirs and srcFiles
    var srcDirs = grunt.file.expandDirs({'dot': true}, src),
        srcFiles = grunt.file.expandFiles({'dot': true}, src);

    // Add srcJS to the head of the srcFiles
    srcFiles.unshift(srcJS);

    // Empty each of the srcDirs
    srcDirs.forEach(function (srcDir) {
      // Remove baseDir from srcDir
      srcDir = srcDir.replace(baseDir, '');

      // Empty the directory
      var destDir = path.join(dest, srcDir);
      rimraf.sync(destDir);
    });

    // Re-create each of the srcDirs
    srcDirs.forEach(function (srcDir) {
      // Remove baseDir from srcDir
      srcDir = srcDir.replace(baseDir, '');

      // Re-creaate the directory
      var destDir = path.join(dest, srcDir);
      grunt.file.mkdir(destDir);
    });

    // Copy over each of the srcFiles
    srcFiles.forEach(function (srcFile) {
      // Remove baseDir from srcFile
      var _srcFile = srcFile.replace(baseDir, '');

      // Copy over the file
      var destFile = path.join(dest, _srcFile);
      grunt.file.copy(srcFile, destFile);
    });

    // Fail task if errors were logged.
    if (this.errorCount) { return false; }

    // Otherwise, print a success message.
    grunt.log.writeln('Grunt init template "' + pluginName + '" installed.');
  });

  // ==========================================================================
  // HELPERS
  // ==========================================================================

  grunt.registerHelper('install-init', function (content) {
    return content;
  });

};
<commit_msg>Caught error but too damn tired<commit_after>/*
 * grunt-install-init
 * https://github.com/twolfson/grunt-install-init
 *
 * Copyright (c) 2012 Todd Wolfson
 * Licensed under the MIT license.
 */

var win32 = process.platform === 'win32',
    path = require('path'),
    gruntRetro = require('grunt-retro');
module.exports = function(grunt) {
  // Load and bind grunt-retro
  grunt = gruntRetro(grunt);

  // TODO: Should this be included in grunt-retro?
  // Fallback userDir manually
  grunt.file.userDir = grunt.file.userDir || function () {
    var dirpath = path.join.apply(path, arguments);
    var homepath = process.env[win32 ? 'USERPROFILE' : 'HOME'];
    return path.resolve(homepath, '.grunt-init', dirpath);
  };

  // Please see the grunt documentation for more information regarding task and
  // helper creation: https://github.com/gruntjs/grunt/blob/master/docs/toc.md

  // ==========================================================================
  // TASKS
  // ==========================================================================

  var path = require('path'),
      rimraf = require('rimraf');
  grunt.registerMultiTask('install-init', 'Installs a grunt init template to the grunt user directory', function() {
    var file = this.file,
        baseDir = file.src,
        pluginName = file.dest,
        srcJS = baseDir + '/' + pluginName + '.js',
        src = baseDir + '/' + pluginName + '/**',
        dest = grunt.file.userDir('tasks/init');

    // Grab the srcDirs and srcFiles
    // TODO: Issue is expandDirs is taking single param and not double -- need to use object.exted
    var srcDirs = grunt.file.expandDirs({'dot': true}, src),
        srcFiles = grunt.file.expandFiles({'dot': true}, src);
    console.log('0');

    // Add srcJS to the head of the srcFiles
    srcFiles.unshift(srcJS);
    console.log('1');

    // Empty each of the srcDirs
    srcDirs.forEach(function (srcDir) {
      // Remove baseDir from srcDir
      srcDir = srcDir.replace(baseDir, '');

      // Empty the directory
      var destDir = path.join(dest, srcDir);
      rimraf.sync(destDir);
    });

    console.log('2');

    // Re-create each of the srcDirs
    srcDirs.forEach(function (srcDir) {
      // Remove baseDir from srcDir
      srcDir = srcDir.replace(baseDir, '');

      // Re-creaate the directory
      var destDir = path.join(dest, srcDir);
      grunt.file.mkdir(destDir);
    });
    console.log('3');

    // Copy over each of the srcFiles
    srcFiles.forEach(function (srcFile) {
      // Remove baseDir from srcFile
      var _srcFile = srcFile.replace(baseDir, '');

      // Copy over the file
      var destFile = path.join(dest, _srcFile);
      grunt.file.copy(srcFile, destFile);
    });
    console.log('4');

    // Fail task if errors were logged.
    if (this.errorCount) { return false; }
    console.log('5');

    // Otherwise, print a success message.
    grunt.log.writeln('Grunt init template "' + pluginName + '" installed.');
  });

  // ==========================================================================
  // HELPERS
  // ==========================================================================

  grunt.registerHelper('install-init', function (content) {
    return content;
  });

};
<|endoftext|>


<commit_before>(function($){
	// This function adds noise to the background-color attribute of an element
	$.fn.noisy = function(options) {
		return this.each(function() {
	 		var canvas = document.createElement('canvas'),
	 		    ctx = canvas.getContext("2d");

	 		options = $.extend({}, $.fn.noisy.defaults, options);

	 		// Use fallback image if canvas isn't supported
	 		if ((options.fallbackImage !== undefined) && !ctx) {
	 			$(this).css('background-image', options.fallbackImage);
	 			return;
	 		}

	 		canvas.width = canvas.height = options.tileSize;
	 		var imgData = ctx.createImageData(canvas.width, canvas.height);

	 		var rand = function(min, max) {
	 			return Math.floor(Math.random()*(max-min)+min);
	 		};

	 		// Add black and white pixels at random positions to the canvas
	 		for (var i = 0; i < options.noise * Math.pow(options.tileSize, 2); i++) {
	 			var x = rand(0, canvas.width),
	 			    y = rand(0, canvas.height),
	 			    index = (x + y * imgData.width) * 4;

	 			var randColorChannel = rand(0, 255);
	 			imgData.data[index  ] = randColorChannel;                                           // red
	 			imgData.data[index+1] = options.monochromatic ? randColorChannel : rand(0, 255);    // green
	 			imgData.data[index+2] = options.monochromatic ? randColorChannel : rand(0, 255);    // blue
	 			imgData.data[index+3] = rand(0, 255*options.maxNoiseOpacity);                       // alpha
	 		}

	 		ctx.putImageData(imgData, 0, 0);
	 		$(this).css('background', 'url(' + canvas.toDataURL('image/png') + ')' + options.backgroundColor);
		});
	};

	$.fn.noisy.defaults = {
		// How many percent of the image that is filled with noise,
		//   represented by a number between 0 and 1 inclusive
		noise:              1,

		// The width and height of the background image in pixels
		tileSize:           200,

		// An empty string is transparent
		backgroundColor:    '',

		// The maximum noise particle opacity,
		//   represented by a number between 0 and 1 inclusive
		maxNoiseOpacity:    20/255,

		// A string linking to the image used if there's no canvas support
		fallbackImage:      undefined,

		// Specifies wheter the particles are grayscale or colorful
		monochromatic:      false
	};
})(jQuery);<commit_msg>Syntactic inconsistencies and potential bug fix<commit_after>(function($){
	// This function adds noise to the background-color attribute of an element
	$.fn.noisy = function(options) {
		return this.each(function() {
	 		var canvas = document.createElement('canvas'),
	 		    ctx = canvas.getContext("2d");

	 		options = $.extend({}, $.fn.noisy.defaults, options);

	 		// Use fallback image if canvas isn't supported
	 		if (!ctx && (options.fallbackImage !== undefined) && (options.fallbackImage !== '')) {
	 			$(this).css('background-image', options.fallbackImage);
	 			return;
	 		}

	 		canvas.width = canvas.height = options.tileSize;
	 		var imgData = ctx.createImageData(canvas.width, canvas.height);

	 		var rand = function(min, max) {
	 			return Math.floor(Math.random()*(max-min)+min);
	 		};

	 		// Add black and white pixels at random positions to the canvas
	 		for (var i = 0; i < options.noise * Math.pow(options.tileSize, 2); i++) {
	 			var x = rand(0, canvas.width),
	 			    y = rand(0, canvas.height),
	 			    index = (x + y * imgData.width) * 4;

	 			var randColorChannel = rand(0, 255);
	 			imgData.data[index  ] = randColorChannel;                                           // red
	 			imgData.data[index+1] = options.monochromatic ? randColorChannel : rand(0, 255);    // green
	 			imgData.data[index+2] = options.monochromatic ? randColorChannel : rand(0, 255);    // blue
	 			imgData.data[index+3] = rand(0, 255 * options.maxNoiseOpacity);                     // alpha
	 		}

	 		ctx.putImageData(imgData, 0, 0);
	 		$(this).css('background', 'url(' + canvas.toDataURL('image/png') + ')' + options.backgroundColor);
		});
	};

	$.fn.noisy.defaults = {
		// How many percent of the image that is filled with noise,
		//   represented by a number between 0 and 1 inclusive
		noise:              0.9,

		// The width and height of the background image in pixels
		tileSize:           200,

		// An empty string is transparent
		backgroundColor:    '',

		// The maximum noise particle opacity,
		//   represented by a number between 0 and 1 inclusive
		maxNoiseOpacity:    0.08,

		// A string linking to the image used if there's no canvas support
		fallbackImage:      '',

		// Specifies wheter the particles are grayscale or colorful
		monochromatic:      false
	};
})(jQuery);<|endoftext|>


<commit_before>import { puid } from 'brisky-struct'

const cache = (client, struct, hub, stamp) => {
  if (!client.cache) client.cache = {}
//  client.cache[puid(struct)] = hub.contextKey === undefined ? stamp
//    : stamp | contextBit
  client.cache[puid(struct)] = stamp
}

//  const isCached = (client, struct, hub, stamp) => client.cache &&
//    client.cache[puid(struct)] === hub.contextKey === undefined ? stamp
//    : stamp | contextBit

const isCached = (client, struct, stamp) => client.cache &&
  client.cache[puid(struct)] === stamp

export { cache, isCached }
<commit_msg>negative fix<commit_after>import { puid } from 'brisky-struct'

const contextBit = 0b100000000000000000000000000000000000000000000
const contextBitNegative = 0b1100000000000000000000000000000000000000000000

const cache = (client, struct, hub, stamp) => {
  if (!client.cache) client.cache = {}
  client.cache[puid(struct)] = hub.contextKey === undefined ? stamp
    : stamp < 0 ? -stamp | contextBitNegative
    : stamp | contextBit
}

const isCached = (client, struct, hub, stamp) => client.cache &&
  client.cache[puid(struct)] === hub.contextKey === undefined ? stamp
    : stamp < 0 ? -stamp | contextBitNegative
    : stamp | contextBit

export { cache, isCached }
<|endoftext|>


<commit_before>var assert = require("assert");
var fs = require('fs');
var path = require('path');
var weixin = require('./../../index');
var validator = require('validator');
var errors = require("web-errors").errors;
var config = require("./../../lib/validate-config/index").pay;

describe("Weixin Pay Test", function () {

  it('should be able to handle unifiedOrder pay result', function (done) {
    var back;

    //Case FAIL
    back = {
      return_code: 'FAIL',
      return_msg: 'FAIL'
    };

    back = weixin.auth.pay.prepare(back);
    weixin.api.pay.handle(function (error, data) {
      assert.equal(true, error === errors.ERROR);
      assert.equal(true, data === 'FAIL');
    }, false, back, config.unified.receiving);

    //Case SUCCESS
    back = {
      return_code: 'SUCCESS',
      return_msg: '',
      result_code: 'SUCCESS',
      trade_type: ['JSAPI', 'NATIVE', 'APP'][0],
      prepay_id: '10303033',
      code_url: 'http://github.com'
    };

    back = weixin.auth.pay.prepare(back);
    weixin.api.pay.handle(function (error, data) {
      assert.equal(true, error === errors.SUCCESS);
      assert.equal(true, back.trade_type === data.trade_type);
      assert.equal(true, back.prepay_id === data.prepay_id);
      assert.equal(true, back.code_url === data.code_url);
      if (data.code_url) {
        validator.isURL(data.code_url);
      }

    }, false, back, config.unified.receiving);
    done();
  });

  if (fs.existsSync(path.resolve(__dirname, '../private/merchant.js')) &&
    fs.existsSync(path.resolve(__dirname, '../private/prod.js')) &&
    fs.existsSync(path.resolve(__dirname, '../private/data.js'))) {
    var merchantConfig = require('../private/merchant');
    var prod = require("../private/prod");
    var data = require('../private/data').unifiedOrder;

    it('should be able to send a unifiedOrder pay request', function (done) {
      var path = require('path');
      weixin.auth.init(prod);

      weixin.auth.merchant.init(merchantConfig.id, merchantConfig.key, merchantConfig.ssl);

      weixin.api.pay.unifiedOrder(data, function (error, data) {
        assert.equal(true, error === errors.SUCCESS);
        assert.equal(true, data.trade_type === 'JSAPI');
        assert.equal(true, data.prepay_id !== null);
        done();
      });
    });

    it('should be able to query orders', function (done) {
      done();
    });
  }
});
<commit_msg>updated jshint<commit_after>var assert = require("assert");
var fs = require('fs');
var path = require('path');
var weixin = require('./../../index');
var validator = require('validator');
var errors = require("web-errors").errors;
var config = require("./../../lib/validate-config/index").pay;

describe("Weixin Pay Test", function () {

  it('should be able to handle unifiedOrder pay result', function (done) {
    var back;

    //Case FAIL
    back = {
      return_code: 'FAIL',
      return_msg: 'FAIL'
    };

    back = weixin.auth.pay.prepare(back);
    weixin.api.pay.handle(function (error, data) {
      assert.equal(true, error === errors.ERROR);
      assert.equal(true, data === 'FAIL');
    }, false, back, config.unified.receiving);

    //Case SUCCESS
    back = {
      return_code: 'SUCCESS',
      return_msg: '',
      result_code: 'SUCCESS',
      trade_type: ['JSAPI', 'NATIVE', 'APP'][0],
      prepay_id: '10303033',
      code_url: 'http://github.com'
    };

    back = weixin.auth.pay.prepare(back);
    weixin.api.pay.handle(function (error, data) {
      assert.equal(true, error === errors.SUCCESS);
      assert.equal(true, back.trade_type === data.trade_type);
      assert.equal(true, back.prepay_id === data.prepay_id);
      assert.equal(true, back.code_url === data.code_url);
      if (data.code_url) {
        validator.isURL(data.code_url);
      }

    }, false, back, config.unified.receiving);
    done();
  });

  if (fs.existsSync(path.resolve(__dirname, '../private/merchant.js')) &&
    fs.existsSync(path.resolve(__dirname, '../private/prod.js')) &&
    fs.existsSync(path.resolve(__dirname, '../private/data.js'))) {
    var merchantConfig = require('../private/merchant');
    var prod = require("../private/prod");
    var data = require('../private/data').unifiedOrder;

    it('should be able to send a unifiedOrder pay request', function (done) {
      weixin.auth.init(prod);
      weixin.auth.merchant.init(merchantConfig.id, merchantConfig.key, merchantConfig.ssl);

      weixin.api.pay.unifiedOrder(data, function (error, data) {
        assert.equal(true, error === errors.SUCCESS);
        assert.equal(true, data.trade_type === 'JSAPI');
        assert.equal(true, data.prepay_id !== null);
        done();
      });
    });

    it('should be able to query orders', function (done) {
      done();
    });
  }
});
<|endoftext|>


<commit_before>import semver from 'semver'
import { expect } from 'chai'
import { node, nodeSync, npm, npmSync, gitSync } from '../../src/util/spawnTool'

import { NODE_REQUIRED, NPM_REQUIRED } from '../../src/constants'

describe('util', () => {
    describe('spawnTool', () => {
        it('node', done => {
            node(['--version'], {}, err => {
                expect(err).to.equal(0)
                done()
            })
        })
        it('nodeSync', () => {
            const result = nodeSync(['--version'], {})
            expect(semver.satisfies(result, NODE_REQUIRED)).to.equal(true)
        })

        it('npm', done => {
            npm(['--version'], {}, err => {
                expect(err).to.equal(0)
                done()
            })
        })
        it('npmSync', () => {
            const result = npmSync(['--version'], {})
            expect(semver.satisfies(result, NPM_REQUIRED)).to.equal(true)
        })

        if (process.platform !== 'win32') {
            it('gitSync', () => {
                const result = gitSync(['rev-parse', '--verify', 'master'])
                expect(
                    typeof result === 'string' && result.length > 0
                ).to.equal(true)
            })
        }
    })
})
<commit_msg>Updating a test<commit_after>import semver from 'semver'
import { expect } from 'chai'
import { node, nodeSync, npm, gitSync } from '../../src/util/spawnTool'

import { NODE_REQUIRED } from '../../src/constants'

describe('util', () => {
    describe('spawnTool', () => {
        it('node', done => {
            node(['--version'], {}, err => {
                expect(err).to.equal(0)
                done()
            })
        })
        it('nodeSync', () => {
            const result = nodeSync(['--version'], {})
            expect(semver.satisfies(result, NODE_REQUIRED)).to.equal(true)
        })

        it('npm', done => {
            npm(['--version'], {}, err => {
                expect(err).to.equal(0)
                done()
            })
        })

        if (process.platform !== 'win32') {
            it('gitSync', () => {
                const result = gitSync(['rev-parse', '--verify', 'master'])
                expect(
                    typeof result === 'string' && result.length > 0
                ).to.equal(true)
            })
        }
    })
})
<|endoftext|>


<commit_before>
const titoHelper = () => {

  const isSponsor = (ticketType) => {
    if(ticketType.toUpperCase().includes('Expo'.toUpperCase()))
      return true
    else
      return false
  }

  const isSpeaker = (ticketType) => {
    if(ticketType.toUpperCase().includes('Counselor'.toUpperCase()))
      return true
    else
      return false
  }

  const isSponsoredSpeaker = (ticketType) => {
    if(ticketType.toUpperCase().includes('Sponsored Counselor'.toUpperCase()))
      return true
    else
      return false
  }

  //youth shirts had a few sizes in that we're wrong. We need to adjust.
  const bitShiftShirts = (shirt) => {
    let newShirt = shirt

    if (shirt.toUpperCase().includes('Youth XS'.toUpperCase()))
      newShirt = 'Youth S     [6-8]' // hardcoded based on what is in tito, has to match

    if (shirt.toUpperCase().includes('Youth XL'.toUpperCase()))
      newShirt = 'Men X-Small' // hardcoded based on what is in tito, has to match

    return newShirt
  }

  return { isSponsor, isSpeaker, isSponsoredSpeaker, bitShiftShirts}
}

module.exports = titoHelper
<commit_msg>updated shirt mappings<commit_after>
const titoHelper = () => {

  const isSponsor = (ticketType) => {
    if(ticketType.toUpperCase().includes('Expo'.toUpperCase()))
      return true
    else
      return false
  }

  const isSpeaker = (ticketType) => {
    if(ticketType.toUpperCase().includes('Counselor'.toUpperCase()))
      return true
    else
      return false
  }

  const isSponsoredSpeaker = (ticketType) => {
    if(ticketType.toUpperCase().includes('Sponsored Counselor'.toUpperCase()))
      return true
    else
      return false
  }

  //youth shirts had a few sizes in that we're wrong. We need to adjust.
  const bitShiftShirts = (shirt) => {
    let newShirt = shirt

    //adjust for odd mismatches
    if (shirt.toUpperCase().includes('MEN XX-LARGE'))
      newShirt = 'Men 2X-Large';
    else if (shirt.toUpperCase().includes('WOMEN\'S-CUT XX-LARGE'))
      newShirt = 'Women\'s-cut 2X-Large'
    else if (shirt.toUpperCase().includes('MEN XXX-LARGE'))
      newShirt = 'Men 3X-Large'

    //adjust for lack of availability from vendor
    if (shirt.toUpperCase().includes('Youth XS'.toUpperCase()))
      newShirt = 'Youth S     [6-8]' // hardcoded based on what is in tito, has to match
    if (shirt.toUpperCase().includes('Women\'s-cut 4X-Large'.toUpperCase()))
      newShirt = 'Men 4X-Large' // hardcoded based on what is in tito, has to match

    return newShirt
  }

  return { isSponsor, isSpeaker, isSponsoredSpeaker, bitShiftShirts}
}

module.exports = titoHelper
<|endoftext|>


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

const fs = require('fs')
const program = require('commander')
const meta = require(`${process.cwd()}/package.json`)

try {
  let ver = meta.version

  program
    .version(meta.version)
    .arguments('<version>')
    .action(v => { ver = v })
    .parse(process.argv)

  if (ver) {
    let fileStr = fs.readFileSync('./bower.json', 'utf-8')
    const verPtn = /(.*"version"\s*:\s*")[^"]*(".*)/

    if (verPtn.test(fileStr)) {
      fileStr = fileStr.replace(verPtn, '$1' + ver + '$2')
      fs.writeFileSync('./bower.json', fileStr, 'utf-8')
    }
  }

  process.exit(0)
} catch (err) {
  console.error(err)
  process.exit(1)
}
<commit_msg>Add a way to specify which files to target<commit_after>#!/usr/bin/env node

const fs = require('fs')
const program = require('commander')
const meta = require(`${process.cwd()}/package.json`)
const path = require('path')

const DEFAULT_FILE_PATHS = [ './bower.json' ]

function collect (val, memo) {
  memo.push(val)
  return memo
}

function updateVersion (file, ver) {
  const resolved = path.resolve(file)

  let fileStr = fs.readFileSync(resolved, 'utf-8')
  const verPtn = /(.*"version"\s*:\s*")[^"]*(".*)/

  if (verPtn.test(fileStr)) {
    fileStr = fileStr.replace(verPtn, '$1' + ver + '$2')
    fs.writeFileSync(resolved, fileStr, 'utf-8')
  }
}

try {
  let ver = meta.version

  program
    .version(meta.version)
    .option('-f, --file [file]', 'The target file to sync. Defaults to bower.json', collect, DEFAULT_FILE_PATHS)
    .arguments('<version>')
    .action(v => { ver = v })
    .parse(process.argv)

  if (ver) {
    program.file.forEach(
      file => updateVersion(file, ver)
    )
  }

  process.exit(0)
} catch (err) {
  console.error(err)
  process.exit(1)
}
<|endoftext|>