<commit_before>var allStyles = [];
var currentStyle = null;
var appliedStyles = [];

// create menu labels
document.getElementById('menuTitle').innerHTML = chrome.i18n.getMessage('extName');
document.getElementById('includeStyle').innerHTML = chrome.i18n.getMessage('includeStyle');
document.getElementById('styleLabel').innerHTML = chrome.i18n.getMessage('styleLabel');
document.getElementById('applyStyle').innerHTML = chrome.i18n.getMessage('applyStyle');
document.getElementById('editStyles').innerHTML = chrome.i18n.getMessage('editStyles');
document.getElementById('savePageLabel').innerHTML = chrome.i18n.getMessage('savePage');
document.getElementById('saveSelectionLabel').innerHTML = chrome.i18n.getMessage('saveSelection');
document.getElementById('pageChapterLabel').innerHTML = chrome.i18n.getMessage('pageChapter');
document.getElementById('selectionChapterLabel').innerHTML = chrome.i18n.getMessage('selectionChapter');
document.getElementById('editChapters').innerHTML = chrome.i18n.getMessage('editChapters');
document.getElementById('waitMessage').innerHTML = chrome.i18n.getMessage('waitMessage');

getStyles(createStyleList);

function createStyleList(styles) {
    allStyles = styles;
    chrome.tabs.query({'active': true}, function (tabs) {
        var currentUrl = tabs[0].url;

        if (!styles || styles.length === 0) {
            return;
        }

        var existingStyles = document.getElementById('allStylesList');
        var foundMatchingUrl = false;

        while (existingStyles.hasChildNodes() && existingStyles.childElementCount > 1) {
            existingStyles.removeChild(existingStyles.lastChild);
        }

        // if multiple URL regexes match, select the longest one
        var allMatchingStyles = [];

        for (var i = 0; i < styles.length; i++) {
            var listItem = document.createElement('option');
            listItem.id = 'option_' + i;
            listItem.className = 'cssEditor-chapter-item';
            listItem.value = 'option_' + i;
            listItem.innerText = styles[i].title;

            currentUrl = currentUrl.replace(/(http[s]?:\/\/|www\.)/i, '').toLowerCase();
            var styleUrl = styles[i].url;
            var styleUrlRegex = null;

            try {
                styleUrlRegex =  new RegExp(styleUrl, 'i');
            } catch (e) {
            }

            if (styleUrlRegex && styleUrlRegex.test(currentUrl)) {
                allMatchingStyles.push({
                    index: i,
                    length: styleUrl.length
                });
            }

            existingStyles.appendChild(listItem);
        }

        if (allMatchingStyles.length >= 1) {
            allMatchingStyles.sort(function (a, b) {
                return b.length - a.length;
            });
            var selStyle = allMatchingStyles[0];
            var tmpListItemElem = document.getElementById('option_' + selStyle.index);
            tmpListItemElem.selected = 'selected';
            currentStyle = styles[selStyle.index];
            setCurrentStyle(currentStyle);
        }
    });
}

function createIncludeStyle(data) {
    var includeStyleCheck = document.getElementById('includeStyleCheck');
    includeStyleCheck.checked = data;
    document.getElementById('customStyles').style.display = !includeStyleCheck.checked ? 'none' : 'block';
}

getIncludeStyle(createIncludeStyle);

document.getElementById('includeStyleCheck').onclick = function () {
    var includeStyleCheck = document.getElementById('includeStyleCheck');
    setIncludeStyle(includeStyleCheck.checked);
    document.getElementById('customStyles').style.display = !includeStyleCheck.checked ? 'none' : 'block';
}

document.getElementById('allStylesList').onchange = function () {
    var newValue = this.value;
    newValue = newValue.replace('option_', '');
    newValue = parseInt(newValue);
    currentStyle = allStyles[newValue];
    setCurrentStyle(currentStyle);
}

document.getElementById("applyStyle").onclick = function() {
    chrome.tabs.query({
        currentWindow: true,
        active: true
    }, function(tab) {
        if (!currentStyle || !currentStyle.style) {
            return;
        }
        chrome.tabs.insertCSS(tab[0].id, {code: currentStyle.style});
        appliedStyles.push(currentStyle);
    });
}

document.getElementById("editStyles").onclick = function() {

    if (document.getElementById('cssEditor-Modal')) {
        return;
    }

    chrome.tabs.query({
        currentWindow: true,
        active: true
    }, function(tab) {

        chrome.tabs.executeScript(tab[0].id, {file: '/jquery.js'});
        chrome.tabs.executeScript(tab[0].id, {file: '/utils.js'});
        chrome.tabs.executeScript(tab[0].id, {file: '/filesaver.js'});
        chrome.tabs.executeScript(tab[0].id, {file: '/jszip.js'});
        chrome.tabs.executeScript(tab[0].id, {file: '/jszip-utils.js'});
        chrome.tabs.executeScript(tab[0].id, {file: '/saveEbook.js'});
        chrome.tabs.insertCSS(tab[0].id, {file: '/cssEditor.css'});

        chrome.tabs.executeScript(tab[0].id, {
            file: '/cssEditor.js'
        });

         window.close();
    });
};

document.getElementById("editChapters").onclick = function() {

    if (document.getElementById('chapterEditor-Modal')) {
        return;
    }

    chrome.tabs.query({
        currentWindow: true,
        active: true
    }, function(tab) {

        chrome.tabs.executeScript(tab[0].id, {file: '/jquery.js'});
        chrome.tabs.executeScript(tab[0].id, {file: '/utils.js'});
        chrome.tabs.executeScript(tab[0].id, {file: '/filesaver.js'});
        chrome.tabs.executeScript(tab[0].id, {file: '/jszip.js'});
        chrome.tabs.executeScript(tab[0].id, {file: '/jszip-utils.js'});
        chrome.tabs.executeScript(tab[0].id, {file: '/saveEbook.js'});
        chrome.tabs.executeScript(tab[0].id, {file: '/jquery-sortable.js'});
        chrome.tabs.insertCSS(tab[0].id, {file: '/chapterEditor.css'});

        chrome.tabs.executeScript(tab[0].id, {
            file: '/chapterEditor.js'
        });

         window.close();
    });
};

function dispatch(action, justAddToBuffer) {
    document.getElementById('busy').style.display = 'block';
    if (!justAddToBuffer) {
        removeEbook();
    }
    chrome.tabs.query({
        currentWindow: true,
        active: true
    }, function(tab) {
        chrome.tabs.sendMessage(tab[0].id, {
            type: 'echo'
        }, function(response) {
            if (!response) {
                chrome.tabs.executeScript(tab[0].id, {file: '/jquery.js'});
                chrome.tabs.executeScript(tab[0].id, {file: '/utils.js'});
                chrome.tabs.executeScript(tab[0].id, {file: '/filesaver.js'});
                chrome.tabs.executeScript(tab[0].id, {file: '/jszip.js'});
                chrome.tabs.executeScript(tab[0].id, {file: '/jszip-utils.js'});
                chrome.tabs.executeScript(tab[0].id, {file: '/pure-parser.js'});
                chrome.tabs.executeScript(tab[0].id, {file: '/cssjson.js'});

                // chrome.tabs.insertCSS(tab[0].id, {code: currentStyle.style}); // TODO remove from here --- see if it still works

                chrome.tabs.executeScript(tab[0].id, {
                    file: 'extractHtml.js'
                }, function() {
                    sendMessage(tab[0].id, action, justAddToBuffer, appliedStyles);
                });
            } else if (response.echo) {
                sendMessage(tab[0].id, action, justAddToBuffer, appliedStyles);
            }
        });
    });
}

function sendMessage(tabId, action, justAddToBuffer, appliedStyles) {
    chrome.tabs.sendMessage(tabId, {
        type: action,
        appliedStyles: appliedStyles
    }, function(response) {
        if (response.length === 0) {
            if (justAddToBuffer) {
                alert('Cannot add an empty selection as chapter!');
            } else {
                alert('Cannot generate the eBook from an empty selection!');
            }
            window.close();
        }
        if (!justAddToBuffer) {
            buildEbook([response]);
        } else {
            getEbookPages(function (allPages) {
                allPages.push(response);
                saveEbookPages(allPages);
                window.close();
            });
        }
        setTimeout(function () {
            document.getElementById('busy').style.display = 'none';
        }, 500);
    });
}

document.getElementById('savePage').onclick = function() {
    dispatch('extract-page', false);
};

document.getElementById('saveSelection').onclick = function() {
    dispatch('extract-selection', false);
};

document.getElementById('pageChapter').onclick = function() {
    dispatch('extract-page', true);
};

document.getElementById('selectionChapter').onclick = function() {
    dispatch('extract-selection', true);
};

// get all shortcuts and display them in the menuTitle
chrome.commands.getAll((commands) => {
    for (let command of commands) {
        if (command.name === 'save-page') {
            document.getElementById('savePageShortcut').appendChild(document.createTextNode(command.shortcut));
        } else if (command.name === 'save-selection') {
            document.getElementById('saveSelectionShortcut').appendChild(document.createTextNode(command.shortcut));
        } else if (command.name === 'add-page') {
            document.getElementById('pageChapterShortcut').appendChild(document.createTextNode(command.shortcut));
        } else if (command.name === 'add-selection') {
            document.getElementById('selectionChapterShortcut').appendChild(document.createTextNode(command.shortcut));
        }

    }
    console.log('ALl commands', commands);
})
<commit_msg>display shortcuts in the menu<commit_after>var allStyles = [];
var currentStyle = null;
var appliedStyles = [];

// create menu labels
document.getElementById('menuTitle').innerHTML = chrome.i18n.getMessage('extName');
document.getElementById('includeStyle').innerHTML = chrome.i18n.getMessage('includeStyle');
document.getElementById('styleLabel').innerHTML = chrome.i18n.getMessage('styleLabel');
document.getElementById('applyStyle').innerHTML = chrome.i18n.getMessage('applyStyle');
document.getElementById('editStyles').innerHTML = chrome.i18n.getMessage('editStyles');
document.getElementById('savePageLabel').innerHTML = chrome.i18n.getMessage('savePage');
document.getElementById('saveSelectionLabel').innerHTML = chrome.i18n.getMessage('saveSelection');
document.getElementById('pageChapterLabel').innerHTML = chrome.i18n.getMessage('pageChapter');
document.getElementById('selectionChapterLabel').innerHTML = chrome.i18n.getMessage('selectionChapter');
document.getElementById('editChapters').innerHTML = chrome.i18n.getMessage('editChapters');
document.getElementById('waitMessage').innerHTML = chrome.i18n.getMessage('waitMessage');

getStyles(createStyleList);

function createStyleList(styles) {
    allStyles = styles;
    chrome.tabs.query({'active': true}, function (tabs) {
        var currentUrl = tabs[0].url;

        if (!styles || styles.length === 0) {
            return;
        }

        var existingStyles = document.getElementById('allStylesList');
        var foundMatchingUrl = false;

        while (existingStyles.hasChildNodes() && existingStyles.childElementCount > 1) {
            existingStyles.removeChild(existingStyles.lastChild);
        }

        // if multiple URL regexes match, select the longest one
        var allMatchingStyles = [];

        for (var i = 0; i < styles.length; i++) {
            var listItem = document.createElement('option');
            listItem.id = 'option_' + i;
            listItem.className = 'cssEditor-chapter-item';
            listItem.value = 'option_' + i;
            listItem.innerText = styles[i].title;

            currentUrl = currentUrl.replace(/(http[s]?:\/\/|www\.)/i, '').toLowerCase();
            var styleUrl = styles[i].url;
            var styleUrlRegex = null;

            try {
                styleUrlRegex =  new RegExp(styleUrl, 'i');
            } catch (e) {
            }

            if (styleUrlRegex && styleUrlRegex.test(currentUrl)) {
                allMatchingStyles.push({
                    index: i,
                    length: styleUrl.length
                });
            }

            existingStyles.appendChild(listItem);
        }

        if (allMatchingStyles.length >= 1) {
            allMatchingStyles.sort(function (a, b) {
                return b.length - a.length;
            });
            var selStyle = allMatchingStyles[0];
            var tmpListItemElem = document.getElementById('option_' + selStyle.index);
            tmpListItemElem.selected = 'selected';
            currentStyle = styles[selStyle.index];
            setCurrentStyle(currentStyle);
        }
    });
}

function createIncludeStyle(data) {
    var includeStyleCheck = document.getElementById('includeStyleCheck');
    includeStyleCheck.checked = data;
    document.getElementById('customStyles').style.display = !includeStyleCheck.checked ? 'none' : 'block';
}

getIncludeStyle(createIncludeStyle);

document.getElementById('includeStyleCheck').onclick = function () {
    var includeStyleCheck = document.getElementById('includeStyleCheck');
    setIncludeStyle(includeStyleCheck.checked);
    document.getElementById('customStyles').style.display = !includeStyleCheck.checked ? 'none' : 'block';
}

document.getElementById('allStylesList').onchange = function () {
    var newValue = this.value;
    newValue = newValue.replace('option_', '');
    newValue = parseInt(newValue);
    currentStyle = allStyles[newValue];
    setCurrentStyle(currentStyle);
}

document.getElementById("applyStyle").onclick = function() {
    chrome.tabs.query({
        currentWindow: true,
        active: true
    }, function(tab) {
        if (!currentStyle || !currentStyle.style) {
            return;
        }
        chrome.tabs.insertCSS(tab[0].id, {code: currentStyle.style});
        appliedStyles.push(currentStyle);
    });
}

document.getElementById("editStyles").onclick = function() {

    if (document.getElementById('cssEditor-Modal')) {
        return;
    }

    chrome.tabs.query({
        currentWindow: true,
        active: true
    }, function(tab) {

        chrome.tabs.executeScript(tab[0].id, {file: '/jquery.js'});
        chrome.tabs.executeScript(tab[0].id, {file: '/utils.js'});
        chrome.tabs.executeScript(tab[0].id, {file: '/filesaver.js'});
        chrome.tabs.executeScript(tab[0].id, {file: '/jszip.js'});
        chrome.tabs.executeScript(tab[0].id, {file: '/jszip-utils.js'});
        chrome.tabs.executeScript(tab[0].id, {file: '/saveEbook.js'});
        chrome.tabs.insertCSS(tab[0].id, {file: '/cssEditor.css'});

        chrome.tabs.executeScript(tab[0].id, {
            file: '/cssEditor.js'
        });

         window.close();
    });
};

document.getElementById("editChapters").onclick = function() {

    if (document.getElementById('chapterEditor-Modal')) {
        return;
    }

    chrome.tabs.query({
        currentWindow: true,
        active: true
    }, function(tab) {

        chrome.tabs.executeScript(tab[0].id, {file: '/jquery.js'});
        chrome.tabs.executeScript(tab[0].id, {file: '/utils.js'});
        chrome.tabs.executeScript(tab[0].id, {file: '/filesaver.js'});
        chrome.tabs.executeScript(tab[0].id, {file: '/jszip.js'});
        chrome.tabs.executeScript(tab[0].id, {file: '/jszip-utils.js'});
        chrome.tabs.executeScript(tab[0].id, {file: '/saveEbook.js'});
        chrome.tabs.executeScript(tab[0].id, {file: '/jquery-sortable.js'});
        chrome.tabs.insertCSS(tab[0].id, {file: '/chapterEditor.css'});

        chrome.tabs.executeScript(tab[0].id, {
            file: '/chapterEditor.js'
        });

         window.close();
    });
};

function dispatch(action, justAddToBuffer) {
    document.getElementById('busy').style.display = 'block';
    if (!justAddToBuffer) {
        removeEbook();
    }
    chrome.tabs.query({
        currentWindow: true,
        active: true
    }, function(tab) {
        chrome.tabs.sendMessage(tab[0].id, {
            type: 'echo'
        }, function(response) {
            if (!response) {
                chrome.tabs.executeScript(tab[0].id, {file: '/jquery.js'});
                chrome.tabs.executeScript(tab[0].id, {file: '/utils.js'});
                chrome.tabs.executeScript(tab[0].id, {file: '/filesaver.js'});
                chrome.tabs.executeScript(tab[0].id, {file: '/jszip.js'});
                chrome.tabs.executeScript(tab[0].id, {file: '/jszip-utils.js'});
                chrome.tabs.executeScript(tab[0].id, {file: '/pure-parser.js'});
                chrome.tabs.executeScript(tab[0].id, {file: '/cssjson.js'});

                // chrome.tabs.insertCSS(tab[0].id, {code: currentStyle.style}); // TODO remove from here --- see if it still works

                chrome.tabs.executeScript(tab[0].id, {
                    file: 'extractHtml.js'
                }, function() {
                    sendMessage(tab[0].id, action, justAddToBuffer, appliedStyles);
                });
            } else if (response.echo) {
                sendMessage(tab[0].id, action, justAddToBuffer, appliedStyles);
            }
        });
    });
}

function sendMessage(tabId, action, justAddToBuffer, appliedStyles) {
    chrome.tabs.sendMessage(tabId, {
        type: action,
        appliedStyles: appliedStyles
    }, function(response) {
        if (response.length === 0) {
            if (justAddToBuffer) {
                alert('Cannot add an empty selection as chapter!');
            } else {
                alert('Cannot generate the eBook from an empty selection!');
            }
            window.close();
        }
        if (!justAddToBuffer) {
            buildEbook([response]);
        } else {
            getEbookPages(function (allPages) {
                allPages.push(response);
                saveEbookPages(allPages);
                window.close();
            });
        }
        setTimeout(function () {
            document.getElementById('busy').style.display = 'none';
        }, 500);
    });
}

document.getElementById('savePage').onclick = function() {
    dispatch('extract-page', false);
};

document.getElementById('saveSelection').onclick = function() {
    dispatch('extract-selection', false);
};

document.getElementById('pageChapter').onclick = function() {
    dispatch('extract-page', true);
};

document.getElementById('selectionChapter').onclick = function() {
    dispatch('extract-selection', true);
};

// get all shortcuts and display them in the menuTitle
chrome.commands.getAll((commands) => {
    for (let command of commands) {
        if (command.name === 'save-page') {
            document.getElementById('savePageShortcut').appendChild(document.createTextNode(command.shortcut));
        } else if (command.name === 'save-selection') {
            document.getElementById('saveSelectionShortcut').appendChild(document.createTextNode(command.shortcut));
        } else if (command.name === 'add-page') {
            document.getElementById('pageChapterShortcut').appendChild(document.createTextNode(command.shortcut));
        } else if (command.name === 'add-selection') {
            document.getElementById('selectionChapterShortcut').appendChild(document.createTextNode(command.shortcut));
        }
    }
})
<|endoftext|>


<commit_before>import * as ValueMax from "./ValueMax.js";

export { decoder_FromArrayBuffer, decoder_FromUint8Array };

const base64String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

// Mapping table for base64 (as Uint8) to index.
let table_base64_Uint8_to_index = new Array(256); // Faster than using Uint8Array().
{
  // For all non-base64 codes, using value greater than 63 (i.e. impossible base64) for identifying them.
  for (let i = 0; i < table_base64_Uint8_to_index.length; ++i)
    table_base64_Uint8_to_index[ i ] = 255;

  // For all legal base64 codes, using value between [0, 63].
  {
    for (let i = 0; i < base64String.length; ++i) {
      let codePoint = base64String.codePointAt(i);
      table_base64_Uint8_to_index[ codePoint ] = i;
    }

    // Support decoding URL-safe base64 strings, as Node.js does.
    // See: https://en.wikipedia.org/wiki/Base64#URL_applications
    table_base64_Uint8_to_index['-'.charCodeAt(0)] = 62;
    table_base64_Uint8_to_index['_'.charCodeAt(0)] = 63;
  }
}

/**
 * Got a generator for Base64 decoding from an array of Base64 encoded string.
 * Join the string array, convert to Uint8Array, decode as Base64, result in another Uint8Array.
 *
 * @param {string[]} sourceBase64EncodedStringArray
 *   Every element of the array is a string whose content is base64 encoded text.
 *
 * @param {TextEncoder} textEncoder
 *   This TextEncoder will convert string to Uint8Array so that the Base64 decoder can work.
 *
 * @param {Uint32} skipLineCount
 *   Skip how many lines in the source before decoding.
 *
 * @param {ValueMax.Percentage.Aggregate} progressRoot
 *   Return this when every time yield. Usually, this is the root container of the progressParent.
 *
 * @param {ValueMax.Percentage.Aggregate} progressParent
 *   This should be progressRoot or some descendant of progressRoot. A new progressToAdvance will be created and added to
 * progressParent. The progressToAdvance will be increased when every time advanced.
 *
 * @param {Uint32} suspendByteCount
 *   Everytime so many bytes decoded, yield for releasing CPU time (and reporting progress).
 *   Default is 1024 bytes.
 *
 * @yields {ValueMax.Percentage.Aggregate}
 *   Yield ( value = progressRoot ) when ( done = false ).
 *
 * @yields {Uint8Array}
 *   Yield ( value = decoded data as Uint8Array ) when ( done = true ).
 */
function *decoder_FromStringArray(
  sourceBase64EncodedStringArray, textEncoder, skipLineCount, progressRoot, progressParent, suspendByteCount ) {

//!!! ...unfinished... ( progressToAdvance / progressParent ) ratio seems too large...

//   let progressToAdvance = new ValueMax.Percentage.Concrete( 2 );
//   progressParent.addChild( progressToAdvance );

  let base64EncodedStringLong = sourceBase64EncodedStringArray.join( "" );
//  ++progressToAdvance.accumulation;

  let base64EncodedUint8Array = textEncoder.encode( base64EncodedStringLong );
//   ++progressToAdvance.accumulation;

  let base64Decoder = decoder_FromUint8Array(
    base64EncodedUint8Array, skipLineCount, progressRoot, progressParent, suspendByteCount );

  let base64DecodedUint8Array = yield *base64Decoder;
  return base64DecodedUint8Array;
}

/**
 * Got a generator for Base64 decoding from an ArrayBufffer.
 *
 * @param {ArrayBuffer} sourceBase64ArrayBuffer
 *   The input base64 data as ArrayBuffer. If the last bytes not enough 4 bytes, they will be discarded (will
 * not be decoded). If an input byte is not a legal base64 code (i.e. not A..Z, a..z, 0..9, +, /, -, _), the
 * byte will be skipped (as if it does not exist). So the input bytes can be separated by new line character
 * (which will be skipped and ignored).
 *
 * @param {Uint32} skipLineCount
 *   Skip how many lines in the source before decoding.
 *
 * @param {ValueMax.Percentage.Aggregate} progressRoot
 *   Return this when every time yield. Usually, this is the root container of the progressParent.
 *
 * @param {ValueMax.Percentage.Aggregate} progressParent
 *   This should be progressRoot or some descendant of progressRoot. A new progressToAdvance will be created and added to
 * progressParent. The progressToAdvance will be increased when every time advanced.
 *
 * @param {Uint32} suspendByteCount
 *   Everytime so many bytes decoded, yield for releasing CPU time (and reporting progress).
 *   Default is 1024 bytes.
 *
 * @yields {ValueMax.Percentage.Aggregate}
 *   Yield ( value = progressRoot ) when ( done = false ).
 *
 * @yields {Uint8Array}
 *   Yield ( value = decoded data as Uint8Array ) when ( done = true ).
 */
function decoder_FromArrayBuffer(
  sourceBase64ArrayBuffer, skipLineCount, progressRoot, progressParent, suspendByteCount ) {

  let sourceBase64Uint8Array = new Uint8Array( sourceBase64ArrayBuffer );
  let decoder = decoder_FromUint8Array( sourceBase64Uint8Array, skipLineCount, progressRoot, progressParent, suspendByteCount );
  return decoder;
}

/**
 * Got a generator for Base64 decoding from an Uint8Array.
 *
 * @param {Uint8Array} sourceBase64Uint8Array
 *   The input base64 data as Uint8Array. If the last bytes not enough 4 bytes, they will be discarded (will
 * not be decoded). If an input byte is not a legal base64 code (i.e. not A..Z, a..z, 0..9, +, /, -, _), the
 * byte will be skipped (as if it does not exist). So the input bytes can be separated by new line character
 * (which will be skipped and ignored).
 *
 * @param {Uint32} skipLineCount
 *   Skip how many lines in the source before decoding.
 *
 * @param {ValueMax.Percentage.Aggregate} progressRoot
 *   Return this when every time yield. Usually, this is the root container of the progressParent.
 *
 * @param {ValueMax.Percentage.Aggregate} progressParent
 *   This should be progressRoot or some descendant of progressRoot. A new progressToAdvance will be created and added to
 * progressParent. The progressToAdvance will be increased when every time advanced.
 *
 * @param {Uint32} suspendByteCount
 *   Everytime so many bytes decoded, yield for releasing CPU time (and reporting progress).
 *   Default is 1024 bytes.
 *
 * @yields {ValueMax.Percentage.Aggregate}
 *   Yield ( value = progressRoot ) when ( done = false ).
 *
 * @yields {Uint8Array}
 *   Yield ( value = decoded data as Uint8Array ) when ( done = true ).
 */
function* decoder_FromUint8Array(
  sourceBase64Uint8Array, skipLineCount, progressRoot, progressParent, suspendByteCount ) {

  // 0. Initialize.

  // If undefined or null or negative or zero or less than 1, set to default.
  // Note: Bitwising OR with zero is for converting to integer (if it is undefined or null).
  if ((suspendByteCount | 0) <= 0)
    suspendByteCount = 1024;

  let sourceByteLength = sourceBase64Uint8Array.length;
  let sourceBytes = sourceBase64Uint8Array;

  // Initialize progress.
  let progressToAdvance = new ValueMax.Percentage.Concrete( sourceByteLength );
  progressParent.addChild( progressToAdvance );

  // It is important that the nextYieldAccumulation is not greater than source length, so that
  // it can be used as boundary checking to reduce checking times and increase performance.
  let nextYieldAccumulation = Math.min(sourceByteLength, progressToAdvance.accumulation + suspendByteCount);

  // 1. Skip specified lines.
  {
    let skippedLineCount = 0;

    while (progressToAdvance.accumulation < sourceByteLength) {
      if (skippedLineCount >= skipLineCount)
        break;                 // Already skip enough lines.

      // (This inner loop combines both source and yield boundary checking. Reducing checking to increase performance.)
      while (progressToAdvance.accumulation < nextYieldAccumulation) {
        if (skippedLineCount >= skipLineCount)
          break;                 // Already skip enough lines.

        let rawByte = sourceBytes[ progressToAdvance.accumulation++ ];

        if (13 == rawByte) {     // "\r" (carriage return; CR)
          ++skippedLineCount;    // One line is skipped.

          // If a LF follows a CR, it is considered as CRLF sequence and viewed as the same one line.
          //
          // Note: It may exceed the nextYieldAccumulation boundary. But should not exceed sourceByteLength.
          if (   (progressToAdvance.accumulation < sourceByteLength)
              && (10 == sourceBytes[ progressToAdvance.accumulation ])
             ) {
            ++progressToAdvance.accumulation; // Skip it.
          }

        } else {
          if (10 == rawByte)    // "\n" (new line; LF)
            ++skippedLineCount; // One line is skipped.
        }
      }

      // Every suspendByteCount, release CPU time (and report progress).
      if (progressToAdvance.accumulation >= nextYieldAccumulation) {
        nextYieldAccumulation = Math.min(sourceByteLength, progressToAdvance.accumulation + suspendByteCount);
        yield progressRoot;
      }
    }
  }

  // 2. Decode.

  // Decoding base64 will result in a shorter data (about 75% (= 3 / 4) in size).
  let possibleBase64ByteLength = (sourceByteLength - progressToAdvance.accumulation);  // Forget the skipped lines.
  let targetByteLength = Math.ceil(possibleBase64ByteLength * 0.75);
  let targetArrayBuffer = new ArrayBuffer( targetByteLength );
  let targetBytes = new Uint8Array( targetArrayBuffer );

  let resultByteCount = 0;  // Accumulate the real result byte count.

  {
    while (progressToAdvance.accumulation < sourceByteLength) {

      nextYieldLoop:

      // (This inner loop combines both source and yield boundary checking. Reducing checking to increase performance.)
      while (progressToAdvance.accumulation < nextYieldAccumulation) {

        // Extract 4 source bytes. (A decode unit consists of 4 base64 encoded source bytes.)
        //
        // Although it is verbose to loop unrolling manually, it is far more faster
        // to use 4 local variables than use a 4-element normal array. (Note: the
        // 4-element normal array is far more faster than a Uint8Array() again).

        let encoded_0;
        do {
          // Note: It may exceed the nextYieldAccumulation boundary. But should not exceed sourceByteLength.
          if (progressToAdvance.accumulation >= sourceByteLength)
            break nextYieldLoop; // Decoding is done. (Ignore last non-4-bytes.)

          encoded_0 = table_base64_Uint8_to_index[ sourceBytes[ progressToAdvance.accumulation++ ] ];
        } while (255 === encoded_0);

        let encoded_1;
        do {
          // Note: It may exceed the nextYieldAccumulation boundary. But should not exceed sourceByteLength.
          if (progressToAdvance.accumulation >= sourceByteLength)
            break nextYieldLoop; // Decoding is done. (Ignore last non-4-bytes.)

          encoded_1 = table_base64_Uint8_to_index[ sourceBytes[ progressToAdvance.accumulation++ ] ];
        } while (255 === encoded_1);

        let encoded_2;
        do {
          // Note: It may exceed the nextYieldAccumulation boundary. But should not exceed sourceByteLength.
          if (progressToAdvance.accumulation >= sourceByteLength)
            break nextYieldLoop; // Decoding is done. (Ignore last non-4-bytes.)

          encoded_2 = table_base64_Uint8_to_index[ sourceBytes[ progressToAdvance.accumulation++ ] ];
        } while (255 === encoded_2);

        let encoded_3;
        do {
          // Note: It may exceed the nextYieldAccumulation boundary. But should not exceed sourceByteLength.
          if (progressToAdvance.accumulation >= sourceByteLength)
            break nextYieldLoop; // Decoding is done. (Ignore last non-4-bytes.)

          encoded_3 = table_base64_Uint8_to_index[ sourceBytes[ progressToAdvance.accumulation++ ] ];
        } while (255 === encoded_3);

        targetBytes[resultByteCount++] =  (encoded_0       << 2) | (encoded_1 >> 4);
        targetBytes[resultByteCount++] = ((encoded_1 & 15) << 4) | (encoded_2 >> 2);
        targetBytes[resultByteCount++] = ((encoded_2 &  3) << 6) | (encoded_3 & 63);
      }

      // Every suspendByteCount, release CPU time (and report progress).
      if (progressToAdvance.accumulation >= nextYieldAccumulation) {
        nextYieldAccumulation = Math.min(sourceByteLength, progressToAdvance.accumulation + suspendByteCount);
        yield progressRoot;
      }

    }
  }

  // 3. Result.

  // The resultBytes is a sub-range of target buffer.
  //
  // Because the source may have some non-base64 codes which will be ignored,
  // the length of resultBytes may be less than targetBytes.
  let resultBytes = new Uint8Array( targetArrayBuffer, 0, resultByteCount );

  yield progressRoot; // Report the progress has been done (100%).

  return resultBytes;
}
<commit_msg>Update Base64ToUint8Array.js<commit_after>import * as ValueMax from "./ValueMax.js";

export { decoder_FromArrayBuffer, decoder_FromUint8Array };

const base64String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

// Mapping table for base64 (as Uint8) to index.
let table_base64_Uint8_to_index = new Array(256); // Faster than using Uint8Array().
{
  // For all non-base64 codes, using value greater than 63 (i.e. impossible base64) for identifying them.
  for (let i = 0; i < table_base64_Uint8_to_index.length; ++i)
    table_base64_Uint8_to_index[ i ] = 255;

  // For all legal base64 codes, using value between [0, 63].
  {
    for (let i = 0; i < base64String.length; ++i) {
      let codePoint = base64String.codePointAt(i);
      table_base64_Uint8_to_index[ codePoint ] = i;
    }

    // Support decoding URL-safe base64 strings, as Node.js does.
    // See: https://en.wikipedia.org/wiki/Base64#URL_applications
    table_base64_Uint8_to_index['-'.charCodeAt(0)] = 62;
    table_base64_Uint8_to_index['_'.charCodeAt(0)] = 63;
  }
}

/**
 * Got a generator for Base64 decoding from an array of Base64 encoded string.
 * Join the string array, convert to Uint8Array, decode as Base64, result in another Uint8Array.
 *
 * @param {string[]} sourceBase64EncodedStringArray
 *   Every element of the array is a string whose content is base64 encoded text.
 *
 * @param {TextEncoder} textEncoder
 *   This TextEncoder will convert string to Uint8Array so that the Base64 decoder can work.
 *
 * @param {Uint32} skipLineCount
 *   Skip how many lines in the source before decoding.
 *
 * @param {ValueMax.Percentage.Aggregate} progressRoot
 *   Return this when every time yield. Usually, this is the root container of the progressParent.
 *
 * @param {ValueMax.Percentage.Aggregate} progressParent
 *   This should be progressRoot or some descendant of progressRoot. A new progressToAdvance will be created and added to
 * progressParent. The progressToAdvance will be increased when every time advanced.
 *
 * @param {Uint32} suspendByteCount
 *   Everytime so many bytes decoded, yield for releasing CPU time (and reporting progress).
 *   Default is 1024 bytes.
 *
 * @yields {ValueMax.Percentage.Aggregate}
 *   Yield ( value = progressRoot ) when ( done = false ).
 *
 * @yields {Uint8Array}
 *   Yield ( value = decoded data as Uint8Array ) when ( done = true ).
 */
function *decoder_FromStringArray(
  sourceBase64EncodedStringArray, textEncoder, skipLineCount, progressRoot, progressParent, suspendByteCount ) {

  // The ( progressToAdvance / progressParent ) ratio (50%) seems a little too large. But it is
  // reasonable in fact because the string Array.join() and TextEncoder.encode() both will scan
  // all input text. This is just the same as the Base64 decoder (i.e. scanning all input text).

  let progressToAdvance = new ValueMax.Percentage.Concrete( 2 ); // For this function (i.e. Array.join() and TextEncoder.encode()).
  let progressParentNew = new ValueMax.Percentage.Aggregate();  // For next sub function (i.e. Base64 decoder).

  progressParent.addChild( progressToAdvance );   // 50%
  progressParent.addChild( progressParentNew );  // 50%

  let base64EncodedStringLong = sourceBase64EncodedStringArray.join( "" );
  ++progressToAdvance.accumulation; // 25%
  yield progressRoot;

  let base64EncodedUint8Array = textEncoder.encode( base64EncodedStringLong );
  ++progressToAdvance.accumulation; // 25%
  yield progressRoot;

  let base64Decoder = decoder_FromUint8Array(
    base64EncodedUint8Array, skipLineCount, progressRoot, progressParentNew, suspendByteCount );

  let base64DecodedUint8Array = yield *base64Decoder;
  return base64DecodedUint8Array;
}

/**
 * Got a generator for Base64 decoding from an ArrayBufffer.
 *
 * @param {ArrayBuffer} sourceBase64ArrayBuffer
 *   The input base64 data as ArrayBuffer. If the last bytes not enough 4 bytes, they will be discarded (will
 * not be decoded). If an input byte is not a legal base64 code (i.e. not A..Z, a..z, 0..9, +, /, -, _), the
 * byte will be skipped (as if it does not exist). So the input bytes can be separated by new line character
 * (which will be skipped and ignored).
 *
 * @param {Uint32} skipLineCount
 *   Skip how many lines in the source before decoding.
 *
 * @param {ValueMax.Percentage.Aggregate} progressRoot
 *   Return this when every time yield. Usually, this is the root container of the progressParent.
 *
 * @param {ValueMax.Percentage.Aggregate} progressParent
 *   This should be progressRoot or some descendant of progressRoot. A new progressToAdvance will be created and added to
 * progressParent. The progressToAdvance will be increased when every time advanced.
 *
 * @param {Uint32} suspendByteCount
 *   Everytime so many bytes decoded, yield for releasing CPU time (and reporting progress).
 *   Default is 1024 bytes.
 *
 * @yields {ValueMax.Percentage.Aggregate}
 *   Yield ( value = progressRoot ) when ( done = false ).
 *
 * @yields {Uint8Array}
 *   Yield ( value = decoded data as Uint8Array ) when ( done = true ).
 */
function decoder_FromArrayBuffer(
  sourceBase64ArrayBuffer, skipLineCount, progressRoot, progressParent, suspendByteCount ) {

  let sourceBase64Uint8Array = new Uint8Array( sourceBase64ArrayBuffer );
  let decoder = decoder_FromUint8Array( sourceBase64Uint8Array, skipLineCount, progressRoot, progressParent, suspendByteCount );
  return decoder;
}

/**
 * Got a generator for Base64 decoding from an Uint8Array.
 *
 * @param {Uint8Array} sourceBase64Uint8Array
 *   The input base64 data as Uint8Array. If the last bytes not enough 4 bytes, they will be discarded (will
 * not be decoded). If an input byte is not a legal base64 code (i.e. not A..Z, a..z, 0..9, +, /, -, _), the
 * byte will be skipped (as if it does not exist). So the input bytes can be separated by new line character
 * (which will be skipped and ignored).
 *
 * @param {Uint32} skipLineCount
 *   Skip how many lines in the source before decoding.
 *
 * @param {ValueMax.Percentage.Aggregate} progressRoot
 *   Return this when every time yield. Usually, this is the root container of the progressParent.
 *
 * @param {ValueMax.Percentage.Aggregate} progressParent
 *   This should be progressRoot or some descendant of progressRoot. A new progressToAdvance will be created and added to
 * progressParent. The progressToAdvance will be increased when every time advanced.
 *
 * @param {Uint32} suspendByteCount
 *   Everytime so many bytes decoded, yield for releasing CPU time (and reporting progress).
 *   Default is 1024 bytes.
 *
 * @yields {ValueMax.Percentage.Aggregate}
 *   Yield ( value = progressRoot ) when ( done = false ).
 *
 * @yields {Uint8Array}
 *   Yield ( value = decoded data as Uint8Array ) when ( done = true ).
 */
function* decoder_FromUint8Array(
  sourceBase64Uint8Array, skipLineCount, progressRoot, progressParent, suspendByteCount ) {

  // 0. Initialize.

  // If undefined or null or negative or zero or less than 1, set to default.
  // Note: Bitwising OR with zero is for converting to integer (if it is undefined or null).
  if ((suspendByteCount | 0) <= 0)
    suspendByteCount = 1024;

  let sourceByteLength = sourceBase64Uint8Array.length;
  let sourceBytes = sourceBase64Uint8Array;

  // Initialize progress.
  let progressToAdvance = new ValueMax.Percentage.Concrete( sourceByteLength );
  progressParent.addChild( progressToAdvance );

  // It is important that the nextYieldAccumulation is not greater than source length, so that
  // it can be used as boundary checking to reduce checking times and increase performance.
  let nextYieldAccumulation = Math.min(sourceByteLength, progressToAdvance.accumulation + suspendByteCount);

  // 1. Skip specified lines.
  {
    let skippedLineCount = 0;

    while (progressToAdvance.accumulation < sourceByteLength) {
      if (skippedLineCount >= skipLineCount)
        break;                 // Already skip enough lines.

      // (This inner loop combines both source and yield boundary checking. Reducing checking to increase performance.)
      while (progressToAdvance.accumulation < nextYieldAccumulation) {
        if (skippedLineCount >= skipLineCount)
          break;                 // Already skip enough lines.

        let rawByte = sourceBytes[ progressToAdvance.accumulation++ ];

        if (13 == rawByte) {     // "\r" (carriage return; CR)
          ++skippedLineCount;    // One line is skipped.

          // If a LF follows a CR, it is considered as CRLF sequence and viewed as the same one line.
          //
          // Note: It may exceed the nextYieldAccumulation boundary. But should not exceed sourceByteLength.
          if (   (progressToAdvance.accumulation < sourceByteLength)
              && (10 == sourceBytes[ progressToAdvance.accumulation ])
             ) {
            ++progressToAdvance.accumulation; // Skip it.
          }

        } else {
          if (10 == rawByte)    // "\n" (new line; LF)
            ++skippedLineCount; // One line is skipped.
        }
      }

      // Every suspendByteCount, release CPU time (and report progress).
      if (progressToAdvance.accumulation >= nextYieldAccumulation) {
        nextYieldAccumulation = Math.min(sourceByteLength, progressToAdvance.accumulation + suspendByteCount);
        yield progressRoot;
      }
    }
  }

  // 2. Decode.

  // Decoding base64 will result in a shorter data (about 75% (= 3 / 4) in size).
  let possibleBase64ByteLength = (sourceByteLength - progressToAdvance.accumulation);  // Forget the skipped lines.
  let targetByteLength = Math.ceil(possibleBase64ByteLength * 0.75);
  let targetArrayBuffer = new ArrayBuffer( targetByteLength );
  let targetBytes = new Uint8Array( targetArrayBuffer );

  let resultByteCount = 0;  // Accumulate the real result byte count.

  {
    while (progressToAdvance.accumulation < sourceByteLength) {

      nextYieldLoop:

      // (This inner loop combines both source and yield boundary checking. Reducing checking to increase performance.)
      while (progressToAdvance.accumulation < nextYieldAccumulation) {

        // Extract 4 source bytes. (A decode unit consists of 4 base64 encoded source bytes.)
        //
        // Although it is verbose to loop unrolling manually, it is far more faster
        // to use 4 local variables than use a 4-element normal array. (Note: the
        // 4-element normal array is far more faster than a Uint8Array() again).

        let encoded_0;
        do {
          // Note: It may exceed the nextYieldAccumulation boundary. But should not exceed sourceByteLength.
          if (progressToAdvance.accumulation >= sourceByteLength)
            break nextYieldLoop; // Decoding is done. (Ignore last non-4-bytes.)

          encoded_0 = table_base64_Uint8_to_index[ sourceBytes[ progressToAdvance.accumulation++ ] ];
        } while (255 === encoded_0);

        let encoded_1;
        do {
          // Note: It may exceed the nextYieldAccumulation boundary. But should not exceed sourceByteLength.
          if (progressToAdvance.accumulation >= sourceByteLength)
            break nextYieldLoop; // Decoding is done. (Ignore last non-4-bytes.)

          encoded_1 = table_base64_Uint8_to_index[ sourceBytes[ progressToAdvance.accumulation++ ] ];
        } while (255 === encoded_1);

        let encoded_2;
        do {
          // Note: It may exceed the nextYieldAccumulation boundary. But should not exceed sourceByteLength.
          if (progressToAdvance.accumulation >= sourceByteLength)
            break nextYieldLoop; // Decoding is done. (Ignore last non-4-bytes.)

          encoded_2 = table_base64_Uint8_to_index[ sourceBytes[ progressToAdvance.accumulation++ ] ];
        } while (255 === encoded_2);

        let encoded_3;
        do {
          // Note: It may exceed the nextYieldAccumulation boundary. But should not exceed sourceByteLength.
          if (progressToAdvance.accumulation >= sourceByteLength)
            break nextYieldLoop; // Decoding is done. (Ignore last non-4-bytes.)

          encoded_3 = table_base64_Uint8_to_index[ sourceBytes[ progressToAdvance.accumulation++ ] ];
        } while (255 === encoded_3);

        targetBytes[resultByteCount++] =  (encoded_0       << 2) | (encoded_1 >> 4);
        targetBytes[resultByteCount++] = ((encoded_1 & 15) << 4) | (encoded_2 >> 2);
        targetBytes[resultByteCount++] = ((encoded_2 &  3) << 6) | (encoded_3 & 63);
      }

      // Every suspendByteCount, release CPU time (and report progress).
      if (progressToAdvance.accumulation >= nextYieldAccumulation) {
        nextYieldAccumulation = Math.min(sourceByteLength, progressToAdvance.accumulation + suspendByteCount);
        yield progressRoot;
      }

    }
  }

  // 3. Result.

  // The resultBytes is a sub-range of target buffer.
  //
  // Because the source may have some non-base64 codes which will be ignored,
  // the length of resultBytes may be less than targetBytes.
  let resultBytes = new Uint8Array( targetArrayBuffer, 0, resultByteCount );

  yield progressRoot; // Report the progress has been done (100%).

  return resultBytes;
}
<|endoftext|>


<commit_before>var _ = require('lodash');
var fs  =  require('fs');
var path = require('path');
var wrench = require('wrench');
var child_process = require('child_process');
var Q = require("q");

var requirejs = require("requirejs");

var exec = function(command, options) {

    var deferred = Q.defer();
    var childProcess;

    var args = Array.prototype.slice.call(arguments, 0);
    args.push(function(err, stdout, stderr) {
        if (err) {
            err.message += command + ' (exited with error code ' + err.code + ')';
            err.stdout = stdout;
            err.stderr = stderr;
            deferred.reject(err);
        }
        else {
            deferred.resolve({
                childProcess: childProcess,
                stdout: stdout,
                stderr: stderr
            });
        }
    });

    childProcess = child_process.exec.apply(child_process, args);

    return deferred.promise;
}

var Addon = function(_rootPath, options) {
    this.root = _rootPath;
    this.infos = {};
    this.options = _.defaults(options || {}, {
        blacklist: [],
        logger: console
    });
    var logger = this.options.logger;

    // Load addon infos from an addon's directory
    this.load = Q.fbind(function(addonDir) {
        addonDir = addonDir || this.root;

        // Check addon
        var packageJsonFile = path.join(addonDir, "package.json");
        if (!fs.existsSync(packageJsonFile)) {
            throw new Error("No 'package.json' in this repository: "+addonDir);
        }
        this.infos = JSON.parse(fs.readFileSync(packageJsonFile, 'utf8'));
        if (!this.isValid()) {
            throw new Error("Invalid 'package.json' file: "+packageJsonFile);
        }

        return this;
    });

    // Valid the addon
    this.isValid = function() {
        return !(!this.infos.name || !this.infos.version
            || (!this.infos.main && !this.infos.client && !this.infos.client.main));
    };

    // Test is symlink
    this.isSymlink = function() {
        return Q.nfcall(fs.lstat, this.root).then(function(stats) {
            return stats.isSymbolicLink();
        });
    };

    // Check if an addon is client side
    this.isClientside = function() {
        return (this.infos.client && this.infos.client.main);
    };

    // Check if an addon is node addon
    this.isNode = function() {
        return (this.infos.main);
    };

    // Check if an addon is already optimized
    this.isOptmized = function() {
        return fs.existsSync(path.join(this.root, "addon-built.js"));
    };

    // Check if an addon has node dependencies
    this.hasDependencies = function() {
        return _.size(this.infos.dependencies || {}) > 0;
    };

    // Check if an addon has npm scripts
    this.hasScripts = function() {
        return _.size(this.infos.scripts || {}) > 0;
    };

    // Check if the addon is blacklisted
    this.isBlacklisted = function() {
        return _.contains(this.options.blacklist, this.infos.name);
    };

    // Optimize the addon
    this.optimizeClient = function(force) {
        var that = this;
        var d = Q.defer();

        if (!this.isClientside()
        || (this.isOptmized() && !force)) {
            return Q(this);
        }

        // Base directory for the addon
        var addonPath = this.root;

        // Path to the require-tools
        var requiretoolsPath = path.resolve(__dirname, "require-tools");

        // Base main
        var main = this.infos.client.main;

        // Output file
        var output = path.resolve(addonPath, "addon-built.js");

        // Build config
        var optconfig = {
            'baseUrl': addonPath,
            'name': main,
            'out': output,
            'logLevel': 4, // silent
            'paths': {
                'require-tools': requiretoolsPath
            },
            'optimize': "uglify",
            'map': {
                '*': {
                    'css': "require-tools/css/css",
                    'less': "require-tools/less/less",
                    'text': "require-tools/text/text"
                }
            }
        };
        // Run optimization
        logger.log("Optimizing", this.infos.name);
        return Q.nfcall(fs.unlink, output).fail(function() {
            return Q();
        }).then(function() {
            var d = Q.defer();

            requirejs.optimize(optconfig, function(resp) {
                d.resolve(resp);
            }, function(err) {
                d.reject(err);
            });

            return d.promise;
        }).then(function() {
            logger.log("Finished", that.infos.name, "optimization");
            return Q(that);
        }, function(err) {
            logger.error("error for optimization of", that.infos.name);
            logger.error("options=", optconfig);
            logger.error(err);
            return Q.reject(err);
        });
    };

    // Install dependencies for this addon
    this.installDependencies = function(force) {
        var that = this;
        if (!force) {
            if (!this.hasDependencies() && !this.hasScripts()) {
                return Q(this);
            }
        }
        logger.log("Install dependencies for", this.root);
        return exec("cd "+this.root+" && npm install .").then(function() {
            return Q(that);
        });
    };

    // Transfer to a new root directory
    this.transfer = function(newRoot, options) {
        var that = this;
        options = _.defaults({}, options || {}, {
            forceDelete: true,
            excludeHiddenUnix: false,
            preserveFiles: false
        });

        var addonPath = path.join(newRoot, this.infos.name);
        return Q.nfcall(wrench.copyDirRecursive, this.root, addonPath, options).then(function() {
            var addon = new Addon(addonPath, that.options);
            return addon.load();
        });
    };

    // Symlink this addons
    this.symlink = function(newRoot) {
        var that = this;
        var addonPath = path.join(newRoot, this.infos.name);
        return Q.nfcall(fs.symlink, this.root, addonPath, 'dir').then(function() {
            var addon = new Addon(addonPath, that.options);
            return addon.load();
        });
    };

    // Unlink this addon
    this.unlink = function() {
        return Q.nfcall(fs.unlink, this.root);
    };

    // Start the node process
    this.start = function(app) {
        var that = this;
        if (!this.isNode()) {
            return Q(this);
        }

        logger.log("start addon", this.root);
        return app.load([
            {
                'packagePath': this.root
            }
        ]).then(function() {
            return Q(that);
        });
    };

    // Return addons cache resources list
    this.resources = function() {
        return ["addon-built.js"].concat(this.infos.client ? (this.infos.client.resources || []) : []);
    };

    // Return addons network resources list
    this.network = function() {
        return [].concat(this.infos.client ? (this.infos.client.network || []) : []);
    };
};

module.exports = Addon;<commit_msg>Handle error during addon build using r.js<commit_after>var _ = require('lodash');
var fs  =  require('fs');
var path = require('path');
var wrench = require('wrench');
var child_process = require('child_process');
var Q = require("q");

var requirejs = require("requirejs");

var exec = function(command, options) {

    var deferred = Q.defer();
    var childProcess;

    var args = Array.prototype.slice.call(arguments, 0);
    args.push(function(err, stdout, stderr) {
        if (err) {
            err.message += command + ' (exited with error code ' + err.code + ')';
            err.stdout = stdout;
            err.stderr = stderr;
            deferred.reject(err);
        }
        else {
            deferred.resolve({
                childProcess: childProcess,
                stdout: stdout,
                stderr: stderr
            });
        }
    });

    childProcess = child_process.exec.apply(child_process, args);

    return deferred.promise;
}

var Addon = function(_rootPath, options) {
    this.root = _rootPath;
    this.infos = {};
    this.options = _.defaults(options || {}, {
        blacklist: [],
        logger: console
    });
    var logger = this.options.logger;

    // Load addon infos from an addon's directory
    this.load = Q.fbind(function(addonDir) {
        addonDir = addonDir || this.root;

        // Check addon
        var packageJsonFile = path.join(addonDir, "package.json");
        if (!fs.existsSync(packageJsonFile)) {
            throw new Error("No 'package.json' in this repository: "+addonDir);
        }
        this.infos = JSON.parse(fs.readFileSync(packageJsonFile, 'utf8'));
        if (!this.isValid()) {
            throw new Error("Invalid 'package.json' file: "+packageJsonFile);
        }

        return this;
    });

    // Valid the addon
    this.isValid = function() {
        return !(!this.infos.name || !this.infos.version
            || (!this.infos.main && !this.infos.client && !this.infos.client.main));
    };

    // Test is symlink
    this.isSymlink = function() {
        return Q.nfcall(fs.lstat, this.root).then(function(stats) {
            return stats.isSymbolicLink();
        });
    };

    // Check if an addon is client side
    this.isClientside = function() {
        return (this.infos.client && this.infos.client.main);
    };

    // Check if an addon is node addon
    this.isNode = function() {
        return (this.infos.main);
    };

    // Check if an addon is already optimized
    this.isOptmized = function() {
        return fs.existsSync(path.join(this.root, "addon-built.js"));
    };

    // Check if an addon has node dependencies
    this.hasDependencies = function() {
        return _.size(this.infos.dependencies || {}) > 0;
    };

    // Check if an addon has npm scripts
    this.hasScripts = function() {
        return _.size(this.infos.scripts || {}) > 0;
    };

    // Check if the addon is blacklisted
    this.isBlacklisted = function() {
        return _.contains(this.options.blacklist, this.infos.name);
    };

    // Optimize the addon
    this.optimizeClient = function(force) {
        var that = this;
        var d = Q.defer();

        if (!this.isClientside()
        || (this.isOptmized() && !force)) {
            return Q(this);
        }

        // Base directory for the addon
        var addonPath = this.root;

        // Path to the require-tools
        var requiretoolsPath = path.resolve(__dirname, "require-tools");

        // Base main
        var main = this.infos.client.main;

        // Output file
        var output = path.resolve(addonPath, "addon-built.js");

        // Build config
        var optconfig = {
            'baseUrl': addonPath,
            'name': main,
            'out': output,
            'logLevel': 4, // silent
            'paths': {
                'require-tools': requiretoolsPath
            },
            'optimize': "uglify",
            'map': {
                '*': {
                    'css': "require-tools/css/css",
                    'less': "require-tools/less/less",
                    'text': "require-tools/text/text"
                }
            }
        };
        // Run optimization
        logger.log("Optimizing", this.infos.name);
        return Q.nfcall(fs.unlink, output).fail(function() {
            return Q();
        }).then(function() {
            var d = Q.defer();

            var catcheErr = function (err) {
                d.reject(err);
                process.removeListener('uncaughtException', catcheErr);
            };

            process.on('uncaughtException', catcheErr);
            requirejs.optimize(optconfig, function(resp) {
                process.removeListener('uncaughtException', catcheErr);
                d.resolve(resp);
            });

            return d.promise;
        }).then(function() {
            logger.log("Finished", that.infos.name, "optimization");
            return Q(that);
        }, function(err) {
            logger.error("error for optimization of", that.infos.name);
            logger.error("options=", optconfig);
            logger.error(err);
            return Q.reject(err);
        });
    };

    // Install dependencies for this addon
    this.installDependencies = function(force) {
        var that = this;
        if (!force) {
            if (!this.hasDependencies() && !this.hasScripts()) {
                return Q(this);
            }
        }
        logger.log("Install dependencies for", this.root);
        return exec("cd "+this.root+" && npm install .").then(function() {
            return Q(that);
        });
    };

    // Transfer to a new root directory
    this.transfer = function(newRoot, options) {
        var that = this;
        options = _.defaults({}, options || {}, {
            forceDelete: true,
            excludeHiddenUnix: false,
            preserveFiles: false
        });

        var addonPath = path.join(newRoot, this.infos.name);
        return Q.nfcall(wrench.copyDirRecursive, this.root, addonPath, options).then(function() {
            var addon = new Addon(addonPath, that.options);
            return addon.load();
        });
    };

    // Symlink this addons
    this.symlink = function(newRoot) {
        var that = this;
        var addonPath = path.join(newRoot, this.infos.name);
        return Q.nfcall(fs.symlink, this.root, addonPath, 'dir').then(function() {
            var addon = new Addon(addonPath, that.options);
            return addon.load();
        });
    };

    // Unlink this addon
    this.unlink = function() {
        return Q.nfcall(fs.unlink, this.root);
    };

    // Start the node process
    this.start = function(app) {
        var that = this;
        if (!this.isNode()) {
            return Q(this);
        }

        logger.log("start addon", this.root);
        return app.load([
            {
                'packagePath': this.root
            }
        ]).then(function() {
            return Q(that);
        });
    };

    // Return addons cache resources list
    this.resources = function() {
        return ["addon-built.js"].concat(this.infos.client ? (this.infos.client.resources || []) : []);
    };

    // Return addons network resources list
    this.network = function() {
        return [].concat(this.infos.client ? (this.infos.client.network || []) : []);
    };
};

module.exports = Addon;<|endoftext|>


<commit_before>// ==UserScript==
// @name        GitHub HTML Preview
// @version     1.0.2
// @description A userscript that adds preview links to HTML files
// @license     MIT
// @author      Rob Garrison
// @namespace   https://github.com/Mottie
// @include     https://github.com/*
// @run-at      document-idle
// @grant       none
// @require     https://greasyfork.org/scripts/28721-mutations/code/mutations.js?version=666427
// @require     https://greasyfork.org/scripts/398877-utils-js/code/utilsjs.js?version=785415
// @icon        https://github.githubassets.com/pinned-octocat.svg
// @updateURL   https://raw.githubusercontent.com/Mottie/GitHub-userscripts/master/github-html-preview.user.js
// @downloadURL https://raw.githubusercontent.com/Mottie/GitHub-userscripts/master/github-html-preview.user.js
// ==/UserScript==
/* global $ $$ on */
(() => {
	"use strict";
	// Example page: https://github.com/codrops/DecorativeLetterAnimations
	// Source: https://github.com/htmlpreview/htmlpreview.github.com
	const prefix = "https://htmlpreview.github.io/?";
	// html & htm extensions
	const regex = /\.html?$/;

	const link = document.createElement("a");
	link.className = "ghhp-btn py-0 v-align-baseline tooltipped tooltipped-e";
	link.setAttribute("aria-label", "Open in new tab");
	link.target = "_blank";
	link.innerHTML = `
		<svg class="octicon v-align-text-bottom" xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="currentColor">
			<path d="M19 19H5V5h7V3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"/>
		</svg>`;
	link.style.marginRight = ".25rem";

	function addLink(el) {
		const cell = el.closest(".js-navigation-item div[role='rowheader'] span");
		if (cell && !$(".ghhp-btn", cell)) {
			const preview = link.cloneNode(true);
			preview.href = prefix + el.href;
			cell.prepend(preview);
		}
	}

	function init() {
		if ($("#files")) {
			const files = $("#files").parentElement;
			$$(".js-navigation-item div[role='rowheader'] .js-navigation-open", files).forEach(el => {
				if (regex.test(el.title)) {
					addLink(el);
				}
			});
		}
	}

	on(document, "ghmo:container", () => {
		// init after a short delay to allow rendering of file list
		setTimeout(() => {
			init();
		}, 200);
	});
	init();

})();
<commit_msg>Html-preview: Increase external link icon to 14px. See #129<commit_after>// ==UserScript==
// @name        GitHub HTML Preview
// @version     1.0.3
// @description A userscript that adds preview links to HTML files
// @license     MIT
// @author      Rob Garrison
// @namespace   https://github.com/Mottie
// @include     https://github.com/*
// @run-at      document-idle
// @grant       none
// @require     https://greasyfork.org/scripts/28721-mutations/code/mutations.js?version=666427
// @require     https://greasyfork.org/scripts/398877-utils-js/code/utilsjs.js?version=785415
// @icon        https://github.githubassets.com/pinned-octocat.svg
// @updateURL   https://raw.githubusercontent.com/Mottie/GitHub-userscripts/master/github-html-preview.user.js
// @downloadURL https://raw.githubusercontent.com/Mottie/GitHub-userscripts/master/github-html-preview.user.js
// ==/UserScript==
/* global $ $$ on */
(() => {
	"use strict";
	// Example page: https://github.com/codrops/DecorativeLetterAnimations
	// Source: https://github.com/htmlpreview/htmlpreview.github.com
	const prefix = "https://htmlpreview.github.io/?";
	// html & htm extensions
	const regex = /\.html?$/;

	const link = document.createElement("a");
	link.className = "ghhp-btn py-0 v-align-baseline tooltipped tooltipped-e";
	link.setAttribute("aria-label", "Open in new tab");
	link.target = "_blank";
	link.innerHTML = `
		<svg class="octicon v-align-text-bottom" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
			<path d="M19 19H5V5h7V3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"/>
		</svg>`;
	link.style.marginRight = ".25rem";

	function addLink(el) {
		const cell = el.closest(".js-navigation-item div[role='rowheader'] span");
		if (cell && !$(".ghhp-btn", cell)) {
			const preview = link.cloneNode(true);
			preview.href = prefix + el.href;
			cell.prepend(preview);
		}
	}

	function init() {
		if ($("#files")) {
			const files = $("#files").parentElement;
			$$(".js-navigation-item div[role='rowheader'] .js-navigation-open", files).forEach(el => {
				if (regex.test(el.title)) {
					addLink(el);
				}
			});
		}
	}

	on(document, "ghmo:container", () => {
		// init after a short delay to allow rendering of file list
		setTimeout(() => {
			init();
		}, 200);
	});
	init();

})();
<|endoftext|>


<commit_before>var XMLSerializer = require("xmldom").XMLSerializer,
	DOMParser = require("xmldom").DOMParser;

var filename = process.argv[2];
console.log(filename);<commit_msg>updates<commit_after>var XMLSerializer = require("xmldom").XMLSerializer,
	DOMParser = require("xmldom").DOMParser;

var filename = process.argv[2];
var doc = new DOMParser().parseFromString(string);
console.log(filename);<|endoftext|>


<commit_before>export { Base, Same, Bool, Int };
export { ActivationFunction };
export { channelCount1_pointwise1Before };
export { AvgMax_Or_ChannelMultiplier };
export { StridesPad };
export { WhetherShuffleChannel };

import * as ValueDesc from "./ValueDesc.js";

/**
 * Describe some properties of a parameter.
 * Usually, a ParamDesc object is also be used as the unique key of a parameter.
 *
 * Q: Why are there ValueDesc and ParamDesc two layers?
 * A: Let different parameter descriptions (ParamDesc) share the same value description (ValueDesc).
 *    It is especially useful for integer parameter ( new ParamDesc.Base( paramName, new ValueDesc.Int() ) ).
 *    For non-converting and boolean parameter, using ParamDesc.Same and ParamDesc.Bool is enough.
 *
 * @example
 * let fruitKinds = new ValueDesc.Int( 0, 2, [ APPLE, ORANGE, BANANA ] );
 * Params.lunch = new ParamDesc.Base( "lunch", fruitKinds );
 * Params.dinner = new ParamDesc.Base( "dinner", fruitKinds );
 *
 *
 * @member {string} paramName
 *   The name of the parameter. It is a string. It should be a legal identifer too (i.e. A-Z, a-z, 0-9 (not at first character), and "_").
 *
 * @member {ValueDesc.Xxx} valueDesc
 *   The description of the parameter's all possible values. It should be a ValueDesc.Xxx object (one of ValueDesc.Same,
 * ValueDesc.Bool, ValueDesc.Int). For example, ValueDesc.Same.Singleton, ValueDesc.Bool.Singleton.
 *
 */
class Base {

  /**
   *
   */
  constructor( paramName, valueDesc ) {
    this.paramName = paramName;
    this.valueDesc = valueDesc;
  }

  /**
   * @return {string}
   *   Return the value's string (or name if it has).
   */
  getStringOfValue( value ) {
    return this.valueDesc.getStringOf( value );
  }
}

/**
 * Describe some properties of a non-converting parameter.
 *
 * @member {ValueDesc.Same} valueDesc
 *   The range of the parameter's all possible values. It is a ValueDesc.Same object.
 */
class Same extends Base {

  constructor( paramName ) {
    super( paramName, ValueDesc.Same.Singleton );
  }
}

/**
 * Describe some properties of a boolean parameter.
 *
 * @member {ValueDesc.Bool} valueDesc
 *   The boolean range of the parameter's all possible values. It is a ValueDesc.Bool object.
 */
class Bool extends Base {

  constructor( paramName ) {
    super( paramName, ValueDesc.Bool.Singleton );
  }
}

/**
 * Describe some properties of an integer parameter.
 *
 * @member {ValueDesc.Int} valueDesc
 *   The integer range of the parameter's all possible values. It is a ValueDesc.Bool object.
 */
class Int extends Base {

  constructor( paramName, valueIntegerMin, valueIntegerMax, valueNames = [], valueObjects = [] ) {
    super( paramName, new ValueDesc.Int( valueIntegerMin, valueIntegerMax, valueNames, valueObjects ) );
  }
}

/**
 * Describe some properties of an activation function parameter.
 *
 * @member {ValueDesc.ActivationFunction} valueDesc
 *   The range of the parameter's all possible values. It is a ValueDesc.ActivationFunction object.
 */
class ActivationFunction extends Base {

  constructor( paramName ) {
    super( paramName, ValueDesc.ActivationFunction.Singleton );
  }
}

/**
 * Describe some properties of an channelCount1_pointwise1Before parameter.
 *
 * @member {ValueDesc.channelCount1_pointwise1Before} valueDesc
 *   The range of the parameter's all possible values. It is a ValueDesc.channelCount1_pointwise1Before object.
 */
class channelCount1_pointwise1Before extends Base {

  constructor( paramName ) {
    super( paramName, ValueDesc.channelCount1_pointwise1Before.Singleton );
  }
}

/**
 * Describe some properties of an depthwise operation parameter.
 *
 * @member {ValueDesc.AvgMax_Or_ChannelMultiplier} valueDesc
 *   The range of the parameter's all possible values. It is a ValueDesc.AvgMax_Or_ChannelMultiplier object.
 */
class AvgMax_Or_ChannelMultiplier extends Base {

  constructor( paramName ) {
    super( paramName, ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton );
  }
}

/**
 * Describe some properties of an strides and pad parameter.
 *
 * @member {ValueDesc.StridesPad} valueDesc
 *   The range of the parameter's all possible values. It is a ValueDesc.StridesPad object.
 */
class StridesPad extends Base {

  constructor( paramName ) {
    super( paramName, ValueDesc.StridesPad.Singleton );
  }
}

/**
 * Describe some properties of an WhetherShuffleChannel parameter.
 *
 * @member {ValueDesc.WhetherShuffleChannel} valueDesc
 *   The range of the parameter's all possible values. It is a ValueDesc.WhetherShuffleChannel object.
 */
class WhetherShuffleChannel extends Base {

  constructor( paramName ) {
    super( paramName, ValueDesc.WhetherShuffleChannel.Singleton );
  }
}
<commit_msg>Update ParamDesc.js<commit_after>export { Base, Same, Bool, Int };
export { ActivationFunction };
export { channelCount1_pointwise1Before };
export { AvgMax_Or_ChannelMultiplier };
export { StridesPad };
export { ConvBlockType };

import * as ValueDesc from "./ValueDesc.js";

/**
 * Describe some properties of a parameter.
 * Usually, a ParamDesc object is also be used as the unique key of a parameter.
 *
 * Q: Why are there ValueDesc and ParamDesc two layers?
 * A: Let different parameter descriptions (ParamDesc) share the same value description (ValueDesc).
 *    It is especially useful for integer parameter ( new ParamDesc.Base( paramName, new ValueDesc.Int() ) ).
 *    For non-converting and boolean parameter, using ParamDesc.Same and ParamDesc.Bool is enough.
 *
 * @example
 * let fruitKinds = new ValueDesc.Int( 0, 2, [ APPLE, ORANGE, BANANA ] );
 * Params.lunch = new ParamDesc.Base( "lunch", fruitKinds );
 * Params.dinner = new ParamDesc.Base( "dinner", fruitKinds );
 *
 *
 * @member {string} paramName
 *   The name of the parameter. It is a string. It should be a legal identifer too (i.e. A-Z, a-z, 0-9 (not at first character), and "_").
 *
 * @member {ValueDesc.Xxx} valueDesc
 *   The description of the parameter's all possible values. It should be a ValueDesc.Xxx object (one of ValueDesc.Same,
 * ValueDesc.Bool, ValueDesc.Int). For example, ValueDesc.Same.Singleton, ValueDesc.Bool.Singleton.
 *
 */
class Base {

  /**
   *
   */
  constructor( paramName, valueDesc ) {
    this.paramName = paramName;
    this.valueDesc = valueDesc;
  }

  /**
   * @return {string}
   *   Return the value's string (or name if it has).
   */
  getStringOfValue( value ) {
    return this.valueDesc.getStringOf( value );
  }
}

/**
 * Describe some properties of a non-converting parameter.
 *
 * @member {ValueDesc.Same} valueDesc
 *   The range of the parameter's all possible values. It is a ValueDesc.Same object.
 */
class Same extends Base {

  constructor( paramName ) {
    super( paramName, ValueDesc.Same.Singleton );
  }
}

/**
 * Describe some properties of a boolean parameter.
 *
 * @member {ValueDesc.Bool} valueDesc
 *   The boolean range of the parameter's all possible values. It is a ValueDesc.Bool object.
 */
class Bool extends Base {

  constructor( paramName ) {
    super( paramName, ValueDesc.Bool.Singleton );
  }
}

/**
 * Describe some properties of an integer parameter.
 *
 * @member {ValueDesc.Int} valueDesc
 *   The integer range of the parameter's all possible values. It is a ValueDesc.Bool object.
 */
class Int extends Base {

  constructor( paramName, valueIntegerMin, valueIntegerMax, valueNames = [], valueObjects = [] ) {
    super( paramName, new ValueDesc.Int( valueIntegerMin, valueIntegerMax, valueNames, valueObjects ) );
  }
}

/**
 * Describe some properties of an activation function parameter.
 *
 * @member {ValueDesc.ActivationFunction} valueDesc
 *   The range of the parameter's all possible values. It is a ValueDesc.ActivationFunction object.
 */
class ActivationFunction extends Base {

  constructor( paramName ) {
    super( paramName, ValueDesc.ActivationFunction.Singleton );
  }
}

/**
 * Describe some properties of an channelCount1_pointwise1Before parameter.
 *
 * @member {ValueDesc.channelCount1_pointwise1Before} valueDesc
 *   The range of the parameter's all possible values. It is a ValueDesc.channelCount1_pointwise1Before object.
 */
class channelCount1_pointwise1Before extends Base {

  constructor( paramName ) {
    super( paramName, ValueDesc.channelCount1_pointwise1Before.Singleton );
  }
}

/**
 * Describe some properties of an depthwise operation parameter.
 *
 * @member {ValueDesc.AvgMax_Or_ChannelMultiplier} valueDesc
 *   The range of the parameter's all possible values. It is a ValueDesc.AvgMax_Or_ChannelMultiplier object.
 */
class AvgMax_Or_ChannelMultiplier extends Base {

  constructor( paramName ) {
    super( paramName, ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton );
  }
}

/**
 * Describe some properties of an strides and pad parameter.
 *
 * @member {ValueDesc.StridesPad} valueDesc
 *   The range of the parameter's all possible values. It is a ValueDesc.StridesPad object.
 */
class StridesPad extends Base {

  constructor( paramName ) {
    super( paramName, ValueDesc.StridesPad.Singleton );
  }
}

/**
 * Describe some properties of an ConvBlockType parameter.
 *
 * @member {ValueDesc.ConvBlockType} valueDesc
 *   The range of the parameter's all possible values. It is a ValueDesc.ConvBlockType object.
 */
class ConvBlockType extends Base {

  constructor( paramName ) {
    super( paramName, ValueDesc.ConvBlockType.Singleton );
  }
}
<|endoftext|>


<commit_before>(function($)
{
    $.fn.NylAutoWriter = function(options, callback)
    {

        var colorstr2Hex = function colourNameToHex(colour)
        {
            var colours = {"aliceblue":"#f0f8ff","antiquewhite":"#faebd7","aqua":"#00ffff","aquamarine":"#7fffd4","azure":"#f0ffff",
                "beige":"#f5f5dc","bisque":"#ffe4c4","black":"#000000","blanchedalmond":"#ffebcd","blue":"#0000ff","blueviolet":"#8a2be2","brown":"#a52a2a","burlywood":"#deb887",
                "cadetblue":"#5f9ea0","chartreuse":"#7fff00","chocolate":"#d2691e","coral":"#ff7f50","cornflowerblue":"#6495ed","cornsilk":"#fff8dc","crimson":"#dc143c","cyan":"#00ffff",
                "darkblue":"#00008b","darkcyan":"#008b8b","darkgoldenrod":"#b8860b","darkgray":"#a9a9a9","darkgreen":"#006400","darkkhaki":"#bdb76b","darkmagenta":"#8b008b","darkolivegreen":"#556b2f",
                "darkorange":"#ff8c00","darkorchid":"#9932cc","darkred":"#8b0000","darksalmon":"#e9967a","darkseagreen":"#8fbc8f","darkslateblue":"#483d8b","darkslategray":"#2f4f4f","darkturquoise":"#00ced1",
                "darkviolet":"#9400d3","deeppink":"#ff1493","deepskyblue":"#00bfff","dimgray":"#696969","dodgerblue":"#1e90ff",
                "firebrick":"#b22222","floralwhite":"#fffaf0","forestgreen":"#228b22","fuchsia":"#ff00ff",
                "gainsboro":"#dcdcdc","ghostwhite":"#f8f8ff","gold":"#ffd700","goldenrod":"#daa520","gray":"#808080","green":"#008000","greenyellow":"#adff2f",
                "honeydew":"#f0fff0","hotpink":"#ff69b4",
                "indianred ":"#cd5c5c","indigo":"#4b0082","ivory":"#fffff0","khaki":"#f0e68c",
                "lavender":"#e6e6fa","lavenderblush":"#fff0f5","lawngreen":"#7cfc00","lemonchiffon":"#fffacd","lightblue":"#add8e6","lightcoral":"#f08080","lightcyan":"#e0ffff","lightgoldenrodyellow":"#fafad2",
                "lightgrey":"#d3d3d3","lightgreen":"#90ee90","lightpink":"#ffb6c1","lightsalmon":"#ffa07a","lightseagreen":"#20b2aa","lightskyblue":"#87cefa","lightslategray":"#778899","lightsteelblue":"#b0c4de",
                "lightyellow":"#ffffe0","lime":"#00ff00","limegreen":"#32cd32","linen":"#faf0e6",
                "magenta":"#ff00ff","maroon":"#800000","mediumaquamarine":"#66cdaa","mediumblue":"#0000cd","mediumorchid":"#ba55d3","mediumpurple":"#9370d8","mediumseagreen":"#3cb371","mediumslateblue":"#7b68ee",
                "mediumspringgreen":"#00fa9a","mediumturquoise":"#48d1cc","mediumvioletred":"#c71585","midnightblue":"#191970","mintcream":"#f5fffa","mistyrose":"#ffe4e1","moccasin":"#ffe4b5",
                "navajowhite":"#ffdead","navy":"#000080",
                "oldlace":"#fdf5e6","olive":"#808000","olivedrab":"#6b8e23","orange":"#ffa500","orangered":"#ff4500","orchid":"#da70d6",
                "palegoldenrod":"#eee8aa","palegreen":"#98fb98","paleturquoise":"#afeeee","palevioletred":"#d87093","papayawhip":"#ffefd5","peachpuff":"#ffdab9","peru":"#cd853f","pink":"#ffc0cb","plum":"#dda0dd","powderblue":"#b0e0e6","purple":"#800080",
                "red":"#ff0000","rosybrown":"#bc8f8f","royalblue":"#4169e1",
                "saddlebrown":"#8b4513","salmon":"#fa8072","sandybrown":"#f4a460","seagreen":"#2e8b57","seashell":"#fff5ee","sienna":"#a0522d","silver":"#c0c0c0","skyblue":"#87ceeb","slateblue":"#6a5acd","slategray":"#708090","snow":"#fffafa","springgreen":"#00ff7f","steelblue":"#4682b4",
                "tan":"#d2b48c","teal":"#008080","thistle":"#d8bfd8","tomato":"#ff6347","turquoise":"#40e0d0",
                "violet":"#ee82ee",
                "wheat":"#f5deb3","white":"#ffffff","whitesmoke":"#f5f5f5",
                "yellow":"#ffff00","yellowgreen":"#9acd32"};

            if (typeof colour != 'undefined' && typeof colours[colour.toLowerCase()] != 'undefined')
                return colours[colour.toLowerCase()];

            return false;
        };

        var textColor = colorstr2Hex(options.color) || options.color || "black",
            backgroundColor = colorstr2Hex(options.backgroundColor) || options.backgroundColor || 'white',
            mask = options.mask || ' abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789?!-,.@/\'"',
            timeout = options.timeout || 1,
            backgroundColorChunk = backgroundColor.slice(3,7),
            noColor = options.noColor || false,
            letters = $(this).html().split(''),
            iterations = options.iterations || 10;
        maskArray = mask.split('');

        $(this).html(' ');

        var morphing = function(wraper, letters, mask, position, morph, iteration, callback){
            if (position < letters.length) {
                setTimeout(function () {
                    var rand = Math.floor(Math.random() * mask.length);
                    if (iteration < iterations ) {
                        var randR = Math.floor(Math.random() * 255).toString(16);

                        if (!noColor) {
                            wraper.html(morph + '' + mask[rand] + "");
                        }
                        else {
                            wraper.html(morph + '' + mask[rand] + "");

                        }

                        morphing(wraper, letters, mask, position, morph, iteration+1, callback);

                    }
                    else {
                        wraper.html(morph + letters[position]);

                        if (position < letters.length) {
                            morph += letters[position];

                            morphing(wraper, letters, mask, position + 1, morph, 0, callback);

                        }
                        else {
                            wraper.html(morph);
                        }

                    }

                },timeout)
            }
            else {
                callback();

            }
        };

        morphing($(this), letters,maskArray, 0, '',0, callback);

    };

})(jQuery);

<commit_msg>If the first argument is a function, that's the callback and there's no options object.<commit_after>(function($)
{
    $.fn.NylAutoWriter = function(options, callback)
    {

        var colorstr2Hex = function colourNameToHex(colour)
        {
            var colours = {"aliceblue":"#f0f8ff","antiquewhite":"#faebd7","aqua":"#00ffff","aquamarine":"#7fffd4","azure":"#f0ffff",
                "beige":"#f5f5dc","bisque":"#ffe4c4","black":"#000000","blanchedalmond":"#ffebcd","blue":"#0000ff","blueviolet":"#8a2be2","brown":"#a52a2a","burlywood":"#deb887",
                "cadetblue":"#5f9ea0","chartreuse":"#7fff00","chocolate":"#d2691e","coral":"#ff7f50","cornflowerblue":"#6495ed","cornsilk":"#fff8dc","crimson":"#dc143c","cyan":"#00ffff",
                "darkblue":"#00008b","darkcyan":"#008b8b","darkgoldenrod":"#b8860b","darkgray":"#a9a9a9","darkgreen":"#006400","darkkhaki":"#bdb76b","darkmagenta":"#8b008b","darkolivegreen":"#556b2f",
                "darkorange":"#ff8c00","darkorchid":"#9932cc","darkred":"#8b0000","darksalmon":"#e9967a","darkseagreen":"#8fbc8f","darkslateblue":"#483d8b","darkslategray":"#2f4f4f","darkturquoise":"#00ced1",
                "darkviolet":"#9400d3","deeppink":"#ff1493","deepskyblue":"#00bfff","dimgray":"#696969","dodgerblue":"#1e90ff",
                "firebrick":"#b22222","floralwhite":"#fffaf0","forestgreen":"#228b22","fuchsia":"#ff00ff",
                "gainsboro":"#dcdcdc","ghostwhite":"#f8f8ff","gold":"#ffd700","goldenrod":"#daa520","gray":"#808080","green":"#008000","greenyellow":"#adff2f",
                "honeydew":"#f0fff0","hotpink":"#ff69b4",
                "indianred ":"#cd5c5c","indigo":"#4b0082","ivory":"#fffff0","khaki":"#f0e68c",
                "lavender":"#e6e6fa","lavenderblush":"#fff0f5","lawngreen":"#7cfc00","lemonchiffon":"#fffacd","lightblue":"#add8e6","lightcoral":"#f08080","lightcyan":"#e0ffff","lightgoldenrodyellow":"#fafad2",
                "lightgrey":"#d3d3d3","lightgreen":"#90ee90","lightpink":"#ffb6c1","lightsalmon":"#ffa07a","lightseagreen":"#20b2aa","lightskyblue":"#87cefa","lightslategray":"#778899","lightsteelblue":"#b0c4de",
                "lightyellow":"#ffffe0","lime":"#00ff00","limegreen":"#32cd32","linen":"#faf0e6",
                "magenta":"#ff00ff","maroon":"#800000","mediumaquamarine":"#66cdaa","mediumblue":"#0000cd","mediumorchid":"#ba55d3","mediumpurple":"#9370d8","mediumseagreen":"#3cb371","mediumslateblue":"#7b68ee",
                "mediumspringgreen":"#00fa9a","mediumturquoise":"#48d1cc","mediumvioletred":"#c71585","midnightblue":"#191970","mintcream":"#f5fffa","mistyrose":"#ffe4e1","moccasin":"#ffe4b5",
                "navajowhite":"#ffdead","navy":"#000080",
                "oldlace":"#fdf5e6","olive":"#808000","olivedrab":"#6b8e23","orange":"#ffa500","orangered":"#ff4500","orchid":"#da70d6",
                "palegoldenrod":"#eee8aa","palegreen":"#98fb98","paleturquoise":"#afeeee","palevioletred":"#d87093","papayawhip":"#ffefd5","peachpuff":"#ffdab9","peru":"#cd853f","pink":"#ffc0cb","plum":"#dda0dd","powderblue":"#b0e0e6","purple":"#800080",
                "red":"#ff0000","rosybrown":"#bc8f8f","royalblue":"#4169e1",
                "saddlebrown":"#8b4513","salmon":"#fa8072","sandybrown":"#f4a460","seagreen":"#2e8b57","seashell":"#fff5ee","sienna":"#a0522d","silver":"#c0c0c0","skyblue":"#87ceeb","slateblue":"#6a5acd","slategray":"#708090","snow":"#fffafa","springgreen":"#00ff7f","steelblue":"#4682b4",
                "tan":"#d2b48c","teal":"#008080","thistle":"#d8bfd8","tomato":"#ff6347","turquoise":"#40e0d0",
                "violet":"#ee82ee",
                "wheat":"#f5deb3","white":"#ffffff","whitesmoke":"#f5f5f5",
                "yellow":"#ffff00","yellowgreen":"#9acd32"};

            if (typeof colour != 'undefined' && typeof colours[colour.toLowerCase()] != 'undefined')
                return colours[colour.toLowerCase()];

            return false;
        };

        var textColor = colorstr2Hex(options.color) || options.color || "black",
            backgroundColor = colorstr2Hex(options.backgroundColor) || options.backgroundColor || 'white',
            mask = options.mask || ' abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789?!-,.@/\'"',
            timeout = options.timeout || 1,
            backgroundColorChunk = backgroundColor.slice(3,7),
            noColor = options.noColor || false,
            letters = $(this).html().split(''),
            iterations = options.iterations || 10;
        maskArray = mask.split('');

        if (typeof options === 'function') {
            callback = options;
        }

        $(this).html(' ');

        var morphing = function(wraper, letters, mask, position, morph, iteration, callback){
            if (position < letters.length) {
                setTimeout(function () {
                    var rand = Math.floor(Math.random() * mask.length);
                    if (iteration < iterations ) {
                        var randR = Math.floor(Math.random() * 255).toString(16);

                        if (!noColor) {
                            wraper.html(morph + '' + mask[rand] + "");
                        }
                        else {
                            wraper.html(morph + '' + mask[rand] + "");

                        }

                        morphing(wraper, letters, mask, position, morph, iteration+1, callback);

                    }
                    else {
                        wraper.html(morph + letters[position]);

                        if (position < letters.length) {
                            morph += letters[position];

                            morphing(wraper, letters, mask, position + 1, morph, 0, callback);

                        }
                        else {
                            wraper.html(morph);
                        }

                    }

                },timeout)
            }
            else {
                callback();

            }
        };

        morphing($(this), letters,maskArray, 0, '',0, callback);

    };

})(jQuery);

<|endoftext|>


<commit_before>// Known spoiler "emotes" to avoid expanding alt-text on. Not all of these are
// known to BPM, and it's not really worth moving this to a data file somewhere.
// - /spoiler is from r/mylittlepony (and copied around like mad)
// - /s is from r/falloutequestria (and r/mylittleanime has a variant)
// - #s is from r/doctorwho
// - /b and /g are from r/dresdenfiles
var spoiler_links = ["/spoiler", "/s", "#s", "/b", "/g"];

/*
 * Sets the sourceinfo hover on an emote element.
 */
function add_sourceinfo(element, state, is_emote, is_unknown) {
    var name = element.getAttribute("data-bpm_emotename");
    var title = "";

    if(is_emote) {
        var subreddit = element.getAttribute("data-bpm_srname");

        if(state.indexOf("d") > -1) {
            title = "Disabled ";
            if(state.indexOf("n") > -1) {
                title += "NSFW ";
            }
            title += "emote ";
        }
        title += name + " from " + subreddit;
    } else if(is_unknown) {
        title = "Unknown emote " + name;
    }

    element.title = title;
}

/*
 * Decides whether or not the alt-text on this element needs to be processed.
 * Returns bpm_state if yes, null if no.
 */
function should_convert_alt_text(element, state, is_emote) {
    if(is_emote) {
        // Emotes require a sourceinfo hover, no matter what
        return true;
    }

    if(!element.title) {
        // Note, we don't bother setting state="a" in this case
        return false;
    }

    // Already processed? Avoid doing silly things like expanding things again
    // (or our sourceinfo hover)
    if(state.indexOf("a") > -1) {
        return false;
    }

    // Avoid spoiler links. We can't rely on any emote data to exist, as
    // of them aren't known as emotes
    var href = element.getAttribute("href");
    if(href && spoiler_links.indexOf(href.split("-")[0]) > -1) {
        return false;
    }

    // Work around RES putting tag links and other things with alt-text on
    // them in the middle of posts- we don't want to expand those.
    if(element.classList.contains("userTagLink") ||
       element.classList.contains("voteWeight")) {
        return false;
    }

    return true;
}

/*
 * Generates the actual alt-text element. Handles embedded links.
 */
function generate_alt_text(title, container) {
    // Split on links, so we can turn those into real links. These are rare,
    // but worth handling nicely. Also prepend a space for formatting- it makes
    // the most difference on non-emote elements.
    // (\b doesn't seem to be working when I put it at the end, here??
    // Also, note that we do grab the space at the end for formatting)
    //                                  http://    < domain name >    /url?params#stuff
    var parts = (" " + title).split(/\b(https?:\/\/[a-zA-Z0-9\-.]+(?:\/[a-zA-Z0-9\-_.~'();:+\/?%#]*)?(?:\s|$))/);

    // Handle items in pairs: one chunk of text and one link at a time
    for(var j = 0; j < Math.floor(parts.length / 2); j += 2) {
        if(parts[j]) {
            container.appendChild(document.createTextNode(parts[j]));
        }
        var link_element = document.createElement("a");
        link_element.textContent = parts[j + 1];
        link_element.href = parts[j + 1];
        container.appendChild(link_element);
    }

    // The last bit is just text. (And likely the only chunk there is, anyway.)
    if(parts[parts.length - 1]) {
        container.appendChild(document.createTextNode(parts[parts.length - 1]));
    }
}

function convert_alt_text(element, is_emote, is_unknown) {
    // If this is an image link, try to put the alt-text on the other side
    // of the RES expando button. It looks better that way.
    var before = element.nextSibling; // Thing to put alt-text before
    while(before && before.className !== undefined &&
          before.classList.contains("expando-button")) {
        before = before.nextSibling;
    }

    // As a note: alt-text kinda has to be a block-level element, in order
    // to go in the same place as the emote it's attached to. The chief
    // exception is -in emotes, so as a bit of a hack, we assume the
    // converter has already run and check for known -in flags. The other
    // possibility is that this is just a normal link of some kind, so
    // treat those as special too.
    var element_type = "div";
    if(element.classList.contains("bpflag-in") ||
       element.classList.contains("bpflag-inp") ||
       (!is_emote && !is_unknown)) {
        element_type = "span";
    }

    // Do the actual conversion
    var container = document.createElement(element_type);
    container.classList.add("bpm-alttext");
    generate_alt_text(element.title, container);
    element.parentNode.insertBefore(container, before);
}

/*
 * Converts alt-text on an <a> element as appropriate. Will respond to the emote
 * converter if it has already run on this element.
 */
function process_alt_text(element) {
    var state = element.getAttribute("data-bpm_state") || "";
    var is_emote = state.indexOf("e") > -1;
    var is_unknown = state.indexOf("u") > -1;

    // Early exit- some elements we just ignore completely
    if(!should_convert_alt_text(element, state, is_emote)) {
        return;
    }

    // Actual alt-text conversion
    if(element.title) {
        convert_alt_text(element, is_emote, is_unknown);
    }

    // Special support for emotes- replace the alt-text with source info
    if(is_emote || is_unknown) {
        add_sourceinfo(element, state, is_emote, is_unknown);
    }

    // Mark as handled, so we don't ever run into it again.
    element.setAttribute("data-bpm_state", state + "a");
}
<commit_msg>Fix alt-text again<commit_after>// Known spoiler "emotes" to avoid expanding alt-text on. Not all of these are
// known to BPM, and it's not really worth moving this to a data file somewhere.
// - /spoiler is from r/mylittlepony (and copied around like mad)
// - /s is from r/falloutequestria (and r/mylittleanime has a variant)
// - #s is from r/doctorwho
// - /b and /g are from r/dresdenfiles
var spoiler_links = ["/spoiler", "/s", "#s", "/b", "/g"];

/*
 * Sets the sourceinfo hover on an emote element.
 */
function add_sourceinfo(element, state, is_emote, is_unknown) {
    var name = element.getAttribute("data-bpm_emotename");
    var title = "";

    if(is_emote) {
        var subreddit = element.getAttribute("data-bpm_srname");

        if(state.indexOf("d") > -1) {
            title = "Disabled ";
            if(state.indexOf("n") > -1) {
                title += "NSFW ";
            }
            title += "emote ";
        }
        title += name + " from " + subreddit;
    } else if(is_unknown) {
        title = "Unknown emote " + name;
    }

    element.title = title;
}

/*
 * Decides whether or not the alt-text on this element needs to be processed.
 * Returns bpm_state if yes, null if no.
 */
function should_convert_alt_text(element, state, is_emote) {
    // Already processed? Avoid doing silly things like expanding things again
    // (or our sourceinfo hover)
    if(state.indexOf("a") > -1) {
        return false;
    }

    if(is_emote) {
        // Emotes require a sourceinfo hover, no matter what
        return true;
    }

    if(!element.title) {
        // Note, we don't bother setting state="a" in this case
        return false;
    }

    // Avoid spoiler links. We can't rely on any emote data to exist, as
    // of them aren't known as emotes
    var href = element.getAttribute("href");
    if(href && spoiler_links.indexOf(href.split("-")[0]) > -1) {
        return false;
    }

    // Work around RES putting tag links and other things with alt-text on
    // them in the middle of posts- we don't want to expand those.
    if(element.classList.contains("userTagLink") ||
       element.classList.contains("voteWeight")) {
        return false;
    }

    return true;
}

/*
 * Generates the actual alt-text element. Handles embedded links.
 */
function generate_alt_text(title, container) {
    // Split on links, so we can turn those into real links. These are rare,
    // but worth handling nicely. Also prepend a space for formatting- it makes
    // the most difference on non-emote elements.
    // (\b doesn't seem to be working when I put it at the end, here??
    // Also, note that we do grab the space at the end for formatting)
    //                                  http://    < domain name >    /url?params#stuff
    var parts = (" " + title).split(/\b(https?:\/\/[a-zA-Z0-9\-.]+(?:\/[a-zA-Z0-9\-_.~'();:+\/?%#]*)?(?:\s|$))/);

    // Handle items in pairs: one chunk of text and one link at a time
    for(var j = 0; j < Math.floor(parts.length / 2); j += 2) {
        if(parts[j]) {
            container.appendChild(document.createTextNode(parts[j]));
        }
        var link_element = document.createElement("a");
        link_element.textContent = parts[j + 1];
        link_element.href = parts[j + 1];
        container.appendChild(link_element);
    }

    // The last bit is just text. (And likely the only chunk there is, anyway.)
    if(parts[parts.length - 1]) {
        container.appendChild(document.createTextNode(parts[parts.length - 1]));
    }
}

function convert_alt_text(element, is_emote, is_unknown) {
    // If this is an image link, try to put the alt-text on the other side
    // of the RES expando button. It looks better that way.
    var before = element.nextSibling; // Thing to put alt-text before
    while(before && before.className !== undefined &&
          before.classList.contains("expando-button")) {
        before = before.nextSibling;
    }

    // As a note: alt-text kinda has to be a block-level element, in order
    // to go in the same place as the emote it's attached to. The chief
    // exception is -in emotes, so as a bit of a hack, we assume the
    // converter has already run and check for known -in flags. The other
    // possibility is that this is just a normal link of some kind, so
    // treat those as special too.
    var element_type = "div";
    if(element.classList.contains("bpflag-in") ||
       element.classList.contains("bpflag-inp") ||
       (!is_emote && !is_unknown)) {
        element_type = "span";
    }

    // Do the actual conversion
    var container = document.createElement(element_type);
    container.classList.add("bpm-alttext");
    generate_alt_text(element.title, container);
    element.parentNode.insertBefore(container, before);
}

/*
 * Converts alt-text on an <a> element as appropriate. Will respond to the emote
 * converter if it has already run on this element.
 */
function process_alt_text(element) {
    var state = element.getAttribute("data-bpm_state") || "";
    var is_emote = state.indexOf("e") > -1;
    var is_unknown = state.indexOf("u") > -1;

    // Early exit- some elements we just ignore completely
    if(!should_convert_alt_text(element, state, is_emote)) {
        return;
    }

    // Actual alt-text conversion
    if(element.title) {
        convert_alt_text(element, is_emote, is_unknown);
    }

    // Special support for emotes- replace the alt-text with source info
    if(is_emote || is_unknown) {
        add_sourceinfo(element, state, is_emote, is_unknown);
    }

    // Mark as handled, so we don't ever run into it again.
    element.setAttribute("data-bpm_state", state + "a");
}
<|endoftext|>