<commit_before>/**
 * @fileOverview Chart data exporter
 * @author NHN Ent.
 *         FE Development Lab <dl_javascript@nhnent.com>
 */

'use strict';
var isIE10OrIE11 = tui.util.browser.msie && (tui.util.browser.version === 10 || tui.util.browser.version === 11);

var isImageDownloadAvailable = isBrowserSupportClientSideDownload()
    && (!isIE10OrIE11 || (isIE10OrIE11 && document.createElement('canvas').getContext('2d').drawSvg));

var DATA_URI_HEADERS = {
    xls: 'data:application/vnd.ms-excel;base64,',
    csv: 'data:text/csv,'
};
var EXPORT_DATA_MAKERS = {
    xls: _makeXlsStringWithRawData,
    csv: _makeCsvTextWithRawData
};
var DOWNLOADER_FUNCTIONS = {
    downloadAttribute: _downloadWithAnchorElementDownloadAttribute,
    msSaveOrOpenBlob: _downloadWithMsSaveOrOpenBlob
};

/**
 * Return given extension type is image format
 * @param {string} extension extension
 * @returns {boolean}
 * @private
 */
function isImageExtension(extension) {
    return extension === 'png' || extension === 'jpeg';
}

/**
 * Get pivoted second dimension array from table to use element.innerText
 * @param {rawData} rawData - chart's raw data
 * @returns {Array.<Array>}
 * @private
 */
function _get2DArrayFromRawData(rawData) {
    var resultArray = [];
    var categories, seriesName, data;
    var isHeatMap = (rawData.categories && tui.util.isExisty(rawData.categories.x));

    if (rawData) {
        if (isHeatMap) {
            categories = rawData.categories.x;
        } else if (rawData.categories) {
            categories = rawData.categories;
        }

        resultArray.push([''].concat(categories));

        tui.util.forEach(rawData.series, function(seriesDatum) {
            tui.util.forEach(seriesDatum, function(seriesItem, index) {
                if (isHeatMap) {
                    seriesName = rawData.categories.y[index];
                    data = seriesItem;
                } else {
                    seriesName = seriesItem.name;
                    data = seriesItem.data;
                }

                resultArray.push([seriesName].concat(data));
            });
        });
    }

    return resultArray;
}

/**
 * Return download method name of current browser supports
 * @returns {string}
 * @private
 */
function _getDownloadMethod() {
    var isDownloadAttributeSupported = tui.util.isExisty(document.createElement('a').download);
    var isMsSaveOrOpenBlobSupported = window.Blob && window.navigator.msSaveOrOpenBlob;
    var method = 'none';

    if (isMsSaveOrOpenBlobSupported) {
        method = 'msSaveOrOpenBlob';
    } else if (isDownloadAttributeSupported) {
        method = 'downloadAttribute';
    }

    return method;
}

/**
 * Get table element from chart data 2D array for xls content
 * @param {Array.<Array<*>>} chartData2DArray - chart data 2D array
 * @returns {string}
 * @private
 */
function _getTableElementStringForXls(chartData2DArray) {
    var tableElementString = '
';
    tui.util.forEach(chartData2DArray, function(row, rowIndex) {
        var cellTagName = rowIndex === 0 ? 'th' : 'td';

        tableElementString += '
';

        tui.util.forEach(row, function(cell, cellIndex) {
            var cellNumberClass = (rowIndex !== 0 || cellIndex === 0) ? ' class="number"' : '';
            var cellString = '<' + cellTagName + cellNumberClass + '>' + cell + '</' + cellTagName + '>';

            tableElementString += cellString;
        });

        tableElementString += '
';
    });

    tableElementString += '
';

    return tableElementString;
}

/**
 * Make xls file with chart series data
 * @param {rawData} rawData - chart rawData
 * @returns {string} xls file content
 * @private
 */
function _makeXlsStringWithRawData(rawData) {
    var chartData2DArray = _get2DArrayFromRawData(rawData);
    var xlsString = '' +
            '' +
                '' +
                '<meta name=ProgId content=Excel.Sheet>' +
                '<meta charset=UTF-8>' +
            '' +
            '' +
                _getTableElementStringForXls(chartData2DArray) +
            '' +
        '';

    return xlsString;
}

/**
 * Make csv text with chart series data
 * @param {rawData} rawData - chart rawData
 * @param {string} itemDelimiterCharacter - item delimiter
 * @param {string} lineDelimiterCharacter - chart rawData
 * @returns {string} csv text
 * @private
 */
function _makeCsvTextWithRawData(rawData, itemDelimiterCharacter, lineDelimiterCharacter) {
    var chartData2DArray = _get2DArrayFromRawData(rawData);
    var csvText = '';
    var lineDelimiter = lineDelimiterCharacter ? lineDelimiterCharacter : '\u000a';
    var itemDelimiter = itemDelimiterCharacter ? itemDelimiterCharacter : ',';
    var lastRowIndex = chartData2DArray.length - 1;

    tui.util.forEachArray(chartData2DArray, function(row, rowIndex) {
        var lastCellIndex = row.length - 1;

        tui.util.forEachArray(row, function(cell, cellIndex) {
            var cellContent = typeof cell === 'number' ? cell : '"' + cell + '"';

            csvText += cellContent;

            if (cellIndex < lastCellIndex) {
                csvText += itemDelimiter;
            }
        });

        if (rowIndex < lastRowIndex) {
            csvText += lineDelimiter;
        }
    });

    return csvText;
}

/**
 * Download image with png format
 * @param {string} fileName - file name to save
 * @param {string} extension - extension type
 */
function _downloadImage(fileName, extension) {
    var container = document.getElementsByClassName('tui-chart')[0];
    var svg = container.getElementsByTagName('svg')[0];
    var svgParent = svg.parentNode;
    var DOMURL = window.URL || window.webkitURL || window;
    var img = new Image();
    var width = container.offsetWidth;
    var height = container.offsetHeight;
    var canvas = document.createElement('canvas');
    var ctx = canvas.getContext('2d');
    var url, blob, svgString;
    var tempWrapper = document.createElement('DIV');

    tempWrapper.appendChild(svg);

    svgString = tempWrapper.innerHTML;
    svgParent.appendChild(svg);
    tempWrapper = null;

    canvas.width = width;
    canvas.height = height;

    // remove name space for IE
    if (isIE10OrIE11) {
        svgString = svgString.replace(/xmlns:NS1=""/, '');
        svgString = svgString.replace(/NS1:xmlns:xlink="http:\/\/www\.w3\.org\/1999\/xlink"/, '');
        svgString = svgString.replace(/xmlns="http:\/\/www\.w3\.org\/2000\/svg"/, '');
        svgString = svgString.replace(/xmlns:xlink="http:\/\/www\.w3\.org\/1999\/xlink"/, '');
    }

    if (isIE10OrIE11 && ctx.drawSvg) {
        ctx.drawSvg(svgString, 0, 0);

        _download(canvas.toDataURL('image/' + extension, 1), fileName, extension);
    } else {
        blob = new Blob([svgString], {type: 'image/svg+xml'});
        url = DOMURL.createObjectURL(blob);

        img.onload = function() {
            ctx.drawImage(img, 0, 0, width, height);

            _download(canvas.toDataURL('image/' + extension, 1), fileName, extension);

            DOMURL.revokeObjectURL(url);
        };
        img.src = url;
    }
}

/**
 * Base64 string to blob
 * @param {string} base64String - base64 string
 * @returns {Blob}
 */
function base64toBlob(base64String) {
    var contentType = base64String.substr(0, base64String.indexOf(';base64,')).substr(base64String.indexOf(':') + 1);
    var sliceSize = 1024;
    var byteCharacters = atob(base64String.substr(base64String.indexOf(',') + 1));
    var byteArrays = [];
    var offset, slice, byteNumbers, i, byteArray, resultBlob;

    for (offset = 0; offset < byteCharacters.length; offset += sliceSize) {
        slice = byteCharacters.slice(offset, offset + sliceSize);

        byteNumbers = new Array(slice.length);

        for (i = 0; i < slice.length; i += 1) {
            byteNumbers[i] = slice.charCodeAt(i);
        }

        byteArray = new window.Uint8Array(byteNumbers);

        byteArrays.push(byteArray);
    }

    resultBlob = new Blob(byteArrays, {type: contentType});

    return resultBlob;
}

/**
 * Download content to file with msSaveOrOpenBlob
 * @param {string} content - file content
 * @param {string} fileName - file name
 * @param {string} extension - file extension
 * @private
 */
function _downloadWithMsSaveOrOpenBlob(content, fileName, extension) {
    var blobObject = isImageExtension(extension) ? base64toBlob(content) : new Blob([content]);
    window.navigator.msSaveOrOpenBlob(blobObject, fileName + '.' + extension);
}

/**
 * Download content to file with anchor element's download attribute
 * @param {string} content - file content
 * @param {string} fileName - file name
 * @param {string} extension - file extension
 * @private
 */
function _downloadWithAnchorElementDownloadAttribute(content, fileName, extension) {
    var anchorElement = document.createElement('a');
    var data = extension !== 'csv' ? window.btoa(unescape(encodeURIComponent(content))) : encodeURIComponent(content);
    var dataUri;

    if (!content) {
        return;
    }

    if (isImageExtension(extension)) {
        dataUri = content;
    } else {
        dataUri = DATA_URI_HEADERS[extension] + data;
    }

    anchorElement.href = dataUri;
    anchorElement.target = '_blank';
    anchorElement.download = fileName + '.' + extension;

    document.body.appendChild(anchorElement);

    anchorElement.click();
    anchorElement.remove();
}

/**
 * Download content to file with given filename and extension
 * @param {string} content - file content
 * @param {string} fileName - file name
 * @param {string} extension - file extension
 * @private
 */
function _download(content, fileName, extension) {
    var downloadMethod = _getDownloadMethod();

    if (downloadMethod && tui.util.isString(content)) {
        DOWNLOADER_FUNCTIONS[downloadMethod](content, fileName, extension);
    }
}

/**
 * Download chart data with given export type
 * @param {string} extension - file extension
 * @param {object} rawData - chart raw data
 * @param {string} chartTitle - chart title
 */
function exportChartData(extension, rawData, chartTitle) {
    var fileName = chartTitle;

    if (isImageExtension(extension)) {
        _downloadImage(fileName, extension);
    } else {
        _download(EXPORT_DATA_MAKERS[extension](rawData), fileName, extension);
    }
}

/**
 * Return boolean value for browser support client side download
 * @returns {boolean}
 */
function isBrowserSupportClientSideDownload() {
    var method = _getDownloadMethod();

    return method !== 'none';
}

function isDataDownloadAvailable(seriesDataModels) {
    var result = true;

    tui.util.forEach(seriesDataModels, function(seriesDataModel) {
        if (seriesDataModel.isCoordinateType) {
            result = false;
        }

        return false;
    });

    return result;
}

module.exports = {
    exportChartData: exportChartData,
    isBrowserSupportClientSideDownload: isBrowserSupportClientSideDownload,
    isDataDownloadAvailable: isDataDownloadAvailable,
    isImageDownloadAvailable: isImageDownloadAvailable,
    isImageExtension: isImageExtension
};
<commit_msg>chore: Add JSDoc and comment<commit_after>/**
 * @fileOverview Chart data exporter
 * @author NHN Ent.
 *         FE Development Lab <dl_javascript@nhnent.com>
 */

'use strict';
var isIE10OrIE11 = tui.util.browser.msie && (tui.util.browser.version === 10 || tui.util.browser.version === 11);

var isImageDownloadAvailable = isBrowserSupportClientSideDownload()
    && (!isIE10OrIE11 || (isIE10OrIE11 && document.createElement('canvas').getContext('2d').drawSvg));

var DATA_URI_HEADERS = {
    xls: 'data:application/vnd.ms-excel;base64,',
    csv: 'data:text/csv,'
};
var EXPORT_DATA_MAKERS = {
    xls: _makeXlsStringWithRawData,
    csv: _makeCsvTextWithRawData
};
var DOWNLOADER_FUNCTIONS = {
    downloadAttribute: _downloadWithAnchorElementDownloadAttribute,
    msSaveOrOpenBlob: _downloadWithMsSaveOrOpenBlob
};

/**
 * Return given extension type is image format
 * @param {string} extension extension
 * @returns {boolean}
 */
function isImageExtension(extension) {
    return extension === 'png' || extension === 'jpeg';
}

/**
 * Get pivoted second dimension array from table to use element.innerText
 * @param {rawData} rawData - chart's raw data
 * @returns {Array.<Array>}
 * @private
 */
function _get2DArrayFromRawData(rawData) {
    var resultArray = [];
    var categories, seriesName, data;
    var isHeatMap = (rawData.categories && tui.util.isExisty(rawData.categories.x));

    if (rawData) {
        if (isHeatMap) {
            categories = rawData.categories.x;
        } else if (rawData.categories) {
            categories = rawData.categories;
        }

        resultArray.push([''].concat(categories));

        tui.util.forEach(rawData.series, function(seriesDatum) {
            tui.util.forEach(seriesDatum, function(seriesItem, index) {
                if (isHeatMap) {
                    seriesName = rawData.categories.y[index];
                    data = seriesItem;
                } else {
                    seriesName = seriesItem.name;
                    data = seriesItem.data;
                }

                resultArray.push([seriesName].concat(data));
            });
        });
    }

    return resultArray;
}

/**
 * Return download method name of current browser supports
 * @returns {string}
 * @private
 */
function _getDownloadMethod() {
    var isDownloadAttributeSupported = tui.util.isExisty(document.createElement('a').download);
    var isMsSaveOrOpenBlobSupported = window.Blob && window.navigator.msSaveOrOpenBlob;
    var method = 'none';

    if (isMsSaveOrOpenBlobSupported) {
        method = 'msSaveOrOpenBlob';
    } else if (isDownloadAttributeSupported) {
        method = 'downloadAttribute';
    }

    return method;
}

/**
 * Get table element from chart data 2D array for xls content
 * @param {Array.<Array<*>>} chartData2DArray - chart data 2D array
 * @returns {string}
 * @private
 */
function _getTableElementStringForXls(chartData2DArray) {
    var tableElementString = '
';
    tui.util.forEach(chartData2DArray, function(row, rowIndex) {
        var cellTagName = rowIndex === 0 ? 'th' : 'td';

        tableElementString += '
';

        tui.util.forEach(row, function(cell, cellIndex) {
            var cellNumberClass = (rowIndex !== 0 || cellIndex === 0) ? ' class="number"' : '';
            var cellString = '<' + cellTagName + cellNumberClass + '>' + cell + '</' + cellTagName + '>';

            tableElementString += cellString;
        });

        tableElementString += '
';
    });

    tableElementString += '
';

    return tableElementString;
}

/**
 * Make xls file with chart series data
 * @param {rawData} rawData - chart rawData
 * @returns {string} xls file content
 * @private
 */
function _makeXlsStringWithRawData(rawData) {
    var chartData2DArray = _get2DArrayFromRawData(rawData);
    var xlsString = '' +
            '' +
                '' +
                '<meta name=ProgId content=Excel.Sheet>' +
                '<meta charset=UTF-8>' +
            '' +
            '' +
                _getTableElementStringForXls(chartData2DArray) +
            '' +
        '';

    return xlsString;
}

/**
 * Make csv text with chart series data
 * @param {rawData} rawData - chart rawData
 * @param {string} itemDelimiterCharacter - item delimiter
 * @param {string} lineDelimiterCharacter - chart rawData
 * @returns {string} csv text
 * @private
 */
function _makeCsvTextWithRawData(rawData, itemDelimiterCharacter, lineDelimiterCharacter) {
    var chartData2DArray = _get2DArrayFromRawData(rawData);
    var csvText = '';
    var lineDelimiter = lineDelimiterCharacter ? lineDelimiterCharacter : '\u000a';
    var itemDelimiter = itemDelimiterCharacter ? itemDelimiterCharacter : ',';
    var lastRowIndex = chartData2DArray.length - 1;

    tui.util.forEachArray(chartData2DArray, function(row, rowIndex) {
        var lastCellIndex = row.length - 1;

        tui.util.forEachArray(row, function(cell, cellIndex) {
            var cellContent = typeof cell === 'number' ? cell : '"' + cell + '"';

            csvText += cellContent;

            if (cellIndex < lastCellIndex) {
                csvText += itemDelimiter;
            }
        });

        if (rowIndex < lastRowIndex) {
            csvText += lineDelimiter;
        }
    });

    return csvText;
}

/**
 * Download image with png format
 * @param {string} fileName - file name to save
 * @param {string} extension - extension type
 * @private
 */
function _downloadImage(fileName, extension) {
    var container = document.getElementsByClassName('tui-chart')[0];
    var svg = container.getElementsByTagName('svg')[0];
    var svgParent = svg.parentNode;
    var DOMURL = window.URL || window.webkitURL || window;
    var img = new Image();
    var width = container.offsetWidth;
    var height = container.offsetHeight;
    var canvas = document.createElement('canvas');
    var ctx = canvas.getContext('2d');
    var url, blob, svgString;
    var tempWrapper = document.createElement('DIV');

    tempWrapper.appendChild(svg);

    svgString = tempWrapper.innerHTML;
    svgParent.appendChild(svg);
    tempWrapper = null;

    canvas.width = width;
    canvas.height = height;

    // remove name space for IE
    if (isIE10OrIE11) {
        svgString = svgString.replace(/xmlns:NS1=""/, '');
        svgString = svgString.replace(/NS1:xmlns:xlink="http:\/\/www\.w3\.org\/1999\/xlink"/, '');
        svgString = svgString.replace(/xmlns="http:\/\/www\.w3\.org\/2000\/svg"/, '');
        svgString = svgString.replace(/xmlns:xlink="http:\/\/www\.w3\.org\/1999\/xlink"/, '');
    }

    if (isIE10OrIE11 && ctx.drawSvg) {
        ctx.drawSvg(svgString, 0, 0);

        _download(canvas.toDataURL('image/' + extension, 1), fileName, extension);
    } else {
        blob = new Blob([svgString], {type: 'image/svg+xml'});
        url = DOMURL.createObjectURL(blob);

        img.onload = function() {
            ctx.drawImage(img, 0, 0, width, height);

            _download(canvas.toDataURL('image/' + extension, 1), fileName, extension);

            DOMURL.revokeObjectURL(url);
        };
        img.src = url;
    }
}

/**
 * Base64 string to blob
 * @param {string} base64String - base64 string
 * @returns {Blob}
 * @private
 */
function base64toBlob(base64String) {
    var contentType = base64String.substr(0, base64String.indexOf(';base64,')).substr(base64String.indexOf(':') + 1);
    var sliceSize = 1024;
    var byteCharacters = atob(base64String.substr(base64String.indexOf(',') + 1));
    var byteArrays = [];
    var offset, slice, byteNumbers, i, byteArray, resultBlob;

    for (offset = 0; offset < byteCharacters.length; offset += sliceSize) {
        slice = byteCharacters.slice(offset, offset + sliceSize);

        byteNumbers = new Array(slice.length);

        for (i = 0; i < slice.length; i += 1) {
            byteNumbers[i] = slice.charCodeAt(i);
        }

        byteArray = new window.Uint8Array(byteNumbers);

        byteArrays.push(byteArray);
    }

    resultBlob = new Blob(byteArrays, {type: contentType});

    return resultBlob;
}

/**
 * Download content to file with msSaveOrOpenBlob
 * @param {string} content - file content
 * @param {string} fileName - file name
 * @param {string} extension - file extension
 * @private
 */
function _downloadWithMsSaveOrOpenBlob(content, fileName, extension) {
    var blobObject = isImageExtension(extension) ? base64toBlob(content) : new Blob([content]);
    window.navigator.msSaveOrOpenBlob(blobObject, fileName + '.' + extension);
}

/**
 * Download content to file with anchor element's download attribute
 * @param {string} content - file content
 * @param {string} fileName - file name
 * @param {string} extension - file extension
 * @private
 */
function _downloadWithAnchorElementDownloadAttribute(content, fileName, extension) {
    var anchorElement = document.createElement('a');
    var data = extension !== 'csv' ? window.btoa(unescape(encodeURIComponent(content))) : encodeURIComponent(content);
    var dataUri;

    if (!content) {
        return;
    }

    if (isImageExtension(extension)) {
        dataUri = content;
    } else {
        dataUri = DATA_URI_HEADERS[extension] + data;
    }

    anchorElement.href = dataUri;
    anchorElement.target = '_blank';
    anchorElement.download = fileName + '.' + extension;

    document.body.appendChild(anchorElement);

    anchorElement.click();
    anchorElement.remove();
}

/**
 * Download content to file with given filename and extension
 * @param {string} content - file content
 * @param {string} fileName - file name
 * @param {string} extension - file extension
 * @private
 */
function _download(content, fileName, extension) {
    var downloadMethod = _getDownloadMethod();

    if (downloadMethod && tui.util.isString(content)) {
        DOWNLOADER_FUNCTIONS[downloadMethod](content, fileName, extension);
    }
}

/**
 * Download chart data with given export type
 * @param {string} extension - file extension
 * @param {object} rawData - chart raw data
 * @param {string} chartTitle - chart title
 */
function exportChartData(extension, rawData, chartTitle) {
    var fileName = chartTitle;

    // Image downloads asynchronous because of waiting until image loaded from svg data URI.
    if (isImageExtension(extension)) {
        _downloadImage(fileName, extension);
    } else {
        _download(EXPORT_DATA_MAKERS[extension](rawData), fileName, extension);
    }
}

/**
 * Return boolean value for browser support client side download
 * @returns {boolean}
 */
function isBrowserSupportClientSideDownload() {
    var method = _getDownloadMethod();

    return method !== 'none';
}

/**
 * Return boolean value for chart data is able to export
 * @param {object} seriesDataModels series data model
 * @returns {boolean}
 */
function isDataDownloadAvailable(seriesDataModels) {
    var result = true;

    tui.util.forEach(seriesDataModels, function(seriesDataModel) {
        if (seriesDataModel.isCoordinateType) {
            result = false;
        }

        return false;
    });

    return result;
}

module.exports = {
    exportChartData: exportChartData,
    isBrowserSupportClientSideDownload: isBrowserSupportClientSideDownload,
    isDataDownloadAvailable: isDataDownloadAvailable,
    isImageDownloadAvailable: isImageDownloadAvailable,
    isImageExtension: isImageExtension
};
<|endoftext|>


<commit_before>define([
  'summernote/base/core/agent',
  'summernote/base/core/dom',
  'summernote/base/core/func',
  'summernote/base/core/list',
  'summernote/base/core/range'
], function (agent, dom, func, list, range) {
  var AirPopover = function (context) {
    var self = this;

    var $document = $(document);
    var $editable = context.layoutInfo.editable;
    var editable = $editable[0];

    var options = context.options;

    this.events = {
      'summernote.keyup summernote.mouseup summernote.scroll': function () {
        self.update();
        self.setLastRange();
      },
      'summernote.change summernote.dialog.shown': function () {
        self.update();
        self.setLastRange();
      },
      'summernote.focusout': function (we, e) {
        // [workaround] Firefox doesn't support relatedTarget on focusout
        //  - Ignore hide action on focus out in FF.
        if (agent.isFF) {
          return;
        }

        if (!e.relatedTarget) {
          self.hide();
        }
      }
    };

    $editable.on('mousedown', function () {
      $document.on('mouseup', function () {
        mouseUp();
      });
      $(window).on('mouseup', function () {
        mouseUp();
      });
      $(window).on('blur', function () {
        mouseUp();
      });

      function mouseUp() {
        self.update();
        self.setLastRange();

        $document.off('mouseup');
        $(window).off('mouseup');
        $(window).off('blur');
      }
    });

    this.shouldInitialize = function () {
      return options.airMode &&
        options.popover &&
        options.popover.air &&
        typeof options.popover.air.hide === 'function' &&
        typeof options.popover.air.update === 'function';
    };

    this.initialize = function () {
      this.fontInstalledMap = {};
    };

    this.destroy = function () {
      delete this.fontInstalledMap;
    };

    this.isFontInstalled = function (name) {
      if (!self.fontInstalledMap.hasOwnProperty(name)) {
        self.fontInstalledMap[name] = agent.isFontInstalled(name) ||
          list.contains(options.fontNamesIgnoreCheck, name);
      }

      return self.fontInstalledMap[name];
    };

    this.getAvailablesFont = function () {
      return options.fontNames.filter(self.isFontInstalled);
    };

    this.setLastRange = function () {
      var currentRange = range.createFromSelection();
      if (currentRange && (self.isAncestor(currentRange.sc, editable) || currentRange.sc === editable)) {
        self.lastRange = currentRange ? currentRange : self.lastRange;
      }
      return self.lastRange;
    };

    this.getLastRange = function () {
      return self.lastRange;
    };

    this.insertNode = function (node, rng, deep) {
      if (!$editable.is(':focus')) {
        $editable.focus();
      }
      rng = rng || self.lastRange || range.create(editable);
      rng = rng.deleteContents();
      var splitFn = deep ? splitPointDeep : splitPoint,
          info = splitFn(rng.getStartPoint());

      if (info.rightNode && info.rightNode.parentNode) {
        info.rightNode.parentNode.insertBefore(node, info.rightNode);
      } else {
        if (!info.container || !self.isAncestor(info.container, editable)) {
          editable.appendChild(node);
        } else {
          info.container.appendChild(node);
        }
      }

      self.lastRange = range.createFromNodeAfter(node);
      self.lastRange.select();
    };

    this.moveCursorToEnd = function () {
      if (editable.lastChild || editable.lastElementChild) {
        self.lastRange = range.createFromNodeAfter(editable.lastChild || editable.lastElementChild);
        self.lastRange.select();
      }
      return self.lastRange;
    };

    this.pasteHTML = function (markup, rng, deep) {
      if (!$editable.is(':focus')) {
        $editable.focus();
      }
      rng = rng || self.lastRange || range.create(editable);
      rng = rng.deleteContents();
      var splitFn = deep ? splitPointDeep : splitPoint,
          info = splitFn(rng.getStartPoint());

      var contentsContainer = $('

').html(markup)[0];
      if (contentsContainer && contentsContainer.childNodes) {
        var childNodes = list.from(contentsContainer.childNodes);

        var contents = childNodes.map(function (childNode) {
          if (info.rightNode && info.rightNode.parentNode) {
            info.rightNode.parentNode.insertBefore(childNode, info.rightNode);
          } else {
            if (!info.container || !self.isAncestor(info.container, editable)) {
              editable.appendChild(childNode);
            } else {
              info.container.appendChild(childNode);
            }
          }
          return childNode;
        });

        self.lastRange = range.createFromNodeAfter(list.last(contents));
        self.lastRange.select();
      }
    };

    this.update = function (force) {
      var styleInfo = context.invoke('editor.currentStyle');
      if (styleInfo.range && (!styleInfo.range.isCollapsed() || force)) {
        var rect = list.last(styleInfo.range.getClientRects());
        var bnd;
        if (rect) {
          bnd = func.rect2bnd(rect);
          options.popover.air.update(styleInfo, bnd);
        } else if (force) {
          options.popover.air.update(styleInfo, null);
        }
      } else {
        this.hide();
      }
    };

    this.hide = function () {
      options.popover.air.hide();
    };

    this.isAncestor = function (node, parentNode) {
      while (node) {
        if (node === parentNode) { return true; }

        node = node.parentNode;
      }
      return null;
    };

    this.removeFormat = function (rng) {
      removeFormat(rng, function (node) {
        return node && /^BACKQUOTE|^A|^LI|^UL|^EM|^B|^I|^STRONG|^H[1-7]/.test(node.nodeName.toUpperCase());
      });
    };

    this.unquote = function (rng) {
      rng = self.getFullParaRange(rng);
      rng = self.createPara(rng);
      removeFormat(rng, dom.isBlockquote);
    };

    this.splitPara = function (rng) {
      if (!$editable.is(':focus')) {
        $editable.focus();
      }
      rng = rng || self.lastRange || range.create(editable);

      context.invoke('editor.beforeCommand');

      splitPointDeep(rng.getStartPoint());

      context.invoke('editor.afterCommand');
    };

    this.createPara = function (rng) {
      if (!$editable.is(':focus')) {
        $editable.focus();
      }
      rng = rng || self.lastRange || range.create(editable);

      context.invoke('editor.beforeCommand');

      var info = {},
          leftPoint, startPoint, endPoint,
          leftRange, rightRange;
      if (rng.sc === rng.ec && rng.so === rng.eo) {
        info.left = splitPointDeep(rng.getStartPoint());
      } else {
        startPoint = rng.getStartPoint();
        endPoint = rng.getEndPoint();
        if (startPoint.node === endPoint.node) {
          endPoint.offset -= startPoint.offset;
        }
        info.left = splitPointDeep(startPoint);
        if (info.left.rightNode) {
          leftRange = range.createFromNodeBefore(info.left.rightNode);
          leftPoint = dom.prevPoint(leftRange.getStartPoint());
          if (leftPoint) {
            leftRange = range.create(leftPoint.node, leftPoint.offset, leftPoint.node, leftPoint.offset);
          }
        }
        info.right = splitPointDeep(endPoint);
      }

      if (info.right) {
        if (info.right.rightNode) {
          rightRange = range.createFromNodeBefore(info.right.rightNode);
          leftPoint = dom.prevPointUntil(rightRange.getStartPoint(), dom.isVisiblePoint);
          if (leftPoint) {
            rightRange = range.create(leftPoint.node, leftPoint.offset, leftPoint.node, leftPoint.offset);
          }
        } else {
          rightRange = self.moveCursorToEnd();
        }
      } else if (!leftRange) {
        leftRange = self.moveCursorToEnd();
      }

      if (rightRange) {
        startPoint = leftRange.getStartPoint();
        endPoint = rightRange.getStartPoint();
        rng = range.create(startPoint.node, startPoint.offset, endPoint.node, endPoint.offset);
      } else {
        startPoint = leftRange.getStartPoint();
        rng = range.create(startPoint.node, startPoint.offset, startPoint.node, startPoint.offset);
      }

      self.lastRange = rng;
      self.lastRange.select();

      context.invoke('editor.afterCommand');

      return self.lastRange;
    };

    this.getFullParaRange = function (rng) {
      rng = rng || self.lastRange || range.create(editable);
      if (!rng) {
        return null;
      }

      var startPoint = rng.getStartPoint(),
          endPoint = rng.getEndPoint(),
          leftPoint = getStartLinePoint(startPoint),
          rightPoint = getEndLinePoint(endPoint);

      if (leftPoint && !dom.isSamePoint(leftPoint, startPoint)) {
        startPoint = leftPoint;
      }

      if (rightPoint && !dom.isSamePoint(rightPoint, endPoint)) {
        endPoint = rightPoint;
      }

      return range.create(startPoint.node, startPoint.offset, endPoint.node, endPoint.offset);

      function getStartLinePoint(point) {
        return dom.prevPointUntil(point, function (newPoint) {
          var prevPoint = dom.prevPoint(newPoint);
          return (dom.isLeftEdgePoint(newPoint) && (dom.isPara(newPoint.node) || dom.isBlockquote(newPoint.node))) ||
            (prevPoint.node && /^BR|^HR|^IFRAME/.test(prevPoint.node.nodeName.toUpperCase()));
        });
      }

      function getEndLinePoint(point) {
        return dom.nextPointUntil(point, function (newPoint) {
          return (dom.isRightEdgePoint(newPoint) && (dom.isPara(newPoint.node) || dom.isBlockquote(newPoint.node))) ||
            (newPoint.node && /^BR|^HR|^IFRAME/.test(newPoint.node.nodeName.toUpperCase()));
        });
      }
    };

    function removeFormat(rng, pred) {
      if (!pred) {
        return;
      }

      if (!$editable.is(':focus')) {
        $editable.focus();
      }
      rng = rng || self.lastRange || range.create(editable);

      context.invoke('editor.beforeCommand');

      if (rng.sc !== rng.ec) {
        $.each(list.from(rng.nodes()), removeFormatNode);
      } else {
        removeFormatNode(rng.sc);
      }

      context.invoke('editor.afterCommand');

      function removeFormatNode(idx, rangeNode) {
        var ancestors = dom.listAncestor(rangeNode);
        ancestors.filter(pred).forEach(function (node) {
          var ancestor = node.parentNode;
          if (ancestor) {
            $.each(list.from(node.childNodes), function (idx, child) {
              ancestor.insertBefore(child, node);
            });
            ancestor.removeChild(node);
          }
        });
      }
    }

    function splitPoint(point) {
      if (dom.isEdgePoint(point) && dom.isRightEdgePoint(point)) {
        if (dom.isText(point.node)) {
          return {
            rightNode: point.node.nextSibling,
            container: point.node.parentNode
          };
        }

        return {
          container: point.node
        };
      }

      // split #text
      if (dom.isText(point.node)) {
        return {
          rightNode: point.node.splitText(point.offset),
          container: point.node.parentNode
        };
      } else {
        if (point.offset < point.node.childNodes.length) {
          return {
            rightNode: point.node.childNodes[point.offset],
            container: point.node
          };
        }
        return {
          container: point.node
        };
      }
    }

    function splitPointDeep(point) {
      var leftPoint = dom.prevPointUntil(point, function (newPoint) {
        return !dom.isLeftEdgePoint(newPoint) || (dom.isLeftEdgePoint(newPoint) && newPoint.node === editable);
      });
      var rightPoint = dom.nextPointUntil(point, function (newPoint) {
        return !dom.isRightEdgePoint(newPoint) || (dom.isRightEdgePoint(newPoint) && newPoint.node === editable);
      });

      if (leftPoint && !dom.isSamePoint(leftPoint, point)) {
        point = leftPoint;
      } else if (rightPoint && !dom.isSamePoint(rightPoint, point)) {
        point = rightPoint;
      }

      var brNode;
      leftPoint = dom.prevPoint(point);
      rightPoint = dom.nextPoint(point);

      if (leftPoint && dom.isBR(leftPoint.node)) {
        brNode = leftPoint.node;
        leftPoint = dom.prevPoint(leftPoint);
        brNode.parentNode.removeChild(brNode);
        if (leftPoint) {
          point = leftPoint;
        }
      } else if (rightPoint && dom.isBR(rightPoint.node)) {
        brNode = rightPoint.node;
        rightPoint = dom.prevPoint(rightPoint);
        brNode.parentNode.removeChild(brNode);
        if (rightPoint) {
          point = rightPoint;
        }
      }

      var leftRng = range.create(editable, 0, point.node, point.offset),
          leftContent = leftRng.nativeRange().extractContents(),
          rightNode;

      if (list.from(editable.childNodes).length) {
        rightNode = editable.childNodes[0];
        editable.insertBefore(leftContent, rightNode);
      } else {
        editable.appendChild(leftContent);
      }

      return {
        container: editable,
        rightNode: rightNode
      };
    }
  };

  return AirPopover;
});
<commit_msg>feat(airPopover): Add beforeCommand & afterCommand triggers<commit_after>define([
  'summernote/base/core/agent',
  'summernote/base/core/dom',
  'summernote/base/core/func',
  'summernote/base/core/list',
  'summernote/base/core/range'
], function (agent, dom, func, list, range) {
  var AirPopover = function (context) {
    var self = this;

    var $document = $(document);
    var $editable = context.layoutInfo.editable;
    var editable = $editable[0];

    var options = context.options;

    this.events = {
      'summernote.keyup summernote.mouseup summernote.scroll': function () {
        self.update();
        self.setLastRange();
      },
      'summernote.change summernote.dialog.shown': function () {
        self.update();
        self.setLastRange();
      },
      'summernote.focusout': function (we, e) {
        // [workaround] Firefox doesn't support relatedTarget on focusout
        //  - Ignore hide action on focus out in FF.
        if (agent.isFF) {
          return;
        }

        if (!e.relatedTarget) {
          self.hide();
        }
      }
    };

    $editable.on('mousedown', function () {
      $document.on('mouseup', function () {
        mouseUp();
      });
      $(window).on('mouseup', function () {
        mouseUp();
      });
      $(window).on('blur', function () {
        mouseUp();
      });

      function mouseUp() {
        self.update();
        self.setLastRange();

        $document.off('mouseup');
        $(window).off('mouseup');
        $(window).off('blur');
      }
    });

    this.shouldInitialize = function () {
      return options.airMode &&
        options.popover &&
        options.popover.air &&
        typeof options.popover.air.hide === 'function' &&
        typeof options.popover.air.update === 'function';
    };

    this.initialize = function () {
      this.fontInstalledMap = {};
    };

    this.destroy = function () {
      delete this.fontInstalledMap;
    };

    this.isFontInstalled = function (name) {
      if (!self.fontInstalledMap.hasOwnProperty(name)) {
        self.fontInstalledMap[name] = agent.isFontInstalled(name) ||
          list.contains(options.fontNamesIgnoreCheck, name);
      }

      return self.fontInstalledMap[name];
    };

    this.getAvailablesFont = function () {
      return options.fontNames.filter(self.isFontInstalled);
    };

    this.setLastRange = function () {
      var currentRange = range.createFromSelection();
      if (currentRange && (self.isAncestor(currentRange.sc, editable) || currentRange.sc === editable)) {
        self.lastRange = currentRange ? currentRange : self.lastRange;
      }
      return self.lastRange;
    };

    this.getLastRange = function () {
      return self.lastRange;
    };

    this.insertNode = function (node, rng, deep) {
      context.invoke('editor.beforeCommand');
      if (!$editable.is(':focus')) {
        $editable.focus();
      }
      rng = rng || self.lastRange || range.create(editable);
      rng = rng.deleteContents();
      var splitFn = deep ? splitPointDeep : splitPoint,
          info = splitFn(rng.getStartPoint());

      if (info.rightNode && info.rightNode.parentNode) {
        info.rightNode.parentNode.insertBefore(node, info.rightNode);
      } else {
        if (!info.container || !self.isAncestor(info.container, editable)) {
          editable.appendChild(node);
        } else {
          info.container.appendChild(node);
        }
      }

      self.lastRange = range.createFromNodeAfter(node);
      self.lastRange.select();
      context.invoke('editor.afterCommand');
    };

    this.moveCursorToEnd = function () {
      context.invoke('editor.beforeCommand');
      if (editable.lastChild || editable.lastElementChild) {
        self.lastRange = range.createFromNodeAfter(editable.lastChild || editable.lastElementChild);
        self.lastRange.select();
      }
      context.invoke('editor.afterCommand');
      return self.lastRange;
    };

    this.pasteHTML = function (markup, rng, deep) {
      context.invoke('editor.beforeCommand');
      if (!$editable.is(':focus')) {
        $editable.focus();
      }
      rng = rng || self.lastRange || range.create(editable);
      rng = rng.deleteContents();
      var splitFn = deep ? splitPointDeep : splitPoint,
          info = splitFn(rng.getStartPoint());

      var contentsContainer = $('

').html(markup)[0];
      if (contentsContainer && contentsContainer.childNodes) {
        var childNodes = list.from(contentsContainer.childNodes);

        var contents = childNodes.map(function (childNode) {
          if (info.rightNode && info.rightNode.parentNode) {
            info.rightNode.parentNode.insertBefore(childNode, info.rightNode);
          } else {
            if (!info.container || !self.isAncestor(info.container, editable)) {
              editable.appendChild(childNode);
            } else {
              info.container.appendChild(childNode);
            }
          }
          return childNode;
        });

        self.lastRange = range.createFromNodeAfter(list.last(contents));
        self.lastRange.select();
      }
      context.invoke('editor.afterCommand');
    };

    this.update = function (force) {
      var styleInfo = context.invoke('editor.currentStyle');
      if (styleInfo.range && (!styleInfo.range.isCollapsed() || force)) {
        var rect = list.last(styleInfo.range.getClientRects());
        var bnd;
        if (rect) {
          bnd = func.rect2bnd(rect);
          options.popover.air.update(styleInfo, bnd);
        } else if (force) {
          options.popover.air.update(styleInfo, null);
        }
      } else {
        this.hide();
      }
    };

    this.hide = function () {
      options.popover.air.hide();
    };

    this.isAncestor = function (node, parentNode) {
      while (node) {
        if (node === parentNode) { return true; }

        node = node.parentNode;
      }
      return null;
    };

    this.removeFormat = function (rng) {
      removeFormat(rng, function (node) {
        return node && /^BACKQUOTE|^A|^LI|^UL|^EM|^B|^I|^STRONG|^H[1-7]/.test(node.nodeName.toUpperCase());
      });
    };

    this.unquote = function (rng) {
      rng = self.getFullParaRange(rng);
      rng = self.createPara(rng);
      removeFormat(rng, dom.isBlockquote);
    };

    this.splitPara = function (rng) {
      if (!$editable.is(':focus')) {
        $editable.focus();
      }
      rng = rng || self.lastRange || range.create(editable);

      context.invoke('editor.beforeCommand');

      splitPointDeep(rng.getStartPoint());

      context.invoke('editor.afterCommand');
    };

    this.createPara = function (rng) {
      if (!$editable.is(':focus')) {
        $editable.focus();
      }
      rng = rng || self.lastRange || range.create(editable);

      var info = {},
          leftPoint, startPoint, endPoint,
          leftRange, rightRange;
      if (rng.sc === rng.ec && rng.so === rng.eo) {
        info.left = splitPointDeep(rng.getStartPoint());
      } else {
        startPoint = rng.getStartPoint();
        endPoint = rng.getEndPoint();
        if (startPoint.node === endPoint.node) {
          endPoint.offset -= startPoint.offset;
        }
        info.left = splitPointDeep(startPoint);
        if (info.left.rightNode) {
          leftRange = range.createFromNodeBefore(info.left.rightNode);
          leftPoint = dom.prevPoint(leftRange.getStartPoint());
          if (leftPoint) {
            leftRange = range.create(leftPoint.node, leftPoint.offset, leftPoint.node, leftPoint.offset);
          }
        }
        info.right = splitPointDeep(endPoint);
      }

      if (info.right) {
        if (info.right.rightNode) {
          rightRange = range.createFromNodeBefore(info.right.rightNode);
          leftPoint = dom.prevPointUntil(rightRange.getStartPoint(), dom.isVisiblePoint);
          if (leftPoint) {
            rightRange = range.create(leftPoint.node, leftPoint.offset, leftPoint.node, leftPoint.offset);
          }
        } else {
          rightRange = self.moveCursorToEnd();
        }
      } else if (!leftRange) {
        leftRange = self.moveCursorToEnd();
      }

      if (rightRange) {
        startPoint = leftRange.getStartPoint();
        endPoint = rightRange.getStartPoint();
        rng = range.create(startPoint.node, startPoint.offset, endPoint.node, endPoint.offset);
      } else {
        startPoint = leftRange.getStartPoint();
        rng = range.create(startPoint.node, startPoint.offset, startPoint.node, startPoint.offset);
      }

      self.lastRange = rng;
      self.lastRange.select();

      return self.lastRange;
    };

    this.getFullParaRange = function (rng) {
      rng = rng || self.lastRange || range.create(editable);
      if (!rng) {
        return null;
      }

      var startPoint = rng.getStartPoint(),
          endPoint = rng.getEndPoint(),
          leftPoint = getStartLinePoint(startPoint),
          rightPoint = getEndLinePoint(endPoint);

      if (leftPoint && !dom.isSamePoint(leftPoint, startPoint)) {
        startPoint = leftPoint;
      }

      if (rightPoint && !dom.isSamePoint(rightPoint, endPoint)) {
        endPoint = rightPoint;
      }

      return range.create(startPoint.node, startPoint.offset, endPoint.node, endPoint.offset);

      function getStartLinePoint(point) {
        return dom.prevPointUntil(point, function (newPoint) {
          var prevPoint = dom.prevPoint(newPoint);
          return (dom.isLeftEdgePoint(newPoint) && (dom.isPara(newPoint.node) || dom.isBlockquote(newPoint.node))) ||
            (prevPoint.node && /^BR|^HR|^IFRAME/.test(prevPoint.node.nodeName.toUpperCase()));
        });
      }

      function getEndLinePoint(point) {
        return dom.nextPointUntil(point, function (newPoint) {
          return (dom.isRightEdgePoint(newPoint) && (dom.isPara(newPoint.node) || dom.isBlockquote(newPoint.node))) ||
            (newPoint.node && /^BR|^HR|^IFRAME/.test(newPoint.node.nodeName.toUpperCase()));
        });
      }
    };

    function removeFormat(rng, pred) {
      if (!pred) {
        return;
      }

      if (!$editable.is(':focus')) {
        $editable.focus();
      }
      rng = rng || self.lastRange || range.create(editable);

      context.invoke('editor.beforeCommand');

      if (rng.sc !== rng.ec) {
        $.each(list.from(rng.nodes()), removeFormatNode);
      } else {
        removeFormatNode(rng.sc);
      }

      context.invoke('editor.afterCommand');

      function removeFormatNode(idx, rangeNode) {
        var ancestors = dom.listAncestor(rangeNode);
        ancestors.filter(pred).forEach(function (node) {
          var ancestor = node.parentNode;
          if (ancestor) {
            $.each(list.from(node.childNodes), function (idx, child) {
              ancestor.insertBefore(child, node);
            });
            ancestor.removeChild(node);
          }
        });
      }
    }

    function splitPoint(point) {
      if (dom.isEdgePoint(point) && dom.isRightEdgePoint(point)) {
        if (dom.isText(point.node)) {
          return {
            rightNode: point.node.nextSibling,
            container: point.node.parentNode
          };
        }

        return {
          container: point.node
        };
      }

      // split #text
      if (dom.isText(point.node)) {
        return {
          rightNode: point.node.splitText(point.offset),
          container: point.node.parentNode
        };
      } else {
        if (point.offset < point.node.childNodes.length) {
          return {
            rightNode: point.node.childNodes[point.offset],
            container: point.node
          };
        }
        return {
          container: point.node
        };
      }
    }

    function splitPointDeep(point) {
      var leftPoint = dom.prevPointUntil(point, function (newPoint) {
        return !dom.isLeftEdgePoint(newPoint) || (dom.isLeftEdgePoint(newPoint) && newPoint.node === editable);
      });
      var rightPoint = dom.nextPointUntil(point, function (newPoint) {
        return !dom.isRightEdgePoint(newPoint) || (dom.isRightEdgePoint(newPoint) && newPoint.node === editable);
      });

      if (leftPoint && !dom.isSamePoint(leftPoint, point)) {
        point = leftPoint;
      } else if (rightPoint && !dom.isSamePoint(rightPoint, point)) {
        point = rightPoint;
      }

      var brNode;
      leftPoint = dom.prevPoint(point);
      rightPoint = dom.nextPoint(point);

      if (leftPoint && dom.isBR(leftPoint.node)) {
        brNode = leftPoint.node;
        leftPoint = dom.prevPoint(leftPoint);
        brNode.parentNode.removeChild(brNode);
        if (leftPoint) {
          point = leftPoint;
        }
      } else if (rightPoint && dom.isBR(rightPoint.node)) {
        brNode = rightPoint.node;
        rightPoint = dom.prevPoint(rightPoint);
        brNode.parentNode.removeChild(brNode);
        if (rightPoint) {
          point = rightPoint;
        }
      }

      var leftRng = range.create(editable, 0, point.node, point.offset),
          leftContent = leftRng.nativeRange().extractContents(),
          rightNode;

      if (list.from(editable.childNodes).length) {
        rightNode = editable.childNodes[0];
        editable.insertBefore(leftContent, rightNode);
      } else {
        editable.appendChild(leftContent);
      }

      return {
        container: editable,
        rightNode: rightNode
      };
    }
  };

  return AirPopover;
});
<|endoftext|>


<commit_before>$(function() {
    // 'New recording': Start button
    $('header').on('submit', '.start-recording', startNewRecording);

    // 'Homepage': 'Record' button
    $('.wr-content').on('submit', '.start-recording-homepage', startNewRecording);

    function startNewRecording(event) {
        event.preventDefault();

        var collection;

        if (!user) {
            user = "$temp";
            collection = "temp";
        } else {
            collection = $('[data-collection-id]').attr('data-collection-id');
        }

        var titleInput = $("input[name='rec-title']");
        var title = titleInput.val();
        var urlInput = $("input[name='url']");
        var url = urlInput.val().trim();

        // alert if title is blank
        if(title.replace(/\s+$/g, '').length === 0) {
            titleInput.parent().addClass('has-error');
            return false;
        } else if( titleInput.parent().hasClass('has-error')) {
            titleInput.parent().removeClass('has-error');
        }

        // alert if url is blank
        if(url.length === 0) {
            urlInput.parent().addClass('has-error');
            return false;
        }

        if (!window.cnt_browser && (isSafari() || isMS())) {
            url = "mp_/" + url;
        }

        if(url.indexOf('http') !== 0) {
            url = 'http://' + url;
        }

        // check for trailing slash
        if(url.match(/^https?\:\/\/[\w-.]+$/))
            url += '/';

        // urlencode title
        title = encodeURIComponent(title);

        RouteTo.newRecording(collection, title, url);

        setStorage("__wr_currRec", title);
    };

    function isSafari() {
        return navigator.userAgent.indexOf('Safari') > -1 && navigator.userAgent.indexOf('Chrome') == -1;
    }

    function isMS() {
        if (/(MSIE|Edge|rv:11)/i.test(navigator.userAgent)) {
            return true;
        }

        return false;
    }

    // 'Homepage': Logged in collection dropdown select
    $('.wr-content').on('click', '.collection-select', function(event) {
        event.preventDefault();

        var currColl = $(this).data('collection-id');

        $('.dropdown-toggle-collection').html(
            $('' +
                    $(this).text() + " " +
                ''));

        setStorage("__wr_currColl", currColl);
    });

    $('#create-coll').on('submit', function(event) {
        event.preventDefault();

        $("#create-coll-error").text("");

        var title = $("#create-coll #title").val();
        var is_public = $("#create-coll #is_public").prop("checked");

        var success = function(data) {
            if (data.error_message) {
                $("#create-coll-error").text(data.error_message);
                return;
            }

            if (!data.collection || !data.collection.id) {
                return;
            }

            setStorage("__wr_currColl", data.collection.id);

            if (window.location.pathname == "/") {
                window.location.reload();
            } else if (curr_user == user) {
                RouteTo.collectionInfo(user, data.collection.id);
            }
        }

        var fail = function(data) {
            window.location.reload();
        }

        Collections.create(title, is_public, success, fail);
    });

    // Set default recording title
    var currRec = getStorage("__wr_currRec");

    if (!currRec) {
        currRec = DEFAULT_RECORDING_SESSION_NAME;
    } else {
        currRec = decodeURIComponent(currRec);
    }

    $("input[name='rec-title']").val(currRec);

    // Only for logged in users below
    if (!user) {
        return;
    }

    // If logged-in user and has collection selector (homepage)
    // select first collection
    var currColl = getStorage("__wr_currColl");

    var collSelect = undefined;

    if (currColl) {
        collSelect = $(".dropdown a.collection-select[data-collection-id='" + currColl + "']");
    }

    if (!collSelect || !collSelect.length) {
        collSelect = $(".collection-select");
    }

    if (collSelect && collSelect.length > 0) {
        collSelect[0].click();
    }
});

<commit_msg>home page url: add mp_/ prefix for safari/IE after checking for http://<commit_after>$(function() {
    // 'New recording': Start button
    $('header').on('submit', '.start-recording', startNewRecording);

    // 'Homepage': 'Record' button
    $('.wr-content').on('submit', '.start-recording-homepage', startNewRecording);

    function startNewRecording(event) {
        event.preventDefault();

        var collection;

        if (!user) {
            user = "$temp";
            collection = "temp";
        } else {
            collection = $('[data-collection-id]').attr('data-collection-id');
        }

        var titleInput = $("input[name='rec-title']");
        var title = titleInput.val();
        var urlInput = $("input[name='url']");
        var url = urlInput.val().trim();

        // alert if title is blank
        if (title.replace(/\s+$/g, '').length === 0) {
            titleInput.parent().addClass('has-error');
            return false;
        } else if (titleInput.parent().hasClass('has-error')) {
            titleInput.parent().removeClass('has-error');
        }

        // alert if url is blank
        if (url.length === 0) {
            urlInput.parent().addClass('has-error');
            return false;
        }

        if (url.indexOf('http') !== 0) {
            url = 'http://' + url;
        }

        // check for trailing slash
        if (url.match(/^https?\:\/\/[\w-.]+$/)) {
            url += '/';
        }

        if (!window.cnt_browser && (isSafari() || isMS())) {
            url = "mp_/" + url;
        }

        // urlencode title
        title = encodeURIComponent(title);

        RouteTo.newRecording(collection, title, url);

        setStorage("__wr_currRec", title);
    };

    function isSafari() {
        return navigator.userAgent.indexOf('Safari') > -1 && navigator.userAgent.indexOf('Chrome') == -1;
    }

    function isMS() {
        if (/(MSIE|Edge|rv:11)/i.test(navigator.userAgent)) {
            return true;
        }

        return false;
    }

    // 'Homepage': Logged in collection dropdown select
    $('.wr-content').on('click', '.collection-select', function(event) {
        event.preventDefault();

        var currColl = $(this).data('collection-id');

        $('.dropdown-toggle-collection').html(
            $('' +
                    $(this).text() + " " +
                ''));

        setStorage("__wr_currColl", currColl);
    });

    $('#create-coll').on('submit', function(event) {
        event.preventDefault();

        $("#create-coll-error").text("");

        var title = $("#create-coll #title").val();
        var is_public = $("#create-coll #is_public").prop("checked");

        var success = function(data) {
            if (data.error_message) {
                $("#create-coll-error").text(data.error_message);
                return;
            }

            if (!data.collection || !data.collection.id) {
                return;
            }

            setStorage("__wr_currColl", data.collection.id);

            if (window.location.pathname == "/") {
                window.location.reload();
            } else if (curr_user == user) {
                RouteTo.collectionInfo(user, data.collection.id);
            }
        }

        var fail = function(data) {
            window.location.reload();
        }

        Collections.create(title, is_public, success, fail);
    });

    // Set default recording title
    var currRec = getStorage("__wr_currRec");

    if (!currRec) {
        currRec = DEFAULT_RECORDING_SESSION_NAME;
    } else {
        currRec = decodeURIComponent(currRec);
    }

    $("input[name='rec-title']").val(currRec);

    // Only for logged in users below
    if (!user) {
        return;
    }

    // If logged-in user and has collection selector (homepage)
    // select first collection
    var currColl = getStorage("__wr_currColl");

    var collSelect = undefined;

    if (currColl) {
        collSelect = $(".dropdown a.collection-select[data-collection-id='" + currColl + "']");
    }

    if (!collSelect || !collSelect.length) {
        collSelect = $(".collection-select");
    }

    if (collSelect && collSelect.length > 0) {
        collSelect[0].click();
    }
});

<|endoftext|>


<commit_before>/**
 * Compile Shader file
 */
shader.compileShader = function(gl, type, str) {
	var tempShader;
	if (type == 'fragment') {
		tempShader = gl.createShader(gl.FRAGMENT_SHADER);
	} else if (type == 'vertex') {
		tempShader = gl.createShader(gl.VERTEX_SHADER);
	} else {
		return null;
	}

	gl.shaderSource(tempShader, str);
	gl.compileShader(tempShader);

	if (!gl.getShaderParameter(tempShader, gl.COMPILE_STATUS)) {
		alert(gl.getShaderInfoLog(tempShader));
		return null;
	}

	return tempShader;
}

/**
 * Load external shader file and link between fragment shader and vertex shader.
 *
 * It is a very important! After Shader is loading. Animate Begins.
 */
shader.loadingAfterStart = function(){
	SHADER_LOADER.load(function (data){
		/** Shader is Loading */
		// ~ Texture Shader
		var gl = webgl.gl;
		var textureShader = gl.createProgram();
		var texVertexShader =  shader.compileShader(gl, 'vertex', data.texture.vertex),
			texFragmentShader = shader.compileShader(gl, 'fragment', data.texture.fragment);

		gl.attachShader(textureShader, texVertexShader);
	    gl.attachShader(textureShader, texFragmentShader);
	    gl.linkProgram(textureShader);

	    if (!gl.getProgramParameter(textureShader, gl.LINK_STATUS)) {
	    	webgl.errorHandler('Could not initialise shaders', 2);
	    }

	    gl.useProgram(textureShader);

	    textureShader.vertexPositionAttribute = gl.getAttribLocation(textureShader, 'a_vertex');
	    gl.enableVertexAttribArray(textureShader.vertexPositionAttribute);

	    textureShader.textureCoordAttribute = gl.getAttribLocation(textureShader, 'a_texcoord');
	    gl.enableVertexAttribArray(textureShader.textureCoordAttribute);

	    textureShader.mvp_matrix_loc = gl.getUniformLocation(textureShader, 'mvp_matrix');
	    textureShader.texid_loc = gl.getUniformLocation(textureShader, 'uTexture');

	    webgl.textureShader = textureShader;
	    webgl.errorHandler('Link Texture shader', 1);

	    // ~ Only White Color Shader
	    var colorShader = gl.createProgram();
		var colorVertexShader =  shader.compileShader(gl, 'vertex', data.color.vertex),
			colorFragmentShader = shader.compileShader(gl, 'fragment', data.color.fragment);

	    gl.attachShader(colorShader, colorVertexShader);
	    gl.attachShader(colorShader, colorFragmentShader);
	    gl.linkProgram(colorShader);

	    if (!gl.getProgramParameter(colorShader, gl.LINK_STATUS)) {
	    	webgl.errorHandler('Could not initialise color shader', 2);
	    }
	    gl.useProgram(colorShader);

	    colorShader.vertexPositionAttribute = gl.getAttribLocation(colorShader, 'a_vertex');
	    gl.enableVertexAttribArray(colorShader.vertexPositionAttribute);

	    colorShader.mvp_matrix_loc = gl.getUniformLocation(colorShader, 'mvp_matrix');

	    // ~ Light
	    webgl.colorShader = colorShader;
	    webgl.errorHandler('Link Color shader', 1);

	    var lightShader = gl.createProgram();
	    var lightVertexShader =  shader.compileShader(gl, 'vertex', data.light.vertex),
	    lightFragmentShader = shader.compileShader(gl, 'fragment', data.light.fragment);

	    gl.attachShader(lightShader, lightVertexShader);
	    gl.attachShader(lightShader, lightFragmentShader);
	    gl.linkProgram(lightShader);

	    if (!gl.getProgramParameter(lightShader, gl.LINK_STATUS)) {
	    	webgl.errorHandler('Could not initialise color shader', 2);
	    }
	    gl.useProgram(lightShader);

	    lightShader.vertexPositionAttribute = gl.getAttribLocation(lightShader, 'a_vertex');
	    gl.enableVertexAttribArray(lightShader.vertexPositionAttribute);

	    lightShader.normalPositionAttribute = gl.getAttribLocation(lightShader, 'a_normal');
	    gl.enableVertexAttribArray(lightShader.vertexPositionAttribute);

	    lightShader.mvp_matrix_loc = gl.getUniformLocation(lightShader, 'u_mvp_matrix');
	    lightShader.mv_matrix_loc = gl.getUniformLocation(lightShader, 'u_mv_matrix');
	    lightShader.normal_matrix_loc = gl.getUniformLocation(lightShader, 'u_normal_matrix');

	    lightShader.light_position = gl.getUniformLocation(lightShader, 'u_light_position');
	    lightShader.light_ambient = gl.getUniformLocation(lightShader, 'u_light_ambient');
	    lightShader.light_diffuse = gl.getUniformLocation(lightShader, 'u_light_diffuse');
	    lightShader.light_specular = gl.getUniformLocation(lightShader, 'u_light_specular');

	    lightShader.material_ambient = gl.getUniformLocation(lightShader, 'u_material_ambient');
	    lightShader.material_diffuse = gl.getUniformLocation(lightShader, 'u_material_diffuse');
	    lightShader.material_specular = gl.getUniformLocation(lightShader, 'u_material_specular');
	    lightShader.material_shininess = gl.getUniformLocation(lightShader, 'u_material_shininess');

	    webgl.lightShader = lightShader;
	    webgl.errorHandler('Link Light shader', 1);

	    /** Animate Begin */
	    webgl.animate();
	    setTimeout(webgl.onFloatingRestart, 1000);
	});
};<commit_msg>Light 하기전.<commit_after>/**
 * Compile Shader file
 */
shader.compileShader = function(gl, type, str) {
	var tempShader;
	if (type == 'fragment') {
		tempShader = gl.createShader(gl.FRAGMENT_SHADER);
	} else if (type == 'vertex') {
		tempShader = gl.createShader(gl.VERTEX_SHADER);
	} else {
		return null;
	}

	gl.shaderSource(tempShader, str);
	gl.compileShader(tempShader);

	if (!gl.getShaderParameter(tempShader, gl.COMPILE_STATUS)) {
		alert(gl.getShaderInfoLog(tempShader));
		return null;
	}

	return tempShader;
}

/**
 * Load external shader file and link between fragment shader and vertex shader.
 *
 * It is a very important! After Shader is loading. Animate Begins.
 */
shader.loadingAfterStart = function(){
	SHADER_LOADER.load(function (data){
		/** Shader is Loading */
		// ~ Texture Shader
		var gl = webgl.gl;
		var textureShader = gl.createProgram();
		var texVertexShader =  shader.compileShader(gl, 'vertex', data.texture.vertex),
			texFragmentShader = shader.compileShader(gl, 'fragment', data.texture.fragment);

		gl.attachShader(textureShader, texVertexShader);
	    gl.attachShader(textureShader, texFragmentShader);
	    gl.linkProgram(textureShader);

	    if (!gl.getProgramParameter(textureShader, gl.LINK_STATUS)) {
	    	webgl.errorHandler('Could not initialise shaders', 2);
	    }

	    gl.useProgram(textureShader);

	    textureShader.vertexPositionAttribute = gl.getAttribLocation(textureShader, 'a_vertex');
	    gl.enableVertexAttribArray(textureShader.vertexPositionAttribute);

	    textureShader.textureCoordAttribute = gl.getAttribLocation(textureShader, 'a_texcoord');
	    gl.enableVertexAttribArray(textureShader.textureCoordAttribute);

	    textureShader.mvp_matrix_loc = gl.getUniformLocation(textureShader, 'mvp_matrix');
	    textureShader.texid_loc = gl.getUniformLocation(textureShader, 'uTexture');

	    webgl.textureShader = textureShader;
	    webgl.errorHandler('Link Texture shader', 1);

	    // ~ Only White Color Shader
	    var colorShader = gl.createProgram();
		var colorVertexShader =  shader.compileShader(gl, 'vertex', data.color.vertex),
			colorFragmentShader = shader.compileShader(gl, 'fragment', data.color.fragment);

	    gl.attachShader(colorShader, colorVertexShader);
	    gl.attachShader(colorShader, colorFragmentShader);
	    gl.linkProgram(colorShader);

	    if (!gl.getProgramParameter(colorShader, gl.LINK_STATUS)) {
	    	webgl.errorHandler('Could not initialise color shader', 2);
	    }
	    gl.useProgram(colorShader);

	    colorShader.vertexPositionAttribute = gl.getAttribLocation(colorShader, 'a_vertex');
	    gl.enableVertexAttribArray(colorShader.vertexPositionAttribute);

	    colorShader.mvp_matrix_loc = gl.getUniformLocation(colorShader, 'mvp_matrix');

	    webgl.colorShader = colorShader;
	    webgl.errorHandler('Link Color shader', 1);

	    /** Animate Begin */
	    webgl.animate();
	    setTimeout(webgl.onFloatingRestart, 1000);
	});
};<|endoftext|>


<commit_before>(function () {

var pubKey = reportConfig.RECAPTCHA_SITE_KEY;
var captchaTimeout = 5 * 60 * 1000;
var REPORTS = {};
var PANEL;

if (pubKey)
	menuOptions.push('Report');

var Report = Backbone.Model.extend({
	defaults: {
		status: 'setup',
		hideAfter: true,
	},

	request_new: function () {
		var self = this;
		grecaptcha.render('captcha', {
			sitekey : pubKey,
			theme: 'dark',
			callback: function(){self.set('status', 'ready');}
		});
		self.set('status', 'ready');

		if (this.get('timeout'))
			clearTimeout(this.get('timeout'));

		this.set('timeout', setTimeout(function () {
			self.set('timeout', 0);
			grecaptcha.reset();
		}, captchaTimeout));
	},

	did_report: function () {
		delete REPORTS[this.id];
		if (this.get('timeout')) {
			clearTimeout(this.get('timeout'));
			this.set('timeout', 0);
		}

		var self = this;
		setTimeout(function () {
			self.trigger('destroy');
		}, 1500);

		if (this.get('hideAfter'))
			this.get('post').set('hide', true);
	},
});

var ReportPanel = Backbone.View.extend({
	id: 'report-panel',
	tagName: 'form',
	className: 'modal',

	events: {
		submit: 'submit',
		'click .close': 'remove',
		'click .hideAfter': 'hide_after_changed',
	},

	initialize: function () {
		this.$captcha = $('
');
		this.$message = $('
');
		this.$submit = $('<input>', {type: 'submit', val: 'Report'});
		var $hideAfter = $('<input>', {
			'class': 'hideAfter',
			type: 'checkbox',
			checked: this.model.get('hideAfter'),
		});
		var $hideLabel = $('and hide')
			.append($hideAfter);
		this.$description = $('<input type="text" class="reportDescription" placeholder="Description (Optional)"/>');
		var num = this.model.id;

		this.$el
		.append('Reporting post ')
		.append($('', {href: '#'+num, text: '>>'+num}))
		.append('x')
		.append(this.$message)
		.append('
')
		.append(this.$description)
		.append(this.$captcha)
		.append(this.$submit)
		.append(' ', $hideLabel);

		/* HACK */
		if (window.x_csrf) {
			this.model.set('hideAfter', false);
			$hideLabel.remove();
		}

		if ($(window).width() < 470) {
			this.$el
			.css('transform','scale('+$(window).width()/500+')')
			.css('transform-origin','bottom right');
		}

		this.listenTo(this.model, {
			'change:error': this.error_changed,
			'change:status': this.status_changed,
			destroy: this.remove,
		});
	},

	render: function () {
		this.error_changed();
		this.status_changed();
		return this;
	},

	submit: function () {
		if (this.model.get('status') != 'ready')
			return false;
		send([DEF.REPORT_POST, this.model.id,
				grecaptcha.getResponse(), this.$description.val()]);
		this.model.set('status', 'reporting');
		return false;
	},

	error_changed: function () {
		this.$message.text(this.model.get('error'));
	},

	status_changed: function () {
		var status = this.model.get('status');
		this.$submit
			.prop('disabled', status != 'ready')
			.toggle(status != 'done')
			.val(status=='reporting' ? 'Reporting...' : 'Report');
		this.$captcha.toggle(
			_.contains(['ready', 'reporting', 'error'], status));
		if (status == 'done')
			this.$('label').remove();

		var msg;
		if (status == 'done')
			msg = 'Report submitted!';
		else if (status == 'setup')
			msg = 'Obtaining reCAPTCHA...';
		else if (status == 'error')
			msg = 'E';
		else if (status == 'ready' && this.model.get('error'))
			msg = 'E';
		this.$message.text(msg=='E' ? this.model.get('error') : msg);
		this.$message
			.toggle(!!msg)
			.toggleClass('error', msg == 'E');

		// not strictly view logic, but only relevant when visible
		if (status == 'done')
			this.model.did_report();
		else if (status == 'error')
			this.model.request_new();
	},

	hide_after_changed: function (e) {
		this.model.set('hideAfter', e.target.checked);
	},

	remove: function () {
		Backbone.View.prototype.remove.call(this);
		if (PANEL == this) {
			PANEL = null;
			grecaptcha.reset();
		}
		return false;
	},
});

var ajaxJs = 'https://www.google.com/recaptcha/api.js?render=explicit';

menuHandlers.Report = function (post) {
	var num = post.id;
	var model = REPORTS[num];
	if (!model)
		REPORTS[num] = model = new Report({id: num, post: post});

	if (PANEL) {
		if (PANEL.model === model) {
			PANEL.focus();
			return;
		}
		PANEL.remove();
	}
	PANEL = new ReportPanel({model: model});
	PANEL.render().$el.appendTo('body');
	yepnope({load: ajaxJs, callback: function () {
		setTimeout(function(){
			if (window.grecaptcha)
				model.request_new();
			else
				model.set({
					status: 'error',
					error: "Couldn't load reCATPCHA.",
				});
		}, 500)
	}});
};

dispatcher[DEF.REPORT_POST] = function (msg, op) {
	var num = msg[0], etc = msg[1];
	var report = REPORTS[num];
	if (report)
		report.set(msg[1] || {status: 'done'});
};

})();
<commit_msg>(Anon Contribution) Fix Bug with Reporting<commit_after>(function () {

var pubKey = reportConfig.RECAPTCHA_SITE_KEY;
var captchaTimeout = 5 * 60 * 1000;
var REPORTS = {};
var PANEL;

if (pubKey)
	menuOptions.push('Report');

var Report = Backbone.Model.extend({
	defaults: {
		status: 'setup',
		hideAfter: true,
	},

	request_new: function () {
		var self = this;
		grecaptcha.render('captcha', {
			sitekey : pubKey,
			theme: 'dark',
			callback: function(){self.set('status', 'ready');}
		});
		self.set('status', 'ready');

		if (this.get('timeout'))
			clearTimeout(this.get('timeout'));

		this.set('timeout', setTimeout(function () {
			self.set('timeout', 0);
			grecaptcha.reset();
		}, captchaTimeout));
	},

	did_report: function () {
		delete REPORTS[this.id];
		if (this.get('timeout')) {
			clearTimeout(this.get('timeout'));
			this.set('timeout', 0);
		}

		var self = this;
		setTimeout(function () {
			self.trigger('destroy');
		}, 1500);

		if (this.get('hideAfter'))
			this.get('post').set('hide', true);
	},
});

var ReportPanel = Backbone.View.extend({
	id: 'report-panel',
	tagName: 'form',
	className: 'modal',

	events: {
		submit: 'submit',
		'click .close': 'remove',
		'click .hideAfter': 'hide_after_changed',
	},

	initialize: function () {
		this.$captcha = $('
');
		this.$message = $('
');
		this.$submit = $('<input>', {type: 'submit', val: 'Report'});
		var $hideAfter = $('<input>', {
			'class': 'hideAfter',
			type: 'checkbox',
			checked: this.model.get('hideAfter'),
		});
		var $hideLabel = $('and hide')
			.append($hideAfter);
		this.$description = $('<input type="text" class="reportDescription" placeholder="Description (Optional)"/>');
		var num = this.model.id;

		this.$el
		.append('Reporting post ')
		.append($('', {href: '#'+num, text: '>>'+num}))
		.append('x')
		.append(this.$message)
		.append('
')
		.append(this.$description)
		.append(this.$captcha)
		.append(this.$submit)
		.append(' ', $hideLabel);

		/* HACK */
		if (window.x_csrf) {
			this.model.set('hideAfter', false);
			$hideLabel.remove();
		}

		if ($(window).width() < 470) {
			this.$el
			.css('transform','scale('+$(window).width()/500+')')
			.css('transform-origin','bottom right');
		}

		this.listenTo(this.model, {
			'change:error': this.error_changed,
			'change:status': this.status_changed,
			destroy: this.remove,
		});
	},

	render: function () {
		this.error_changed();
		this.status_changed();
		return this;
	},

	submit: function () {
		if (this.model.get('status') != 'ready')
			return false;
		send([DEF.REPORT_POST, this.model.id,
				grecaptcha.getResponse(), this.$description.val()]);
		this.model.set('status', 'reporting');
		return false;
	},

	error_changed: function () {
		this.$message.text(this.model.get('error'));
	},

	status_changed: function () {
		var status = this.model.get('status');
		this.$submit
			.prop('disabled', status != 'ready')
			.toggle(status != 'done')
			.val(status=='reporting' ? 'Reporting...' : 'Report');
		this.$captcha.toggle(
			_.contains(['ready', 'reporting', 'error'], status));
		if (status == 'done')
			this.$('label').remove();

		var msg;
		if (status == 'done')
			msg = 'Report submitted!';
		else if (status == 'setup')
			msg = 'Obtaining reCAPTCHA...';
		else if (status == 'error')
			msg = 'E';
		else if (status == 'ready' && this.model.get('error'))
			msg = 'E';
		this.$message.text(msg=='E' ? this.model.get('error') : msg);
		this.$message
			.toggle(!!msg)
			.toggleClass('error', msg == 'E');

		// not strictly view logic, but only relevant when visible
		if (status == 'done')
			this.model.did_report();
		else if (status == 'error')
			this.model.request_new();
	},

	hide_after_changed: function (e) {
		this.model.set('hideAfter', e.target.checked);
	},

	remove: function () {
		Backbone.View.prototype.remove.call(this);
		if (PANEL == this) {
			PANEL = null;
		}
		return false;
	},
});

var ajaxJs = 'https://www.google.com/recaptcha/api.js?render=explicit';

menuHandlers.Report = function (post) {
	var num = post.id;
	var model = REPORTS[num];
	if (!model)
		REPORTS[num] = model = new Report({id: num, post: post});

	if (PANEL) {
		if (PANEL.model === model) {
			PANEL.focus();
			return;
		}
		PANEL.remove();
	}
	PANEL = new ReportPanel({model: model});
	PANEL.render().$el.appendTo('body');
	yepnope({load: ajaxJs, callback: function () {
		setTimeout(function(){
			if (window.grecaptcha)
				model.request_new();
			else
				model.set({
					status: 'error',
					error: "Couldn't load reCATPCHA.",
				});
		}, 500)
	}});
};

dispatcher[DEF.REPORT_POST] = function (msg, op) {
	var num = msg[0], etc = msg[1];
	var report = REPORTS[num];
	if (report)
		report.set(msg[1] || {status: 'done'});
};

})();
<|endoftext|>


<commit_before><commit_msg>cell component<commit_after><|endoftext|>


<commit_before> function createNodeWithAttributes(tagName, attributes, name) {
 	var node = createNode(tagName, name);
 	var i, len = attributes.length;
 	for(i = 0; i < len; i += 1) {
 		addAttribute(node, attributes[i].key, attributes[i].value);
 	}
 	return node;
 }

 function isArray(element) {
 	var what = Object.prototype.toString;
 	return what.call(element)  === '[object Array]';
 }

 function createNode(tagName, name) {
 	var node = {
 		[tagName]:{_attr:{}}
 	};
 	if(name) {
 		addAttribute(node, 'android:name', name);
 	}
 	return node;
 }

 function addAttribute(object, key, value) {
 	var tagName = getTagName(object);
 	var children = getChildren(object);
 	if(isArray(children)){
 		var i = 0, len = children.length;
	 	var attrsContainer;
	 	while(i < len) {
	 		if(children[i]._attr) {
	 			attrsContainer = children[i];
	 			break;
	 		}
	 		i += 1;
	 	}
	 } else {
	 	attrsContainer = children;
	 }

 	if (!attrsContainer) {
 		attrsContainer = {_attr:{}};
 		object[tagName] = attrsContainer;
 	}
 	attrsContainer._attr[key] = value;
 }

 function getTagName(nodeElem) {
 	var keys = Object.keys(nodeElem);
 	return keys[0];
 }

 function getAttribute(nodeElem, key) {
 	var children = getChildren(nodeElem);
 	if(isArray(children)){
	 	var i =0, len = children.length;
	 	while(i < len) {
	 		if(children[i]._attr && children[i]._attr[key]) {
	 			return children[i]._attr[key];
	 		}
	 		i += 1;
	 	}
	} else if(children._attr && children._attr[key]) {
		return children._attr[key];
	}
 	return '';
 }

function getChildren(nodeElem) {
 	var nodeTagName = getTagName(nodeElem);
 	var children = nodeElem[nodeTagName];
 	return children;
}

 function getChild(nodeElem, childName) {
 	var children = getChildren(nodeElem);
 	if(isArray(children)){
	 	var i =0, len = children.length, tagName;
	 	while(i < len) {
	 		tagName = getTagName(children[i]);
	 		if(tagName === childName) {
	 			return children[i];
	 		}
	 		i += 1;
	 	}
	}
	return '';
 }

 function nestChild(nodeElem, nested) {
 	if(!nested) {
 		return;
 	}
 	var tagName = getTagName(nodeElem);
 	if(!isArray(nodeElem[tagName])){
 		var attrs = nodeElem[tagName];
 		nodeElem[tagName] = [attrs];
 	}
 	nodeElem[tagName].push(nested);
 }

 function cloneNode(node, targets, suffix) {
 	var cloningNode = JSON.parse(JSON.stringify(node));
 	renameNode(cloningNode, targets, suffix);
 	return cloningNode;
 }

 function renameNode(nodeElem, targets, suffix) {
 	var children = getChildren(nodeElem);
 	if(children && isArray(children)) {
	 	var i, len = children.length;
	 	for( i = 0; i < len; i += 1) {
	 		renameNode(children[i], targets, suffix);
	 	}
 	}
 	var androidName = getAttribute(nodeElem, 'android:name');
 	if(androidName) {
 		duplicateTargets(targets, androidName, androidName + suffix);
 		addAttribute(nodeElem, 'android:name', androidName + suffix);
 	}
 }

 function duplicateTargets(targets, name, newName) {
 	var i, len = targets.length, newTarget;
 	for( i = 0 ; i < len; i += 1) {
 		if(targets[i].target[0]._attr['android:name'] === name) {
 			newTarget = JSON.parse(JSON.stringify(targets[i]));
 			newTarget.target[0]._attr['android:name'] = newName;
 			targets.push(newTarget);
 		}
 	}
 }

 function nestArray(array) {
 	var i, len = array.length;
 	for(i = 1; i < len; i += 1) {
 		nestChild(array[i],array[i - 1]);
 	}
 	return array[array.length - 1];
 }

 function getLastLeaves(node) {
 	var leaves = [];
 	var children = getChildren(node);
 	var hasChildren = false;
 	if(children && isArray(children)) {
 		var i, len = children.length, tagName;
 		for(i = 0; i < len; i += 1) {
 			tagName = getTagName(children[i]);
 			if (tagName !== '_attr') {
 				hasChildren = true;
 				leaves = leaves.concat(getLastLeaves(children[i]));
 			}
 		}
 	}
 	if(!hasChildren) {
 		leaves.push(node);
 	}
 	return leaves;
 }

 module.exports = {
 	createNode: createNode,
 	createNodeWithAttributes: createNodeWithAttributes,
 	addAttribute: addAttribute,
 	getTagName: getTagName,
 	getAttribute: getAttribute,
 	nestChild: nestChild,
 	nestArray: nestArray,
 	getChild: getChild,
 	getChildren: getChildren,
 	getLastLeaves: getLastLeaves,
 	cloneNode: cloneNode
 }<commit_msg>using Object.defineproperty for object dynamic property name<commit_after> function createNodeWithAttributes(tagName, attributes, name) {
 	var node = createNode(tagName, name);
 	var i, len = attributes.length;
 	for(i = 0; i < len; i += 1) {
 		addAttribute(node, attributes[i].key, attributes[i].value);
 	}
 	return node;
 }

 function isArray(element) {
 	var what = Object.prototype.toString;
 	return what.call(element)  === '[object Array]';
 }

 function createNode(tagName, name) {
 	var node = {};
 	Object.defineProperty(node, tagName, {
	  value: { _attr: {} },
	  writable: true,
	  enumerable: true,
	  configurable: true
	});
 	if(name) {
 		addAttribute(node, 'android:name', name);
 	}
 	return node;
 }

 function addAttribute(object, key, value) {
 	var tagName = getTagName(object);
 	var children = getChildren(object);
 	if(isArray(children)){
 		var i = 0, len = children.length;
	 	var attrsContainer;
	 	while(i < len) {
	 		if(children[i]._attr) {
	 			attrsContainer = children[i];
	 			break;
	 		}
	 		i += 1;
	 	}
	 } else {
	 	attrsContainer = children;
	 }

 	if (!attrsContainer) {
 		attrsContainer = {_attr:{}};
 		object[tagName] = attrsContainer;
 	}
 	attrsContainer._attr[key] = value;
 }

 function getTagName(nodeElem) {
 	var keys = Object.keys(nodeElem);
 	return keys[0];
 }

 function getAttribute(nodeElem, key) {
 	var children = getChildren(nodeElem);
 	if(isArray(children)){
	 	var i =0, len = children.length;
	 	while(i < len) {
	 		if(children[i]._attr && children[i]._attr[key]) {
	 			return children[i]._attr[key];
	 		}
	 		i += 1;
	 	}
	} else if(children._attr && children._attr[key]) {
		return children._attr[key];
	}
 	return '';
 }

function getChildren(nodeElem) {
 	var nodeTagName = getTagName(nodeElem);
 	var children = nodeElem[nodeTagName];
 	return children;
}

 function getChild(nodeElem, childName) {
 	var children = getChildren(nodeElem);
 	if(isArray(children)){
	 	var i =0, len = children.length, tagName;
	 	while(i < len) {
	 		tagName = getTagName(children[i]);
	 		if(tagName === childName) {
	 			return children[i];
	 		}
	 		i += 1;
	 	}
	}
	return '';
 }

 function nestChild(nodeElem, nested) {
 	if(!nested) {
 		return;
 	}
 	var tagName = getTagName(nodeElem);
 	if(!isArray(nodeElem[tagName])){
 		var attrs = nodeElem[tagName];
 		nodeElem[tagName] = [attrs];
 	}
 	nodeElem[tagName].push(nested);
 }

 function cloneNode(node, targets, suffix) {
 	var cloningNode = JSON.parse(JSON.stringify(node));
 	renameNode(cloningNode, targets, suffix);
 	return cloningNode;
 }

 function renameNode(nodeElem, targets, suffix) {
 	var children = getChildren(nodeElem);
 	if(children && isArray(children)) {
	 	var i, len = children.length;
	 	for( i = 0; i < len; i += 1) {
	 		renameNode(children[i], targets, suffix);
	 	}
 	}
 	var androidName = getAttribute(nodeElem, 'android:name');
 	if(androidName) {
 		duplicateTargets(targets, androidName, androidName + suffix);
 		addAttribute(nodeElem, 'android:name', androidName + suffix);
 	}
 }

 function duplicateTargets(targets, name, newName) {
 	var i, len = targets.length, newTarget;
 	for( i = 0 ; i < len; i += 1) {
 		if(targets[i].target[0]._attr['android:name'] === name) {
 			newTarget = JSON.parse(JSON.stringify(targets[i]));
 			newTarget.target[0]._attr['android:name'] = newName;
 			targets.push(newTarget);
 		}
 	}
 }

 function nestArray(array) {
 	var i, len = array.length;
 	for(i = 1; i < len; i += 1) {
 		nestChild(array[i],array[i - 1]);
 	}
 	return array[array.length - 1];
 }

 function getLastLeaves(node) {
 	var leaves = [];
 	var children = getChildren(node);
 	var hasChildren = false;
 	if(children && isArray(children)) {
 		var i, len = children.length, tagName;
 		for(i = 0; i < len; i += 1) {
 			tagName = getTagName(children[i]);
 			if (tagName !== '_attr') {
 				hasChildren = true;
 				leaves = leaves.concat(getLastLeaves(children[i]));
 			}
 		}
 	}
 	if(!hasChildren) {
 		leaves.push(node);
 	}
 	return leaves;
 }

 module.exports = {
 	createNode: createNode,
 	createNodeWithAttributes: createNodeWithAttributes,
 	addAttribute: addAttribute,
 	getTagName: getTagName,
 	getAttribute: getAttribute,
 	nestChild: nestChild,
 	nestArray: nestArray,
 	getChild: getChild,
 	getChildren: getChildren,
 	getLastLeaves: getLastLeaves,
 	cloneNode: cloneNode
 }<|endoftext|>


<commit_before>var click = new Audio('click.mp3');
var bing = new Audio('bing.mp3');
var count = 1;
var stage = 1;

function reset() {
  document.querySelector('.content').classList.toggle('animate');
  count = 1;
  stage = 1;
}

function tick() {
  setTimeout(function () {
    if (count % 40 < 30) {
      click.play();
      if (count % 40 == 0) {
        document.querySelector('.stage-' + stage + ' .rest').classList.toggle('on')
        document.querySelector('.stage-' + stage + ' .done').classList.toggle('on')
        stage++;
      }
    } else if (count % 40 == 30) {
      document.querySelector('.stage-' + stage + ' .timer').classList.toggle('off')
      document.querySelector('.stage-' + stage + ' .rest').classList.toggle('on')
      bing.play();
    }

    count++;

    if (count < 470) {
      tick();
    } else {
      reset();
    }
  }, 1000);
}

document.querySelector('.overlay').addEventListener('click', function() {
  document.querySelector('.content').classList.toggle('animate');
  this.classList.toggle('on');
  tick();
});

document.querySelector('.mute').addEventListener('click', function() {
  click.volume = click.volume == 1 ? 0 : 1;
  bing.volume = bing.volume == 1 ? 0 : 1;
  this.classList.toggle('fa-volume-up');
  this.classList.toggle('fa-volume-off');
});
<commit_msg>End fix<commit_after>var click = new Audio('click.mp3');
var bing = new Audio('bing.mp3');
var count = 1;
var stage = 1;

function reset() {
  document.querySelector('.overlay.finished').classList.toggle('on')
  document.querySelector('.content').classList.toggle('animate');
  count = 1;
  stage = 1;
}

function tick() {
  setTimeout(function () {
    if (count % 40 < 30) {
      click.play();
      if (count % 40 == 0) {
        document.querySelector('.stage-' + stage + ' .rest').classList.toggle('on')
        document.querySelector('.stage-' + stage + ' .done').classList.toggle('on')
        stage++;
      }
    } else if (count % 40 == 30) {
      document.querySelector('.stage-' + stage + ' .timer').classList.toggle('off')
      document.querySelector('.stage-' + stage + ' .rest').classList.toggle('on')
      bing.play();
    }

    count++;

    if (count < 470) {
      tick();
    } else {
      bing.play();
      reset();
    }
  }, 1000);
}

document.querySelector('.overlay').addEventListener('click', function() {
  document.querySelector('.content').classList.toggle('animate');
  this.classList.toggle('on');
  tick();
});

document.querySelector('.mute').addEventListener('click', function() {
  click.volume = click.volume == 1 ? 0 : 1;
  bing.volume = bing.volume == 1 ? 0 : 1;
  this.classList.toggle('fa-volume-up');
  this.classList.toggle('fa-volume-off');
});
<|endoftext|>


<commit_before><commit_msg>Fixed accessing appProvider methods<commit_after><|endoftext|>


<commit_before>
var list        = document.querySelectorAll(".list"),
    items_shell = document.querySelectorAll(".list-items-shell"),
    selected    = "selected",
    list_anim   = 250;

window.addEventListener("resize", smart_nav);
window.addEventListener("scroll", smart_nav);

function smart_nav() {
  // if desktop
  if (window.innerWidth >= 800) {
    var main       = document.querySelector("main"),
        scroll_pos = window.pageYOffset
                     || document.documentElement.scrollTop
                     || document.body.scrollTop,
        nav_pos    = main.offsetTop;

    // highlight nav item when scrolled to/past respective list
    for (var i = 0; i < list.length; i++ ) {
      var nav_item = document.querySelectorAll(".nav-item"),
          list_pos = list[i].offsetTop;

      // -1 compensates for frag rounding bug
      scroll_pos >= nav_pos + list_pos - 1
        ? nav_item[i].classList.add("current")
        : nav_item[i].classList.remove("current")
      ;
    }
  }
}

// toggle selected list (i)
function toggle_list(i) {
  // if mobile
  if (window.innerWidth < 800) {
    // if selected list is closed & another list is open
    if (!list[i].classList.contains(selected) && is_any_list_open()) {
      // close that other list
      close_lists();
      // wait for other list to close
      setTimeout(function() {
        open_list(i)
      }, list_anim);
    // if selected list is closed (and all other lists are closed)
    } else if (!list[i].classList.contains(selected)) {
      open_list(i)
    // if selected list is open
    } else {
      // close selected list
      items_shell[i].style.height = "0";
      list[i].classList.remove(selected);
    }
  }
}

// return true if any lists are open
function is_any_list_open() {
  for (var i = 0; i < list.length; i++) {
    if (list[i].classList.contains(selected)) {
      return true;
    }
  }
}

// close all lists
function close_lists() {
  for (var i = 0; i < list.length; i++) {
    items_shell[i].style.height = "0";
    list[i].classList.remove(selected);
  }
}

// open selected list
function open_list(i) {
  // add class for animating open/close icon and list color
  list[i].classList.add(selected);
  // open list
  set_list_height();
  // scroll to selected list
  scroll_to_list(i);
}

// adjust height on font-size/padding change
window.addEventListener("resize", set_list_height);

// open list (set height so opening can be animated)
function set_list_height() {
  var list_items = document.querySelectorAll(".list-items");

  for (var i = 0; i < list.length; i++) {
    if (list[i].classList.contains(selected)) {
      items_shell[i].style.height = list_items[i].offsetHeight + "px";
    }
  }
}

// scroll to selected list
function scroll_to_list(i) {
  // wait for list to open
  setTimeout(function() {
    // get list's top-position
    var elem_pos = items_shell[i].offsetTop;
    // scroll to top of list/to list title
    smoothScroll.animateScroll(elem_pos);
  }, list_anim);
}
<commit_msg>replace double quotes with singles<commit_after>
var list        = document.querySelectorAll('.list'),
    items_shell = document.querySelectorAll('.list-items-shell'),
    selected    = 'selected',
    list_anim   = 250;

window.addEventListener('resize', smart_nav);
window.addEventListener('scroll', smart_nav);

function smart_nav() {
  // if desktop
  if (window.innerWidth >= 800) {
    var main       = document.querySelector('main'),
        scroll_pos = window.pageYOffset
                     || document.documentElement.scrollTop
                     || document.body.scrollTop,
        nav_pos    = main.offsetTop;

    // highlight nav item when scrolled to/past respective list
    for (var i = 0; i < list.length; i++ ) {
      var nav_item = document.querySelectorAll('.nav-item'),
          list_pos = list[i].offsetTop;

      // -1 compensates for frag rounding bug
      scroll_pos >= nav_pos + list_pos - 1
        ? nav_item[i].classList.add   ('current')
        : nav_item[i].classList.remove('current')
      ;
    }
  }
}

// toggle selected list (i)
function toggle_list(i) {
  // if mobile
  if (window.innerWidth < 800) {
    // if selected list is closed & another list is open
    if (!list[i].classList.contains(selected) && is_any_list_open()) {
      // close that other list
      close_lists();
      // wait for other list to close
      setTimeout(function() {
        open_list(i)
      }, list_anim);
    // if selected list is closed (and all other lists are closed)
    } else if (!list[i].classList.contains(selected)) {
      open_list(i)
    // if selected list is open
    } else {
      // close selected list
      items_shell[i].style.height = '0';
      list[i].classList.remove(selected);
    }
  }
}

// return true if any lists are open
function is_any_list_open() {
  for (var i = 0; i < list.length; i++) {
    if (list[i].classList.contains(selected)) {
      return true;
    }
  }
}

// close all lists
function close_lists() {
  for (var i = 0; i < list.length; i++) {
    items_shell[i].style.height = '0';
    list[i].classList.remove(selected);
  }
}

// open selected list
function open_list(i) {
  // add class for animating open/close icon and list color
  list[i].classList.add(selected);
  // open list
  set_list_height();
  // scroll to selected list
  scroll_to_list(i);
}

// adjust height on font-size/padding change
window.addEventListener('resize', set_list_height);

// open list (set height so opening can be animated)
function set_list_height() {
  var list_items = document.querySelectorAll('.list-items');

  for (var i = 0; i < list.length; i++) {
    if (list[i].classList.contains(selected)) {
      items_shell[i].style.height = list_items[i].offsetHeight + 'px';
    }
  }
}

// scroll to selected list
function scroll_to_list(i) {
  // wait for list to open
  setTimeout(function() {
    // get list's top-position
    var elem_pos = items_shell[i].offsetTop;
    // scroll to top of list/to list title
    smoothScroll.animateScroll(elem_pos);
  }, list_anim);
}
<|endoftext|>


<commit_before>// Universal Subtitles, universalsubtitles.org
//
// Copyright (C) 2010 Participatory Culture Foundation
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see
// http://www.gnu.org/licenses/agpl-3.0.html.

goog.provide('mirosubs.video.Html5VideoSource');

/**
 * @constructor
 * @param {string} videoURL
 * @param {mirosubs.video.Html5VideoType} videoType
 * @param {Object.<string, string>=} opt_videoConfig Attributes to use for
 *     video element, plus optional 'click_to_play' parameter
 */
mirosubs.video.Html5VideoSource = function(videoURL, videoType, opt_videoConfig) {
    this.videoURL_ = videoURL;
    this.videoType_ = videoType;
    this.videoConfig_ = opt_videoConfig;
};

mirosubs.video.Html5VideoSource.forURL = function(videoURL, opt_videoConfig) {
    var queryStringIndex = videoURL.indexOf('?');
    if (queryStringIndex > -1)
        videoURL = videoURL.substring(0, queryStringIndex);
    var vt = mirosubs.video.Html5VideoType;
    var videoType = null;
    if (/\.ogv$|\.ogg$/.test(videoURL))
        videoType = vt.OGG;
    else if (/\.mp4$|\.m4v$/.test(videoURL))
        videoType = vt.H264;
    else if (/\.webm$/.test(videoURL))
        videoType = vt.WEBM;
    if (videoType != null)
        return new mirosubs.video.Html5VideoSource(
            videoURL, videoType, opt_videoConfig);
    else
        return null;
};

mirosubs.video.Html5VideoSource.prototype.isBestVideoSource = function() {
    if (this.videoType_ == mirosubs.video.Html5VideoType.H264 &&
        (mirosubs.video.supportsOgg() || mirosubs.video.supportsWebM()))
        return false;
    return mirosubs.video.supportsVideoType(this.videoType_);
};

mirosubs.video.Html5VideoSource.prototype.createPlayer = function() {
    return this.createPlayer_(false);
};

mirosubs.video.Html5VideoSource.prototype.createControlledPlayer =
    function()
{
    return new mirosubs.video.ControlledVideoPlayer(
        this.createPlayer_(true));
};

mirosubs.video.Html5VideoSource.prototype.createPlayer_ =
    function(forSubDialog)
{
    return new mirosubs.video.Html5VideoPlayer(
        new mirosubs.video.Html5VideoSource(
            this.videoURL_, this.videoType_, this.videoConfig_),
        forSubDialog);
};

mirosubs.video.Html5VideoSource.prototype.getVideoURL = function() {
    return this.videoURL_;
};

mirosubs.video.Html5VideoSource.prototype.getVideoType = function() {
    return this.videoType_;
};

mirosubs.video.Html5VideoSource.prototype.getVideoConfig = function() {
    return this.videoConfig_;
};

mirosubs.video.Html5VideoSource.prototype.setVideoConfig = function(config) {
    this.videoConfig_ = config;
};
<commit_msg>Now falls back to Flowplayer for mp4 when the browser can't play mp4. For http://bugzilla.pculture.org/show_bug.cgi?id=14657<commit_after>// Universal Subtitles, universalsubtitles.org
//
// Copyright (C) 2010 Participatory Culture Foundation
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see
// http://www.gnu.org/licenses/agpl-3.0.html.

goog.provide('mirosubs.video.Html5VideoSource');

/**
 * @constructor
 * @param {string} videoURL
 * @param {mirosubs.video.Html5VideoType} videoType
 * @param {Object.<string, string>=} opt_videoConfig Attributes to use for
 *     video element, plus optional 'click_to_play' parameter
 */
mirosubs.video.Html5VideoSource = function(videoURL, videoType, opt_videoConfig) {
    this.videoURL_ = videoURL;
    this.videoType_ = videoType;
    this.videoConfig_ = opt_videoConfig;
};

mirosubs.video.Html5VideoSource.forURL = function(videoURL, opt_videoConfig) {
    var queryStringIndex = videoURL.indexOf('?');
    if (queryStringIndex > -1)
        videoURL = videoURL.substring(0, queryStringIndex);
    var vt = mirosubs.video.Html5VideoType;
    var videoType = null;
    if (/\.ogv$|\.ogg$/.test(videoURL))
        videoType = vt.OGG;
    else if (/\.mp4$|\.m4v$/.test(videoURL))
        videoType = vt.H264;
    else if (/\.webm$/.test(videoURL))
        videoType = vt.WEBM;
    if (videoType != null)
        return new mirosubs.video.Html5VideoSource(
            videoURL, videoType, opt_videoConfig);
    else
        return null;
};

mirosubs.video.Html5VideoSource.prototype.isBestVideoSource = function() {
    if (this.videoType_ == mirosubs.video.Html5VideoType.H264 &&
        (mirosubs.video.supportsOgg() || mirosubs.video.supportsWebM()))
        return false;
    return mirosubs.video.supportsVideoType(this.videoType_);
};

mirosubs.video.Html5VideoSource.prototype.createPlayer = function() {
    return this.createPlayer_(false);
};

mirosubs.video.Html5VideoSource.prototype.createControlledPlayer =
    function()
{
    return new mirosubs.video.ControlledVideoPlayer(
        this.createPlayer_(true));
};

mirosubs.video.Html5VideoSource.prototype.createPlayer_ =
    function(forSubDialog)
{
    if (this.videoType_ == mirosubs.video.Html5VideoType.H264 &&
        !mirosubs.video.supportsH264())
        return new mirosubs.video.FlvVideoPlayer(this, forSubDialog);
    else
        return new mirosubs.video.Html5VideoPlayer(
            new mirosubs.video.Html5VideoSource(
                this.videoURL_, this.videoType_, this.videoConfig_),
            forSubDialog);
};

mirosubs.video.Html5VideoSource.prototype.getFlvURL = function() {
    if (this.videoType_ != mirosubs.video.Html5VideoType.H264)
        throw new Error();
    return this.videoURL_;
};

mirosubs.video.Html5VideoSource.prototype.getVideoURL = function() {
    return this.videoURL_;
};

mirosubs.video.Html5VideoSource.prototype.getVideoType = function() {
    return this.videoType_;
};

mirosubs.video.Html5VideoSource.prototype.getVideoConfig = function() {
    return this.videoConfig_;
};

mirosubs.video.Html5VideoSource.prototype.setVideoConfig = function(config) {
    this.videoConfig_ = config;
};
<|endoftext|>