<commit_before>var Surface = require('./graphics').Surface;
var objects = require('./utils/objects');

/**
 * @fileoverview Methods for creating Font objects which can render text
 * to a Surface.
 *
 * @example
 * var font = new Font('20px monospace');
 * // render text - this returns a surface with the text written on it.
 * var helloSurface = font.render('Hello World')
 */

/**
 * Create a Font to draw on the screen. The Font allows you to
 * `render()` text. Rendering text returns a Surface which
 * in turn can be put on screen.
 *
 * @constructor
 * @property {Number} fontHeight the line height of this Font
 *
 * @param {String} fontSettings a css font definition, e.g., "20px monospace"
 * @param {STring} backgroundColor valid #rgb string, "#ff00cc"
 */
var Font = exports.Font = function(fontSettings, backgroundColor) {
    /**
     * @ignore
     */
   this.sampleSurface = new Surface([10,10]);
   this.sampleSurface.context.font = fontSettings;
   this.sampleSurface.context.textAlign = 'start';
   // http://diveintohtml5.org/canvas.html#text
   this.sampleSurface.context.textBaseline = 'bottom';
   this.backgroundColor = backgroundColor || false;
   return this;
};

/**
 * Returns a Surface with the given text on it.
 * @param {String} text the text to render
 * @param {String} color a valid #RGB String, "#ffcc00"
 * @returns {gamejs.Surface} Surface with the rendered text on it.
 */
Font.prototype.render = function(text, color) {
   var dims = this.size(text);
   var surface = new Surface(dims);
   var ctx = surface.context;
   ctx.save();
   if ( this.backgroundColor ) {
       ctx.fillStyle = this.backgroundColor;
       ctx.fillRect(0, 0, surface.rect.width, surface.rect.height);
   }
   ctx.font = this.sampleSurface.context.font;
   ctx.textBaseline = this.sampleSurface.context.textBaseline;
   ctx.textAlign = this.sampleSurface.context.textAlign;
   ctx.fillStyle = ctx.strokeStyle = color || "#000000";
   ctx.fillText(text, 0, surface.rect.height, surface.rect.width);
   ctx.restore();
   return surface;
};

/**
 * Determine the width and height of the given text if rendered
 * with this Font.
 * @param {String} text the text to measure
 * @returns {Array} the [width, height] of the text if rendered with this Font
 */
Font.prototype.size = function(text) {
   var metrics = this.sampleSurface.context.measureText(text);
   // FIXME measuretext is buggy, make extra wide
   return [metrics.width, this.fontHeight];
};

/**
 * Height of the font in pixels.
 */
objects.accessors(Font.prototype, {
   'fontHeight': {
      get: function() {
         // Returns an approximate line height of the text
         // »This version of the specification does not provide a way to obtain
         // the bounding box dimensions of the text.«
         // see http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-measuretext
         return this.sampleSurface.context.measureText('M').width * 1.5;
      }
   }

});
<commit_msg>Fix font.render text alignment (#100)<commit_after>var Surface = require('./graphics').Surface;
var objects = require('./utils/objects');

/**
 * @fileoverview Methods for creating Font objects which can render text
 * to a Surface.
 *
 * @example
 * var font = new Font('20px monospace');
 * // render text - this returns a surface with the text written on it.
 * var helloSurface = font.render('Hello World')
 */

/**
 * Create a Font to draw on the screen. The Font allows you to
 * `render()` text. Rendering text returns a Surface which
 * in turn can be put on screen.
 *
 * @constructor
 * @property {Number} fontHeight the line height of this Font
 *
 * @param {String} fontSettings a css font definition, e.g., "20px monospace"
 * @param {STring} backgroundColor valid #rgb string, "#ff00cc"
 */
var Font = exports.Font = function(fontSettings, backgroundColor) {
    /**
     * @ignore
     */
   this.sampleSurface = new Surface([10,10]);
   this.sampleSurface.context.font = fontSettings;
   this.sampleSurface.context.textAlign = 'start';
   // http://diveintohtml5.org/canvas.html#text
   this.sampleSurface.context.textBaseline = 'bottom';
   this.backgroundColor = backgroundColor || false;
   return this;
};

/**
 * Returns a Surface with the given text on it.
 * @param {String} text the text to render
 * @param {String=} color a valid #RGB String, "#ffcc00"
 * @param {String=} textBaseline a valid textBaseline value, "alphabetic|top|hanging|middle|ideographic|bottom"
 * @param {Number=} padding number of pixels of padding space to contain the text in.
 * @returns {gamejs.Surface} Surface with the rendered text on it.
 */
Font.prototype.render = function(text, color, textBaseline, padding) {
   var dims = this.size(text);
   var padding = padding || Math.ceil(dims[1] * 0.2);
   dims[1] += padding;
   var surface = new Surface(dims);
   var ctx = surface.context;
   ctx.save();
   if ( this.backgroundColor ) {
       ctx.fillStyle = this.backgroundColor;
       ctx.fillRect(0, 0, surface.rect.width, surface.rect.height);
   }
   ctx.font = this.sampleSurface.context.font;
   ctx.textBaseline = textBaseline || 'alphabetic';
   ctx.textAlign = this.sampleSurface.context.textAlign;
   ctx.fillStyle = ctx.strokeStyle = color || "#000000";
   ctx.fillText(text, 0, surface.rect.height - padding, surface.rect.width);
   ctx.restore();
   return surface;
};
/**
 * Determine the width and height of the given text if rendered
 * with this Font.
 * @param {String} text the text to measure
 * @returns {Array} the [width, height] of the text if rendered with this Font
 */
Font.prototype.size = function(text) {
   var metrics = this.sampleSurface.context.measureText(text);
   // FIXME measuretext is buggy, make extra wide
   return [metrics.width, this.fontHeight];
};

/**
 * Height of the font in pixels.
 */
objects.accessors(Font.prototype, {
   'fontHeight': {
      get: function() {
         // Returns an approximate line height of the text
         // »This version of the specification does not provide a way to obtain
         // the bounding box dimensions of the text.«
         // see http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-measuretext
         return this.sampleSurface.context.measureText('M').width * 1.5;
      }
   }

});
<|endoftext|>


<commit_before>// @flow
import * as React from 'react'
import {Image, type ImageProps} from 'react-native'
import FastImageImpl from 'react-native-fast-image'
import {isArray} from 'lodash-es'

export class NativeImage extends React.Component<ImageProps> {
  static getSize = Image.getSize
  render() {
    return <Image {...this.props} fadeDuration={0} />
  }
}

export class FastImage extends React.Component<ImageProps> {
  static getSize = Image.getSize
  render() {
    let source = this.props.source
    if (isArray(this.props.source)) {
      source = this.props.source[0] // TODO smarter choice?
    }
    return <FastImageImpl {...this.props} source={source} />
  }
}
<commit_msg>add guard to fast image (#17286)<commit_after>// @flow
import * as React from 'react'
import {Image, type ImageProps} from 'react-native'
import FastImageImpl from 'react-native-fast-image'
import {isArray} from 'lodash-es'

export class NativeImage extends React.Component<ImageProps> {
  static getSize = Image.getSize
  render() {
    return <Image {...this.props} fadeDuration={0} />
  }
}

export class FastImage extends React.Component<ImageProps> {
  static getSize = Image.getSize
  render() {
    let source = this.props.source
    if (isArray(this.props.source)) {
      source = this.props.source[0] // TODO smarter choice?
    }
    return !!source && !!source.uri && <FastImageImpl {...this.props} source={source} />
  }
}
<|endoftext|>


<commit_before>import Node from '../Node.js';

export default class ArrowFunctionExpression extends Node {
	initialise ( transforms ) {
		this.body.createScope();
		super.initialise( transforms );
	}

	transpile ( code, transforms ) {
		if ( transforms.arrow || this.needsArguments(transforms) ) {
			// remove arrow
			let charIndex = this.body.start;
			while ( code.original[ charIndex ] !== '=' ) {
				charIndex -= 1;
			}
			code.remove( charIndex, this.body.start );

			// wrap naked parameter
			if ( this.params.length === 1 && this.start === this.params[0].start ) {
				code.prependRight( this.params[0].start, '(' );
				code.appendLeft( this.params[0].end, ')' );
			}

			// add function
			if ( this.parent && this.parent.type === 'ExpressionStatement' ) {
				// standalone expression statement
				code.prependRight( this.start, '(function' );
				code.appendLeft( this.end, ')' );
			} else {
				code.prependRight( this.start, 'function ' );
			}
		}

		super.transpile( code, transforms );
	}

	// Returns whether any transforms that will happen use `arguments`
	needsArguments(transforms) {
		return transforms.spreadRest && this.params.filter( param => param.type === 'RestElement' ).length > 0
	}
}
<commit_msg>depth first<commit_after>import Node from '../Node.js';

export default class ArrowFunctionExpression extends Node {
	initialise ( transforms ) {
		this.body.createScope();
		super.initialise( transforms );
	}

	transpile ( code, transforms ) {
		if ( transforms.arrow || this.needsArguments(transforms) ) {
			// remove arrow
			let charIndex = this.body.start;
			while ( code.original[ charIndex ] !== '=' ) {
				charIndex -= 1;
			}
			code.remove( charIndex, this.body.start );

			super.transpile( code, transforms );

			// wrap naked parameter
			if ( this.params.length === 1 && this.start === this.params[0].start ) {
				code.prependRight( this.params[0].start, '(' );
				code.appendLeft( this.params[0].end, ')' );
			}

			// add function
			if ( this.parent && this.parent.type === 'ExpressionStatement' ) {
				// standalone expression statement
				code.prependRight( this.start, '(function' );
				code.appendLeft( this.end, ')' );
			} else {
				code.prependRight( this.start, 'function ' );
			}
		}

		else {
			super.transpile( code, transforms );
		}
	}

	// Returns whether any transforms that will happen use `arguments`
	needsArguments(transforms) {
		return transforms.spreadRest && this.params.filter( param => param.type === 'RestElement' ).length > 0
	}
}
<|endoftext|>


<commit_before>/**
 * Created with JetBrains WebStorm.
 * User: lachsen
 * Date: 12/17/13
 * Time: 1:21 PM
 * To change this template use File | Settings | File Templates.
 */
(function (ns) {

ns.LightLoop = function LightLoop(position, ambientIntensity){
    var V = position.flip().normalize(), dist, atten;
    var kdComplete = new Vec3(0,0,0), ksComplete = new Vec3(0,0,0);
    if (this.MAX_POINTLIGHTS)
    for (var i = 0; i < this.MAX_POINTLIGHTS; i++) {
        if (!this.pointLightOn[i])
            continue;

        var L = this.viewMatrix.mulVec(this.pointLightPosition[i], 1.0).xyz();
        L = L.sub(position);
        dist = L.length();
        L = L.normalize();

        var kd = new Vec3(0,0,0), ks = new Vec3(0,0,0);
        "BRDF_ENTRY";

        atten = 1.0 / (this.pointLightAttenuation[i].x() + this.pointLightAttenuation[i].y() * dist + this.pointLightAttenuation[i].z() * dist * dist);
        kd = kd.mul(this.pointLightIntensity[i]).mul(atten);
        ks = ks.mul(this.pointLightIntensity[i]).mul(atten);
        kdComplete = kdComplete.add(kd);
        ksComplete = ksComplete.add(ks);
    }
    if (this.MAX_DIRECTIONALLIGHTS)
    for (i = 0; i < this.MAX_DIRECTIONALLIGHTS; i++) {
        if (!this.directionalLightOn[i])
            continue;

        L = this.viewMatrix.mulVec(this.directionalLightDirection[i], 0).xyz();
        L = L.flip().normalize();

        var kd = new Vec3(0,0,0), ks = new Vec3(0,0,0);
        "BRDF_ENTRY";

        kd = kd.mul(this.directionalLightIntensity[i]);
        ks = ks.mul(this.directionalLightIntensity[i]);
        kdComplete = kdComplete.add(kd);
        ksComplete = ksComplete.add(ks);
    }
    if (this.MAX_SPOTLIGHTS)
    for (i = 0; i < this.MAX_SPOTLIGHTS; i++) {
        if (this.spotLightOn[i]) {
            L = this.viewMatrix.mulVec(this.spotLightPosition[i], 1.0).xyz();
            L = L.sub(position);
            dist = L.length();
            L = L.normalize();

            var lDirection = this.viewMatrix.mulVec(this.spotLightDirection[i].flip(), 0).xyz().normalize();
            var angle = L.dot(lDirection);
            if(angle > this.spotLightCosFalloffAngle[i]) {
                var kd = new Vec3(0,0,0), ks = new Vec3(0,0,0);
                "BRDF_ENTRY";

                var c = 1.0;
                if (this.spotLightCastShadow[i]) {
                    var wpos = this.viewInverseMatrix.mulVec(position, 1.0).xyz();

                    var lsPos = this.spotLightMatrix[i].mulVec(new Vec4(wpos, 1));
                    var perspectiveDivPos = lsPos.xyz().div(lsPos.w()).mul(0.5).add(0.5);
                    var lsDepth = perspectiveDivPos.z() - this.spotLightShadowBias[i];

                    var lightuv = perspectiveDivPos.xy();
                    var bitShift = new Vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );

                    var texSize = new Vec2(Math.max(this.coords.x(), this.coords.y())).mul(2);
                    var texelSize = new Vec2(1.0, 1.0).div(texSize);
                    var f = Math.fract(lightuv.mul(texSize).add(0.5));
                    var centroidUV = Math.floor(lightuv.mul(texSize).add(0.5));
                    centroidUV = centroidUV.div(texSize);

                    var lb = this.spotLightShadowMap[i].sample2D(centroidUV.add(texelSize.mul(new Vec2(0.0, 0.0)))).dot(bitShift);
                    if (lb >= lsDepth)
                        lb = 1.0;
                    else
                        lb = 0.0;

                    var lt = this.spotLightShadowMap[i].sample2D(centroidUV.add(texelSize.mul(new Vec2(0.0, 1.0)))).dot(bitShift)
                    if (lt >= lsDepth)
                        lt = 1.0;
                    else
                        lt = 0.0;

                    var rb = this.spotLightShadowMap[i].sample2D(centroidUV.add(texelSize.mul(new Vec2(1.0, 0.0)))).dot(bitShift);
                    if (rb >= lsDepth)
                        rb = 1.0;
                    else
                        rb = 0.0;

                    var rt = this.spotLightShadowMap[i].sample2D(centroidUV.add(texelSize.mul(new Vec2(1.0, 1.0)))).dot(bitShift);
                    if (rt >= lsDepth)
                        rt = 1.0;
                    else
                        rt = 0.0;

                    var a = Math.mix(lb, lt, f.y());
                    var b = Math.mix(rb, rt, f.y());
                    c = Math.mix(a, b, f.x());
                }

                var softness = 1.0;
                if(angle < this.spotLightCosSoftFalloffAngle[i])
                    softness = (angle - this.spotLightCosFalloffAngle[i]) /
                        (this.spotLightCosSoftFalloffAngle[i] -  this.spotLightCosFalloffAngle[i]);

                atten = 1.0 / (this.spotLightAttenuation[i].x() + this.spotLightAttenuation[i].y() * dist + this.spotLightAttenuation[i].z() * dist * dist);
                kd = kd.mul(this.spotLightIntensity[i]).mul(atten * softness * c);
                ks = ks.mul(this.spotLightIntensity[i]).mul(atten * softness * c);
                kdComplete = kdComplete.add(kd);
                ksComplete = ksComplete.add(ks);
            }
        }
    }
    var ambientColor = new Vec3(0,0,0);
    "AMBIENT_ENTRY";
    kdComplete = kdComplete.add(ambientColor);
    var emissiveColor = new Vec3(0, 0, 0);
    "EMISSIVE_ENTRY"
    if (this.ssaoMap) {
        kdComplete = kdComplete.mul(1 - this.ssaoMap.sample2D(this.normalizedCoords).r());
    }
    var refractColor = new Vec3(0, 0, 0);
    var reflectColor = new Vec3(0, 0, 0);
    "REFRACT_REFLECT_ENTRY"
    return Math.pow(new Vec4(emissiveColor.add(kdComplete.add(ksComplete)).add(refractColor).add(reflectColor), 1.0), new Vec4(1/2.2));
}

}(exports));
<commit_msg>XML3D light loop: Include shadowmap routine only of spotLightShadowMap has entries.<commit_after>/**
 * Created with JetBrains WebStorm.
 * User: lachsen
 * Date: 12/17/13
 * Time: 1:21 PM
 * To change this template use File | Settings | File Templates.
 */
(function (ns) {

ns.LightLoop = function LightLoop(position, ambientIntensity){
    var V = position.flip().normalize(), dist, atten;
    var kdComplete = new Vec3(0,0,0), ksComplete = new Vec3(0,0,0);
    if (this.MAX_POINTLIGHTS)
    for (var i = 0; i < this.MAX_POINTLIGHTS; i++) {
        if (!this.pointLightOn[i])
            continue;

        var L = this.viewMatrix.mulVec(this.pointLightPosition[i], 1.0).xyz();
        L = L.sub(position);
        dist = L.length();
        L = L.normalize();

        var kd = new Vec3(0,0,0), ks = new Vec3(0,0,0);
        "BRDF_ENTRY";

        atten = 1.0 / (this.pointLightAttenuation[i].x() + this.pointLightAttenuation[i].y() * dist + this.pointLightAttenuation[i].z() * dist * dist);
        kd = kd.mul(this.pointLightIntensity[i]).mul(atten);
        ks = ks.mul(this.pointLightIntensity[i]).mul(atten);
        kdComplete = kdComplete.add(kd);
        ksComplete = ksComplete.add(ks);
    }
    if (this.MAX_DIRECTIONALLIGHTS)
    for (i = 0; i < this.MAX_DIRECTIONALLIGHTS; i++) {
        if (!this.directionalLightOn[i])
            continue;

        L = this.viewMatrix.mulVec(this.directionalLightDirection[i], 0).xyz();
        L = L.flip().normalize();

        var kd = new Vec3(0,0,0), ks = new Vec3(0,0,0);
        "BRDF_ENTRY";

        kd = kd.mul(this.directionalLightIntensity[i]);
        ks = ks.mul(this.directionalLightIntensity[i]);
        kdComplete = kdComplete.add(kd);
        ksComplete = ksComplete.add(ks);
    }
    if (this.MAX_SPOTLIGHTS)
    for (i = 0; i < this.MAX_SPOTLIGHTS; i++) {
        if (this.spotLightOn[i]) {
            L = this.viewMatrix.mulVec(this.spotLightPosition[i], 1.0).xyz();
            L = L.sub(position);
            dist = L.length();
            L = L.normalize();

            var lDirection = this.viewMatrix.mulVec(this.spotLightDirection[i].flip(), 0).xyz().normalize();
            var angle = L.dot(lDirection);
            if(angle > this.spotLightCosFalloffAngle[i]) {
                var kd = new Vec3(0,0,0), ks = new Vec3(0,0,0);
                "BRDF_ENTRY";

                var c = 1.0;
                if (this.spotLightShadowMap.length && this.spotLightCastShadow[i]) {
                    var wpos = this.viewInverseMatrix.mulVec(position, 1.0).xyz();

                    var lsPos = this.spotLightMatrix[i].mulVec(new Vec4(wpos, 1));
                    var perspectiveDivPos = lsPos.xyz().div(lsPos.w()).mul(0.5).add(0.5);
                    var lsDepth = perspectiveDivPos.z() - this.spotLightShadowBias[i];

                    var lightuv = perspectiveDivPos.xy();
                    var bitShift = new Vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );

                    var texSize = new Vec2(Math.max(this.coords.x(), this.coords.y())).mul(2);
                    var texelSize = new Vec2(1.0, 1.0).div(texSize);
                    var f = Math.fract(lightuv.mul(texSize).add(0.5));
                    var centroidUV = Math.floor(lightuv.mul(texSize).add(0.5));
                    centroidUV = centroidUV.div(texSize);

                    var lb = this.spotLightShadowMap[i].sample2D(centroidUV.add(texelSize.mul(new Vec2(0.0, 0.0)))).dot(bitShift);
                    if (lb >= lsDepth)
                        lb = 1.0;
                    else
                        lb = 0.0;

                    var lt = this.spotLightShadowMap[i].sample2D(centroidUV.add(texelSize.mul(new Vec2(0.0, 1.0)))).dot(bitShift)
                    if (lt >= lsDepth)
                        lt = 1.0;
                    else
                        lt = 0.0;

                    var rb = this.spotLightShadowMap[i].sample2D(centroidUV.add(texelSize.mul(new Vec2(1.0, 0.0)))).dot(bitShift);
                    if (rb >= lsDepth)
                        rb = 1.0;
                    else
                        rb = 0.0;

                    var rt = this.spotLightShadowMap[i].sample2D(centroidUV.add(texelSize.mul(new Vec2(1.0, 1.0)))).dot(bitShift);
                    if (rt >= lsDepth)
                        rt = 1.0;
                    else
                        rt = 0.0;

                    var a = Math.mix(lb, lt, f.y());
                    var b = Math.mix(rb, rt, f.y());
                    c = Math.mix(a, b, f.x());
                }

                var softness = 1.0;
                if(angle < this.spotLightCosSoftFalloffAngle[i])
                    softness = (angle - this.spotLightCosFalloffAngle[i]) /
                        (this.spotLightCosSoftFalloffAngle[i] -  this.spotLightCosFalloffAngle[i]);

                atten = 1.0 / (this.spotLightAttenuation[i].x() + this.spotLightAttenuation[i].y() * dist + this.spotLightAttenuation[i].z() * dist * dist);
                kd = kd.mul(this.spotLightIntensity[i]).mul(atten * softness * c);
                ks = ks.mul(this.spotLightIntensity[i]).mul(atten * softness * c);
                kdComplete = kdComplete.add(kd);
                ksComplete = ksComplete.add(ks);
            }
        }
    }
    var ambientColor = new Vec3(0,0,0);
    "AMBIENT_ENTRY";
    kdComplete = kdComplete.add(ambientColor);
    var emissiveColor = new Vec3(0, 0, 0);
    "EMISSIVE_ENTRY"
    if (this.ssaoMap) {
        kdComplete = kdComplete.mul(1 - this.ssaoMap.sample2D(this.normalizedCoords).r());
    }
    var refractColor = new Vec3(0, 0, 0);
    var reflectColor = new Vec3(0, 0, 0);
    "REFRACT_REFLECT_ENTRY"
    return Math.pow(new Vec4(emissiveColor.add(kdComplete.add(ksComplete)).add(refractColor).add(reflectColor), 1.0), new Vec4(1/2.2));
}

}(exports));
<|endoftext|>


<commit_before>/**
 * AngularJS Directive to allow autocomplete and dropdown suggestions
 * on textareas.
 *
 * Homepage: https://github.com/aurbano/smart-area
 *
 * @author Alejandro U. Alvarez (http://urbanoalvarez.es)
 * @license AGPLv3 (See LICENSE)
 */

angular.module('smartArea', [])
    .directive('smartArea', ['$compile', function($compile) {
    return {
        restrict: 'A',
        scope: {
            areaConfig: '=smartArea',
            areaData: '=ngModel'
        },
        replace: true,
        link: function(scope, textArea){
            if(textArea[0].tagName.toLowerCase() !== 'textarea'){
                console.warn("smartArea can only be used on textareas");
                return false;
            }

            // Caret tracking inspired by
            // https://github.com/component/textarea-caret-position
            // Properties to be copied over from the textarea
            var properties = [
                'direction',  // RTL support
                'boxSizing',
                'width',  // on Chrome and IE, exclude the scrollbar, so the mirror div wraps exactly as the textarea does
                'overflowX',
                'overflowY',  // copy the scrollbar for IE
                'color',
                'height',

                'borderTopWidth',
                'borderRightWidth',
                'borderBottomWidth',
                'borderLeftWidth',

                'borderTopColor',
                'borderRightColor',
                'borderBottomColor',
                'borderLeftColor',

                'borderTopStyle',
                'borderRightStyle',
                'borderBottomStyle',
                'borderLeftStyle',
                'borderRadius',

                'backgroundColor',

                'paddingTop',
                'paddingRight',
                'paddingBottom',
                'paddingLeft',

                // https://developer.mozilla.org/en-US/docs/Web/CSS/font
                'fontStyle',
                'fontVariant',
                'fontWeight',
                'fontStretch',
                'fontSize',
                'fontSizeAdjust',
                'lineHeight',
                'fontFamily',

                'textAlign',
                'textTransform',
                'textIndent',
                'textDecoration',  // might not make a difference, but better be safe

                'letterSpacing',
                'wordSpacing',
                'whiteSpace',
                'wordBreak',
                'wordWrap'
            ];

            // Build the HTML structure
            var mainWrap = angular.element('

'),
                isFirefox = !(window.mozInnerScreenX === null);

            scope.fakeAreaElement = angular.element($compile('

')(scope))
                .appendTo(mainWrap);

            scope.dropdown.element = angular.element($compile('
 0"><input type="text" class="form-control" ng-show="dropdown.showFilter"/>

')(scope))
                .appendTo(mainWrap);

            scope.dropdown.filterElement = scope.dropdown.element.find('input');
            scope.dropdown.filterElement.bind('keydown', scope.keyboardEvents);

            // Default textarea css for the div
            scope.fakeAreaElement.css('whiteSpace', 'pre-wrap');
            scope.fakeAreaElement.css('wordWrap', 'break-word');

            // Transfer the element's properties to the div
            properties.forEach(function (prop) {
                scope.fakeAreaElement.css(prop, textArea.css(prop));
            });

            scope.fakeAreaElement.css('width',(parseInt(textArea.outerWidth()) + 1) + 'px');

            // Special considerations for Firefox
//            if (isFirefox) {
//                scope.fakeAreaElement.css('width',parseInt(textArea.width()) - 2 + 'px');  // Firefox adds 2 pixels to the padding - https://bugzilla.mozilla.org/show_bug.cgi?id=753662
//                // Firefox lies about the overflow property for textareas: https://bugzilla.mozilla.org/show_bug.cgi?id=984275
//                if (textArea.scrollHeight > parseInt(textArea.height)){
//                    scope.fakeAreaElement.css('overflowY', 'scroll');
//                }
//            }

            // Insert the HTML elements
            mainWrap.insertBefore(textArea);
            textArea.appendTo(mainWrap).addClass('sa-realArea').attr('ng-trim',false);
            $compile(textArea);

            // Dirty hack to maintain the height
            textArea.on('keyup', function(){
                scope.fakeAreaElement.height(textArea.height());
            });

            return mainWrap;
        },
        controller: ['$scope', '$element', '$timeout', '$sce', function($scope, $element, $timeout, $sce){
            /* +----------------------------------------------------+
             * +                     Scope Data                     +
             * +----------------------------------------------------+ */

            $scope.fakeArea = $scope.areaData;
            $scope.dropdownContent = 'Dropdown';
            $scope.dropdown = {
                content: [],
                element: null,
                current: 0,
                select: null,
                customSelect: null,
                filter: '',
                match: '',
                mode: 'append',
                showFilter: false,
                filterElement: null
            };

            /* +----------------------------------------------------+
             * +                   Scope Watches                    +
             * +----------------------------------------------------+ */
            $scope.$watch('areaData', function(){
                $scope.trackCaret();

                // TODO Track caret on another fake area, so I don't have to recalculate autocomplete triggers every time the cursor moves.
                checkTriggers();
            });

            /* +----------------------------------------------------+
             * +                  Scope Functions                   +
             * +----------------------------------------------------+ */

            /**
             * Update the Dropdown position according to the current caret position
             * on the textarea
             */
            $scope.trackCaret = function(){
                var text = $scope.areaData,
                    position = getCharacterPosition();

                $scope.fakeArea = $sce.trustAsHtml(text.substring(0, position) + '' + text.substring(position));

                // Tracking span
                $timeout(function(){
                    var span = $scope.fakeAreaElement.find('span.sa-tracking');
                    if(span.length > 0){
                        var spanOffset = span.position();
                        // Move the dropdown
                        $scope.dropdown.element.css({
                            top: (spanOffset.top + parseInt($element.css('fontSize')) + 2)+'px',
                            left: (spanOffset.left)+'px'
                        });
                    }
                    highlightText();
                }, 0);
            };

            /**
             * Keyboard event reacting. This function is triggered by
             * keydown events in the dropdown filter and the main textarea
             *
             * @param event JavaScript event
             */
            $scope.keyboardEvents = function(event){
                if($scope.dropdown.content.length > 0) {
                    var code = event.keyCode || event.which;
                    if (code === 13) { // Enter
                        event.preventDefault();
                        event.stopPropagation();
                        // Add the selected word from the Dropdown
                        // to the areaData in the current position
                        $timeout(function(){
                            $scope.dropdown.selected($scope.dropdown.content[$scope.dropdown.current]);
                        },0);
                    }else if(code === 38){ // Up
                        event.preventDefault();
                        event.stopPropagation();
                        $timeout(function(){
                            $scope.dropdown.current--;
                            if($scope.dropdown.current < 0){
                                $scope.dropdown.current = $scope.dropdown.content.length - 1; // Wrap around
                            }
                        },0);
                    }else if(code === 40){ // Down
                        event.preventDefault();
                        event.stopPropagation();
                        $timeout(function(){
                            $scope.dropdown.current++;
                            if($scope.dropdown.current >= $scope.dropdown.content.length){
                                $scope.dropdown.current = 0; // Wrap around
                            }
                        },0);
                    }else if(code === 27){ // Esc
                        event.preventDefault();
                        event.stopPropagation();
                        $timeout(function(){
                            $scope.dropdown.content = [];
                            $element[0].focus();
                        },0);
                    }else{
                        $scope.dropdown.filterElement.focus();
                    }
                }
            };

            /**
             * Add an item to the textarea, this is called
             * when selecting an element from the dropdown.
             * @param item Selected object
             */
            $scope.dropdown.selected = function(item){
                if($scope.dropdown.customSelect !== null){
                    var append = $scope.dropdown.mode === 'append';
                    addSelectedDropdownText($scope.dropdown.customSelect(item), append);
                }else{
                    addSelectedDropdownText(item.display);
                }
                $scope.dropdown.content = [];
            };

            /* +----------------------------------------------------+
             * +                Internal Functions                  +
             * +----------------------------------------------------+ */

            /**
             * Add text to the textarea, this handles positioning the text
             * at the caret position, and also either replacing the last word
             * or appending as new content.
             *
             * @param selectedWord Word to add to the textarea
             * @param append Whether it should be appended or replace the last word
             */
            function addSelectedDropdownText(selectedWord, append){

                $scope.dropdown.showFilter = false;

                var text = $scope.areaData,
                    position = getCharacterPosition(),
                    lastWord = text.substr(0, position).split(/[\s\b{}]/),
                    remove = lastWord[lastWord.length - 1].length;

                if(!append && $scope.dropdown.match){
                  remove = $scope.dropdown.match.length;
                }

                if(append || remove < 0){
                    remove = 0;
                }

                // Now remove the last word, and replace with the dropped down one
                $scope.areaData = text.substr(0, position - remove) +
                    selectedWord +
                    text.substr(position);

                if(!append && $scope.dropdown.match){
                  position = position - $scope.dropdown.match.length + selectedWord.toString().length;
                }

                // Now reset the caret position
                if($element[0].selectionStart) {
                    $timeout(function(){
                        $element[0].focus();
                        $element[0].setSelectionRange(position - remove + selectedWord.toString().length, position - remove + selectedWord.toString().length);
                        checkTriggers();
                    }, 100);
                }

            }

            /**
             * Perform the "syntax" highlighting of autocomplete words that have
             * a cssClass specified.
             */
            function highlightText(){
                var text = $scope.areaData;

                if(typeof($scope.areaConfig.autocomplete) === 'undefined' || $scope.areaConfig.autocomplete.length === 0){
                    return;
                }

                $scope.areaConfig.autocomplete.forEach(function(autoList){
                    for(var i=0; i<autoList.words.length; i++){
                        if(typeof(autoList.words[i]) === "string"){
                            text = text.replace(new RegExp("([^\\w]|\\b)("+autoList.words[i]+")([^\\w]|\\b)", 'g'), '$1$2$3');
                        }else{
                            text = text.replace(autoList.words[i], function(match){
                                return ''+match+'';
                            });
                        }
                    }
                });
                // Add to the fakeArea
                $scope.fakeArea = $sce.trustAsHtml(text);
            }

            /**
             * Check all the triggers
             */
            function checkTriggers(){
                triggerDropdownAutocomplete();
                triggerDropdownAdvanced();
            }

            /**
             * Trigger the advanced dropdown system, this will check
             * all the specified triggers in the configuration object under dropdown,
             * and if any of them match it will call it's list() function and add the
             * elements returned from it to the dropdown.
             */
            function triggerDropdownAdvanced(){
                $scope.dropdown.showFilter = false;
                $scope.dropdown.match = false;

                if(typeof($scope.areaConfig.dropdown) === 'undefined' || $scope.areaConfig.dropdown.length === 0){
                    return;
                }

                $scope.areaConfig.dropdown.forEach(function(element){
                    // Check if the trigger is under the cursor
                    var text = $scope.areaData,
                        position = getCharacterPosition();
                    if(typeof(element.trigger) === 'string' && element.trigger === text.substr(position - element.trigger.length, element.trigger.length)){
                        // The cursor is exactly at the end of the trigger

                        element.list(function(data){
                            $scope.dropdown.content = data.map(function(el){
                              el.display = $sce.trustAsHtml(el.display);
                              return el;
                            });

                            $scope.dropdown.customSelect = element.onSelect;
                            $scope.dropdown.mode = element.mode || 'append';
                            $scope.dropdown.match = '';
                            $scope.dropdown.showFilter = element.filter || false;

                            $timeout(function(){
                                $scope.dropdown.filterElement.focus();
                            }, 10);
                        });
                    }else if(typeof(element.trigger) === 'object'){
                        // I need to get the index of the last match
                        var searchable = text.substr(0, position),
                            match, found = false, lastPosition = -1;
			element.trigger.lastIndex = 0;
                        while ((match = element.trigger.exec(searchable)) !== null){
                            if(match.index === lastPosition){
                                break;
                            }
                            lastPosition = match.index;
                            if(match.index + match[0].length === position){
                                found = true;
                                break;
                            }
                        }
                        if(found){
                            element.list(match, function(data){
                                $scope.dropdown.content = data.map(function(el){
                                  el.display = $sce.trustAsHtml(el.display);
                                  return el;
                                });

                                $scope.dropdown.customSelect = element.onSelect;
                                $scope.dropdown.mode = element.mode || 'append';
                                $scope.dropdown.match = match[1];
                                $scope.dropdown.showFilter = element.filter || false;
                            });

                        }
                    }
                });
            }

            /**
             * Set the scroll on the fake area
             */
            function resetScroll(){
              $timeout(function(){
                $scope.fakeAreaElement.scrollTop($element.scrollTop());
              }, 5);
            }

            /**
             * Trigger a simple autocomplete, this checks the last word and determines
             * whether any word on the autocomplete lists matches it
             */
            function triggerDropdownAutocomplete(){
                // First check with the autocomplete words (the ones that are not objects
                var autocomplete = [],
                    suggestions = [],
                    text = $scope.areaData,
                    position = getCharacterPosition(),
                    lastWord = text.substr(0, position).split(/[\s\b{}]/);

                // Get the last typed word
                lastWord = lastWord[lastWord.length-1];

                $scope.areaConfig.autocomplete.forEach(function(autoList){
                    autoList.words.forEach(function(word){
                        if(typeof(word) === 'string' && autocomplete.indexOf(word) < 0){
                            if(lastWord.length > 0 || lastWord.length < 1 && autoList.autocompleteOnSpace){
                                autocomplete.push(word);
                            }
                        }
                    });
                });

                if ($scope.areaConfig.dropdown !== undefined){
                    $scope.areaConfig.dropdown.forEach(function(element){
                        if(typeof(element.trigger) === 'string' && autocomplete.indexOf(element.trigger) < 0){
                            autocomplete.push(element.trigger);
                        }
                    });
                }

                // Now with the list, filter and return
                autocomplete.forEach(function(word){
                    if(lastWord.length < word.length && word.toLowerCase().substr(0, lastWord.length) === lastWord.toLowerCase()){
                        suggestions.push({
                            display: $sce.trustAsHtml(word),
                            data: null
                        });
                    }
                });

                $scope.dropdown.customSelect = null;
                $scope.dropdown.current = 0;
                $scope.dropdown.content = suggestions;
            }

            /**
             * Get Character count on an editable field
             * http://stackoverflow.com/questions/4767848/get-caret-cursor-position-in-contenteditable-area-containing-html-content
             */
            function getCharacterPosition() {
              var el = $element[0];
              if (typeof(el.selectionEnd) == "number") {
                return el.selectionEnd;
              }
          }

            /* +----------------------------------------------------+
             * +                   Event Binding                    +
             * +----------------------------------------------------+ */

            $element.bind('keyup click focus', function () {
                $timeout(function(){
                    $scope.trackCaret();
                }, 0);
            });

            $element.bind('keydown', function(event){
              resetScroll();
              $scope.keyboardEvents(event);
            });
        }]
    };
}]);
<commit_msg>Refresh trigger check when irrelevant for dropdown<commit_after>/**
 * AngularJS Directive to allow autocomplete and dropdown suggestions
 * on textareas.
 *
 * Homepage: https://github.com/aurbano/smart-area
 *
 * @author Alejandro U. Alvarez (http://urbanoalvarez.es)
 * @license AGPLv3 (See LICENSE)
 */

angular.module('smartArea', [])
    .directive('smartArea', ['$compile', function($compile) {
    return {
        restrict: 'A',
        scope: {
            areaConfig: '=smartArea',
            areaData: '=ngModel'
        },
        replace: true,
        link: function(scope, textArea){
            if(textArea[0].tagName.toLowerCase() !== 'textarea'){
                console.warn("smartArea can only be used on textareas");
                return false;
            }

            // Caret tracking inspired by
            // https://github.com/component/textarea-caret-position
            // Properties to be copied over from the textarea
            var properties = [
                'direction',  // RTL support
                'boxSizing',
                'width',  // on Chrome and IE, exclude the scrollbar, so the mirror div wraps exactly as the textarea does
                'overflowX',
                'overflowY',  // copy the scrollbar for IE
                'color',
                'height',

                'borderTopWidth',
                'borderRightWidth',
                'borderBottomWidth',
                'borderLeftWidth',

                'borderTopColor',
                'borderRightColor',
                'borderBottomColor',
                'borderLeftColor',

                'borderTopStyle',
                'borderRightStyle',
                'borderBottomStyle',
                'borderLeftStyle',
                'borderRadius',

                'backgroundColor',

                'paddingTop',
                'paddingRight',
                'paddingBottom',
                'paddingLeft',

                // https://developer.mozilla.org/en-US/docs/Web/CSS/font
                'fontStyle',
                'fontVariant',
                'fontWeight',
                'fontStretch',
                'fontSize',
                'fontSizeAdjust',
                'lineHeight',
                'fontFamily',

                'textAlign',
                'textTransform',
                'textIndent',
                'textDecoration',  // might not make a difference, but better be safe

                'letterSpacing',
                'wordSpacing',
                'whiteSpace',
                'wordBreak',
                'wordWrap'
            ];

            // Build the HTML structure
            var mainWrap = angular.element('

'),
                isFirefox = !(window.mozInnerScreenX === null);

            scope.fakeAreaElement = angular.element($compile('

')(scope))
                .appendTo(mainWrap);

            scope.dropdown.element = angular.element($compile('
 0"><input type="text" class="form-control" ng-show="dropdown.showFilter"/>

')(scope))
                .appendTo(mainWrap);

            scope.dropdown.filterElement = scope.dropdown.element.find('input');
            scope.dropdown.filterElement.bind('keydown', scope.keyboardEvents);

            // Default textarea css for the div
            scope.fakeAreaElement.css('whiteSpace', 'pre-wrap');
            scope.fakeAreaElement.css('wordWrap', 'break-word');

            // Transfer the element's properties to the div
            properties.forEach(function (prop) {
                scope.fakeAreaElement.css(prop, textArea.css(prop));
            });

            scope.fakeAreaElement.css('width',(parseInt(textArea.outerWidth()) + 1) + 'px');

            // Special considerations for Firefox
//            if (isFirefox) {
//                scope.fakeAreaElement.css('width',parseInt(textArea.width()) - 2 + 'px');  // Firefox adds 2 pixels to the padding - https://bugzilla.mozilla.org/show_bug.cgi?id=753662
//                // Firefox lies about the overflow property for textareas: https://bugzilla.mozilla.org/show_bug.cgi?id=984275
//                if (textArea.scrollHeight > parseInt(textArea.height)){
//                    scope.fakeAreaElement.css('overflowY', 'scroll');
//                }
//            }

            // Insert the HTML elements
            mainWrap.insertBefore(textArea);
            textArea.appendTo(mainWrap).addClass('sa-realArea').attr('ng-trim',false);
            $compile(textArea);

            // Dirty hack to maintain the height
            textArea.on('keyup', function(){
                scope.fakeAreaElement.height(textArea.height());
            });

            return mainWrap;
        },
        controller: ['$scope', '$element', '$timeout', '$sce', function($scope, $element, $timeout, $sce){
            /* +----------------------------------------------------+
             * +                     Scope Data                     +
             * +----------------------------------------------------+ */

            $scope.fakeArea = $scope.areaData;
            $scope.dropdownContent = 'Dropdown';
            $scope.dropdown = {
                content: [],
                element: null,
                current: 0,
                select: null,
                customSelect: null,
                filter: '',
                match: '',
                mode: 'append',
                showFilter: false,
                filterElement: null
            };

            /* +----------------------------------------------------+
             * +                   Scope Watches                    +
             * +----------------------------------------------------+ */
            $scope.$watch('areaData', function(){
                $scope.trackCaret();

                // TODO Track caret on another fake area, so I don't have to recalculate autocomplete triggers every time the cursor moves.
                checkTriggers();
            });

            /* +----------------------------------------------------+
             * +                  Scope Functions                   +
             * +----------------------------------------------------+ */

            /**
             * Update the Dropdown position according to the current caret position
             * on the textarea
             */
            $scope.trackCaret = function(){
                var text = $scope.areaData,
                    position = getCharacterPosition();

                $scope.fakeArea = $sce.trustAsHtml(text.substring(0, position) + '' + text.substring(position));

                // Tracking span
                $timeout(function(){
                    var span = $scope.fakeAreaElement.find('span.sa-tracking');
                    if(span.length > 0){
                        var spanOffset = span.position();
                        // Move the dropdown
                        $scope.dropdown.element.css({
                            top: (spanOffset.top + parseInt($element.css('fontSize')) + 2)+'px',
                            left: (spanOffset.left)+'px'
                        });
                    }
                    highlightText();
                }, 0);
            };

            /**
             * Keyboard event reacting. This function is triggered by
             * keydown events in the dropdown filter and the main textarea
             *
             * @param event JavaScript event
             */
            $scope.keyboardEvents = function(event){
                if($scope.dropdown.content.length > 0) {
                    var code = event.keyCode || event.which;
                    if (code === 13) { // Enter
                        event.preventDefault();
                        event.stopPropagation();
                        // Add the selected word from the Dropdown
                        // to the areaData in the current position
                        $timeout(function(){
                            $scope.dropdown.selected($scope.dropdown.content[$scope.dropdown.current]);
                        },0);
                    }else if(code === 38){ // Up
                        event.preventDefault();
                        event.stopPropagation();
                        $timeout(function(){
                            $scope.dropdown.current--;
                            if($scope.dropdown.current < 0){
                                $scope.dropdown.current = $scope.dropdown.content.length - 1; // Wrap around
                            }
                        },0);
                    }else if(code === 40){ // Down
                        event.preventDefault();
                        event.stopPropagation();
                        $timeout(function(){
                            $scope.dropdown.current++;
                            if($scope.dropdown.current >= $scope.dropdown.content.length){
                                $scope.dropdown.current = 0; // Wrap around
                            }
                        },0);
                    }else if(code === 27){ // Esc
                        event.preventDefault();
                        event.stopPropagation();
                        $timeout(function(){
                            $scope.dropdown.content = [];
                            $element[0].focus();
                        },0);
                    }else{
                        $scope.dropdown.filterElement.focus();
			checkTriggers();
                    }
                }
            };

            /**
             * Add an item to the textarea, this is called
             * when selecting an element from the dropdown.
             * @param item Selected object
             */
            $scope.dropdown.selected = function(item){
                if($scope.dropdown.customSelect !== null){
                    var append = $scope.dropdown.mode === 'append';
                    addSelectedDropdownText($scope.dropdown.customSelect(item), append);
                }else{
                    addSelectedDropdownText(item.display);
                }
                $scope.dropdown.content = [];
            };

            /* +----------------------------------------------------+
             * +                Internal Functions                  +
             * +----------------------------------------------------+ */

            /**
             * Add text to the textarea, this handles positioning the text
             * at the caret position, and also either replacing the last word
             * or appending as new content.
             *
             * @param selectedWord Word to add to the textarea
             * @param append Whether it should be appended or replace the last word
             */
            function addSelectedDropdownText(selectedWord, append){

                $scope.dropdown.showFilter = false;

                var text = $scope.areaData,
                    position = getCharacterPosition(),
                    lastWord = text.substr(0, position).split(/[\s\b{}]/),
                    remove = lastWord[lastWord.length - 1].length;

                if(!append && $scope.dropdown.match){
                  remove = $scope.dropdown.match.length;
                }

                if(append || remove < 0){
                    remove = 0;
                }

                // Now remove the last word, and replace with the dropped down one
                $scope.areaData = text.substr(0, position - remove) +
                    selectedWord +
                    text.substr(position);

                if(!append && $scope.dropdown.match){
                  position = position - $scope.dropdown.match.length + selectedWord.toString().length;
                }

                // Now reset the caret position
                if($element[0].selectionStart) {
                    $timeout(function(){
                        $element[0].focus();
                        $element[0].setSelectionRange(position - remove + selectedWord.toString().length, position - remove + selectedWord.toString().length);
                        checkTriggers();
                    }, 100);
                }

            }

            /**
             * Perform the "syntax" highlighting of autocomplete words that have
             * a cssClass specified.
             */
            function highlightText(){
                var text = $scope.areaData;

                if(typeof($scope.areaConfig.autocomplete) === 'undefined' || $scope.areaConfig.autocomplete.length === 0){
                    return;
                }

                $scope.areaConfig.autocomplete.forEach(function(autoList){
                    for(var i=0; i<autoList.words.length; i++){
                        if(typeof(autoList.words[i]) === "string"){
                            text = text.replace(new RegExp("([^\\w]|\\b)("+autoList.words[i]+")([^\\w]|\\b)", 'g'), '$1$2$3');
                        }else{
                            text = text.replace(autoList.words[i], function(match){
                                return ''+match+'';
                            });
                        }
                    }
                });
                // Add to the fakeArea
                $scope.fakeArea = $sce.trustAsHtml(text);
            }

            /**
             * Check all the triggers
             */
            function checkTriggers(){
                triggerDropdownAutocomplete();
                triggerDropdownAdvanced();
            }

            /**
             * Trigger the advanced dropdown system, this will check
             * all the specified triggers in the configuration object under dropdown,
             * and if any of them match it will call it's list() function and add the
             * elements returned from it to the dropdown.
             */
            function triggerDropdownAdvanced(){
                $scope.dropdown.showFilter = false;
                $scope.dropdown.match = false;

                if(typeof($scope.areaConfig.dropdown) === 'undefined' || $scope.areaConfig.dropdown.length === 0){
                    return;
                }

                $scope.areaConfig.dropdown.forEach(function(element){
                    // Check if the trigger is under the cursor
                    var text = $scope.areaData,
                        position = getCharacterPosition();
                    if(typeof(element.trigger) === 'string' && element.trigger === text.substr(position - element.trigger.length, element.trigger.length)){
                        // The cursor is exactly at the end of the trigger

                        element.list(function(data){
                            $scope.dropdown.content = data.map(function(el){
                              el.display = $sce.trustAsHtml(el.display);
                              return el;
                            });

                            $scope.dropdown.customSelect = element.onSelect;
                            $scope.dropdown.mode = element.mode || 'append';
                            $scope.dropdown.match = '';
                            $scope.dropdown.showFilter = element.filter || false;

                            $timeout(function(){
                                $scope.dropdown.filterElement.focus();
                            }, 10);
                        });
                    }else if(typeof(element.trigger) === 'object'){
                        // I need to get the index of the last match
                        var searchable = text.substr(0, position),
                            match, found = false, lastPosition = -1;
			element.trigger.lastIndex = 0;
                        while ((match = element.trigger.exec(searchable)) !== null){
                            if(match.index === lastPosition){
                                break;
                            }
                            lastPosition = match.index;
                            if(match.index + match[0].length === position){
                                found = true;
                                break;
                            }
                        }
                        if(found){
                            element.list(match, function(data){
                                $scope.dropdown.content = data.map(function(el){
                                  el.display = $sce.trustAsHtml(el.display);
                                  return el;
                                });

                                $scope.dropdown.customSelect = element.onSelect;
                                $scope.dropdown.mode = element.mode || 'append';
                                $scope.dropdown.match = match[1];
                                $scope.dropdown.showFilter = element.filter || false;
                            });

                        }
                    }
                });
            }

            /**
             * Set the scroll on the fake area
             */
            function resetScroll(){
              $timeout(function(){
                $scope.fakeAreaElement.scrollTop($element.scrollTop());
              }, 5);
            }

            /**
             * Trigger a simple autocomplete, this checks the last word and determines
             * whether any word on the autocomplete lists matches it
             */
            function triggerDropdownAutocomplete(){
                // First check with the autocomplete words (the ones that are not objects
                var autocomplete = [],
                    suggestions = [],
                    text = $scope.areaData,
                    position = getCharacterPosition(),
                    lastWord = text.substr(0, position).split(/[\s\b{}]/);

                // Get the last typed word
                lastWord = lastWord[lastWord.length-1];

                $scope.areaConfig.autocomplete.forEach(function(autoList){
                    autoList.words.forEach(function(word){
                        if(typeof(word) === 'string' && autocomplete.indexOf(word) < 0){
                            if(lastWord.length > 0 || lastWord.length < 1 && autoList.autocompleteOnSpace){
                                autocomplete.push(word);
                            }
                        }
                    });
                });

                if ($scope.areaConfig.dropdown !== undefined){
                    $scope.areaConfig.dropdown.forEach(function(element){
                        if(typeof(element.trigger) === 'string' && autocomplete.indexOf(element.trigger) < 0){
                            autocomplete.push(element.trigger);
                        }
                    });
                }

                // Now with the list, filter and return
                autocomplete.forEach(function(word){
                    if(lastWord.length < word.length && word.toLowerCase().substr(0, lastWord.length) === lastWord.toLowerCase()){
                        suggestions.push({
                            display: $sce.trustAsHtml(word),
                            data: null
                        });
                    }
                });

                $scope.dropdown.customSelect = null;
                $scope.dropdown.current = 0;
                $scope.dropdown.content = suggestions;
            }

            /**
             * Get Character count on an editable field
             * http://stackoverflow.com/questions/4767848/get-caret-cursor-position-in-contenteditable-area-containing-html-content
             */
            function getCharacterPosition() {
              var el = $element[0];
              if (typeof(el.selectionEnd) == "number") {
                return el.selectionEnd;
              }
          }

            /* +----------------------------------------------------+
             * +                   Event Binding                    +
             * +----------------------------------------------------+ */

            $element.bind('keyup click focus', function () {
                $timeout(function(){
                    $scope.trackCaret();
                }, 0);
            });

            $element.bind('keydown', function(event){
              resetScroll();
              $scope.keyboardEvents(event);
            });
        }]
    };
}]);
<|endoftext|>


<commit_before>/*!
 * ${copyright}
 */

/**
 * Initialization Code and shared classes of library sap.ui.table.
 */
sap.ui.define(['jquery.sap.global', 'sap/ui/core/Core',
	'sap/ui/core/library', // library dependency
	'sap/ui/unified/library'], // library dependency
	function(jQuery, Core) {

	"use strict";

	/**
	 * Table-like controls, mainly for desktop scenarios.
	 *
	 * @namespace
	 * @name sap.ui.table
	 * @author SAP SE
	 * @version ${version}
	 * @public
	 */

	// delegate further initialization of this library to the Core
	sap.ui.getCore().initLibrary({
		name : "sap.ui.table",
		version: "${version}",
		dependencies : ["sap.ui.core","sap.ui.unified"],
		types: [
			"sap.ui.table.NavigationMode",
			"sap.ui.table.SelectionBehavior",
			"sap.ui.table.SelectionMode",
			"sap.ui.table.SortOrder",
			"sap.ui.table.VisibleRowCountMode",
			"sap.ui.table.SharedDomRef"
		],
		interfaces: [],
		controls: [
			"sap.ui.table.AnalyticalColumnMenu",
			"sap.ui.table.AnalyticalTable",
			"sap.ui.table.ColumnMenu",
			"sap.ui.table.Table",
			"sap.ui.table.TreeTable"
		],
		elements: [
			"sap.ui.table.AnalyticalColumn",
			"sap.ui.table.Column",
			"sap.ui.table.Row"
		],
		extensions: {
			flChangeHandlers: {
				"sap.ui.table.Column": {
					"propertyChange" : "default"
				},
				"sap.ui.table.Table" : {
					"moveElements": "default"
				},
				"sap.ui.table.AnalyticalTable" : {
					"moveElements": "default"
				}
			}
		}
	});

	/* eslint-disable no-undef */
	var thisLib = sap.ui.table;
	/* eslint-enable no-undef */

	/**
	 * Navigation mode of the table
	 *
	 * @version ${version}
	 * @enum {string}
	 * @public
	 * @ui5-metamodel This enumeration also will be described in the UI5 (legacy) designtime metamodel
	 */
	thisLib.NavigationMode = {

		/**
		 * Uses the scrollbar control.
		 * @public
		 */
		Scrollbar : "Scrollbar",

		/**
		 * Uses the paginator control.
		 * @public
		 */
		Paginator : "Paginator"

	};

	/**
	 * Selection behavior of the table
	 *
	 * @version ${version}
	 * @enum {string}
	 * @public
	 * @ui5-metamodel This enumeration also will be described in the UI5 (legacy) designtime metamodel
	 */
	thisLib.SelectionBehavior = {

		/**
		 * Rows can be selected on the complete row.
		 * @public
		 */
		Row : "Row",

		/**
		 * Rows can only be selected on the row selector.
		 * @public
		 */
		RowSelector : "RowSelector",

		/**
		 * Rows can only be selected on the row (and the selector is hidden).
		 * @public
		 */
		RowOnly : "RowOnly"

	};

	/**
	 * Selection mode of the table
	 *
	 * @version ${version}
	 * @enum {string}
	 * @public
	 * @ui5-metamodel This enumeration also will be described in the UI5 (legacy) designtime metamodel
	 */
	thisLib.SelectionMode = {

		/**
		 * Select multiple rows at a time (toggle behavior).
		 * @public
		 */
		MultiToggle : "MultiToggle",

		/**
		 * Select multiple rows at a time.
		 * @public
		 */
		Multi : "Multi",

		/**
		 * Select one row at a time.
		 * @public
		 */
		Single : "Single",

		/**
		 * No rows can be selected.
		 * @public
		 */
		None : "None"

	};

	/**
	 * Sort order of a column
	 *
	 * @version ${version}
	 * @enum {string}
	 * @public
	 * @ui5-metamodel This enumeration also will be described in the UI5 (legacy) designtime metamodel
	 */
	thisLib.SortOrder = {

		/**
		 * Sort Order: ascending.
		 * @public
		 */
		Ascending : "Ascending",

		/**
		 * Sort Order: descending.
		 * @public
		 */
		Descending : "Descending"

	};

	/**
	 * VisibleRowCountMode of the table
	 *
	 * @version ${version}
	 * @enum {string}
	 * @public
	 * @ui5-metamodel This enumeration also will be described in the UI5 (legacy) designtime metamodel
	 */
	thisLib.VisibleRowCountMode = {

		/**
		 * The table always has as many rows as defined in the visibleRowCount property.
		 * @public
		 */
		Fixed : "Fixed",

		/**
		 * After rendering the table has as many rows as defined in visibleRowCount property. The user is able to change the visible rows by moving a grip with the mouse. The visibleRowCount property is changed accordingly.
		 * @public
		 */
		Interactive : "Interactive",

		/**
		 * The table automatically fills the height of the surrounding container.
		 * The visibleRowCount property is automatically changed accordingly.
		 * All rows need the same height, otherwise the auto mode doesn't always work as expected.
		 * The height of all siblings within the same layout container of the table will be subtracted from the available height.
		 * For performance reasons, it is recommended to add no siblings in the table's parent container.
		 * @public
		 */
		Auto : "Auto"

	};

	/**
	 * Shared DOM Reference IDs of the table.
	 *
	 * Contains IDs of shared DOM references, which should be accessible to inheriting controls via getDomRef() function.
	 *
	 * @version ${version}
	 * @enum {string}
	 * @public
	 */
	thisLib.SharedDomRef = {

		/**
		 * The element id of the Horizontal Scroll Bar of the sap.ui.table.Table.
		 * @public
		 */
		HorizontalScrollBar : "hsb",

		/**
		 * The element id of the Vertical Scroll Bar of the sap.ui.table.Table.
		 * @public
		 */
		VerticalScrollBar : "vsb"
	};

	/**
	 * Details about the group event to distinguish between different actions associated with grouping
	 * @enum {string}
	 * @public
	 * @type {{group: string, ungroup: string, ungroupAll: string, moveUp: string, moveDown: string, showGroupedColumn: string, hideGroupedColumn: string}}
	 */
	thisLib.GroupEventType = {
		/**
		 * Group Column
		 * @public
		 */
		group: "group",
		/**
		 * Ungroup Column
		 * @public
		 */
		ungroup: "ungroup",
		/**
		 * Ungroup All Columns
		 * @public
		 */
		ungroupAll: "ungroupAll",
		/**
		 * Change the group order of the columns. Move column one position up in the group sequence
		 * @public
		 */
		moveUp: "moveUp",
		/**
		 * Change the group order of the columns. Move column one position down in the group sequence
		 * @public
		 */
		moveDown: "moveDown",
		/**
		 * Show grouped column also as a column, not just as group header
		 * @public
		 */
		showGroupedColumn: "showGroupedColumn",
		/**
		 * Show grouped column only as group header
		 * @public
		 */
		hideGroupedColumn: "hideGroupedColumn"
	};

	// map the new Column to the old ColumnHeader
	thisLib.ColumnHeader = thisLib.Column;

	//factory for table to create labels an textviews to be overwritten by commons and mobile library
	if (!thisLib.TableHelper) {
		thisLib.TableHelper = {
			createLabel: function(mConfig){ throw new Error("no Label control available!"); }, /* must return a Label control */
			createTextView: function(mConfig){ throw new Error("no TextView control available!"); }, /* must return a textview control */
			createTextField: function(mConfig){ throw new Error("no TextField control available!"); }, /* must return a textfield control */
			createImage: function(mConfig){ throw new Error("no Image control available!"); }, /* must return a textview control */
			bFinal: false /* if true, the helper must not be overwritten by an other library */
		};
	}

	return thisLib;

});
<commit_msg>[FIX] Table: fix JSDoc for enumerations in sap/ui/table/library.js<commit_after>/*!
 * ${copyright}
 */

/**
 * Initialization Code and shared classes of library sap.ui.table.
 */
sap.ui.define(['jquery.sap.global', 'sap/ui/core/Core',
	'sap/ui/core/library', // library dependency
	'sap/ui/unified/library'], // library dependency
	function(jQuery, Core) {

	"use strict";

	// delegate further initialization of this library to the Core
	sap.ui.getCore().initLibrary({
		name : "sap.ui.table",
		version: "${version}",
		dependencies : ["sap.ui.core","sap.ui.unified"],
		types: [
			"sap.ui.table.NavigationMode",
			"sap.ui.table.SelectionBehavior",
			"sap.ui.table.SelectionMode",
			"sap.ui.table.SortOrder",
			"sap.ui.table.VisibleRowCountMode",
			"sap.ui.table.SharedDomRef"
		],
		interfaces: [],
		controls: [
			"sap.ui.table.AnalyticalColumnMenu",
			"sap.ui.table.AnalyticalTable",
			"sap.ui.table.ColumnMenu",
			"sap.ui.table.Table",
			"sap.ui.table.TreeTable"
		],
		elements: [
			"sap.ui.table.AnalyticalColumn",
			"sap.ui.table.Column",
			"sap.ui.table.Row"
		],
		extensions: {
			flChangeHandlers: {
				"sap.ui.table.Column": {
					"propertyChange" : "default"
				},
				"sap.ui.table.Table" : {
					"moveElements": "default"
				},
				"sap.ui.table.AnalyticalTable" : {
					"moveElements": "default"
				}
			}
		}
	});

	/* eslint-disable no-undef */
	/**
	 * Table-like controls, mainly for desktop scenarios.
	 *
	 * @namespace
	 * @alias sap.ui.table
	 * @author SAP SE
	 * @version ${version}
	 * @public
	 */
	var thisLib = sap.ui.table;
	/* eslint-enable no-undef */

	/**
	 * Navigation mode of the table
	 *
	 * @version ${version}
	 * @enum {string}
	 * @public
	 * @ui5-metamodel This enumeration also will be described in the UI5 (legacy) designtime metamodel
	 */
	thisLib.NavigationMode = {

		/**
		 * Uses the scrollbar control.
		 * @public
		 */
		Scrollbar : "Scrollbar",

		/**
		 * Uses the paginator control.
		 * @public
		 */
		Paginator : "Paginator"

	};

	/**
	 * Selection behavior of the table
	 *
	 * @version ${version}
	 * @enum {string}
	 * @public
	 * @ui5-metamodel This enumeration also will be described in the UI5 (legacy) designtime metamodel
	 */
	thisLib.SelectionBehavior = {

		/**
		 * Rows can be selected on the complete row.
		 * @public
		 */
		Row : "Row",

		/**
		 * Rows can only be selected on the row selector.
		 * @public
		 */
		RowSelector : "RowSelector",

		/**
		 * Rows can only be selected on the row (and the selector is hidden).
		 * @public
		 */
		RowOnly : "RowOnly"

	};

	/**
	 * Selection mode of the table
	 *
	 * @version ${version}
	 * @enum {string}
	 * @public
	 * @ui5-metamodel This enumeration also will be described in the UI5 (legacy) designtime metamodel
	 */
	thisLib.SelectionMode = {

		/**
		 * Select multiple rows at a time (toggle behavior).
		 * @public
		 */
		MultiToggle : "MultiToggle",

		/**
		 * Select multiple rows at a time.
		 * @public
		 */
		Multi : "Multi",

		/**
		 * Select one row at a time.
		 * @public
		 */
		Single : "Single",

		/**
		 * No rows can be selected.
		 * @public
		 */
		None : "None"

	};

	/**
	 * Sort order of a column
	 *
	 * @version ${version}
	 * @enum {string}
	 * @public
	 * @ui5-metamodel This enumeration also will be described in the UI5 (legacy) designtime metamodel
	 */
	thisLib.SortOrder = {

		/**
		 * Sort Order: ascending.
		 * @public
		 */
		Ascending : "Ascending",

		/**
		 * Sort Order: descending.
		 * @public
		 */
		Descending : "Descending"

	};

	/**
	 * VisibleRowCountMode of the table
	 *
	 * @version ${version}
	 * @enum {string}
	 * @public
	 * @ui5-metamodel This enumeration also will be described in the UI5 (legacy) designtime metamodel
	 */
	thisLib.VisibleRowCountMode = {

		/**
		 * The table always has as many rows as defined in the visibleRowCount property.
		 * @public
		 */
		Fixed : "Fixed",

		/**
		 * After rendering the table has as many rows as defined in visibleRowCount property. The user is able to change the visible rows by moving a grip with the mouse. The visibleRowCount property is changed accordingly.
		 * @public
		 */
		Interactive : "Interactive",

		/**
		 * The table automatically fills the height of the surrounding container.
		 * The visibleRowCount property is automatically changed accordingly.
		 * All rows need the same height, otherwise the auto mode doesn't always work as expected.
		 * The height of all siblings within the same layout container of the table will be subtracted from the available height.
		 * For performance reasons, it is recommended to add no siblings in the table's parent container.
		 * @public
		 */
		Auto : "Auto"

	};

	/**
	 * Shared DOM Reference IDs of the table.
	 *
	 * Contains IDs of shared DOM references, which should be accessible to inheriting controls via getDomRef() function.
	 *
	 * @version ${version}
	 * @enum {string}
	 * @public
	 */
	thisLib.SharedDomRef = {

		/**
		 * The element id of the Horizontal Scroll Bar of the sap.ui.table.Table.
		 * @public
		 */
		HorizontalScrollBar : "hsb",

		/**
		 * The element id of the Vertical Scroll Bar of the sap.ui.table.Table.
		 * @public
		 */
		VerticalScrollBar : "vsb"
	};

	/**
	 * Details about the group event to distinguish between different actions associated with grouping
	 * @enum {string}
	 * @public
	 * @type {{group: string, ungroup: string, ungroupAll: string, moveUp: string, moveDown: string, showGroupedColumn: string, hideGroupedColumn: string}}
	 */
	thisLib.GroupEventType = {
		/**
		 * Group Column
		 * @public
		 */
		group: "group",
		/**
		 * Ungroup Column
		 * @public
		 */
		ungroup: "ungroup",
		/**
		 * Ungroup All Columns
		 * @public
		 */
		ungroupAll: "ungroupAll",
		/**
		 * Change the group order of the columns. Move column one position up in the group sequence
		 * @public
		 */
		moveUp: "moveUp",
		/**
		 * Change the group order of the columns. Move column one position down in the group sequence
		 * @public
		 */
		moveDown: "moveDown",
		/**
		 * Show grouped column also as a column, not just as group header
		 * @public
		 */
		showGroupedColumn: "showGroupedColumn",
		/**
		 * Show grouped column only as group header
		 * @public
		 */
		hideGroupedColumn: "hideGroupedColumn"
	};

	// map the new Column to the old ColumnHeader
	thisLib.ColumnHeader = thisLib.Column;

	//factory for table to create labels an textviews to be overwritten by commons and mobile library
	if (!thisLib.TableHelper) {
		thisLib.TableHelper = {
			createLabel: function(mConfig){ throw new Error("no Label control available!"); }, /* must return a Label control */
			createTextView: function(mConfig){ throw new Error("no TextView control available!"); }, /* must return a textview control */
			createTextField: function(mConfig){ throw new Error("no TextField control available!"); }, /* must return a textfield control */
			createImage: function(mConfig){ throw new Error("no Image control available!"); }, /* must return a textview control */
			bFinal: false /* if true, the helper must not be overwritten by an other library */
		};
	}

	return thisLib;

});
<|endoftext|>


<commit_before>/* eslint-disable promise/always-return */
import { runQuery } from "schema/v2/test/utils"

describe("MatchArtist", () => {
  it("queries match/artist for the term 'ok'", () => {
    const query = `
      {
        match_artist(term: "ok") {
          id
          name
          birthday
        }
      }
    `
    const matchArtistsLoader = () =>
      Promise.resolve([
        {
          id: "han-myung-ok",
          name: "Han Myung-Ok",
          birthday: "1958",
          artworks_count: 12,
        },
      ])

    return runQuery(query, { matchArtistsLoader }).then(data => {
      expect(data).toEqual({
        match_artist: [
          { birthday: "1958", id: "han-myung-ok", name: "Han Myung-Ok" },
        ],
      })
    })
  })
})
<commit_msg>Fix v2 match artist test<commit_after>/* eslint-disable promise/always-return */
import { runQuery } from "schema/v2/test/utils"

describe("MatchArtist", () => {
  it("queries match/artist for the term 'ok'", () => {
    const query = `
      {
        matchArtist(term: "ok") {
          slug
          name
          birthday
        }
      }
    `
    const matchArtistsLoader = () =>
      Promise.resolve([
        {
          id: "han-myung-ok",
          name: "Han Myung-Ok",
          birthday: "1958",
          artworks_count: 12,
        },
      ])

    return runQuery(query, { matchArtistsLoader }).then(data => {
      expect(data).toEqual({
        matchArtist: [
          { birthday: "1958", slug: "han-myung-ok", name: "Han Myung-Ok" },
        ],
      })
    })
  })
})
<|endoftext|>


<commit_before>import webpack from 'webpack';
import { resolve as pathResolve } from 'path';
import appRootDir from 'app-root-dir';
import md5 from 'md5';
import fs from 'fs-extra';

function createVendorDLL(bundleConfig) {
  const dllConfig = bundleConfig.devVendorDLL;

  // $FlowFixMe
  const pkg = require(pathResolve(appRootDir.get(), './package.json'));

  const devDLLDependencies = dllConfig.include
    .sort()
    .filter((elem, pos, arr) => arr.indexOf(elem) === pos);

  // We calculate a hash of the package.json's dependencies, which we can use
  // to determine if dependencies have changed since the last time we built
  // the vendor dll.
  const currentDependenciesHash = md5(
    JSON.stringify(
      devDLLDependencies.map((dep) => [
        dep,
        pkg.dependencies[dep],
        pkg.devDependencies[dep],
      ]),
    ),
  );

  const vendorDLLHashFolderPath = pathResolve(
    appRootDir.get(),
    bundleConfig.outputPath,
  );
  const vendorDLLHashFilePath = pathResolve(
    appRootDir.get(),
    bundleConfig.outputPath,
    `${dllConfig.name}_hash`,
  );

  function webpackConfigFactory() {
    return {
      // We only use this for development, so lets always include source maps.
      devtool: 'inline-source-map',
      entry: {
        [dllConfig.name]: devDLLDependencies,
      },
      node: {
        fs: 'empty',
        module: 'empty',
        child_process: 'empty',
      },
      output: {
        path: pathResolve(appRootDir.get(), bundleConfig.outputPath),
        filename: `${dllConfig.name}.js`,
        library: dllConfig.name,
      },
      plugins: [
        new webpack.DllPlugin({
          path: pathResolve(
            appRootDir.get(),
            bundleConfig.outputPath,
            `./${dllConfig.name}.json`,
          ),
          name: dllConfig.name,
        }),
      ],
    };
  }

  function buildVendorDLL() {
    return new Promise(async (resolve, reject) => {
      const webpackConfig = webpackConfigFactory();
      const vendorDLLCompiler = webpack(webpackConfig);

      vendorDLLCompiler.run((err, stats) => {
        if (err || stats.hasErrors()) {
          const info = stats.toJson();
          console.log(err);
          console.log(info.errors);
          reject(err);
          return;
        }

        // Update the dependency hash
        fs.ensureDir(vendorDLLHashFolderPath).then(() => {
          fs.writeFileSync(vendorDLLHashFilePath, currentDependenciesHash);

          resolve();
        });
      });
    });
  }

  return new Promise((resolve, reject) => {
    if (!fs.existsSync(vendorDLLHashFilePath)) {
      // builddll
      buildVendorDLL()
        .then(resolve)
        .catch(reject);
    } else {
      // first check if the md5 hashes match
      const dependenciesHash = fs.readFileSync(vendorDLLHashFilePath, 'utf8');
      const dependenciesChanged = dependenciesHash !== currentDependenciesHash;

      if (dependenciesChanged) {
        buildVendorDLL()
          .then(resolve)
          .catch(reject);
      } else {
        resolve();
      }
    }
  });
}

export default createVendorDLL;
<commit_msg>error handling in dev showing as if build is production fix<commit_after>import webpack from 'webpack';
import { resolve as pathResolve } from 'path';
import appRootDir from 'app-root-dir';
import md5 from 'md5';
import fs from 'fs-extra';

function createVendorDLL(bundleConfig) {
  const dllConfig = bundleConfig.devVendorDLL;

  // $FlowFixMe
  const pkg = require(pathResolve(appRootDir.get(), './package.json'));

  const devDLLDependencies = dllConfig.include
    .sort()
    .filter((elem, pos, arr) => arr.indexOf(elem) === pos);

  // We calculate a hash of the package.json's dependencies, which we can use
  // to determine if dependencies have changed since the last time we built
  // the vendor dll.
  const currentDependenciesHash = md5(
    JSON.stringify(
      devDLLDependencies.map((dep) => [
        dep,
        pkg.dependencies[dep],
        pkg.devDependencies[dep],
      ]),
    ),
  );

  const vendorDLLHashFolderPath = pathResolve(
    appRootDir.get(),
    bundleConfig.outputPath,
  );
  const vendorDLLHashFilePath = pathResolve(
    appRootDir.get(),
    bundleConfig.outputPath,
    `${dllConfig.name}_hash`,
  );

  function webpackConfigFactory() {
    return {
      // We only use this for development, so lets always include source maps.
      mode: 'development',
      devtool: 'inline-source-map',
      entry: {
        [dllConfig.name]: devDLLDependencies,
      },
      node: {
        fs: 'empty',
        module: 'empty',
        child_process: 'empty',
      },
      output: {
        path: pathResolve(appRootDir.get(), bundleConfig.outputPath),
        filename: `${dllConfig.name}.js`,
        library: dllConfig.name,
      },
      plugins: [
        new webpack.DllPlugin({
          path: pathResolve(
            appRootDir.get(),
            bundleConfig.outputPath,
            `./${dllConfig.name}.json`,
          ),
          name: dllConfig.name,
        }),
      ],
    };
  }

  function buildVendorDLL() {
    return new Promise(async (resolve, reject) => {
      const webpackConfig = webpackConfigFactory();
      const vendorDLLCompiler = webpack(webpackConfig);

      vendorDLLCompiler.run((err, stats) => {
        if (err || stats.hasErrors()) {
          const info = stats.toJson();
          console.log(err);
          console.log(info.errors);
          reject(err);
          return;
        }

        // Update the dependency hash
        fs.ensureDir(vendorDLLHashFolderPath).then(() => {
          fs.writeFileSync(vendorDLLHashFilePath, currentDependenciesHash);

          resolve();
        });
      });
    });
  }

  return new Promise((resolve, reject) => {
    if (!fs.existsSync(vendorDLLHashFilePath)) {
      // builddll
      buildVendorDLL()
        .then(resolve)
        .catch(reject);
    } else {
      // first check if the md5 hashes match
      const dependenciesHash = fs.readFileSync(vendorDLLHashFilePath, 'utf8');
      const dependenciesChanged = dependenciesHash !== currentDependenciesHash;

      if (dependenciesChanged) {
        buildVendorDLL()
          .then(resolve)
          .catch(reject);
      } else {
        resolve();
      }
    }
  });
}

export default createVendorDLL;
<|endoftext|>


<commit_before>    function echo ( ) {
    // http://kevin.vanzonneveld.net
    // +   original by: Philip Peterson
    // +   improved by: echo is bad
    // +   improved by: Nate
    // +    revised by: Der Simon (http://innerdom.sourceforge.net/)
    // +   improved by: Brett Zamir (http://brett-zamir.me)
    // +   bugfixed by: Eugene Bulkin (http://doubleaw.com/)
    // +   input by: JB
    // +   improved by: Brett Zamir (http://brett-zamir.me)
    // %        note 1: If browsers start to support DOM Level 3 Load and Save (parsing/serializing),
    // %        note 1: we wouldn't need any such long code (even most of the code below). See
    // %        note 1: link below for a cross-browser implementation in JavaScript. HTML5 might
    // %        note 1: possibly support DOMParser, but that is not presently a standard.
    // %        note 2: Although innerHTML is widely used and may become standard as of HTML5, it is also not ideal for
    // %        note 2: use with a temporary holder before appending to the DOM (as is our last resort below),
    // %        note 2: since it may not work in an XML context
    // %        note 3: Using innerHTML to directly add to the BODY is very dangerous because it will
    // %        note 3: break all pre-existing references to HTMLElements.
    // *     example 1: echo('

abc

abc

');
    // *     returns 1: undefined

    var arg = '', argc = arguments.length, argv = arguments, i = 0;
    var win = this.window;
    var d = win.document;
    var ns_xhtml = 'http://www.w3.org/1999/xhtml';
    var ns_xul = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'; // If we're in a XUL context

    var holder;

    var stringToDOM = function (str, parent, ns, container) {
        var extraNSs = '';
        if (ns === ns_xul) {
            extraNSs = ' xmlns:html="'+ns_xhtml+'"';
        }
        var stringContainer = '<'+container+' xmlns="'+ns+'"'+extraNSs+'>'+str+'</'+container+'>';
        if (win.DOMImplementationLS &&
            win.DOMImplementationLS.createLSInput &&
            win.DOMImplementationLS.createLSParser) { // Follows the DOM 3 Load and Save standard, but not
            // implemented in browsers at present; HTML5 is to standardize on innerHTML, but not for XML (though
            // possibly will also standardize with DOMParser); in the meantime, to ensure fullest browser support, could
            // attach http://svn2.assembla.com/svn/brettz9/DOMToString/DOM3.js (see http://svn2.assembla.com/svn/brettz9/DOMToString/DOM3.xhtml for a simple test file)
            var lsInput = DOMImplementationLS.createLSInput();
            // If we're in XHTML, we'll try to allow the XHTML namespace to be available by default
            lsInput.stringData = stringContainer;
            var lsParser = DOMImplementationLS.createLSParser(1, null); // synchronous, no schema type
            return lsParser.parse(lsInput).firstChild;
        }
        else if (win.DOMParser) {
            // If we're in XHTML, we'll try to allow the XHTML namespace to be available by default
            return new DOMParser().parseFromString(stringContainer, 'text/xml').documentElement.firstChild;
        }
        else if (win.ActiveXObject) { // We don't bother with a holder in Explorer as it doesn't support namespaces
            var d = new ActiveXObject('MSXML2.DOMDocument');
            d.loadXML(str);
            return d.documentElement;
        }
        /*else if (win.XMLHttpRequest) { // Supposed to work in older Safari
            var req = new win.XMLHttpRequest;
            req.open('GET', 'data:application/xml;charset=utf-8,'+encodeURIComponent(str), false);
            if (req.overrideMimeType) {
                req.overrideMimeType('application/xml');
            }
            req.send(null);
            return req.responseXML;
        }*/
        else { // Document fragment did not work with innerHTML, so we create a temporary element holder
            // If we're in XHTML, we'll try to allow the XHTML namespace to be available by default
            if (d.createElementNS && (d.contentType && d.contentType !== 'text/html')) { // Don't create namespaced elements if we're being served as HTML (currently only Mozilla supports this detection in true XHTML-supporting browsers, but Safari and Opera should work with the above DOMParser anyways, and IE doesn't support createElementNS anyways)
                holder = d.createElementNS(ns, container);
            }
            else {
                holder = d.createElement(container); // Document fragment did not work with innerHTML
            }
            holder.innerHTML = str;
            while (holder.firstChild) {
                parent.appendChild(holder.firstChild);
            }
            return false;
        }
        // throw 'Your browser does not support DOM parsing as required by echo()';
    };

    var ieFix = function (node) {
        if (node.nodeType === 1) {
            var newNode = d.createElement(node.nodeName);
            var i, len;
            if (node.attributes && node.attributes.length > 0) {
                for (i = 0, len = node.attributes.length; i < len; i++) {
                    newNode.setAttribute(node.attributes[i].nodeName, node.getAttribute(node.attributes[i].nodeName));
                }
            }
            if (node.childNodes && node.childNodes.length > 0) {
                for (i = 0, len = node.childNodes.length; i < len; i++) {
                    newNode.appendChild(ieFix(node.childNodes[i]));
                }
            }
            return newNode;
        }
        else {
            return d.createTextNode(node.nodeValue);
        }
    };

    for (i = 0; i < argc; i++ ) {
        arg = argv[i];
        if (this.php_js && this.php_js.ini && this.php_js.ini['phpjs.echo_embedded_vars']) {
            arg = arg.replace(/\{\$(.*?)\}/g, function (s, m1) { // We will assume embedded variables do not have dollar sign; to add a dollar sign, you must use {$$var}
                return eval(m1);
            });
        }
        if (d.appendChild) {
            if (d.body) {
                if (win.navigator.appName == 'Microsoft Internet Explorer') { // We unfortunately cannot use feature detection, since this is an IE bug with cloneNode nodes being appended
                    d.body.appendChild(ieFix(stringToDOM(arg)));
                }
                else {
                    var unappendedLeft = stringToDOM(arg, d.body, ns_xhtml, 'div').cloneNode(true); // We will not actually append the div tag (just using for providing XHTML namespace by default)
                    if (unappendedLeft) {
                        d.body.appendChild(unappendedLeft);
                    }
                }
            } else {
                d.documentElement.appendChild(stringToDOM(arg, d.documentElement, ns_xul, 'description')); // We will not actually append the description tag (just using for providing XUL namespace by default)
            }
        } else if (d.write) {
            d.write(arg);
        }/* else { // This could recurse if we ever add print!
            print(arg);
        }*/
    }
}<commit_msg>Variable substitution changes/comments<commit_after>    function echo ( ) {
    // http://kevin.vanzonneveld.net
    // +   original by: Philip Peterson
    // +   improved by: echo is bad
    // +   improved by: Nate
    // +    revised by: Der Simon (http://innerdom.sourceforge.net/)
    // +   improved by: Brett Zamir (http://brett-zamir.me)
    // +   bugfixed by: Eugene Bulkin (http://doubleaw.com/)
    // +   input by: JB
    // +   improved by: Brett Zamir (http://brett-zamir.me)
    // %        note 1: If browsers start to support DOM Level 3 Load and Save (parsing/serializing),
    // %        note 1: we wouldn't need any such long code (even most of the code below). See
    // %        note 1: link below for a cross-browser implementation in JavaScript. HTML5 might
    // %        note 1: possibly support DOMParser, but that is not presently a standard.
    // %        note 2: Although innerHTML is widely used and may become standard as of HTML5, it is also not ideal for
    // %        note 2: use with a temporary holder before appending to the DOM (as is our last resort below),
    // %        note 2: since it may not work in an XML context
    // %        note 3: Using innerHTML to directly add to the BODY is very dangerous because it will
    // %        note 3: break all pre-existing references to HTMLElements.
    // *     example 1: echo('

abc

abc

');
    // *     returns 1: undefined

    var arg = '', argc = arguments.length, argv = arguments, i = 0;
    var win = this.window;
    var d = win.document;
    var ns_xhtml = 'http://www.w3.org/1999/xhtml';
    var ns_xul = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'; // If we're in a XUL context

    var holder;

    var stringToDOM = function (str, parent, ns, container) {
        var extraNSs = '';
        if (ns === ns_xul) {
            extraNSs = ' xmlns:html="'+ns_xhtml+'"';
        }
        var stringContainer = '<'+container+' xmlns="'+ns+'"'+extraNSs+'>'+str+'</'+container+'>';
        if (win.DOMImplementationLS &&
            win.DOMImplementationLS.createLSInput &&
            win.DOMImplementationLS.createLSParser) { // Follows the DOM 3 Load and Save standard, but not
            // implemented in browsers at present; HTML5 is to standardize on innerHTML, but not for XML (though
            // possibly will also standardize with DOMParser); in the meantime, to ensure fullest browser support, could
            // attach http://svn2.assembla.com/svn/brettz9/DOMToString/DOM3.js (see http://svn2.assembla.com/svn/brettz9/DOMToString/DOM3.xhtml for a simple test file)
            var lsInput = DOMImplementationLS.createLSInput();
            // If we're in XHTML, we'll try to allow the XHTML namespace to be available by default
            lsInput.stringData = stringContainer;
            var lsParser = DOMImplementationLS.createLSParser(1, null); // synchronous, no schema type
            return lsParser.parse(lsInput).firstChild;
        }
        else if (win.DOMParser) {
            // If we're in XHTML, we'll try to allow the XHTML namespace to be available by default
            return new DOMParser().parseFromString(stringContainer, 'text/xml').documentElement.firstChild;
        }
        else if (win.ActiveXObject) { // We don't bother with a holder in Explorer as it doesn't support namespaces
            var d = new ActiveXObject('MSXML2.DOMDocument');
            d.loadXML(str);
            return d.documentElement;
        }
        /*else if (win.XMLHttpRequest) { // Supposed to work in older Safari
            var req = new win.XMLHttpRequest;
            req.open('GET', 'data:application/xml;charset=utf-8,'+encodeURIComponent(str), false);
            if (req.overrideMimeType) {
                req.overrideMimeType('application/xml');
            }
            req.send(null);
            return req.responseXML;
        }*/
        else { // Document fragment did not work with innerHTML, so we create a temporary element holder
            // If we're in XHTML, we'll try to allow the XHTML namespace to be available by default
            if (d.createElementNS && (d.contentType && d.contentType !== 'text/html')) { // Don't create namespaced elements if we're being served as HTML (currently only Mozilla supports this detection in true XHTML-supporting browsers, but Safari and Opera should work with the above DOMParser anyways, and IE doesn't support createElementNS anyways)
                holder = d.createElementNS(ns, container);
            }
            else {
                holder = d.createElement(container); // Document fragment did not work with innerHTML
            }
            holder.innerHTML = str;
            while (holder.firstChild) {
                parent.appendChild(holder.firstChild);
            }
            return false;
        }
        // throw 'Your browser does not support DOM parsing as required by echo()';
    };

    var ieFix = function (node) {
        if (node.nodeType === 1) {
            var newNode = d.createElement(node.nodeName);
            var i, len;
            if (node.attributes && node.attributes.length > 0) {
                for (i = 0, len = node.attributes.length; i < len; i++) {
                    newNode.setAttribute(node.attributes[i].nodeName, node.getAttribute(node.attributes[i].nodeName));
                }
            }
            if (node.childNodes && node.childNodes.length > 0) {
                for (i = 0, len = node.childNodes.length; i < len; i++) {
                    newNode.appendChild(ieFix(node.childNodes[i]));
                }
            }
            return newNode;
        }
        else {
            return d.createTextNode(node.nodeValue);
        }
    };

    for (i = 0; i < argc; i++ ) {
        arg = argv[i];
        if (this.php_js && this.php_js.ini && this.php_js.ini['phpjs.echo_embedded_vars']) {
            arg = arg.replace(/(.?)\{\$(.*?)\}/g, function (s, m1, m2) {
                // We assume for now that embedded variables do not have dollar sign; to add a dollar sign, you currently must use {$$var} (We might change this, however.)
                // Doesn't cover all cases yet: see http://php.net/manual/en/language.types.string.php#language.types.string.syntax.double
                if (m1 !== '\\') {
                    return m1+eval(m2);
                }
                else {
                    return s;
                }
            });
        }
        if (d.appendChild) {
            if (d.body) {
                if (win.navigator.appName == 'Microsoft Internet Explorer') { // We unfortunately cannot use feature detection, since this is an IE bug with cloneNode nodes being appended
                    d.body.appendChild(ieFix(stringToDOM(arg)));
                }
                else {
                    var unappendedLeft = stringToDOM(arg, d.body, ns_xhtml, 'div').cloneNode(true); // We will not actually append the div tag (just using for providing XHTML namespace by default)
                    if (unappendedLeft) {
                        d.body.appendChild(unappendedLeft);
                    }
                }
            } else {
                d.documentElement.appendChild(stringToDOM(arg, d.documentElement, ns_xul, 'description')); // We will not actually append the description tag (just using for providing XUL namespace by default)
            }
        } else if (d.write) {
            d.write(arg);
        }/* else { // This could recurse if we ever add print!
            print(arg);
        }*/
    }
}<|endoftext|>