<commit_before>// The `sfnt` wrapper provides organization for the tables in the font.
// It is the top-level data structure in a font.
// https://www.microsoft.com/typography/OTSPEC/otff.htm
// Recommendations for creating OpenType Fonts:
// http://www.microsoft.com/typography/otspec140/recom.htm

'use strict';

var check = require('../check');
var table = require('../table');

var cmap = require('./cmap');
var cff = require('./cff');
var head = require('./head');
var hhea = require('./hhea');
var hmtx = require('./hmtx');
var ltag = require('./ltag');
var maxp = require('./maxp');
var _name = require('./name');
var os2 = require('./os2');
var post = require('./post');

function log2(v) {
    return Math.log(v) / Math.log(2) | 0;
}

function computeCheckSum(bytes) {
    while (bytes.length % 4 !== 0) {
        bytes.push(0);
    }

    var sum = 0;
    for (var i = 0; i < bytes.length; i += 4) {
        sum += (bytes[i] << 24) +
            (bytes[i + 1] << 16) +
            (bytes[i + 2] << 8) +
            (bytes[i + 3]);
    }

    sum %= Math.pow(2, 32);
    return sum;
}

function makeTableRecord(tag, checkSum, offset, length) {
    return new table.Record('Table Record', [
        {name: 'tag', type: 'TAG', value: tag !== undefined ? tag : ''},
        {name: 'checkSum', type: 'ULONG', value: checkSum !== undefined ? checkSum : 0},
        {name: 'offset', type: 'ULONG', value: offset !== undefined ? offset : 0},
        {name: 'length', type: 'ULONG', value: length !== undefined ? length : 0}
    ]);
}

function makeSfntTable(tables) {
    var sfnt = new table.Table('sfnt', [
        {name: 'version', type: 'TAG', value: 'OTTO'},
        {name: 'numTables', type: 'USHORT', value: 0},
        {name: 'searchRange', type: 'USHORT', value: 0},
        {name: 'entrySelector', type: 'USHORT', value: 0},
        {name: 'rangeShift', type: 'USHORT', value: 0}
    ]);
    sfnt.tables = tables;
    sfnt.numTables = tables.length;
    var highestPowerOf2 = Math.pow(2, log2(sfnt.numTables));
    sfnt.searchRange = 16 * highestPowerOf2;
    sfnt.entrySelector = log2(highestPowerOf2);
    sfnt.rangeShift = sfnt.numTables * 16 - sfnt.searchRange;

    var recordFields = [];
    var tableFields = [];

    var offset = sfnt.sizeOf() + (makeTableRecord().sizeOf() * sfnt.numTables);
    while (offset % 4 !== 0) {
        offset += 1;
        tableFields.push({name: 'padding', type: 'BYTE', value: 0});
    }

    for (var i = 0; i < tables.length; i += 1) {
        var t = tables[i];
        check.argument(t.tableName.length === 4, 'Table name' + t.tableName + ' is invalid.');
        var tableLength = t.sizeOf();
        var tableRecord = makeTableRecord(t.tableName, computeCheckSum(t.encode()), offset, tableLength);
        recordFields.push({name: tableRecord.tag + ' Table Record', type: 'RECORD', value: tableRecord});
        tableFields.push({name: t.tableName + ' table', type: 'RECORD', value: t});
        offset += tableLength;
        check.argument(!isNaN(offset), 'Something went wrong calculating the offset.');
        while (offset % 4 !== 0) {
            offset += 1;
            tableFields.push({name: 'padding', type: 'BYTE', value: 0});
        }
    }

    // Table records need to be sorted alphabetically.
    recordFields.sort(function(r1, r2) {
        if (r1.value.tag > r2.value.tag) {
            return 1;
        } else {
            return -1;
        }
    });

    sfnt.fields = sfnt.fields.concat(recordFields);
    sfnt.fields = sfnt.fields.concat(tableFields);
    return sfnt;
}

// Get the metrics for a character. If the string has more than one character
// this function returns metrics for the first available character.
// You can provide optional fallback metrics if no characters are available.
function metricsForChar(font, chars, notFoundMetrics) {
    for (var i = 0; i < chars.length; i += 1) {
        var glyphIndex = font.charToGlyphIndex(chars[i]);
        if (glyphIndex > 0) {
            var glyph = font.glyphs.get(glyphIndex);
            return glyph.getMetrics();
        }
    }

    return notFoundMetrics;
}

function average(vs) {
    var sum = 0;
    for (var i = 0; i < vs.length; i += 1) {
        sum += vs[i];
    }

    return sum / vs.length;
}

// Convert the font object to a SFNT data structure.
// This structure contains all the necessary tables and metadata to create a binary OTF file.
function fontToSfntTable(font) {
    var xMins = [];
    var yMins = [];
    var xMaxs = [];
    var yMaxs = [];
    var advanceWidths = [];
    var leftSideBearings = [];
    var rightSideBearings = [];
    var firstCharIndex;
    var lastCharIndex = 0;
    var ulUnicodeRange1 = 0;
    var ulUnicodeRange2 = 0;
    var ulUnicodeRange3 = 0;
    var ulUnicodeRange4 = 0;

    for (var i = 0; i < font.glyphs.length; i += 1) {
        var glyph = font.glyphs.get(i);
        var unicode = glyph.unicode | 0;

        if (typeof glyph.advanceWidth === 'undefined') {
            throw new Error('Glyph ' + glyph.name + ' (' + i + '): advanceWidth is required.');
        }

        if (firstCharIndex > unicode || firstCharIndex === undefined) {
            // ignore .notdef char
            if (unicode > 0) {
                firstCharIndex = unicode;
            }
        }

        if (lastCharIndex < unicode) {
            lastCharIndex = unicode;
        }

        var position = os2.getUnicodeRange(unicode);
        if (position < 32) {
            ulUnicodeRange1 |= 1 << position;
        } else if (position < 64) {
            ulUnicodeRange2 |= 1 << position - 32;
        } else if (position < 96) {
            ulUnicodeRange3 |= 1 << position - 64;
        } else if (position < 123) {
            ulUnicodeRange4 |= 1 << position - 96;
        } else {
            throw new Error('Unicode ranges bits > 123 are reserved for internal usage');
        }
        // Skip non-important characters.
        if (glyph.name === '.notdef') continue;
        var metrics = glyph.getMetrics();
        xMins.push(metrics.xMin);
        yMins.push(metrics.yMin);
        xMaxs.push(metrics.xMax);
        yMaxs.push(metrics.yMax);
        leftSideBearings.push(metrics.leftSideBearing);
        rightSideBearings.push(metrics.rightSideBearing);
        advanceWidths.push(glyph.advanceWidth);
    }

    var globals = {
        xMin: Math.min.apply(null, xMins),
        yMin: Math.min.apply(null, yMins),
        xMax: Math.max.apply(null, xMaxs),
        yMax: Math.max.apply(null, yMaxs),
        advanceWidthMax: Math.max.apply(null, advanceWidths),
        advanceWidthAvg: average(advanceWidths),
        minLeftSideBearing: Math.min.apply(null, leftSideBearings),
        maxLeftSideBearing: Math.max.apply(null, leftSideBearings),
        minRightSideBearing: Math.min.apply(null, rightSideBearings)
    };
    globals.ascender = font.ascender;
    globals.descender = font.descender;

    var headTable = head.make({
        flags: 3, // 00000011 (baseline for font at y=0; left sidebearing point at x=0)
        unitsPerEm: font.unitsPerEm,
        xMin: globals.xMin,
        yMin: globals.yMin,
        xMax: globals.xMax,
        yMax: globals.yMax,
        lowestRecPPEM: 3,
        createdTimestamp: font.createdTimestamp
    });

    var hheaTable = hhea.make({
        ascender: globals.ascender,
        descender: globals.descender,
        advanceWidthMax: globals.advanceWidthMax,
        minLeftSideBearing: globals.minLeftSideBearing,
        minRightSideBearing: globals.minRightSideBearing,
        xMaxExtent: globals.maxLeftSideBearing + (globals.xMax - globals.xMin),
        numberOfHMetrics: font.glyphs.length
    });

    var maxpTable = maxp.make(font.glyphs.length);

    var os2Table = os2.make({
        xAvgCharWidth: Math.round(globals.advanceWidthAvg),
        usWeightClass: font.tables.os2.usWeightClass,
        usWidthClass: font.tables.os2.usWidthClass,
        usFirstCharIndex: firstCharIndex,
        usLastCharIndex: lastCharIndex,
        ulUnicodeRange1: ulUnicodeRange1,
        ulUnicodeRange2: ulUnicodeRange2,
        ulUnicodeRange3: ulUnicodeRange3,
        ulUnicodeRange4: ulUnicodeRange4,
        fsSelection: font.tables.os2.fsSelection, // REGULAR
        // See http://typophile.com/node/13081 for more info on vertical metrics.
        // We get metrics for typical characters (such as "x" for xHeight).
        // We provide some fallback characters if characters are unavailable: their
        // ordering was chosen experimentally.
        sTypoAscender: globals.ascender,
        sTypoDescender: globals.descender,
        sTypoLineGap: 0,
        usWinAscent: globals.yMax,
        usWinDescent: Math.abs(globals.yMin),
        ulCodePageRange1: 1, // FIXME: hard-code Latin 1 support for now
        sxHeight: metricsForChar(font, 'xyvw', {yMax: Math.round(globals.ascender / 2)}).yMax,
        sCapHeight: metricsForChar(font, 'HIKLEFJMNTZBDPRAGOQSUVWXY', globals).yMax,
        usDefaultChar: font.hasChar(' ') ? 32 : 0, // Use space as the default character, if available.
        usBreakChar: font.hasChar(' ') ? 32 : 0 // Use space as the break character, if available.
    });

    var hmtxTable = hmtx.make(font.glyphs);
    var cmapTable = cmap.make(font.glyphs);

    var englishFamilyName = font.getEnglishName('fontFamily');
    var englishStyleName = font.getEnglishName('fontSubfamily');
    var englishFullName = englishFamilyName + ' ' + englishStyleName;
    var postScriptName = font.getEnglishName('postScriptName');
    if (!postScriptName) {
        postScriptName = englishFamilyName.replace(/\s/g, '') + '-' + englishStyleName;
    }

    var names = {};
    for (var n in font.names) {
        names[n] = font.names[n];
    }

    if (!names.uniqueID) {
        names.uniqueID = {en: font.getEnglishName('manufacturer') + ':' + englishFullName};
    }

    if (!names.postScriptName) {
        names.postScriptName = {en: postScriptName};
    }

    if (!names.preferredFamily) {
        names.preferredFamily = font.names.fontFamily;
    }

    if (!names.preferredSubfamily) {
        names.preferredSubfamily = font.names.fontSubfamily;
    }

    var languageTags = [];
    var nameTable = _name.make(names, languageTags);
    var ltagTable = (languageTags.length > 0 ? ltag.make(languageTags) : undefined);

    var postTable = post.make();
    var cffTable = cff.make(font.glyphs, {
        version: font.getEnglishName('version'),
        fullName: englishFullName,
        familyName: englishFamilyName,
        weightName: englishStyleName,
        postScriptName: postScriptName,
        unitsPerEm: font.unitsPerEm,
        fontBBox: [0, globals.yMin, globals.ascender, globals.advanceWidthMax]
    });

    // The order does not matter because makeSfntTable() will sort them.
    var tables = [headTable, hheaTable, maxpTable, os2Table, nameTable, cmapTable, postTable, cffTable, hmtxTable];
    if (ltagTable) {
        tables.push(ltagTable);
    }

    var sfntTable = makeSfntTable(tables);

    // Compute the font's checkSum and store it in head.checkSumAdjustment.
    var bytes = sfntTable.encode();
    var checkSum = computeCheckSum(bytes);
    var tableFields = sfntTable.fields;
    var checkSumAdjusted = false;
    for (i = 0; i < tableFields.length; i += 1) {
        if (tableFields[i].name === 'head table') {
            tableFields[i].value.checkSumAdjustment = 0xB1B0AFBA - checkSum;
            checkSumAdjusted = true;
            break;
        }
    }

    if (!checkSumAdjusted) {
        throw new Error('Could not find head table with checkSum to adjust.');
    }

    return sfntTable;
}

exports.computeCheckSum = computeCheckSum;
exports.make = makeSfntTable;
exports.fontToTable = fontToSfntTable;
<commit_msg>fix detection of incorrect  advanceWidth<commit_after>// The `sfnt` wrapper provides organization for the tables in the font.
// It is the top-level data structure in a font.
// https://www.microsoft.com/typography/OTSPEC/otff.htm
// Recommendations for creating OpenType Fonts:
// http://www.microsoft.com/typography/otspec140/recom.htm

'use strict';

var check = require('../check');
var table = require('../table');

var cmap = require('./cmap');
var cff = require('./cff');
var head = require('./head');
var hhea = require('./hhea');
var hmtx = require('./hmtx');
var ltag = require('./ltag');
var maxp = require('./maxp');
var _name = require('./name');
var os2 = require('./os2');
var post = require('./post');

function log2(v) {
    return Math.log(v) / Math.log(2) | 0;
}

function computeCheckSum(bytes) {
    while (bytes.length % 4 !== 0) {
        bytes.push(0);
    }

    var sum = 0;
    for (var i = 0; i < bytes.length; i += 4) {
        sum += (bytes[i] << 24) +
            (bytes[i + 1] << 16) +
            (bytes[i + 2] << 8) +
            (bytes[i + 3]);
    }

    sum %= Math.pow(2, 32);
    return sum;
}

function makeTableRecord(tag, checkSum, offset, length) {
    return new table.Record('Table Record', [
        {name: 'tag', type: 'TAG', value: tag !== undefined ? tag : ''},
        {name: 'checkSum', type: 'ULONG', value: checkSum !== undefined ? checkSum : 0},
        {name: 'offset', type: 'ULONG', value: offset !== undefined ? offset : 0},
        {name: 'length', type: 'ULONG', value: length !== undefined ? length : 0}
    ]);
}

function makeSfntTable(tables) {
    var sfnt = new table.Table('sfnt', [
        {name: 'version', type: 'TAG', value: 'OTTO'},
        {name: 'numTables', type: 'USHORT', value: 0},
        {name: 'searchRange', type: 'USHORT', value: 0},
        {name: 'entrySelector', type: 'USHORT', value: 0},
        {name: 'rangeShift', type: 'USHORT', value: 0}
    ]);
    sfnt.tables = tables;
    sfnt.numTables = tables.length;
    var highestPowerOf2 = Math.pow(2, log2(sfnt.numTables));
    sfnt.searchRange = 16 * highestPowerOf2;
    sfnt.entrySelector = log2(highestPowerOf2);
    sfnt.rangeShift = sfnt.numTables * 16 - sfnt.searchRange;

    var recordFields = [];
    var tableFields = [];

    var offset = sfnt.sizeOf() + (makeTableRecord().sizeOf() * sfnt.numTables);
    while (offset % 4 !== 0) {
        offset += 1;
        tableFields.push({name: 'padding', type: 'BYTE', value: 0});
    }

    for (var i = 0; i < tables.length; i += 1) {
        var t = tables[i];
        check.argument(t.tableName.length === 4, 'Table name' + t.tableName + ' is invalid.');
        var tableLength = t.sizeOf();
        var tableRecord = makeTableRecord(t.tableName, computeCheckSum(t.encode()), offset, tableLength);
        recordFields.push({name: tableRecord.tag + ' Table Record', type: 'RECORD', value: tableRecord});
        tableFields.push({name: t.tableName + ' table', type: 'RECORD', value: t});
        offset += tableLength;
        check.argument(!isNaN(offset), 'Something went wrong calculating the offset.');
        while (offset % 4 !== 0) {
            offset += 1;
            tableFields.push({name: 'padding', type: 'BYTE', value: 0});
        }
    }

    // Table records need to be sorted alphabetically.
    recordFields.sort(function(r1, r2) {
        if (r1.value.tag > r2.value.tag) {
            return 1;
        } else {
            return -1;
        }
    });

    sfnt.fields = sfnt.fields.concat(recordFields);
    sfnt.fields = sfnt.fields.concat(tableFields);
    return sfnt;
}

// Get the metrics for a character. If the string has more than one character
// this function returns metrics for the first available character.
// You can provide optional fallback metrics if no characters are available.
function metricsForChar(font, chars, notFoundMetrics) {
    for (var i = 0; i < chars.length; i += 1) {
        var glyphIndex = font.charToGlyphIndex(chars[i]);
        if (glyphIndex > 0) {
            var glyph = font.glyphs.get(glyphIndex);
            return glyph.getMetrics();
        }
    }

    return notFoundMetrics;
}

function average(vs) {
    var sum = 0;
    for (var i = 0; i < vs.length; i += 1) {
        sum += vs[i];
    }

    return sum / vs.length;
}

// Convert the font object to a SFNT data structure.
// This structure contains all the necessary tables and metadata to create a binary OTF file.
function fontToSfntTable(font) {
    var xMins = [];
    var yMins = [];
    var xMaxs = [];
    var yMaxs = [];
    var advanceWidths = [];
    var leftSideBearings = [];
    var rightSideBearings = [];
    var firstCharIndex;
    var lastCharIndex = 0;
    var ulUnicodeRange1 = 0;
    var ulUnicodeRange2 = 0;
    var ulUnicodeRange3 = 0;
    var ulUnicodeRange4 = 0;

    for (var i = 0; i < font.glyphs.length; i += 1) {
        var glyph = font.glyphs.get(i);
        var unicode = glyph.unicode | 0;

        if (isNaN(glyph.advanceWidth)) {
            throw new Error('Glyph ' + glyph.name + ' (' + i + '): advanceWidth is required.');
        }

        if (firstCharIndex > unicode || firstCharIndex === undefined) {
            // ignore .notdef char
            if (unicode > 0) {
                firstCharIndex = unicode;
            }
        }

        if (lastCharIndex < unicode) {
            lastCharIndex = unicode;
        }

        var position = os2.getUnicodeRange(unicode);
        if (position < 32) {
            ulUnicodeRange1 |= 1 << position;
        } else if (position < 64) {
            ulUnicodeRange2 |= 1 << position - 32;
        } else if (position < 96) {
            ulUnicodeRange3 |= 1 << position - 64;
        } else if (position < 123) {
            ulUnicodeRange4 |= 1 << position - 96;
        } else {
            throw new Error('Unicode ranges bits > 123 are reserved for internal usage');
        }
        // Skip non-important characters.
        if (glyph.name === '.notdef') continue;
        var metrics = glyph.getMetrics();
        xMins.push(metrics.xMin);
        yMins.push(metrics.yMin);
        xMaxs.push(metrics.xMax);
        yMaxs.push(metrics.yMax);
        leftSideBearings.push(metrics.leftSideBearing);
        rightSideBearings.push(metrics.rightSideBearing);
        advanceWidths.push(glyph.advanceWidth);
    }

    var globals = {
        xMin: Math.min.apply(null, xMins),
        yMin: Math.min.apply(null, yMins),
        xMax: Math.max.apply(null, xMaxs),
        yMax: Math.max.apply(null, yMaxs),
        advanceWidthMax: Math.max.apply(null, advanceWidths),
        advanceWidthAvg: average(advanceWidths),
        minLeftSideBearing: Math.min.apply(null, leftSideBearings),
        maxLeftSideBearing: Math.max.apply(null, leftSideBearings),
        minRightSideBearing: Math.min.apply(null, rightSideBearings)
    };
    globals.ascender = font.ascender;
    globals.descender = font.descender;

    var headTable = head.make({
        flags: 3, // 00000011 (baseline for font at y=0; left sidebearing point at x=0)
        unitsPerEm: font.unitsPerEm,
        xMin: globals.xMin,
        yMin: globals.yMin,
        xMax: globals.xMax,
        yMax: globals.yMax,
        lowestRecPPEM: 3,
        createdTimestamp: font.createdTimestamp
    });

    var hheaTable = hhea.make({
        ascender: globals.ascender,
        descender: globals.descender,
        advanceWidthMax: globals.advanceWidthMax,
        minLeftSideBearing: globals.minLeftSideBearing,
        minRightSideBearing: globals.minRightSideBearing,
        xMaxExtent: globals.maxLeftSideBearing + (globals.xMax - globals.xMin),
        numberOfHMetrics: font.glyphs.length
    });

    var maxpTable = maxp.make(font.glyphs.length);

    var os2Table = os2.make({
        xAvgCharWidth: Math.round(globals.advanceWidthAvg),
        usWeightClass: font.tables.os2.usWeightClass,
        usWidthClass: font.tables.os2.usWidthClass,
        usFirstCharIndex: firstCharIndex,
        usLastCharIndex: lastCharIndex,
        ulUnicodeRange1: ulUnicodeRange1,
        ulUnicodeRange2: ulUnicodeRange2,
        ulUnicodeRange3: ulUnicodeRange3,
        ulUnicodeRange4: ulUnicodeRange4,
        fsSelection: font.tables.os2.fsSelection, // REGULAR
        // See http://typophile.com/node/13081 for more info on vertical metrics.
        // We get metrics for typical characters (such as "x" for xHeight).
        // We provide some fallback characters if characters are unavailable: their
        // ordering was chosen experimentally.
        sTypoAscender: globals.ascender,
        sTypoDescender: globals.descender,
        sTypoLineGap: 0,
        usWinAscent: globals.yMax,
        usWinDescent: Math.abs(globals.yMin),
        ulCodePageRange1: 1, // FIXME: hard-code Latin 1 support for now
        sxHeight: metricsForChar(font, 'xyvw', {yMax: Math.round(globals.ascender / 2)}).yMax,
        sCapHeight: metricsForChar(font, 'HIKLEFJMNTZBDPRAGOQSUVWXY', globals).yMax,
        usDefaultChar: font.hasChar(' ') ? 32 : 0, // Use space as the default character, if available.
        usBreakChar: font.hasChar(' ') ? 32 : 0 // Use space as the break character, if available.
    });

    var hmtxTable = hmtx.make(font.glyphs);
    var cmapTable = cmap.make(font.glyphs);

    var englishFamilyName = font.getEnglishName('fontFamily');
    var englishStyleName = font.getEnglishName('fontSubfamily');
    var englishFullName = englishFamilyName + ' ' + englishStyleName;
    var postScriptName = font.getEnglishName('postScriptName');
    if (!postScriptName) {
        postScriptName = englishFamilyName.replace(/\s/g, '') + '-' + englishStyleName;
    }

    var names = {};
    for (var n in font.names) {
        names[n] = font.names[n];
    }

    if (!names.uniqueID) {
        names.uniqueID = {en: font.getEnglishName('manufacturer') + ':' + englishFullName};
    }

    if (!names.postScriptName) {
        names.postScriptName = {en: postScriptName};
    }

    if (!names.preferredFamily) {
        names.preferredFamily = font.names.fontFamily;
    }

    if (!names.preferredSubfamily) {
        names.preferredSubfamily = font.names.fontSubfamily;
    }

    var languageTags = [];
    var nameTable = _name.make(names, languageTags);
    var ltagTable = (languageTags.length > 0 ? ltag.make(languageTags) : undefined);

    var postTable = post.make();
    var cffTable = cff.make(font.glyphs, {
        version: font.getEnglishName('version'),
        fullName: englishFullName,
        familyName: englishFamilyName,
        weightName: englishStyleName,
        postScriptName: postScriptName,
        unitsPerEm: font.unitsPerEm,
        fontBBox: [0, globals.yMin, globals.ascender, globals.advanceWidthMax]
    });

    // The order does not matter because makeSfntTable() will sort them.
    var tables = [headTable, hheaTable, maxpTable, os2Table, nameTable, cmapTable, postTable, cffTable, hmtxTable];
    if (ltagTable) {
        tables.push(ltagTable);
    }

    var sfntTable = makeSfntTable(tables);

    // Compute the font's checkSum and store it in head.checkSumAdjustment.
    var bytes = sfntTable.encode();
    var checkSum = computeCheckSum(bytes);
    var tableFields = sfntTable.fields;
    var checkSumAdjusted = false;
    for (i = 0; i < tableFields.length; i += 1) {
        if (tableFields[i].name === 'head table') {
            tableFields[i].value.checkSumAdjustment = 0xB1B0AFBA - checkSum;
            checkSumAdjusted = true;
            break;
        }
    }

    if (!checkSumAdjusted) {
        throw new Error('Could not find head table with checkSum to adjust.');
    }

    return sfntTable;
}

exports.computeCheckSum = computeCheckSum;
exports.make = makeSfntTable;
exports.fontToTable = fontToSfntTable;
<|endoftext|>


<commit_before>/**
 * Created by anonymous on 10/12/15 2:22.
 */

(function() {
    'use strict';

    angular
        .module('client')
        .controller('ClientFooterController', ClientFooterController);

    ClientFooterController.$inject = [];

    /* @ngInject */
    function ClientFooterController() {
        var vm   = this;
        vm.title = 'ClientFooterController';

        activate();

        ////////////////

        function activate() {
            //
        }
    }

})();

<commit_msg>Modified footer.controller<commit_after>/**
 * footer.controller.js
 * Created by anonymous on 10/12/15 2:22.
 */

(function() {
    'use strict';

    angular
        .module('app')
        .controller('ClientFooterController', ClientFooterController);

    ClientFooterController.$inject = [];

    /* @ngInject */
    function ClientFooterController() {
        var vm   = this;

        activate();

        ////////////////

        function activate() {
            //
        }
    }

})();

<|endoftext|>


<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

 // All functions in this file should be treated as deprecated legacy routines.
 // Gadget authors are explicitly discouraged from using any of them.

var JSON = gadgets.json;
var _IG_Prefs = gadgets.Prefs;

// Yes, these technically modifiy gadget.Prefs as well. Unfortunately,
// simply setting IG_Prefs.prototype to a new gadgets.Prefs object means
// that we'd have to duplicate the gadgets.Prefs constructor.
_IG_Prefs._parseURL = gadgets.Prefs.parseUrl;

function _IG_Fetch_wrapper(callback, obj) {
  callback(obj.data);
}

function _IG_FetchContent(url, callback, opt_params) {
  var params = opt_params || {};
  // this is really the only legacy parameter documented
  // at http://code.google.com/apis/gadgets/docs/remote-content.html#Params
  if (params.refreshInterval) {
    params['REFRESH_INTERVAL'] = params.refreshInterval;
  } else {
    params['REFRESH_INTERVAL'] = 3600;
  }
  var cb = gadgets.util.makeClosure(null, _IG_Fetch_wrapper, callback);
  gadgets.io.makeRequest(url, cb, params);
}

function _IG_FetchXmlContent(url, callback, opt_params) {
  var params = opt_params || {};
  if (params.refreshInterval) {
    params['REFRESH_INTERVAL'] = params.refreshInterval;
  } else {
    params['REFRESH_INTERVAL'] = 3600;
  }
  params.CONTENT_TYPE = "DOM";
  var cb = gadgets.util.makeClosure(null, _IG_Fetch_wrapper, callback);
  gadgets.io.makeRequest(url, cb, params);
}

// TODO: The server doesn't actually support FEED. Fix this!
function _IG_FetchFeedAsJSON(url, callback, numItems, getDescriptions,
                             opt_params) {
  var params = opt_params || {};
  params.CONTENT_TYPE = "FEED";
  params.NUM_ENTRIES = numItems;
  params.GET_SUMMARIES = getDescriptions;
  gadgets.io.makeRequest(url,
      function(resp) {
        // special case error reporting for back-compatibility
        // see http://code.google.com/apis/gadgets/docs/remote-content.html#Fetch_JSON
        if (resp.errors) {
          resp.data = resp.data || {};
          if (resp.errors && resp.errors.length > 0) {
            resp.data.ErrorMsg = resp.errors[0];
          }
        }
        // for Gadgets back-compatibility, return the feed obj directly
        callback(resp.data);
      }, params);
}

function _IG_GetCachedUrl(url) {
  return gadgets.io.getProxyUrl(url);
}
function _IG_GetImageUrl(url) {
  return gadgets.io.getProxyUrl(url);
}

function _IG_RegisterOnloadHandler(callback) {
  gadgets.util.registerOnLoadHandler(callback);
}

// _IG_Callback takes the arguments in the scope the callback is executed and
// places them first in the argument array. MakeClosure takes the arguments
// from the scope at callback construction and pushes them first in the array
function _IG_Callback(handler_func, var_args) {
  var orig_args = arguments;
  return function() {
    var combined_args = Array.prototype.slice.call(arguments);
    // call the handler with all args combined
    handler_func.apply(null,
      combined_args.concat(Array.prototype.slice.call(orig_args, 1)));
  };
}

var _args = gadgets.util.getUrlParameters;

/**
 * Fetches an object by document id.
 *
 * @param {String | Object} el The element you wish to fetch. You may pass
 *     an object in which allows this to be called regardless of whether or
 *     not the type of the input is known.
 * @return {HTMLElement} The element, if it exists in the document, or null.
 */
function _gel(el) {
  return document.getElementById ? document.getElementById(el) : null;
}

/**
 * Fetches elements by tag name.
 * This is functionally identical to document.getElementsByTagName()
 *
 * @param {String} tag The tag to match elements against.
 * @return {Array.<HTMLElement>} All elements of this tag type.
 */
function _gelstn(tag) {
  if (tag === "*" && document.all) {
    return document.all;
  }
  return document.getElementsByTagName ?
         document.getElementsByTagName(tag) : [];
}

/**
 * Fetches elements with ids matching a given regular expression.
 *
 * @param {tagName} tag The tag to match elements against.
 * @param {RegEx} regex The expression to match.
 * @return {Array.<HTMLElement>} All elements of this tag type that match
 *     regex.
 */
function _gelsbyregex(tagName, regex) {
  var matchingTags = _gelstn(tagName);
  var matchingRegex = [];
  for (var i = 0, j = matchingTags.length; i < j; ++i) {
    if (regex.test(matchingTags[i].id)) {
      matchingRegex.push(matchingTags[i]);
    }
  }
  return matchingRegex;
}

/**
 * URI escapes the given string.
 * @param {String} str The string to escape.
 * @return {String} The escaped string.
 */
function _esc(str) {
  return window.encodeURIComponent ? encodeURIComponent(str) : escape(str);
}

/**
 * URI unescapes the given string.
 * @param {String} str The string to unescape.
 * @return {String} The unescaped string.
 */
function _unesc(str) {
  return window.decodeURIComponent ? decodeURIComponent(str) : unescape(str);
}

/**
 * Encodes HTML entities such as <, " and >.
 *
 * @param {String} str The string to escape.
 * @return The escaped string.
 */
function _hesc(str) {
  return gadgets.util.escapeString(str);
}

/**
 * Removes HTML tags from the given input string.
 *
 * @param {String} str The string to strip.
 * @return The stripped string.
 */
function _striptags(str) {
  return str.replace(/<\/?[^>]+>/g, "");
}

/**
 * Trims leading & trailing whitespace from the given string.
 *
 * @param {String} str The string to trim.
 * @return {String} The trimmed string.
 */
function _trim(str) {
  return str.replace(/^\s+|\s+$/g, "");
}

/**
 * Toggles the given element between being shown and block-style display.
 *
 * @param {String | HTMLElement} el The element to toggle.
 */
function _toggle(el) {
  el = _gel(el);
  if (el !== null) {
    if (el.style.display.length === 0 || el.style.display === "block") {
      el.style.display = "none";
    } else if (el.style.display === "none") {
      el.style.display = "block";
    }
  }
}

/**
 * {Number} A counter used by uniqueId().
 */
var _global_legacy_uidCounter = 0;

/**
 * @return a unique number.
 */
function _uid() {
  return _global_legacy_uidCounter++;
}

/**
 * @param {Number} a
 * @param {Number} b
 * @return The lesser of a or b.
 */
function _min(a, b) {
  return (a < b ? a : b);
}

/**
 * @param {Number} a
 * @param {Number} b
 * @return The greater of a or b.
 */
function _max(a, b) {
  return (a > b ? a : b);
}

/**
 * @param {String} name
 * @param {Array.<String | Object>} sym
 */
function _exportSymbols(name, sym) {
  var obj = {};

  for (var i = 0, j = sym.length; i < j; i += 2) {
    obj[sym[i]] = sym[i + 1];
  }
  var parts = name.split(".");
  var attach = window;
  for (var k = 0, l = parts.length - 1; k < l; ++k) {
    var tmp = {};
    attach[parts[k]] = tmp;
    attach = tmp;
  }
  attach[parts[parts.length - 1]] = obj;
}
<commit_msg>Adding _IG_GetImage to legacy shim API.<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

 // All functions in this file should be treated as deprecated legacy routines.
 // Gadget authors are explicitly discouraged from using any of them.

var JSON = gadgets.json;
var _IG_Prefs = gadgets.Prefs;

// Yes, these technically modifiy gadget.Prefs as well. Unfortunately,
// simply setting IG_Prefs.prototype to a new gadgets.Prefs object means
// that we'd have to duplicate the gadgets.Prefs constructor.
_IG_Prefs._parseURL = gadgets.Prefs.parseUrl;

function _IG_Fetch_wrapper(callback, obj) {
  callback(obj.data);
}

function _IG_FetchContent(url, callback, opt_params) {
  var params = opt_params || {};
  // this is really the only legacy parameter documented
  // at http://code.google.com/apis/gadgets/docs/remote-content.html#Params
  if (params.refreshInterval) {
    params['REFRESH_INTERVAL'] = params.refreshInterval;
  } else {
    params['REFRESH_INTERVAL'] = 3600;
  }
  var cb = gadgets.util.makeClosure(null, _IG_Fetch_wrapper, callback);
  gadgets.io.makeRequest(url, cb, params);
}

function _IG_FetchXmlContent(url, callback, opt_params) {
  var params = opt_params || {};
  if (params.refreshInterval) {
    params['REFRESH_INTERVAL'] = params.refreshInterval;
  } else {
    params['REFRESH_INTERVAL'] = 3600;
  }
  params.CONTENT_TYPE = "DOM";
  var cb = gadgets.util.makeClosure(null, _IG_Fetch_wrapper, callback);
  gadgets.io.makeRequest(url, cb, params);
}

// TODO: The server doesn't actually support FEED. Fix this!
function _IG_FetchFeedAsJSON(url, callback, numItems, getDescriptions,
                             opt_params) {
  var params = opt_params || {};
  params.CONTENT_TYPE = "FEED";
  params.NUM_ENTRIES = numItems;
  params.GET_SUMMARIES = getDescriptions;
  gadgets.io.makeRequest(url,
      function(resp) {
        // special case error reporting for back-compatibility
        // see http://code.google.com/apis/gadgets/docs/remote-content.html#Fetch_JSON
        if (resp.errors) {
          resp.data = resp.data || {};
          if (resp.errors && resp.errors.length > 0) {
            resp.data.ErrorMsg = resp.errors[0];
          }
        }
        // for Gadgets back-compatibility, return the feed obj directly
        callback(resp.data);
      }, params);
}

function _IG_GetCachedUrl(url) {
  return gadgets.io.getProxyUrl(url);
}
function _IG_GetImageUrl(url) {
  return gadgets.io.getProxyUrl(url);
}
function _IG_GetImage(url) {
  var img = document.createElement('img');
  img.src = _IG_GetCachedUrl(url);
  return img;
}

function _IG_RegisterOnloadHandler(callback) {
  gadgets.util.registerOnLoadHandler(callback);
}

// _IG_Callback takes the arguments in the scope the callback is executed and
// places them first in the argument array. MakeClosure takes the arguments
// from the scope at callback construction and pushes them first in the array
function _IG_Callback(handler_func, var_args) {
  var orig_args = arguments;
  return function() {
    var combined_args = Array.prototype.slice.call(arguments);
    // call the handler with all args combined
    handler_func.apply(null,
      combined_args.concat(Array.prototype.slice.call(orig_args, 1)));
  };
}

var _args = gadgets.util.getUrlParameters;

/**
 * Fetches an object by document id.
 *
 * @param {String | Object} el The element you wish to fetch. You may pass
 *     an object in which allows this to be called regardless of whether or
 *     not the type of the input is known.
 * @return {HTMLElement} The element, if it exists in the document, or null.
 */
function _gel(el) {
  return document.getElementById ? document.getElementById(el) : null;
}

/**
 * Fetches elements by tag name.
 * This is functionally identical to document.getElementsByTagName()
 *
 * @param {String} tag The tag to match elements against.
 * @return {Array.<HTMLElement>} All elements of this tag type.
 */
function _gelstn(tag) {
  if (tag === "*" && document.all) {
    return document.all;
  }
  return document.getElementsByTagName ?
         document.getElementsByTagName(tag) : [];
}

/**
 * Fetches elements with ids matching a given regular expression.
 *
 * @param {tagName} tag The tag to match elements against.
 * @param {RegEx} regex The expression to match.
 * @return {Array.<HTMLElement>} All elements of this tag type that match
 *     regex.
 */
function _gelsbyregex(tagName, regex) {
  var matchingTags = _gelstn(tagName);
  var matchingRegex = [];
  for (var i = 0, j = matchingTags.length; i < j; ++i) {
    if (regex.test(matchingTags[i].id)) {
      matchingRegex.push(matchingTags[i]);
    }
  }
  return matchingRegex;
}

/**
 * URI escapes the given string.
 * @param {String} str The string to escape.
 * @return {String} The escaped string.
 */
function _esc(str) {
  return window.encodeURIComponent ? encodeURIComponent(str) : escape(str);
}

/**
 * URI unescapes the given string.
 * @param {String} str The string to unescape.
 * @return {String} The unescaped string.
 */
function _unesc(str) {
  return window.decodeURIComponent ? decodeURIComponent(str) : unescape(str);
}

/**
 * Encodes HTML entities such as <, " and >.
 *
 * @param {String} str The string to escape.
 * @return The escaped string.
 */
function _hesc(str) {
  return gadgets.util.escapeString(str);
}

/**
 * Removes HTML tags from the given input string.
 *
 * @param {String} str The string to strip.
 * @return The stripped string.
 */
function _striptags(str) {
  return str.replace(/<\/?[^>]+>/g, "");
}

/**
 * Trims leading & trailing whitespace from the given string.
 *
 * @param {String} str The string to trim.
 * @return {String} The trimmed string.
 */
function _trim(str) {
  return str.replace(/^\s+|\s+$/g, "");
}

/**
 * Toggles the given element between being shown and block-style display.
 *
 * @param {String | HTMLElement} el The element to toggle.
 */
function _toggle(el) {
  el = _gel(el);
  if (el !== null) {
    if (el.style.display.length === 0 || el.style.display === "block") {
      el.style.display = "none";
    } else if (el.style.display === "none") {
      el.style.display = "block";
    }
  }
}

/**
 * {Number} A counter used by uniqueId().
 */
var _global_legacy_uidCounter = 0;

/**
 * @return a unique number.
 */
function _uid() {
  return _global_legacy_uidCounter++;
}

/**
 * @param {Number} a
 * @param {Number} b
 * @return The lesser of a or b.
 */
function _min(a, b) {
  return (a < b ? a : b);
}

/**
 * @param {Number} a
 * @param {Number} b
 * @return The greater of a or b.
 */
function _max(a, b) {
  return (a > b ? a : b);
}

/**
 * @param {String} name
 * @param {Array.<String | Object>} sym
 */
function _exportSymbols(name, sym) {
  var obj = {};

  for (var i = 0, j = sym.length; i < j; i += 2) {
    obj[sym[i]] = sym[i + 1];
  }
  var parts = name.split(".");
  var attach = window;
  for (var k = 0, l = parts.length - 1; k < l; ++k) {
    var tmp = {};
    attach[parts[k]] = tmp;
    attach = tmp;
  }
  attach[parts[parts.length - 1]] = obj;
}
<|endoftext|>


<commit_before>function isImage(mime) {
    switch (mime) {
    case "image/gif":
    case "image/jpeg":
    case "image/png":
    case "image/tiff":
    case "image/x-ms-bmp":
    case "image/bmp":
        return true;
    default:
        return false;
    }
}

function numberWithCommas(n) {
    n = n.toString();
    var pattern = /(-?\d+)(\d{3})/;
    while (pattern.test(n)) {
        n = n.replace(pattern, "$1,$2");
    }
    return n;
}

function bytesToSizeStr(bytes) {
    if(bytes === 0) return '0 Byte';
    var k = 1000;
    var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
    var i = Math.floor(Math.log(bytes) / Math.log(k));
    return (bytes / Math.pow(k, i)).toPrecision(3) + ' ' + sizes[i];

}

function endsWith(str, suffix) {
    return str.indexOf(suffix, str.length - suffix.length) !== -1;
}

function _add_json_callback(url) {
    return url + "&callback=JSON_CALLBACK";
}

function _add_json_callback2(url) {
    var qIndex = url.indexOf("?");
    var argSeparator = "&";
    if (qIndex == -1) {
        argSeparator = "?";
    }

    return url + argSeparator + "callback=JSON_CALLBACK";
}

function differenceById(from, others) {
    var idsFrom = from.map(function(entry) {
        return entry.id;
    });
    var idsOthers = others.map(function(entry) {
        return entry.id;
    });

    var diff = _.difference(idsFrom, idsOthers);
    return from.filter(function(entry) {
        return _.indexOf(diff, function(e) {
            return e == entry.id;
        }) !== -1;
    });
}

function removeById(from, what) {
    var i = _.indexOf(from, function(item) {
        return item.id === what.id;
    });

    if (i !== -1) {
        from.splice(i, 1);
    }
}

String.prototype.capitalize = function () {
    return this.replace(/(?:^|\s)\S/g, function (a) {
        return a.toUpperCase();
    });
};

// save a reference to the core implementation
var indexOfValue = _.indexOf;

// using .mixin allows both wrapped and unwrapped calls:
// _(array).indexOf(...) and _.indexOf(array, ...)
_.mixin({

    // return the index of the first array element passing a test
    indexOf: function (array, test) {
        // delegate to standard indexOf if the test isn't a function
        if (!_.isFunction(test)) return indexOfValue(array, test);
        // otherwise, look for the index
        for (var x = 0; x < array.length; x++) {
            if (test(array[x])) return x;
        }
        // not found, return fail value
        return -1;
    }

    //contains: function(list, value, fromIndex) {
    //    return _.includes(list, value, fromIndex);
    //}
});
<commit_msg>Return index for the found item.<commit_after>function isImage(mime) {
    switch (mime) {
    case "image/gif":
    case "image/jpeg":
    case "image/png":
    case "image/tiff":
    case "image/x-ms-bmp":
    case "image/bmp":
        return true;
    default:
        return false;
    }
}

function numberWithCommas(n) {
    n = n.toString();
    var pattern = /(-?\d+)(\d{3})/;
    while (pattern.test(n)) {
        n = n.replace(pattern, "$1,$2");
    }
    return n;
}

function bytesToSizeStr(bytes) {
    if(bytes === 0) return '0 Byte';
    var k = 1000;
    var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
    var i = Math.floor(Math.log(bytes) / Math.log(k));
    return (bytes / Math.pow(k, i)).toPrecision(3) + ' ' + sizes[i];

}

function endsWith(str, suffix) {
    return str.indexOf(suffix, str.length - suffix.length) !== -1;
}

function _add_json_callback(url) {
    return url + "&callback=JSON_CALLBACK";
}

function _add_json_callback2(url) {
    var qIndex = url.indexOf("?");
    var argSeparator = "&";
    if (qIndex == -1) {
        argSeparator = "?";
    }

    return url + argSeparator + "callback=JSON_CALLBACK";
}

function differenceById(from, others) {
    var idsFrom = from.map(function(entry) {
        return entry.id;
    });
    var idsOthers = others.map(function(entry) {
        return entry.id;
    });

    var diff = _.difference(idsFrom, idsOthers);
    return from.filter(function(entry) {
        return _.indexOf(diff, function(e) {
            return e == entry.id;
        }) !== -1;
    });
}

function removeById(from, what) {
    var i = _.indexOf(from, function(item) {
        return item.id === what.id;
    });

    if (i !== -1) {
        from.splice(i, 1);
    }

    return i;
}

String.prototype.capitalize = function () {
    return this.replace(/(?:^|\s)\S/g, function (a) {
        return a.toUpperCase();
    });
};

// save a reference to the core implementation
var indexOfValue = _.indexOf;

// using .mixin allows both wrapped and unwrapped calls:
// _(array).indexOf(...) and _.indexOf(array, ...)
_.mixin({

    // return the index of the first array element passing a test
    indexOf: function (array, test) {
        // delegate to standard indexOf if the test isn't a function
        if (!_.isFunction(test)) return indexOfValue(array, test);
        // otherwise, look for the index
        for (var x = 0; x < array.length; x++) {
            if (test(array[x])) return x;
        }
        // not found, return fail value
        return -1;
    }

    //contains: function(list, value, fromIndex) {
    //    return _.includes(list, value, fromIndex);
    //}
});
<|endoftext|>


<commit_before>function Module(initLang, langFile){
	var MODULE_EXTENSION = ".ccsvm";
	var baseDirectory;
	var instance = this;

	var callbacks = {
		scriptsLoaded: [],
		scriptLoaded: [],

		modulesLoaded: [],
		moduleLoaded: [],

		langInitDone: [],
		langChanged: []
	}

	var langStore = {};
	var langList = [];

	this.sharedMemory = {};
	this.selectedLang = {langId: "en", langIdSub: "us"};

	//Constructor
	function initialize(initLang, langFile){
		if ((initLang) &&
			(initLang.langId) &&
			(initLang.langIdSub)) {
			instance.selectedLang.langId = initLang.langId;
			instance.selectedLang.langIdSub = initLang.langIdSub;
		}

		$.getJSON(langFile).done(function(json) {
			langStore = json;

			for (id in langStore) {
				for (subid in langStore[id]) {
					langStore[id][subid].content = {};
					langList.push({
						langName: langStore[id][subid].langName,
						langNameSub: langStore[id][subid].langNameSub,
						langId: id,
						langIdSub: subid
					});
				}
			}

			for (id in callbacks.langInitDone) {
				callbacks.langInitDone[id]();
			}
		}).fail(function(){
			console.error("langlist not loaded");
		});
	}

	this.on = function(type, cb){
		if (type === "scriptsLoaded") {
			callbacks.scriptsLoaded.push(cb);
		}
		else if (type === "scriptLoaded") {
			callbacks.scriptLoaded.push(cb);
		}
		else if (type === "modulesLoaded") {
			callbacks.modulesLoaded.push(cb);
		}
		else if (type === "moduleLoaded") {
			callbacks.moduleLoaded.push(cb);
		}
		else if (type === "langInitDone") {
			callbacks.langInitDone.push(cb);
		}
		else if (type === "langChanged") {
			callbacks.langChanged.push(cb);
		}
	}

	this.trigger = function(type){
		if (type === "scriptsLoaded") {
			for (id in callbacks.langInitDone) {
				callbacks.scriptsLoaded[id]();
			}
		}
		else if (type === "scriptLoaded") {
			for (id in callbacks.scriptLoaded) {
				callbacks.scriptLoaded[id]();
			}
		}
		else if (type === "modulesLoaded") {
			for (id in callbacks.modulesLoaded) {
				callbacks.modulesLoaded[id]();
			}
		}
		else if (type === "moduleLoaded") {
			for (id in callbacks.moduleLoaded) {
				callbacks.moduleLoaded[id]();
			}
		}
		else if (type === "langInitDone") {
			for (id in callbacks.langInitDone) {
				callbacks.langInitDone[id]();
			}
		}
		else if (type === "langChanged") {
			for (id in callbacks.langChanged) {
				callbacks.langChanged[id]();
			}
		}
	}

	this.getLangData = function() {
		if ((!langStore[instance.selectedLang.langId]) ||
			(!langStore[instance.selectedLang.langId][instance.selectedLang.langIdSub])) {
			return {};
		}

		return langStore[instance.selectedLang.langId][instance.selectedLang.langIdSub];
	}

	this.getLangList = function() {
		return langList;
	}

	this.setLang = function(langIdOrObject, langIdSub) {

		if (!langIdOrObject) {
			return;
		}

		if ((langIdOrObject) && (langIdSub) &&
			(typeof langIdOrObject === 'string') &&
			(typeof langIdSub === 'string')) {
			instance.selectedLang.langId = langIdOrObject;
			instance.selectedLang.langIdSub = langIdSub;
		}
		else if ((langIdOrObject) &&
				 (langIdOrObject.langId) &&
				 (langIdOrObject.langIdSub)) {
			instance.selectedLang.langId = langIdOrObject.langId;
			instance.selectedLang.langIdSub = langIdOrObject.langIdSub;
		}

		for (id in callbacks.langChanged) {
			callbacks.langChanged[id].apply(this, [...arguments, langStore[instance.selectedLang.langId][instance.selectedLang.langIdSub]]);
		}

	}

	//Loads scripts. Takes an Array of scripts
	this.loadScripts = function(scripts, cb){
		var cnt = scripts.length;
		var loaded = 0;
		for(var i in scripts){
			this.loadScript(scripts[i], function(){
				loaded++;
				if(loaded >= cnt){
					if (typeof cb === "function") {
						cb.apply(this, arguments);
					}
					else {
						for (id in callbacks.scriptsLoaded) {
							callbacks.scriptsLoaded[id].apply(this, arguments);
						}
					}
				}
			});
		}
	}

	//Loads a script
	this.loadScript = function(script, cb){
		$.getScript(script).done(function(){
			if (typeof cb === "function") {
				cb.apply(this, arguments);
			}
			else {
				for (id in callbacks.scriptLoaded) {
					callbacks.scriptLoaded[id].apply(this, arguments);
				}
			}
		});
	}

	this.loadModules = function(modules, cb){
		var cnt = modules.length;
		var loaded = 0;
		for(var i in modules){
			this.loadModule(modules[i], function(){
				++loaded;
				if(loaded >= cnt){
					if (typeof cb === "function") {
						cb.apply(this, arguments);
					}
					else {
						for (id in callbacks.modulesLoaded) {
							callbacks.modulesLoaded[id].apply(this, arguments);
						}
					}
				}
			});
		}
	}

	this.loadModule = function(file, cb){
		if(_isZipped(file)){
			_unzip(file);
			file = _removeExtension(file) + "/package.json";
		}

		_getData(file, function(data){

			// has langfolder
			if (data.langfolder) {
				// loaded files
				var langLoadedCount = 0;

				// lang file list
				const langList = instance.findLangFiles(path.join(path.dirname(file), data.langfolder));

				for (var i in langList) {
					$.getJSON(langList[i]).done(function(json) {

						if ((langStore[json.langId]) &&
							(langStore[json.langId][json.langIdSub]) &&
							(langStore[json.langId][json.langIdSub].content)) {
							langStore[json.langId][json.langIdSub].content = Object.assign(langStore[json.langId][json.langIdSub].content, json.content);
						}

						++langLoadedCount;
						if (langLoadedCount >= langList.length) {
							instance.loadScript(path.join(path.dirname(file), data.main), function(){
								if (typeof cb === "function") {
									cb.apply(this, arguments);
								}
								else {
									for (id in callbacks.moduleLoaded) {
										callbacks.moduleLoaded[id].apply(this, arguments);
									}
								}
							});
						}
					}).fail(function(){
						++langLoadedCount;
						if (langLoadedCount >= langList.length) {
							instance.loadScript(path.join(path.dirname(file), data.main), function(){
								if (typeof cb === "function") {
									cb.apply(this, arguments);
								}
								else {
									for (id in callbacks.moduleLoaded) {
										callbacks.moduleLoaded[id].apply(this, arguments);
									}
								}
							});
						}
					})
				}
			}
			else {
				instance.loadScript(path.join(path.dirname(file), data.main), function(){
					if (typeof cb === "function") {
						cb.apply(this, arguments);
					}
					else {
						for (id in callbacks.moduleLoaded) {
							callbacks.moduleLoaded[id].apply(this, arguments);
						}
					}
				});
			}
		})
	}

	function _findFiles(directories){
		var files = [];

		if (typeof directories === 'string' || directories instanceof String) {
			try {
				files = fs.readdirSync(directories);
				for(var i in files){
					files[i] = directories + "/" + files[i];
				}
			} catch(err) {
				// no module folder - ignore files
			}
		} else {
			for(var i in directories){
				try {
					var _tempFiles = fs.readdirSync(directories[i]);
					for(var j in _tempFiles){
						_tempFiles[j] = directories[i] + "/" + _tempFiles[j];
					}
					files = files.concat(_tempFiles);
				} catch(err) {
					// no module folder - ignore files
				}
			}
		}

		return files;
	}

	this.findLangFiles = function(directories){
		var result = [];
		var files = _findFiles(directories);

		for(var i in files){
			var file = fs.realpathSync(files[i]);
			if(file.endsWith(".json")){
				result.push(file);
			}
		}
		return result;
	}

	this.findModules = function(directories){
		var result = [];
		var files = _findFiles(directories);

		for(var i in files){
			var file = fs.realpathSync(files[i]);
			if(file.endsWith(MODULE_EXTENSION)){
				result.push(file);
			} else if (fs.lstatSync(file).isDirectory() && fs.existsSync(file + "/package.json")){
				result.push(file + "/package.json");
			}
		}
		return result;
	}

	function _isZipped(path){
		return path.endsWith(MODULE_EXTENSION);
	}

	function _unzip(path){
		fs.createReadStream(path).pipe(unzip.Extract({ path: _removeExtension(path) }));
	}

	function _removeExtension(path){
		return path.substr(0, path.length - MODULE_EXTENSION.length);
	}

	function _getData(path, cb){
		$.ajax({
			url: path,
			success: cb,
			dataType: "json",
			context: this
		});
	}

	initialize(initLang, langFile);
}<commit_msg>fix load local modules: packed app<commit_after>function Module(initLang, langFile){
	var MODULE_EXTENSION = ".ccsvm";
	var baseDirectory;
	var instance = this;

	var callbacks = {
		scriptsLoaded: [],
		scriptLoaded: [],

		modulesLoaded: [],
		moduleLoaded: [],

		langInitDone: [],
		langChanged: []
	}

	var langStore = {};
	var langList = [];

	this.sharedMemory = {};
	this.selectedLang = {langId: "en", langIdSub: "us"};

	//Constructor
	function initialize(initLang, langFile){
		if ((initLang) &&
			(initLang.langId) &&
			(initLang.langIdSub)) {
			instance.selectedLang.langId = initLang.langId;
			instance.selectedLang.langIdSub = initLang.langIdSub;
		}

		$.getJSON(langFile).done(function(json) {
			langStore = json;

			for (id in langStore) {
				for (subid in langStore[id]) {
					langStore[id][subid].content = {};
					langList.push({
						langName: langStore[id][subid].langName,
						langNameSub: langStore[id][subid].langNameSub,
						langId: id,
						langIdSub: subid
					});
				}
			}

			for (id in callbacks.langInitDone) {
				callbacks.langInitDone[id]();
			}
		}).fail(function(){
			console.error("langlist not loaded");
		});
	}

	this.on = function(type, cb){
		if (type === "scriptsLoaded") {
			callbacks.scriptsLoaded.push(cb);
		}
		else if (type === "scriptLoaded") {
			callbacks.scriptLoaded.push(cb);
		}
		else if (type === "modulesLoaded") {
			callbacks.modulesLoaded.push(cb);
		}
		else if (type === "moduleLoaded") {
			callbacks.moduleLoaded.push(cb);
		}
		else if (type === "langInitDone") {
			callbacks.langInitDone.push(cb);
		}
		else if (type === "langChanged") {
			callbacks.langChanged.push(cb);
		}
	}

	this.trigger = function(type){
		if (type === "scriptsLoaded") {
			for (id in callbacks.langInitDone) {
				callbacks.scriptsLoaded[id]();
			}
		}
		else if (type === "scriptLoaded") {
			for (id in callbacks.scriptLoaded) {
				callbacks.scriptLoaded[id]();
			}
		}
		else if (type === "modulesLoaded") {
			for (id in callbacks.modulesLoaded) {
				callbacks.modulesLoaded[id]();
			}
		}
		else if (type === "moduleLoaded") {
			for (id in callbacks.moduleLoaded) {
				callbacks.moduleLoaded[id]();
			}
		}
		else if (type === "langInitDone") {
			for (id in callbacks.langInitDone) {
				callbacks.langInitDone[id]();
			}
		}
		else if (type === "langChanged") {
			for (id in callbacks.langChanged) {
				callbacks.langChanged[id]();
			}
		}
	}

	this.getLangData = function() {
		if ((!langStore[instance.selectedLang.langId]) ||
			(!langStore[instance.selectedLang.langId][instance.selectedLang.langIdSub])) {
			return {};
		}

		return langStore[instance.selectedLang.langId][instance.selectedLang.langIdSub];
	}

	this.getLangList = function() {
		return langList;
	}

	this.setLang = function(langIdOrObject, langIdSub) {

		if (!langIdOrObject) {
			return;
		}

		if ((langIdOrObject) && (langIdSub) &&
			(typeof langIdOrObject === 'string') &&
			(typeof langIdSub === 'string')) {
			instance.selectedLang.langId = langIdOrObject;
			instance.selectedLang.langIdSub = langIdSub;
		}
		else if ((langIdOrObject) &&
				 (langIdOrObject.langId) &&
				 (langIdOrObject.langIdSub)) {
			instance.selectedLang.langId = langIdOrObject.langId;
			instance.selectedLang.langIdSub = langIdOrObject.langIdSub;
		}

		for (id in callbacks.langChanged) {
			callbacks.langChanged[id].apply(this, [...arguments, langStore[instance.selectedLang.langId][instance.selectedLang.langIdSub]]);
		}

	}

	//Loads scripts. Takes an Array of scripts
	this.loadScripts = function(scripts, cb){
		var cnt = scripts.length;
		var loaded = 0;
		for(var i in scripts){
			this.loadScript(scripts[i], function(){
				loaded++;
				if(loaded >= cnt){
					if (typeof cb === "function") {
						cb.apply(this, arguments);
					}
					else {
						for (id in callbacks.scriptsLoaded) {
							callbacks.scriptsLoaded[id].apply(this, arguments);
						}
					}
				}
			});
		}
	}

	//Loads a script
	this.loadScript = function(script, cb){
		$.getScript(script).done(function(){
			if (typeof cb === "function") {
				cb.apply(this, arguments);
			}
			else {
				for (id in callbacks.scriptLoaded) {
					callbacks.scriptLoaded[id].apply(this, arguments);
				}
			}
		});
	}

	this.loadModules = function(modules, cb){
		var cnt = modules.length;
		var loaded = 0;
		for(var i in modules){
			this.loadModule(modules[i], function(){
				++loaded;
				if(loaded >= cnt){
					if (typeof cb === "function") {
						cb.apply(this, arguments);
					}
					else {
						for (id in callbacks.modulesLoaded) {
							callbacks.modulesLoaded[id].apply(this, arguments);
						}
					}
				}
			});
		}
	}

	this.loadModule = function(file, cb){
		if(_isZipped(file)){
			_unzip(file);
			file = _removeExtension(file) + "/package.json";
		}

		_getData(file, function(data){

			// has langfolder
			if (data.langfolder) {
				// loaded files
				var langLoadedCount = 0;

				// lang file list
				const langList = instance.findLangFiles(path.join(path.dirname(file), data.langfolder));

				for (var i in langList) {
					$.getJSON(langList[i]).done(function(json) {

						if ((langStore[json.langId]) &&
							(langStore[json.langId][json.langIdSub]) &&
							(langStore[json.langId][json.langIdSub].content)) {
							langStore[json.langId][json.langIdSub].content = Object.assign(langStore[json.langId][json.langIdSub].content, json.content);
						}

						++langLoadedCount;
						if (langLoadedCount >= langList.length) {
							instance.loadScript(path.join(path.dirname(file), data.main), function(){
								if (typeof cb === "function") {
									cb.apply(this, arguments);
								}
								else {
									for (id in callbacks.moduleLoaded) {
										callbacks.moduleLoaded[id].apply(this, arguments);
									}
								}
							});
						}
					}).fail(function(){
						++langLoadedCount;
						if (langLoadedCount >= langList.length) {
							instance.loadScript(path.join(path.dirname(file), data.main), function(){
								if (typeof cb === "function") {
									cb.apply(this, arguments);
								}
								else {
									for (id in callbacks.moduleLoaded) {
										callbacks.moduleLoaded[id].apply(this, arguments);
									}
								}
							});
						}
					})
				}
			}
			else {
				instance.loadScript(path.join(path.dirname(file), data.main), function(){
					if (typeof cb === "function") {
						cb.apply(this, arguments);
					}
					else {
						for (id in callbacks.moduleLoaded) {
							callbacks.moduleLoaded[id].apply(this, arguments);
						}
					}
				});
			}
		})
	}

	function _findFiles(directories){
		var files = [];

		if (typeof directories === 'string' || directories instanceof String) {
			directories = directories.replace(/^file:/, '');
			try {
				files = fs.readdirSync(directories);
				for(var i in files){
					files[i] = directories + "/" + files[i];
				}
			} catch(err) {
				// no module folder - ignore files
			}
		} else {
			for(var i in directories){
				directories[i] = directories[i].replace(/^file:/, '');
				try {
					var _tempFiles = fs.readdirSync(directories[i]);
					for(var j in _tempFiles){
						_tempFiles[j] = directories[i] + "/" + _tempFiles[j];
					}
					files = files.concat(_tempFiles);
				} catch(err) {
					// no module folder - ignore files
				}
			}
		}

		return files;
	}

	this.findLangFiles = function(directories){
		var result = [];
		var files = _findFiles(directories);

		for(var i in files){
			var file = fs.realpathSync(files[i]);
			if(file.endsWith(".json")){
				result.push(file);
			}
		}
		return result;
	}

	this.findModules = function(directories){
		var result = [];
		var files = _findFiles(directories);

		for(var i in files){
			var file = fs.realpathSync(files[i]);
			if(file.endsWith(MODULE_EXTENSION)){
				result.push(file);
			} else if (fs.lstatSync(file).isDirectory() && fs.existsSync(file + "/package.json")){
				result.push(file + "/package.json");
			}
		}
		return result;
	}

	function _isZipped(path){
		return path.endsWith(MODULE_EXTENSION);
	}

	function _unzip(path){
		fs.createReadStream(path).pipe(unzip.Extract({ path: _removeExtension(path) }));
	}

	function _removeExtension(path){
		return path.substr(0, path.length - MODULE_EXTENSION.length);
	}

	function _getData(path, cb){
		$.ajax({
			url: path,
			success: cb,
			dataType: "json",
			context: this
		});
	}

	initialize(initLang, langFile);
}<|endoftext|>


<commit_before>'use strict';

    var callButton = document.getElementById('callButton');
    //var hangupButton = document.getElementById('hangupButton');
    callButton.disabled = false;
    //hangupButton.disabled = true;
    callButton.onclick = call;
    //hangupButton.onclick = hangup;

    var startTime;
    var localVideo = document.getElementById('localVideo');
    var remoteVideo = document.getElementById('remoteVideo');

    var localStream;
    var connection;

    var sdpConstraints = {
        'mandatory': {
            'OfferToReceiveAudio': false,
            'OfferToReceiveVideo': true
        }
    };

    localVideo.addEventListener('loadedmetadata', function () {
        trace('Local video currentSrc: ' + this.currentSrc +
          ', videoWidth: ' + this.videoWidth +
          'px,  videoHeight: ' + this.videoHeight + 'px');
    });

    remoteVideo.addEventListener('loadedmetadata', function () {
        trace('Remote video currentSrc: ' + this.currentSrc +
          ', videoWidth: ' + this.videoWidth +
          'px,  videoHeight: ' + this.videoHeight + 'px');
    });

    remoteVideo.onresize = function () {
        trace('Remote video size changed to ' +
          remoteVideo.videoWidth + 'x' + remoteVideo.videoHeight);
        // We'll use the first onsize callback as an indication that video has started playing out.
        if (startTime) {
            var elapsedTime = window.performance.now() - startTime;
            trace('Setup time: ' + elapsedTime.toFixed(3) + 'ms');
            startTime = null;
        }
    };

function start() {
  // Call into getUserMedia via the polyfill (adapter.js).
  getUserMedia({
      audio: true,
      video: true
    }, gotStream,
    function(e) {
        //alert('getUserMedia() error: ' + e.name);
        alert('Sorry, you web cam is absent or unavailable');
        callButton.disabled = true;
    });
  //var servers = null;
  //var servers = { 'iceServers': [{ 'url': 'stun:stun.l.google.com:19302' }] };
  var servers = { 'iceServers': [{ 'url': 'stun:74.125.142.127:19302' }] };
  //var  _iceServers = [{ url: 'stun:74.125.142.127:19302' }], // stun.l.google.com - Firefox does not support DNS names.
  connection = new RTCPeerConnection(servers);
  //connection = new RTCPeerConnection({ iceServers: _iceServers });
  trace('Created connection object');
  connection.onicecandidate = function (e) {
      chat.server.iceCandidate(JSON.stringify({ "candidate": e.candidate }));
  };
  connection.onaddstream = function (e) {
      // Call the polyfill wrapper to attach the media stream to this element.
      callButton.disabled = true;
      remoteVideo.hidden = false;
      attachMediaStream(remoteVideo, e.stream);
      trace('received remote stream');
  };
}

function call() {
  callButton.disabled = true;
  //hangupButton.disabled = false;
  trace('Starting call');
  startTime = window.performance.now();
  var videoTracks = localStream.getVideoTracks();
  var audioTracks = localStream.getAudioTracks();
  if (videoTracks.length > 0) {
    trace('Using video device: ' + videoTracks[0].label);
  }
  if (audioTracks.length > 0) {
    trace('Using audio device: ' + audioTracks[0].label);
  }

  if (localStream != null) {
      connection.addStream(localStream);
      trace('Added local stream to connection');
  }

  trace('connection createOffer start');
  connection.createOffer(onCreateOfferSuccess, onCreateSessionDescriptionError);
}

function answer(message) {
    trace('send answer ' + message.sdp);
    connection.setRemoteDescription(new RTCSessionDescription(message.sdp), function () {
        trace('setRemoteDescription');
        if (localStream != null) {
            connection.addStream(localStream);
        }
            connection.createAnswer(function (desc) {
                connection.setLocalDescription(desc, function () {
                    chat.server.answer(JSON.stringify({ "sdp": desc}));
                });
            });
    });
}

function addIceCandidate(message) {
    if (message.candidate != null) {
        trace('add ice candidate');
        connection.addIceCandidate(new RTCIceCandidate(message.candidate));
    }
}

function getAnswer(message) {
    if (message.sdp != null) {
        trace('get answer');
        connection.setRemoteDescription(new RTCSessionDescription(message.sdp));
    }
}

function gotStream(stream) {
    trace('Received local stream');
    // Call the polyfill wrapper to attach the media stream to this element.
    attachMediaStream(localVideo, stream);
    localStream = stream;
    callButton.disabled = false;
}

function onCreateSessionDescriptionError(error) {
  trace('Failed to create session description: ' + error.toString());
}

function onCreateOfferSuccess(desc) {
    trace('Offer created');
  trace('setLocalDescription start');
  connection.setLocalDescription(desc, function () {
    chat.server.offer(JSON.stringify({ "sdp": desc }));
    onSetLocalSuccess(connection);
  });
}

function onSetLocalSuccess(connection) {
  trace(' setLocalDescription complete');
}

function onSetRemoteSuccess(connection) {
  trace(' setRemoteDescription complete');
}

function onCreateAnswerSuccess(desc) {
    trace('Answer from pc2:\n' + desc.sdp);
    trace('pc1 setRemoteDescription start');
  trace('pc2 setLocalDescription start');
  connection.setLocalDescription(desc, function () {
      onSetLocalSuccess(connection);
  });
}

function onAddIceCandidateSuccess(connection) {
  trace(' addIceCandidate success');
}

function onAddIceCandidateError(connection, error) {
  trace(' failed to add ICE Candidate: ' + error.toString());
}

//function hangup() {
//  trace('Ending call');
//  connection.close();
//  connection = null;
//  hangupButton.disabled = true;
//  callButton.disabled = false;
//}
<commit_msg>video local and remote<commit_after>'use strict';

    var callButton = document.getElementById('callButton');
    //var hangupButton = document.getElementById('hangupButton');
    callButton.disabled = false;
    //hangupButton.disabled = true;
    callButton.onclick = call;
    //hangupButton.onclick = hangup;

    var startTime;
    var localVideo = document.getElementById('localVideo');
    var remoteVideo = document.getElementById('remoteVideo');

    var localStream;
    var connection;

    var sdpConstraints = {
        'mandatory': {
            'OfferToReceiveAudio': false,
            'OfferToReceiveVideo': true
        }
    };

    localVideo.addEventListener('loadedmetadata', function () {
        trace('Local video currentSrc: ' + this.currentSrc +
          ', videoWidth: ' + this.videoWidth +
          'px,  videoHeight: ' + this.videoHeight + 'px');
    });

    remoteVideo.addEventListener('loadedmetadata', function () {
        trace('Remote video currentSrc: ' + this.currentSrc +
          ', videoWidth: ' + this.videoWidth +
          'px,  videoHeight: ' + this.videoHeight + 'px');
    });

    remoteVideo.onresize = function () {
        trace('Remote video size changed to ' +
          remoteVideo.videoWidth + 'x' + remoteVideo.videoHeight);
        // We'll use the first onsize callback as an indication that video has started playing out.
        if (startTime) {
            var elapsedTime = window.performance.now() - startTime;
            trace('Setup time: ' + elapsedTime.toFixed(3) + 'ms');
            startTime = null;
        }
    };

    function start() {
        remoteVideo.hidden = true;
  // Call into getUserMedia via the polyfill (adapter.js).
  getUserMedia({
      audio: true,
      video: true
    }, gotStream,
    function(e) {
        //alert('getUserMedia() error: ' + e.name);
        alert('Sorry, you web cam is absent or unavailable');
        callButton.disabled = true;
    });
  //var servers = null;
  //var servers = { 'iceServers': [{ 'url': 'stun:stun.l.google.com:19302' }] };
  var servers = { 'iceServers': [{ 'url': 'stun:74.125.142.127:19302' }] };
  //var  _iceServers = [{ url: 'stun:74.125.142.127:19302' }], // stun.l.google.com - Firefox does not support DNS names.
  connection = new RTCPeerConnection(servers);
  //connection = new RTCPeerConnection({ iceServers: _iceServers });
  trace('Created connection object');
  connection.onicecandidate = function (e) {
      chat.server.iceCandidate(JSON.stringify({ "candidate": e.candidate }));
  };
  connection.onaddstream = function (e) {
      // Call the polyfill wrapper to attach the media stream to this element.
      callButton.disabled = true;
      //remoteVideo.hidden = false;
      attachMediaStream(remoteVideo, e.stream);
      trace('received remote stream');
  };
}

function call() {
    callButton.disabled = true;
    remoteVideo.hidden = false;
  //hangupButton.disabled = false;
  trace('Starting call');
  startTime = window.performance.now();
  var videoTracks = localStream.getVideoTracks();
  var audioTracks = localStream.getAudioTracks();
  if (videoTracks.length > 0) {
    trace('Using video device: ' + videoTracks[0].label);
  }
  if (audioTracks.length > 0) {
    trace('Using audio device: ' + audioTracks[0].label);
  }

  if (localStream != null) {
      connection.addStream(localStream);
      trace('Added local stream to connection');
  }

  trace('connection createOffer start');
  connection.createOffer(onCreateOfferSuccess, onCreateSessionDescriptionError);
}

function answer(message) {
    remoteVideo.hidden = false;
    trace('send answer ' + message.sdp);
    connection.setRemoteDescription(new RTCSessionDescription(message.sdp), function () {
        trace('setRemoteDescription');
        if (localStream != null) {
            connection.addStream(localStream);
        }
            connection.createAnswer(function (desc) {
                connection.setLocalDescription(desc, function () {
                    chat.server.answer(JSON.stringify({ "sdp": desc}));
                });
            });
    });
}

function addIceCandidate(message) {
    if (message.candidate != null) {
        trace('add ice candidate');
        connection.addIceCandidate(new RTCIceCandidate(message.candidate));
    }
}

function getAnswer(message) {
    if (message.sdp != null) {
        trace('get answer');
        connection.setRemoteDescription(new RTCSessionDescription(message.sdp));
    }
}

function gotStream(stream) {
    trace('Received local stream');
    // Call the polyfill wrapper to attach the media stream to this element.
    attachMediaStream(localVideo, stream);
    localStream = stream;
    callButton.disabled = false;
}

function onCreateSessionDescriptionError(error) {
  trace('Failed to create session description: ' + error.toString());
}

function onCreateOfferSuccess(desc) {
    trace('Offer created');
  trace('setLocalDescription start');
  connection.setLocalDescription(desc, function () {
    chat.server.offer(JSON.stringify({ "sdp": desc }));
    onSetLocalSuccess(connection);
  });
}

function onSetLocalSuccess(connection) {
  trace(' setLocalDescription complete');
}

function onSetRemoteSuccess(connection) {
  trace(' setRemoteDescription complete');
}

function onCreateAnswerSuccess(desc) {
    trace('Answer from pc2:\n' + desc.sdp);
    trace('pc1 setRemoteDescription start');
  trace('pc2 setLocalDescription start');
  connection.setLocalDescription(desc, function () {
      onSetLocalSuccess(connection);
  });
}

function onAddIceCandidateSuccess(connection) {
  trace(' addIceCandidate success');
}

function onAddIceCandidateError(connection, error) {
  trace(' failed to add ICE Candidate: ' + error.toString());
}

//function hangup() {
//  trace('Ending call');
//  connection.close();
//  connection = null;
//  hangupButton.disabled = true;
//  callButton.disabled = false;
//}
<|endoftext|>


<commit_before>/*
    version: 2.0
    Last modified date: 2016/11/15
    Author: Cao Shuyang(Lawrence)
    Description: The module is implemented for fuzzy search, advanced search and autocomplete.
                 This module assumes that firebase SDK has been referred  in a webpage.
                 This module assumes that jQuery has been referred in a webpage.
*/
//
// Extend array method, unique an array, which must be sorted before this function is invoked
//
Array.prototype.uniqueArray = function()
{
	var new_arr = [];
	var precedent = null;
	for(let val of this)
	{
		if(val == precedent)
			continue;
		precedent = val;
		new_arr.push(val);
	}

	return new_arr;
}

//
// Extend array method, unique an array and count repeating number of every unique element in the array
// The array must be sorted first
//
Array.prototype.reduceAndCount = function()
{
	var uniqueObj = {};
	for(let ele of this)
	{
		let str = ele.toString();
		if(uniqueObj.hasOwnProperty(str))
			uniqueObj[str].count += 1;
		else
			uniqueObj[str] = {"Element": ele, "count":1};
	}
	return Object.keys(uniqueObj).map(function(value) {return uniqueObj[value];})
}

//
// Declare the namespace for this module
//
function Search()
{
    this.configure();
    this.initializeFirebase();
}

//
// Initialize some firebase properties of Search
//
Search.prototype.initializeFirebase = function()
{
    this.auth = firebase.auth();
    this.database = firebase.database();
    this.storage = firebase.storage();
}

//
// Configure Search arguments
//
Search.prototype.configure = function()
{
    this.diffTolerance = 3;
    this.filterWordList = {'a':null, 'and':null};
}

//
// Calculate the Edit Distance between two strings
// Reference: https://en.wikipedia.org/wiki/Edit_distance
// Instead of using a 2D matrix, this implemention use a 1D array to iterate a.length+1 times
// This will save space, hence, save the space allocation time.
//
Search.prototype.editDistance = function(a, b)
{
    if(a.length == 0) return b.length;
    if(b.length == 0) return a.length;

    var iter_len = b.length + 1;
    var iter_arr = new Array(iter_len);
    var iter_arr_tmp = new Array(iter_len);
    for(let i=0; i<iter_arr.length; ++i)    //initialize the iter_arr
        iter_arr[i] = i;

    var tmp = null;
    for(let i =1; i<=a.length; ++i)
    {
        iter_arr_tmp[0] = i;
        for(let j=1; j<=b.length; ++j)
        {
            if(a.charAt(i-1) == b.charAt(j-1))
                iter_arr_tmp[j] = iter_arr[j-1];
            else {
                iter_arr_tmp[j] = Math.min(iter_arr[j-1]+1, Math.min(iter_arr_tmp[j-1]+1, iter_arr[j]+1));
            }
        }
        tmp = iter_arr_tmp;
        iter_arr_tmp = iter_arr;
        iter_arr = tmp;
    }
    return iter_arr[b.length];
}

//
// Extrate words from a text, return an array
// The parameter is assumed to be a string, the elements in the returned array are all in lowercase
//
Search.prototype.extractWord = function(paragraph)
{
    var that = this;

	return paragraph.split(/[^\w+|C\+\+]/).map(function(value){
        return value.trim().toLowerCase();
    }).filter(function(val) {
        return Boolean(val) && (val.length>1) && !(that.filterWordList.hasOwnProperty(val));
    }).sort().uniqueArray()
}

//
// Fuzzy Search
//
Search.prototype.fuzzySearch = function(searchSentence)
{
    // break search sentence into words
    var that = this;
    var words = this.extractWord(searchSentence);

    var queryList = this.database.ref('Index').orderByKey();
    queryList.once("value").then(function(snapshot) {
        var matchRefs = [];
        snapshot.forEach(function(childSnapshot) {
            var dist = that.matchSearchWords(childSnapshot.key, words);
            if(dist < that.diffTolerance)
            {
                var obj = childSnapshot.val();
                for(let item in obj)
                    matchRefs.push(obj[item]);
            }
        });
		var LoadingData = matchRefs.reduceAndCount().sort(function(a, b) {return b.count - a.count}).map(function(value) {
            return that.database.ref(value.Element).once('value').then(function(dataRef){return dataRef.val();});
        });
		Promise.all(LoadingData).then(function(data_arr) {
            console.log("render data");
            console.log(data_arr);
            that.renderSearchResult(data_arr);
        });
    });
}

//
// A helper function for fuzzySearch to determine whether JSON key matched with search words.
// This function return the minimum distance between key and all of search words
//
Search.prototype.matchSearchWords = function(key, words)
{
    var minDistance = 100000;   // Initialize the distance to a larger enough number
    for(let word of words)
        minDistance = Math.min(minDistance, this.editDistance(key, word));

    return minDistance;
}

//
// A helper function for fuzzySearch to reduce a map from word-array to a result list
//
Search.prototype.reduceList = function(matchRefs)
{
    var resultObj = {};
    for(let path of matchRefs)
    {
        if(resultObj.hasOwnProperty(path))
        {
            resultObj[path].count += 1;
        }else {
            resultObj[path] = {"Path": path, "count": 1};
        }
    }
    var resultList = Object.keys(resultObj).map(function(value) {return resultObj[value];});
    return resultList;
}

//
// Render the search results in search Result area
//
Search.prototype.renderSearchResult = function(resultArr)
{
    $(".recommendationView").hide();
    $(".searchResultView").show();
    $(".searchResultView").empty();
    var parent = $(".searchResultView");

    for(let result of resultArr)
    {
        let renderResult = null;
        switch(result.Type)
        {
            case "Team": renderResult = this.renderTeamElement(result); break;
            case "Event": renderResult = this.renderEventElement(result); break;
            case "User": renderResult = this.renderUserElement(result); break;
            default: renderResult = null;
        }
        if(renderResult)
            parent.append(renderResult);
    }
}

//
// Render team elements
//
Search.prototype.renderTeamElement = function(teamObj)
{
    var divEle = $("

").addClass("col-sm-offset-1 col-xs-11");
    divEle.append($("

").html(teamObj.EventName + "/" + teamObj.Name));
    divEle.append($("

").text(teamObj.Introduction).append($("
")));
    divEle.append($("

").html('Event Time:Current Team Size:  ' + teamObj.Members.length +''));

    var teamElement = divEle.wrap("

").parent().addClass("row pendingTeam").wrap("").parent().attr("href", "teaminfo.html").wrap("

").parent().addClass("teams");
    return teamElement;
}

//
// Render event elements
//
Search.prototype.renderEventElement = function(eventObj)
{
    var divEle = $("

").addClass("col-sm-offset-1 col-xs-11");
    divEle.append($("

").html(eventObj.Name));
    divEle.append($("

").text(eventObj.Introduction).append($("
")));
    var eventTime = new Date(eventObj.Time);
    divEle.append($("

").html('Event Time: ' + eventTime.toLocaleDateString() + ' ' + eventTime.toLocaleTimeString() ));

    var eventElement = divEle.wrap("

").parent().addClass("row openEvent").wrap("").parent().attr("href", "eventinfo.html").wrap("

").parent().addClass("events");
    return eventElement;
}

//
// Render user elements
//
Search.prototype.renderUserElement = function(userObj)
{
    var divEle = $("

").addClass("col-sm-offset-1 col-xs-11");
    divEle.append($("

").html(userObj.Name));
    divEle.append($("

").text(userObj.Introduction).append($("
")));
    divEle.append($("

").html('Skills: ' + userObj.Skills.join(', ') + ''));

    var userElement = divEle.wrap("

").parent().addClass("row pendingTeam").wrap("").parent().attr("href", "#").wrap("

").parent().addClass("teams");
    return userElement;
}

Search.prototype.extracDatabasePath = function(Ref)
{
	return Ref.toString().substring(Ref.root.toString().length);
}

//
// Automatically index new user
//
Search.prototype.indexNewUser = function(newUser, userRef)
{
	var storePath = this.extracDatabasePath(userRef);

    var content_arr = [];
    content_arr.push(newUser.Name);
    content_arr.push(newUser.Languages.join(','));
    content_arr.push(newUser.Education);
    content_arr.push(newUser.Skills.join(','));
    content_arr.push(newUser.Country);
    content_arr.push(newUser.City);
    content_arr.push(newUser.Introduction);

    var word_arr = this.extractWord(content_arr.join(' , '));

    var indexRef = firebase.database().ref('Index');
    word_arr.forEach(function(value) {
        indexRef.child(value).push(storePath);
    });
}

Search.prototype.indexNewEvent = function(newEvent, eventRef)
{
	var storePath = this.extracDatabasePath(eventRef);

	var content_arr = [];
	content_arr.push(newEvent.Name);
	content_arr.push(newEvent.Location.Country);
	content_arr.push(newEvent.Location.City);
	content_arr.push(newEvent.Keywords.join(', '));
	content_arr.push(newEvent.Introduction);

	var word_arr = this.extractWord(content_arr.join(' , '));

	var indexRef = firebase.database().ref("Index");
	word_arr.forEach(function(value) {
		indexRef.child(value).push(storePath);
	})
}

// attach search function to #searchButton
var search = new Search();
$("#searchButton").click(function(){
	search.fuzzySearch($("#searchBox").val());
});<commit_msg>redefine match rule<commit_after>/*
    version: 2.0
    Last modified date: 2016/11/15
    Author: Cao Shuyang(Lawrence)
    Description: The module is implemented for fuzzy search, advanced search and autocomplete.
                 This module assumes that firebase SDK has been referred  in a webpage.
                 This module assumes that jQuery has been referred in a webpage.
*/
//
// Extend array method, unique an array, which must be sorted before this function is invoked
//
Array.prototype.uniqueArray = function()
{
	var new_arr = [];
	var precedent = null;
	for(let val of this)
	{
		if(val == precedent)
			continue;
		precedent = val;
		new_arr.push(val);
	}

	return new_arr;
}

//
// Extend array method, unique an array and count repeating number of every unique element in the array
// The array must be sorted first
//
Array.prototype.reduceAndCount = function()
{
	var uniqueObj = {};
	for(let ele of this)
	{
		let str = ele.toString();
		if(uniqueObj.hasOwnProperty(str))
			uniqueObj[str].count += 1;
		else
			uniqueObj[str] = {"Element": ele, "count":1};
	}
	return Object.keys(uniqueObj).map(function(value) {return uniqueObj[value];})
}

//
// Declare the namespace for this module
//
function Search()
{
    this.configure();
    this.initializeFirebase();
}

//
// Initialize some firebase properties of Search
//
Search.prototype.initializeFirebase = function()
{
    this.auth = firebase.auth();
    this.database = firebase.database();
    this.storage = firebase.storage();
}

//
// Configure Search arguments
//
Search.prototype.configure = function()
{
    this.diffTolerance = 3;
    this.filterWordList = {'a':null, 'and':null};
}

//
// Calculate the Edit Distance between two strings
// Reference: https://en.wikipedia.org/wiki/Edit_distance
// Instead of using a 2D matrix, this implemention use a 1D array to iterate a.length+1 times
// This will save space, hence, save the space allocation time.
//
Search.prototype.editDistance = function(a, b)
{
    if(a.length == 0) return b.length;
    if(b.length == 0) return a.length;

    var iter_len = b.length + 1;
    var iter_arr = new Array(iter_len);
    var iter_arr_tmp = new Array(iter_len);
    for(let i=0; i<iter_arr.length; ++i)    //initialize the iter_arr
        iter_arr[i] = i;

    var tmp = null;
    for(let i =1; i<=a.length; ++i)
    {
        iter_arr_tmp[0] = i;
        for(let j=1; j<=b.length; ++j)
        {
            if(a.charAt(i-1) == b.charAt(j-1))
                iter_arr_tmp[j] = iter_arr[j-1];
            else {
                iter_arr_tmp[j] = Math.min(iter_arr[j-1]+1, Math.min(iter_arr_tmp[j-1]+1, iter_arr[j]+1));
            }
        }
        tmp = iter_arr_tmp;
        iter_arr_tmp = iter_arr;
        iter_arr = tmp;
    }
    return iter_arr[b.length];
}

//
// Extrate words from a text, return an array
// The parameter is assumed to be a string, the elements in the returned array are all in lowercase
//
Search.prototype.extractWord = function(paragraph)
{
    var that = this;

	return paragraph.split(/[^\w+|C\+\+]/).map(function(value){
        return value.trim().toLowerCase();
    }).filter(function(val) {
        return Boolean(val) && (val.length>1) && !(that.filterWordList.hasOwnProperty(val));
    }).sort().uniqueArray()
}

//
// Fuzzy Search
//
Search.prototype.fuzzySearch = function(searchSentence)
{
    var that = this;

	firebase.database().ref('Index').orderByKey().once("value").then(function(snapshot) {
		// break search sentence into words
		var words = that.extractWord(searchSentence);

        var matchRefs = [];
        snapshot.forEach(function(childSnapshot) {
            if(that.matchSearchWords(childSnapshot.key, words))
            {
                var obj = childSnapshot.val();
                for(let item in obj)
                    matchRefs.push(obj[item]);
            }
        });
		var LoadingData = matchRefs.reduceAndCount().sort(function(a, b) {return b.count - a.count}).map(function(value) {
            return that.database.ref(value.Element).once('value').then(function(dataRef){return dataRef.val();});
        });
		Promise.all(LoadingData).then(function(data_arr) {
            console.log("render data");
            that.renderSearchResult(data_arr);
        });
    });
}

//
// A helper function for fuzzySearch to determine whether JSON key matched with search words.
// This function return the minimum distance between key and all of search words
//
Search.prototype.matchSearchWords = function(key, words)
{
    var minDistance = 100000;   // Initialize the distance to a larger enough number
	for(let word of words)
	{
		if(word.length>3 && key.includes(word))
			return true;
		minDistance = Math.min(minDistance, this.editDistance(key, word));
	}

    return minDistance < this.diffTolerance;
}

//
// A helper function for fuzzySearch to reduce a map from word-array to a result list
//
Search.prototype.reduceList = function(matchRefs)
{
    var resultObj = {};
    for(let path of matchRefs)
    {
        if(resultObj.hasOwnProperty(path))
        {
            resultObj[path].count += 1;
        }else {
            resultObj[path] = {"Path": path, "count": 1};
        }
    }
    var resultList = Object.keys(resultObj).map(function(value) {return resultObj[value];});
    return resultList;
}

//
// Render the search results in search Result area
//
Search.prototype.renderSearchResult = function(resultArr)
{
    $(".recommendationView").hide();
    $(".searchResultView").show();
    $(".searchResultView").empty();
    var parent = $(".searchResultView");

    for(let result of resultArr)
    {
        let renderResult = null;
        switch(result.Type)
        {
            case "Team": renderResult = this.renderTeamElement(result); break;
            case "Event": renderResult = this.renderEventElement(result); break;
            case "User": renderResult = this.renderUserElement(result); break;
            default: renderResult = null;
        }
        if(renderResult)
            parent.append(renderResult);
    }
}

//
// Render team elements
//
Search.prototype.renderTeamElement = function(teamObj)
{
    var divEle = $("

").addClass("col-sm-offset-1 col-xs-11");
    divEle.append($("

").html(teamObj.EventName + "/" + teamObj.Name));
    divEle.append($("

").text(teamObj.Introduction).append($("
")));
    divEle.append($("

").html('Event Time:Current Team Size:  ' + teamObj.Members.length +''));

    var teamElement = divEle.wrap("

").parent().addClass("row pendingTeam").wrap("").parent().attr("href", "teaminfo.html").wrap("

").parent().addClass("teams");
    return teamElement;
}

//
// Render event elements
//
Search.prototype.renderEventElement = function(eventObj)
{
    var divEle = $("

").addClass("col-sm-offset-1 col-xs-11");
    divEle.append($("

").html(eventObj.Name));
    divEle.append($("

").text(eventObj.Introduction).append($("
")));
    var eventTime = new Date(eventObj.Time);
    divEle.append($("

").html('Event Time: ' + eventTime.toLocaleDateString() + ' ' + eventTime.toLocaleTimeString() ));

    var eventElement = divEle.wrap("

").parent().addClass("row openEvent").wrap("").parent().attr("href", "eventinfo.html").wrap("

").parent().addClass("events");
    return eventElement;
}

//
// Render user elements
//
Search.prototype.renderUserElement = function(userObj)
{
    var divEle = $("

").addClass("col-sm-offset-1 col-xs-11");
    divEle.append($("

").html(userObj.Name));
    divEle.append($("

").text(userObj.Introduction).append($("
")));
    divEle.append($("

").html('Skills: ' + userObj.Skills.join(', ') + ''));

    var userElement = divEle.wrap("

").parent().addClass("row pendingTeam").wrap("").parent().attr("href", "#").wrap("

").parent().addClass("teams");
    return userElement;
}

Search.prototype.extracDatabasePath = function(Ref)
{
	return Ref.toString().substring(Ref.root.toString().length);
}

//
// Automatically index new user
//
Search.prototype.indexNewUser = function(newUser, userRef)
{
	var storePath = this.extracDatabasePath(userRef);

    var content_arr = [];
    content_arr.push(newUser.Name);
    content_arr.push(newUser.Languages.join(','));
    content_arr.push(newUser.Education);
    content_arr.push(newUser.Skills.join(','));
    content_arr.push(newUser.Country);
    content_arr.push(newUser.City);
    content_arr.push(newUser.Introduction);

    var word_arr = this.extractWord(content_arr.join(' , '));

    var indexRef = firebase.database().ref('Index');
    word_arr.forEach(function(value) {
        indexRef.child(value).push(storePath);
    });
}

Search.prototype.indexNewEvent = function(newEvent, eventRef)
{
	var storePath = this.extracDatabasePath(eventRef);

	var content_arr = [];
	content_arr.push(newEvent.Name);
	content_arr.push(newEvent.Location.Country);
	content_arr.push(newEvent.Location.City);
	content_arr.push(newEvent.Keywords.join(', '));
	content_arr.push(newEvent.Introduction);

	var word_arr = this.extractWord(content_arr.join(' , '));

	var indexRef = firebase.database().ref("Index");
	word_arr.forEach(function(value) {
		indexRef.child(value).push(storePath);
	})
}

// attach search function to #searchButton
var search = new Search();
$("#searchButton").click(function(){
	search.fuzzySearch($("#searchBox").val());
});<|endoftext|>


<commit_before>jQuery(document).ready(function() {
        //        $("#bw-upload-box").hide();

        $('#bw-upload-link').hover(function() {
                $("#bw-upload-box").slideDown('fast');
            },
            function() {}
            );
        $('#bw-shell-table').hover(function() {
                $("#bw-upload-box").slideUp('fast');
            },
            function() {} );

        $('.bw-what-is-openid-link').click(function() {
                $("#bw-what-is-openid").fadeIn('normal');
            }
            );
        $('#bw-search-lang-change').click(function() {
                $("#bw-search-lang-selection").fadeIn('normal');
            }
            );
        $('#bw-search-lang-hide').click(function() {
                $("#bw-search-lang-selection").fadeOut('normal');
            }
            );

        var main_text = $('#bw-book-content');
        var current_size = get_font_sizing(main_text);
        if (current_size != null) {
            var num = get_font_num(current_size);
            var unit = get_font_unit(current_size);
        }

        // Font-size changer
        $("a.bw-font-size-changer").click(function(){

            // Font threshold should be toggled here
            if (this.id == 'bw-increase-font'){
                num = num * 1.2;
            } else if (this.id == 'bw-decrease-font'){
                num = num / 1.2;
            }
            num = round_to(num, 5);
            update_font_size(main_text, num, unit);
            console.log(get_font_sizing(main_text));
            jQuery.post('/account/profile/change-font-size/' + num + unit + '/');
            return false;
        });

        // Font-family changer
        $("a.bw-font-family-changer").click(function(){
            var font = '';
            // Font threshold should be toggled here
            if (this.id == 'bw-serif-font'){
                font = 'serif';
            } else if (this.id == 'bw-sans-serif-font'){
                font = 'sans-serif';
            }
            update_font_family(main_text, font);
            jQuery.post('/account/profile/change-font-family/' + font + '/');
            return false;
        });

});
function round_to(n, sig) {
    var mult = Math.pow(10, sig - Math.floor(Math.log(n) / Math.LN10) - 1);
    return Math.round(n * mult) / mult;
}

function get_font_sizing(mainText) {
    return  mainText.css('font-size');
}
function get_font_num(current_size) {
    return parseFloat(current_size, 10);
}
function get_font_unit(current_size) {
    return current_size.slice(-2);
}
function update_font_size(el, num, unit) {
    el.css('font-size', num + unit);
    el.find('p').css('font-size', num + unit);
    el.find('div').css('font-size', num + unit);

}
function update_font_family(el, font) {
    el.css('font-family', font);
    el.find('p').css('font-family', font);
    el.find('div').css('font-family', font);
}<commit_msg>Remove console logger<commit_after>jQuery(document).ready(function() {
        //        $("#bw-upload-box").hide();

        $('#bw-upload-link').hover(function() {
                $("#bw-upload-box").slideDown('fast');
            },
            function() {}
            );
        $('#bw-shell-table').hover(function() {
                $("#bw-upload-box").slideUp('fast');
            },
            function() {} );

        $('.bw-what-is-openid-link').click(function() {
                $("#bw-what-is-openid").fadeIn('normal');
            }
            );
        $('#bw-search-lang-change').click(function() {
                $("#bw-search-lang-selection").fadeIn('normal');
            }
            );
        $('#bw-search-lang-hide').click(function() {
                $("#bw-search-lang-selection").fadeOut('normal');
            }
            );

        var main_text = $('#bw-book-content');
        var current_size = get_font_sizing(main_text);
        if (current_size != null) {
            var num = get_font_num(current_size);
            var unit = get_font_unit(current_size);
        }

        // Font-size changer
        $("a.bw-font-size-changer").click(function(){

            // Font threshold should be toggled here
            if (this.id == 'bw-increase-font'){
                num = num * 1.2;
            } else if (this.id == 'bw-decrease-font'){
                num = num / 1.2;
            }
            num = round_to(num, 5);
            update_font_size(main_text, num, unit);
            jQuery.post('/account/profile/change-font-size/' + num + unit + '/');
            return false;
        });

        // Font-family changer
        $("a.bw-font-family-changer").click(function(){
            var font = '';
            // Font threshold should be toggled here
            if (this.id == 'bw-serif-font'){
                font = 'serif';
            } else if (this.id == 'bw-sans-serif-font'){
                font = 'sans-serif';
            }
            update_font_family(main_text, font);
            jQuery.post('/account/profile/change-font-family/' + font + '/');
            return false;
        });

});
function round_to(n, sig) {
    var mult = Math.pow(10, sig - Math.floor(Math.log(n) / Math.LN10) - 1);
    return Math.round(n * mult) / mult;
}

function get_font_sizing(mainText) {
    return  mainText.css('font-size');
}
function get_font_num(current_size) {
    return parseFloat(current_size, 10);
}
function get_font_unit(current_size) {
    return current_size.slice(-2);
}
function update_font_size(el, num, unit) {
    el.css('font-size', num + unit);
    el.find('p').css('font-size', num + unit);
    el.find('div').css('font-size', num + unit);

}
function update_font_family(el, font) {
    el.css('font-family', font);
    el.find('p').css('font-family', font);
    el.find('div').css('font-family', font);
}<|endoftext|>