Input:
Base class for many xtal - components [CODESPLIT] function XtallatX ( superClass ) { return class extends superClass { constructor ( ) { super ( ... arguments ) ; this . _evCount = { } ; } static get observedAttributes ( ) { return [ disabled ] ; } /**
         * Any component that emits events should not do so if it is disabled.
         * Note that this is not enforced, but the disabled property is made available.
         * Users of this mix-in should ensure not to call "de" if this property is set to true.
         */ get disabled ( ) { return this . _disabled ; } set disabled ( val ) { this . attr ( disabled , val , '' ) ; } /**
         * Set attribute value.
         * @param name
         * @param val
         * @param trueVal String to set attribute if true.
         */ attr ( name , val , trueVal ) { const v = val ? 'set' : 'remove' ; //verb this [ v + 'Attribute' ] ( name , trueVal || val ) ; } /**
         * Turn number into string with even and odd values easy to query via css.
         * @param n
         */ to$ ( n ) { const mod = n % 2 ; return ( n - mod ) / 2 + '-' + mod ; } /**
         * Increment event count
         * @param name
         */ incAttr ( name ) { const ec = this . _evCount ; if ( name in ec ) { ec [ name ] ++ ; } else { ec [ name ] = 0 ; } this . attr ( 'data-' + name , this . to$ ( ec [ name ] ) ) ; } attributeChangedCallback ( name , oldVal , newVal ) { switch ( name ) { case disabled : this . _disabled = newVal !== null ; break ; } } /**
         * Dispatch Custom Event
         * @param name Name of event to dispatch ("-changed" will be appended if asIs is false)
         * @param detail Information to be passed with the event
         * @param asIs If true, don't append event name with '-changed'
         */ de ( name , detail , asIs = false ) { const eventName = name + ( asIs ? '' : '-changed' ) ; const newEvent = new CustomEvent ( eventName , { detail : detail , bubbles : true , composed : false , } ) ; this . dispatchEvent ( newEvent ) ; this . incAttr ( eventName ) ; return newEvent ; } /**
         * Needed for asynchronous loading
         * @param props Array of property names to "upgrade", without losing value set while element was Unknown
         */ _upgradeProperties ( props ) { props . forEach ( prop => { if ( this . hasOwnProperty ( prop ) ) { let value = this [ prop ] ; delete this [ prop ] ; this [ prop ] = value ; } } ) ; } } ; }


Input: ( C ) Andrea Giammarchi - [CODESPLIT] function ( re ) { var arr = [ ] , tag ; for ( tag in register ) { if ( re . test ( tag ) ) arr . push ( tag ) ; } return arr ; }


Input: ( C ) Andrea Giammarchi - [CODESPLIT] function ( Class , tag ) { tag = tag . toLowerCase ( ) ; if ( ! ( tag in register ) ) { register [ Class ] = ( register [ Class ] || [ ] ) . concat ( tag ) ; register [ tag ] = ( register [ tag . toUpperCase ( ) ] = Class ) ; } }


Input: Normalize Path [CODESPLIT] function normalizePath ( path ) { path = '/' + path path = resolve ( normalize ( path ) ) path = path . replace ( / (%[a-f0-9]{2}) / g , $1 => $1 . toUpperCase ( ) ) if ( path === '' ) path = '/' return path }


Input: Parse the arguments and return an special array . [CODESPLIT] function parseArgs ( ... args ) { const l = args . length const last = args [ l - 1 ] let cb , opts , paths if ( _ . isFunction ( last ) ) { cb = last args . pop ( ) ; // don't remove this semicolon [ paths , opts ] = parseArgs ( ... args ) } else if ( _ . isObject ( last ) && ! Array . isArray ( last ) ) { opts = last args . pop ( ) paths = args } else if ( ! last && l > 0 ) { args . pop ( ) return parseArgs ( ... args ) } else { paths = args } return [ _ . compact ( _ . flatten ( paths , true ) ) , opts || { } , cb ] }


Input: / * sort functions each sort function takes two parameters a and b you are comparing a [ 0 ] and b [ 0 ] [CODESPLIT] function sort_numeric ( a , b ) { var aa = void 0 , bb = void 0 ; aa = parseFloat ( a [ 0 ] . replace ( / [^0-9.-] / g , '' ) ) ; if ( isNaN ( aa ) ) aa = 0 ; bb = parseFloat ( b [ 0 ] . replace ( / [^0-9.-] / g , '' ) ) ; if ( isNaN ( bb ) ) bb = 0 ; return aa - bb ; }


Input:
A Bitmap represents an Image Canvas or Video in the display list . A Bitmap can be instantiated using an existing HTML element or a string . [CODESPLIT] function Bitmap ( imageOrUri ) { this . DisplayObject_constructor ( ) ; // public properties: /**
		 * The source image to display. This can be a CanvasImageSource
		 * (image, video, canvas), an object with a `getImage` method that returns a CanvasImageSource, or a string URL to an image.
		 * If the latter, a new Image instance with the URL as its src will be used.
		 * @property image
		 * @type CanvasImageSource | Object
		 **/ if ( typeof imageOrUri == "string" ) { this . image = document . createElement ( "img" ) ; this . image . src = imageOrUri ; } else { this . image = imageOrUri ; } /**
		 * Specifies an area of the source image to draw. If omitted, the whole image will be drawn.
		 * Note that video sources must have a width / height set to work correctly with `sourceRect`.
		 * @property sourceRect
		 * @type Rectangle
		 * @default null
		 */ this . sourceRect = null ; // private properties: /**
		 * Docced in superclass.
		 */ this . _webGLRenderStyle = createjs . DisplayObject . _StageGL_BITMAP ; }


Input:
constructor : Applies a box blur to DisplayObjects . Note that this filter is fairly CPU intensive particularly if the quality is set higher than 1 . [CODESPLIT] function BlurFilter ( blurX , blurY , quality ) { this . Filter_constructor ( ) ; // public properties: /**
		 * Horizontal blur radius in pixels
		 * @property blurX
		 * @default 0
		 * @type Number
		 **/ this . _blurX = blurX ; this . _blurXTable = [ ] ; /**
		 * Vertical blur radius in pixels
		 * @property blurY
		 * @default 0
		 * @type Number
		 **/ this . _blurY = blurY ; this . _blurYTable = [ ] ; /**
		 * Number of blur iterations. For example, a value of 1 will produce a rough blur. A value of 2 will produce a
		 * smoother blur, but take twice as long to run.
		 * @property quality
		 * @default 1
		 * @type Number
		 **/ this . _quality ; /**
		 * This is a template to generate the shader for {{#crossLink FRAG_SHADER_BODY}}{{/crossLink}}
		 */ this . FRAG_SHADER_TEMPLATE = ( "uniform float xWeight[{{blurX}}];" + "uniform float yWeight[{{blurY}}];" + "uniform vec2 textureOffset;" + "void main(void) {" + "vec4 color = vec4(0.0);" + "float xAdj = ({{blurX}}.0-1.0)/2.0;" + "float yAdj = ({{blurY}}.0-1.0)/2.0;" + "vec2 sampleOffset;" + "for(int i=0; i<{{blurX}}; i++) {" + "for(int j=0; j<{{blurY}}; j++) {" + "sampleOffset = vRenderCoord + (textureOffset * vec2(float(i)-xAdj, float(j)-yAdj));" + "color += texture2D(uSampler, sampleOffset) * (xWeight[i] * yWeight[j]);" + "}" + "}" + "gl_FragColor = color.rgba;" + "}" ) ; // update the filter using the setters if ( isNaN ( quality ) || quality < 1 ) { quality = 1 ; } this . setQuality ( quality | 0 ) ; }


Input: NODE_MODULES_PATHS ( START ) 1 . let PARTS = path split ( START ) 2 . let I = count of PARTS - 1 3 . let DIRS = [] 4 . while I > = 0 a . if PARTS [ I ] = node_modules CONTINUE c . DIR = path join ( PARTS [ 0 .. I ] + node_modules ) b . DIRS = DIRS + DIR c . let I = I - 1 5 . return DIRS Alloy doesn t like it when you include the file extension ... [CODESPLIT] function convert_to_alloy_path ( resolved_path ) { var parsed_path = path . posix . parse ( resolved_path ) ; return path . posix . join ( parsed_path . dir , parsed_path . name ) ; }


Input: LOAD_AS_FILE ( X ) 1 . If X is a file load X as JavaScript text . STOP 2 . If X . js is a file load X . js as JavaScript text . STOP 3 . If X . json is a file parse X . json to a JavaScript Object . STOP 4 . If X . node is a file load X . node as binary addon . STOP [CODESPLIT] function load_as_file ( request , startpath ) { var module_path ; var resolved_path = path . posix . resolve ( startpath , request ) ; _ . includes ( registry . files , resolved_path ) && ( module_path = resolved_path ) ; if ( module_path ) { // logger.trace("file found: " + module_path); return module_path ; } var extension = path . extname ( request ) ; if ( ! extension ) { var exts = [ ".js" , ".json" ] ; _ . forEach ( exts , function ( ext ) { resolved_path = path . posix . resolve ( startpath , request + ext ) ; _ . includes ( registry . files , resolved_path ) && ( module_path = resolved_path ) ; if ( ! module_path ) { return ! module_path ; } } ) ; } return module_path ; }


Input: LOAD_AS_DIRECTORY ( X ) 1 . If X / package . json is a file a . Parse X / package . json and look for main field . b . let M = X + ( json main field ) c . LOAD_AS_FILE ( M ) 2 . If X / index . js is a file load X / index . js as JavaScript text . STOP 3 . If X / index . json is a file parse X / index . json to a JavaScript object . STOP 4 . If X / index . node is a file load X / index . node as binary addon . STOP [CODESPLIT] function load_as_directory ( request , startpath ) { var resolved_path = path . posix . resolve ( startpath , request ) ; var module_path = _ . find ( registry . directories , function ( item ) { return item . id === resolved_path ; } ) ; if ( module_path ) { return module_path . path ; } }


Input: LOAD_NODE_MODULES ( X START ) 1 . let DIRS = NODE_MODULES_PATHS ( START ) 2 . for each DIR in DIRS : a . LOAD_AS_FILE ( DIR / X ) b . LOAD_AS_DIRECTORY ( DIR / X ) [CODESPLIT] function load_node_modules ( request , startpath ) { var resolved_path ; var nodepaths = node_modules_paths ( startpath ) ; _ . forEach ( nodepaths , function ( nodepath ) { resolved_path = load_as_file ( request , nodepath ) ; return ! resolved_path ; } ) ; if ( resolved_path ) { return resolved_path ; } _ . forEach ( nodepaths , function ( nodepath ) { resolved_path = load_as_directory ( request , nodepath ) ; return ! resolved_path ; } ) ; return resolved_path ; }


Input: Return a new Thing that has the keys in sorted order . Recursive . [CODESPLIT] function canonicalize ( value , stack ) { var canonicalizedObj ; /* eslint-disable no-unused-vars */ var prop ; /* eslint-enable no-unused-vars */ var type = getType ( value ) ; function withStack ( value , fn ) { stack . push ( value ) ; fn ( ) ; stack . pop ( ) ; } stack = stack || [ ] ; if ( stack . indexOf ( value ) !== - 1 ) { return '[Circular]' ; } switch ( type ) { case 'undefined' : case 'buffer' : case 'null' : canonicalizedObj = value ; break ; case 'array' : withStack ( value , function ( ) { canonicalizedObj = value . map ( function ( item ) { return canonicalize ( item , stack ) ; } ) ; } ) ; break ; case 'function' : /* eslint-disable guard-for-in */ for ( prop in value ) { canonicalizedObj = { } ; break ; } /* eslint-enable guard-for-in */ if ( ! canonicalizedObj ) { canonicalizedObj = emptyRepresentation ( value , type ) ; break ; } /* falls through */ case 'object' : canonicalizedObj = canonicalizedObj || { } ; withStack ( value , function ( ) { Object . keys ( value ) . sort ( ) . forEach ( function ( key ) { canonicalizedObj [ key ] = canonicalize ( value [ key ] , stack ) ; } ) ; } ) ; break ; case 'date' : case 'number' : case 'regexp' : case 'boolean' : canonicalizedObj = value ; break ; default : canonicalizedObj = value . toString ( ) ; } return canonicalizedObj ; }


Input: If a value could have properties and has none this function is called which returns a string representation of the empty value . [CODESPLIT] function emptyRepresentation ( value , type ) { type = type || getType ( value ) ; switch ( type ) { case 'function' : return '[Function]' ; case 'object' : return '{}' ; case 'array' : return '[]' ; default : return value . toString ( ) ; } }


Input: Used with spread to transform ( data metadata ) pairs from dynode into just data [CODESPLIT] function createDynodeOptions ( dynamoAsPromisedOptions , key , extraDynodeOptions ) { var dynodeOptions = _ . clone ( extraDynodeOptions || { } ) ; // If given an `onlyIfExists` option, assemble the `Expected` Dynode option value by looking at the key and values. // Example: //     var key = { hash: "H", range: 5 }; //     var dapOptions = { onlyIfExists: { hash: "h", range: "r" } }; //     createDynodeOptions(dapOptions, key) === { //         Expected: { h: { Value: { S: "H" }, r: { Value: { N: 5 } } } //     }; if ( typeof dynamoAsPromisedOptions === "object" && dynamoAsPromisedOptions . onlyIfExists ) { dynodeOptions . Expected = { } ; var keyValues = typeof key === "string" ? { hash : key } : key ; var keysThatMustExist = typeof dynamoAsPromisedOptions . onlyIfExists === "string" ? { hash : dynamoAsPromisedOptions . onlyIfExists } : dynamoAsPromisedOptions . onlyIfExists ; Object . keys ( keysThatMustExist ) . forEach ( function ( keyType ) { var keyName = keysThatMustExist [ keyType ] ; var beforeTypeAnnotations = { } ; beforeTypeAnnotations [ keyName ] = keyValues [ keyType ] ; var withTypeAnnotations = addDynamoTypeAnnotations ( beforeTypeAnnotations ) ; dynodeOptions . Expected [ keyName ] = { Value : withTypeAnnotations [ keyName ] } ; } ) ; } return dynodeOptions ; }


Input: increment anchor count [CODESPLIT] function ( doc , first , last ) { var f = doc . WordPos [ first ] ; var l ; if ( last == doc . WordPos . length - 1 ) // l = doc . DocLength ; else l = doc . WordPos [ last + 1 ] ; return l - f ; }


Input: Parse the given js currently supporting : [CODESPLIT] function parseConditionals ( js ) { var lines = js . split ( '\n' ) , len = lines . length , buffer = true , browser = false , buf = [ ] , line , cond ; for ( var i = 0 ; i < len ; ++ i ) { line = lines [ i ] ; if ( / ^ *\/\/ *if *(node|browser) / gm . exec ( line ) ) { cond = RegExp . $1 ; buffer = browser = 'browser' == cond ; } else if ( / ^ *\/\/ *end / . test ( line ) ) { buffer = true ; browser = false ; } else if ( browser ) { buf . push ( line . replace ( / ^( *)\/\/ / , '$1' ) ) ; } else if ( buffer ) { buf . push ( line ) ; } } return buf . join ( '\n' ) ; }


Input: Compile the files . [CODESPLIT] function compile ( ) { var buf = '' ; buf += '(function() {\n' ; buf += '\n// CommonJS require()\n\n' ; buf += browser . require + '\n\n' ; buf += 'require.modules = {};\n\n' ; buf += 'require.resolve = ' + browser . resolve + ';\n\n' ; buf += 'require.register = ' + browser . register + ';\n\n' ; buf += 'require.relative = ' + browser . relative + ';\n\n' ; args . forEach ( function ( file ) { var js = files [ file ] ; file = file . replace ( 'lib/' , '' ) ; buf += '\nrequire.register("' + file + '", function(module, exports, require){\n' ; buf += js ; buf += '\n}); // module: ' + file + '\n' ; } ) ; buf += '\nwindow.kiwi = require("kiwi");\n' ; buf += '})();\n' ; fs . writeFile ( 'kiwi.js' , buf , function ( err ) { if ( err ) throw err ; console . log ( '  \033[90m create : \033[0m\033[36m%s\033[0m' , 'kiwi.js' ) ; console . log ( ) ; } ) ; }


Input: [ a . b . c d ] - > [ a b c d ] [CODESPLIT] function splitDots ( list ) { var result = [ ] ; list . forEach ( function ( x ) { if ( typeof x === 'string' ) { x . split ( '.' ) . forEach ( function ( part ) { result . push ( part ) ; } ) ; } else { result . push ( x ) ; } } ) ; return result ; }


Input: Creates an optimistic chain no checks before calling a function or accessing a property or calling a method [CODESPLIT] function fp ( ) { var args = Array . prototype . slice . call ( arguments , 0 ) ; if ( args . length ) { if ( ! args . every ( isStringOrFunction ) ) { var signature = args . map ( humanizeArgument ) . join ( '\n\t' ) ; throw new Error ( 'Invalid arguments to functional pipeline - not a string or function\n\t' + signature ) ; } var fns = splitDots ( args ) ; return function ( d ) { var originalObject = d ; fns . forEach ( function ( fn ) { if ( typeof fn === 'string' ) { if ( typeof d [ fn ] === 'function' ) { d = d [ fn ] . call ( d , d ) ; } else if ( typeof d [ fn ] !== 'undefined' ) { d = d [ fn ] ; } else { var signature = args . map ( humanizeArgument ) . join ( '\n\t' ) ; throw new Error ( 'Cannot use property ' + fn + ' from object ' + JSON . stringify ( d , null , 2 ) + '\npipeline\n\t' + signature + '\noriginal object\n' + JSON . stringify ( originalObject , null , 2 ) ) ; } } else if ( typeof fn === 'function' ) { d = fn ( d ) ; } else { throw new Error ( 'Cannot apply ' + JSON . stringify ( fn , null , 2 ) + ' to value ' + d + ' not a property name or a function' ) ; } } ) ; return d ; } ; } }


Input: VATPeriod [CODESPLIT] function VATPeriod ( effectiveFrom , superReduced , reduced , standard , parking ) { this . _effectiveFrom = effectiveFrom ; this . _superReduced = superReduced ; this . _reduced = reduced ; this . _standard = standard ; this . _parking = parking ; }


Input: / * Read tags from tags directory [CODESPLIT] function loadFilters ( loadedFiles ) { loadedFiles = loadedFiles || frame . files . requireDir ( __dirname + '/filters/' ) ; for ( var file in loadedFiles ) { var fileFilters = loadedFiles [ file ] ; for ( var filter in fileFilters ) { filters [ filter ] = fileFilters [ filter ] ; } } }


Input: Construct a new Rieussec stopwatch [CODESPLIT] function ( tickRate ) { events . EventEmitter . call ( this ) ; // Initialize private properties this . _milliseconds = 0 ; this . _setState ( 'stopped' ) ; this . _timer = new NanoTimer ( ) ; tickRate = tickRate || 100 ; Object . defineProperty ( this , 'tickRate' , { enumerable : true , configurable : false , writable : false , value : tickRate } ) ; }


Input: Callback for utils . applyAll [CODESPLIT] function onProcessed ( err , processed ) { if ( err ) return callback ( err ) ; _this . _tokenize ( processed , onTokenized ) ; }


Input: Callback for Compiler#_tokenize [CODESPLIT] function onTokenized ( err , tokenized ) { if ( err ) return callback ( err ) ; tokenized . compile ( _this , onCompiled ) ; }


Input: Callback for Compiler#_compileTokens [CODESPLIT] function onCompiled ( err , compiled ) { if ( err ) return callback ( err ) ; var func ; try { func = new Function ( "$template" , "$tools" , "_" , "$data" , "$helpers" , "$callback" , compiled ) ; } catch ( err ) { return callback ( err ) ; } func . $helpers = _this . helpers ; callback ( null , func ) ; }


Input: / * Base token / * Initializes BaseToken with parent and root . [CODESPLIT] function BaseToken ( root , parent , options ) { this . parent = parent ; this . children = [ ] ; this . tag = null ; this . tagType = null ; this . options = options ; this . root = root ; }


Input: / * Literal token / * Initialize LiteralToken with parent root and literal [CODESPLIT] function LiteralToken ( literal , root , parent , options ) { LiteralToken . _superclass . call ( this , root , parent , options ) ; this . literal = literal ; }


Input: / * Block token / * Initialize BlockToken for tag of tagType with parent and children [CODESPLIT] function BlockToken ( tag , tagType , root , parent , children , options ) { BlockToken . _superclass . call ( this , root , parent , options ) ; this . tag = tag ; this . tagType = tagType ; if ( ! children ) children = [ ] ; this . children = children ; this . intermediate = [ ] ; }


Input: / * Initialize IntermediateToken for tag of tagType with parent and children [CODESPLIT] function IntermediateToken ( tag , tagType , root , parent , children , options ) { IntermediateToken . _superclass . call ( this , tag , tagType , root , parent , children , options ) ; }


Input: Global functions Outputs if clause based on condition . If not strict actual test will be wrapped in a try…catch statement to catch ReferenceErrors silently [CODESPLIT] function createIfCondition ( condition , strict ) { var compiled ; if ( strict ) { compiled = 'if(' + condition + ')' ; } else { compiled = 'try {' + '__tmp = ' + condition + '} catch(__err) {' + 'if(__err instanceof ReferenceError) {' + '__tmp = false;' + '} else {' + 'throw __err;' + '}' + '}' + 'if(__tmp)' ; } return compiled ; }


Input: VATCountry [CODESPLIT] function VATCountry ( name , code , countryCode , periods , date ) { this . _name = name ; this . _code = code ; this . _countryCode = countryCode ; this . _periods = periods ; this . setDate ( date ) ; }


Input: Asynchronously compiles tokens and invoke callback ( err compiled ) with compiled as an array . [CODESPLIT] function compileTokenArray ( tokens , compiler , callback ) { var acc = [ ] ; var index = 0 ; function compileOne ( token , next ) { token . compile ( compiler , function onCompiled ( err , compiled ) { if ( err ) return next ( err ) ; acc . push ( compiled ) ; next ( null , compiled ) ; } ) ; index ++ ; } function done ( err ) { if ( err ) return callback ( err ) ; callback ( null , acc ) ; } asyncForEach ( tokens , compileOne , done ) ; }


Input: Initializes Template with optionnally the given str and options . [CODESPLIT] function Template ( str , options ) { // Handle the case where the only argument passed is the `options` object if ( _ . isObject ( str ) && ! options ) { options = str ; str = null ; } // Create options if not provided options = options ? _ . clone ( options ) : { } ; // Set default cache behavior // if node if ( ! _ . isBoolean ( options . cache ) ) { options . cache = process . env . NODE_ENV === 'production' ; } // end // Merges given `options` with `DEFAULTS` options = _ . defaults ( options , DEFAULTS ) ; options . cacheContext = options . cacheContext || Template ; // Sets instance variables this . template = str ; this . options = options ; this . _compiled = null ; // Creates the cache if not already done if ( options . cache && ! ( this . _getCache ( ) instanceof options . cacheHandler ) ) { var cacheOptions = [ options . cacheHandler ] . concat ( options . cacheOptions ) ; options . cacheContext [ options . _cacheProp ] = typeof window !== 'undefined' ? new options . cacheHandler ( ) : construct . apply ( this , cacheOptions ) ; } }


Input: used to fix fs . watch on windows triggering cascading events the function fn can only be called once per rate [CODESPLIT] function ( fn , rate ) { var allowed = true ; return function ( ) { if ( allowed ) { allowed = false ; fn . apply ( null , [ ] . slice . call ( arguments , 0 ) ) ; setTimeout ( function ( ) { allowed = true ; } , rate ) ; } } }


Input: get optional data from command line as json or file containing json or javascript [CODESPLIT] function get_data ( callback ) { var data ; try { data = program . data ? JSON . parse ( program . data ) : { } ; callback ( data ) ; } catch ( err ) { fs . readFile ( program . data , function ( err , str ) { str = '' + str ; if ( ! err ) { try { data = JSON . parse ( str ) ; callback ( data ) ; } catch ( err ) { data = eval ( str ) ; callback ( data ) ; } } } ) ; } return data ; }


Input: function ispunct ( ch ) { return / [ ! #$%& () * + - . \ / : ; < = > ? [CODESPLIT] function ispunct ( ch ) { ch = ch . charCodeAt ( 0 ) ; return ( ch >= 0x21 && ch <= 0x2F ) || ( ch >= 0x3a && ch <= 0x40 ) || ( ch >= 0x5B && ch <= 0x60 ) || ( ch >= 0x7F ) ; }


Input: / * hash : function ( s ) { using Java s hash method ( except on empty string ) var hash = 0 i l char ; if ( s . length == 0 ) return 1 ; for ( i = 0 l = s . length ; i < l ; i ++ ) { char = s . charCodeAt ( i ) ; hash = (( hash << 5 ) - hash ) + char ; hash | = 0 ; // Convert to 32bit integer } return hash ; [CODESPLIT] function ( str ) { var inhash = 0 ; var charcount = 0 ; var char ; if ( str . length == 0 ) return 1 ; // if word is null, return 1 as hash value else for ( var i = 0 ; i < str . length ; i ++ ) { char = str . charCodeAt ( i ) ; inhash = ( ( inhash << 7 ) | ( inhash >>> 25 ) ) ^ char ; // xor into the rotateleft(7) of inhash inhash >>>= 0 ; // Convert to 32bit unsigned integer } return inhash ; }


Input: adds the webhook authentication middleware module to the webserver [CODESPLIT] function secureWebhookEndpoints ( ) { var authenticationMiddleware = require ( __dirname + '/middleware/slack_authentication.js' ) ; // convert a variable argument list to an array, drop the webserver argument var tokens = Array . prototype . slice . call ( arguments ) ; var webserver = tokens . shift ( ) ; slack_botkit . logger . info ( '** Requiring token authentication for webhook endpoints for Slash commands ' + 'and outgoing webhooks; configured ' + tokens . length + ' token(s)' ) ; webserver . use ( authenticationMiddleware ( tokens ) ) ; }


Input: Makes a POST request as a form to the given url with the options as data [CODESPLIT] function postForm ( url , formData , cb , multipart ) { cb = cb || noop ; bot . logger . info ( '** API CALL: ' + url ) ; var params = { url : url , headers : { 'User-Agent' : bot . userAgent ( ) , } } ; if ( multipart === true ) { params . formData = formData ; } else { params . form = formData ; } request . post ( params , function ( error , response , body ) { bot . logger . debug ( 'Got response' , error , body ) ; if ( error ) { return cb ( error ) ; } if ( response . statusCode == 200 ) { var json ; try { json = JSON . parse ( body ) ; } catch ( parseError ) { return cb ( parseError ) ; } return cb ( ( json . ok ? null : json . error ) , json ) ; } else if ( response . statusCode == 429 ) { return cb ( new Error ( 'Rate limit exceeded' ) ) ; } else { return cb ( new Error ( 'Invalid response' ) ) ; } } ) ; }


Input:
var PKG_VERSION = require ( .. / package . json ) . version ; var express = require ( express ) ; var bodyParser = require ( body - parser ) ; [CODESPLIT] function Botkit ( configuration ) { var botkit = { events : { } , // this will hold event handlers config : { } , // this will hold the configuration tasks : [ ] , taskCount : 0 , convoCount : 0 , my_version : null , my_user_agent : null , memory_store : { users : { } , channels : { } , teams : { } , } } ; /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ // TODO: externalize this into some sort of utterances.json file botkit . utterances = { yes : new RegExp ( / ^(yes|yea|yup|yep|ya|sure|ok|y|yeah|yah) / i ) , no : new RegExp ( / ^(no|nah|nope|n) / i ) , quit : new RegExp ( / ^(quit|cancel|end|stop|done|exit|nevermind|never mind) / i ) } ; /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ // define some middleware points where custom functions // can plug into key points of botkits process botkit . middleware = { spawn : ware ( ) , ingest : ware ( ) , normalize : ware ( ) , categorize : ware ( ) , receive : ware ( ) , heard : ware ( ) , // best place for heavy i/o because fewer messages triggered : ware ( ) , // like heard, but for other events capture : ware ( ) , format : ware ( ) , send : ware ( ) , } ; botkit . ingest = function ( bot , payload , source ) { // keep an unmodified copy of the message payload . raw_message = clone ( payload ) ; payload . _pipeline = { stage : 'ingest' , } ; botkit . middleware . ingest . run ( bot , payload , source , function ( err , bot , payload , source ) { if ( err ) { console . error ( 'An error occured in the ingest middleware: ' , err ) ; return ; } botkit . normalize ( bot , payload ) ; } ) ; } ; botkit . normalize = function ( bot , payload ) { payload . _pipeline . stage = 'normalize' ; botkit . middleware . normalize . run ( bot , payload , function ( err , bot , message ) { if ( err ) { console . error ( 'An error occured in the normalize middleware: ' , err ) ; return ; } if ( ! message . type ) { message . type = 'message_received' ; } botkit . categorize ( bot , message ) ; } ) ; } ; botkit . categorize = function ( bot , message ) { message . _pipeline . stage = 'categorize' ; botkit . middleware . categorize . run ( bot , message , function ( err , bot , message ) { if ( err ) { console . error ( 'An error occured in the categorize middleware: ' , err ) ; return ; } botkit . receiveMessage ( bot , message ) ; } ) ; } ; botkit . receiveMessage = function ( bot , message ) { message . _pipeline . stage = 'receive' ; botkit . middleware . receive . run ( bot , message , function ( err , bot , message ) { if ( err ) { console . error ( 'An error occured in the receive middleware: ' , err ) ; return ; } else { botkit . logger . debug ( 'RECEIVED MESSAGE' ) ; bot . findConversation ( message , function ( convo ) { if ( convo ) { convo . handle ( message ) ; } else { botkit . trigger ( message . type , [ bot , message ] ) ; } } ) ; } } ) ; } ; /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ function Conversation ( task , message ) { this . messages = [ ] ; this . sent = [ ] ; this . transcript = [ ] ; this . context = { user : message . user , channel : message . channel , bot : task . bot , } ; this . events = { } ; this . vars = { } ; this . threads = { } ; this . thread = null ; this . status = 'new' ; this . task = task ; this . source_message = message ; this . handler = null ; this . responses = { } ; this . capture_options = { } ; this . startTime = new Date ( ) ; this . lastActive = new Date ( ) ; /** will be pointing to a callback which will be called after timeout,
     * conversation will be not be ended and should be taken care by callback
     */ this . timeOutHandler = null ; this . collectResponse = function ( key , value ) { this . responses [ key ] = value ; } ; this . capture = function ( response , cb ) { var that = this ; var capture_key = this . sent [ this . sent . length - 1 ] . text ; botkit . middleware . capture . run ( that . task . bot , response , that , function ( err , bot , response , convo ) { if ( response . text ) { response . text = response . text . trim ( ) ; } else { response . text = '' ; } if ( that . capture_options . key != undefined ) { capture_key = that . capture_options . key ; } // capture the question that was asked // if text is an array, get 1st if ( typeof ( that . sent [ that . sent . length - 1 ] . text ) == 'string' ) { response . question = that . sent [ that . sent . length - 1 ] . text ; } else if ( Array . isArray ( that . sent [ that . sent . length - 1 ] . text ) ) { response . question = that . sent [ that . sent . length - 1 ] . text [ 0 ] ; } else { response . question = '' ; } if ( that . capture_options . multiple ) { if ( ! that . responses [ capture_key ] ) { that . responses [ capture_key ] = [ ] ; } that . responses [ capture_key ] . push ( response ) ; } else { that . responses [ capture_key ] = response ; } if ( cb ) cb ( response ) ; } ) ; } ; this . handle = function ( message ) { var that = this ; this . lastActive = new Date ( ) ; this . transcript . push ( message ) ; botkit . logger . debug ( 'HANDLING MESSAGE IN CONVO' , message ) ; // do other stuff like call custom callbacks if ( this . handler ) { this . capture ( message , function ( message ) { // if the handler is a normal function, just execute it! // NOTE: anyone who passes in their own handler has to call // convo.next() to continue after completing whatever it is they want to do. if ( typeof ( that . handler ) == 'function' ) { that . handler ( message , that ) ; } else { // handle might be a mapping of keyword to callback. // lets see if the message matches any of the keywords let match , patterns = that . handler ; for ( let p = 0 ; p < patterns . length ; p ++ ) { if ( patterns [ p ] . pattern && botkit . hears_test ( [ patterns [ p ] . pattern ] , message ) ) { botkit . middleware . heard . run ( that . task . bot , message , function ( err , bot , message ) { patterns [ p ] . callback ( message , that ) ; } ) ; return ; } } // none of the messages matched! What do we do? // if a default exists, fire it! for ( let p = 0 ; p < patterns . length ; p ++ ) { if ( patterns [ p ] . default ) { botkit . middleware . heard . run ( that . task . bot , message , function ( err , bot , message ) { patterns [ p ] . callback ( message , that ) ; } ) ; return ; } } } } ) ; } else { // do nothing } } ; this . setVar = function ( field , value ) { if ( ! this . vars ) { this . vars = { } ; } this . vars [ field ] = value ; } ; this . activate = function ( ) { this . task . trigger ( 'conversationStarted' , [ this ] ) ; this . task . botkit . trigger ( 'conversationStarted' , [ this . task . bot , this ] ) ; this . status = 'active' ; } ; /**
     * active includes both ACTIVE and ENDING
     * in order to allow the timeout end scripts to play out
     **/ this . isActive = function ( ) { return ( this . status == 'active' || this . status == 'ending' ) ; } ; this . deactivate = function ( ) { this . status = 'inactive' ; } ; this . say = function ( message ) { this . addMessage ( message ) ; } ; this . sayFirst = function ( message ) { if ( typeof ( message ) == 'string' ) { message = { text : message , channel : this . source_message . channel , } ; } else { message . channel = this . source_message . channel ; } this . messages . unshift ( message ) ; } ; this . on = function ( event , cb ) { botkit . logger . debug ( 'Setting up a handler for' , event ) ; var events = event . split ( / \, / g ) ; for ( var e in events ) { if ( ! this . events [ events [ e ] ] ) { this . events [ events [ e ] ] = [ ] ; } this . events [ events [ e ] ] . push ( cb ) ; } return this ; } ; this . trigger = function ( event , data ) { if ( this . events [ event ] ) { for ( var e = 0 ; e < this . events [ event ] . length ; e ++ ) { var res = this . events [ event ] [ e ] . apply ( this , data ) ; if ( res === false ) { return ; } } } else { } } ; // proceed to the next message after waiting for an answer this . next = function ( ) { this . handler = null ; } ; this . repeat = function ( ) { if ( this . sent . length ) { this . messages . push ( this . sent [ this . sent . length - 1 ] ) ; } else { // console.log('TRIED TO REPEAT, NOTHING TO SAY'); } } ; this . silentRepeat = function ( ) { return ; } ; this . addQuestion = function ( message , cb , capture_options , thread ) { if ( typeof ( message ) == 'string' ) { message = { text : message , channel : this . source_message . channel } ; } else { message . channel = this . source_message . channel ; } if ( capture_options ) { message . capture_options = capture_options ; } message . handler = cb ; this . addMessage ( message , thread ) ; } ; this . ask = function ( message , cb , capture_options ) { this . addQuestion ( message , cb , capture_options , this . thread || 'default' ) ; } ; this . addMessage = function ( message , thread ) { if ( ! thread ) { thread = this . thread ; } if ( typeof ( message ) == 'string' ) { message = { text : message , channel : this . source_message . channel , } ; } else { message . channel = this . source_message . channel ; } if ( ! this . threads [ thread ] ) { this . threads [ thread ] = [ ] ; } this . threads [ thread ] . push ( message ) ; // this is the current topic, so add it here as well if ( this . thread == thread ) { this . messages . push ( message ) ; } } ; // how long should the bot wait while a user answers? this . setTimeout = function ( timeout ) { this . task . timeLimit = timeout ; } ; // For backwards compatibility, wrap gotoThread in its previous name this . changeTopic = function ( topic ) { this . gotoThread ( topic ) ; } ; this . hasThread = function ( thread ) { return ( this . threads [ thread ] != undefined ) ; } ; this . transitionTo = function ( thread , message ) { // add a new transition thread // add this new message to it // set that message action to execute the actual transition // then change threads to transition thread var num = 1 ; while ( this . hasThread ( 'transition_' + num ) ) { num ++ ; } var threadname = 'transition_' + num ; if ( typeof ( message ) == 'string' ) { message = { text : message , action : thread } ; } else { message . action = thread ; } this . addMessage ( message , threadname ) ; this . gotoThread ( threadname ) ; } ; this . beforeThread = function ( thread , callback ) { if ( ! this . before_hooks ) { this . before_hooks = { } ; } if ( ! this . before_hooks [ thread ] ) { this . before_hooks [ thread ] = [ ] ; } this . before_hooks [ thread ] . push ( callback ) ; } ; this . gotoThread = function ( thread ) { var that = this ; that . next_thread = thread ; that . processing = true ; var makeChange = function ( ) { if ( ! that . hasThread ( that . next_thread ) ) { if ( that . next_thread == 'default' ) { that . threads [ that . next_thread ] = [ ] ; } else { botkit . logger . debug ( 'WARN: gotoThread() to an invalid thread!' , thread ) ; that . stop ( 'unknown_thread' ) ; return ; } } that . thread = that . next_thread ; that . messages = that . threads [ that . next_thread ] . slice ( ) ; that . handler = null ; that . processing = false ; } ; if ( that . before_hooks && that . before_hooks [ that . next_thread ] ) { // call any beforeThread hooks in sequence async . eachSeries ( this . before_hooks [ that . next_thread ] , function ( before_hook , next ) { before_hook ( that , next ) ; } , function ( err ) { if ( ! err ) { makeChange ( ) ; } } ) ; } else { makeChange ( ) ; } } ; this . combineMessages = function ( messages ) { if ( ! messages ) { return '' ; } if ( Array . isArray ( messages ) && ! messages . length ) { return '' ; } if ( messages . length > 1 ) { var txt = [ ] ; var last_user = null ; var multi_users = false ; last_user = messages [ 0 ] . user ; for ( let x = 0 ; x < messages . length ; x ++ ) { if ( messages [ x ] . user != last_user ) { multi_users = true ; } } last_user = '' ; for ( let x = 0 ; x < messages . length ; x ++ ) { if ( multi_users && messages [ x ] . user != last_user ) { last_user = messages [ x ] . user ; if ( txt . length ) { txt . push ( '' ) ; } txt . push ( '<@' + messages [ x ] . user + '>:' ) ; } txt . push ( messages [ x ] . text ) ; } return txt . join ( '\n' ) ; } else { if ( messages . length ) { return messages [ 0 ] . text ; } else { return messages . text ; } } } ; this . getResponses = function ( ) { var res = { } ; for ( var key in this . responses ) { res [ key ] = { question : this . responses [ key ] . length ? this . responses [ key ] [ 0 ] . question : this . responses [ key ] . question , key : key , answer : this . extractResponse ( key ) , } ; } return res ; } ; this . getResponsesAsArray = function ( ) { var res = [ ] ; for ( var key in this . responses ) { res . push ( { question : this . responses [ key ] . length ? this . responses [ key ] [ 0 ] . question : this . responses [ key ] . question , key : key , answer : this . extractResponse ( key ) , } ) ; } return res ; } ; this . extractResponses = function ( ) { var res = { } ; for ( var key in this . responses ) { res [ key ] = this . extractResponse ( key ) ; } return res ; } ; this . extractResponse = function ( key ) { return this . combineMessages ( this . responses [ key ] ) ; } ; this . replaceAttachmentTokens = function ( attachments ) { if ( attachments && attachments . length ) { for ( let a = 0 ; a < attachments . length ; a ++ ) { for ( let key in attachments [ a ] ) { if ( typeof ( attachments [ a ] [ key ] ) == 'string' ) { attachments [ a ] [ key ] = this . replaceTokens ( attachments [ a ] [ key ] ) ; } else { attachments [ a ] [ key ] = this . replaceAttachmentTokens ( attachments [ a ] [ key ] ) ; } } } } else { for ( let a in attachments ) { if ( typeof ( attachments [ a ] ) == 'string' ) { attachments [ a ] = this . replaceTokens ( attachments [ a ] ) ; } else { attachments [ a ] = this . replaceAttachmentTokens ( attachments [ a ] ) ; } } } return attachments ; } ; this . replaceTokens = function ( text ) { var vars = { identity : this . task . bot . identity , responses : this . extractResponses ( ) , origin : this . task . source_message , vars : this . vars , } ; var rendered = '' ; try { rendered = mustache . render ( text , vars ) ; } catch ( err ) { botkit . logger . error ( 'Error in message template. Mustache failed with error: ' , err ) ; rendered = text ; } return rendered ; } ; this . stop = function ( status ) { this . handler = null ; this . messages = [ ] ; this . status = status || 'stopped' ; botkit . logger . debug ( 'Conversation is over with status ' + this . status ) ; this . task . conversationEnded ( this ) ; } ; // was this conversation successful? // return true if it was completed // otherwise, return false // false could indicate a variety of failed states: // manually stopped, timed out, etc this . successful = function ( ) { // if the conversation is still going, it can't be successful yet if ( this . isActive ( ) ) { return false ; } if ( this . status == 'completed' ) { return true ; } else { return false ; } } ; this . cloneMessage = function ( message ) { // clone this object so as not to modify source var outbound = clone ( message ) ; if ( typeof ( message . text ) == 'string' ) { outbound . text = this . replaceTokens ( message . text ) ; } else if ( message . text ) { outbound . text = this . replaceTokens ( message . text [ Math . floor ( Math . random ( ) * message . text . length ) ] ) ; } if ( outbound . attachments ) { outbound . attachments = this . replaceAttachmentTokens ( outbound . attachments ) ; } if ( outbound . attachment ) { // pick one variation of the message text at random if ( outbound . attachment . payload . text && typeof ( outbound . attachment . payload . text ) != 'string' ) { outbound . attachment . payload . text = this . replaceTokens ( outbound . attachment . payload . text [ Math . floor ( Math . random ( ) * outbound . attachment . payload . text . length ) ] ) ; } outbound . attachment = this . replaceAttachmentTokens ( [ outbound . attachment ] ) [ 0 ] ; } if ( this . messages . length && ! message . handler ) { outbound . continue_typing = true ; } if ( typeof ( message . attachments ) == 'function' ) { outbound . attachments = message . attachments ( this ) ; } return outbound ; } ; this . onTimeout = function ( handler ) { if ( typeof ( handler ) == 'function' ) { this . timeOutHandler = handler ; } else { botkit . logger . debug ( 'Invalid timeout function passed to onTimeout' ) ; } } ; this . tick = function ( ) { var now = new Date ( ) ; if ( this . isActive ( ) ) { if ( this . processing ) { // do nothing. The bot is waiting for async process to complete. } else if ( this . handler ) { // check timeout! // how long since task started? var duration = ( now . getTime ( ) - this . task . startTime . getTime ( ) ) ; // how long since last active? var lastActive = ( now . getTime ( ) - this . lastActive . getTime ( ) ) ; if ( this . task . timeLimit && // has a timelimit ( duration > this . task . timeLimit ) && // timelimit is up ( lastActive > this . task . timeLimit ) // nobody has typed for 60 seconds at least ) { // if timeoutHandler is set then call it, otherwise follow the normal flow // this will not break others code, after the update if ( this . timeOutHandler ) { this . timeOutHandler ( this ) ; } else if ( this . hasThread ( 'on_timeout' ) ) { this . status = 'ending' ; this . gotoThread ( 'on_timeout' ) ; } else { this . stop ( 'timeout' ) ; } } // otherwise do nothing } else { if ( this . messages . length ) { if ( this . sent . length && ! this . sent [ this . sent . length - 1 ] . sent ) { return ; } if ( this . task . bot . botkit . config . require_delivery && this . sent . length && ! this . sent [ this . sent . length - 1 ] . delivered ) { return ; } if ( typeof ( this . messages [ 0 ] . timestamp ) == 'undefined' || this . messages [ 0 ] . timestamp <= now . getTime ( ) ) { var message = this . messages . shift ( ) ; //console.log('HANDLING NEW MESSAGE',message); // make sure next message is delayed appropriately if ( this . messages . length && this . messages [ 0 ] . delay ) { this . messages [ 0 ] . timestamp = now . getTime ( ) + this . messages [ 0 ] . delay ; } if ( message . handler ) { //console.log(">>>>>> SET HANDLER IN TICK"); this . handler = message . handler ; } else { this . handler = null ; //console.log(">>>>>>> CLEARING HANDLER BECAUSE NO HANDLER NEEDED"); } if ( message . capture_options ) { this . capture_options = message . capture_options ; } else { this . capture_options = { } ; } this . lastActive = new Date ( ) ; // is there any text? // or an attachment? (facebook) // or multiple attachments (slack) // if (message.text || message.attachments || message.attachment) { if ( message ) { var outbound = this . cloneMessage ( message ) ; var that = this ; outbound . sent_timestamp = new Date ( ) . getTime ( ) ; that . sent . push ( outbound ) ; that . transcript . push ( outbound ) ; this . task . bot . reply ( this . source_message , outbound , function ( err , sent_message ) { if ( err ) { botkit . logger . error ( 'An error occurred while sending a message: ' , err ) ; // even though an error occured, set sent to true // this will allow the conversation to keep going even if one message fails // TODO: make a message that fails to send _resend_ at least once that . sent [ that . sent . length - 1 ] . sent = true ; that . sent [ that . sent . length - 1 ] . api_response = err ; } else { that . sent [ that . sent . length - 1 ] . sent = true ; that . sent [ that . sent . length - 1 ] . api_response = sent_message ; // if sending via slack's web api, there is no further confirmation // so we can mark the message delivered if ( that . task . bot . type == 'slack' && sent_message && sent_message . ts ) { that . sent [ that . sent . length - 1 ] . delivered = true ; } that . trigger ( 'sent' , [ sent_message ] ) ; } } ) ; } if ( message . action ) { if ( typeof ( message . action ) == 'function' ) { message . action ( this ) ; } else if ( message . action == 'repeat' ) { this . repeat ( ) ; } else if ( message . action == 'wait' ) { this . silentRepeat ( ) ; } else if ( message . action == 'stop' ) { this . stop ( ) ; } else if ( message . action == 'timeout' ) { this . stop ( 'timeout' ) ; } else if ( this . threads [ message . action ] ) { this . gotoThread ( message . action ) ; } } } else { //console.log('Waiting to send next message...'); } // end immediately instad of waiting til next tick. // if it hasn't already been ended by a message action! if ( this . isActive ( ) && ! this . messages . length && ! this . handler && ! this . processing ) { this . stop ( 'completed' ) ; } } else if ( this . sent . length ) { // sent at least 1 message this . stop ( 'completed' ) ; } } } } ; botkit . logger . debug ( 'CREATED A CONVO FOR' , this . source_message . user , this . source_message . channel ) ; this . gotoThread ( 'default' ) ; } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ function Task ( bot , message , botkit ) { this . convos = [ ] ; this . botkit = botkit ; this . bot = bot ; this . events = { } ; this . source_message = message ; this . status = 'active' ; this . startTime = new Date ( ) ; this . isActive = function ( ) { return this . status == 'active' ; } ; this . createConversation = function ( message ) { var convo = new Conversation ( this , message ) ; convo . id = botkit . convoCount ++ ; this . convos . push ( convo ) ; return convo ; } ; this . startConversation = function ( message ) { var convo = this . createConversation ( message ) ; botkit . logger . debug ( '>   [Start] ' , convo . id , ' Conversation with ' , message . user , 'in' , message . channel ) ; convo . activate ( ) ; return convo ; } ; this . conversationEnded = function ( convo ) { botkit . logger . debug ( '>   [End] ' , convo . id , ' Conversation with ' , convo . source_message . user , 'in' , convo . source_message . channel ) ; this . trigger ( 'conversationEnded' , [ convo ] ) ; this . botkit . trigger ( 'conversationEnded' , [ bot , convo ] ) ; convo . trigger ( 'end' , [ convo ] ) ; var actives = 0 ; for ( var c = 0 ; c < this . convos . length ; c ++ ) { if ( this . convos [ c ] . isActive ( ) ) { actives ++ ; } } if ( actives == 0 ) { this . taskEnded ( ) ; } } ; this . endImmediately = function ( reason ) { for ( var c = 0 ; c < this . convos . length ; c ++ ) { if ( this . convos [ c ] . isActive ( ) ) { this . convos [ c ] . stop ( reason || 'stopped' ) ; } } } ; this . taskEnded = function ( ) { botkit . logger . debug ( '[End] ' , this . id , ' Task for ' , this . source_message . user , 'in' , this . source_message . channel ) ; this . status = 'completed' ; this . trigger ( 'end' , [ this ] ) ; } ; this . on = function ( event , cb ) { botkit . logger . debug ( 'Setting up a handler for' , event ) ; var events = event . split ( / \, / g ) ; for ( var e in events ) { if ( ! this . events [ events [ e ] ] ) { this . events [ events [ e ] ] = [ ] ; } this . events [ events [ e ] ] . push ( cb ) ; } return this ; } ; this . trigger = function ( event , data ) { if ( this . events [ event ] ) { for ( var e = 0 ; e < this . events [ event ] . length ; e ++ ) { var res = this . events [ event ] [ e ] . apply ( this , data ) ; if ( res === false ) { return ; } } } } ; this . getResponsesByUser = function ( ) { var users = { } ; // go through all conversations // extract normalized answers for ( var c = 0 ; c < this . convos . length ; c ++ ) { var user = this . convos [ c ] . source_message . user ; users [ this . convos [ c ] . source_message . user ] = { } ; var convo = this . convos [ c ] ; users [ user ] = convo . extractResponses ( ) ; } return users ; } ; this . getResponsesBySubject = function ( ) { var answers = { } ; // go through all conversations // extract normalized answers for ( var c = 0 ; c < this . convos . length ; c ++ ) { var convo = this . convos [ c ] ; for ( var key in convo . responses ) { if ( ! answers [ key ] ) { answers [ key ] = { } ; } answers [ key ] [ convo . source_message . user ] = convo . extractResponse ( key ) ; } } return answers ; } ; this . tick = function ( ) { for ( var c = 0 ; c < this . convos . length ; c ++ ) { if ( this . convos [ c ] . isActive ( ) ) { this . convos [ c ] . tick ( ) ; } } } ; } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ botkit . storage = { teams : { get : function ( team_id , cb ) { cb ( null , botkit . memory_store . teams [ team_id ] ) ; } , save : function ( team , cb ) { botkit . logger . warn ( 'Warning: using temporary storage. Data will be lost when process restarts.' ) ; if ( team . id ) { botkit . memory_store . teams [ team . id ] = team ; cb ( null , team . id ) ; } else { cb ( 'No ID specified' ) ; } } , all : function ( cb ) { cb ( null , botkit . memory_store . teams ) ; } } , users : { get : function ( user_id , cb ) { cb ( null , botkit . memory_store . users [ user_id ] ) ; } , save : function ( user , cb ) { botkit . logger . warn ( 'Warning: using temporary storage. Data will be lost when process restarts.' ) ; if ( user . id ) { botkit . memory_store . users [ user . id ] = user ; cb ( null , user . id ) ; } else { cb ( 'No ID specified' ) ; } } , all : function ( cb ) { cb ( null , botkit . memory_store . users ) ; } } , channels : { get : function ( channel_id , cb ) { cb ( null , botkit . memory_store . channels [ channel_id ] ) ; } , save : function ( channel , cb ) { botkit . logger . warn ( 'Warning: using temporary storage. Data will be lost when process restarts.' ) ; if ( channel . id ) { botkit . memory_store . channels [ channel . id ] = channel ; cb ( null , channel . id ) ; } else { cb ( 'No ID specified' ) ; } } , all : function ( cb ) { cb ( null , botkit . memory_store . channels ) ; } } } ; /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ /**
   * hears_regexp - default string matcher uses regular expressions
   *
   * @param  {array}  tests    patterns to match
   * @param  {object} message message object with various fields
   * @return {boolean}        whether or not a pattern was matched
   */ botkit . hears_regexp = function ( tests , message ) { for ( var t = 0 ; t < tests . length ; t ++ ) { if ( message . text ) { // the pattern might be a string to match (including regular expression syntax) // or it might be a prebuilt regular expression var test = null ; if ( typeof ( tests [ t ] ) == 'string' ) { try { test = new RegExp ( tests [ t ] , 'i' ) ; } catch ( err ) { botkit . logger . error ( 'Error in regular expression: ' + tests [ t ] + ': ' + err ) ; return false ; } if ( ! test ) { return false ; } } else { test = tests [ t ] ; } let match = message . text . match ( test ) ; if ( match ) { message . match = match ; return true ; } } } return false ; } ; /**
   * changeEars - change the default matching function
   *
   * @param  {function} new_test a function that accepts (tests, message) and returns a boolean
   */ botkit . changeEars = function ( new_test ) { botkit . hears_test = new_test ; } ; botkit . hears = function ( keywords , events , middleware_or_cb , cb ) { // the third parameter is EITHER a callback handler // or a middleware function that redefines how the hear works var test_function = botkit . hears_test ; if ( cb ) { test_function = middleware_or_cb ; } else { cb = middleware_or_cb ; } if ( typeof ( keywords ) == 'string' ) { keywords = [ keywords ] ; } if ( keywords instanceof RegExp ) { keywords = [ keywords ] ; } if ( typeof ( events ) == 'string' ) { events = events . split ( / \, / g ) . map ( function ( str ) { return str . trim ( ) ; } ) ; } for ( var e = 0 ; e < events . length ; e ++ ) { ( function ( keywords , test_function ) { botkit . on ( events [ e ] , function ( bot , message ) { if ( test_function && test_function ( keywords , message ) ) { botkit . logger . debug ( 'I HEARD' , keywords ) ; botkit . middleware . heard . run ( bot , message , function ( err , bot , message ) { cb . apply ( this , [ bot , message ] ) ; botkit . trigger ( 'heard_trigger' , [ bot , keywords , message ] ) ; } ) ; return false ; } } , true ) ; } ) ( keywords , test_function ) ; } return this ; } ; botkit . on = function ( event , cb , is_hearing ) { botkit . logger . debug ( 'Setting up a handler for' , event ) ; var events = ( typeof ( event ) == 'string' ) ? event . split ( / \, / g ) : event ; for ( var e in events ) { if ( ! this . events [ events [ e ] ] ) { this . events [ events [ e ] ] = [ ] ; } this . events [ events [ e ] ] . push ( { callback : cb , type : is_hearing ? 'hearing' : 'event' } ) ; } return this ; } ; botkit . trigger = function ( event , data ) { if ( this . events [ event ] ) { var hearing = this . events [ event ] . filter ( function ( e ) { return ( e . type == 'hearing' ) ; } ) ; var handlers = this . events [ event ] . filter ( function ( e ) { return ( e . type != 'hearing' ) ; } ) ; // first, look for hearing type events // these are always handled before normal event handlers for ( var e = 0 ; e < hearing . length ; e ++ ) { var res = hearing [ e ] . callback . apply ( this , data ) ; if ( res === false ) { return ; } } // now, if we haven't already heard something, // fire the remaining event handlers if ( handlers . length ) { botkit . middleware . triggered . run ( data [ 0 ] , data [ 1 ] , function ( err , bot , message ) { for ( var e = 0 ; e < handlers . length ; e ++ ) { var res = handlers [ e ] . callback . apply ( this , data ) ; if ( res === false ) { return ; } } } ) ; } } } ; botkit . startConversation = function ( bot , message , cb ) { botkit . startTask ( bot , message , function ( task , convo ) { cb ( null , convo ) ; } ) ; } ; botkit . createConversation = function ( bot , message , cb ) { var task = new Task ( bot , message , this ) ; task . id = botkit . taskCount ++ ; var convo = task . createConversation ( message ) ; this . tasks . push ( task ) ; cb ( null , convo ) ; } ; botkit . defineBot = function ( unit ) { if ( typeof ( unit ) != 'function' ) { throw new Error ( 'Bot definition must be a constructor function' ) ; } this . worker = unit ; } ; botkit . spawn = function ( config , cb ) { var worker = new this . worker ( this , config ) ; // mutate the worker so that we can call middleware worker . say = function ( message , cb ) { var platform_message = { } ; botkit . middleware . send . run ( worker , message , function ( err , worker , message ) { if ( err ) { botkit . logger . error ( 'An error occured in the send middleware:: ' + err ) ; } else { botkit . middleware . format . run ( worker , message , platform_message , function ( err , worker , message , platform_message ) { if ( err ) { botkit . logger . error ( 'An error occured in the format middleware: ' + err ) ; } else { worker . send ( platform_message , cb ) ; } } ) ; } } ) ; } ; // add platform independent convenience methods worker . startConversation = function ( message , cb ) { botkit . startConversation ( worker , message , cb ) ; } ; worker . createConversation = function ( message , cb ) { botkit . createConversation ( worker , message , cb ) ; } ; botkit . middleware . spawn . run ( worker , function ( err , worker ) { if ( err ) { botkit . logger . error ( 'Error in middlware.spawn.run: ' + err ) ; } else { botkit . trigger ( 'spawned' , [ worker ] ) ; if ( cb ) { cb ( worker ) ; } } } ) ; return worker ; } ; botkit . startTicking = function ( ) { if ( ! botkit . tickInterval ) { // set up a once a second tick to process messages botkit . tickInterval = setInterval ( function ( ) { botkit . tick ( ) ; } , 1500 ) ; } } ; botkit . shutdown = function ( ) { if ( botkit . tickInterval ) { clearInterval ( botkit . tickInterval ) ; } } ; botkit . startTask = function ( bot , message , cb ) { var task = new Task ( bot , message , this ) ; task . id = botkit . taskCount ++ ; botkit . logger . debug ( '[Start] ' , task . id , ' Task for ' , message . user , 'in' , message . channel ) ; var convo = task . startConversation ( message ) ; this . tasks . push ( task ) ; if ( cb ) { cb ( task , convo ) ; } else { return task ; } } ; botkit . tick = function ( ) { for ( let t = 0 ; t < botkit . tasks . length ; t ++ ) { botkit . tasks [ t ] . tick ( ) ; } for ( let t = botkit . tasks . length - 1 ; t >= 0 ; t -- ) { if ( ! botkit . tasks [ t ] . isActive ( ) ) { botkit . tasks . splice ( t , 1 ) ; } } this . trigger ( 'tick' , [ ] ) ; } ; // Provide a fairly simple Express-based webserver botkit . setupWebserver = function ( port , cb ) { if ( ! port ) { throw new Error ( 'Cannot start webserver without a port' ) ; } var static_dir = process . cwd ( ) + '/public' ; if ( botkit . config && botkit . config . webserver && botkit . config . webserver . static_dir ) static_dir = botkit . config . webserver . static_dir ; botkit . config . port = port ; var express = require ( 'express' ) ; var bodyParser = require ( 'body-parser' ) ; botkit . webserver = express ( ) ; botkit . webserver . use ( bodyParser . json ( ) ) ; botkit . webserver . use ( bodyParser . urlencoded ( { extended : true } ) ) ; botkit . webserver . use ( express . static ( static_dir ) ) ; var server = botkit . webserver . listen ( botkit . config . port , botkit . config . hostname , function ( ) { botkit . logger . info ( '** Starting webserver on port ' + botkit . config . port ) ; if ( cb ) { cb ( null , botkit . webserver ) ; } botkit . trigger ( 'webserver_up' , [ botkit . webserver ] ) ; } ) ; return botkit ; } ; /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ /**
   * Define a default worker bot. This function should be customized outside
   * of Botkit and passed in as a parameter by the developer
   **/ botkit . worker = function ( botkit , config ) { this . botkit = botkit ; this . config = config ; this . say = function ( message , cb ) { botkit . logger . debug ( 'SAY:' , message ) ; } ; this . replyWithQuestion = function ( message , question , cb ) { botkit . startConversation ( message , function ( convo ) { convo . ask ( question , cb ) ; } ) ; } ; this . reply = function ( src , resp ) { botkit . logger . debug ( 'REPLY:' , resp ) ; } ; this . findConversation = function ( message , cb ) { botkit . logger . debug ( 'DEFAULT FIND CONVO' ) ; cb ( null ) ; } ; } ; /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ botkit . userAgent = function ( ) { if ( ! botkit . my_user_agent ) { // set user agent to Botkit var ua = 'Botkit/' + botkit . version ( ) ; // add OS info ua = ua + ' ' + os . platform ( ) + '/' + os . release ( ) ; // add Node info ua = ua + ' ' + 'node/' + process . version . replace ( 'v' , '' ) ; botkit . my_user_agent = ua ; } return botkit . my_user_agent ; } ; botkit . version = function ( ) { if ( ! botkit . my_version ) { botkit . my_version = '0.0.0' ; } return botkit . my_version ; } ; botkit . config = configuration ; /** Default the application to listen to the 0.0.0.0, the default
   * for node's http module. Developers can specify a hostname or IP
   * address to override this.
   **/ if ( ! botkit . config . hostname ) { botkit . config . hostname = '0.0.0.0' ; } if ( ! configuration . logLevel ) { if ( configuration . debug ) { configuration . logLevel = 'debug' ; } else if ( configuration . log === false ) { configuration . logLevel = 'error' ; } else { configuration . logLevel = 'info' ; } } if ( configuration . logger ) { botkit . logger = configuration . logger ; } else { botkit . logger = logging ( 'abbott-framework:botkit:CoreBot' ) ; } // botkit.log = function () { //   botkit.logger.info.apply(botkit.log, arguments); // }; // Object.keys(LogLevels).forEach(function (level) { //   botkit.log[level] = botkit.logger.log.bind(botkit.logger, level); // }); // botkit.debug = botkit.logger.debug; // if (!botkit.config.disable_startup_messages) { //   console.log('Initializing Botkit v' + botkit.version()); // } if ( configuration . storage ) { if ( configuration . storage . teams && configuration . storage . teams . get && configuration . storage . teams . save && configuration . storage . users && configuration . storage . users . get && configuration . storage . users . save && configuration . storage . channels && configuration . storage . channels . get && configuration . storage . channels . save ) { botkit . logger . debug ( '** Using custom storage system.' ) ; botkit . storage = configuration . storage ; } else { throw new Error ( 'Storage object does not have all required methods!' ) ; } // } else if (configuration.json_file_store) { //   botkit.logger.debug('** Using simple storage. Saving data to ' + configuration.json_file_store); //   botkit.storage = simple_storage({ //     path: configuration.json_file_store //   }); } else { botkit . logger . debug ( '** No persistent storage method specified! Data may be lost when process shuts down.' ) ; } // set the default set of ears to use the regular expression matching botkit . changeEars ( botkit . hears_regexp ) ; //enable Botkit Studio // studio(botkit); return botkit ; }


Input:
/ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [CODESPLIT] function Conversation ( task , message ) { this . messages = [ ] ; this . sent = [ ] ; this . transcript = [ ] ; this . context = { user : message . user , channel : message . channel , bot : task . bot , } ; this . events = { } ; this . vars = { } ; this . threads = { } ; this . thread = null ; this . status = 'new' ; this . task = task ; this . source_message = message ; this . handler = null ; this . responses = { } ; this . capture_options = { } ; this . startTime = new Date ( ) ; this . lastActive = new Date ( ) ; /** will be pointing to a callback which will be called after timeout,
     * conversation will be not be ended and should be taken care by callback
     */ this . timeOutHandler = null ; this . collectResponse = function ( key , value ) { this . responses [ key ] = value ; } ; this . capture = function ( response , cb ) { var that = this ; var capture_key = this . sent [ this . sent . length - 1 ] . text ; botkit . middleware . capture . run ( that . task . bot , response , that , function ( err , bot , response , convo ) { if ( response . text ) { response . text = response . text . trim ( ) ; } else { response . text = '' ; } if ( that . capture_options . key != undefined ) { capture_key = that . capture_options . key ; } // capture the question that was asked // if text is an array, get 1st if ( typeof ( that . sent [ that . sent . length - 1 ] . text ) == 'string' ) { response . question = that . sent [ that . sent . length - 1 ] . text ; } else if ( Array . isArray ( that . sent [ that . sent . length - 1 ] . text ) ) { response . question = that . sent [ that . sent . length - 1 ] . text [ 0 ] ; } else { response . question = '' ; } if ( that . capture_options . multiple ) { if ( ! that . responses [ capture_key ] ) { that . responses [ capture_key ] = [ ] ; } that . responses [ capture_key ] . push ( response ) ; } else { that . responses [ capture_key ] = response ; } if ( cb ) cb ( response ) ; } ) ; } ; this . handle = function ( message ) { var that = this ; this . lastActive = new Date ( ) ; this . transcript . push ( message ) ; botkit . logger . debug ( 'HANDLING MESSAGE IN CONVO' , message ) ; // do other stuff like call custom callbacks if ( this . handler ) { this . capture ( message , function ( message ) { // if the handler is a normal function, just execute it! // NOTE: anyone who passes in their own handler has to call // convo.next() to continue after completing whatever it is they want to do. if ( typeof ( that . handler ) == 'function' ) { that . handler ( message , that ) ; } else { // handle might be a mapping of keyword to callback. // lets see if the message matches any of the keywords let match , patterns = that . handler ; for ( let p = 0 ; p < patterns . length ; p ++ ) { if ( patterns [ p ] . pattern && botkit . hears_test ( [ patterns [ p ] . pattern ] , message ) ) { botkit . middleware . heard . run ( that . task . bot , message , function ( err , bot , message ) { patterns [ p ] . callback ( message , that ) ; } ) ; return ; } } // none of the messages matched! What do we do? // if a default exists, fire it! for ( let p = 0 ; p < patterns . length ; p ++ ) { if ( patterns [ p ] . default ) { botkit . middleware . heard . run ( that . task . bot , message , function ( err , bot , message ) { patterns [ p ] . callback ( message , that ) ; } ) ; return ; } } } } ) ; } else { // do nothing } } ; this . setVar = function ( field , value ) { if ( ! this . vars ) { this . vars = { } ; } this . vars [ field ] = value ; } ; this . activate = function ( ) { this . task . trigger ( 'conversationStarted' , [ this ] ) ; this . task . botkit . trigger ( 'conversationStarted' , [ this . task . bot , this ] ) ; this . status = 'active' ; } ; /**
     * active includes both ACTIVE and ENDING
     * in order to allow the timeout end scripts to play out
     **/ this . isActive = function ( ) { return ( this . status == 'active' || this . status == 'ending' ) ; } ; this . deactivate = function ( ) { this . status = 'inactive' ; } ; this . say = function ( message ) { this . addMessage ( message ) ; } ; this . sayFirst = function ( message ) { if ( typeof ( message ) == 'string' ) { message = { text : message , channel : this . source_message . channel , } ; } else { message . channel = this . source_message . channel ; } this . messages . unshift ( message ) ; } ; this . on = function ( event , cb ) { botkit . logger . debug ( 'Setting up a handler for' , event ) ; var events = event . split ( / \, / g ) ; for ( var e in events ) { if ( ! this . events [ events [ e ] ] ) { this . events [ events [ e ] ] = [ ] ; } this . events [ events [ e ] ] . push ( cb ) ; } return this ; } ; this . trigger = function ( event , data ) { if ( this . events [ event ] ) { for ( var e = 0 ; e < this . events [ event ] . length ; e ++ ) { var res = this . events [ event ] [ e ] . apply ( this , data ) ; if ( res === false ) { return ; } } } else { } } ; // proceed to the next message after waiting for an answer this . next = function ( ) { this . handler = null ; } ; this . repeat = function ( ) { if ( this . sent . length ) { this . messages . push ( this . sent [ this . sent . length - 1 ] ) ; } else { // console.log('TRIED TO REPEAT, NOTHING TO SAY'); } } ; this . silentRepeat = function ( ) { return ; } ; this . addQuestion = function ( message , cb , capture_options , thread ) { if ( typeof ( message ) == 'string' ) { message = { text : message , channel : this . source_message . channel } ; } else { message . channel = this . source_message . channel ; } if ( capture_options ) { message . capture_options = capture_options ; } message . handler = cb ; this . addMessage ( message , thread ) ; } ; this . ask = function ( message , cb , capture_options ) { this . addQuestion ( message , cb , capture_options , this . thread || 'default' ) ; } ; this . addMessage = function ( message , thread ) { if ( ! thread ) { thread = this . thread ; } if ( typeof ( message ) == 'string' ) { message = { text : message , channel : this . source_message . channel , } ; } else { message . channel = this . source_message . channel ; } if ( ! this . threads [ thread ] ) { this . threads [ thread ] = [ ] ; } this . threads [ thread ] . push ( message ) ; // this is the current topic, so add it here as well if ( this . thread == thread ) { this . messages . push ( message ) ; } } ; // how long should the bot wait while a user answers? this . setTimeout = function ( timeout ) { this . task . timeLimit = timeout ; } ; // For backwards compatibility, wrap gotoThread in its previous name this . changeTopic = function ( topic ) { this . gotoThread ( topic ) ; } ; this . hasThread = function ( thread ) { return ( this . threads [ thread ] != undefined ) ; } ; this . transitionTo = function ( thread , message ) { // add a new transition thread // add this new message to it // set that message action to execute the actual transition // then change threads to transition thread var num = 1 ; while ( this . hasThread ( 'transition_' + num ) ) { num ++ ; } var threadname = 'transition_' + num ; if ( typeof ( message ) == 'string' ) { message = { text : message , action : thread } ; } else { message . action = thread ; } this . addMessage ( message , threadname ) ; this . gotoThread ( threadname ) ; } ; this . beforeThread = function ( thread , callback ) { if ( ! this . before_hooks ) { this . before_hooks = { } ; } if ( ! this . before_hooks [ thread ] ) { this . before_hooks [ thread ] = [ ] ; } this . before_hooks [ thread ] . push ( callback ) ; } ; this . gotoThread = function ( thread ) { var that = this ; that . next_thread = thread ; that . processing = true ; var makeChange = function ( ) { if ( ! that . hasThread ( that . next_thread ) ) { if ( that . next_thread == 'default' ) { that . threads [ that . next_thread ] = [ ] ; } else { botkit . logger . debug ( 'WARN: gotoThread() to an invalid thread!' , thread ) ; that . stop ( 'unknown_thread' ) ; return ; } } that . thread = that . next_thread ; that . messages = that . threads [ that . next_thread ] . slice ( ) ; that . handler = null ; that . processing = false ; } ; if ( that . before_hooks && that . before_hooks [ that . next_thread ] ) { // call any beforeThread hooks in sequence async . eachSeries ( this . before_hooks [ that . next_thread ] , function ( before_hook , next ) { before_hook ( that , next ) ; } , function ( err ) { if ( ! err ) { makeChange ( ) ; } } ) ; } else { makeChange ( ) ; } } ; this . combineMessages = function ( messages ) { if ( ! messages ) { return '' ; } if ( Array . isArray ( messages ) && ! messages . length ) { return '' ; } if ( messages . length > 1 ) { var txt = [ ] ; var last_user = null ; var multi_users = false ; last_user = messages [ 0 ] . user ; for ( let x = 0 ; x < messages . length ; x ++ ) { if ( messages [ x ] . user != last_user ) { multi_users = true ; } } last_user = '' ; for ( let x = 0 ; x < messages . length ; x ++ ) { if ( multi_users && messages [ x ] . user != last_user ) { last_user = messages [ x ] . user ; if ( txt . length ) { txt . push ( '' ) ; } txt . push ( '<@' + messages [ x ] . user + '>:' ) ; } txt . push ( messages [ x ] . text ) ; } return txt . join ( '\n' ) ; } else { if ( messages . length ) { return messages [ 0 ] . text ; } else { return messages . text ; } } } ; this . getResponses = function ( ) { var res = { } ; for ( var key in this . responses ) { res [ key ] = { question : this . responses [ key ] . length ? this . responses [ key ] [ 0 ] . question : this . responses [ key ] . question , key : key , answer : this . extractResponse ( key ) , } ; } return res ; } ; this . getResponsesAsArray = function ( ) { var res = [ ] ; for ( var key in this . responses ) { res . push ( { question : this . responses [ key ] . length ? this . responses [ key ] [ 0 ] . question : this . responses [ key ] . question , key : key , answer : this . extractResponse ( key ) , } ) ; } return res ; } ; this . extractResponses = function ( ) { var res = { } ; for ( var key in this . responses ) { res [ key ] = this . extractResponse ( key ) ; } return res ; } ; this . extractResponse = function ( key ) { return this . combineMessages ( this . responses [ key ] ) ; } ; this . replaceAttachmentTokens = function ( attachments ) { if ( attachments && attachments . length ) { for ( let a = 0 ; a < attachments . length ; a ++ ) { for ( let key in attachments [ a ] ) { if ( typeof ( attachments [ a ] [ key ] ) == 'string' ) { attachments [ a ] [ key ] = this . replaceTokens ( attachments [ a ] [ key ] ) ; } else { attachments [ a ] [ key ] = this . replaceAttachmentTokens ( attachments [ a ] [ key ] ) ; } } } } else { for ( let a in attachments ) { if ( typeof ( attachments [ a ] ) == 'string' ) { attachments [ a ] = this . replaceTokens ( attachments [ a ] ) ; } else { attachments [ a ] = this . replaceAttachmentTokens ( attachments [ a ] ) ; } } } return attachments ; } ; this . replaceTokens = function ( text ) { var vars = { identity : this . task . bot . identity , responses : this . extractResponses ( ) , origin : this . task . source_message , vars : this . vars , } ; var rendered = '' ; try { rendered = mustache . render ( text , vars ) ; } catch ( err ) { botkit . logger . error ( 'Error in message template. Mustache failed with error: ' , err ) ; rendered = text ; } return rendered ; } ; this . stop = function ( status ) { this . handler = null ; this . messages = [ ] ; this . status = status || 'stopped' ; botkit . logger . debug ( 'Conversation is over with status ' + this . status ) ; this . task . conversationEnded ( this ) ; } ; // was this conversation successful? // return true if it was completed // otherwise, return false // false could indicate a variety of failed states: // manually stopped, timed out, etc this . successful = function ( ) { // if the conversation is still going, it can't be successful yet if ( this . isActive ( ) ) { return false ; } if ( this . status == 'completed' ) { return true ; } else { return false ; } } ; this . cloneMessage = function ( message ) { // clone this object so as not to modify source var outbound = clone ( message ) ; if ( typeof ( message . text ) == 'string' ) { outbound . text = this . replaceTokens ( message . text ) ; } else if ( message . text ) { outbound . text = this . replaceTokens ( message . text [ Math . floor ( Math . random ( ) * message . text . length ) ] ) ; } if ( outbound . attachments ) { outbound . attachments = this . replaceAttachmentTokens ( outbound . attachments ) ; } if ( outbound . attachment ) { // pick one variation of the message text at random if ( outbound . attachment . payload . text && typeof ( outbound . attachment . payload . text ) != 'string' ) { outbound . attachment . payload . text = this . replaceTokens ( outbound . attachment . payload . text [ Math . floor ( Math . random ( ) * outbound . attachment . payload . text . length ) ] ) ; } outbound . attachment = this . replaceAttachmentTokens ( [ outbound . attachment ] ) [ 0 ] ; } if ( this . messages . length && ! message . handler ) { outbound . continue_typing = true ; } if ( typeof ( message . attachments ) == 'function' ) { outbound . attachments = message . attachments ( this ) ; } return outbound ; } ; this . onTimeout = function ( handler ) { if ( typeof ( handler ) == 'function' ) { this . timeOutHandler = handler ; } else { botkit . logger . debug ( 'Invalid timeout function passed to onTimeout' ) ; } } ; this . tick = function ( ) { var now = new Date ( ) ; if ( this . isActive ( ) ) { if ( this . processing ) { // do nothing. The bot is waiting for async process to complete. } else if ( this . handler ) { // check timeout! // how long since task started? var duration = ( now . getTime ( ) - this . task . startTime . getTime ( ) ) ; // how long since last active? var lastActive = ( now . getTime ( ) - this . lastActive . getTime ( ) ) ; if ( this . task . timeLimit && // has a timelimit ( duration > this . task . timeLimit ) && // timelimit is up ( lastActive > this . task . timeLimit ) // nobody has typed for 60 seconds at least ) { // if timeoutHandler is set then call it, otherwise follow the normal flow // this will not break others code, after the update if ( this . timeOutHandler ) { this . timeOutHandler ( this ) ; } else if ( this . hasThread ( 'on_timeout' ) ) { this . status = 'ending' ; this . gotoThread ( 'on_timeout' ) ; } else { this . stop ( 'timeout' ) ; } } // otherwise do nothing } else { if ( this . messages . length ) { if ( this . sent . length && ! this . sent [ this . sent . length - 1 ] . sent ) { return ; } if ( this . task . bot . botkit . config . require_delivery && this . sent . length && ! this . sent [ this . sent . length - 1 ] . delivered ) { return ; } if ( typeof ( this . messages [ 0 ] . timestamp ) == 'undefined' || this . messages [ 0 ] . timestamp <= now . getTime ( ) ) { var message = this . messages . shift ( ) ; //console.log('HANDLING NEW MESSAGE',message); // make sure next message is delayed appropriately if ( this . messages . length && this . messages [ 0 ] . delay ) { this . messages [ 0 ] . timestamp = now . getTime ( ) + this . messages [ 0 ] . delay ; } if ( message . handler ) { //console.log(">>>>>> SET HANDLER IN TICK"); this . handler = message . handler ; } else { this . handler = null ; //console.log(">>>>>>> CLEARING HANDLER BECAUSE NO HANDLER NEEDED"); } if ( message . capture_options ) { this . capture_options = message . capture_options ; } else { this . capture_options = { } ; } this . lastActive = new Date ( ) ; // is there any text? // or an attachment? (facebook) // or multiple attachments (slack) // if (message.text || message.attachments || message.attachment) { if ( message ) { var outbound = this . cloneMessage ( message ) ; var that = this ; outbound . sent_timestamp = new Date ( ) . getTime ( ) ; that . sent . push ( outbound ) ; that . transcript . push ( outbound ) ; this . task . bot . reply ( this . source_message , outbound , function ( err , sent_message ) { if ( err ) { botkit . logger . error ( 'An error occurred while sending a message: ' , err ) ; // even though an error occured, set sent to true // this will allow the conversation to keep going even if one message fails // TODO: make a message that fails to send _resend_ at least once that . sent [ that . sent . length - 1 ] . sent = true ; that . sent [ that . sent . length - 1 ] . api_response = err ; } else { that . sent [ that . sent . length - 1 ] . sent = true ; that . sent [ that . sent . length - 1 ] . api_response = sent_message ; // if sending via slack's web api, there is no further confirmation // so we can mark the message delivered if ( that . task . bot . type == 'slack' && sent_message && sent_message . ts ) { that . sent [ that . sent . length - 1 ] . delivered = true ; } that . trigger ( 'sent' , [ sent_message ] ) ; } } ) ; } if ( message . action ) { if ( typeof ( message . action ) == 'function' ) { message . action ( this ) ; } else if ( message . action == 'repeat' ) { this . repeat ( ) ; } else if ( message . action == 'wait' ) { this . silentRepeat ( ) ; } else if ( message . action == 'stop' ) { this . stop ( ) ; } else if ( message . action == 'timeout' ) { this . stop ( 'timeout' ) ; } else if ( this . threads [ message . action ] ) { this . gotoThread ( message . action ) ; } } } else { //console.log('Waiting to send next message...'); } // end immediately instad of waiting til next tick. // if it hasn't already been ended by a message action! if ( this . isActive ( ) && ! this . messages . length && ! this . handler && ! this . processing ) { this . stop ( 'completed' ) ; } } else if ( this . sent . length ) { // sent at least 1 message this . stop ( 'completed' ) ; } } } } ; botkit . logger . debug ( 'CREATED A CONVO FOR' , this . source_message . user , this . source_message . channel ) ; this . gotoThread ( 'default' ) ; }


Input: / * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [CODESPLIT] function Task ( bot , message , botkit ) { this . convos = [ ] ; this . botkit = botkit ; this . bot = bot ; this . events = { } ; this . source_message = message ; this . status = 'active' ; this . startTime = new Date ( ) ; this . isActive = function ( ) { return this . status == 'active' ; } ; this . createConversation = function ( message ) { var convo = new Conversation ( this , message ) ; convo . id = botkit . convoCount ++ ; this . convos . push ( convo ) ; return convo ; } ; this . startConversation = function ( message ) { var convo = this . createConversation ( message ) ; botkit . logger . debug ( '>   [Start] ' , convo . id , ' Conversation with ' , message . user , 'in' , message . channel ) ; convo . activate ( ) ; return convo ; } ; this . conversationEnded = function ( convo ) { botkit . logger . debug ( '>   [End] ' , convo . id , ' Conversation with ' , convo . source_message . user , 'in' , convo . source_message . channel ) ; this . trigger ( 'conversationEnded' , [ convo ] ) ; this . botkit . trigger ( 'conversationEnded' , [ bot , convo ] ) ; convo . trigger ( 'end' , [ convo ] ) ; var actives = 0 ; for ( var c = 0 ; c < this . convos . length ; c ++ ) { if ( this . convos [ c ] . isActive ( ) ) { actives ++ ; } } if ( actives == 0 ) { this . taskEnded ( ) ; } } ; this . endImmediately = function ( reason ) { for ( var c = 0 ; c < this . convos . length ; c ++ ) { if ( this . convos [ c ] . isActive ( ) ) { this . convos [ c ] . stop ( reason || 'stopped' ) ; } } } ; this . taskEnded = function ( ) { botkit . logger . debug ( '[End] ' , this . id , ' Task for ' , this . source_message . user , 'in' , this . source_message . channel ) ; this . status = 'completed' ; this . trigger ( 'end' , [ this ] ) ; } ; this . on = function ( event , cb ) { botkit . logger . debug ( 'Setting up a handler for' , event ) ; var events = event . split ( / \, / g ) ; for ( var e in events ) { if ( ! this . events [ events [ e ] ] ) { this . events [ events [ e ] ] = [ ] ; } this . events [ events [ e ] ] . push ( cb ) ; } return this ; } ; this . trigger = function ( event , data ) { if ( this . events [ event ] ) { for ( var e = 0 ; e < this . events [ event ] . length ; e ++ ) { var res = this . events [ event ] [ e ] . apply ( this , data ) ; if ( res === false ) { return ; } } } } ; this . getResponsesByUser = function ( ) { var users = { } ; // go through all conversations // extract normalized answers for ( var c = 0 ; c < this . convos . length ; c ++ ) { var user = this . convos [ c ] . source_message . user ; users [ this . convos [ c ] . source_message . user ] = { } ; var convo = this . convos [ c ] ; users [ user ] = convo . extractResponses ( ) ; } return users ; } ; this . getResponsesBySubject = function ( ) { var answers = { } ; // go through all conversations // extract normalized answers for ( var c = 0 ; c < this . convos . length ; c ++ ) { var convo = this . convos [ c ] ; for ( var key in convo . responses ) { if ( ! answers [ key ] ) { answers [ key ] = { } ; } answers [ key ] [ convo . source_message . user ] = convo . extractResponse ( key ) ; } } return answers ; } ; this . tick = function ( ) { for ( var c = 0 ; c < this . convos . length ; c ++ ) { if ( this . convos [ c ] . isActive ( ) ) { this . convos [ c ] . tick ( ) ; } } } ; }


Input: Verifies the SHA1 signature of the raw request payload before bodyParser parses it Will abort parsing if signature is invalid and pass a generic error to response [CODESPLIT] function verifyRequest ( req , res , buf , encoding ) { var expected = req . headers [ 'x-hub-signature' ] ; var calculated = getSignature ( buf ) ; if ( expected !== calculated ) { throw new Error ( 'Invalid signature on incoming request' ) ; } else { // facebook_botkit.logger.debug('** X-Hub Verification successful!') } }


Input: Route [CODESPLIT] function Route ( pattern , Handler , observe , router ) { this . pattern = pattern ; this . map = parsePattern ( pattern ) ; this . regExp = patternToRegExp ( pattern ) ; this . strictRegExp = patternToStrictRegExp ( pattern ) ; this . isComponent = ! ! Handler . extend ; this . Handler = Handler ; this . observe = assign ( { qs : [ ] , hash : [ ] , state : [ ] } , observe ) ; this . allObserved = this . observe . qs . concat ( this . observe . hash , this . observe . state ) ; this . router = router || { } ; this . view = null ; }


Input: 获取某个cookie [CODESPLIT] function getCookie ( cookies , name ) { var match = String ( cookies ) . match ( new RegExp ( name + '=([^;]+)' ) ) ; if ( match ) { return match [ 1 ] ; } return false ; }


Input: Append each option from the given <code > fromOptions< / code > to the given <code > options< / code > array flattening them to pass them later as parameters to a sub call process . [CODESPLIT] function appendOptions ( options , fromOptions ) { function pushOption ( optionName , optionValue ) { var valueDefined = optionValue !== null && optionValue !== undefined ; if ( optionName . length === 1 ) { // simple letter option options . push ( '-' + optionName ) ; if ( valueDefined ) { options . push ( optionValue ) ; } } else { // full option name options . push ( '--' + optionName + ( valueDefined ? '=' + optionValue : '' ) ) ; } } for ( var optionName in fromOptions ) { if ( fromOptions . hasOwnProperty ( optionName ) && optionName !== '_' ) { var optionValue = fromOptions [ optionName ] ; if ( Array . isArray ( optionValue ) ) { // we have multiple values for the same option, let's iterate on each optionValue . forEach ( function ( iOptionValue ) { pushOption ( optionName , iOptionValue ) ; } ) ; } else { pushOption ( optionName , optionValue ) ; } } } // now append the "_" which are not "options" but args if ( fromOptions && fromOptions . _ ) { [ ] . concat ( fromOptions . _ ) . forEach ( function ( arg ) { options . push ( arg ) ; } ) ; } }


Input: **************************************************************************** Performs the given request on the KISSmetrics tracker host . [CODESPLIT] function request_kissmetrics_client ( pathname , query_params , callback ) { var query_string = [ '_k=' , this . key , '&' ] . join ( '' ) , uri , v ; query_params [ '_t' ] = ( query_params [ '_d' ] ? query_params [ '_t' ] : Date . now ( ) ) ; for ( var k in query_params ) { v = query_params [ k ] ; if ( query_string !== '' ) { query_string += '&' ; } query_string += [ k , v ] . join ( '=' ) ; } uri = encodeURI ( [ 'http' , ':' , '//' , this . host , ':' , this . port , pathname , '?' , query_string ] . join ( '' ) ) ; // should encode the = sign in the user id var regex = new RegExp ( "&_p=" + query_params [ "_p" ] + "&" ) ; var uri = uri . replace ( regex , "&_p=" + query_params [ "_p" ] . replace ( "=" , "%3D" ) + "&" ) ; request ( { uri : uri } , function ( err , res ) { if ( err ) { console . log ( 'xxx KISSmetrics error' ) ; console . log ( '    uri    : ' + uri ) ; console . log ( '    error  : ' + err . message ) ; } else if ( res . statusCode !== 200 ) { err = new Error ( 'KISSmetrics error ---> RECEIVED WRONG STATUS CODE [' + res . statusCode + ']' ) ; } callback ( err , res ) ; } ) ; }


Input: **************************************************************************** Sets properties on a person without recording an event by making a request . [CODESPLIT] function set_kissmetrics_client ( person , properties , callback ) { var query_params = new Object ( properties ) ; query_params [ '_p' ] = person ; this . request ( '/s' , query_params , callback ) ; }


Input: **************************************************************************** Aliases the user identified by person with aliases . [CODESPLIT] function alias_kissmetrics_client ( person , aliases , callback ) { var aliases = ( Array . isArray ( aliases ) ? aliases : [ aliases ] ) , that = this ; async . forEach ( aliases , function ( alias , callback ) { var query_params = { } ; query_params [ '_p' ] = person ; query_params [ '_n' ] = alias ; that . request ( '/a' , query_params , callback ) ; } , callback ) ; }


Input: **************************************************************************** Records event for person . Also sets properties on the person if specified . [CODESPLIT] function record_kissmetrics_client ( ) { var args = Array . prototype . slice . call ( arguments ) , callback = args . pop ( ) , person = args . shift ( ) , event = args . shift ( ) , properties = args . shift ( ) || { } , query_params = new Object ( properties ) ; query_params [ '_p' ] = person ; query_params [ '_n' ] = event ; this . request ( '/e' , query_params , callback ) ; }


Input: **************************************************************************** KISSmetrics REST client constructor . [CODESPLIT] function kissmetrics_client ( options ) { var options = options || { } ; this . host = options . host || DEFAULT_TRACKER_SERVER ; this . port = options . port || DEFAULT_TRACKER_PORT ; this . key = options . key ; }


Input: TODO split into two functions? i . e . one for the top - level call one for the cascade [CODESPLIT] function notifyPatternObservers ( ractive , registeredKeypath , actualKeypath , isParentOfChangedKeypath , isTopLevelCall ) { var i , patternObserver , children , child , key , childActualKeypath , potentialWildcardMatches , cascade ; // First, observers that match patterns at the same level // or higher in the tree i = ractive . _patternObservers . length ; while ( i -- ) { patternObserver = ractive . _patternObservers [ i ] ; if ( patternObserver . regex . test ( actualKeypath ) ) { patternObserver . update ( actualKeypath ) ; } } if ( isParentOfChangedKeypath ) { return ; } // If the changed keypath is 'foo.bar', we need to see if there are // any pattern observer dependants of keypaths below any of // 'foo.bar', 'foo.*', '*.bar' or '*.*' (e.g. 'foo.bar.*' or 'foo.*.baz' ) cascade = function ( keypath ) { if ( children = ractive . _depsMap [ keypath ] ) { i = children . length ; while ( i -- ) { child = children [ i ] ; // foo.*.baz key = lastKey . exec ( child ) [ 0 ] ; // 'baz' childActualKeypath = actualKeypath ? actualKeypath + '.' + key : key ; // 'foo.bar.baz' notifyPatternObservers ( ractive , child , childActualKeypath ) ; } } } ; if ( isTopLevelCall ) { potentialWildcardMatches = getPotentialWildcardMatches ( actualKeypath ) ; potentialWildcardMatches . forEach ( cascade ) ; } else { cascade ( registeredKeypath ) ; } }


Input: This function returns all the possible true / false combinations for a given number - e . g . for two the possible combinations are [ true true ] [ true false ] [ false true ] [ false false ] . It does so by getting all the binary values between 0 and e . g . 11 [CODESPLIT] function getStarMap ( num ) { var ones = '' , max , binary , starMap , mapper , i ; if ( ! starMaps [ num ] ) { starMap = [ ] ; while ( ones . length < num ) { ones += 1 ; } max = parseInt ( ones , 2 ) ; mapper = function ( digit ) { return digit === '1' ; } ; for ( i = 0 ; i <= max ; i += 1 ) { binary = i . toString ( 2 ) ; while ( binary . length < num ) { binary = '0' + binary ; } starMap [ i ] = Array . prototype . map . call ( binary , mapper ) ; } starMaps [ num ] = starMap ; } return starMaps [ num ] ; }


Input: TODO optimise this [CODESPLIT] function ( keypath , root ) { var i = queue . length , animation ; while ( i -- ) { animation = queue [ i ] ; if ( animation . root === root && animation . keypath === keypath ) { animation . stop ( ) ; } } }


Input: This method forces the evaluator to sync with the current model in the case of a smart update [CODESPLIT] function ( ) { if ( ! this . selfUpdating ) { this . deferred = true ; } var i = this . refs . length ; while ( i -- ) { this . refs [ i ] . update ( ) ; } if ( this . deferred ) { this . update ( ) ; this . deferred = false ; } }


Input: TODO teardown when element torn down [CODESPLIT] function ( ) { var i ; if ( this . custom ) { this . custom . teardown ( ) ; } else { this . node . removeEventListener ( this . name , genericHandler , false ) ; } i = this . proxies . length ; while ( i -- ) { this . proxies [ i ] . teardown ( ) ; } }


Input: TODO what happens if a transition is aborted? TODO use this with Animation to dedupe some code? [CODESPLIT] function ( options ) { var easing ; this . duration = options . duration ; this . step = options . step ; this . complete = options . complete ; // easing if ( typeof options . easing === 'string' ) { easing = options . root . easing [ options . easing ] ; if ( ! easing ) { warn ( 'Missing easing function ("' + options . easing + '"). You may need to download a plugin from [TODO]' ) ; easing = linear ; } } else if ( typeof options . easing === 'function' ) { easing = options . easing ; } else { easing = linear ; } this . easing = easing ; this . start = getTime ( ) ; this . end = this . start + this . duration ; this . running = true ; animations . add ( this ) ; }


Input: TODO maybe refactor this? [CODESPLIT] function getRefs ( token , refs ) { var i , list ; if ( token . t === types . REFERENCE ) { if ( refs . indexOf ( token . n ) === - 1 ) { refs . unshift ( token . n ) ; } } list = token . o || token . m ; if ( list ) { if ( isObject ( list ) ) { getRefs ( list , refs ) ; } else { i = list . length ; while ( i -- ) { getRefs ( list [ i ] , refs ) ; } } } if ( token . x ) { getRefs ( token . x , refs ) ; } if ( token . r ) { getRefs ( token . r , refs ) ; } if ( token . v ) { getRefs ( token . v , refs ) ; } }


Input: Assign [CODESPLIT] function assign ( object , source ) { for ( var i = 1 , c = arguments . length ; i < c ; i ++ ) { for ( var x in arguments [ i ] ) { if ( arguments [ i ] . hasOwnProperty ( x ) && arguments [ i ] [ x ] !== undefined ) { object [ x ] = arguments [ i ] [ x ] ; } } } return object ; }


Input: Join paths [CODESPLIT] function joinPaths ( parts ) { return Array . prototype . slice . call ( arguments ) . join ( '/' ) . replace ( / \/+ / g , '/' ) ; }


Input: Parents [CODESPLIT] function parents ( el , name ) { while ( el && el . nodeName . toLowerCase ( ) !== name ) { el = el . parentNode ; } return el && el . nodeName . toLowerCase ( ) === name ? el : null ; }


Input: Parse hash [CODESPLIT] function parseHash ( hash , keys ) { try { var parsed = compact ( JSON . parse ( decodeURIComponent ( hash . substr ( 2 ) ) ) ) ; return keys ? pick ( parsed , keys ) : parsed ; } catch ( e ) { return { } ; } }


Input: Parse URI [CODESPLIT] function parseUri ( uri ) { var parts = uri . match ( / ^(?:([\w+.-]+):\/\/([^/]+))?([^?#]*)?(\?[^#]*)?(#.*)? / ) ; return { protocol : parts [ 1 ] || '' , host : parts [ 2 ] || '' , path : parts [ 3 ] || '' , qs : parts [ 4 ] || '' , hash : parts [ 5 ] || '' } ; }


Input: Parse QS [CODESPLIT] function parseQS ( qs , keys ) { var index = qs . indexOf ( '?' ) ; var parsed = { } ; if ( index !== - 1 ) { var pairs = qs . substr ( index + 1 ) . split ( '&' ) ; var pair = [ ] ; for ( var i = 0 , c = pairs . length ; i < c ; i ++ ) { pair = pairs [ i ] . split ( '=' ) ; if ( ( ! isEmpty ( pair [ 1 ] ) ) && ( ! isEmpty ( parseJSON ( pair [ 1 ] ) ) ) ) { parsed [ decodeForm ( decodeURIComponent ( pair [ 0 ] ) ) ] = parseJSON ( decodeForm ( decodeURIComponent ( pair [ 1 ] ) ) ) ; } } } return keys ? pick ( parsed , keys ) : parsed ; }


Input: Pick [CODESPLIT] function pick ( object , keys ) { var data = { } ; if ( typeof keys === 'function' ) { for ( var x in object ) { if ( object . hasOwnProperty ( x ) && keys ( object [ x ] , x ) ) { data [ x ] = object [ x ] ; } } } else { for ( var i = 0 , c = keys . length ; i < c ; i ++ ) { data [ keys [ i ] ] = object [ keys [ i ] ] ; } } return data ; }


Input: Scroll to [CODESPLIT] function scrollTo ( id ) { var el = document . getElementById ( id ) ; if ( el ) { window . scrollBy ( 0 , el . getBoundingClientRect ( ) . top ) ; } else { window . scrollTo ( 0 , 0 ) ; } }


Input: Stringify hash [CODESPLIT] function stringifyHash ( data ) { data = compact ( data ) ; return ! isEmpty ( data ) ? '#!' + encodeURIComponent ( stringify ( data ) ) : '' ; }


Input: Stringify QS [CODESPLIT] function stringifyQS ( data ) { var qs = '' ; for ( var x in data ) { if ( data . hasOwnProperty ( x ) && ! isEmpty ( data [ x ] ) ) { qs += '&' + encodeURIComponent ( x ) + '=' + encodeURIComponent ( stringify ( data [ x ] ) ) ; } } return qs ? '?' + qs . substr ( 1 ) : '' ; }


Input: Router [CODESPLIT] function Router ( options ) { this . globals = options . globals || [ ] ; this . basePath = options . basePath || '' ; this . el = options . el ; this . data = options . data || function ( ) { return { } ; } ; this . history = options . history || history ; this . strictMode = ! ! options . strictMode ; this . reloadOnClick = options . reloadOnClick ; this . linksWatcher = null ; this . stateWatcher = null ; this . route = null ; this . routes = [ ] ; this . uri = { } ; }


Input: Should dispatch [CODESPLIT] function shouldDispatch ( oldUri , newUri , route ) { return oldUri . path !== newUri . path || oldUri . qs !== newUri . qs || ( decodeURIComponent ( oldUri . hash ) !== decodeURIComponent ( newUri . hash ) && ( ! route || route . observe . hash . length ) ) ; }


Input: This class manages the DockerCmd and handles dockerdesc . json [CODESPLIT] function DockerCmdManager ( dockerdescPath ) { dockerdescPath = dockerdescPath || './dockerdesc.json' ; if ( ! fs . existsSync ( dockerdescPath ) ) { throw new Error ( util . format ( 'The path "%s" does not exists.' , dockerdescPath ) ) ; } /** @type {string} */ this . dockerdescDir = path . dirname ( dockerdescPath ) ; var dockerdescPathStat = fs . statSync ( dockerdescPath ) ; if ( dockerdescPathStat . isDirectory ( ) ) { this . dockerdescDir = dockerdescPath ; dockerdescPath = path . join ( dockerdescPath , 'dockerdesc.json' ) ; } /** @type {Dockerdesc} */ var dockerdescContent = fs . readFileSync ( dockerdescPath ) ; try { this . dockerdesc = JSON . parse ( dockerdescContent ) ; } catch ( err ) { throw new Error ( 'Problem in the dockerdesc.json file format.\n' + err . stack ) ; } }