<commit_before>/*
 * biojs-io-biom
 * https://github.com/iimog/biojs-io-biom
 *
 * Copyright (c) 2016 Markus J. Ankenbrand
 * Licensed under the MIT license.
 */

/**
 * Version
 * @type {string} version of this module
 */
export const VERSION = '0.1.0';

/**
 * Default Biom Object for empty initialization
 * @type {{
 * id: null,
 * format: string,
 * format_url: string,
 * type: string,
 * generated_by: string,
 * date: Date,
 * rows: Array,
 * columns: Array,
 * matrix_type: string,
 * matrix_element_type: string,
 * shape: number[],
 * data: Array
 * }}
 */
export const DEFAULT_BIOM = {
    id: null,
    format: 'Biological Observation Matrix 1.0.0',
    format_url: 'http://biom-format.org',
    type: 'OTU table',
    generated_by: `biojs-io-biom v${VERSION}`,
    date: null, // will be set to Date.now() in the constructor
    rows: [],
    columns: [],
    matrix_type: 'sparse',
    matrix_element_type: 'float',
    shape: [0,0],
    data: [],
    comment: null
};

/**
 * Controlled vocabulary for the type field of biom objects
 * @type {string[]}
 */
export const TYPE_CV = [
    'OTU table',
    'Pathway table',
    'Function table',
    'Ortholog table',
    'Gene table',
    'Metabolite table',
    'Taxon table'
];

/**
 * Controlled vocabulary for the matrix_type field of biom objects
 * @type {string[]}
 */
export const MATRIX_TYPE_CV = [
    'sparse', // only non-zero values are specified
    'dense'   // every element must be specified
];

/**
 * Controlled vocabulary for the matrix_element_type field of biom objects
 * @type {string[]}
 */
export const MATRIX_ELEMENT_TYPE_CV = [
    'int',    // integer
    'float',  // floating point
    'unicode' // unicode string
];

/**
 * @class Biom
 */
export class Biom {
    /**
     * constructor
     * @param _id
     * @param _format
     * @param _format_url
     * @param _type
     * @param _generated_by
     * @param _date
     * @param _rows
     * @param _columns
     * @param _matrix_type
     * @param _matrix_element_type
     * @param _shape
     * @param _data
     * @param _comment
     */
    constructor({
        id: _id = null,
        format: _format = DEFAULT_BIOM.format,
        format_url: _format_url = DEFAULT_BIOM.format_url,
        type: _type = DEFAULT_BIOM.type,
        generated_by: _generated_by = DEFAULT_BIOM.generated_by,
        date: _date = null,
        rows: _rows = DEFAULT_BIOM.rows,
        columns: _columns = DEFAULT_BIOM.columns,
        matrix_type: _matrix_type = DEFAULT_BIOM.matrix_type,
        matrix_element_type:
            _matrix_element_type = DEFAULT_BIOM.matrix_element_type,
        shape: _shape = DEFAULT_BIOM.shape,
        data: _data = DEFAULT_BIOM.data,
        comment: _comment = DEFAULT_BIOM.comment
    } = {}){
        this.id = _id;
        this.format = _format;
        this.format_url = _format_url;
        this.type = _type;
        this.generated_by = _generated_by;
        if(_date === null){
            _date = new Date().toISOString();
        }
        this.date = _date;
        this.rows = _rows;
        this.columns = _columns;
        this.matrix_type = _matrix_type;
        this.matrix_element_type = _matrix_element_type;
        this.shape = _shape;
        this.data = _data;
        this._comment = _comment;
    }

    /**
     * Getter for id
     * @returns {string} - A field that can be used to id a table (or null)
     */
    get id(){
        return this._id;
    }

    /**
     * Setter for id
     * @param id {string} - A field that can be used to id a table (or null)
     * @throws {TypeError} if id is not a string (or null)
     */
    set id(id){
        if(id !== null && typeof id !== 'string'){
            throw new TypeError('id must be string or null');
        }
        this._id = id;
    }

    /**
     * Getter for format
     * @returns {string} - The name and version of the current biom format
     */
    get format(){
        return this._format;
    }

    /**
     * Setter for format
     * @param format {string} - The name and version of the current biom format
     * @throws {TypeError} if format is not a string
     */
    set format(format){
        if(typeof format !== 'string'){
            throw new TypeError('format must be string');
        }
        this._format = format;
    }

    /**
     * Getter for format_url
     * @returns {string} - A string with a static URL providing format details
     */
    get format_url(){
        return this._format_url;
    }

    /**
     * Setter for format_url
     * @param format_url {string} - A string with a static URL
     *                              providing format details
     *                              (not checked whether the string is an url)
     * @throws {TypeError} if format_url is not a string
     */
    set format_url(format_url){
        if(typeof format_url !== 'string'){
            throw new TypeError('format_url must be string' +
                ' (representing a static URL)');
        }
        this._format_url = format_url;
    }

    /**
     * Getter for type
     * @returns {string} - Table type (a controlled vocabulary)
     *                     Acceptable values:
     *                       'OTU table'
     *                       'Pathway table'
     *                       'Function table'
     *                       'Ortholog table'
     *                       'Gene table'
     *                       'Metabolite table'
     *                       'Taxon table'
     */
    get type(){
        return this._type;
    }

    /**
     * Setter for type
     * @param type {string} - Table type (a controlled vocabulary)
     *                        Acceptable values:
     *                          'OTU table'
     *                          'Pathway table'
     *                          'Function table'
     *                          'Ortholog table'
     *                          'Gene table'
     *                          'Metabolite table'
     *                          'Taxon table'
     * @throws {TypeError} if type is not a string
     * @throws {Error} if type is not in the controlled vocabulary
     */
    set type(type){
        if(typeof type !== 'string'){
            throw new TypeError('type must be string' +
                ' (part of the controlled vocabulary)');
        }
        if(TYPE_CV.indexOf(type) === -1){
            throw new Error('type must be part of the controlled vocabulary');
        }
        this._type = type;
    }

    /**
     * Getter for generated_by
     * @returns {string} - Package and revision that built the table
     */
    get generated_by(){
        return this._generated_by;
    }

    /**
     * Setter for generated_by
     * @param generated_by {string} - Package and revision that built the table
     * @throws {TypeError} if generated_by is not a string
     */
    set generated_by(generated_by){
        if(typeof generated_by !== 'string'){
            throw new TypeError('generated_by must be string');
        }
        this._generated_by = generated_by;
    }

    /**
     * Getter for date
     * @returns {string} - Date the table was built (ISO 8601 format)
     */
    get date(){
        return this._date;
    }

    /**
     * Setter for date
     * @param date {string} - Date the table was built (ISO 8601 format)
     *                       (not checked whether the string is a date)
     * @throws {TypeError} if date is not a string
     */
    set date(date){
        if(typeof date !== 'string'){
            throw new TypeError('date must be string (ISO 8601 format)');
        }
        this._date = date;
    }

    /**
     * Getter for rows
     * @returns {Array} - An ORDERED list of obj describing the rows
     */
    get rows(){
        return this._rows;
    }

    /**
     * Setter for rows
     * @param rows {Array} - An ORDERED list of obj describing the rows
     * @throws {TypeError} if date is not an Array
     */
    set rows(rows){
        if(Object.prototype.toString.call(rows) !== '[object Array]'){
            throw new TypeError('rows must be an Array');
        }
        this._rows = rows;
    }

    /**
     * Getter for columns
     * @returns {Array} - An ORDERED list of obj describing the columns
     */
    get columns() {
        return this._columns;
    }

    /**
     * Setter for columns
     * @param columns {Array} - An ORDERED list of obj describing the columns
     * @throws {TypeError} if date is not an Array
     */
    set columns(columns){
        if(Object.prototype.toString.call(columns) !== '[object Array]'){
            throw new TypeError('columns must be an Array');
        }
        this._columns = columns;
    }

    /**
     * Getter for matrix_type
     * @returns {string} - Type of matrix data representation
     *                     (a controlled vocabulary) Acceptable values:
     *                       'sparse' : only non-zero values are specified
     *                       'dense' : every element must be specified
     */
    get matrix_type(){
        return this._matrix_type;
    }

    /**
     * Setter for matrix_type
     * @param matrix_type {string} - Type of matrix data representation
     *                               (a controlled vocabulary)
     *                               Acceptable values:
     *                             'sparse' : only non-zero values are specified
     *                             'dense' : every element must be specified
     * @throws {TypeError} if matrix_type is not a string
     * @throws {Error} if matrix_type is not in the controlled vocabulary
     */
    set matrix_type(matrix_type){
        if(typeof matrix_type !== 'string'){
            throw new TypeError('matrix_type must be string' +
                ' (part of the controlled vocabulary: "dense" or "sparse")');
        }
        if(MATRIX_TYPE_CV.indexOf(matrix_type) === -1){
            throw new Error('matrix_type must be part of the' +
                ' controlled vocabulary: "dense" or "sparse"');
        }
        this._matrix_type = matrix_type;
    }

    /**
     * Getter for matrix_element_type
     * @returns {string} - Value type in matrix (a controlled vocabulary)
     *                     Acceptable values:
     *                       'int' : integer
     *                       'float' : floating point
     *                       'unicode' : unicode string
     */
    get matrix_element_type(){
        return this._matrix_element_type;
    }

    /**
     * Setter for matrix_element_type
     * @param matrix_element_type {string} - Value type in matrix
     *                               (a controlled vocabulary)
     *                               Acceptable values:
     *                                 'int' : integer
     *                                 'float' : floating point
     *                                 'unicode' : unicode string
     * @throws {TypeError} if matrix_element_type is not a string
     * @throws {Error} if matrix_element_type is not in controlled vocabulary
     */
    set matrix_element_type(matrix_element_type){
        if(typeof matrix_element_type !== 'string'){
            throw new TypeError('matrix_element_type must be string' +
                ' (part of the controlled vocabulary:' +
                ' "int", "float" or "unicode")');
        }
        if(MATRIX_ELEMENT_TYPE_CV.indexOf(matrix_element_type) === -1){
            throw new Error('matrix_element_type must be part of the' +
                ' controlled vocabulary: "int", "float" or "unicode"');
        }
        this._matrix_element_type = matrix_element_type;
    }

    /**
     * Getter for shape
     * @returns {Array} - the number of rows and number of columns in data
     */
    get shape(){
        return this._shape;
    }

    /**
     * Setter for shape
     * @param shape {Array} - the number of rows and number of columns in data
     * @throws {TypeError} if shape is not an Array
     * @throws {Error} if shape contains something other than
     *                 two non-negative integers
     */
    set shape(shape){
        if(Object.prototype.toString.call(shape) !== '[object Array]'){
            throw new TypeError('shape must be an Array containing' +
                ' exactly two non-negative integers');
        }
        if(shape.length !== 2){
            throw new Error('shape does not contain exactly two elements');
        }
        if(!Number.isInteger(shape[0]) ||
            shape[0] < 0 || !Number.isInteger(shape[1]) || shape[1] < 0){
            throw new Error('shape does not contain non-negative integers');
        }
        this._shape = shape;
    }

    /**
     * Getter for data
     * @returns {Array} - counts of observations by sample
     *                   if matrix_type is 'sparse', [[row, column, value],
     *                                                 [row, column, value],
     *                                                                  ...]
     *                   if matrix_type is 'dense', [[value, value, value, ...],
     *                                               [value, value, value, ...],
     *                                                                      ...]
     */
    get data(){
        return this._data;
    }

    /**
     * Setter for data
     * @param data {Array} - counts of observations by sample
     *                   if matrix_type is 'sparse', [[row, column, value],
     *                                                 [row, column, value],
     *                                                                  ...]
     *                   if matrix_type is 'dense', [[value, value, value, ...],
     *                                               [value, value, value, ...],
     *                                                                      ...]
     * @throws {TypeError} if date is not an Array
     */
    set data(data){
        if(Object.prototype.toString.call(data) !== '[object Array]'){
            throw new TypeError('data must be an Array');
        }
        this._data = data;
    }

    /**
     * Getter for comment
     * @returns {string} - A free text field containing any information that you
     *                     feel is relevant (or just feel like sharing)
     */
    get comment(){
        return this._comment;
    }

    /**
     * Setter for comment
     * @param comment {string} - A free text field containing any information that
     *                           you feel is relevant (or just feel like sharing)
     * @throws {TypeError} if comment is not a string (or null)
     */
    set comment(comment){
        if(comment !== null && typeof comment !== 'string'){
            throw new TypeError('comment must be string or null');
        }
        this._comment = comment;
    }
}
<commit_msg>Adjust type to string or null<commit_after>/*
 * biojs-io-biom
 * https://github.com/iimog/biojs-io-biom
 *
 * Copyright (c) 2016 Markus J. Ankenbrand
 * Licensed under the MIT license.
 */

/**
 * Version
 * @type {string} version of this module
 */
export const VERSION = '0.1.0';

/**
 * Default Biom Object for empty initialization
 * @type {{
 * id: null,
 * format: string,
 * format_url: string,
 * type: string,
 * generated_by: string,
 * date: Date,
 * rows: Array,
 * columns: Array,
 * matrix_type: string,
 * matrix_element_type: string,
 * shape: number[],
 * data: Array
 * }}
 */
export const DEFAULT_BIOM = {
    id: null,
    format: 'Biological Observation Matrix 1.0.0',
    format_url: 'http://biom-format.org',
    type: 'OTU table',
    generated_by: `biojs-io-biom v${VERSION}`,
    date: null, // will be set to Date.now() in the constructor
    rows: [],
    columns: [],
    matrix_type: 'sparse',
    matrix_element_type: 'float',
    shape: [0,0],
    data: [],
    comment: null
};

/**
 * Controlled vocabulary for the type field of biom objects
 * @type {string[]}
 */
export const TYPE_CV = [
    'OTU table',
    'Pathway table',
    'Function table',
    'Ortholog table',
    'Gene table',
    'Metabolite table',
    'Taxon table'
];

/**
 * Controlled vocabulary for the matrix_type field of biom objects
 * @type {string[]}
 */
export const MATRIX_TYPE_CV = [
    'sparse', // only non-zero values are specified
    'dense'   // every element must be specified
];

/**
 * Controlled vocabulary for the matrix_element_type field of biom objects
 * @type {string[]}
 */
export const MATRIX_ELEMENT_TYPE_CV = [
    'int',    // integer
    'float',  // floating point
    'unicode' // unicode string
];

/**
 * @class Biom
 */
export class Biom {
    /**
     * constructor
     * @param _id
     * @param _format
     * @param _format_url
     * @param _type
     * @param _generated_by
     * @param _date
     * @param _rows
     * @param _columns
     * @param _matrix_type
     * @param _matrix_element_type
     * @param _shape
     * @param _data
     * @param _comment
     */
    constructor({
        id: _id = null,
        format: _format = DEFAULT_BIOM.format,
        format_url: _format_url = DEFAULT_BIOM.format_url,
        type: _type = DEFAULT_BIOM.type,
        generated_by: _generated_by = DEFAULT_BIOM.generated_by,
        date: _date = null,
        rows: _rows = DEFAULT_BIOM.rows,
        columns: _columns = DEFAULT_BIOM.columns,
        matrix_type: _matrix_type = DEFAULT_BIOM.matrix_type,
        matrix_element_type:
            _matrix_element_type = DEFAULT_BIOM.matrix_element_type,
        shape: _shape = DEFAULT_BIOM.shape,
        data: _data = DEFAULT_BIOM.data,
        comment: _comment = DEFAULT_BIOM.comment
    } = {}){
        this.id = _id;
        this.format = _format;
        this.format_url = _format_url;
        this.type = _type;
        this.generated_by = _generated_by;
        if(_date === null){
            _date = new Date().toISOString();
        }
        this.date = _date;
        this.rows = _rows;
        this.columns = _columns;
        this.matrix_type = _matrix_type;
        this.matrix_element_type = _matrix_element_type;
        this.shape = _shape;
        this.data = _data;
        this.comment = _comment;
    }

    /**
     * Getter for id
     * @returns {string|null} - A field that can be used to id a table (or null)
     */
    get id(){
        return this._id;
    }

    /**
     * Setter for id
     * @param id {string|null} - A field that can be used to id a table (or null)
     * @throws {TypeError} if id is not a string (or null)
     */
    set id(id){
        if(id !== null && typeof id !== 'string'){
            throw new TypeError('id must be string or null');
        }
        this._id = id;
    }

    /**
     * Getter for format
     * @returns {string} - The name and version of the current biom format
     */
    get format(){
        return this._format;
    }

    /**
     * Setter for format
     * @param format {string} - The name and version of the current biom format
     * @throws {TypeError} if format is not a string
     */
    set format(format){
        if(typeof format !== 'string'){
            throw new TypeError('format must be string');
        }
        this._format = format;
    }

    /**
     * Getter for format_url
     * @returns {string} - A string with a static URL providing format details
     */
    get format_url(){
        return this._format_url;
    }

    /**
     * Setter for format_url
     * @param format_url {string} - A string with a static URL
     *                              providing format details
     *                              (not checked whether the string is an url)
     * @throws {TypeError} if format_url is not a string
     */
    set format_url(format_url){
        if(typeof format_url !== 'string'){
            throw new TypeError('format_url must be string' +
                ' (representing a static URL)');
        }
        this._format_url = format_url;
    }

    /**
     * Getter for type
     * @returns {string} - Table type (a controlled vocabulary)
     *                     Acceptable values:
     *                       'OTU table'
     *                       'Pathway table'
     *                       'Function table'
     *                       'Ortholog table'
     *                       'Gene table'
     *                       'Metabolite table'
     *                       'Taxon table'
     */
    get type(){
        return this._type;
    }

    /**
     * Setter for type
     * @param type {string} - Table type (a controlled vocabulary)
     *                        Acceptable values:
     *                          'OTU table'
     *                          'Pathway table'
     *                          'Function table'
     *                          'Ortholog table'
     *                          'Gene table'
     *                          'Metabolite table'
     *                          'Taxon table'
     * @throws {TypeError} if type is not a string
     * @throws {Error} if type is not in the controlled vocabulary
     */
    set type(type){
        if(typeof type !== 'string'){
            throw new TypeError('type must be string' +
                ' (part of the controlled vocabulary)');
        }
        if(TYPE_CV.indexOf(type) === -1){
            throw new Error('type must be part of the controlled vocabulary');
        }
        this._type = type;
    }

    /**
     * Getter for generated_by
     * @returns {string} - Package and revision that built the table
     */
    get generated_by(){
        return this._generated_by;
    }

    /**
     * Setter for generated_by
     * @param generated_by {string} - Package and revision that built the table
     * @throws {TypeError} if generated_by is not a string
     */
    set generated_by(generated_by){
        if(typeof generated_by !== 'string'){
            throw new TypeError('generated_by must be string');
        }
        this._generated_by = generated_by;
    }

    /**
     * Getter for date
     * @returns {string} - Date the table was built (ISO 8601 format)
     */
    get date(){
        return this._date;
    }

    /**
     * Setter for date
     * @param date {string} - Date the table was built (ISO 8601 format)
     *                       (not checked whether the string is a date)
     * @throws {TypeError} if date is not a string
     */
    set date(date){
        if(typeof date !== 'string'){
            throw new TypeError('date must be string (ISO 8601 format)');
        }
        this._date = date;
    }

    /**
     * Getter for rows
     * @returns {Array} - An ORDERED list of obj describing the rows
     */
    get rows(){
        return this._rows;
    }

    /**
     * Setter for rows
     * @param rows {Array} - An ORDERED list of obj describing the rows
     * @throws {TypeError} if date is not an Array
     */
    set rows(rows){
        if(Object.prototype.toString.call(rows) !== '[object Array]'){
            throw new TypeError('rows must be an Array');
        }
        this._rows = rows;
    }

    /**
     * Getter for columns
     * @returns {Array} - An ORDERED list of obj describing the columns
     */
    get columns() {
        return this._columns;
    }

    /**
     * Setter for columns
     * @param columns {Array} - An ORDERED list of obj describing the columns
     * @throws {TypeError} if date is not an Array
     */
    set columns(columns){
        if(Object.prototype.toString.call(columns) !== '[object Array]'){
            throw new TypeError('columns must be an Array');
        }
        this._columns = columns;
    }

    /**
     * Getter for matrix_type
     * @returns {string} - Type of matrix data representation
     *                     (a controlled vocabulary) Acceptable values:
     *                       'sparse' : only non-zero values are specified
     *                       'dense' : every element must be specified
     */
    get matrix_type(){
        return this._matrix_type;
    }

    /**
     * Setter for matrix_type
     * @param matrix_type {string} - Type of matrix data representation
     *                               (a controlled vocabulary)
     *                               Acceptable values:
     *                             'sparse' : only non-zero values are specified
     *                             'dense' : every element must be specified
     * @throws {TypeError} if matrix_type is not a string
     * @throws {Error} if matrix_type is not in the controlled vocabulary
     */
    set matrix_type(matrix_type){
        if(typeof matrix_type !== 'string'){
            throw new TypeError('matrix_type must be string' +
                ' (part of the controlled vocabulary: "dense" or "sparse")');
        }
        if(MATRIX_TYPE_CV.indexOf(matrix_type) === -1){
            throw new Error('matrix_type must be part of the' +
                ' controlled vocabulary: "dense" or "sparse"');
        }
        this._matrix_type = matrix_type;
    }

    /**
     * Getter for matrix_element_type
     * @returns {string} - Value type in matrix (a controlled vocabulary)
     *                     Acceptable values:
     *                       'int' : integer
     *                       'float' : floating point
     *                       'unicode' : unicode string
     */
    get matrix_element_type(){
        return this._matrix_element_type;
    }

    /**
     * Setter for matrix_element_type
     * @param matrix_element_type {string} - Value type in matrix
     *                               (a controlled vocabulary)
     *                               Acceptable values:
     *                                 'int' : integer
     *                                 'float' : floating point
     *                                 'unicode' : unicode string
     * @throws {TypeError} if matrix_element_type is not a string
     * @throws {Error} if matrix_element_type is not in controlled vocabulary
     */
    set matrix_element_type(matrix_element_type){
        if(typeof matrix_element_type !== 'string'){
            throw new TypeError('matrix_element_type must be string' +
                ' (part of the controlled vocabulary:' +
                ' "int", "float" or "unicode")');
        }
        if(MATRIX_ELEMENT_TYPE_CV.indexOf(matrix_element_type) === -1){
            throw new Error('matrix_element_type must be part of the' +
                ' controlled vocabulary: "int", "float" or "unicode"');
        }
        this._matrix_element_type = matrix_element_type;
    }

    /**
     * Getter for shape
     * @returns {Array} - the number of rows and number of columns in data
     */
    get shape(){
        return this._shape;
    }

    /**
     * Setter for shape
     * @param shape {Array} - the number of rows and number of columns in data
     * @throws {TypeError} if shape is not an Array
     * @throws {Error} if shape contains something other than
     *                 two non-negative integers
     */
    set shape(shape){
        if(Object.prototype.toString.call(shape) !== '[object Array]'){
            throw new TypeError('shape must be an Array containing' +
                ' exactly two non-negative integers');
        }
        if(shape.length !== 2){
            throw new Error('shape does not contain exactly two elements');
        }
        if(!Number.isInteger(shape[0]) ||
            shape[0] < 0 || !Number.isInteger(shape[1]) || shape[1] < 0){
            throw new Error('shape does not contain non-negative integers');
        }
        this._shape = shape;
    }

    /**
     * Getter for data
     * @returns {Array} - counts of observations by sample
     *                   if matrix_type is 'sparse', [[row, column, value],
     *                                                 [row, column, value],
     *                                                                  ...]
     *                   if matrix_type is 'dense', [[value, value, value, ...],
     *                                               [value, value, value, ...],
     *                                                                      ...]
     */
    get data(){
        return this._data;
    }

    /**
     * Setter for data
     * @param data {Array} - counts of observations by sample
     *                   if matrix_type is 'sparse', [[row, column, value],
     *                                                 [row, column, value],
     *                                                                  ...]
     *                   if matrix_type is 'dense', [[value, value, value, ...],
     *                                               [value, value, value, ...],
     *                                                                      ...]
     * @throws {TypeError} if date is not an Array
     */
    set data(data){
        if(Object.prototype.toString.call(data) !== '[object Array]'){
            throw new TypeError('data must be an Array');
        }
        this._data = data;
    }

    /**
     * Getter for comment
     * @returns {string|null} - A free text field containing any information that you
     *                     feel is relevant (or just feel like sharing)
     */
    get comment(){
        return this._comment;
    }

    /**
     * Setter for comment
     * @param comment {string|null} - A free text field containing any information that
     *                           you feel is relevant (or just feel like sharing)
     * @throws {TypeError} if comment is not a string (or null)
     */
    set comment(comment){
        if(comment !== null && typeof comment !== 'string'){
            throw new TypeError('comment must be string or null');
        }
        this._comment = comment;
    }
}
<|endoftext|>


<commit_before>/**
 * .reel Unit Tests
 */
(function($){

  module('Sprite', { teardown: function teardown(){
    $('.jquery-reel').trigger('teardown');
  }});

  asyncTest( 'Multi-row: Frame shifting (4 rows)', function(){
		expect(12);
    var
      selector= '#image',
      $reel= $(selector).reel({
        frames: 6,
        frame: 2,
        footage: 3,
        rows: 4,
        row: 1
      }),
      /*
      Sprite layout:

      A1 A2 A3                     1  2  3
      A4 A5 A6                     4  5  6
      B1 B2 B3                     7  8  9
      B4 B5 B6  in real frames »  10 11 12
      C1 C2 C3                    13 14 15
      C4 C5 C6                    16 17 18
      D1 D2 D3                    19 20 21
      D4 D5 D6                    22 23 24

      (letter ~ row; number ~ frame)
      */
      entries= {
        '1': [         0,           2,      '-276px 0px' ],
        '2': [    0.3333,           8,      '-276px -252px' ],
        '3': [    0.6667,          14,      '-276px -504px' ],
        '4': [         1,          20,      '-276px -756px' ]
      }
    $.each(entries, function(ix,it){
      $reel.trigger('rowChange', Number(ix));
      equal( $reel.data('row'), it[0], 'Row '+ix+': Interpolated row');
      equal( $reel.data('frame'), it[1], 'Row '+ix+': Shifted frame');
      equal( $reel.css('backgroundPosition'), it[2], 'Row '+ix+': Sprite CSS background position');
    });
    start();
  });

  asyncTest( 'Multi-row: Uneven rows frame shifting (3 rows)', function(){
		expect(9);
    var
      selector= '#image',
      $reel= $(selector).reel({
        frames: 6,
        footage: 4,
        frame: 5,
        rows: 3,
        row: 3
      }),
      /*
      Sprite layout:

      A1 A2 A3 A4                    1  2  3  4
      A5 A6 B1 B2                    5  6  7  8
      B3 B4 B5 B6  in real frames »  9 10 11 12
      C1 C2 C3 C4                   13 14 15 16
      C5 C6                         17 18

      (letter ~ row; number ~ frame)
      */
      entries= {
        '3': [         1,          17,      '0px -504px' ],
        '2': [       0.5,          11,      '-552px -252px' ],
        '1': [         0,           5,      '0px -126px' ]
      }
    $.each(entries, function(ix,it){
      $reel.trigger('rowChange', Number(ix));
      equal( $reel.data('row'), it[0], 'Row '+ix+': Interpolated row');
      equal( $reel.data('frame'), it[1], 'Row '+ix+': Shifted frame');
      equal( $reel.css('backgroundPosition'), it[2], 'Row '+ix+': Sprite CSS background position');
    });
    start();
  });

  asyncTest( 'Multi-row: Immune to `backwards` value', function(){
		expect(6);
    var
      selector= '#image',
      $reel= $(selector).reel({
        frames: 6,
        footage: 4,
        frame: 5,
        rows: 3,
        row: 3
      })
    equal( $reel.data('row'), 1);
    equal( $reel.data('frame'), 17);
    equal( $reel.data('backwards'), false);

    $reel.data('backwards', true);
    equal( $reel.data('row'), 1);
    equal( $reel.data('frame'), 17);
    equal( $reel.data('backwards'), true);
    start();
  });

})(jQuery);<commit_msg>Multi-row frame shifting test updated by binding to "loaded" event. <commit_after>/**
 * .reel Unit Tests
 */
(function($){

  module('Sprite', { teardown: function teardown(){
    $('.jquery-reel').unbind('loaded').trigger('teardown');
  }});

  asyncTest( 'Multi-row: Frame shifting (4 rows)', function(){
		expect(12);
    var
      selector= '#image',
      $reel= $(selector).reel({
        frames: 6,
        frame: 2,
        footage: 3,
        rows: 4,
        row: 1
      }),
      /*
      Sprite layout:

      A1 A2 A3                     1  2  3
      A4 A5 A6                     4  5  6
      B1 B2 B3                     7  8  9
      B4 B5 B6  in real frames »  10 11 12
      C1 C2 C3                    13 14 15
      C4 C5 C6                    16 17 18
      D1 D2 D3                    19 20 21
      D4 D5 D6                    22 23 24

      (letter ~ row; number ~ frame)
      */
      entries= {
        '1': [         0,           2,      '-276px 0px' ],
        '2': [    0.3333,           8,      '-276px -252px' ],
        '3': [    0.6667,          14,      '-276px -504px' ],
        '4': [         1,          20,      '-276px -756px' ]
      }
    $.each(entries, function(ix,it){
      $reel.trigger('rowChange', Number(ix));
      equal( $reel.data('row'), it[0], 'Row '+ix+': Interpolated row');
      equal( $reel.data('frame'), it[1], 'Row '+ix+': Shifted frame');
      equal( $reel.css('backgroundPosition'), it[2], 'Row '+ix+': Sprite CSS background position');
    });
    start();
  });

  asyncTest( 'Multi-row: Uneven rows frame shifting (3 rows)', function(){
		expect(9);
    var
      selector= '#image',
      $reel= $(selector).reel({
        frames: 6,
        footage: 4,
        frame: 5,
        rows: 3,
        row: 3
      }),
      /*
      Sprite layout:

      A1 A2 A3 A4                    1  2  3  4
      A5 A6 B1 B2                    5  6  7  8
      B3 B4 B5 B6  in real frames »  9 10 11 12
      C1 C2 C3 C4                   13 14 15 16
      C5 C6                         17 18

      (letter ~ row; number ~ frame)
      */
      entries= {
        '3': [         1,          17,      '0px -504px' ],
        '2': [       0.5,          11,      '-552px -252px' ],
        '1': [         0,           5,      '0px -126px' ]
      }
    $.each(entries, function(ix,it){
      $reel.trigger('rowChange', Number(ix));
      equal( $reel.data('row'), it[0], 'Row '+ix+': Interpolated row');
      equal( $reel.data('frame'), it[1], 'Row '+ix+': Shifted frame');
      equal( $reel.css('backgroundPosition'), it[2], 'Row '+ix+': Sprite CSS background position');
    });
    start();
  });

  asyncTest( 'Multi-row: Immune to `backwards` value', function(){
		expect(6);
    var
      selector= '#image',
      $reel= $(selector).reel({
        frames: 6,
        footage: 4,
        frame: 5,
        rows: 3,
        row: 3
      })

    $reel.bind('loaded', function(){
      equal( $reel.data('row'), 1);
      equal( $reel.data('frame'), 17);
      equal( $reel.data('backwards'), false);

      $reel.data('backwards', true);
      equal( $reel.data('row'), 1);
      equal( $reel.data('frame'), 17);
      equal( $reel.data('backwards'), true);
      start();
    });
  });

})(jQuery);<|endoftext|>


<commit_before>QUnit.module('timing');

QUnit.test('Function#debounce(wait, leading=false)', function(assert) {
  assert.expect(4);

  var args = ['a', 2, { arg: 3 }];
  var context = {};
  var done = assert.async();
  var callCount = 0;
  var debounced = (function() {
    callCount++;

    assert.deepEqual(Array.from(arguments), args, 'Invokes the function with the proper arguments.');

    assert.equal(this, context, 'Invokes the function with the proper context.');
  }).debounce(50);

  debounced.apply(context, args);
  debounced.apply(context, args);

  assert.equal(callCount, 0, 'Debounces the function invocation to a later time.');

  setTimeout(function() {
    assert.equal(callCount, 1, 'Invokes the function after `wait` milliseconds.');
    done();
  }, 50);
});

QUnit.test('Function#debounce(wait, leading=true)', function(assert) {
  assert.expect(8);

  var args = ['a', 2, { arg: 3 }];
  var context = {};
  var done = assert.async();
  var callCount = 0;
  var debounced = (function() {
    callCount++;

    assert.deepEqual(Array.from(arguments), args, 'Invokes the function with the proper arguments.');

    assert.equal(this, context, 'Invokes the function with the proper context.');
  }).debounce(50, true);

  debounced.apply(context, args);

  assert.equal(callCount, 1, 'Invokes the function immediately the first time the debounced function is called.');

  debounced.apply(context, args);
  debounced.apply(context, args);

  assert.equal(callCount, 1, 'Debounces subsequent calls made within `wait` milliseconds.');

  setTimeout(function() {
    assert.equal(callCount, 1, 'The function is not invoked on the trailing edge of the wait timeout.');

    debounced.apply(context, args);

    assert.equal(callCount, 2, 'Invokes the function when debounced is called after `wait` milliseconds.');

    done();
  }, 50);
});

QUnit.test('Function#delay', function(assert) {
  assert.expect(16);

  var args = ['a', 2, {arg: 3}];

  var done1 = assert.async();
  var ref1 = (function fn() {
    assert.deepEqual(Array.from(arguments), args,
      'Applies the specified arguments to the function when `thisArg` is not specified.');

    assert.equal(this, ref1, 'Sets `this` to the returned reference object if `thisArg` is not specified.');

    assert.equal(ref1.fn, fn, '`.fn` is set to the function `.delay()` was called on.');

    assert.equal(ref1.hasExecuted, false, '`.hasExecuted` is `false` while the function executes.');

    done1();
  }).delay(50, args);

  assert.equal(ref1.hasExecuted, false, '`.hasExecuted` is `false` before the function executes.');

  var done2 = assert.async();
  var thisArg = {};
  (function() {
    // Final test for ref1
    assert.equal(ref1.hasExecuted, true, '`.hasExecuted` is `true` after the function executes.');

    assert.deepEqual(Array.from(arguments), args,
      'Applies the specified arguments to the function when `thisArg` is specified.');

    assert.equal(this, thisArg, 'Sets `this` to the value specified by the `thisArg` parameter.');

    done2();
  }).delay(50, args, thisArg);

  var done3 = assert.async();
  (function() {
    assert.equal(this, thisArg, 'Sets `this` to the value specified by the `thisArg` parameter' +
                                ' when `args` is not specified and `thisArg` is not an array.');
    done3();
  }).delay(50, thisArg);

  var done4 = assert.async();
  var thisArgArray = [];
  (function() {
    assert.equal(this, thisArgArray, 'Sets `this` to the value specified by the `thisArg` parameter' +
                                     ' when `args` is `null` and `thisArg` is an array.');
    done4();
  }).delay(50, null, thisArgArray);

  var done5 = assert.async();
  var testVal5 = 0;
  var ref5 = (function() {
    testVal5++;
  }).delay(50);

  ref5.exec();
  assert.equal(testVal5, 1, 'Calling `.exec()` executes the function immediately.');
  assert.equal(ref5.hasExecuted, true,
    '`.hasExecuted` is `true` after the function is executed manually with `.exec()`.');

  setTimeout(function() {
    assert.equal(testVal5, 1,
      'Calling `.exec()` without the parameter `false` cancels the delayed callback.');
    done5();
  }, 50);

  var done6 = assert.async();
  var testVal6 = 0;
  (function() {
    testVal6++;
  }).delay(50).exec(false);

  setTimeout(function() {
    assert.equal(testVal6, 2,
      'Calling `.exec()` with the parameter `false` does not cancel the delayed callback.');
    done6();
  }, 50);

  var done7 = assert.async();
  var testVal7 = 0;
  var ref7 = (function() {
    testVal7++;
  }).delay(50);

  ref7.cancel();

  setTimeout(function() {
    assert.equal(testVal7, 0, 'Calling `.cancel()` cancels the delayed callback.');

    assert.equal(ref7.hasExecuted, false, '`.hasExecuted` remains `false` when a callback is cancelled.');

    done7();
  }, 50);
});

QUnit.test('Function#every', function(assert) {
  assert.expect(19);

  var args = ['a', 2, {arg: 3}];

  var done1 = assert.async();
  var ref1 = (function fn() {
    assert.deepEqual(Array.from(arguments), args,
      'Applies the specified arguments to the function when `thisArg` is not specified.');

    assert.equal(this, ref1, 'Sets `this` to the returned reference object if `thisArg` is not specified.');

    assert.equal(ref1.fn, fn, '`.fn` is set to the function `.delay()` was called on.');

    assert.equal(ref1.hasExecuted, false, '`.hasExecuted` is `false` while the function executes.');

    this.cancel();
    done1();
  }).every(50, args);

  assert.equal(ref1.hasExecuted, false, '`.hasExecuted` is `false` before the function executes.');

  var done2 = assert.async();
  var thisArg = {};
  var ref2 = (function() {
    // Final test for ref1
    assert.equal(ref1.hasExecuted, true, '`.hasExecuted` is `true` after the function executes.');

    assert.deepEqual(Array.from(arguments), args,
      'Applies the specified arguments to the function when `thisArg` is specified.');

    assert.equal(this, thisArg, 'Sets `this` to the value specified by the `thisArg` parameter.');

    ref2.cancel();
    done2();
  }).every(50, args, thisArg);

  var done3 = assert.async();
  var ref3 = (function() {
    assert.equal(this, thisArg, 'Sets `this` to the value specified by the `thisArg` parameter' +
                                ' when `args` is not specified and `thisArg` is not an array.');
    ref3.cancel();
    done3();
  }).every(50, thisArg);

  var done4 = assert.async();
  var thisArgArray = [];
  var ref4 = (function() {
    assert.equal(this, thisArgArray, 'Sets `this` to the value specified by the `thisArg` parameter' +
                                     ' when `args` is `null` and `thisArg` is an array.');
    ref4.cancel();
    done4();
  }).every(50, null, thisArgArray);

  var done5 = assert.async();
  var testVal5 = 0;
  var ref5 = (function() {
    testVal5++;
  }).every(50);

  ref5.exec();
  assert.equal(testVal5, 1, 'Calling `.exec()` executes the function immediately.');
  assert.equal(ref5.hasExecuted, true,
    '`.hasExecuted` is `true` after the function is executed manually with `.exec()`.');

  setTimeout(function() {
    assert.equal(testVal5, 1,
      'Calling `.exec()` without the parameter `false` cancels the delayed callback.');
    done5();
  }, 50);

  var done6 = assert.async();
  var testVal6 = 0;
  var ref6 = (function() {
    testVal6++;
  }).every(50);

  ref6.exec(false);
  assert.equal(testVal6, 1, 'Calling `.exec(false)` executes the function immediately.');

  setTimeout(function() {
    assert.equal(testVal6, 2,
      'Calling `.exec(false)` does not cancel the delayed callback.');

    // Set to `false` to test if it will get set to `true` on the next interval
    ref6.hasExecuted = false;
  }, 50);

  setTimeout(function() {
    assert.equal(testVal6, 3,
      'Causes the function to be executed repeatedly at regular intervals.');

    assert.strictEqual(ref6.hasExecuted, true,
      'Resets `.hasExecuted` to `true` each time the function executes.');

    ref6.cancel();
    done6();
  }, 130);

  var done7 = assert.async();
  var testVal7 = 0;
  var ref7 = (function() {
    testVal7++;
  }).every(50);

  ref7.cancel();

  setTimeout(function() {
    assert.equal(testVal7, 0, 'Calling `.cancel()` cancels the delayed callback.');

    assert.equal(ref7.hasExecuted, false, '`.hasExecuted` remains `false` when a callback is cancelled.');

    done7();
  }, 50);
});

QUnit.test('Function#throttle(wait, noTrailing=false)', function(assert) {
  assert.expect(13);

  var args = ['a', 2, { arg: 3 }];
  var context = {};
  var done = assert.async();
  var callCount = 0;
  var throttled = (function() {
    callCount++;

    assert.deepEqual(Array.from(arguments), args, 'Invokes the function with the proper arguments.');

    assert.equal(this, context, 'Invokes the function with the proper context.');
  }).throttle(50);

  throttled.apply(context, args);

  assert.equal(callCount, 1, 'Invokes the function immediately the first time the throttled function is called.');

  throttled.apply(context, args);
  throttled.apply(context, args);

  assert.equal(callCount, 1, 'Throttles subsequent calls made within `wait` milliseconds.');

  setTimeout(function() {
    assert.equal(callCount, 2, 'The function is invoked on the trailing edge of the wait timeout.');

    throttled.apply(context, args);

    assert.equal(callCount, 2, 'Throttles calls after the wait timeout if invoked during the wait period.');

    setTimeout(function() {
      throttled.apply(context, args);

      assert.equal(callCount, 4, 'Calls immediately after a wait period when `throttled` was not called.');

      done();
    }, 150);
  }, 50);
});

QUnit.test('Function#throttle(wait, noTrailing=true)', function(assert) {
  assert.expect(8);

  var args = ['a', 2, { arg: 3 }];
  var context = {};
  var done = assert.async();
  var callCount = 0;
  var throttled = (function() {
    callCount++;

    assert.deepEqual(Array.from(arguments), args, 'Invokes the function with the proper arguments.');

    assert.equal(this, context, 'Invokes the function with the proper context.');
  }).throttle(50, true);

  throttled.apply(context, args);

  assert.equal(callCount, 1, 'Invokes the function immediately the first time the throttled function is called.');

  throttled.apply(context, args);
  throttled.apply(context, args);

  assert.equal(callCount, 1, 'Throttles subsequent calls made within `wait` milliseconds.');

  setTimeout(function() {
    assert.equal(callCount, 1, 'The function is not invoked on the trailing edge of the wait timeout.');

    throttled.apply(context, args);

    assert.equal(callCount, 2, 'Invokes the function when throttled is called after `wait` milliseconds.');

    done();
  }, 50);
});
<commit_msg>test: Make Function#every() test a little more robust<commit_after>QUnit.module('timing');

QUnit.test('Function#debounce(wait, leading=false)', function(assert) {
  assert.expect(4);

  var args = ['a', 2, { arg: 3 }];
  var context = {};
  var done = assert.async();
  var callCount = 0;
  var debounced = (function() {
    callCount++;

    assert.deepEqual(Array.from(arguments), args, 'Invokes the function with the proper arguments.');

    assert.equal(this, context, 'Invokes the function with the proper context.');
  }).debounce(50);

  debounced.apply(context, args);
  debounced.apply(context, args);

  assert.equal(callCount, 0, 'Debounces the function invocation to a later time.');

  setTimeout(function() {
    assert.equal(callCount, 1, 'Invokes the function after `wait` milliseconds.');
    done();
  }, 50);
});

QUnit.test('Function#debounce(wait, leading=true)', function(assert) {
  assert.expect(8);

  var args = ['a', 2, { arg: 3 }];
  var context = {};
  var done = assert.async();
  var callCount = 0;
  var debounced = (function() {
    callCount++;

    assert.deepEqual(Array.from(arguments), args, 'Invokes the function with the proper arguments.');

    assert.equal(this, context, 'Invokes the function with the proper context.');
  }).debounce(50, true);

  debounced.apply(context, args);

  assert.equal(callCount, 1, 'Invokes the function immediately the first time the debounced function is called.');

  debounced.apply(context, args);
  debounced.apply(context, args);

  assert.equal(callCount, 1, 'Debounces subsequent calls made within `wait` milliseconds.');

  setTimeout(function() {
    assert.equal(callCount, 1, 'The function is not invoked on the trailing edge of the wait timeout.');

    debounced.apply(context, args);

    assert.equal(callCount, 2, 'Invokes the function when debounced is called after `wait` milliseconds.');

    done();
  }, 50);
});

QUnit.test('Function#delay', function(assert) {
  assert.expect(16);

  var args = ['a', 2, {arg: 3}];

  var done1 = assert.async();
  var ref1 = (function fn() {
    assert.deepEqual(Array.from(arguments), args,
      'Applies the specified arguments to the function when `thisArg` is not specified.');

    assert.equal(this, ref1, 'Sets `this` to the returned reference object if `thisArg` is not specified.');

    assert.equal(ref1.fn, fn, '`.fn` is set to the function `.delay()` was called on.');

    assert.equal(ref1.hasExecuted, false, '`.hasExecuted` is `false` while the function executes.');

    done1();
  }).delay(50, args);

  assert.equal(ref1.hasExecuted, false, '`.hasExecuted` is `false` before the function executes.');

  var done2 = assert.async();
  var thisArg = {};
  (function() {
    // Final test for ref1
    assert.equal(ref1.hasExecuted, true, '`.hasExecuted` is `true` after the function executes.');

    assert.deepEqual(Array.from(arguments), args,
      'Applies the specified arguments to the function when `thisArg` is specified.');

    assert.equal(this, thisArg, 'Sets `this` to the value specified by the `thisArg` parameter.');

    done2();
  }).delay(50, args, thisArg);

  var done3 = assert.async();
  (function() {
    assert.equal(this, thisArg, 'Sets `this` to the value specified by the `thisArg` parameter' +
                                ' when `args` is not specified and `thisArg` is not an array.');
    done3();
  }).delay(50, thisArg);

  var done4 = assert.async();
  var thisArgArray = [];
  (function() {
    assert.equal(this, thisArgArray, 'Sets `this` to the value specified by the `thisArg` parameter' +
                                     ' when `args` is `null` and `thisArg` is an array.');
    done4();
  }).delay(50, null, thisArgArray);

  var done5 = assert.async();
  var testVal5 = 0;
  var ref5 = (function() {
    testVal5++;
  }).delay(50);

  ref5.exec();
  assert.equal(testVal5, 1, 'Calling `.exec()` executes the function immediately.');
  assert.equal(ref5.hasExecuted, true,
    '`.hasExecuted` is `true` after the function is executed manually with `.exec()`.');

  setTimeout(function() {
    assert.equal(testVal5, 1,
      'Calling `.exec()` without the parameter `false` cancels the delayed callback.');
    done5();
  }, 50);

  var done6 = assert.async();
  var testVal6 = 0;
  (function() {
    testVal6++;
  }).delay(50).exec(false);

  setTimeout(function() {
    assert.equal(testVal6, 2,
      'Calling `.exec()` with the parameter `false` does not cancel the delayed callback.');
    done6();
  }, 50);

  var done7 = assert.async();
  var testVal7 = 0;
  var ref7 = (function() {
    testVal7++;
  }).delay(50);

  ref7.cancel();

  setTimeout(function() {
    assert.equal(testVal7, 0, 'Calling `.cancel()` cancels the delayed callback.');

    assert.equal(ref7.hasExecuted, false, '`.hasExecuted` remains `false` when a callback is cancelled.');

    done7();
  }, 50);
});

QUnit.test('Function#every', function(assert) {
  assert.expect(19);

  var args = ['a', 2, {arg: 3}];

  var done1 = assert.async();
  var ref1 = (function fn() {
    assert.deepEqual(Array.from(arguments), args,
      'Applies the specified arguments to the function when `thisArg` is not specified.');

    assert.equal(this, ref1, 'Sets `this` to the returned reference object if `thisArg` is not specified.');

    assert.equal(ref1.fn, fn, '`.fn` is set to the function `.delay()` was called on.');

    assert.equal(ref1.hasExecuted, false, '`.hasExecuted` is `false` while the function executes.');

    this.cancel();
    done1();
  }).every(50, args);

  assert.equal(ref1.hasExecuted, false, '`.hasExecuted` is `false` before the function executes.');

  var done2 = assert.async();
  var thisArg = {};
  var ref2 = (function() {
    // Final test for ref1
    assert.equal(ref1.hasExecuted, true, '`.hasExecuted` is `true` after the function executes.');

    assert.deepEqual(Array.from(arguments), args,
      'Applies the specified arguments to the function when `thisArg` is specified.');

    assert.equal(this, thisArg, 'Sets `this` to the value specified by the `thisArg` parameter.');

    ref2.cancel();
    done2();
  }).every(50, args, thisArg);

  var done3 = assert.async();
  var ref3 = (function() {
    assert.equal(this, thisArg, 'Sets `this` to the value specified by the `thisArg` parameter' +
                                ' when `args` is not specified and `thisArg` is not an array.');
    ref3.cancel();
    done3();
  }).every(50, thisArg);

  var done4 = assert.async();
  var thisArgArray = [];
  var ref4 = (function() {
    assert.equal(this, thisArgArray, 'Sets `this` to the value specified by the `thisArg` parameter' +
                                     ' when `args` is `null` and `thisArg` is an array.');
    ref4.cancel();
    done4();
  }).every(50, null, thisArgArray);

  var done5 = assert.async();
  var testVal5 = 0;
  var ref5 = (function() {
    testVal5++;
  }).every(50);

  ref5.exec();
  assert.equal(testVal5, 1, 'Calling `.exec()` executes the function immediately.');
  assert.equal(ref5.hasExecuted, true,
    '`.hasExecuted` is `true` after the function is executed manually with `.exec()`.');

  setTimeout(function() {
    assert.equal(testVal5, 1,
      'Calling `.exec()` without the parameter `false` cancels the delayed callback.');
    done5();
  }, 50);

  var done6 = assert.async();
  var testVal6 = 0;
  var ref6 = (function() {
    testVal6++;
  }).every(50);

  ref6.exec(false);
  assert.equal(testVal6, 1, 'Calling `.exec(false)` executes the function immediately.');

  setTimeout(function() {
    assert.equal(testVal6, 2,
      'Calling `.exec(false)` does not cancel the delayed callback.');

    // Set to `false` to test if it will get set to `true` on the next interval
    ref6.hasExecuted = false;
  }, 50);

  setTimeout(function() {
    assert.ok(testVal6 > 2, 'Causes the function to be executed repeatedly at regularish intervals.');

    assert.strictEqual(ref6.hasExecuted, true,
      'Resets `.hasExecuted` to `true` each time the function executes.');

    ref6.cancel();
    done6();
  }, 150);

  var done7 = assert.async();
  var testVal7 = 0;
  var ref7 = (function() {
    testVal7++;
  }).every(50);

  ref7.cancel();

  setTimeout(function() {
    assert.equal(testVal7, 0, 'Calling `.cancel()` cancels the delayed callback.');

    assert.equal(ref7.hasExecuted, false, '`.hasExecuted` remains `false` when a callback is cancelled.');

    done7();
  }, 50);
});

QUnit.test('Function#throttle(wait, noTrailing=false)', function(assert) {
  assert.expect(13);

  var args = ['a', 2, { arg: 3 }];
  var context = {};
  var done = assert.async();
  var callCount = 0;
  var throttled = (function() {
    callCount++;

    assert.deepEqual(Array.from(arguments), args, 'Invokes the function with the proper arguments.');

    assert.equal(this, context, 'Invokes the function with the proper context.');
  }).throttle(50);

  throttled.apply(context, args);

  assert.equal(callCount, 1, 'Invokes the function immediately the first time the throttled function is called.');

  throttled.apply(context, args);
  throttled.apply(context, args);

  assert.equal(callCount, 1, 'Throttles subsequent calls made within `wait` milliseconds.');

  setTimeout(function() {
    assert.equal(callCount, 2, 'The function is invoked on the trailing edge of the wait timeout.');

    throttled.apply(context, args);

    assert.equal(callCount, 2, 'Throttles calls after the wait timeout if invoked during the wait period.');

    setTimeout(function() {
      throttled.apply(context, args);

      assert.equal(callCount, 4, 'Calls immediately after a wait period when `throttled` was not called.');

      done();
    }, 150);
  }, 50);
});

QUnit.test('Function#throttle(wait, noTrailing=true)', function(assert) {
  assert.expect(8);

  var args = ['a', 2, { arg: 3 }];
  var context = {};
  var done = assert.async();
  var callCount = 0;
  var throttled = (function() {
    callCount++;

    assert.deepEqual(Array.from(arguments), args, 'Invokes the function with the proper arguments.');

    assert.equal(this, context, 'Invokes the function with the proper context.');
  }).throttle(50, true);

  throttled.apply(context, args);

  assert.equal(callCount, 1, 'Invokes the function immediately the first time the throttled function is called.');

  throttled.apply(context, args);
  throttled.apply(context, args);

  assert.equal(callCount, 1, 'Throttles subsequent calls made within `wait` milliseconds.');

  setTimeout(function() {
    assert.equal(callCount, 1, 'The function is not invoked on the trailing edge of the wait timeout.');

    throttled.apply(context, args);

    assert.equal(callCount, 2, 'Invokes the function when throttled is called after `wait` milliseconds.');

    done();
  }, 50);
});
<|endoftext|>


<commit_before>/* eslint-env node */

var Ping = require('./ping');
var realtimeSetInterval = require('./realtime-set-interval');
var _ = require('underscore');
var Backbone = require('backbone');

/**
 * The amount of active pings (i.e. connections) that can run concurrently.
 *
 * This should solve the problem of the connection being restored after
 * being offline for a while (thus, there being a lot of open connections),
 * and new pings not working as the browser already reached the limit
 * of open connections.
 *
 * @type {integer}
 */
var PINGS_CONCURRENCY_LIMIT = 20;

var Pings = module.exports = function() {
    this._list = [];
    this._max = 100;
    this._lastRespondedPing;

    _.extend(this, Backbone.Events);

    this._ping(); // Start pinging ASAP
    realtimeSetInterval(this._ping.bind(this), this.interval);
};

Pings.prototype.interval = 1000;/* msecs */ // How often pings are created

Pings.prototype.all = function () { return this._list; };

Pings.prototype.setMax = function (max) { this._max = max; };

Pings.prototype.max = function () { return this._max; };

Pings.prototype.currentLag = function () {
    var pings = this;
    var lastRespondedPingLag = this._lastRespondedPing ? this._lastRespondedPing.lag() : 0;
    var firstOfTheLastUnrespondedPingsLag = (function() {
        var ping = pings._getFirstOfTheLastUnrespondedPings();
        return ping ? ping.lag() : 0;
    })();
    return Math.max(lastRespondedPingLag, firstOfTheLastUnrespondedPingsLag);
};

Pings.prototype._getFirstOfTheLastUnrespondedPings = function() {
    var first;
    for (var i = this._list.length - 1; i >= 0; i--) {
        if (this._list[i].end) break;
        first = this._list[i];
    }
    return first;
};

Pings.prototype._getRunningPings = function () {
    return this._list.filter(function (ping) { return !ping.done; });
};

Pings.prototype._removePingsOverLimit = function () {
    this._list = _.last(this._list, this._max);
};

Pings.prototype._abortOldestPingsOverConcurrencyLimit = function () {
    _.chain(this._getRunningPings()).initial(PINGS_CONCURRENCY_LIMIT).invoke('abort');
};

Pings.prototype._addPing = function () {
    var pings = this;
    var ping = new Ping;
    ping.on('pong', function() {
        if (!pings._lastRespondedPing || pings._lastRespondedPing.start < ping.start) {
            pings._lastRespondedPing = ping;
        }

        pings.trigger('pong', ping);
    });
    this._list.push(ping);
    pings.trigger('add', ping);
};

Pings.prototype._ping = function () {
    this._removePingsOverLimit();
    this._abortOldestPingsOverConcurrencyLimit();
    this._addPing();
};<commit_msg>Pings from prototype to class<commit_after>/* eslint-env node, es6 */

var Ping = require('./ping');
var realtimeSetInterval = require('./realtime-set-interval');
var _ = require('underscore');
var Backbone = require('backbone');

/**
 * The amount of active pings (i.e. connections) that can run concurrently.
 *
 * This should solve the problem of the connection being restored after
 * being offline for a while (thus, there being a lot of open connections),
 * and new pings not working as the browser already reached the limit
 * of open connections.
 *
 * @type {integer}
 */
var PINGS_CONCURRENCY_LIMIT = 20;

module.exports = class Pings {
    constructor() {
        _.extend(this, Backbone.Events);

        this.interval = 1000;/* msecs */ // How often pings are created
        this._list = [];
        this._max = 100;
        this._lastRespondedPing;

        this._ping(); // Start pinging ASAP
        realtimeSetInterval(this._ping.bind(this), this.interval);
    }

    all() { return this._list; }

    setMax(max) { this._max = max; }

    max() { return this._max; }

    currentLag() {
        var pings = this;
        var lastRespondedPingLag = this._lastRespondedPing ? this._lastRespondedPing.lag() : 0;
        var firstOfTheLastUnrespondedPingsLag = (() => {
            var ping = pings._getFirstOfTheLastUnrespondedPings();
            return ping ? ping.lag() : 0;
        })();
        return Math.max(lastRespondedPingLag, firstOfTheLastUnrespondedPingsLag);
    };

    _getFirstOfTheLastUnrespondedPings() {
        var first;
        for (var i = this._list.length - 1; i >= 0; i--) {
            if (this._list[i].end) break;
            first = this._list[i];
        }
        return first;
    };

    _getRunningPings() {
        return this._list.filter(ping => !ping.done);
    };

    _ping() {
        this._removePingsOverLimit();
        this._abortOldestPingsOverConcurrencyLimit();
        this._addPing();
    };

    _removePingsOverLimit() {
        this._list = _.last(this._list, this._max);
    };

    _abortOldestPingsOverConcurrencyLimit() {
        _.chain(this._getRunningPings()).initial(PINGS_CONCURRENCY_LIMIT).invoke('abort');
    };

    _addPing() {
        var ping = new Ping;
        ping.on('pong', () => {
            var isLastResponded = !this._lastRespondedPing || this._lastRespondedPing.start < ping.start;
            if (isLastResponded) this._lastRespondedPing = ping;
            this.trigger('pong', ping);
        });
        this._list.push(ping);
        this.trigger('add', ping);
    };
};
<|endoftext|>


<commit_before>/*
* Copyright 2014 Space Dynamics Laboratory - Utah State University Research Foundation.
*
* Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an 'AS IS' BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

'use strict';

app.controller('adminEditArticlesCtrl',['$scope','business', '$uiModal', '$timeout', '$q', function ($scope, Business, $uiModal, $timeout, $q) {
  $scope.type = $scope.$parent.type || null;
  $scope.code = $scope.$parent.code || null;

  $scope.predicate = 'title';
  $scope.reverse = false;
  $scope.$emit('$TRIGGEREVENT', '$TRIGGERLOAD', 'adminArticlesEdit');

  // /***************************************************************
  // * If we don't have a landing page, we're going to set up one for now so that
  // * there will always be one in the editor when we look, unless we click on a button
  // * that says 'add landing page'
  // ***************************************************************/

  $scope.clearSort = function() {
    $scope.predicate = 'title';
    $scope.reverse = false;
    if(!$scope.$$phase) {
      $scope.$apply();
    }
  }

  $scope.setPredicate = function (predicate) {
    if ($scope.predicate === predicate) {
      $scope.reverse = !$scope.reverse;
    } else {
      $scope.predicate = predicate;
      $scope.reverse = false;
    }
  };

  $scope.getArticles = function(override){
    var deferred = $q.defer();
    Business.articleservice.getArticles(override, true).then(function(result){
      $scope.articles = result? angular.copy(result): [];
      $scope.$emit('$TRIGGEREVENT', '$TRIGGERUNLOAD', 'adminArticlesEdit');
      deferred.resolve();
    }, function(){
      $scope.articles = [];
      $scope.$emit('$TRIGGEREVENT', '$TRIGGERUNLOAD', 'adminArticlesEdit');
      deferred.resolve();
    })
    return deferred.promise;
  }
  $scope.getArticles(true);

  $scope.getArticleDesc = function(desc){
    if (desc && desc !== undefined  && desc !== null && desc !== '') {
      return desc;
    }
    return ' ';
  }

  $scope.editContent = function(type, code){
    var modalInstance = $uiModal.open({
      templateUrl: 'views/admin/editlandingform.html',
      controller: 'AdminEditLandingCtrl',
      size: 'lg',
      backdrop: 'static',
      resolve: {
        type: function () {
          return type;
        },
        code: function () {
          return code;
        },
        articles: function () {
          return $scope.articles;
        }
      }
    });

    modalInstance.result.then(function (result) {
      $scope.getArticles(true);
    }, function (result) {
      $scope.getArticles(true);
    });
  }

  $scope.changeActivity = function(article){
    if (article && article.attributeType && article.attributeCode) {
      var message = "Warning: You are about to change the active status of an Attribute Code. This will activate or deactivate the code and all related metadata. Continue?";
      var conf = confirm(message);
      if (conf) {
        if (article.activeStatus === 'A') {
          $scope.$emit('$TRIGGEREVENT', '$TRIGGERLOAD', 'adminArticlesEdit');
          Business.articleservice.activateCode(article.attributeType, article.attributeCode).then(function(){
            $timeout(function(){
              $scope.$emit('$TRIGGEREVENT', '$TRIGGERUNLOAD', 'adminArticlesEdit');
              $scope.getArticles(true);
            }, 1000);
          }, function(){
            $timeout(function(){
              $scope.$emit('$TRIGGEREVENT', '$TRIGGERUNLOAD', 'adminArticlesEdit');
              $scope.getArticles(true);
            }, 1000);
          })
        } else {
          Business.articleservice.deactivateCode(article.attributeType, article.attributeCode).then(function(){
            $timeout(function(){
              $scope.$emit('$TRIGGEREVENT', '$TRIGGERUNLOAD', 'adminArticlesEdit');
              $scope.getArticles(true);
            }, 1000);
          }, function(){
            $timeout(function(){
              $scope.$emit('$TRIGGEREVENT', '$TRIGGERUNLOAD', 'adminArticlesEdit');
              $scope.getArticles(true);
            }, 1000);
          })
        }
      }
    }
  }

  $scope.importFile = function(){
    var modalInstance = $uiModal.open({
      templateUrl: 'views/admin/fileupload.html',
      controller: 'AdminAddMediaCtrl',
      backdrop: 'static',
      size: 'sm',
      resolve: {
        title: function () {
          return "Import Articles File";
        },
        url: function () {
          return "Upload.action?UploadArticles";
        },
        single: function () {
          return true;
        },
        alias: function () {
          return 'uploadFile';
        }
      }
    });

    modalInstance.result.then(function (result) {
      triggerAlert('Your changes to the article have been saved.', 'ArticleEditAlert', 'body', 6000);
      $scope.$emit('$TRIGGEREVENT', '$TRIGGERLOAD', 'adminArticlesEdit');
      $timeout(function(){
        $scope.getArticles(true);
      }, 1000);
    }, function (result) {
      $scope.$emit('$TRIGGEREVENT', '$TRIGGERLOAD', 'adminArticlesEdit');
      $timeout(function(){
        $scope.getArticles(true);
      }, 1000);
    });
  };

  $scope.deleteArticle = function(article){
    var conf = confirm("You are about to remove an article permanently. Would you like to continue?");
    if (conf) {
      Business.articleservice.deleteArticle(article).then(function(result){
        triggerAlert('The article was deleted.', 'articleAlert', 'body', 6000)
        $scope.getArticles(true);
      })
    }
  }

  if ($scope.type && $scope.code) {
    $scope.getArticles(true).then(function(){
      $scope.editContent($scope.type, $scope.code, $scope.articles);
    })
  }

  $timeout(function() {
    $('[data-toggle=\'tooltip\']').tooltip();
  }, 300);

  var stickThatTable = function(){
    var offset = $('.top').outerHeight() + $('#editArticlesToolbar').outerHeight();
    $(".stickytable").stickyTableHeaders({
      fixedOffset: offset
    });
  }

  $(window).resize(stickThatTable);
  $timeout(stickThatTable, 100);

}]);

app.controller('AdminEditLandingCtrl',['$scope', '$uiModalInstance', 'type', 'code', 'articles', 'business', '$location', function ($scope, $uiModalInstance, type, code, articles, Business, $location) {

  $scope.type = {};
  $scope.type.type = type || null;
  $scope.code = {};
  $scope.code.code = code || null;
  $scope.articles = articles;
  $scope.article;
  $scope.attributes;
  $scope.codes;

  var compare = function (a, b) {
    return ((a.label == b.label) ? 0 : ((a.label > b.label) ? 1 : -1));
  }

  $scope.getAttributeCodes = function(override) {
    // console.log('we\'re getting codes', $scope.type.type);
    if ($scope.type && $scope.type.type) {
      Business.articleservice.getType($scope.type.type, true, true).then(function(result){
        result.codes.sort(compare);
        // console.log('result', result);

        $scope.codes = (result && result.codes)? angular.copy(result.codes): [];
      }, function(){
        $scope.codes = [];
      });
    } else {
      $scope.codes = [];
    }
  }
  $scope.getAttributes = function(override) {
    Business.getFilters(override, true).then(function(result){
      $scope.attributes = result? angular.copy(result): [];
    }, function(){
      $scope.attributes = [];
    });
  }
  $scope.getAttributes(true);

  $scope.$watch('type', function(){
    if ($scope.type.type){
      // console.log('The change triggered the code gathering');

      $scope.getAttributeCodes();
    }
  }, true)

  $scope.$watch('code', function(){
    if ($scope.code.code){
      // console.log('The change triggered the code gathering');
      $scope.checkType($scope.type, $scope.code);
    }
  }, true)

  var popupWin;
  $scope.editorContent = '';
  $scope.editorOptions = getCkConfig();

  $scope.preview = function(){
    $scope.article.html = $scope.getEditorContent();
    $scope.article.attributeType = $scope.type.type;
    $scope.article.attributeCode = $scope.code.code;
    if ($scope.article && $scope.article.html && $scope.article.attributeType && $scope.article.attributeCode) {
      Business.articleservice.previewArticle($scope.article).then(function(result){
        var url = $location.absUrl().substring(0, $location.absUrl().length - $location.url().length);
        url = url + '/landing' + '?' + $.param({
          type: 'preview',
          code: result
        });
        popupWin = utils.openWindow(url, 'Aritlce_Preview_' + $scope.article.attributeType + $scope.article.attributeCode, 'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=yes, resizable=yes, width=840, height=840', popupWin, 'articleEditModal');
      }, function(){

      })
    } else {
      triggerAlert('There is missing information required for previewing the article, please try again.', 'articleAlert', 'articleEditModal', 6000);
    }
  }

  $scope.getEditorContent = function(){
    var html = angular.copy($scope.editorContentWatch);
    var temp = null;
    if (_.contains(html, 'id="allTheDContent"')) {
      temp = $(html);
    }
    if (!temp || !temp.find('#allTheContent').length) {
      html = '
' + html + '
';
      var temp = $(html);
    }

    var content = $('

');
    _.each(temp, function(e){
      content.append(e);
    })
    var lists = content.find('div.componentlistBody');
    if (lists.length) {
      _.each(lists, function(componentList, $index){
        // console.log('Component list $index --------------', $index);
        // console.log('componentList', componentList);
        var attributes = $(componentList).find('[data-attributeLabel]');
        var componentListElement = $('<component-list></component-list>');
        _.each(attributes, function(attribute){
          var attrib = $(attribute);
          var attr = attrib.attr('data-attributeLabel');
          var value = attrib.html();
          // console.log('label', attr);
          // console.log('value', value);
          componentListElement.attr(attr, value);
        });
        var alreadythere = $(componentList).parent().find('component-list');
        if (alreadythere.length) {
          alreadythere.remove();
        }
        $(componentList).parent().append(componentListElement);
      });
    }
    return content.find('#allTheContent').html();
  }

  $scope.ok = function () {
    // $scope.editorContentWatch = $scope.editorContent;
    $scope.article.html = $scope.getEditorContent();
    $scope.article.attributeType = $scope.type.type;
    $scope.article.attributeCode = $scope.code.code;
    if ($scope.article && $scope.article.html && $scope.article.attributeType && $scope.article.attributeCode) {
      Business.articleservice.saveArticle($scope.article).then(function(result){
        $uiModalInstance.close();
      }, function(){
        triggerAlert('There was an error saving your article, please try again.', 'articleAlert', 'articleEditModal', 6000);
      })
    } else {
      triggerAlert('There is missing information required for saving the article, please try again.', 'articleAlert', 'articleEditModal', 6000);
    }
  };

  $scope.cancel = function () {
    $uiModalInstance.dismiss('cancel');
  };

  $scope.getContent = function(){
    if ($scope.type && $scope.type.type && $scope.code && $scope.code.code && $scope.article && !$scope.article.html) {
      Business.articleservice.getArticle($scope.type.type, $scope.code.code, true).then(function (result) { /*jshint unused:false*/
        $scope.editorContent = result || ' ';
      }, function(){
        $scope.editorContent = ' ';
      });
    } else if ($scope.article && $scope.article.html) {
      $scope.editorContent = $scope.article.html;
    } else {
      $scope.editorContent = ' ';
    }
  }

  $scope.checkType = function(type, code) {
    if (type && code) {
      var article = _.find($scope.articles, {'attributeCode': code, 'attributeType': type});
      if (article) {
        $scope.article = angular.copy(article);
      } else {
        $scope.article = {
          attributeCode: code,
          attributeType: type,
          description: '',
          title: '',
        };
      }
    }else {
      $scope.article = {
        attributeCode: code,
        attributeType: type,
        description: '',
        title: '',
      };
    }

    $scope.getContent();
  }
  $scope.checkType($scope.type.type, $scope.code.code);

}]);

<commit_msg>Managed to get the articles editor to load the content EVERY time.<commit_after>/*
* Copyright 2014 Space Dynamics Laboratory - Utah State University Research Foundation.
*
* Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an 'AS IS' BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

'use strict';

app.controller('adminEditArticlesCtrl',['$scope','business', '$uiModal', '$timeout', '$q', function ($scope, Business, $uiModal, $timeout, $q) {
  $scope.type = $scope.$parent.type || null;
  $scope.code = $scope.$parent.code || null;

  $scope.predicate = 'title';
  $scope.reverse = false;
  $scope.$emit('$TRIGGEREVENT', '$TRIGGERLOAD', 'adminArticlesEdit');

  // /***************************************************************
  // * If we don't have a landing page, we're going to set up one for now so that
  // * there will always be one in the editor when we look, unless we click on a button
  // * that says 'add landing page'
  // ***************************************************************/

  $scope.clearSort = function() {
    $scope.predicate = 'title';
    $scope.reverse = false;
    if(!$scope.$$phase) {
      $scope.$apply();
    }
  }

  $scope.setPredicate = function (predicate) {
    if ($scope.predicate === predicate) {
      $scope.reverse = !$scope.reverse;
    } else {
      $scope.predicate = predicate;
      $scope.reverse = false;
    }
  };

  $scope.getArticles = function(override){
    var deferred = $q.defer();
    Business.articleservice.getArticles(override, true).then(function(result){
      $scope.articles = result? angular.copy(result): [];
      $scope.$emit('$TRIGGEREVENT', '$TRIGGERUNLOAD', 'adminArticlesEdit');
      deferred.resolve();
    }, function(){
      $scope.articles = [];
      $scope.$emit('$TRIGGEREVENT', '$TRIGGERUNLOAD', 'adminArticlesEdit');
      deferred.resolve();
    })
    return deferred.promise;
  }
  $scope.getArticles(true);

  $scope.getArticleDesc = function(desc){
    if (desc && desc !== undefined  && desc !== null && desc !== '') {
      return desc;
    }
    return ' ';
  }

  $scope.editContent = function(type, code){
    var modalInstance = $uiModal.open({
      templateUrl: 'views/admin/editlandingform.html',
      controller: 'AdminEditLandingCtrl',
      size: 'lg',
      backdrop: 'static',
      resolve: {
        type: function () {
          return type;
        },
        code: function () {
          return code;
        }
      }
    });

    modalInstance.result.then(function (result) {
      $scope.getArticles(true);
    }, function (result) {
      $scope.getArticles(true);
    });
  }

  $scope.changeActivity = function(article){
    if (article && article.attributeType && article.attributeCode) {
      var message = "Warning: You are about to change the active status of an Attribute Code. This will activate or deactivate the code and all related metadata. Continue?";
      var conf = confirm(message);
      if (conf) {
        if (article.activeStatus === 'A') {
          $scope.$emit('$TRIGGEREVENT', '$TRIGGERLOAD', 'adminArticlesEdit');
          Business.articleservice.activateCode(article.attributeType, article.attributeCode).then(function(){
            $timeout(function(){
              $scope.$emit('$TRIGGEREVENT', '$TRIGGERUNLOAD', 'adminArticlesEdit');
              $scope.getArticles(true);
            }, 1000);
          }, function(){
            $timeout(function(){
              $scope.$emit('$TRIGGEREVENT', '$TRIGGERUNLOAD', 'adminArticlesEdit');
              $scope.getArticles(true);
            }, 1000);
          })
        } else {
          Business.articleservice.deactivateCode(article.attributeType, article.attributeCode).then(function(){
            $timeout(function(){
              $scope.$emit('$TRIGGEREVENT', '$TRIGGERUNLOAD', 'adminArticlesEdit');
              $scope.getArticles(true);
            }, 1000);
          }, function(){
            $timeout(function(){
              $scope.$emit('$TRIGGEREVENT', '$TRIGGERUNLOAD', 'adminArticlesEdit');
              $scope.getArticles(true);
            }, 1000);
          })
        }
      }
    }
  }

  $scope.importFile = function(){
    var modalInstance = $uiModal.open({
      templateUrl: 'views/admin/fileupload.html',
      controller: 'AdminAddMediaCtrl',
      backdrop: 'static',
      size: 'sm',
      resolve: {
        title: function () {
          return "Import Articles File";
        },
        url: function () {
          return "Upload.action?UploadArticles";
        },
        single: function () {
          return true;
        },
        alias: function () {
          return 'uploadFile';
        }
      }
    });

    modalInstance.result.then(function (result) {
      triggerAlert('Your changes to the article have been saved.', 'ArticleEditAlert', 'body', 6000);
      $scope.$emit('$TRIGGEREVENT', '$TRIGGERLOAD', 'adminArticlesEdit');
      $timeout(function(){
        $scope.getArticles(true);
      }, 1000);
    }, function (result) {
      $scope.$emit('$TRIGGEREVENT', '$TRIGGERLOAD', 'adminArticlesEdit');
      $timeout(function(){
        $scope.getArticles(true);
      }, 1000);
    });
  };

  $scope.deleteArticle = function(article){
    var conf = confirm("You are about to remove an article permanently. Would you like to continue?");
    if (conf) {
      Business.articleservice.deleteArticle(article).then(function(result){
        triggerAlert('The article was deleted.', 'articleAlert', 'body', 6000)
        $scope.getArticles(true);
      })
    }
  }

  if ($scope.type && $scope.code) {
    $scope.getArticles(true).then(function(){
      $scope.editContent($scope.type, $scope.code, $scope.articles);
    })
  }

  $timeout(function() {
    $('[data-toggle=\'tooltip\']').tooltip();
  }, 300);

  var stickThatTable = function(){
    var offset = $('.top').outerHeight() + $('#editArticlesToolbar').outerHeight();
    $(".stickytable").stickyTableHeaders({
      fixedOffset: offset
    });
  }

  $(window).resize(stickThatTable);
  $timeout(stickThatTable, 100);

}]);

app.controller('AdminEditLandingCtrl',['$scope', '$uiModalInstance', 'type', 'code', 'business', '$location', '$q', '$timeout', function ($scope, $uiModalInstance, type, code, Business, $location, $q, $timeout) {

  $scope.type = {};
  $scope.type.type = type || null;
  $scope.code = {};
  $scope.code.code = code || null;
  $scope.article;
  $scope.attributes;
  $scope.codes;
  var compare = function (a, b) {
    return ((a.label == b.label) ? 0 : ((a.label > b.label) ? 1 : -1));
  }

  $scope.getArticles = function(override){
    var deferred = $q.defer();
    Business.articleservice.getArticles(override, true).then(function(result){
      $scope.articles = result? angular.copy(result): [];
      deferred.resolve();
    }, function(){
      $scope.articles = [];
      deferred.resolve();
    })
    return deferred.promise;
  }
  $scope.getArticles(true);

  $scope.getAttributeCodes = function(override) {
    // console.log('we\'re getting codes', $scope.type.type);
    if ($scope.type && $scope.type.type) {
      Business.articleservice.getType($scope.type.type, true, true).then(function(result){
        result.codes.sort(compare);
        // console.log('result', result);
        $scope.getArticles();
        $scope.codes = (result && result.codes)? angular.copy(result.codes): [];
      }, function(){
        $scope.codes = [];
      });
    } else {
      $scope.codes = [];
    }
  }
  $scope.getAttributes = function(override) {
    Business.getFilters(override, true).then(function(result){
      $scope.attributes = result? angular.copy(result): [];
    }, function(){
      $scope.attributes = [];
    });
  }
  $scope.getAttributes(true);

  $scope.$watch('type', function(){
    if ($scope.type.type){
      // console.log('The change triggered the code gathering');

      $scope.getAttributeCodes();
    }
  }, true)

  $scope.$watch('code', function(){
    if ($scope.code.code){
      // console.log('The change triggered the code gathering');
      $scope.checkType($scope.type.type, $scope.code.code);
    }
  }, true)

  var popupWin;
  $scope.editorOptions = getCkConfig();

  $scope.preview = function(){
    $scope.article.html = $scope.getEditorContent();
    $scope.article.attributeType = $scope.type.type;
    $scope.article.attributeCode = $scope.code.code;
    if ($scope.article && $scope.article.html && $scope.article.attributeType && $scope.article.attributeCode) {
      Business.articleservice.previewArticle($scope.article).then(function(result){
        var url = $location.absUrl().substring(0, $location.absUrl().length - $location.url().length);
        url = url + '/landing?' + $.param({
          type: 'preview',
          code: result
        });
        popupWin = utils.openWindow(url, 'Aritlce_Preview_' + $scope.article.attributeType + $scope.article.attributeCode, 'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=yes, resizable=yes, width=840, height=840', popupWin, 'articleEditModal');
      }, function(){

      })
    } else {
      triggerAlert('There is missing information required for previewing the article, please try again.', 'articleAlert', 'articleEditModal', 6000);
    }
  }

  $scope.getEditorContent = function(){
    var html = angular.copy($scope.editorContentWatch);
    var temp = null;
    if (_.contains(html, 'id="allTheDContent"')) {
      temp = $(html);
    }
    if (!temp || !temp.find('#allTheContent').length) {
      html = '
' + html + '
';
      var temp = $(html);
    }

    var content = $('

');
    _.each(temp, function(e){
      content.append(e);
    })
    var lists = content.find('div.componentlistBody');
    if (lists.length) {
      _.each(lists, function(componentList, $index){
        // console.log('Component list $index --------------', $index);
        // console.log('componentList', componentList);
        var attributes = $(componentList).find('[data-attributeLabel]');
        var componentListElement = $('<component-list></component-list>');
        _.each(attributes, function(attribute){
          var attrib = $(attribute);
          var attr = attrib.attr('data-attributeLabel');
          var value = attrib.html();
          // console.log('label', attr);
          // console.log('value', value);
          componentListElement.attr(attr, value);
        });
        var alreadythere = $(componentList).parent().find('component-list');
        if (alreadythere.length) {
          alreadythere.remove();
        }
        $(componentList).parent().append(componentListElement);
      });
    }
    return content.find('#allTheContent').html();
  }

  $scope.ok = function () {
    // $scope.editorContentWatch = $scope.editorContent;
    $scope.article.html = $scope.getEditorContent();
    $scope.article.attributeType = $scope.type.type;
    $scope.article.attributeCode = $scope.code.code;
    if ($scope.article && $scope.article.html && $scope.article.attributeType && $scope.article.attributeCode) {
      Business.articleservice.saveArticle($scope.article).then(function(result){
        $uiModalInstance.close();
      }, function(){
        triggerAlert('There was an error saving your article, please try again.', 'articleAlert', 'articleEditModal', 6000);
      })
    } else {
      triggerAlert('There is missing information required for saving the article, please try again.', 'articleAlert', 'articleEditModal', 6000);
    }
  };

  $scope.cancel = function () {
    $uiModalInstance.dismiss('cancel');
  };

  var lock = false;

  // investigate why this is being called twice!
  $scope.getContent = function(type, code){
    if (type && code && $scope.article && !$scope.article.html) {
      if (!lock) {
        lock = true;
        Business.articleservice.getArticle(type, code, true).then(function (result) { /*jshint unused:false*/
          lock = false;
          $scope.editorContent = result || '';
        }, function(){
          lock = false;
          $scope.editorContent = '';
        });
      }
    } else if ($scope.article && $scope.article.html) {
      $scope.editorContent = $scope.article.html;
    } else {
      $scope.editorContent = '';
    }
  }

  $scope.checkType = function(type, code) {
    if (type && code) {
      $scope.getArticles(true).then(function(){
        var article = _.find($scope.articles, {'attributeCode': code, 'attributeType': type});
        if (article) {
          $scope.article = angular.copy(article);
        } else {
          $scope.article = {
            attributeCode: code,
            attributeType: type,
            description: '',
            title: '',
          };
        }
        $scope.getContent(type, code);
        if(!$scope.$$phase) {
          $scope.$apply();
        }
      })
    }else {
      $scope.article = {
        attributeCode: code,
        attributeType: type,
        description: '',
        title: '',
      };
      $scope.getContent(type, code);
    }

  }
  $scope.$on('ckeditor.ready', function(){
    $scope.checkType($scope.type.type, $scope.code.code);
  })

}]);

<|endoftext|>


<commit_before>// ==UserScript==
// @name         Redirect OSRS Wiki
// @namespace    https://github.com/raku-cat/
// @version      1.0.1
// @description  Redirects old wikia pages to the new wiki, and replaces google links with new wiki links.
// @author       Raku <raku(at)raku(dot)party>
// @license      GPL version 3; https://www.gnu.org/licenses/gpl-3.0.txt
// @homepageURL  https://github.com/raku-cat/userscripts
// @updateURL    https://github.com/raku-cat/userscripts/raw/master/osrswiki_redir.user.js
// @downloadURL  https://github.com/raku-cat/userscripts/raw/master/osrswiki_redir.user.js
// @include      /^https?:\/\/(oldschoolrunescape\.wikia\.com|(www.)?google\.com)\/(wiki\/.+?\/?|search\?(q|client)\=.+?)/
// @grant        none
// @run-at       document-end
// ==/UserScript==

var hostname = location.hostname;
var pathname = location.pathname;
var new_wiki = "https://oldschool.runescape.wiki";
var google = "www.google.com";
var old_wiki = "oldschoolrunescape.wikia.com";

if (hostname == old_wiki) {
  var converted_url = convert_wiki(pathname);
  if (converted_url) {
    window.location.replace(converted_url);
  }
}

else if (hostname == google) {
  var old_wiki_result = document.querySelectorAll("a[href^='http://oldschoolrunescape.wikia.com']");
  for (var i=0; i<old_wiki_result.length; i++) {
    old_wiki_result[i].removeAttribute("onmousedown");
    var old_wiki_href = old_wiki_result[i].href;
    var old_wiki_path = old_wiki_href.split("oldschoolrunescape.wikia.com")[1];
    var new_wiki_href = convert_wiki(old_wiki_path);
    if (new_wiki_href) {
      old_wiki_result[i].href = new_wiki_href;
    }
  }
}

function convert_wiki(old_wiki_url) {
  var new_suffix = old_wiki_url.replace("\/wiki\/", "\/w\/");
  var new_wiki_url = new_wiki + new_suffix;
  return new_wiki_url;
}
<commit_msg>Trying again<commit_after>// ==UserScript==
// @name         Redirect OSRS Wiki
// @namespace    https://github.com/raku-cat/
// @version      1.1
// @description  Redirects old wikia pages to the new wiki, and replaces google links with new wiki links.
// @author       Raku <raku(at)raku(dot)party>
// @license      GPL version 3; https://www.gnu.org/licenses/gpl-3.0.txt
// @homepageURL  https://github.com/raku-cat/userscripts
// @updateURL    https://github.com/raku-cat/userscripts/raw/master/osrswiki_redir.user.js
// @downloadURL  https://github.com/raku-cat/userscripts/raw/master/osrswiki_redir.user.js
// @include      /^https?:\/\/(oldschoolrunescape\.wikia\.com|(www.)?google\.com)\/(wiki\/.+?\/?|search\?(q|client)\=.+?)/
// @grant        none
// @run-at       document-end
// ==/UserScript==

var hostname = location.hostname;
var pathname = location.pathname;
var new_wiki = "https://oldschool.runescape.wiki";
var google = "www.google.com";
var old_wiki = "oldschoolrunescape.wikia.com";

if (hostname == old_wiki) {
  var converted_url = convert_wiki(pathname);
  if (converted_url) {
    window.location.replace(converted_url);
  }
}

else if (hostname == google) {
  var old_wiki_result = document.querySelectorAll("a[href^='http://oldschoolrunescape.wikia.com']");
  for (var i=0; i<old_wiki_result.length; i++) {
    old_wiki_result[i].removeAttribute("onmousedown");
    var old_wiki_href = old_wiki_result[i].href;
    var old_wiki_path = old_wiki_href.split("oldschoolrunescape.wikia.com")[1];
    var new_wiki_href = convert_wiki(old_wiki_path);
    if (new_wiki_href) {
      old_wiki_result[i].href = new_wiki_href;
    }
  }
}

function convert_wiki(old_wiki_url) {
  var new_suffix = old_wiki_url.replace("\/wiki\/", "\/w\/");
  var new_wiki_url = new_wiki + new_suffix;
  return new_wiki_url;
}
<|endoftext|>