<commit_before>'use strict';

if (typeof GJS === "undefined") {
    var GJS = {};
}

var arrayUtil = {}; // Utilities for working with JS arrays
var stringUtil = {}; // Utilities for working with JS strings
var objectUtil = {}; // Utilities for working with JS objects
var querystringUtil = {}; // Utilities for working with querystring parameters

/**
 * Random function. Prefers using mathUtil.random() if available, falls back to Math.random().
 * @return {number} Random value between 0 and 1 (not including 1).
 */
arrayUtil._random = function() {
    if (window.mathUtil && mathUtil.random) {
        return mathUtil.random();
    } else {
        return Math.random();
    }
};

/**
 * Count the number of matches in an array.
 * @param {Array} array Array to check.
 * @param {function} matchFunc Function that takes one array item and returns true if it should be counted.
 * @return {number} The number of matching entries.
 */
arrayUtil.count = function(array, matchFunc) {
    return array.filter(matchFunc).length;
};

/**
 * Filter an array by removing elements that are found in the other array.
 * @param {Array} array Array to filter.
 * @param {Array} arrayToRemove Values to remove from the first array.
 * @return {Array} A new array with the filtered elements.
 */
arrayUtil.filterArray = function(array, arrayToRemove) {
    return array.filter(function(value) {
        return arrayToRemove.indexOf(value) < 0;
    });
};

/**
 * @param {Array} array Array to shuffle.
 * @return {Array} A shuffled copy of the array.
 */
arrayUtil.shuffle = function(array) {
    array = array.slice(0);
    var currentIndex = array.length, temporaryValue, randomIndex;

    // While there remain elements to shuffle...
    while (0 !== currentIndex) {
        // Pick a remaining element...
        randomIndex = Math.floor(arrayUtil._random() * currentIndex);
        currentIndex -= 1;

        // And swap it with the current element.
        temporaryValue = array[currentIndex];
        array[currentIndex] = array[randomIndex];
        array[randomIndex] = temporaryValue;
    }
    return array;
};

/**
 * @param {Array} array Array to edit in place.
 * @param {number} indexA Index of one element to swap.
 * @param {number} indexB Index of another element to swap.
 */
arrayUtil.swap = function(array, indexA, indexB) {
    if (indexA !== indexB) {
        var temporaryValue = array[indexA];
        array[indexA] = array[indexB];
        array[indexB] = temporaryValue;
    }
};

/**
 * @param {Array} array Array to shuffle.
 * @param {number} maxSubsetLength Maximum subset to return.
 * @return {Array} A random subset of the array containing at most maxSubsetLength elements.
 */
arrayUtil.randomSubset = function(array, maxSubsetLength) {
    var shuffled = arrayUtil.shuffle(array);
    return shuffled.splice(0, maxSubsetLength);
};

/**
 * @param {Array} array Array to shuffle.
 * @return {Object} A random item from the array.
 */
arrayUtil.randomItem = function(array) {
    var index = Math.floor(arrayUtil._random() * array.length);
    return array[index];
};

/**
 * Set a property in all elements in an array to a certain value.
 * @param {Array} array Array to edit.
 * @param {string} key Property to set in all elements.
 * @param {Object} value A value to set to the property in all elements.
 */
arrayUtil.setPropertyInAll = function(array, key, value) {
    for (var i = 0; i < array.length; ++i) {
        array[i][key] = value;
    }
};

/**
 * Stable sort an array in place.
 * @param {Array} array Array to sort.
 * @param {function} compareFunction Function as in Array.prototype.sort.
 * @return {Array} The sorted array.
 */
arrayUtil.stableSort = function(array, compareFunction) {
    if (array.length < 2) {
        return array;
    }
    var merge = function(left, right) {
        var result  = [];
        var l = 0;
        var r = 0;

        while (l < left.length && r < right.length) {
            if (compareFunction(left[l], right[r]) <= 0) {
                result.push(left[l]);
                ++l;
            } else {
                result.push(right[r]);
                ++r;
            }
        }
        result = result.concat(left.slice(l));
        result = result.concat(right.slice(r));
        return result;
    };

    var middle = Math.floor(array.length / 2);
    var left = array.slice(0, middle);
    var right = array.slice(middle);
    arrayUtil.stableSort(left, compareFunction);
    arrayUtil.stableSort(right, compareFunction);
    var spliceParams = [0, array.length]; // First two parameters of splice()
    var merged = merge(left, right);
    spliceParams = spliceParams.concat(merged);
    array.splice.apply(array, spliceParams);
    return array;
};

/**
 * Get nth unique subset of length subsetLength from array.
 * Reliable as long as there are less than 2^32 possible subsets.
 * To get the number of possible subsets, use mathUtil.binomialCoefficient(array.length, subsetLength).
 * @param {Array} array An array to get the subset from.
 * @param {number} subsetLength Length of the subset.
 * @param {number} n Index of the subset to generate, starting from 0.
 * @return {Array} A new array that is the nth unique subset of subsetLength elements from the input array.
 */
arrayUtil.nthSubset = function(array, subsetLength, n) {
    // Generate the combinations in a more natural order.
    // TODO: the algorithm below could probably be adjusted to do things in different order, so this step would not be needed:
    n = mathUtil.binomialCoefficient(array.length, subsetLength) - n - 1;

    var subset = [];
    var i = 0;
    var remainingSubsetLength = subsetLength;
    while (remainingSubsetLength > 0) {
        if (array.length - i <= remainingSubsetLength) {
            // Only one possible subset.
            subset.push(array[i]);
            --remainingSubsetLength;
            ++i;
            continue;
        }
        // Elements left in the array after element i:
        var elementsLeftAfterI = array.length - i - 1;
        // You can form (elementsLeft over remainingSubsetLength) combinations without using element i.
        var combinationsWithoutI = mathUtil.binomialCoefficient(elementsLeftAfterI, remainingSubsetLength);
        if (n >= combinationsWithoutI) {
            subset.push(array[i]);
            --remainingSubsetLength;
            n -= combinationsWithoutI;
        }
        ++i;
    }
    return subset;
};

/**
 * @param {Array} array An array to permute.
 * @param {number} n Index of the permutation to generate.
 * @return {Array} A new array that is the nth permutation of the input array.
 */
arrayUtil.nthPermutation = function(array, n) {
    var copy = array.slice(0);
    // Generate indices used to permute.
    var indices = [];
    var remainingIndices = [];
    for (var i = 0; i < array.length; ++i) {
        remainingIndices.push(i);
    }
    var x = n;
    for (var i = 0; i < array.length; ++i) {
        var permutationsOfOneSmaller = mathUtil.factorial(array.length - i - 1);
        var remainingIndicesIndex = Math.floor(x / permutationsOfOneSmaller);
        // TODO: This could probably be more efficient.
        indices.push(remainingIndices[remainingIndicesIndex]);
        remainingIndices.splice(remainingIndicesIndex, 1);
        x = x % permutationsOfOneSmaller;
    }
    // Reorder copy according to indices.
    for (var i = 0; i < array.length; ++i) {
        copy[i] = array[(indices[i])];
    }
    return copy;
};

/**
 * @param {Array} a An array.
 * @param {Array} b An array with the same elements as a, but possibly in a different permutation.
 * @return {number} How much elements are offset between the two arrays.
 */
arrayUtil.permutationDistance = function(a, b) {
    var distance = 0;
    for (var i = 0; i < a.length; ++i) {
        distance += Math.abs(b.indexOf(a[i]) - i);
    }
    return distance;
};

/**
 * @param {string} string Input string.
 * @return {string} String with the first letter capitalized.
 */
stringUtil.capitalizeFirstLetter = function(string) {
    return string.charAt(0).toUpperCase() + string.slice(1);
};

/**
 * @protected
 */
objectUtil._copy = function(target, source, key) {
    if (source[key] !== null && typeof source[key] === 'object') {
        if (Array.isArray(source[key])) {
            target[key] = source[key].slice();
            for (var i = 0; i < target[key].length; ++i) {
                if (typeof target[key][i] === 'object') {
                    throw new Error('copying an array of objects not supported');
                }
            }
        } else {
            target[key] = {};
            objectUtil.initWithDefaults(target[key], source[key], {});
        }
    } else {
        target[key] = source[key];
    }
};

/**
 * Initialize an object with default values. obj may end up referencing objects in "options".
 * @param {Object} obj Object to set properties on.
 * @param {Object} defaults Default properties. Every property needs to have a default value here.
 * @param {Object} options Options to override the default properties.
 */
objectUtil.initWithDefaults = function(obj, defaults, options) {
    for (var key in defaults) {
        if (!options.hasOwnProperty(key)) {
            objectUtil._copy(obj, defaults, key);
        } else {
            obj[key] = options[key];
        }
    }
};

/**
 * Fill in properties missing from an object.
 * @param {Object} obj Object that may already have some properties set.
 * @param {Object} fillIn Properties to fill in in case obj doesn't already have them.
 */
objectUtil.fillIn = function(obj, fillIn) {
    for (var key in fillIn) {
        if (!obj.hasOwnProperty(key)) {
            objectUtil._copy(obj, fillIn, key);
        }
    }
};

/**
 * Create a wrapper for an object that forwards method calls and set/get on properties.
 * @param {Object} toWrap Object to wrap.
 * @param {function()=} excludeFromForwarding Function that takes a key string and returns true if it should be
 *                      excluded from forwarding. Defaults to not excluding anything.
 * @return {Object} Wrapped object.
 */
objectUtil.wrap = function(toWrap, excludeFromForwarding) {
    if (excludeFromForwarding === undefined) {
        excludeFromForwarding = function() { return false; };
    }
    var wrapper = {};
    for (var prop in toWrap) {
        (function(p) {
            if (!excludeFromForwarding(p)) {
                if (typeof toWrap[p] == 'function') {
                    wrapper[p] = function() {
                        toWrap[p].apply(toWrap, arguments);
                    };
                } else  {
                    Object.defineProperty(wrapper, p, {
                        get: function() { return toWrap[p]; },
                        set: function(v) { toWrap[p] = v; }
                    });
                }
            }
        })(prop);
    }
    return wrapper;
};

/**
 * Get the value of the given querystring key else return undefined.
 * @param {String} key Key to search for in the querystring.
 * @param {String} [querystring] Querystring to search. If undefined then falls back to window.location.search.
 * @return {(String|undefined)} Value as a string.
 */
querystringUtil.get = function(key, querystring) {
    querystring = querystring || window.location.search;

    if (querystring.indexOf('?') === 0) {
        querystring = querystring.substring(1);  // Drop the starting "?"
    }

    var items = querystring.split('&');

    for (var i = 0; i < items.length; i++) {
        var item = items[i].split('=');

        if (item[0] === key) {
            return item[1] || '';
        }
    }

    return undefined;
};

/**
 * Request fullscreen on a given element.
 * @param {HTMLElement} elem Element to make fullscreen.
 */
GJS.requestFullscreen = function(elem) {
    if (elem.requestFullscreen) {
        elem.requestFullscreen();
    } else if (elem.msRequestFullscreen) {
        elem.msRequestFullscreen();
    } else if (elem.mozRequestFullScreen) {
        elem.mozRequestFullScreen();
    } else if (elem.webkitRequestFullscreen) {
        elem.webkitRequestFullscreen();
    }
};

/**
 * Exit fullscreen.
 */
GJS.exitFullscreen = function() {
    if(document.exitFullscreen) {
        document.exitFullscreen();
    } else if(document.mozCancelFullScreen) {
        document.mozCancelFullScreen();
    } else if(document.webkitExitFullscreen) {
        document.webkitExitFullscreen();
    }
};

/**
 * @param {function} listener Listener to call when fullscreen state changes.
 */
GJS.addFullscreenChangeListener = function(listener) {
    document.addEventListener('fullscreenchange', listener);
    document.addEventListener('mozfullscreenchange', listener);
    document.addEventListener('webkitfullscreenchange', listener);
};

/**
 * @return {boolean} True if document is currently fullscreen.
 */
GJS.isFullscreen = function() {
    if (document.fullscreenElement ||
        document.webkitFullscreenElement ||
        document.mozFullScreenElement ||
        document.webkitCurrentFullScreenElement)
    {
        return true;
    }
    return false;
};

/**
 * Change a value towards zero by a certain delta value.
 * @param {number} value Value to change.
 * @param {number} delta How much to change the value.
 * @return {number} Changed value.
 */
GJS.towardsZero = function(value, delta) {
    return GJS.towardsValue(value, 0, delta);
};

/**
 * Change a value closer to a target value by a certain delta value.
 * @param {number} value Value to change.
 * @param {number} targetValue Value to approach.
 * @param {number} delta How much to change the value.
 * @return {number} Changed value.
 */
GJS.towardsValue = function(value, targetValue, delta) {
    if (value > targetValue) {
        value -= delta;
        if (value < targetValue)
            value = targetValue;
    }
    if (value < targetValue) {
        value += delta;
        if (value > targetValue)
            value = targetValue;
    }
    return value;
};

GJS.debugLog = function(output) {
    if ( window.console && console.log ) {
        console.log(output);
    }
};

/*-----------*/
/* Polyfills */
/*-----------*/

if (!String.prototype.startsWith) {
    String.prototype.startsWith = function(searchString, position){
      position = position || 0;
      return this.substr(position, searchString.length) === searchString;
  };
}

if (!String.prototype.endsWith) {
  String.prototype.endsWith = function(searchString, position) {
      var subjectString = this.toString();
      if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) {
        position = subjectString.length;
      }
      position -= searchString.length;
      var lastIndex = subjectString.indexOf(searchString, position);
      return lastIndex !== -1 && lastIndex === position;
  };
}
<commit_msg>Only use own properties from defaults in initWithDefaults<commit_after>'use strict';

if (typeof GJS === "undefined") {
    var GJS = {};
}

var arrayUtil = {}; // Utilities for working with JS arrays
var stringUtil = {}; // Utilities for working with JS strings
var objectUtil = {}; // Utilities for working with JS objects
var querystringUtil = {}; // Utilities for working with querystring parameters

/**
 * Random function. Prefers using mathUtil.random() if available, falls back to Math.random().
 * @return {number} Random value between 0 and 1 (not including 1).
 */
arrayUtil._random = function() {
    if (window.mathUtil && mathUtil.random) {
        return mathUtil.random();
    } else {
        return Math.random();
    }
};

/**
 * Count the number of matches in an array.
 * @param {Array} array Array to check.
 * @param {function} matchFunc Function that takes one array item and returns true if it should be counted.
 * @return {number} The number of matching entries.
 */
arrayUtil.count = function(array, matchFunc) {
    return array.filter(matchFunc).length;
};

/**
 * Filter an array by removing elements that are found in the other array.
 * @param {Array} array Array to filter.
 * @param {Array} arrayToRemove Values to remove from the first array.
 * @return {Array} A new array with the filtered elements.
 */
arrayUtil.filterArray = function(array, arrayToRemove) {
    return array.filter(function(value) {
        return arrayToRemove.indexOf(value) < 0;
    });
};

/**
 * @param {Array} array Array to shuffle.
 * @return {Array} A shuffled copy of the array.
 */
arrayUtil.shuffle = function(array) {
    array = array.slice(0);
    var currentIndex = array.length, temporaryValue, randomIndex;

    // While there remain elements to shuffle...
    while (0 !== currentIndex) {
        // Pick a remaining element...
        randomIndex = Math.floor(arrayUtil._random() * currentIndex);
        currentIndex -= 1;

        // And swap it with the current element.
        temporaryValue = array[currentIndex];
        array[currentIndex] = array[randomIndex];
        array[randomIndex] = temporaryValue;
    }
    return array;
};

/**
 * @param {Array} array Array to edit in place.
 * @param {number} indexA Index of one element to swap.
 * @param {number} indexB Index of another element to swap.
 */
arrayUtil.swap = function(array, indexA, indexB) {
    if (indexA !== indexB) {
        var temporaryValue = array[indexA];
        array[indexA] = array[indexB];
        array[indexB] = temporaryValue;
    }
};

/**
 * @param {Array} array Array to shuffle.
 * @param {number} maxSubsetLength Maximum subset to return.
 * @return {Array} A random subset of the array containing at most maxSubsetLength elements.
 */
arrayUtil.randomSubset = function(array, maxSubsetLength) {
    var shuffled = arrayUtil.shuffle(array);
    return shuffled.splice(0, maxSubsetLength);
};

/**
 * @param {Array} array Array to shuffle.
 * @return {Object} A random item from the array.
 */
arrayUtil.randomItem = function(array) {
    var index = Math.floor(arrayUtil._random() * array.length);
    return array[index];
};

/**
 * Set a property in all elements in an array to a certain value.
 * @param {Array} array Array to edit.
 * @param {string} key Property to set in all elements.
 * @param {Object} value A value to set to the property in all elements.
 */
arrayUtil.setPropertyInAll = function(array, key, value) {
    for (var i = 0; i < array.length; ++i) {
        array[i][key] = value;
    }
};

/**
 * Stable sort an array in place.
 * @param {Array} array Array to sort.
 * @param {function} compareFunction Function as in Array.prototype.sort.
 * @return {Array} The sorted array.
 */
arrayUtil.stableSort = function(array, compareFunction) {
    if (array.length < 2) {
        return array;
    }
    var merge = function(left, right) {
        var result  = [];
        var l = 0;
        var r = 0;

        while (l < left.length && r < right.length) {
            if (compareFunction(left[l], right[r]) <= 0) {
                result.push(left[l]);
                ++l;
            } else {
                result.push(right[r]);
                ++r;
            }
        }
        result = result.concat(left.slice(l));
        result = result.concat(right.slice(r));
        return result;
    };

    var middle = Math.floor(array.length / 2);
    var left = array.slice(0, middle);
    var right = array.slice(middle);
    arrayUtil.stableSort(left, compareFunction);
    arrayUtil.stableSort(right, compareFunction);
    var spliceParams = [0, array.length]; // First two parameters of splice()
    var merged = merge(left, right);
    spliceParams = spliceParams.concat(merged);
    array.splice.apply(array, spliceParams);
    return array;
};

/**
 * Get nth unique subset of length subsetLength from array.
 * Reliable as long as there are less than 2^32 possible subsets.
 * To get the number of possible subsets, use mathUtil.binomialCoefficient(array.length, subsetLength).
 * @param {Array} array An array to get the subset from.
 * @param {number} subsetLength Length of the subset.
 * @param {number} n Index of the subset to generate, starting from 0.
 * @return {Array} A new array that is the nth unique subset of subsetLength elements from the input array.
 */
arrayUtil.nthSubset = function(array, subsetLength, n) {
    // Generate the combinations in a more natural order.
    // TODO: the algorithm below could probably be adjusted to do things in different order, so this step would not be needed:
    n = mathUtil.binomialCoefficient(array.length, subsetLength) - n - 1;

    var subset = [];
    var i = 0;
    var remainingSubsetLength = subsetLength;
    while (remainingSubsetLength > 0) {
        if (array.length - i <= remainingSubsetLength) {
            // Only one possible subset.
            subset.push(array[i]);
            --remainingSubsetLength;
            ++i;
            continue;
        }
        // Elements left in the array after element i:
        var elementsLeftAfterI = array.length - i - 1;
        // You can form (elementsLeft over remainingSubsetLength) combinations without using element i.
        var combinationsWithoutI = mathUtil.binomialCoefficient(elementsLeftAfterI, remainingSubsetLength);
        if (n >= combinationsWithoutI) {
            subset.push(array[i]);
            --remainingSubsetLength;
            n -= combinationsWithoutI;
        }
        ++i;
    }
    return subset;
};

/**
 * @param {Array} array An array to permute.
 * @param {number} n Index of the permutation to generate.
 * @return {Array} A new array that is the nth permutation of the input array.
 */
arrayUtil.nthPermutation = function(array, n) {
    var copy = array.slice(0);
    // Generate indices used to permute.
    var indices = [];
    var remainingIndices = [];
    for (var i = 0; i < array.length; ++i) {
        remainingIndices.push(i);
    }
    var x = n;
    for (var i = 0; i < array.length; ++i) {
        var permutationsOfOneSmaller = mathUtil.factorial(array.length - i - 1);
        var remainingIndicesIndex = Math.floor(x / permutationsOfOneSmaller);
        // TODO: This could probably be more efficient.
        indices.push(remainingIndices[remainingIndicesIndex]);
        remainingIndices.splice(remainingIndicesIndex, 1);
        x = x % permutationsOfOneSmaller;
    }
    // Reorder copy according to indices.
    for (var i = 0; i < array.length; ++i) {
        copy[i] = array[(indices[i])];
    }
    return copy;
};

/**
 * @param {Array} a An array.
 * @param {Array} b An array with the same elements as a, but possibly in a different permutation.
 * @return {number} How much elements are offset between the two arrays.
 */
arrayUtil.permutationDistance = function(a, b) {
    var distance = 0;
    for (var i = 0; i < a.length; ++i) {
        distance += Math.abs(b.indexOf(a[i]) - i);
    }
    return distance;
};

/**
 * @param {string} string Input string.
 * @return {string} String with the first letter capitalized.
 */
stringUtil.capitalizeFirstLetter = function(string) {
    return string.charAt(0).toUpperCase() + string.slice(1);
};

/**
 * @protected
 */
objectUtil._copy = function(target, source, key) {
    if (source[key] !== null && typeof source[key] === 'object') {
        if (Array.isArray(source[key])) {
            target[key] = source[key].slice();
            for (var i = 0; i < target[key].length; ++i) {
                if (typeof target[key][i] === 'object') {
                    throw new Error('copying an array of objects not supported');
                }
            }
        } else {
            target[key] = {};
            objectUtil.initWithDefaults(target[key], source[key], {});
        }
    } else {
        target[key] = source[key];
    }
};

/**
 * Initialize an object with default values. obj may end up referencing objects in "options".
 * @param {Object} obj Object to set properties on.
 * @param {Object} defaults Default properties. Every property needs to have a default value here.
 * @param {Object} options Options to override the default properties.
 */
objectUtil.initWithDefaults = function(obj, defaults, options) {
    for (var key in defaults) {
        if (defaults.hasOwnProperty(key)) {
            if (!options.hasOwnProperty(key)) {
                objectUtil._copy(obj, defaults, key);
            } else {
                obj[key] = options[key];
            }
        }
    }
};

/**
 * Fill in properties missing from an object.
 * @param {Object} obj Object that may already have some properties set.
 * @param {Object} fillIn Properties to fill in in case obj doesn't already have them.
 */
objectUtil.fillIn = function(obj, fillIn) {
    for (var key in fillIn) {
        if (!obj.hasOwnProperty(key)) {
            objectUtil._copy(obj, fillIn, key);
        }
    }
};

/**
 * Create a wrapper for an object that forwards method calls and set/get on properties.
 * @param {Object} toWrap Object to wrap.
 * @param {function()=} excludeFromForwarding Function that takes a key string and returns true if it should be
 *                      excluded from forwarding. Defaults to not excluding anything.
 * @return {Object} Wrapped object.
 */
objectUtil.wrap = function(toWrap, excludeFromForwarding) {
    if (excludeFromForwarding === undefined) {
        excludeFromForwarding = function() { return false; };
    }
    var wrapper = {};
    for (var prop in toWrap) {
        (function(p) {
            if (!excludeFromForwarding(p)) {
                if (typeof toWrap[p] == 'function') {
                    wrapper[p] = function() {
                        toWrap[p].apply(toWrap, arguments);
                    };
                } else  {
                    Object.defineProperty(wrapper, p, {
                        get: function() { return toWrap[p]; },
                        set: function(v) { toWrap[p] = v; }
                    });
                }
            }
        })(prop);
    }
    return wrapper;
};

/**
 * Get the value of the given querystring key else return undefined.
 * @param {String} key Key to search for in the querystring.
 * @param {String} [querystring] Querystring to search. If undefined then falls back to window.location.search.
 * @return {(String|undefined)} Value as a string.
 */
querystringUtil.get = function(key, querystring) {
    querystring = querystring || window.location.search;

    if (querystring.indexOf('?') === 0) {
        querystring = querystring.substring(1);  // Drop the starting "?"
    }

    var items = querystring.split('&');

    for (var i = 0; i < items.length; i++) {
        var item = items[i].split('=');

        if (item[0] === key) {
            return item[1] || '';
        }
    }

    return undefined;
};

/**
 * Request fullscreen on a given element.
 * @param {HTMLElement} elem Element to make fullscreen.
 */
GJS.requestFullscreen = function(elem) {
    if (elem.requestFullscreen) {
        elem.requestFullscreen();
    } else if (elem.msRequestFullscreen) {
        elem.msRequestFullscreen();
    } else if (elem.mozRequestFullScreen) {
        elem.mozRequestFullScreen();
    } else if (elem.webkitRequestFullscreen) {
        elem.webkitRequestFullscreen();
    }
};

/**
 * Exit fullscreen.
 */
GJS.exitFullscreen = function() {
    if(document.exitFullscreen) {
        document.exitFullscreen();
    } else if(document.mozCancelFullScreen) {
        document.mozCancelFullScreen();
    } else if(document.webkitExitFullscreen) {
        document.webkitExitFullscreen();
    }
};

/**
 * @param {function} listener Listener to call when fullscreen state changes.
 */
GJS.addFullscreenChangeListener = function(listener) {
    document.addEventListener('fullscreenchange', listener);
    document.addEventListener('mozfullscreenchange', listener);
    document.addEventListener('webkitfullscreenchange', listener);
};

/**
 * @return {boolean} True if document is currently fullscreen.
 */
GJS.isFullscreen = function() {
    if (document.fullscreenElement ||
        document.webkitFullscreenElement ||
        document.mozFullScreenElement ||
        document.webkitCurrentFullScreenElement)
    {
        return true;
    }
    return false;
};

/**
 * Change a value towards zero by a certain delta value.
 * @param {number} value Value to change.
 * @param {number} delta How much to change the value.
 * @return {number} Changed value.
 */
GJS.towardsZero = function(value, delta) {
    return GJS.towardsValue(value, 0, delta);
};

/**
 * Change a value closer to a target value by a certain delta value.
 * @param {number} value Value to change.
 * @param {number} targetValue Value to approach.
 * @param {number} delta How much to change the value.
 * @return {number} Changed value.
 */
GJS.towardsValue = function(value, targetValue, delta) {
    if (value > targetValue) {
        value -= delta;
        if (value < targetValue)
            value = targetValue;
    }
    if (value < targetValue) {
        value += delta;
        if (value > targetValue)
            value = targetValue;
    }
    return value;
};

GJS.debugLog = function(output) {
    if ( window.console && console.log ) {
        console.log(output);
    }
};

/*-----------*/
/* Polyfills */
/*-----------*/

if (!String.prototype.startsWith) {
    String.prototype.startsWith = function(searchString, position){
      position = position || 0;
      return this.substr(position, searchString.length) === searchString;
  };
}

if (!String.prototype.endsWith) {
  String.prototype.endsWith = function(searchString, position) {
      var subjectString = this.toString();
      if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) {
        position = subjectString.length;
      }
      position -= searchString.length;
      var lastIndex = subjectString.indexOf(searchString, position);
      return lastIndex !== -1 && lastIndex === position;
  };
}
<|endoftext|>


<commit_before>'use strict';

let knexDb = require('knex');

let instance = null;

module.exports.getInstance = () => {
  if (instance === null) {

    try {
      instance = knexDb({
        client: 'pg',
        debug: true,
        connection: {
          connection: process.env.DATABASE_URL,
          database: 'd1ou8ff1cn9r17',
          timezone: 'UTC',
        },
        pool: {
          min: 0,
          max: 1,
        }
      });
    } catch (err) {
      console.log('ERROR', err.stack);
      throw err;
    }
  }

  return instance;
}<commit_msg>testing<commit_after>'use strict';

let knexDb = require('knex');

let instance = null;

module.exports.getInstance = () => {
  if (instance === null) {

    instance = knexDb({
      client: 'pg',
      debug: true,
      connection: process.env.DATABASE_URL,
      pool: {
        min: 0,
        max: 1,
      }
    });
  }

  return instance;
}<|endoftext|>


<commit_before>!function() {
/* ==========================    =
=========================== */

// instantiation
const view = new AsksView( '.question-item' )

view.fetch_then_render( 'https://nfte-2017-writeme.github.io/writeme/mocks/endpoints/qna/index.json' )
/* =====  End of run  ====== */

}()
<commit_msg>testing swipe feature<commit_after>!function() {
/* ==========================    =
=========================== */

// instantiation
const view = new AsksView( '.question-item' )

view.fetch_then_render( 'https://nfte-2017-writeme.github.io/writeme/mocks/endpoints/qna/index.json' )
/* =====  End of run  ====== */

}()

var element = $('body');

Hammer(element).on("swipeleft", function() {
    location.reload();
});
<|endoftext|>


<commit_before>var fs = require('fs');
var winston = require('winston');
var my = require('./my');
var parse = require('./parse');

var debug = process.env.DEBUG ? true : false;

var ainPrefix = "";
var pwmPrefix = {};
var gpioFile  = {};

module.exports = {

    logfile : '/var/lib/cloud9/bonescript.log',

    readPWMFreqAndValue : function(pin, pwm) {
        var mode = {};
        try {
            var period = fs.readFileSync(pwmPrefix[pin.pwm.name]+'/period');
            var duty = fs.readFileSync(pwmPrefix[pin.pwm.name]+'/duty');
            mode.freq = 1.0e9 / period;
            mode.value = duty / period;
        } catch(ex) {
        }
        return(mode);
    },

    readGPIODirection : function(n, gpio) {
        var mode = {};
        var directionFile = "/sys/class/gpio/gpio" + n + "/direction";
        if(my.file_existsSync(directionFile)) {
            mode.active = true;
            var direction = fs.readFileSync(directionFile, 'utf-8');
            direction = direction.replace(/^\s+|\s+$/g, '');
            mode.direction = direction;
        }
        return(mode);
    },

    readPinMux : function(pin, mode, callback) {
        var pinctrlFile = '/sys/kernel/debug/pinctrl/44e10800.pinmux/pins';
        var muxRegOffset = parseInt(pin.muxRegOffset, 16);
        var readPinctrl = function(err, data){
            if(err) {
                mode.err = 'readPinctrl error: ' + err;
                if(debug) winston.debug(mode.err);
                callback(mode);
            }
            mode = parse.modeFromPinctrl(data, muxRegOffset, 0x44e10800, mode);
            callback(mode);
        };
        var tryPinctrl = function(exists) {
            if(exists) {
                fs.readFile(pinctrlFile, 'utf8', readPinctrl);
            } else {
                if(debug) winston.debug('getPinMode(' + pin.key + '): no valid mux data');
                callback(mode);
            }
        };
        my.file_exists(pinctrlFile, tryPinctrl);
    },

    setPinMode : function(pin, pinData, template, resp, callback) {
        if(debug) winston.debug('hw.setPinMode(' + [pin.key, pinData, template, JSON.stringify(resp)] + ');');
        if(template == 'bspm') {
            gpioFile[pin.key] = '/sys/class/gpio/gpio' + pin.gpio + '/value';
            doCreateDT(resp);
        } else if(template == 'bspwm') {
            my.load_dt('am33xx_pwm', null, resp, doCreateDT);
        } else {
            resp.err = 'Unknown pin mode template';
            callback(resp);
        }

        function doCreateDT(resp) {
            if(resp.err) {
                callback(resp);
                return;
            }
            my.create_dt(pin, pinData, template, true, false, resp, onCreateDT);
        }

        function onCreateDT(resp) {
            if(resp.err) {
                callback(resp);
                return;
            }
            if(template == 'bspwm') {
                my.file_find('/sys/devices', 'ocp.', 1, onFindOCP);
            } else {
                callback(resp);
            }

            function onFindOCP(ocp) {
                if(ocp.err) {
                    resp.err = "Error searching for ocp: " + ocp.err;
                    if(debug) winston.debug(resp.err);
                    callback(resp);
                    return;
                }
                my.file_find(ocp.path, 'bs_pwm_test_' + pin.key + '.', 1, onFindPWM);
            }

            function onFindPWM(pwm_test) {
                if(pwm_test.err) {
                    resp.err = "Error searching for pwm_test: " + pwm_test.err;
                    if(debug) winston.debug(resp.err);
                    callback(resp);
                    return;
                }
                my.file_find(pwm_test.path, 'period', 1, onFindPeriod);

                function onFindPeriod(period) {
                    if(period.err) {
                        resp.err = "Error searching for period: " + period.err;
                        if(debug) winston.debug(resp.err);
                        callback(resp);
                        return;
                    }
                    pwmPrefix[pin.pwm.name] = pwm_test.path;
                    fs.writeFile(pwm_test.path+'/polarity', 0, 'ascii', onPolarityWrite);
                }
            }

            function onPolarityWrite(err) {
                if(err) {
                    resp.err = "Error writing PWM polarity: " + err;
                    if(debug) winston.debug(resp.err);
                }
                callback(resp);
            }
        }
    },

    setLEDPinToGPIO : function(pin, resp) {
        var path = "/sys/class/leds/beaglebone:green:" + pin.led + "/trigger";

        if(my.file_existsSync(path)) {
            fs.writeFileSync(path, "gpio");
        } else {
            resp.err = "Unable to find LED " + pin.led;
            winston.error(resp.err);
            resp.value = false;
        }

        return(resp);
    },

    exportGPIOControls : function(pin, direction, resp, callback) {
        if(debug) winston.debug('hw.exportGPIOControls(' + [pin.key, direction, resp] + ');');
        var n = pin.gpio;
        my.file_exists(gpioFile[pin.key], onFileExists);

        function onFileExists(exists) {
            if(exists) {
                if(debug) winston.debug("gpio: " + n + " already exported.");
                fs.writeFile("/sys/class/gpio/gpio" + n + "/direction",
                    direction, null, onGPIODirectionSet);
            } else {
                if(debug) winston.debug("exporting gpio: " + n);
                fs.writeFile("/sys/class/gpio/export", "" + n, null, onGPIOExport);
            }
        }

        function onGPIOExport(err) {
            if(err) onError(err);
            if(debug) winston.debug("setting gpio " + n +
                " direction to " + direction);
            fs.writeFile("/sys/class/gpio/gpio" + n + "/direction",
                direction, null, onGPIODirectionSet);
        }

        function onGPIODirectionSet(err) {
            if(err) onError(err);
            else callback(resp);
        }

        function onError(err) {
            resp.err = 'Unable to export gpio-' + n + ': ' + err;
            resp.value = false;
            if(debug) winston.debug(resp.err);
            findOwner();
        }

        function findOwner() {
            fs.readFile('/sys/kernel/debug/gpio', 'utf-8', onGPIOUsers);
        }

        function onGPIOUsers(err, data) {
            if(!err) {
                var gpioUsers = data.split('\n');
                for(var x in gpioUsers) {
                    var y = gpioUsers[x].match(/gpio-(\d+)\s+\((\S+)\s*\)/);
                    if(y && y[1] == n) {
                        resp.err += '\nconsumed by ' + y[2];
                        if(debug) winston.debug(resp.err);
                    }
                }
            }
            callback(resp);
        }

    },

    writeGPIOValue : function(pin, value, callback) {
        if(typeof gpioFile[pin.key] == 'undefined') {
            gpioFile[pin.key] = '/sys/class/gpio/gpio' + pin.gpio + '/value';
            if(pin.led) {
                gpioFile[pin.key] = "/sys/class/leds/beaglebone:";
                gpioFile[pin.key] += "green:" + pin.led + "/brightness";
            }
            if(!my.file_existsSync(gpioFile[pin.key])) {
                winston.error("Unable to find gpio: " + gpioFile[pin.key]);
            }
        }
        if(debug) winston.debug("gpioFile = " + gpioFile[pin.key]);
        fs.writeFile(gpioFile[pin.key], '' + value, null, writeFile);

        function writeFile(err) {
            if(err) {
                resp.err = 'digitalWrite error: ' + err;
                winston.error(resp.err);
            }
            callback(err);
        }
    },

    readGPIOValue : function(pin, resp, callback) {
        var gpioFile = '/sys/class/gpio/gpio' + pin.gpio + '/value';
        var readFile = function(err, data) {
            if(err) {
                resp.err = 'digitalRead error: ' + err;
                winston.error(resp.err);
            }
            resp.value = parseInt(data, 2);
            callback(resp);
        };
        fs.readFile(gpioFile, readFile);
    },

    enableAIN : function(callback) {
        var resp = {};
        var ocp = my.is_ocp();
        if(!ocp) {
            resp.err = 'enableAIN: Unable to open ocp file';
            if(debug) winston.debug(resp.err);
            callback(resp);
            return;
        }

        my.load_dt('cape-bone-iio', null, {}, onLoadDT);

        function onLoadDT(x) {
            if(x.err) {
                callback(x);
                return;
            }
            my.find_sysfsFile('helper', ocp, 'helper.', onHelper);
        }

        function onHelper(x) {
            if(x.err || !x.path) {
                resp.err = 'Error enabling analog inputs: ' + x.err;
                if(debug) winston.debug(resp.err);
            } else {
                ainPrefix = x.path + '/AIN';
                if(debug) winston.debug("Setting ainPrefix to " + ainPrefix);
            }
            callback(x);
        }
    },

    readAIN : function(pin, resp, callback) {
        var ainFile = ainPrefix + pin.ain.toString();
        fs.readFile(ainFile, readFile);

        function readFile(err, data) {
            if(err) {
                resp.err = 'analogRead error: ' + err;
                winston.error(resp.err);
            }
            resp.value = parseInt(data, 10) / 1800;
            callback(resp);
        }
    },

    writeGPIOEdge : function(pin, mode) {
        fs.writeFileSync('/sys/class/gpio/gpio' + pin.gpio + '/edge', mode);

        var resp = {};
        resp.gpioFile = '/sys/class/gpio/gpio' + pin.gpio + '/value';
        resp.valuefd = fs.openSync(resp.gpioFile, 'r');
        resp.value = new Buffer(1);

        return(resp);
    },

    writePWMFreqAndValue : function(pin, pwm, freq, value, resp, callback) {
        if(debug) winston.debug('hw.writePWMFreqAndValue(' + [pin.key,pwm,freq,value,resp] + ');');
        var path = pwmPrefix[pin.pwm.name];
        try {
            var period = Math.round( 1.0e9 / freq ); // period in ns
            var duty = Math.round( period * value );
            fs.writeFileSync(path+'/duty', 0);
            if(pwm.freq != freq) {
                fs.writeFileSync(path+'/period', period);
            }
            fs.writeFileSync(path+'/duty', duty);
        } catch(ex) {
            resp.err = 'error updating PWM freq and value: ' + path + ', ' + ex;
            winston.error(resp.err);
        }
        return(resp);
    },

    readEeproms : function(eeproms) {
        var boardName = fs.readFileSync(my.is_capemgr() + '/baseboard/board-name',
                'ascii');
        var version = fs.readFileSync(my.is_capemgr() + '/baseboard/revision',
                'ascii');
        var serialNumber = fs.readFileSync(my.is_capemgr() + '/baseboard/serial-number',
                'ascii');
        eeproms['/sys/bus/i2c/drivers/at24/1-0050/eeprom'] = {};
        eeproms['/sys/bus/i2c/drivers/at24/1-0050/eeprom'].boardName = boardName;
        eeproms['/sys/bus/i2c/drivers/at24/1-0050/eeprom'].version = version;
        eeproms['/sys/bus/i2c/drivers/at24/1-0050/eeprom'].serialNumber = serialNumber;
        return(eeproms);
    },

    readPlatform : function(platform) {
        platform.name = fs.readFileSync(my.is_capemgr() + '/baseboard/board-name',
            'ascii').trim();
        if(platform.name == 'A335BONE') platform.name = 'BeagleBone';
        if(platform.name == 'A335BNLT') platform.name = 'BeagleBone Black';
        platform.version = fs.readFileSync(my.is_capemgr() + '/baseboard/revision',
            'ascii').trim();
        if(!platform.version.match(/^[\040-\176]*$/)) delete platform.version;
        platform.serialNumber = fs.readFileSync(my.is_capemgr() +
            '/baseboard/serial-number', 'ascii').trim();
        if(!platform.serialNumber.match(/^[\040-\176]*$/)) delete platform.serialNumber;
        return(platform);
    }
};
<commit_msg>Bug fix in capemanager<commit_after>var fs = require('fs');
var winston = require('winston');
var my = require('./my');
var parse = require('./parse');

var debug = process.env.DEBUG ? true : false;

var ainPrefix = "";
var pwmPrefix = {};
var gpioFile  = {};

module.exports = {

    logfile : '/var/lib/cloud9/bonescript.log',

    readPWMFreqAndValue : function(pin, pwm) {
        var mode = {};
        try {
            var period = fs.readFileSync(pwmPrefix[pin.pwm.name]+'/period');
            var duty = fs.readFileSync(pwmPrefix[pin.pwm.name]+'/duty');
            mode.freq = 1.0e9 / period;
            mode.value = duty / period;
        } catch(ex) {
        }
        return(mode);
    },

    readGPIODirection : function(n, gpio) {
        var mode = {};
        var directionFile = "/sys/class/gpio/gpio" + n + "/direction";
        if(my.file_existsSync(directionFile)) {
            mode.active = true;
            var direction = fs.readFileSync(directionFile, 'utf-8');
            direction = direction.replace(/^\s+|\s+$/g, '');
            mode.direction = direction;
        }
        return(mode);
    },

    readPinMux : function(pin, mode, callback) {
        var pinctrlFile = '/sys/kernel/debug/pinctrl/44e10800.pinmux/pins';
        var muxRegOffset = parseInt(pin.muxRegOffset, 16);
        var readPinctrl = function(err, data){
            if(err) {
                mode.err = 'readPinctrl error: ' + err;
                if(debug) winston.debug(mode.err);
                callback(mode);
            }
            mode = parse.modeFromPinctrl(data, muxRegOffset, 0x44e10800, mode);
            callback(mode);
        };
        var tryPinctrl = function(exists) {
            if(exists) {
                fs.readFile(pinctrlFile, 'utf8', readPinctrl);
            } else {
                if(debug) winston.debug('getPinMode(' + pin.key + '): no valid mux data');
                callback(mode);
            }
        };
        my.file_exists(pinctrlFile, tryPinctrl);
    },

    setPinMode : function(pin, pinData, template, resp, callback) {
        if(debug) winston.debug('hw.setPinMode(' + [pin.key, pinData, template, JSON.stringify(resp)] + ');');
        if(template == 'bspm') {
            gpioFile[pin.key] = '/sys/class/gpio/gpio' + pin.gpio + '/value';
            doCreateDT(resp);
        } else if(template == 'bspwm') {
            my.load_dt('am33xx_pwm', null, resp, doCreateDT);
        } else {
            resp.err = 'Unknown pin mode template';
            callback(resp);
        }

        function doCreateDT(resp) {
            if(resp.err) {
                callback(resp);
                return;
            }
            my.create_dt(pin, pinData, template, true, false, resp, onCreateDT);
        }

        function onCreateDT(resp) {
            if(resp.err) {
                callback(resp);
                return;
            }
            if(template == 'bspwm') {
                my.file_find('/sys/devices', 'ocp.', 1, onFindOCP);
            } else {
                callback(resp);
            }

            function onFindOCP(ocp) {
                if(ocp.err) {
                    resp.err = "Error searching for ocp: " + ocp.err;
                    if(debug) winston.debug(resp.err);
                    callback(resp);
                    return;
                }
                my.file_find(ocp.path, 'bs_pwm_test_' + pin.key + '.', 1, onFindPWM);
            }

            function onFindPWM(pwm_test) {
                if(pwm_test.err) {
                    resp.err = "Error searching for pwm_test: " + pwm_test.err;
                    if(debug) winston.debug(resp.err);
                    callback(resp);
                    return;
                }
                my.file_find(pwm_test.path, 'period', 1, onFindPeriod);

                function onFindPeriod(period) {
                    if(period.err) {
                        resp.err = "Error searching for period: " + period.err;
                        if(debug) winston.debug(resp.err);
                        callback(resp);
                        return;
                    }
                    pwmPrefix[pin.pwm.name] = pwm_test.path;
                    fs.writeFile(pwm_test.path+'/polarity', 0, 'ascii', onPolarityWrite);
                }
            }

            function onPolarityWrite(err) {
                if(err) {
                    resp.err = "Error writing PWM polarity: " + err;
                    if(debug) winston.debug(resp.err);
                }
                callback(resp);
            }
        }
    },

    setLEDPinToGPIO : function(pin, resp) {
        var path = "/sys/class/leds/beaglebone:green:" + pin.led + "/trigger";

        if(my.file_existsSync(path)) {
            fs.writeFileSync(path, "gpio");
        } else {
            resp.err = "Unable to find LED " + pin.led;
            winston.error(resp.err);
            resp.value = false;
        }

        return(resp);
    },

    exportGPIOControls : function(pin, direction, resp, callback) {
        if(debug) winston.debug('hw.exportGPIOControls(' + [pin.key, direction, resp] + ');');
        var n = pin.gpio;
        my.file_exists(gpioFile[pin.key], onFileExists);

        function onFileExists(exists) {
            if(exists) {
                if(debug) winston.debug("gpio: " + n + " already exported.");
                fs.writeFile("/sys/class/gpio/gpio" + n + "/direction",
                    direction, null, onGPIODirectionSet);
            } else {
                if(debug) winston.debug("exporting gpio: " + n);
                fs.writeFile("/sys/class/gpio/export", "" + n, null, onGPIOExport);
            }
        }

        function onGPIOExport(err) {
            if(err) onError(err);
            if(debug) winston.debug("setting gpio " + n +
                " direction to " + direction);
            fs.writeFile("/sys/class/gpio/gpio" + n + "/direction",
                direction, null, onGPIODirectionSet);
        }

        function onGPIODirectionSet(err) {
            if(err) onError(err);
            else callback(resp);
        }

        function onError(err) {
            resp.err = 'Unable to export gpio-' + n + ': ' + err;
            resp.value = false;
            if(debug) winston.debug(resp.err);
            findOwner();
        }

        function findOwner() {
            fs.readFile('/sys/kernel/debug/gpio', 'utf-8', onGPIOUsers);
        }

        function onGPIOUsers(err, data) {
            if(!err) {
                var gpioUsers = data.split('\n');
                for(var x in gpioUsers) {
                    var y = gpioUsers[x].match(/gpio-(\d+)\s+\((\S+)\s*\)/);
                    if(y && y[1] == n) {
                        resp.err += '\nconsumed by ' + y[2];
                        if(debug) winston.debug(resp.err);
                    }
                }
            }
            callback(resp);
        }

    },

    writeGPIOValue : function(pin, value, callback) {
        if(typeof gpioFile[pin.key] == 'undefined') {
            gpioFile[pin.key] = '/sys/class/gpio/gpio' + pin.gpio + '/value';
            if(pin.led) {
                gpioFile[pin.key] = "/sys/class/leds/beaglebone:";
                gpioFile[pin.key] += "green:" + pin.led + "/brightness";
            }
            if(!my.file_existsSync(gpioFile[pin.key])) {
                winston.error("Unable to find gpio: " + gpioFile[pin.key]);
            }
        }
        if(debug) winston.debug("gpioFile = " + gpioFile[pin.key]);
        fs.writeFile(gpioFile[pin.key], '' + value, null, writeFile);

        function writeFile(err) {
            if(err) {
                winston.error(err);
            }
            callback(err);
        }
    },

    readGPIOValue : function(pin, resp, callback) {
        var gpioFile = '/sys/class/gpio/gpio' + pin.gpio + '/value';
        var readFile = function(err, data) {
            if(err) {
                resp.err = 'digitalRead error: ' + err;
                winston.error(resp.err);
            }
            resp.value = parseInt(data, 2);
            callback(resp);
        };
        fs.readFile(gpioFile, readFile);
    },

    enableAIN : function(callback) {
        var resp = {};
        var ocp = my.is_ocp();
        if(!ocp) {
            resp.err = 'enableAIN: Unable to open ocp file';
            if(debug) winston.debug(resp.err);
            callback(resp);
            return;
        }

        my.load_dt('cape-bone-iio', null, {}, onLoadDT);

        function onLoadDT(x) {
            if(x.err) {
                callback(x);
                return;
            }
            my.find_sysfsFile('helper', ocp, 'helper.', onHelper);
        }

        function onHelper(x) {
            if(x.err || !x.path) {
                resp.err = 'Error enabling analog inputs: ' + x.err;
                if(debug) winston.debug(resp.err);
            } else {
                ainPrefix = x.path + '/AIN';
                if(debug) winston.debug("Setting ainPrefix to " + ainPrefix);
            }
            callback(x);
        }
    },

    readAIN : function(pin, resp, callback) {
        var ainFile = ainPrefix + pin.ain.toString();
        fs.readFile(ainFile, readFile);

        function readFile(err, data) {
            if(err) {
                resp.err = 'analogRead error: ' + err;
                winston.error(resp.err);
            }
            resp.value = parseInt(data, 10) / 1800;
            callback(resp);
        }
    },

    writeGPIOEdge : function(pin, mode) {
        fs.writeFileSync('/sys/class/gpio/gpio' + pin.gpio + '/edge', mode);

        var resp = {};
        resp.gpioFile = '/sys/class/gpio/gpio' + pin.gpio + '/value';
        resp.valuefd = fs.openSync(resp.gpioFile, 'r');
        resp.value = new Buffer(1);

        return(resp);
    },

    writePWMFreqAndValue : function(pin, pwm, freq, value, resp, callback) {
        if(debug) winston.debug('hw.writePWMFreqAndValue(' + [pin.key,pwm,freq,value,resp] + ');');
        var path = pwmPrefix[pin.pwm.name];
        try {
            var period = Math.round( 1.0e9 / freq ); // period in ns
            var duty = Math.round( period * value );
            fs.writeFileSync(path+'/duty', 0);
            if(pwm.freq != freq) {
                fs.writeFileSync(path+'/period', period);
            }
            fs.writeFileSync(path+'/duty', duty);
        } catch(ex) {
            resp.err = 'error updating PWM freq and value: ' + path + ', ' + ex;
            winston.error(resp.err);
        }
        return(resp);
    },

    readEeproms : function(eeproms) {
        var boardName = fs.readFileSync(my.is_capemgr() + '/baseboard/board-name',
                'ascii');
        var version = fs.readFileSync(my.is_capemgr() + '/baseboard/revision',
                'ascii');
        var serialNumber = fs.readFileSync(my.is_capemgr() + '/baseboard/serial-number',
                'ascii');
        eeproms['/sys/bus/i2c/drivers/at24/1-0050/eeprom'] = {};
        eeproms['/sys/bus/i2c/drivers/at24/1-0050/eeprom'].boardName = boardName;
        eeproms['/sys/bus/i2c/drivers/at24/1-0050/eeprom'].version = version;
        eeproms['/sys/bus/i2c/drivers/at24/1-0050/eeprom'].serialNumber = serialNumber;
        return(eeproms);
    },

    readPlatform : function(platform) {
        platform.name = fs.readFileSync(my.is_capemgr() + '/baseboard/board-name',
            'ascii').trim();
        if(platform.name == 'A335BONE') platform.name = 'BeagleBone';
        if(platform.name == 'A335BNLT') platform.name = 'BeagleBone Black';
        platform.version = fs.readFileSync(my.is_capemgr() + '/baseboard/revision',
            'ascii').trim();
        if(!platform.version.match(/^[\040-\176]*$/)) delete platform.version;
        platform.serialNumber = fs.readFileSync(my.is_capemgr() +
            '/baseboard/serial-number', 'ascii').trim();
        if(!platform.serialNumber.match(/^[\040-\176]*$/)) delete platform.serialNumber;
        return(platform);
    }
};
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import {
  Platform,
  TouchableHighlight,
  View,
  StyleSheet,
  TouchableNativeFeedback,
} from 'react-native';
import Color from 'color';

import getIconType from '../helpers/getIconType';
import getIconStyle from '../helpers/getIconStyle';
import { withTheme } from '../config';

const Icon = (props) => {
  const {
    type,
    name,
    size,
    color: colorProp,
    iconStyle,
    iconProps,
    underlayColor,
    reverse,
    raised,
    containerStyle,
    reverseColor: reverseColorProp,
    disabled,
    disabledStyle,
    onPress,
    Component = onPress
      ? Platform.select({
          android: TouchableNativeFeedback,
          default: TouchableHighlight,
        })
      : View,
    solid,
    brand,
    theme,
    ...attributes
  } = props;
  const color = colorProp || theme.colors.black;
  const reverseColor = reverseColor || theme.colors.white;

  const IconComponent = getIconType(type);
  const iconSpecificStyle = getIconStyle(type, { solid, brand });
  const getBackgroundColor = () => {
    if (reverse) {
      return color;
    }

    return raised ? theme.colors.white : 'transparent';
  };

  const buttonStyles = {
    borderRadius: size + 4,
    height: size * 2 + 4,
    width: size * 2 + 4,
  };

  if (Platform.OS === 'android' && !attributes.background) {
    if (Platform.Version >= 21) {
      attributes.background = TouchableNativeFeedback.Ripple(
        Color(color).alpha(0.2).rgb().string(),
        true
      );
    }
  }

  return (
    <View
      style={StyleSheet.flatten([
        styles.container,
        (reverse || raised) && styles.button,
        (reverse || raised) && buttonStyles,
        raised && styles.raised,
        iconStyle && iconStyle.borderRadius
          ? {
              borderRadius: iconStyle.borderRadius,
            }
          : {},
        containerStyle && containerStyle,
      ])}
    >
      <Component
        {...attributes}
        {...(onPress && {
          onPress,
          disabled,
          underlayColor: reverse ? color : underlayColor,
          activeOpacity: 0.3,
        })}
      >
        <View
          style={StyleSheet.flatten([
            (reverse || raised) && buttonStyles,
            {
              backgroundColor: getBackgroundColor(),
              alignItems: 'center',
              justifyContent: 'center',
            },
            disabled && styles.disabled,
            disabled && disabledStyle,
          ])}
        >
          <IconComponent
            testID="iconIcon"
            style={StyleSheet.flatten([
              { backgroundColor: 'transparent' },
              iconStyle && iconStyle,
            ])}
            size={size}
            name={name}
            color={reverse ? reverseColor : color}
            {...iconSpecificStyle}
            {...iconProps}
          />
        </View>
      </Component>
    </View>
  );
};

Icon.propTypes = {
  type: PropTypes.string,
  name: PropTypes.string,
  size: PropTypes.number,
  color: PropTypes.string,
  Component: PropTypes.elementType,
  underlayColor: PropTypes.string,
  reverse: PropTypes.bool,
  raised: PropTypes.bool,
  containerStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
  iconStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
  onPress: PropTypes.func,
  reverseColor: PropTypes.string,
  disabled: PropTypes.bool,
  disabledStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
  solid: PropTypes.bool,
  brand: PropTypes.bool,
};

Icon.defaultProps = {
  underlayColor: 'transparent',
  reverse: false,
  raised: false,
  size: 24,
  disabled: false,
  type: 'material',
  solid: false,
  brand: false,
};

const styles = StyleSheet.create({
  container: {
    overflow: 'hidden',
  },
  button: {
    margin: 7,
  },
  raised: {
    ...Platform.select({
      android: {
        elevation: 2,
      },
      default: {
        shadowColor: 'rgba(0,0,0, .4)',
        shadowOffset: { height: 1, width: 1 },
        shadowOpacity: 1,
        shadowRadius: 1,
      },
    }),
  },
  disabled: {
    backgroundColor: '#D1D5D8',
  },
});

export { Icon };
export default withTheme(Icon, 'Icon');
<commit_msg>Cannot access 'reverseColor' before initialization issue fixed. (#2545)<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import {
  Platform,
  TouchableHighlight,
  View,
  StyleSheet,
  TouchableNativeFeedback,
} from 'react-native';
import Color from 'color';

import getIconType from '../helpers/getIconType';
import getIconStyle from '../helpers/getIconStyle';
import { withTheme } from '../config';

const Icon = (props) => {
  const {
    type,
    name,
    size,
    color: colorProp,
    iconStyle,
    iconProps,
    underlayColor,
    reverse,
    raised,
    containerStyle,
    reverseColor: reverseColorProp,
    disabled,
    disabledStyle,
    onPress,
    Component = onPress
      ? Platform.select({
          android: TouchableNativeFeedback,
          default: TouchableHighlight,
        })
      : View,
    solid,
    brand,
    theme,
    ...attributes
  } = props;
  const color = colorProp || theme.colors.black;
  const reverseColor = reverseColorProp || theme.colors.white;

  const IconComponent = getIconType(type);
  const iconSpecificStyle = getIconStyle(type, { solid, brand });
  const getBackgroundColor = () => {
    if (reverse) {
      return color;
    }

    return raised ? theme.colors.white : 'transparent';
  };

  const buttonStyles = {
    borderRadius: size + 4,
    height: size * 2 + 4,
    width: size * 2 + 4,
  };

  if (Platform.OS === 'android' && !attributes.background) {
    if (Platform.Version >= 21) {
      attributes.background = TouchableNativeFeedback.Ripple(
        Color(color).alpha(0.2).rgb().string(),
        true
      );
    }
  }

  return (
    <View
      style={StyleSheet.flatten([
        styles.container,
        (reverse || raised) && styles.button,
        (reverse || raised) && buttonStyles,
        raised && styles.raised,
        iconStyle && iconStyle.borderRadius
          ? {
              borderRadius: iconStyle.borderRadius,
            }
          : {},
        containerStyle && containerStyle,
      ])}
    >
      <Component
        {...attributes}
        {...(onPress && {
          onPress,
          disabled,
          underlayColor: reverse ? color : underlayColor,
          activeOpacity: 0.3,
        })}
      >
        <View
          style={StyleSheet.flatten([
            (reverse || raised) && buttonStyles,
            {
              backgroundColor: getBackgroundColor(),
              alignItems: 'center',
              justifyContent: 'center',
            },
            disabled && styles.disabled,
            disabled && disabledStyle,
          ])}
        >
          <IconComponent
            testID="iconIcon"
            style={StyleSheet.flatten([
              { backgroundColor: 'transparent' },
              iconStyle && iconStyle,
            ])}
            size={size}
            name={name}
            color={reverse ? reverseColor : color}
            {...iconSpecificStyle}
            {...iconProps}
          />
        </View>
      </Component>
    </View>
  );
};

Icon.propTypes = {
  type: PropTypes.string,
  name: PropTypes.string,
  size: PropTypes.number,
  color: PropTypes.string,
  Component: PropTypes.elementType,
  underlayColor: PropTypes.string,
  reverse: PropTypes.bool,
  raised: PropTypes.bool,
  containerStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
  iconStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
  onPress: PropTypes.func,
  reverseColor: PropTypes.string,
  disabled: PropTypes.bool,
  disabledStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
  solid: PropTypes.bool,
  brand: PropTypes.bool,
};

Icon.defaultProps = {
  underlayColor: 'transparent',
  reverse: false,
  raised: false,
  size: 24,
  disabled: false,
  type: 'material',
  solid: false,
  brand: false,
};

const styles = StyleSheet.create({
  container: {
    overflow: 'hidden',
  },
  button: {
    margin: 7,
  },
  raised: {
    ...Platform.select({
      android: {
        elevation: 2,
      },
      default: {
        shadowColor: 'rgba(0,0,0, .4)',
        shadowOffset: { height: 1, width: 1 },
        shadowOpacity: 1,
        shadowRadius: 1,
      },
    }),
  },
  disabled: {
    backgroundColor: '#D1D5D8',
  },
});

export { Icon };
export default withTheme(Icon, 'Icon');
<|endoftext|>


<commit_before>const {Plugin, PluginKey} = require("prosemirror-state")

// ::- Input rules are regular expressions describing a piece of text
// that, when typed, causes something to happen. This might be
// changing two dashes into an emdash, wrapping a paragraph starting
// with `"> "` into a blockquote, or something entirely different.
class InputRule {
  // :: (RegExp, union<string, (state: EditorState, match: [string], start: number, end: number) → ?Transaction>)
  // Create an input rule. The rule applies when the user typed
  // something and the text directly in front of the cursor matches
  // `match`, which should probably end with `$`.
  //
  // The `handler` can be a string, in which case the matched text, or
  // the first matched group in the regexp, simply be replaced by that
  // string.
  //
  // Or a it can be a function, which will be called with the match
  // array produced by
  // [`RegExp.exec`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec),
  // as well as the start and end of the matched range, and which can
  // return a [transaction](#state.Transaction) that describes the
  // rule's effect, or null to indicate the input was not handled.
  constructor(match, handler) {
    this.match = match
    this.handler = typeof handler == "string" ? stringHandler(handler) : handler
  }
}
exports.InputRule = InputRule

function stringHandler(string) {
  return function(state, match, start, end) {
    let insert = string
    if (match[1]) {
      let offset = match[0].lastIndexOf(match[1])
      insert += match[0].slice(offset + match[1].length)
      start += offset
      let cutOff = start - end
      if (cutOff > 0) {
        insert = match[0].slice(offset - cutOff, offset) + insert
        start = end
      }
    }
    let marks = state.doc.resolve(start).marks()
    return state.tr.replaceWith(start, end, state.schema.text(insert, marks))
  }
}

const MAX_MATCH = 100

const stateKey = new PluginKey("fromInputRule")

// :: (config: {rules: [InputRule]}) → Plugin
// Create an input rules plugin. When enabled, it will cause text
// input that matches any of the given rules to trigger the rule's
// action, and binds the backspace key, when applied directly after an
// input rule triggered, to undo the rule's effect.
function inputRules({rules}) {
  return new Plugin({
    state: {
      init() { return null },
      apply(tr, prev) {
        let stored = tr.get(stateKey)
        if (stored) return stored
        return tr.selectionSet || tr.steps.length ? null : prev
      }
    },

    props: {
      handleTextInput(view, from, to, text) {
        let state = view.state, $from = state.doc.resolve(from)
        let textBefore = $from.parent.textBetween(Math.max(0, $from.parentOffset - MAX_MATCH), $from.parentOffset,
                                                  null, "\ufffc") + text
        for (let i = 0; i < rules.length; i++) {
          let match = rules[i].match.exec(textBefore)
          let tr = match && rules[i].handler(state, match, from - (match[0].length - text.length), to)
          if (!tr) continue
          view.dispatch(tr.set(stateKey, {transform: tr, from, to, text}))
          return true
        }
        return false
      },

      handleKeyDown(view, event) {
        if (event.keyCode == 8) return maybeUndoInputRule(view.state, view.dispatch, this.getState(view.state))
        return false
      }
    }
  })
}
exports.inputRules = inputRules

function maybeUndoInputRule(state, dispatch, undoable) {
  if (!undoable) return false
  let tr = state.tr, toUndo = undoable.transform
  for (let i = toUndo.steps.length - 1; i >= 0; i--)
    tr.step(toUndo.steps[i].invert(toUndo.docs[i]))
  let marks = tr.doc.resolve(undoable.from).marks()
  dispatch(tr.replaceWith(undoable.from, undoable.to, state.schema.text(undoable.text, marks)))
  return true
}
<commit_msg>Use Transaction.docChanged accessor<commit_after>const {Plugin, PluginKey} = require("prosemirror-state")

// ::- Input rules are regular expressions describing a piece of text
// that, when typed, causes something to happen. This might be
// changing two dashes into an emdash, wrapping a paragraph starting
// with `"> "` into a blockquote, or something entirely different.
class InputRule {
  // :: (RegExp, union<string, (state: EditorState, match: [string], start: number, end: number) → ?Transaction>)
  // Create an input rule. The rule applies when the user typed
  // something and the text directly in front of the cursor matches
  // `match`, which should probably end with `$`.
  //
  // The `handler` can be a string, in which case the matched text, or
  // the first matched group in the regexp, simply be replaced by that
  // string.
  //
  // Or a it can be a function, which will be called with the match
  // array produced by
  // [`RegExp.exec`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec),
  // as well as the start and end of the matched range, and which can
  // return a [transaction](#state.Transaction) that describes the
  // rule's effect, or null to indicate the input was not handled.
  constructor(match, handler) {
    this.match = match
    this.handler = typeof handler == "string" ? stringHandler(handler) : handler
  }
}
exports.InputRule = InputRule

function stringHandler(string) {
  return function(state, match, start, end) {
    let insert = string
    if (match[1]) {
      let offset = match[0].lastIndexOf(match[1])
      insert += match[0].slice(offset + match[1].length)
      start += offset
      let cutOff = start - end
      if (cutOff > 0) {
        insert = match[0].slice(offset - cutOff, offset) + insert
        start = end
      }
    }
    let marks = state.doc.resolve(start).marks()
    return state.tr.replaceWith(start, end, state.schema.text(insert, marks))
  }
}

const MAX_MATCH = 100

const stateKey = new PluginKey("fromInputRule")

// :: (config: {rules: [InputRule]}) → Plugin
// Create an input rules plugin. When enabled, it will cause text
// input that matches any of the given rules to trigger the rule's
// action, and binds the backspace key, when applied directly after an
// input rule triggered, to undo the rule's effect.
function inputRules({rules}) {
  return new Plugin({
    state: {
      init() { return null },
      apply(tr, prev) {
        let stored = tr.get(stateKey)
        if (stored) return stored
        return tr.selectionSet || tr.docChanged ? null : prev
      }
    },

    props: {
      handleTextInput(view, from, to, text) {
        let state = view.state, $from = state.doc.resolve(from)
        let textBefore = $from.parent.textBetween(Math.max(0, $from.parentOffset - MAX_MATCH), $from.parentOffset,
                                                  null, "\ufffc") + text
        for (let i = 0; i < rules.length; i++) {
          let match = rules[i].match.exec(textBefore)
          let tr = match && rules[i].handler(state, match, from - (match[0].length - text.length), to)
          if (!tr) continue
          view.dispatch(tr.set(stateKey, {transform: tr, from, to, text}))
          return true
        }
        return false
      },

      handleKeyDown(view, event) {
        if (event.keyCode == 8) return maybeUndoInputRule(view.state, view.dispatch, this.getState(view.state))
        return false
      }
    }
  })
}
exports.inputRules = inputRules

function maybeUndoInputRule(state, dispatch, undoable) {
  if (!undoable) return false
  let tr = state.tr, toUndo = undoable.transform
  for (let i = toUndo.steps.length - 1; i >= 0; i--)
    tr.step(toUndo.steps[i].invert(toUndo.docs[i]))
  let marks = tr.doc.resolve(undoable.from).marks()
  dispatch(tr.replaceWith(undoable.from, undoable.to, state.schema.text(undoable.text, marks)))
  return true
}
<|endoftext|>


<commit_before>/*#ifndef(UMD)*/
"use strict";
/*global _gpfAttributesAdd*/ // Shortcut for gpf.attributes.add
/*global _gpfDefAttr*/ // gpf.define for attributes
/*global _gpfDefine*/ // Shortcut for gpf.define
/*global _gpfEmptyFunc*/ // An empty function
/*global _gpfErrorDeclare*/ // Declare new gpf.Error names
/*global _gpfGenDefHandler*/ // Class handler for class types (interfaces...)
/*global _gpfGetClassDefinition*/ // Get GPF class definition for a constructor
/*global _gpfIgnore*/ // Helper to remove unused parameter warning
/*exported _gpfDefIntrf*/ // gpf.define for interfaces
/*exported _gpfI*/ // gpf.interfaces
/*#endif*/

_gpfErrorDeclare("interfaces", {
    interfaceExpected:
        "Expected interface not implemented: {name}"
});

/**
 * Verify that the object implements the current interface
 *
 * @param {Object} inspectedObject object (or class) to inspect
 * @param {gpf.interfaces.Interface} interfaceDefinition reference interface
 * @return {Boolean}
 */
function _gpfIsImplementedBy (inspectedObject, interfaceDefinition) {
    var member,
        memberReference,
        memberValue,
        memberType;
    /*
     * IMPORTANT note: we test the object itself (i.e. own members and the prototype).
     * That's why the hasOwnProperty is skipped
     */
    /*jslint forin:true*/
    for (member in interfaceDefinition.prototype) {
        if ("constructor" === member) { // Object
            continue;
        }
        memberReference = interfaceDefinition.prototype[member];
        memberValue = inspectedObject[member];
        memberType = typeof memberValue;
        if (typeof memberReference !== memberType) {
            return false;
        }
        if ("function" === memberType && memberReference.length !== memberValue.length) {
            return false;
        }
    }
    return true;
}

var
    /**
     * gpf.attributes shortcut
     *
     * @type {Object}
     * @private
     */
    _gpfI = gpf.interfaces = {

        /**
         * Verify that the object (or class) implements the current interface
         *
         * @param {Object|Function} inspectedObject object (or class) to inspect
         * @param {gpf.interfaces.Interface} interfaceDefinition reference interface
         * @return {Boolean}
         */
        isImplementedBy: function (inspectedObject, interfaceDefinition) {
            if (inspectedObject instanceof Function) {
                inspectedObject = inspectedObject.prototype;
            }
            return _gpfIsImplementedBy(inspectedObject, interfaceDefinition);
        },

        /**
         * Retrieve an object implementing the expected interface from an object.
         * This is done in two tests:
         * - Either the object implements the interface, it is returned
         * - Or the object implements IUnknown, then queryInterface is used
         *
         * @param {Object} objectInstance object to inspect
         * @param {gpf.interfaces.Interface} interfaceDefinition reference interface
         * @param {Boolean} [throwError=true] throwError Throws an error if the interface is not found (otherwise, null
         * is returned)
         * @return {Object|null}
         */
        query: function (objectInstance, interfaceDefinition, throwError) {
            var result = null;
            if (_gpfIsImplementedBy(objectInstance, interfaceDefinition)) {
                return objectInstance;
            } else if (_gpfIsImplementedBy(objectInstance, gpf.interfaces.IUnknown)) {
                result = objectInstance.queryInterface(interfaceDefinition);
            }
            if (null === result && (undefined === throwError || throwError)) {
                throw gpf.Error.interfaceExpected({
                    name: _gpfGetClassDefinition(interfaceDefinition)._name
                });
            }
            return result;
        }

    };

/**
 * Defines an interface (relies on gpf.define)
 *
 * @param {String} name Interface name. If it contains a dot, it is treated as absolute contextual.
 * Otherwise, it is relative to "gpf.interfaces"
 * @param {Function|string} [base=undefined] base Base interface (or contextual name)
 * @param {Object} [definition=undefined] definition Interface definition
 * @return {Function}
 */
var _gpfDefIntrf = _gpfGenDefHandler("gpf.interfaces", "Interface");

/**
 * Base class for any interface
 *
 * @class gpf.interfaces.Interface
 */
_gpfDefine("gpf.interfaces.Interface", Object, {});

//region IEventTarget

/* istanbul ignore next */ // Interface
_gpfDefIntrf("IEventDispatcher", {

    /**
     * Add an event listener to the dispatcher
     *
     * @param {String} event name
     * @param {gpf.events.Handler} eventsHandler
     * @return {gpf.interfaces.IEventDispatcher}
     * @chainable
     */
    addEventListener: function (event, eventsHandler) {
        _gpfIgnore(event, eventsHandler);
        return this;
    },

    /**
     * Remove an event listener from the dispatcher
     *
     * @param {String} event name
     * @param {gpf.events.Handler} eventsHandler
     * @return {gpf.interfaces.IEventDispatcher}
     * @chainable
     */
    removeEventListener: function (event, eventsHandler) {
        _gpfIgnore(event, eventsHandler);
        return this;
    },

    /**
     * Broadcast the event
     *
     * @param {String|gpf.events.Event} event name or object
     * @param {Object} [params={}] event parameters
     * @return {gpf.events.Event}
     */
    dispatchEvent: function (event, params) {
        _gpfIgnore(event, params);
    }

});

//endregion

//region IUnknown

/* istanbul ignore next */ // Interface
/**
 * Provide a way for any object to implement an interface using an intermediate object (this avoids overloading the
 * object with temporary / useless members)
 */
var _gpfIUnknown = _gpfDefIntrf("IUnknown", {

    /**
     * Retrieves an object supporting the provided interface (maybe the object itself)
     *
     * @param {gpf.interfaces.Interface} interfaceDefinition The expected interface
     * @return {Object|null} The object supporting the interface (or null)
     */
    queryInterface: function (interfaceDefinition) {
        _gpfIgnore(interfaceDefinition);
        return null;
    }

});

//endregion

//region InterfaceImplement attribute

/**
 * Retrieves an object supporting the provided interface (maybe the object itself).
 * This function (added to any object declaring the attribute InterfaceImplementAttribute with a builder) uses the
 * InterfaceImplementAttribute attribute list to see if the one corresponding to the interface provides a builder and
 * calls it
 *
 * @param {gpf.interfaces.Interface} interfaceDefinition The expected interface
 * @return {Object|null} The object supporting the interface (or null)
 */
function _queryInterface (interfaceDefinition) {
    /*jshint validthis:true*/ // Called with apply
    var
        array = (new gpf.attributes.Map(this))
            .getMemberAttributes("Class")
            .filter(gpf.attributes.InterfaceImplementAttribute),
        builder;
    if (!array.every(function (attribute) {
        builder = attribute._builder;
        return attribute._interfaceDefinition !== interfaceDefinition || !builder;
    })) {
        if ("function" === typeof builder) {
            return builder(this);
        }
        // Expects a member name
        return this[builder]();
    }
    // Otherwise
    return null;
}

/**
 * Creates a wrapper calling _queryInterface and, if no result is built, the original one defined in the object
 * prototype
 *
 * @param {Function} orgQueryInterface
 * @closure
 */
function _wrapQueryInterface (orgQueryInterface) {
    /**
     * @inheritdoc gpf.interfaces.IUnknown#queryInterface
     * @this
     */
    return function (interfaceDefinition) {
        _gpfIgnore(interfaceDefinition);
        var result = _queryInterface.apply(this, arguments);
        if (null === result) {
            result = orgQueryInterface.apply(this, arguments);
        }
        return result;
    };
}

/**
 * Document the class by telling the interface is implemented and may provide a builder to access it
 *
 * @param {Function} interfaceDefinition Implemented interface definition
 * @param {Function|String} [queryInterfaceBuilder=undefined] queryInterfaceBuilder Function or member name to executed
 * if the implemented interface is requested
 *
 * @class gpf.attributes.InterfaceImplementAttribute
 * @extends gpf.attributes.ClassAttribute
 * @alias gpf.$InterfaceImplement
 */
_gpfDefAttr("$InterfaceImplement", {
    "-": {

        // Interface definition
        "[_interfaceDefinition]": [gpf.$ClassProperty(false)],
        _interfaceDefinition: _gpfEmptyFunc,

        /**
         * Builder function
         *
         * @type {Function|null}
         */
        "[_builder]": [gpf.$ClassProperty(false)],
        _builder: null,

        _addMissingInterfaceMembers: function (objPrototype) {
            var
                iProto = this._interfaceDefinition.prototype,
                member;
            // Fill the missing methods
            /*jslint forin:true*/ // Wants inherited members too
            for (member in iProto) {
                if (!(member in objPrototype)) {
                    objPrototype[member] = iProto[member];
                }
            }
        },

        _addQueryInterface: function (objPrototype) {
            if (undefined === objPrototype.queryInterface) {
                objPrototype.queryInterface = _queryInterface;
                _gpfAttributesAdd(objPrototype.constructor, "Class", [gpf.$InterfaceImplement(_gpfIUnknown)]);
            } else if (_queryInterface !== objPrototype.queryInterface) {
                /*
                 * Two situations here:
                 * - Either the class (or one of its parent) already owns the $InterfaceImplement attribute
                 * - Or the class (or one of its parent) implements its own queryInterface
                 * In that last case, wrap it to use the attribute version first
                 *
                 * In both case, we take the assumption that the class already owns
                 * gpf.$InterfaceImplement(gpf.interfaces.IUnknown)
                 */
                objPrototype.queryInterface = _wrapQueryInterface(objPrototype.queryInterface);
            }
        }

    },
    "#": {

        // @inheritdoc gpf.attributes.Attribute#_alterPrototype
        _alterPrototype: function (objPrototype) {
            if (this._builder) {
                this._addQueryInterface(objPrototype);
            } else {
                this._addMissingInterfaceMembers(objPrototype);
                // Get the interface's attributes apply them to the obj
                new gpf.attributes.Map()
                    .fillFromClassDef(_gpfGetClassDefinition(this._interfaceDefinition))
                    .forEach(function (attributes, member) {
                        _gpfAttributesAdd(objPrototype.constructor, member, attributes);
                    });
            }
        }

    },

    "+": {

        /**
         * @param {Function} interfaceDefinition Interface definition
         * @param {Function|null} [queryInterfaceBuilder=null]
         * queryInterfaceBuilder Builder function
         * @constructor
         */
        constructor: function (interfaceDefinition, queryInterfaceBuilder) {
            this._interfaceDefinition = interfaceDefinition;
            if (queryInterfaceBuilder) {
                this._builder = queryInterfaceBuilder;
            }
        }

    }

});

//endregion
<commit_msg>Exposed internal<commit_after>/*#ifndef(UMD)*/
"use strict";
/*global _gpfAttributesAdd*/ // Shortcut for gpf.attributes.add
/*global _gpfDefAttr*/ // gpf.define for attributes
/*global _gpfDefine*/ // Shortcut for gpf.define
/*global _gpfEmptyFunc*/ // An empty function
/*global _gpfErrorDeclare*/ // Declare new gpf.Error names
/*global _gpfGenDefHandler*/ // Class handler for class types (interfaces...)
/*global _gpfGetClassDefinition*/ // Get GPF class definition for a constructor
/*global _gpfIgnore*/ // Helper to remove unused parameter warning
/*exported _gpfDefIntrf*/ // gpf.define for interfaces
/*exported _gpfI*/ // gpf.interfaces
/*exported _gpfQueryInterface*/ // gpf.interfaces.query
/*#endif*/

_gpfErrorDeclare("interfaces", {
    interfaceExpected:
        "Expected interface not implemented: {name}"
});

/**
 * Verify that the object implements the current interface
 *
 * @param {Object} inspectedObject object (or class) to inspect
 * @param {gpf.interfaces.Interface} interfaceDefinition reference interface
 * @return {Boolean}
 */
function _gpfIsImplementedBy (inspectedObject, interfaceDefinition) {
    var member,
        memberReference,
        memberValue,
        memberType;
    /*
     * IMPORTANT note: we test the object itself (i.e. own members and the prototype).
     * That's why the hasOwnProperty is skipped
     */
    /*jslint forin:true*/
    for (member in interfaceDefinition.prototype) {
        if ("constructor" === member) { // Object
            continue;
        }
        memberReference = interfaceDefinition.prototype[member];
        memberValue = inspectedObject[member];
        memberType = typeof memberValue;
        if (typeof memberReference !== memberType) {
            return false;
        }
        if ("function" === memberType && memberReference.length !== memberValue.length) {
            return false;
        }
    }
    return true;
}

/**
 * Retrieve an object implementing the expected interface from an object.
 * This is done in two tests:
 * - Either the object implements the interface, it is returned
 * - Or the object implements IUnknown, then queryInterface is used
 *
 * @param {Object} objectInstance object to inspect
 * @param {gpf.interfaces.Interface} interfaceDefinition reference interface
 * @param {Boolean} [throwError=true] throwError Throws an error if the interface is not found (otherwise, null
 * is returned)
 * @return {Object|null}
 */
function _gpfQueryInterface (objectInstance, interfaceDefinition, throwError) {
    var result = null;
    if (_gpfIsImplementedBy(objectInstance, interfaceDefinition)) {
        return objectInstance;
    } else if (_gpfIsImplementedBy(objectInstance, gpf.interfaces.IUnknown)) {
        result = objectInstance.queryInterface(interfaceDefinition);
    }
    if (null === result && (undefined === throwError || throwError)) {
        throw gpf.Error.interfaceExpected({
            name: _gpfGetClassDefinition(interfaceDefinition)._name
        });
    }
    return result;
}

var
    /**
     * gpf.attributes shortcut
     *
     * @type {Object}
     * @private
     */
    _gpfI = gpf.interfaces = {

        /**
         * Verify that the object (or class) implements the current interface
         *
         * @param {Object|Function} inspectedObject object (or class) to inspect
         * @param {gpf.interfaces.Interface} interfaceDefinition reference interface
         * @return {Boolean}
         */
        isImplementedBy: function (inspectedObject, interfaceDefinition) {
            if (inspectedObject instanceof Function) {
                inspectedObject = inspectedObject.prototype;
            }
            return _gpfIsImplementedBy(inspectedObject, interfaceDefinition);
        },

        // @inheritdoc _gpfQueryInterface
        query: _gpfQueryInterface

    };

/**
 * Defines an interface (relies on gpf.define)
 *
 * @param {String} name Interface name. If it contains a dot, it is treated as absolute contextual.
 * Otherwise, it is relative to "gpf.interfaces"
 * @param {Function|string} [base=undefined] base Base interface (or contextual name)
 * @param {Object} [definition=undefined] definition Interface definition
 * @return {Function}
 */
var _gpfDefIntrf = _gpfGenDefHandler("gpf.interfaces", "Interface");

/**
 * Base class for any interface
 *
 * @class gpf.interfaces.Interface
 */
_gpfDefine("gpf.interfaces.Interface", Object, {});

//region IEventTarget

/* istanbul ignore next */ // Interface
_gpfDefIntrf("IEventDispatcher", {

    /**
     * Add an event listener to the dispatcher
     *
     * @param {String} event name
     * @param {gpf.events.Handler} eventsHandler
     * @return {gpf.interfaces.IEventDispatcher}
     * @chainable
     */
    addEventListener: function (event, eventsHandler) {
        _gpfIgnore(event, eventsHandler);
        return this;
    },

    /**
     * Remove an event listener from the dispatcher
     *
     * @param {String} event name
     * @param {gpf.events.Handler} eventsHandler
     * @return {gpf.interfaces.IEventDispatcher}
     * @chainable
     */
    removeEventListener: function (event, eventsHandler) {
        _gpfIgnore(event, eventsHandler);
        return this;
    },

    /**
     * Broadcast the event
     *
     * @param {String|gpf.events.Event} event name or object
     * @param {Object} [params={}] event parameters
     * @return {gpf.events.Event}
     */
    dispatchEvent: function (event, params) {
        _gpfIgnore(event, params);
    }

});

//endregion

//region IUnknown

/* istanbul ignore next */ // Interface
/**
 * Provide a way for any object to implement an interface using an intermediate object (this avoids overloading the
 * object with temporary / useless members)
 */
var _gpfIUnknown = _gpfDefIntrf("IUnknown", {

    /**
     * Retrieves an object supporting the provided interface (maybe the object itself)
     *
     * @param {gpf.interfaces.Interface} interfaceDefinition The expected interface
     * @return {Object|null} The object supporting the interface (or null)
     */
    queryInterface: function (interfaceDefinition) {
        _gpfIgnore(interfaceDefinition);
        return null;
    }

});

//endregion

//region InterfaceImplement attribute

/**
 * Retrieves an object supporting the provided interface (maybe the object itself).
 * This function (added to any object declaring the attribute InterfaceImplementAttribute with a builder) uses the
 * InterfaceImplementAttribute attribute list to see if the one corresponding to the interface provides a builder and
 * calls it
 *
 * @param {gpf.interfaces.Interface} interfaceDefinition The expected interface
 * @return {Object|null} The object supporting the interface (or null)
 */
function _queryInterface (interfaceDefinition) {
    /*jshint validthis:true*/ // Called with apply
    var
        array = (new gpf.attributes.Map(this))
            .getMemberAttributes("Class")
            .filter(gpf.attributes.InterfaceImplementAttribute),
        builder;
    if (!array.every(function (attribute) {
        builder = attribute._builder;
        return attribute._interfaceDefinition !== interfaceDefinition || !builder;
    })) {
        if ("function" === typeof builder) {
            return builder(this);
        }
        // Expects a member name
        return this[builder]();
    }
    // Otherwise
    return null;
}

/**
 * Creates a wrapper calling _queryInterface and, if no result is built, the original one defined in the object
 * prototype
 *
 * @param {Function} orgQueryInterface
 * @closure
 */
function _wrapQueryInterface (orgQueryInterface) {
    /**
     * @inheritdoc gpf.interfaces.IUnknown#queryInterface
     * @this
     */
    return function (interfaceDefinition) {
        _gpfIgnore(interfaceDefinition);
        var result = _queryInterface.apply(this, arguments);
        if (null === result) {
            result = orgQueryInterface.apply(this, arguments);
        }
        return result;
    };
}

/**
 * Document the class by telling the interface is implemented and may provide a builder to access it
 *
 * @param {Function} interfaceDefinition Implemented interface definition
 * @param {Function|String} [queryInterfaceBuilder=undefined] queryInterfaceBuilder Function or member name to executed
 * if the implemented interface is requested
 *
 * @class gpf.attributes.InterfaceImplementAttribute
 * @extends gpf.attributes.ClassAttribute
 * @alias gpf.$InterfaceImplement
 */
_gpfDefAttr("$InterfaceImplement", {
    "-": {

        // Interface definition
        "[_interfaceDefinition]": [gpf.$ClassProperty(false)],
        _interfaceDefinition: _gpfEmptyFunc,

        /**
         * Builder function
         *
         * @type {Function|null}
         */
        "[_builder]": [gpf.$ClassProperty(false)],
        _builder: null,

        _addMissingInterfaceMembers: function (objPrototype) {
            var
                iProto = this._interfaceDefinition.prototype,
                member;
            // Fill the missing methods
            /*jslint forin:true*/ // Wants inherited members too
            for (member in iProto) {
                if (!(member in objPrototype)) {
                    objPrototype[member] = iProto[member];
                }
            }
        },

        _addQueryInterface: function (objPrototype) {
            if (undefined === objPrototype.queryInterface) {
                objPrototype.queryInterface = _queryInterface;
                _gpfAttributesAdd(objPrototype.constructor, "Class", [gpf.$InterfaceImplement(_gpfIUnknown)]);
            } else if (_queryInterface !== objPrototype.queryInterface) {
                /*
                 * Two situations here:
                 * - Either the class (or one of its parent) already owns the $InterfaceImplement attribute
                 * - Or the class (or one of its parent) implements its own queryInterface
                 * In that last case, wrap it to use the attribute version first
                 *
                 * In both case, we take the assumption that the class already owns
                 * gpf.$InterfaceImplement(gpf.interfaces.IUnknown)
                 */
                objPrototype.queryInterface = _wrapQueryInterface(objPrototype.queryInterface);
            }
        }

    },
    "#": {

        // @inheritdoc gpf.attributes.Attribute#_alterPrototype
        _alterPrototype: function (objPrototype) {
            if (this._builder) {
                this._addQueryInterface(objPrototype);
            } else {
                this._addMissingInterfaceMembers(objPrototype);
                // Get the interface's attributes apply them to the obj
                new gpf.attributes.Map()
                    .fillFromClassDef(_gpfGetClassDefinition(this._interfaceDefinition))
                    .forEach(function (attributes, member) {
                        _gpfAttributesAdd(objPrototype.constructor, member, attributes);
                    });
            }
        }

    },

    "+": {

        /**
         * @param {Function} interfaceDefinition Interface definition
         * @param {Function|null} [queryInterfaceBuilder=null]
         * queryInterfaceBuilder Builder function
         * @constructor
         */
        constructor: function (interfaceDefinition, queryInterfaceBuilder) {
            this._interfaceDefinition = interfaceDefinition;
            if (queryInterfaceBuilder) {
                this._builder = queryInterfaceBuilder;
            }
        }

    }

});

//endregion
<|endoftext|>