<commit_before>import qs from 'qs';
import 'whatwg-fetch'; // fetch polyfill

import { includes, isEmpty, merge, extend, over } from 'lodash';
import { action } from 'mobx';
import Config     from '../config';
import { logger } from '../lib/util';

const methodMap = {
    create:  'POST',
    update:  'PUT',
    patch:   'PATCH',
    destroy: 'DELETE',
    read:    'GET',
};

const paramsMap = {
    fields:  'f',
    with:    'w',
    query:   'q',
    include: 'i',
    order:   'o',
    limit:   'l',
    start:   's',
    format:  'df',
};

function isUpdate(method) {
    return includes(['POST', 'PUT', 'PATCH'], method);
}

class SyncProgess {
    constructor(options) {
        extend(this, options);
        this._whenComplete = [];
    }
    whenComplete(fn) {
        this._whenComplete.push(fn);
    }
    get isCreate() { return 'POST' === this.method;  }
    get isUpdate() { return isUpdate(this.method); }
}

function invokeWhenComplete(json, progress) {
    return progress._whenComplete.reverse().reduce((p, fn) => p.then(() => {
        const ret = fn(json);
        return (ret && ret.then) ? ret : Promise.resolve();
    }), Promise.resolve());
}

function perform(urlPrefix, defaultOptions = {}) {
    const query   = {};
    const options = {};

    Object.keys(defaultOptions).forEach((key) => {
        const value = defaultOptions[key];
        if (paramsMap[key]) {
            query[paramsMap[key]] = value;
        } else {
            options[key] = value;
        }
    });

    let url = `${Config.api_host}${urlPrefix}.json`;
    if (Config.access_token) {
        query.jwt = Config.access_token;
    }
    if (!isEmpty(query)) {
        url += `?${qs.stringify(query, { arrayFormat: 'brackets' })}`;
    }
    if (!options.headers) { options.headers = {}; }

    options.headers['Content-Type'] = 'application/json';
    return fetch(url, options)
        .then(resp => resp.json())
        .then((json) => {
            if (json.token) { Config.access_token = json.token; }
            return json;
        });
}

const peformMobxyRequest = action('SyncforModel', (mobxObj, options = {}) => {
    mobxObj.syncInProgress = new SyncProgess(options);  // eslint-disable-line no-param-reassign
    return perform(options.url || mobxObj.syncUrl, options)
        .then(action('syncSuccessHandler', (json) => {
            extend(mobxObj, {
                errors:            json.errors,
                syncData:          json.data,
                lastServerMessage: json.message,
            });
            // sometimes the polyfill (or fetch iteself) sets a `:success` property?
            if (false === json[':success']) { merge(mobxObj, { errors: { fetch: json[':message'] } }); }
            return json;
        }))
        .then(action('whenComplete', json => invokeWhenComplete(json, mobxObj.syncInProgress)))
        .then(action('syncDoneHandler', () => {
            extend(mobxObj, {
                syncInProgress: undefined,
            });
            return mobxObj;
        }))
        .catch(action('syncErrorHandler', (e) => {
            logger.warn(e);
            extend(mobxObj, {
                errors:            { network: e },
                syncInProgress:     undefined,
                lastServerMessage:  e.toString(),
            });
            return {};
        }));
});

function forCollection(collection, options = {}) {
    return peformMobxyRequest(collection, options);
}

const forModel = action('SyncforModel', (model, options = {}) => {
    const httpAction = options.action || (model.isNew ? 'create' : 'update');
    const requestOptions = merge({
        method: methodMap[httpAction],
    }, options);

    if (isUpdate(requestOptions.method)) {
        requestOptions.body = JSON.stringify(options.json || model.syncData);
    }
    return peformMobxyRequest(model, requestOptions);
});

export default {
    forModel,
    forCollection,
    perform,
};
<commit_msg>method to detect fetch<commit_after>import qs from 'qs';
import 'whatwg-fetch'; // fetch polyfill

import { includes, isEmpty, merge, extend, over } from 'lodash';
import { action } from 'mobx';
import Config     from '../config';
import { logger } from '../lib/util';

const methodMap = {
    create:  'POST',
    update:  'PUT',
    patch:   'PATCH',
    destroy: 'DELETE',
    read:    'GET',
};

const paramsMap = {
    fields:  'f',
    with:    'w',
    query:   'q',
    include: 'i',
    order:   'o',
    limit:   'l',
    start:   's',
    format:  'df',
};

function isUpdate(method) {
    return includes(['POST', 'PUT', 'PATCH'], method);
}

class SyncProgess {
    constructor(options) {
        extend(this, options);
        this._whenComplete = [];
    }
    whenComplete(fn) {
        this._whenComplete.push(fn);
    }
    get isFetch() { return 'GET' === this.method; }
    get isCreate() { return 'POST' === this.method; }
    get isUpdate() { return isUpdate(this.method); }
}

function invokeWhenComplete(json, progress) {
    return progress._whenComplete.reverse().reduce((p, fn) => p.then(() => {
        const ret = fn(json);
        return (ret && ret.then) ? ret : Promise.resolve();
    }), Promise.resolve());
}

function perform(urlPrefix, defaultOptions = {}) {
    const query   = {};
    const options = {};

    Object.keys(defaultOptions).forEach((key) => {
        const value = defaultOptions[key];
        if (paramsMap[key]) {
            query[paramsMap[key]] = value;
        } else {
            options[key] = value;
        }
    });

    let url = `${Config.api_host}${urlPrefix}.json`;
    if (Config.access_token) {
        query.jwt = Config.access_token;
    }
    if (!isEmpty(query)) {
        url += `?${qs.stringify(query, { arrayFormat: 'brackets' })}`;
    }
    if (!options.headers) { options.headers = {}; }

    options.headers['Content-Type'] = 'application/json';
    return fetch(url, options)
        .then(resp => resp.json())
        .then((json) => {
            if (json.token) { Config.access_token = json.token; }
            return json;
        });
}

const peformMobxyRequest = action('SyncforModel', (mobxObj, options = {}) => {
    mobxObj.syncInProgress = new SyncProgess(options);  // eslint-disable-line no-param-reassign
    return perform(options.url || mobxObj.syncUrl, options)
        .then(action('syncSuccessHandler', (json) => {
            extend(mobxObj, {
                errors:            json.errors,
                syncData:          json.data,
                lastServerMessage: json.message,
            });
            // sometimes the polyfill (or fetch iteself) sets a `:success` property?
            if (false === json[':success']) { merge(mobxObj, { errors: { fetch: json[':message'] } }); }
            return json;
        }))
        .then(action('whenComplete', json => invokeWhenComplete(json, mobxObj.syncInProgress)))
        .then(action('syncDoneHandler', () => {
            extend(mobxObj, {
                syncInProgress: undefined,
            });
            return mobxObj;
        }))
        .catch(action('syncErrorHandler', (e) => {
            logger.warn(e);
            extend(mobxObj, {
                errors:            { network: e },
                syncInProgress:     undefined,
                lastServerMessage:  e.toString(),
            });
            return {};
        }));
});

function forCollection(collection, options = {}) {
    return peformMobxyRequest(collection, options);
}

const forModel = action('SyncforModel', (model, options = {}) => {
    const httpAction = options.action || (model.isNew ? 'create' : 'update');
    const requestOptions = merge({
        method: methodMap[httpAction],
    }, options);

    if (isUpdate(requestOptions.method)) {
        requestOptions.body = JSON.stringify(options.json || model.syncData);
    }
    return peformMobxyRequest(model, requestOptions);
});

export default {
    forModel,
    forCollection,
    perform,
};
<|endoftext|>


<commit_before>;(function () {
    "use strict";
    if (window.substringMatcher == undefined) {
        window.substringMatcher = function (value_list) {
            var clearText = function (x) {
                return S(x).latinise().s;
            };
            return function findMatches(q, cb) {
                var matches = [];
                var substrRegex = new RegExp(clearText(q), 'i');
                $.each(value_list, function (i, value) {
                    if (substrRegex.test(clearText(value['value']))) {
                        matches.push(value);
                    }
                });
                cb(matches);
            };
        };
    }
}());

(function ($) {
    "use strict";
    window.SingularTypeahead = {
        configure: function (container, valueField) {

            var clear =
                    "" +
                    "   " +
                    "",
                typeaheadField = '#' + container + ' > span  > input',
                $typeaheadField = $(typeaheadField);

            function updateValue(newValue) {
                var $valueField = $('#' + valueField);
                $valueField.val(newValue);
                $valueField.trigger('change');
            }

            function onClear() {
                var _$typeaheadField = $(typeaheadField);
                $(this).remove();
                _$typeaheadField.typeahead('val', '');
                _$typeaheadField.removeAttr('readonly');
                updateValue('');
            }

            function triggerKeydown(component, keyCode) {
                var e = $.Event('keydown');
                e.keyCode = e.which = keyCode;
                $(component).trigger(e);
            }

            function focusNextComponent(keydownEvent) {
                var focusables = $(":focusable");
                $(focusables[focusables.index($(typeaheadField)) + (keydownEvent.shiftKey ? -1 : 1)]).focus();
            }

            function appendClearButton() {
                var _$typeaheadField = $(typeaheadField);
                _$typeaheadField.attr('readonly', true);
                _$typeaheadField.after(clear);
                $('#' + container + '_clear').on('click', onClear);
            }

            $typeaheadField.on('typeahead:selected', function (event, selection) {
                var _$typeaheadField = $(typeaheadField);
                updateValue(selection.key);
                appendClearButton();
                _$typeaheadField.blur();
            });

            $typeaheadField.on('keydown', function (keydownEvent) {
                var code = keydownEvent.keyCode || keydownEvent.which;
                if (code === 9) {
                    if ($(this).val()) {
                        keydownEvent.preventDefault();
                        triggerKeydown(this, 40);
                        triggerKeydown(this, 13);
                        focusNextComponent(keydownEvent);
                    }
                }
            });

            if ($typeaheadField.val()) {
                appendClearButton();
            }
        }
    };
}(jQuery));<commit_msg>[Typeahead][JS] Escutando eventos de delete e backspace<commit_after>;(function () {
    "use strict";
    if (window.substringMatcher == undefined) {
        window.substringMatcher = function (value_list) {
            var clearText = function (x) {
                return S(x).latinise().s;
            };
            return function findMatches(q, cb) {
                var matches = [];
                var substrRegex = new RegExp(clearText(q), 'i');
                $.each(value_list, function (i, value) {
                    if (substrRegex.test(clearText(value['value']))) {
                        matches.push(value);
                    }
                });
                cb(matches);
            };
        };
    }
}());

(function ($) {
    "use strict";
    window.SingularTypeahead = {
        configure: function (container, valueField) {

            var clear =
                    "" +
                    "   " +
                    "",
                typeaheadField = '#' + container + ' > span  > input',
                $typeaheadField = $(typeaheadField);

            function updateValue(newValue) {
                var $valueField = $('#' + valueField);
                $valueField.val(newValue);
                $valueField.trigger('change');
            }

            function onClear() {
                var _$typeaheadField = $(typeaheadField);
                $(this).remove();
                _$typeaheadField.typeahead('val', '');
                _$typeaheadField.removeAttr('readonly');
                updateValue('');
            }

            function triggerKeydown(component, keyCode) {
                var e = $.Event('keydown');
                e.keyCode = e.which = keyCode;
                $(component).trigger(e);
            }

            function focusNextComponent(keydownEvent) {
                var focusables = $(":focusable");
                $(focusables[focusables.index($(typeaheadField)) + (keydownEvent.shiftKey ? -1 : 1)]).focus();
            }

            function appendClearButton() {
                var _$typeaheadField = $(typeaheadField);
                _$typeaheadField.attr('readonly', true);
                _$typeaheadField.after(clear);
                $('#' + container + '_clear').on('click', onClear);
            }

            $typeaheadField.on('typeahead:selected', function (event, selection) {
                var _$typeaheadField = $(typeaheadField);
                updateValue(selection.key);
                appendClearButton();
                _$typeaheadField.blur();
            });

            $typeaheadField.on('keydown', function (keydownEvent) {
                var code = keydownEvent.keyCode || keydownEvent.which;
                if (code === 9) {
                    if ($(this).val()) {
                        keydownEvent.preventDefault();
                        triggerKeydown(this, 40);
                        triggerKeydown(this, 13);
                        focusNextComponent(keydownEvent);
                    }
                }
                if (code === 8 || code === 46) {
                    if ($(this).val()) {
                        keydownEvent.preventDefault();
                        $('#' + container + '_clear').click();
                    }
                }
            });

            if ($typeaheadField.val()) {
                appendClearButton();
            }
        }
    };
}(jQuery));<|endoftext|>


<commit_before>'use strict';

var SingleTreatment = module.exports.SingleTreatment = function(treatment) {
    var treatmentText = '';

    if (treatment.concentration) {
        treatmentText += treatment.concentration + (treatment.concentration_units ? ' ' + treatment.concentration_units : '') + ' ';
    }
    treatmentText += treatment.treatment_term_name + (treatment.treatment_term_id ? ' (' + treatment.treatment_term_id + ')' : '') + ' ';
    if (treatment.duration) {
        treatmentText += 'for ' + treatment.duration + ' ' + (treatment.duration_units ? treatment.duration_units : '');
    }
    return treatmentText;
};

/**
 * Check if JS is processing on serverside, vs in browser (clientside).
 * Adapted from react/node_modules/fbjs/lib/ExecutionEnvironment.canUseDOM()
 *
 * @return {boolean} - True if processing on serverside.
 */
var isServerSide = module.exports.isServerSide = function(){
    if (typeof window == 'undefined' || !window || !window.document || !window.document.createElement){
        return true;
    }
    return false;
}

var setJWTHeaders = function(xhr, headers = {}) {
        if (typeof headers["Content-Type"] == 'undefined'){
          headers["Content-Type"] = "application/json;charset=UTF-8";
          headers['Accept'] = 'application/json';
        }

        var userInfo = localStorage.getItem('user_info') || null;
        var idToken = userInfo ? JSON.parse(userInfo).id_token : null;

        //Add req'd headers if not exist already
        if(userInfo && typeof headers['Authorization'] == 'undefined'){
          headers['Authorization'] = 'Bearer '+idToken;
        }

        // put everything in the header
        var headerKeys = Object.keys(headers);
        for (var i=0; i < headerKeys.length; i++){
          xhr.setRequestHeader(headerKeys[i], headers[headerKeys[i]]);
        }

        return xhr;
      }

/**
 * Check if process.env.NODE_ENV is not on 'production'.
 *
 * @return {boolean} - True if NODE_ENV != 'production'.
 */
var isDebugging = module.exports.isDebugging = function(){
    // process.env.NODE_ENV is set in webpack.config.js if running 'npm run build'
    if (typeof process !== 'undefined' && process.env && process.env.NODE_ENV === 'production') {
        return false;
    }
    return true;
}

/**
 * Custom patched console for debugging. Only print out statements if debugging/development environment.
 * Prevent potential issues where console might not be available (earlier IE).
 */
var patchedConsole = module.exports.console = (function(){

    if (!isServerSide() && window.patchedConsole) return window.patchedConsole; // Re-use instance if available.

    var PatchedConsole = function(){
        this._initArgs = arguments; // arguments variable contains any arguments passed to function in an array.
        this._enabled = true; // Default
        this._available = true;

        if (!console || !console.log) { // Check for seldomly incompatible browsers
            this._available = false;
        }

        if (!isDebugging) {
            this._enabled = false; // Be silent on production.
        }

        this._methods = ['log', 'assert', 'dir', 'error', 'info', 'warn', 'clear', 'profile', 'profileEnd'];
        this._nativeConsole = console;

        this._patchMethods = function(){
            this._methods.forEach(function(methodName){
                if (!(this._enabled && this._available)) {
                    this[methodName] = function(){return false;};
                } else {
                    this[methodName] = this._nativeConsole[methodName];
                }
            }.bind(this));
        }.bind(this);

        // Ability to override, e.g. on production.
        this.on = function(){
            this._enabled = true;
            this._patchMethods();
        }.bind(this);

        this.off = function(){
            this._enabled = false;
            this._patchMethods();
        }.bind(this);

        this._patchMethods();
    }

    var patchedConsole = new PatchedConsole();

    if (!isServerSide()) {
        window.patchedConsole = patchedConsole;
    }
    return patchedConsole;
})();

var ajaxLoad = module.exports.ajaxLoad = function(url, callback, method = 'GET', fallback = null, data = null, headers = {}){
    if (typeof window == 'undefined') return null;
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function() {
        if (xhr.readyState == XMLHttpRequest.DONE ) {
            if (xhr.status == 200) {
                if (typeof callback == 'function'){
                    callback(JSON.parse(xhr.responseText));
                }
            } else if (xmlhttp.status == 400) {
                (patchedConsole || console).error('There was an error 400');
                if (typeof fallback == 'function'){
                    fallback();
                }
            } else {
                (patchedConsole || console).error('something else other than 200 was returned');
                if (typeof fallback == 'function'){
                    fallback();
                }
            }
        }
    };
    xhr.open(method, url, true);
    xhr = setJWTHeaders(xhr, headers);
    if(data){
        xhr.send(data);
    }else{
        xhr.send();
    }
}

var ajaxPromise = module.exports.ajaxPromise = function(url, method, headers = {}, data = null){
    return new Promise(function(resolve, reject) {
        var xhr = new XMLHttpRequest();
        xhr.onload = function() {
            // response SHOULD be json
            resolve(JSON.parse(xhr.responseText));
        };
        xhr.onerror = reject;
        xhr.open(method, url, true);
        xhr = setJWTHeaders(xhr, headers);
        if(data){
            xhr.send(data);
        }else{
            xhr.send();
        }
    });
}

/**
 * Format a timestamp to pretty output. Uses moment.js, which uses Date() object in underlying code.
 *
 * @param {string} timestamp - Timestamp as provided by server output. No timezone corrections currently.
 * @param {string} [outputFormat] - Defaults to "MMMM Do, YYYY" for, e.g. "October 31st, 2016".
 * @return {string} Prettified date/time output.
 */
var parseDateTime = module.exports.parseDateTime = function(timestamp, outputFormat = "MMMM Do, YYYY"){
    if (!Date) {
        return timestamp; // Date object may or may not be available server-side.
    } else {
        var moment = require('moment'); // require allows to load code in conditionally, so lets do that until more funcs require moment.
        return moment(timestamp).format(outputFormat);
    }
};

var DateUtility = module.exports.DateUtility = (function(){

    // ToDo : Handle locales (w/ moment)

    // 'require' allows to load code in conditionally, so lets do that here until more funcs require moment.
    var moment = require('moment');

    // Class itself, if need to create non-static instance at some point.
    var DateUtility = function(timestamp = null){
        this._dateClassExists = DateUtility.dateClassExists();
        this._timestamp = timestamp || null;
        this._moment = timestamp && this._dateClassExists ? moment(timestamp) : null;

        this.format = function(outputFormat){
            return this._moment.format(outputFormat);
        }
    };

    // Static Class Methods

    /** Check that Date class/object exists in execution environment. */
    DateUtility.dateClassExists = function(){ return !!Date; };

    /**
     * Presets for date/time output formats for 4DN.
     * Uses bootstrap grid sizing name convention, so may utilize with responsiveGridState
     * to set responsively according to screen size, e.g. in a (debounced/delayed) window
     * resize event listener.
     *
     * @see responsiveGridState
     * @param {string} [formatType] - Key for date/time format to display. Defaults to 'date-md'.
     * @param {string} [dateTimeSeparator] - Separator between date and time if formatting a date-time. Defaults to ' '.
     */
    DateUtility.preset = function(formatType = 'date-md', dateTimeSeparator = " "){

        function date(ft){
            switch(ft){
                case 'date-xs':
                    // 11/03/2016
                    return "MM/DD/YYYY";
                case 'date-sm':
                    // Nov 3rd, 2016
                    return "MMM Do, YYYY";
                case 'date-md':
                    // November 3rd, 2016   (default)
                    return "MMMM Do, YYYY";
                case 'date-lg':
                    // Thursday, November 3rd, 2016
                    return "dddd, MMMM Do, YYYY";
            }
        }

        function time(ft){
            switch(ft){
                case 'time-xs':
                    // 12pm
                    return "ha";
                case 'time-sm':
                case 'time-md':
                    // 12:27pm
                    return "h:mma";
                case 'time-lg':
                    // 12:27:34 pm
                    return "h:mm:ss a";
            }
        }

        if (formatType.indexOf('date-time-') > -1){
            return date(formatType.replace('time-','')) + '[' + dateTimeSeparator + ']' + time(formatType.replace('date-',''));
        } else if (formatType.indexOf('date-') > -1){
            return date(formatType);
        } else if (formatType.indexOf('time-') > -1){
            return time(formatType);
        }
        return null;
    };

    /**
     * Format a timestamp to pretty output. Uses moment.js, which uses Date() object in underlying code.
     * @see DateUtility.preset
     *
     * @param {string} timestamp - Timestamp as provided by server output. No timezone corrections currently.
     * @param {string} [formatType] - Preset format to use. Defaults to 'date-md', e.g. "October 31st, 2016".
     * @param {string} [customOutputFormat] - Custom format to use in lieu of formatType.
     * @return {string} Prettified date/time output.
     */
    DateUtility.format = function(timestamp, formatType = 'date-md', dateTimeSeparator = " ", customOutputFormat = null){

        var outputFormat;
        if (customOutputFormat) {
            outputFormat = customOutputFormat;
        } else {
            outputFormat = DateUtility.preset(formatType, dateTimeSeparator);
        }

        return moment.utc(timestamp).format(outputFormat);
    };

    return DateUtility;
})();

/**
 * Check width of text or text-like content if it were to fit on one line.
 * @param {string} textContent - Either text or text-like content, e.g. with span elements.
 * @param {string} [containerElementType] - Type of element to fit into, e.g. 'div' or 'p'.
 * @param {string} [containerClassName] - ClassName of containing element, e.g. with 'text-large' to use larger text size.
 * @param {integer} [widthForHeightCheck] - If provided, will return an object which will return height of text content when constrained to width.
 * @return {integer|Object} - Width of text if whitespace style set to nowrap, or object containing 'containerHeight' & 'textWidth' if widthForHeightCheck is set.
 */
var textContentWidth = module.exports.textContentWidth = function(
    textContent,
    containerElementType = 'div',
    containerClassName = '',
    widthForHeightCheck = null
){
    if (isServerSide()){
        return null;
    };
    var contElem = document.createElement(containerElementType);
    contElem.className = "off-screen " + containerClassName;
    contElem.innerHTML = textContent;
    contElem.style.whiteSpace = "nowrap";
    document.body.appendChild(contElem);
    var textLineWidth = contElem.clientWidth;
    var fullContainerHeight;
    if (widthForHeightCheck){
        contElem.style.whiteSpace = "";
        contElem.style.display = "block";
        contElem.style.width = widthForHeightCheck + "px";
        fullContainerHeight = contElem.clientHeight;
    }
    document.body.removeChild(contElem);
    if (fullContainerHeight) {
        return { containerHeight : fullContainerHeight, textWidth : textLineWidth };
    }
    return textLineWidth;
}

/**
 * Get the width of what a 12-column bootstrap section would be in current viewport size.
 * Keep widths in sync with stylesheet, e.g.
 * $container-tablet - $grid-gutter-width,
 * $container-desktop - $grid-gutter-width, and
 * $container-large-desktop - $grid-gutter-width
 * in src/encoded/static/scss/bootstrap/_variables.scss.
 *
 * @return {integer}
 */
var gridContainerWidth = module.exports.gridContainerWidth = function(){
    // Subtract 20 for padding/margins.
    switch(responsiveGridState()){
        case 'lg': return 1140;
        case 'md': return 940;
        case 'sm': return 720;
        case 'xs':
            if (isServerSide()) return 400;
            return window.innerWidth - 20;
    }

};

/**
 * Get current grid size, if need to sidestep CSS.
 * Keep widths in sync with stylesheet, e.g. $screen-sm-min, $screen-md-min, & $screen-lg-min
 * in src/encoded/static/scss/bootstrap/_variables.scss.
 *
 * @return {string} - Abbreviation for column/grid Bootstrap size, e.g. 'lg', 'md', 'sm', or 'xs'.
 */

var responsiveGridState = module.exports.responsiveGridState = function(){
    if (isServerSide()) return 'lg';
    if (window.innerWidth >= 1200) return 'lg';
    if (window.innerWidth >= 992) return 'md';
    if (window.innerWidth >= 768) return 'sm';
    return 'xs';
};
<commit_msg>Fixing codacy complaints<commit_after>'use strict';

var SingleTreatment = module.exports.SingleTreatment = function(treatment) {
    var treatmentText = '';

    if (treatment.concentration) {
        treatmentText += treatment.concentration + (treatment.concentration_units ? ' ' + treatment.concentration_units : '') + ' ';
    }
    treatmentText += treatment.treatment_term_name + (treatment.treatment_term_id ? ' (' + treatment.treatment_term_id + ')' : '') + ' ';
    if (treatment.duration) {
        treatmentText += 'for ' + treatment.duration + ' ' + (treatment.duration_units ? treatment.duration_units : '');
    }
    return treatmentText;
};

/**
 * Check if JS is processing on serverside, vs in browser (clientside).
 * Adapted from react/node_modules/fbjs/lib/ExecutionEnvironment.canUseDOM()
 *
 * @return {boolean} - True if processing on serverside.
 */
var isServerSide = module.exports.isServerSide = function(){
    if (typeof window == 'undefined' || !window || !window.document || !window.document.createElement){
        return true;
    }
    return false;
}

var setJWTHeaders = function(xhr, headers = {}) {
    if (typeof headers["Content-Type"] == 'undefined'){
        headers["Content-Type"] = "application/json;charset=UTF-8";
        headers['Accept'] = 'application/json';
    }

    var userInfo = localStorage.getItem('user_info') || null;
    var idToken = userInfo ? JSON.parse(userInfo).id_token : null;

    //Add req'd headers if not exist already
    if(userInfo && typeof headers['Authorization'] == 'undefined'){
        headers['Authorization'] = 'Bearer '+idToken;
    }

    // put everything in the header
    var headerKeys = Object.keys(headers);
    for (var i=0; i < headerKeys.length; i++){
        xhr.setRequestHeader(headerKeys[i], headers[headerKeys[i]]);
    }

    return xhr;
}

/**
 * Check if process.env.NODE_ENV is not on 'production'.
 *
 * @return {boolean} - True if NODE_ENV != 'production'.
 */
var isDebugging = module.exports.isDebugging = function(){
    // process.env.NODE_ENV is set in webpack.config.js if running 'npm run build'
    if (typeof process !== 'undefined' && process.env && process.env.NODE_ENV === 'production') {
        return false;
    }
    return true;
}

/**
 * Custom patched console for debugging. Only print out statements if debugging/development environment.
 * Prevent potential issues where console might not be available (earlier IE).
 */
var patchedConsole = module.exports.console = (function(){

    if (!isServerSide() && window.patchedConsole) return window.patchedConsole; // Re-use instance if available.

    var PatchedConsole = function(){
        this._initArgs = arguments; // arguments variable contains any arguments passed to function in an array.
        this._enabled = true; // Default
        this._available = true;

        if (!console || !console.log) { // Check for seldomly incompatible browsers
            this._available = false;
        }

        if (!isDebugging) {
            this._enabled = false; // Be silent on production.
        }

        this._methods = ['log', 'assert', 'dir', 'error', 'info', 'warn', 'clear', 'profile', 'profileEnd'];
        this._nativeConsole = console;

        this._patchMethods = function(){
            this._methods.forEach(function(methodName){
                if (!(this._enabled && this._available)) {
                    this[methodName] = function(){return false;};
                } else {
                    this[methodName] = this._nativeConsole[methodName];
                }
            }.bind(this));
        }.bind(this);

        // Ability to override, e.g. on production.
        this.on = function(){
            this._enabled = true;
            this._patchMethods();
        }.bind(this);

        this.off = function(){
            this._enabled = false;
            this._patchMethods();
        }.bind(this);

        this._patchMethods();
    }

    var patchedConsole = new PatchedConsole();

    if (!isServerSide()) {
        window.patchedConsole = patchedConsole;
    }
    return patchedConsole;
})();

var ajaxLoad = module.exports.ajaxLoad = function(url, callback, method = 'GET', fallback = null, data = null, headers = {}){
    if (typeof window == 'undefined') return null;
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function() {
        if (xhr.readyState == XMLHttpRequest.DONE ) {
            if (xhr.status == 200) {
                if (typeof callback == 'function'){
                    callback(JSON.parse(xhr.responseText));
                }
            } else if (xmlhttp.status == 400) {
                (patchedConsole || console).error('There was an error 400');
                if (typeof fallback == 'function'){
                    fallback();
                }
            } else {
                (patchedConsole || console).error('something else other than 200 was returned');
                if (typeof fallback == 'function'){
                    fallback();
                }
            }
        }
    };
    xhr.open(method, url, true);
    xhr = setJWTHeaders(xhr, headers);
    if(data){
        xhr.send(data);
    }else{
        xhr.send();
    }
}

var ajaxPromise = module.exports.ajaxPromise = function(url, method, headers = {}, data = null){
    return new Promise(function(resolve, reject) {
        var xhr = new XMLHttpRequest();
        xhr.onload = function() {
            // response SHOULD be json
            resolve(JSON.parse(xhr.responseText));
        };
        xhr.onerror = reject;
        xhr.open(method, url, true);
        xhr = setJWTHeaders(xhr, headers);
        if(data){
            xhr.send(data);
        }else{
            xhr.send();
        }
    });
}

/**
 * Format a timestamp to pretty output. Uses moment.js, which uses Date() object in underlying code.
 *
 * @param {string} timestamp - Timestamp as provided by server output. No timezone corrections currently.
 * @param {string} [outputFormat] - Defaults to "MMMM Do, YYYY" for, e.g. "October 31st, 2016".
 * @return {string} Prettified date/time output.
 */
var parseDateTime = module.exports.parseDateTime = function(timestamp, outputFormat = "MMMM Do, YYYY"){
    if (!Date) {
        return timestamp; // Date object may or may not be available server-side.
    } else {
        var moment = require('moment'); // require allows to load code in conditionally, so lets do that until more funcs require moment.
        return moment(timestamp).format(outputFormat);
    }
};

var DateUtility = module.exports.DateUtility = (function(){

    // ToDo : Handle locales (w/ moment)

    // 'require' allows to load code in conditionally, so lets do that here until more funcs require moment.
    var moment = require('moment');

    // Class itself, if need to create non-static instance at some point.
    var DateUtility = function(timestamp = null){
        this._dateClassExists = DateUtility.dateClassExists();
        this._timestamp = timestamp || null;
        this._moment = timestamp && this._dateClassExists ? moment(timestamp) : null;

        this.format = function(outputFormat){
            return this._moment.format(outputFormat);
        }
    };

    // Static Class Methods

    /** Check that Date class/object exists in execution environment. */
    DateUtility.dateClassExists = function(){ return !!Date; };

    /**
     * Presets for date/time output formats for 4DN.
     * Uses bootstrap grid sizing name convention, so may utilize with responsiveGridState
     * to set responsively according to screen size, e.g. in a (debounced/delayed) window
     * resize event listener.
     *
     * @see responsiveGridState
     * @param {string} [formatType] - Key for date/time format to display. Defaults to 'date-md'.
     * @param {string} [dateTimeSeparator] - Separator between date and time if formatting a date-time. Defaults to ' '.
     */
    DateUtility.preset = function(formatType = 'date-md', dateTimeSeparator = " "){

        function date(ft){
            switch(ft){
                case 'date-xs':
                    // 11/03/2016
                    return "MM/DD/YYYY";
                case 'date-sm':
                    // Nov 3rd, 2016
                    return "MMM Do, YYYY";
                case 'date-md':
                    // November 3rd, 2016   (default)
                    return "MMMM Do, YYYY";
                case 'date-lg':
                    // Thursday, November 3rd, 2016
                    return "dddd, MMMM Do, YYYY";
            }
        }

        function time(ft){
            switch(ft){
                case 'time-xs':
                    // 12pm
                    return "ha";
                case 'time-sm':
                case 'time-md':
                    // 12:27pm
                    return "h:mma";
                case 'time-lg':
                    // 12:27:34 pm
                    return "h:mm:ss a";
            }
        }

        if (formatType.indexOf('date-time-') > -1){
            return date(formatType.replace('time-','')) + '[' + dateTimeSeparator + ']' + time(formatType.replace('date-',''));
        } else if (formatType.indexOf('date-') > -1){
            return date(formatType);
        } else if (formatType.indexOf('time-') > -1){
            return time(formatType);
        }
        return null;
    };

    /**
     * Format a timestamp to pretty output. Uses moment.js, which uses Date() object in underlying code.
     * @see DateUtility.preset
     *
     * @param {string} timestamp - Timestamp as provided by server output. No timezone corrections currently.
     * @param {string} [formatType] - Preset format to use. Defaults to 'date-md', e.g. "October 31st, 2016".
     * @param {string} [customOutputFormat] - Custom format to use in lieu of formatType.
     * @return {string} Prettified date/time output.
     */
    DateUtility.format = function(timestamp, formatType = 'date-md', dateTimeSeparator = " ", customOutputFormat = null){

        var outputFormat;
        if (customOutputFormat) {
            outputFormat = customOutputFormat;
        } else {
            outputFormat = DateUtility.preset(formatType, dateTimeSeparator);
        }

        return moment.utc(timestamp).format(outputFormat);
    };

    return DateUtility;
})();

/**
 * Check width of text or text-like content if it were to fit on one line.
 * @param {string} textContent - Either text or text-like content, e.g. with span elements.
 * @param {string} [containerElementType] - Type of element to fit into, e.g. 'div' or 'p'.
 * @param {string} [containerClassName] - ClassName of containing element, e.g. with 'text-large' to use larger text size.
 * @param {integer} [widthForHeightCheck] - If provided, will return an object which will return height of text content when constrained to width.
 * @return {integer|Object} - Width of text if whitespace style set to nowrap, or object containing 'containerHeight' & 'textWidth' if widthForHeightCheck is set.
 */
var textContentWidth = module.exports.textContentWidth = function(
    textContent,
    containerElementType = 'div',
    containerClassName = '',
    widthForHeightCheck = null
){
    if (isServerSide()){
        return null;
    };
    var contElem = document.createElement(containerElementType);
    contElem.className = "off-screen " + containerClassName;
    contElem.innerHTML = textContent;
    contElem.style.whiteSpace = "nowrap";
    document.body.appendChild(contElem);
    var textLineWidth = contElem.clientWidth;
    var fullContainerHeight;
    if (widthForHeightCheck){
        contElem.style.whiteSpace = "";
        contElem.style.display = "block";
        contElem.style.width = widthForHeightCheck + "px";
        fullContainerHeight = contElem.clientHeight;
    }
    document.body.removeChild(contElem);
    if (fullContainerHeight) {
        return { containerHeight : fullContainerHeight, textWidth : textLineWidth };
    }
    return textLineWidth;
}

/**
 * Get the width of what a 12-column bootstrap section would be in current viewport size.
 * Keep widths in sync with stylesheet, e.g.
 * $container-tablet - $grid-gutter-width,
 * $container-desktop - $grid-gutter-width, and
 * $container-large-desktop - $grid-gutter-width
 * in src/encoded/static/scss/bootstrap/_variables.scss.
 *
 * @return {integer}
 */
var gridContainerWidth = module.exports.gridContainerWidth = function(){
    // Subtract 20 for padding/margins.
    switch(responsiveGridState()){
        case 'lg': return 1140;
        case 'md': return 940;
        case 'sm': return 720;
        case 'xs':
            if (isServerSide()) return 400;
            return window.innerWidth - 20;
    }

};

/**
 * Get current grid size, if need to sidestep CSS.
 * Keep widths in sync with stylesheet, e.g. $screen-sm-min, $screen-md-min, & $screen-lg-min
 * in src/encoded/static/scss/bootstrap/_variables.scss.
 *
 * @return {string} - Abbreviation for column/grid Bootstrap size, e.g. 'lg', 'md', 'sm', or 'xs'.
 */

var responsiveGridState = module.exports.responsiveGridState = function(){
    if (isServerSide()) return 'lg';
    if (window.innerWidth >= 1200) return 'lg';
    if (window.innerWidth >= 992) return 'md';
    if (window.innerWidth >= 768) return 'sm';
    return 'xs';
};
<|endoftext|>


<commit_before>;(function () {
    "use strict";
    if (window.substringMatcher == undefined) {
        window.substringMatcher = function (value_list) {
            var clearText = function (x) {
                return S(x).latinise().s;
            };
            return function findMatches(q, cb) {
                var matches = [];
                var substrRegex = new RegExp(clearText(q), 'i');
                $.each(value_list, function (i, value) {
                    if (substrRegex.test(clearText(value['value']))) {
                        matches.push(value);
                    }
                });
                cb(matches);
            };
        };
    }
}());

(function ($) {
    "use strict";
    window.SingularTypeahead = {
        configure: function (container, valueField) {
        	var clearText = function (x) {return S(x).latinise().s.toUpperCase();};

            var clear =
                    "" +
                    "   " +
                    "",
            typeaheadField = '#' + container + ' > span  > input',
            $typeaheadField = $(typeaheadField),
            subscriberMetadata = {jumpToNext: false,factor: 0},
            clearCalled = false;

            var Typeahead_ = $typeaheadField.data('tt-typeahead');

            function hasAjaxUpdate() {
                var events = $('#' + valueField).data('events');
                if (events && events.hasOwnProperty('change')) {
                    for (var i = 0; i <= events.change.length; i += 1) {
                        if (events.change[i].handler.toString().indexOf('Wicket') > 0) {
                            return true;
                        }
                    }
                }
                return false;
            }
            if (hasAjaxUpdate()) {
                Wicket.Event.subscribe(Wicket.Event.Topic.AJAX_CALL_COMPLETE, function (e,at) {
                    if (subscriberMetadata.jumpToNext) {
                        var focusables = $(":focusable");
                        $(focusables[focusables.index($(typeaheadField)) + subscriberMetadata.factor]).focus();
                        subscriberMetadata.jumpToNext = false;
                        clearCalled = false;
                    }
                })
            }
            function updateValue(newValue) {
                var $valueField = $('#' + valueField);
                var oldValue = $valueField.val();
                $valueField.val(newValue);
                if(oldValue !== newValue){
                	$valueField.trigger('change');
                }
            }

            function onClear(e) {
            	clearCalled = true;
                $('#' + container + '_clear').remove();
                $typeaheadField.removeAttr('readonly');
                $typeaheadField.typeahead('val', '');
                Typeahead_.enable();
            	updateValue('');
                $typeaheadField.focus();
            }

            function focusNextComponent(keydownEvent) {
                if (hasAjaxUpdate()) {
                    subscriberMetadata.jumpToNext = true;
                    subscriberMetadata.factor = keydownEvent.shiftKey ? -1 : 1;
                } else {
                    var focusables = $(":focusable");
                    $(focusables[focusables.index($(typeaheadField)) + (keydownEvent.shiftKey ? -1 : 1)]).focus();
                }
            }

            function appendClearButton() {
                $typeaheadField.attr('readonly', true);
                $typeaheadField.after(clear);
                $('#' + container + '_clear').on('click', onClear);
                Typeahead_.disable();
            }

            $typeaheadField.on('typeahead:selected', function (event, selection) {
            	clearCalled = false;
                updateValue(selection.key);
                appendClearButton();
                if (subscriberMetadata.jumpToNext) {
                	$typeaheadField.trigger('blur');
                }
            });

            $typeaheadField.on('keydown', function (event) {
            	var code = event.keyCode || event.which;
            	if(code === 9 && hasAjaxUpdate()){
            		focusNextComponent(event);
            	}
            	// ativa o campo se estiver readonly e backspace ou delete
            	if ($typeaheadField.attr('readonly') && (code === 8) || (code === 46)) {
            		event.stopPropagation();
            		event.preventDefault();
            		onClear(event);
            	}
            });
            $typeaheadField.on('keypress', function (event) {
            	var code = event.keyCode || event.which;
            	// ativa o campo se estiver readonly e diferente de tab
            	if ($typeaheadField.attr('readonly') && code !== 9) {
            		event.preventDefault();
            		$typeaheadField.typeahead('val', String.fromCharCode(code));
            		$('#' + valueField).val(String.fromCharCode(code));

            		$('#' + container + '_clear').remove();
                    $typeaheadField.removeAttr('readonly');
                    Typeahead_.enable();
                    Typeahead_.open();
            	}
            });
            $typeaheadField.on('keyup', function (event) {
            	var code = event.keyCode || event.which;
            	//diferente de up arrow, down arrow, tab
            	if(!$typeaheadField.attr('readonly') && code !== 38 && code !== 40 && code !== 9 && $typeaheadField.val()){
            		// mantém o primeiro da lista selecionado
            		if(Typeahead_.menu.getActiveSelectable() !== Typeahead_.menu.getTopSelectable()){
            			Typeahead_.menu.setCursor(Typeahead_.menu.getTopSelectable());
            		}
            	} else if((code === 8 || code === 46) && !$typeaheadField.val() && !clearCalled){
            		event.preventDefault();
            		onClear(event);
            	}
            });
            //previne que ao teclar Tab, o primeiro item seja selecionado quando ninguém foi marcado
            Typeahead_._onTabKeyed = function onTabKeyed(type, $e) {
            	console.log('_onTabKeyed');
                var $selectable;
                if ($selectable = Typeahead_.menu.getActiveSelectable()) {
                	$e.preventDefault();
                	if (hasAjaxUpdate()) {
                		focusNextComponent($e);
                	}
                	Typeahead_.select($selectable);
                	if (!hasAjaxUpdate()) {
                		focusNextComponent($e);
                	}
                } else if(possuiDadoInvalido() && hasAjaxUpdate()){
                	// necessário para manter o foco após o ajax
                	focusNextComponent($e);
        		}
            }
            function possuiDadoInvalido(){
            	if(!$typeaheadField.attr('readonly') && $typeaheadField.val()){
        			var $selectad = $.grep(Typeahead_.menu._getSelectables(), function(obj,i){
        				return clearText($typeaheadField.val()) == clearText(Typeahead_.menu.getSelectableData($(obj)).val);
        			});
        			return Typeahead_.menu._getSelectables().size() == 0 || $selectad.length == 0;
        		}
            	return false;
            }
            // limpa o campo se tiver um valor inválido
            $typeaheadField.on('typeahead:change', function ($e, selection) {
            	console.log('change');
        		if(possuiDadoInvalido()){
        			$e.stopPropagation();
        			Typeahead_.close();
        			$typeaheadField.typeahead('val', '');
                    updateValue('');
        		} else if($typeaheadField.val()){
        			var $selectad = $.grep(Typeahead_.menu._getSelectables(), function(obj,i){
        				return clearText($typeaheadField.val()) === clearText(Typeahead_.menu.getSelectableData($(obj)).val);
        			});
        			if((Typeahead_.menu._getSelectables().size() == 1 || $selectad.length == 1) && $typeaheadField.val() !== Typeahead_.menu.getSelectableData($selectad).val){
        				$e.preventDefault();
        				Typeahead_.select($selectad);
        			}
        		}
            });
            if ($typeaheadField.val() && possuiDadoInvalido()) {
                appendClearButton();
            }
        }
    };
}(jQuery));<commit_msg>[FORM-UX] Correções no autocomplete<commit_after>;(function () {
    "use strict";
    if (window.substringMatcher == undefined) {
        window.substringMatcher = function (value_list) {
            var clearText = function (x) {
                return S(x).latinise().s;
            };
            return function findMatches(q, cb) {
                var matches = [];
                var substrRegex = new RegExp(clearText(q), 'i');
                $.each(value_list, function (i, value) {
                    if (substrRegex.test(clearText(value['value']))) {
                        matches.push(value);
                    }
                });
                cb(matches);
            };
        };
    }
}());

(function ($) {
    "use strict";
    window.SingularTypeahead = {
        configure: function (container, valueField) {
        	var clearText = function (x) {return S(x).latinise().s.toUpperCase();};

            var clear =
                    "" +
                    "   " +
                    "",
            typeaheadField = '#' + container + ' > span  > input',
            $typeaheadField = $(typeaheadField),
            subscriberMetadata = {jumpToNext: false,factor: 0},
            clearCalled = false;

            var Typeahead_ = $typeaheadField.data('tt-typeahead');

            function hasAjaxUpdate() {
                var events = $('#' + valueField).data('events');
                if (events && events.hasOwnProperty('change')) {
                    for (var i = 0; i <= events.change.length; i += 1) {
                        if (events.change[i].handler.toString().indexOf('Wicket') > 0) {
                            return true;
                        }
                    }
                }
                return false;
            }
            if (hasAjaxUpdate()) {
                Wicket.Event.subscribe(Wicket.Event.Topic.AJAX_CALL_COMPLETE, function (e,at) {
                    if (subscriberMetadata.jumpToNext) {
                        var focusables = $(":focusable");
                        $(focusables[focusables.index($(typeaheadField)) + subscriberMetadata.factor]).focus();
                        subscriberMetadata.jumpToNext = false;
                        clearCalled = false;
                    }
                })
            }
            function updateValue(newValue) {
                var $valueField = $('#' + valueField);
                var oldValue = $valueField.val();
                $valueField.val(newValue);
                if(oldValue !== newValue){
                	$valueField.trigger('change');
                }
            }

            function onClear(e) {
            	clearCalled = true;
                $('#' + container + '_clear').remove();
                $typeaheadField.removeAttr('readonly');
                $typeaheadField.typeahead('val', '');
                Typeahead_.enable();
            	updateValue('');
                $typeaheadField.focus();
            }

            function focusNextComponent(keydownEvent) {
                if (hasAjaxUpdate()) {
                    subscriberMetadata.jumpToNext = true;
                    subscriberMetadata.factor = keydownEvent.shiftKey ? -1 : 1;
                } else {
                    var focusables = $(":focusable");
                    $(focusables[focusables.index($(typeaheadField)) + (keydownEvent.shiftKey ? -1 : 1)]).focus();
                }
            }

            function appendClearButton() {
                $typeaheadField.attr('readonly', true);
                $typeaheadField.after(clear);
                $('#' + container + '_clear').on('click', onClear);
                Typeahead_.disable();
            }

            $typeaheadField.on('typeahead:selected', function (event, selection) {
            	clearCalled = false;
                updateValue(selection.key);
                appendClearButton();
                if (subscriberMetadata.jumpToNext) {
                	$typeaheadField.trigger('blur');
                }
            });

            $typeaheadField.on('keydown', function (event) {
            	var code = event.keyCode || event.which;
            	if(code === 9 && hasAjaxUpdate()){
            		focusNextComponent(event);
            	}
            	// ativa o campo se estiver readonly e backspace ou delete
            	if ($typeaheadField.attr('readonly') && (code === 8) || (code === 46)) {
            		event.stopPropagation();
            		event.preventDefault();
            		onClear(event);
            	}
            });
            $typeaheadField.on('keypress', function (event) {
            	var code = event.keyCode || event.which;
            	// ativa o campo se estiver readonly e diferente de tab
            	if ($typeaheadField.attr('readonly') && code !== 9) {
            		event.preventDefault();
            		$typeaheadField.typeahead('val', String.fromCharCode(code));
            		$('#' + valueField).val(String.fromCharCode(code));

            		$('#' + container + '_clear').remove();
                    $typeaheadField.removeAttr('readonly');
                    Typeahead_.enable();
                    Typeahead_.open();
            	}
            });
            $typeaheadField.on('keyup', function (event) {
            	var code = event.keyCode || event.which;
            	//diferente de up arrow, down arrow, tab
            	if(!$typeaheadField.attr('readonly') && code !== 38 && code !== 40 && code !== 9 && $typeaheadField.val()){
            		// mantém o primeiro da lista selecionado
            		if(Typeahead_.menu.getActiveSelectable() !== Typeahead_.menu.getTopSelectable()){
            			Typeahead_.menu.setCursor(Typeahead_.menu.getTopSelectable());
            		}
            	} else if((code === 8 || code === 46) && !$typeaheadField.val() && !clearCalled){
            		event.preventDefault();
            		onClear(event);
            	}
            });
            //previne que ao teclar Tab, o primeiro item seja selecionado quando ninguém foi marcado
            Typeahead_._onTabKeyed = function onTabKeyed(type, $e) {
                var $selectable;
                if ($selectable = Typeahead_.menu.getActiveSelectable()) {
                	$e.preventDefault();
                	if (hasAjaxUpdate()) {
                		focusNextComponent($e);
                	}
                	Typeahead_.select($selectable);
                	if (!hasAjaxUpdate()) {
                		focusNextComponent($e);
                	}
                } else if(possuiDadoInvalido() && hasAjaxUpdate()){
                	// necessário para manter o foco após o ajax
                	focusNextComponent($e);
        		}
            }
            function possuiDadoInvalido(){
            	if(!$typeaheadField.attr('readonly') && $typeaheadField.val()){
        			var $selectad = $.grep(Typeahead_.menu._getSelectables(), function(obj,i){
        				return clearText($typeaheadField.val()) == clearText(Typeahead_.menu.getSelectableData($(obj)).val);
        			});
        			return Typeahead_.menu._getSelectables().size() == 0 || $selectad.length == 0;
        		}
            	return false;
            }
            // limpa o campo se tiver um valor inválido
            $typeaheadField.on('typeahead:change', function ($e, selection) {
        		if(possuiDadoInvalido()){
        			$e.stopPropagation();
        			Typeahead_.close();
        			$typeaheadField.typeahead('val', '');
                    updateValue('');
        		} else if($typeaheadField.val()){
        			var $selectad = $.grep(Typeahead_.menu._getSelectables(), function(obj,i){
        				return clearText($typeaheadField.val()) === clearText(Typeahead_.menu.getSelectableData($(obj)).val);
        			});
        			if((Typeahead_.menu._getSelectables().size() == 1 || $selectad.length == 1) && $typeaheadField.val() !== Typeahead_.menu.getSelectableData($selectad).val){
        				$e.preventDefault();
        				Typeahead_.select($selectad);
        			}
        		}
            });
            if ($typeaheadField.val() && possuiDadoInvalido()) {
                appendClearButton();
            }
        }
    };
}(jQuery));<|endoftext|>


<commit_before>var path = require('path');
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require("extract-text-webpack-plugin");

require('es6-promise').polyfill();

module.exports = function(options) {
  var entry, jsLoaders, plugins, cssLoaders;
  // If production is true
  if (options.prod) {
    // Entry
    entry = [
      path.resolve(__dirname, 'js/app.js') // Start with js/app.js...
    ];
    cssLoaders = ExtractTextPlugin.extract('style-loader', 'css-loader!postcss-loader');
    // Plugins
    plugins = [// Plugins for Webpack
      new webpack.optimize.UglifyJsPlugin({ // Optimize the JavaScript...
        compress: {
          warnings: false // ...but do not show warnings in the console (there is a lot of them)
        }
      }),
      new HtmlWebpackPlugin({
        template: 'index.html', // Move the index.html file...
        minify: { // Minifying it while it is parsed
          removeComments: true,
          collapseWhitespace: true,
          removeRedundantAttributes: true,
          useShortDoctype: true,
          removeEmptyAttributes: true,
          removeStyleLinkTypeAttributes: true,
          keepClosingSlash: true,
          minifyJS: true,
          minifyCSS: true,
          minifyURLs: true
        },
        inject: true // inject all files that are generated by webpack, e.g. bundle.js, main.css with the correct HTML tags
      }),
      new ExtractTextPlugin("css/main.css"),
      new webpack.DefinePlugin({
        "process.env": {
          NODE_ENV: JSON.stringify("production"),
          API_BASE_URL: JSON.stringify(options.apiBaseUrl),
          GOOGLE_SETTINGS: JSON.stringify(options.googleSettings)
        }
      })
    ];

  // If app is in development
  } else {
    // Entry
    entry = [
      "webpack-dev-server/client?http://localhost:3000", // Needed for hot reloading
      "webpack/hot/only-dev-server", // See above
      path.resolve(__dirname, 'js/app.js') // Start with js/app.js...
    ];
    cssLoaders = 'style-loader!css-loader!postcss-loader';
    // Only plugin is the hot module replacement plugin
    plugins = [
      new webpack.HotModuleReplacementPlugin(), // Make hot loading work
      new HtmlWebpackPlugin({
        template: 'index.html', // Move the index.html file
        inject: true // inject all files that are generated by webpack, e.g. bundle.js, main.css with the correct HTML tags
      }),
      new webpack.DefinePlugin({
        "process.env": {
          NODE_ENV: JSON.stringify("development"),
          API_BASE_URL: JSON.stringify(options.apiBaseUrl),
          GOOGLE_SETTINGS: JSON.stringify(options.googleSettings)
        }
      })
    ]
  }

  return {
    entry: entry,
    output: { // Compile into js/build.js
      path: path.resolve(__dirname, 'build'),
      filename: "js/bundle.js"
    },
    module: {
      loaders: [{
          test: /\.js$/, // Transform all .js files required somewhere within an entry point...
          loader: 'babel', // ...with the specified loaders...
          exclude: path.join(__dirname, '/node_modules/') // ...except for the node_modules folder.
        }, {
          test:   /\.css$/, // Transform all .css files required somewhere within an entry point...
          loader: cssLoaders // ...with PostCSS
        }, {
          test: /\.jpe?g$|\.gif$|\.png$/i,
          loader: "url-loader?limit=10000"
        }
      ]
    },
    plugins: plugins,
    postcss: function() {
      return [
        require('postcss-import')({ // Import all the css files...
          glob: true,
          onImport: function (files) {
              files.forEach(this.addDependency); // ...and add dependecies from the main.css files to the other css files...
          }.bind(this) // ...so they get hot–reloaded when something changes...
        }),
        require('postcss-simple-vars')(), // ...then replace the variables...
        require('postcss-focus')(), // ...add a :focus to ever :hover...
        require('autoprefixer')({ // ...and add vendor prefixes...
          browsers: ['last 2 versions', 'IE > 8'] // ...supporting the last 2 major browser versions and IE 8 and up...
        }),
        require('postcss-reporter')({ // This plugin makes sure we get warnings in the console
          clearMessages: true
        })
      ];
    },
    target: "web", // Make web variables accessible to webpack, e.g. window
    stats: false, // Don't show stats in the console
    progress: true
  }
}
<commit_msg>Closes #84 Broken page refresh<commit_after>var path = require('path');
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require("extract-text-webpack-plugin");

require('es6-promise').polyfill();

module.exports = function(options) {
  var entry, jsLoaders, plugins, cssLoaders;
  // If production is true
  if (options.prod) {
    // Entry
    entry = [
      path.resolve(__dirname, 'js/app.js') // Start with js/app.js...
    ];
    cssLoaders = ExtractTextPlugin.extract('style-loader', 'css-loader!postcss-loader');
    // Plugins
    plugins = [// Plugins for Webpack
      new webpack.optimize.UglifyJsPlugin({ // Optimize the JavaScript...
        compress: {
          warnings: false // ...but do not show warnings in the console (there is a lot of them)
        }
      }),
      new HtmlWebpackPlugin({
        template: 'index.html', // Move the index.html file...
        minify: { // Minifying it while it is parsed
          removeComments: true,
          collapseWhitespace: true,
          removeRedundantAttributes: true,
          useShortDoctype: true,
          removeEmptyAttributes: true,
          removeStyleLinkTypeAttributes: true,
          keepClosingSlash: true,
          minifyJS: true,
          minifyCSS: true,
          minifyURLs: true
        },
        inject: true // inject all files that are generated by webpack, e.g. bundle.js, main.css with the correct HTML tags
      }),
      new ExtractTextPlugin("css/main.css"),
      new webpack.DefinePlugin({
        "process.env": {
          NODE_ENV: JSON.stringify("production"),
          API_BASE_URL: JSON.stringify(options.apiBaseUrl),
          GOOGLE_SETTINGS: JSON.stringify(options.googleSettings)
        }
      })
    ];

  // If app is in development
  } else {
    // Entry
    entry = [
      "webpack-dev-server/client?http://localhost:3000", // Needed for hot reloading
      "webpack/hot/only-dev-server", // See above
      path.resolve(__dirname, 'js/app.js') // Start with js/app.js...
    ];
    cssLoaders = 'style-loader!css-loader!postcss-loader';
    // Only plugin is the hot module replacement plugin
    plugins = [
      new webpack.HotModuleReplacementPlugin(), // Make hot loading work
      new HtmlWebpackPlugin({
        template: 'index.html', // Move the index.html file
        inject: true // inject all files that are generated by webpack, e.g. bundle.js, main.css with the correct HTML tags
      }),
      new webpack.DefinePlugin({
        "process.env": {
          NODE_ENV: JSON.stringify("development"),
          API_BASE_URL: JSON.stringify(options.apiBaseUrl),
          GOOGLE_SETTINGS: JSON.stringify(options.googleSettings)
        }
      })
    ]
  }

  return {
    entry: entry,
    output: { // Compile into js/build.js
      path: path.resolve(__dirname, 'build'),
      filename: "js/bundle.js",
      publicPath: "/"
    },
    module: {
      loaders: [{
          test: /\.js$/, // Transform all .js files required somewhere within an entry point...
          loader: 'babel', // ...with the specified loaders...
          exclude: path.join(__dirname, '/node_modules/') // ...except for the node_modules folder.
        }, {
          test:   /\.css$/, // Transform all .css files required somewhere within an entry point...
          loader: cssLoaders // ...with PostCSS
        }, {
          test: /\.jpe?g$|\.gif$|\.png$/i,
          loader: "url-loader?limit=10000"
        }
      ]
    },
    plugins: plugins,
    postcss: function() {
      return [
        require('postcss-import')({ // Import all the css files...
          glob: true,
          onImport: function (files) {
              files.forEach(this.addDependency); // ...and add dependecies from the main.css files to the other css files...
          }.bind(this) // ...so they get hot–reloaded when something changes...
        }),
        require('postcss-simple-vars')(), // ...then replace the variables...
        require('postcss-focus')(), // ...add a :focus to ever :hover...
        require('autoprefixer')({ // ...and add vendor prefixes...
          browsers: ['last 2 versions', 'IE > 8'] // ...supporting the last 2 major browser versions and IE 8 and up...
        }),
        require('postcss-reporter')({ // This plugin makes sure we get warnings in the console
          clearMessages: true
        })
      ];
    },
    target: "web", // Make web variables accessible to webpack, e.g. window
    stats: false, // Don't show stats in the console
    progress: true
  }
}
<|endoftext|>


<commit_before>/**
 * cad_result.js
 * Used in the cad_result.php page.
 */

var circus = circus || {};

circus.feedback = function() {
	var postUrl = null;

	var global = {
		initialize: function() {
			var idata = circus.feedback.initdata;
			$('.result-block').each(function() {
				var block = this;
				var id = $("input.display-id", block).val();
				$(block).data('displayid', id);
				if (idata && idata.blockFeedback instanceof Object)
					circus.evalListener.set(block, idata.blockFeedback[id]);
			});
			if (circus.feedback.additional instanceof Array)
			{
				$.each(circus.feedback.additional, function(key, additional) {
					if (idata && idata.additionalFeedback)
						data = idata.additionalFeedback[additional.name];
					else
						data = null;
					additional.initialize(data);
				});
			}
		},
		collect: function() {
			var blockFeedback = {};
			var additionalFeedback = {};
			$('.result-block').each(function() {
				var block = this;
				var id = $(block).data('displayid');
				blockFeedback[id] = circus.evalListener.get(block);
			});
			if (circus.feedback.additional instanceof Array)
				$.each(circus.feedback.additional, function (name, afb) {
					additionalFeedback[afb.name] = afb.collect();
				});
			return {
				blockFeedback: blockFeedback,
				additionalFeedback: additionalFeedback
			};
		},
		disable: function () {
			$('.result-block').each(function () {
				circus.evalListener.disable(this);
			});
		},
		enable: function () {
			$('.result-block').each(function () {
				circus.evalListener.enable(this);
			})
		},
		register_ok: function() {
			if (circus.feedback.feedbackStatus != 'normal')
				return { register_ok: false, messages: [] }
			var register_ok = true;
			var messages = [];
			var caption = circus.cadresult.presentation.displayPresenter.caption;
			$('.result-block').each(function() {
				var block = this;
				var id = $(block).data('displayid');
				var tmp = circus.evalListener.validate(block);
				if (!tmp.register_ok) {
					register_ok = false;
					if (tmp.message)
					{
						var mes = caption + ': ' + tmp.message + '';
						if (messages.indexOf(mes) == -1)
						messages.push(mes);
					}
				}
			});
			if (register_ok)
				messages.push(caption + ': Complete');
			if (circus.feedback.additional instanceof Array) {
				var ad = circus.feedback.additional;
				for (var i = 0; i < ad.length; i++) {
					var tmp = ad[i].validate();
					if (!tmp.register_ok) {
						register_ok = false;
					}
					if (tmp.message && messages.indexOf(tmp.message) == -1)
						messages.push(tmp.message);
				}
			}
			return { register_ok: register_ok, messages: messages };
		},
		change: function() {
			circus.feedback.modified = true;
			var ok = circus.feedback.register_ok();
			if (ok.register_ok === true) {
				$('#register').enable();
				var data = circus.feedback.collect();
				$('#result').val(JSON.stringify(data));
			} else {
				$('#register').disable();
			}
			$('#register-error').empty();
			$.each(ok.messages, function (index, msg) {
				$('
').html(msg).appendTo('#register-error');
			})
		},
		register: function(temporary, postLocation) {
			var feedback = circus.feedback.collect();
			postUrl = temporary ? postLocation : null;
			$.post("register_feedback.php",
				{
					jobID: $("#job-id").val(),
					feedbackMode: circus.feedback.feedbackMode,
					temporary: temporary ? 1 : 0,
					feedback: JSON.stringify(feedback)
				},
				global.register_success,
				"text"
			);
		},
		register_success: function(result) {
			var obj = JSON.parse(result);
			if (obj)
			{
				if (obj.status == 'OK')
				{
					if (postUrl)
					{
						$(window).trigger('actionlog', { action: 'save' });
						location.replace(postUrl);
					}
					else
					{
						$(window).trigger('actionlog', { action: 'register' });
						location.reload(true);
					}
				}
				else
					alert("Error while registering feedback:\n" + obj.error.message);
			}
			else
				alert("System Error:\n" + result);
		},
		additional: []
	};
	return global;
}();

circus.cadresult = function() {
	var global = {
		sortBlocks: function(key, order) {
			var data = circus.cadresult.displays;
			var sorted = $('#result-blocks .result-block').sort(function(a,b){
				var aid = $(a).data('displayid');
				var bid = $(b).data('displayid');
				var tmp = data[aid][key] - data[bid][key];
				return order == 'desc' ? -tmp : tmp;
			});
			$.each(sorted, function(index, item){
				$('#result-blocks').append(item);
			});
		},
		showTab: function(index) {
			$('.tab-content > div').hide();
			$('.tab-content > div:nth-child(' + (index+1) + ')').show();
			$('.tabArea a').removeClass('btn-tab-active');
			$('.tabArea ul li:nth-child(' + (index+1) + ') a').addClass('btn-tab-active');
		},
		showTabLabel: function(label) {
			$('.tabArea a').each(function () {
				if ($(this).text() == label) {
					circus.cadresult.showTab($('.tabArea a').index($(this)));
				}
			});
		}
	};
	return global;
}();

$(function(){
	// Initialize the evaluator status.
	circus.evalListener.setup();
	circus.feedback.initialize();
	circus.feedback.change();
	if (circus.feedback.feedbackStatus == 'disabled')
	{
		circus.feedback.disable();
	}
	circus.feedback.modified = false;

	$('#register').click(function() {
		circus.feedback.register(false);
	});

	$('.tabArea a').click(function(event) {
		var target = $(event.target);
		var index = $('.tabArea a').index(target);
		circus.cadresult.showTab(index);
	});

	$('#mode-form input[name=feedbackMode]')
		.val([circus.feedback.feedbackMode])
		.trigger('flush');
	$('#mode-form a.radio-to-button-l').click(function() {
		var mode = $('#mode-form input[name=feedbackMode]:checked').val();
		if (mode != circus.feedback.feedbackMode)
			$('#mode-form').submit();
	});

	var dialog = $('#temporary-confirm');
	var postLocation = null;

	$('#temporary-confirm').dialog({
		autoOpen: false,
		draggable: false,
		resizable: false,
		modal: true,
		buttons: [
			{
				text: 'Save',
				click: function() {
					dialog.dialog('close');
					circus.feedback.register(true, postLocation);
				}
			},
			{
				text: "Don't Save",
				click: function() {
					dialog.dialog('close');
					location.replace(postLocation);
				}
			},
			{
				text: 'Cancel',
				click: function() {
					dialog.dialog('close');
				}
			}
		]
	});

	if (circus.feedback.feedbackStatus == 'normal')
	{
		$('#menu .jq-btn').click(function(event) {
			if (!circus.feedback.modified)
				return;
			postLocation = $(event.currentTarget).attr('href');
			dialog.dialog('open');
			return false;
		});
	}

	if (circus.feedback.consensualFeedbackAvail != 'locked')
	{
		$('#consensual-mode').enable();
	}

	$(window).trigger('actionlog', { action: "open", options: "CAD result, " + circus.feedback.feedbackMode });

	// tags
	var refresh = function(tags) {
		$('#cad-tags').refreshTags(tags, '../cad_log.php', 'filterTag');
	};
	$('#edit-cad-tags').click(function() {
		circus.edittag.openEditor(4, circus.jobID, '../', refresh);
	})
	circus.edittag.load(4, circus.jobID, '../', refresh);
});<commit_msg>New action log events for tab switching.<commit_after>/**
 * cad_result.js
 * Used in the cad_result.php page.
 */

var circus = circus || {};

circus.feedback = function() {
	var postUrl = null;

	var global = {
		initialize: function() {
			var idata = circus.feedback.initdata;
			$('.result-block').each(function() {
				var block = this;
				var id = $("input.display-id", block).val();
				$(block).data('displayid', id);
				if (idata && idata.blockFeedback instanceof Object)
					circus.evalListener.set(block, idata.blockFeedback[id]);
			});
			if (circus.feedback.additional instanceof Array)
			{
				$.each(circus.feedback.additional, function(key, additional) {
					if (idata && idata.additionalFeedback)
						data = idata.additionalFeedback[additional.name];
					else
						data = null;
					additional.initialize(data);
				});
			}
		},
		collect: function() {
			var blockFeedback = {};
			var additionalFeedback = {};
			$('.result-block').each(function() {
				var block = this;
				var id = $(block).data('displayid');
				blockFeedback[id] = circus.evalListener.get(block);
			});
			if (circus.feedback.additional instanceof Array)
				$.each(circus.feedback.additional, function (name, afb) {
					additionalFeedback[afb.name] = afb.collect();
				});
			return {
				blockFeedback: blockFeedback,
				additionalFeedback: additionalFeedback
			};
		},
		disable: function () {
			$('.result-block').each(function () {
				circus.evalListener.disable(this);
			});
		},
		enable: function () {
			$('.result-block').each(function () {
				circus.evalListener.enable(this);
			})
		},
		register_ok: function() {
			if (circus.feedback.feedbackStatus != 'normal')
				return { register_ok: false, messages: [] }
			var register_ok = true;
			var messages = [];
			var caption = circus.cadresult.presentation.displayPresenter.caption;
			$('.result-block').each(function() {
				var block = this;
				var id = $(block).data('displayid');
				var tmp = circus.evalListener.validate(block);
				if (!tmp.register_ok) {
					register_ok = false;
					if (tmp.message)
					{
						var mes = caption + ': ' + tmp.message + '';
						if (messages.indexOf(mes) == -1)
						messages.push(mes);
					}
				}
			});
			if (register_ok)
				messages.push(caption + ': Complete');
			if (circus.feedback.additional instanceof Array) {
				var ad = circus.feedback.additional;
				for (var i = 0; i < ad.length; i++) {
					var tmp = ad[i].validate();
					if (!tmp.register_ok) {
						register_ok = false;
					}
					if (tmp.message && messages.indexOf(tmp.message) == -1)
						messages.push(tmp.message);
				}
			}
			return { register_ok: register_ok, messages: messages };
		},
		change: function() {
			circus.feedback.modified = true;
			var ok = circus.feedback.register_ok();
			if (ok.register_ok === true) {
				$('#register').enable();
				var data = circus.feedback.collect();
				$('#result').val(JSON.stringify(data));
			} else {
				$('#register').disable();
			}
			$('#register-error').empty();
			$.each(ok.messages, function (index, msg) {
				$('
').html(msg).appendTo('#register-error');
			})
		},
		register: function(temporary, postLocation) {
			var feedback = circus.feedback.collect();
			postUrl = temporary ? postLocation : null;
			$.post("register_feedback.php",
				{
					jobID: $("#job-id").val(),
					feedbackMode: circus.feedback.feedbackMode,
					temporary: temporary ? 1 : 0,
					feedback: JSON.stringify(feedback)
				},
				global.register_success,
				"text"
			);
		},
		register_success: function(result) {
			var obj = JSON.parse(result);
			if (obj)
			{
				if (obj.status == 'OK')
				{
					if (postUrl)
					{
						$(window).trigger('actionlog', { action: 'save' });
						location.replace(postUrl);
					}
					else
					{
						$(window).trigger('actionlog', { action: 'register' });
						location.reload(true);
					}
				}
				else
					alert("Error while registering feedback:\n" + obj.error.message);
			}
			else
				alert("System Error:\n" + result);
		},
		additional: []
	};
	return global;
}();

circus.cadresult = function() {
	var global = {
		sortBlocks: function(key, order) {
			var data = circus.cadresult.displays;
			var sorted = $('#result-blocks .result-block').sort(function(a,b){
				var aid = $(a).data('displayid');
				var bid = $(b).data('displayid');
				var tmp = data[aid][key] - data[bid][key];
				return order == 'desc' ? -tmp : tmp;
			});
			$.each(sorted, function(index, item){
				$('#result-blocks').append(item);
			});
		},
		showTab: function(index) {
			$('.tab-content > div').hide();
			$('.tab-content > div:nth-child(' + (index+1) + ')').show();
			$('.tabArea a').removeClass('btn-tab-active');
			var text = $('.tabArea ul li:nth-child(' + (index+1) + ') a').addClass('btn-tab-active').text();
			$(window).trigger('actionlog', { action: 'switchtab', options: text });
		},
		showTabLabel: function(label) {
			$('.tabArea a').each(function () {
				if ($(this).text() == label) {
					circus.cadresult.showTab($('.tabArea a').index($(this)));
				}
			});
		}
	};
	return global;
}();

$(function(){
	// Initialize the evaluator status.
	circus.evalListener.setup();
	circus.feedback.initialize();
	circus.feedback.change();
	if (circus.feedback.feedbackStatus == 'disabled')
	{
		circus.feedback.disable();
	}
	circus.feedback.modified = false;

	$('#register').click(function() {
		circus.feedback.register(false);
	});

	$('.tabArea a').click(function(event) {
		var target = $(event.target);
		var index = $('.tabArea a').index(target);
		circus.cadresult.showTab(index);
	});

	$('#mode-form input[name=feedbackMode]')
		.val([circus.feedback.feedbackMode])
		.trigger('flush');
	$('#mode-form a.radio-to-button-l').click(function() {
		var mode = $('#mode-form input[name=feedbackMode]:checked').val();
		if (mode != circus.feedback.feedbackMode)
			$('#mode-form').submit();
	});

	var dialog = $('#temporary-confirm');
	var postLocation = null;

	$('#temporary-confirm').dialog({
		autoOpen: false,
		draggable: false,
		resizable: false,
		modal: true,
		buttons: [
			{
				text: 'Save',
				click: function() {
					dialog.dialog('close');
					circus.feedback.register(true, postLocation);
				}
			},
			{
				text: "Don't Save",
				click: function() {
					dialog.dialog('close');
					location.replace(postLocation);
				}
			},
			{
				text: 'Cancel',
				click: function() {
					dialog.dialog('close');
				}
			}
		]
	});

	if (circus.feedback.feedbackStatus == 'normal')
	{
		$('#menu .jq-btn').click(function(event) {
			if (!circus.feedback.modified)
				return;
			postLocation = $(event.currentTarget).attr('href');
			dialog.dialog('open');
			return false;
		});
	}

	if (circus.feedback.consensualFeedbackAvail != 'locked')
	{
		$('#consensual-mode').enable();
	}

	$(window).trigger('actionlog', { action: "open", options: "CAD result, " + circus.feedback.feedbackMode });

	// tags
	var refresh = function(tags) {
		$('#cad-tags').refreshTags(tags, '../cad_log.php', 'filterTag');
	};
	$('#edit-cad-tags').click(function() {
		circus.edittag.openEditor(4, circus.jobID, '../', refresh);
	})
	circus.edittag.load(4, circus.jobID, '../', refresh);
});<|endoftext|>


<commit_before>/* global define, require */
(function (root, factory) {
	'use strict';

	if (typeof define === 'function' && define.amd) {
		// AMD. Register as an anonymous module.
		define(['seriously'], factory);
	} else if (typeof exports === 'object') {
		// Node/CommonJS
		factory(require('seriously'));
	} else {
		if (!root.Seriously) {
			root.Seriously = { plugin: function (name, opt) { this[name] = opt; } };
		}
		factory(root.Seriously);
	}
}(this, function (Seriously) {
	'use strict';

	/*
		experimental chroma key algorithm
		todo: try allowing some color despill on opaque pixels
		todo: add different modes?
	*/

	Seriously.plugin('chroma', {
		shader: function (inputs, shaderSource) {
			shaderSource.vertex = [
				'precision mediump float;',

				'attribute vec4 position;',
				'attribute vec2 texCoord;',

				'uniform vec2 resolution;',
				'uniform mat4 transform;',

				'varying vec2 vTexCoord;',

				'uniform vec4 screen;',
				'uniform float balance;',
				'varying float screenSat;',
				'varying vec3 screenPrimary;',

				'void main(void) {',
				'	float fmin = min(min(screen.r, screen.g), screen.b);', //Min. value of RGB
				'	float fmax = max(max(screen.r, screen.g), screen.b);', //Max. value of RGB
				'	float secondaryComponents;',

				'	screenPrimary = step(fmax, screen.rgb);',
				'	secondaryComponents = dot(1.0 - screenPrimary, screen.rgb);',
				'	screenSat = fmax - mix(secondaryComponents - fmin, secondaryComponents / 2.0, balance);',

				// first convert to screen space
				'	vec4 screenPosition = vec4(position.xy * resolution / 2.0, position.z, position.w);',
				'	screenPosition = transform * screenPosition;',

				// convert back to OpenGL coords
				'	gl_Position = screenPosition;',
				'	gl_Position.xy = screenPosition.xy * 2.0 / resolution;',
				'	gl_Position.z = screenPosition.z * 2.0 / (resolution.x / resolution.y);',
				'	vTexCoord = texCoord;',
				'}'
			].join('\n');
			shaderSource.fragment = [
				this.inputs.mask ? '#define MASK' : '',
				'precision mediump float;',

				'varying vec2 vTexCoord;',

				'uniform sampler2D source;',
				'uniform vec4 screen;',
				'uniform float screenWeight;',
				'uniform float balance;',
				'uniform float clipBlack;',
				'uniform float clipWhite;',
				'uniform bool mask;',

				'varying float screenSat;',
				'varying vec3 screenPrimary;',

				'void main(void) {',
				'	float pixelSat, secondaryComponents;',
				'	vec4 sourcePixel = texture2D(source, vTexCoord);',

				'	float fmin = min(min(sourcePixel.r, sourcePixel.g), sourcePixel.b);', //Min. value of RGB
				'	float fmax = max(max(sourcePixel.r, sourcePixel.g), sourcePixel.b);', //Max. value of RGB
				//	luminance = fmax

				'	vec3 pixelPrimary = step(fmax, sourcePixel.rgb);',

				'	secondaryComponents = dot(1.0 - pixelPrimary, sourcePixel.rgb);',
				'	pixelSat = fmax - mix(secondaryComponents - fmin, secondaryComponents / 2.0, balance);', // Saturation

				// solid pixel if primary color component is not the same as the screen color
				'	float diffPrimary = dot(abs(pixelPrimary - screenPrimary), vec3(1.0));',
				'	float solid = step(1.0, step(pixelSat, 0.1) + step(fmax, 0.1) + diffPrimary);',

				/*
				Semi-transparent pixel if the primary component matches but if saturation is less
				than that of screen color. Otherwise totally transparent
				*/
				'	float alpha = max(0.0, 1.0 - pixelSat / screenSat);',
				'	alpha = smoothstep(clipBlack, clipWhite, alpha);',
				'	vec4 semiTransparentPixel = vec4((sourcePixel.rgb - (1.0 - alpha) * screen.rgb * screenWeight) / alpha, alpha);',

				'	vec4 pixel = mix(semiTransparentPixel, sourcePixel, solid);',

				/*
				Old branching code
				'	if (pixelSat < 0.1 || fmax < 0.1 || any(notEqual(pixelPrimary, screenPrimary))) {',
				'		pixel = sourcePixel;',

				'	} else if (pixelSat < screenSat) {',
				'		float alpha = max(0.0, 1.0 - pixelSat / screenSat);',
				'		alpha = smoothstep(clipBlack, clipWhite, alpha);',
				'		pixel = vec4((sourcePixel.rgb - (1.0 - alpha) * screen.rgb * screenWeight) / alpha, alpha);',
				'	}',
				//*/

				'#ifdef MASK',
				'	gl_FragColor = vec4(vec3(pixel.a), 1.0);',
				'#else',
				'	gl_FragColor = pixel;',
				'#endif',
				'}'
			].join('\n');
			return shaderSource;
		},
		inPlace: true,
		inputs: {
			source: {
				type: 'image',
				uniform: 'source'
			},
			screen: {
				type: 'color',
				uniform: 'screen',
				defaultValue: [66 / 255, 195 / 255, 31 / 255, 1]
			},
			weight: {
				type: 'number',
				uniform: 'screenWeight',
				defaultValue: 1,
				min: 0
			},
			balance: {
				type: 'number',
				uniform: 'balance',
				defaultValue: 1,
				min: 0,
				max: 1
			},
			clipBlack: {
				type: 'number',
				uniform: 'clipBlack',
				defaultValue: 0,
				min: 0,
				max: 1
			},
			clipWhite: {
				type: 'number',
				uniform: 'clipWhite',
				defaultValue: 1,
				min: 0,
				max: 1
			},
			mask: {
				type: 'boolean',
				defaultValue: false,
				uniform: 'mask',
				shaderDirty: true
			}
		},
		title: 'Chroma Key',
		description: ''
	});
}));
<commit_msg>Fix divide-by-zero bug in chroma effect #80<commit_after>/* global define, require */
(function (root, factory) {
	'use strict';

	if (typeof define === 'function' && define.amd) {
		// AMD. Register as an anonymous module.
		define(['seriously'], factory);
	} else if (typeof exports === 'object') {
		// Node/CommonJS
		factory(require('seriously'));
	} else {
		if (!root.Seriously) {
			root.Seriously = { plugin: function (name, opt) { this[name] = opt; } };
		}
		factory(root.Seriously);
	}
}(this, function (Seriously) {
	'use strict';

	/*
		experimental chroma key algorithm
		todo: try allowing some color despill on opaque pixels
		todo: add different modes?
	*/

	Seriously.plugin('chroma', {
		shader: function (inputs, shaderSource) {
			shaderSource.vertex = [
				'precision mediump float;',

				'attribute vec4 position;',
				'attribute vec2 texCoord;',

				'uniform vec2 resolution;',
				'uniform mat4 transform;',

				'varying vec2 vTexCoord;',

				'uniform vec4 screen;',
				'uniform float balance;',
				'varying float screenSat;',
				'varying vec3 screenPrimary;',

				'void main(void) {',
				'	float fmin = min(min(screen.r, screen.g), screen.b);', //Min. value of RGB
				'	float fmax = max(max(screen.r, screen.g), screen.b);', //Max. value of RGB
				'	float secondaryComponents;',

				'	screenPrimary = step(fmax, screen.rgb);',
				'	secondaryComponents = dot(1.0 - screenPrimary, screen.rgb);',
				'	screenSat = fmax - mix(secondaryComponents - fmin, secondaryComponents / 2.0, balance);',

				// first convert to screen space
				'	vec4 screenPosition = vec4(position.xy * resolution / 2.0, position.z, position.w);',
				'	screenPosition = transform * screenPosition;',

				// convert back to OpenGL coords
				'	gl_Position = screenPosition;',
				'	gl_Position.xy = screenPosition.xy * 2.0 / resolution;',
				'	gl_Position.z = screenPosition.z * 2.0 / (resolution.x / resolution.y);',
				'	vTexCoord = texCoord;',
				'}'
			].join('\n');
			shaderSource.fragment = [
				this.inputs.mask ? '#define MASK' : '',
				'precision mediump float;',

				'varying vec2 vTexCoord;',

				'uniform sampler2D source;',
				'uniform vec4 screen;',
				'uniform float screenWeight;',
				'uniform float balance;',
				'uniform float clipBlack;',
				'uniform float clipWhite;',
				'uniform bool mask;',

				'varying float screenSat;',
				'varying vec3 screenPrimary;',

				'void main(void) {',
				'	float pixelSat, secondaryComponents;',
				'	vec4 sourcePixel = texture2D(source, vTexCoord);',

				'	float fmin = min(min(sourcePixel.r, sourcePixel.g), sourcePixel.b);', //Min. value of RGB
				'	float fmax = max(max(sourcePixel.r, sourcePixel.g), sourcePixel.b);', //Max. value of RGB
				//	luminance = fmax

				'	vec3 pixelPrimary = step(fmax, sourcePixel.rgb);',

				'	secondaryComponents = dot(1.0 - pixelPrimary, sourcePixel.rgb);',
				'	pixelSat = fmax - mix(secondaryComponents - fmin, secondaryComponents / 2.0, balance);', // Saturation

				// solid pixel if primary color component is not the same as the screen color
				'	float diffPrimary = dot(abs(pixelPrimary - screenPrimary), vec3(1.0));',
				'	float solid = step(1.0, step(pixelSat, 0.1) + step(fmax, 0.1) + diffPrimary);',

				/*
				Semi-transparent pixel if the primary component matches but if saturation is less
				than that of screen color. Otherwise totally transparent
				*/
				'	float alpha = max(0.0, 1.0 - pixelSat / screenSat);',
				'	alpha = smoothstep(clipBlack, clipWhite, alpha);',
				'	vec4 semiTransparentPixel = vec4((sourcePixel.rgb - (1.0 - alpha) * screen.rgb * screenWeight) / max(0.00001, alpha), alpha);',

				'	vec4 pixel = mix(semiTransparentPixel, sourcePixel, solid);',

				/*
				Old branching code
				'	if (pixelSat < 0.1 || fmax < 0.1 || any(notEqual(pixelPrimary, screenPrimary))) {',
				'		pixel = sourcePixel;',

				'	} else if (pixelSat < screenSat) {',
				'		float alpha = max(0.0, 1.0 - pixelSat / screenSat);',
				'		alpha = smoothstep(clipBlack, clipWhite, alpha);',
				'		pixel = vec4((sourcePixel.rgb - (1.0 - alpha) * screen.rgb * screenWeight) / alpha, alpha);',
				'	}',
				//*/

				'#ifdef MASK',
				'	gl_FragColor = vec4(vec3(pixel.a), 1.0);',
				'#else',
				'	gl_FragColor = pixel;',
				'#endif',
				'}'
			].join('\n');
			return shaderSource;
		},
		inPlace: true,
		inputs: {
			source: {
				type: 'image',
				uniform: 'source'
			},
			screen: {
				type: 'color',
				uniform: 'screen',
				defaultValue: [66 / 255, 195 / 255, 31 / 255, 1]
			},
			weight: {
				type: 'number',
				uniform: 'screenWeight',
				defaultValue: 1,
				min: 0
			},
			balance: {
				type: 'number',
				uniform: 'balance',
				defaultValue: 1,
				min: 0,
				max: 1
			},
			clipBlack: {
				type: 'number',
				uniform: 'clipBlack',
				defaultValue: 0,
				min: 0,
				max: 1
			},
			clipWhite: {
				type: 'number',
				uniform: 'clipWhite',
				defaultValue: 1,
				min: 0,
				max: 1
			},
			mask: {
				type: 'boolean',
				defaultValue: false,
				uniform: 'mask',
				shaderDirty: true
			}
		},
		title: 'Chroma Key',
		description: ''
	});
}));
<|endoftext|>


<commit_before><commit_msg>Compile results<commit_after><|endoftext|>