### The MIT License (MIT) Copyright (c) 2013-2016, Reactive Sets equals( a, b ) Returns true if a and b are deeply equal, false otherwise. Parameters: - a (Any type): value to compare to b - b (Any type): value compared to a Implementation: 'a' is considered equal to 'b' if all scalar values in a and b are strictly equal as compared with operator '==' except for these two special cases: - 0 == -0 but are not equal. - NaN is not == to itself but is equal. RegExp objects are considered equal if they have the same lastIndex, i.e. both regular expressions have matched the same number of times. Functions must be identical, so that they have the same closure context. "undefined" is a valid value, including in Objects ### equals = ( a, b ) -> return a == b && # strict equality should be enough unless zero a != 0 || # because 0 == -0, requires test by _equals() _equals( a, b ) # handles not strictly equal or zero values _equals = ( a, b ) -> # a and b have already failed test for strict equality or are zero # They should have the same toString() signature if ( ( s = toString.call( a ) ) != toString.call( b ) ) return false switch s when '[object Number]' # Converts Number instances into primitive values # This is required also for NaN test bellow a = +a b = +b if a return a == b else if a == a # a is 0 or -O return 1/a == 1/b # 1/0 != 1/-0 because Infinity != -Infinity return b != b # NaN, the only Number not equal to itself! when '[object RegExp]' return a.source == b.source && a.global == b.global && a.ignoreCase == b.ignoreCase && a.multiline == b.multiline && a.lastIndex == b.lastIndex when '[object Function]' return false # functions should be strictly equal because of closure context when '[object Array]' if ( ( l = a.length ) != b.length ) return false # Both have as many elements while ( l-- ) if ( ( x = a[ l ] ) == ( y = b[ l ] ) && x != 0 || _equals( x, y ) ) continue return false return true when '[object Object]' l = 0 # counter of own properties for p in a if ( a.hasOwnProperty( p ) ) ++l if ( ( x = a[ p ] ) == ( y = b[ p ] ) && x != 0 || _equals( x, y ) ) continue return false # Check if 'b' has as not more own properties than 'a' for p in b if ( b.hasOwnProperty( p ) && --l < 0 ) return false return true else # Boolean, Date, String return a.valueOf() == b.valueOf() partial = (func) -> boundArgs = arguments #slice.call(arguments, 1) bound = () -> position = 0 length = boundArgs.length args = Array(length) for i in [0..length] args[i] = boundArgs[i] while (position < arguments.length) args.push(arguments[position++]); return executeBound(func, bound, this, this, args) return bound TAG_COMPARISON = TOTAL: 1 PARTIAL: 0 MISMATCH: -1 compareTags = (a, b) -> # Returns a TAG_COMPARISON value if (equals(a, b)) return TAG_COMPARISON.TOTAL # If the tags are unequal but have the same length, it stands to reason # there is a mismatch. if (a.length == b.length) return TAG_COMPARISON.MISMATCH if a < b [shorter, longer] = [a, b] else [shorter, longer] = [b, a] for x, index in shorter if x != longer[index] return TAG_COMPARISON.MISMATCH return TAG_COMPARISON.PARTIAL # Ensures that the group and model don't have any mismatched tags mismatchFilterSub = (group, model) -> if not group.tags? return 0 result = group.tags.find((groupTag) -> # Look for a mismatch. matched = model.tags.find (modelTag) => modelTag[0] == groupTag[0] if not matched? return false if compareTags(groupTag, matched) == TAG_COMPARISON.MISMATCH return true; return false ) if not result? return 0 return null bonusCompare = (mode, bonus = 1, cumulative = false) -> return (group, model) -> results = group.tags.filter((groupTag) -> matched = model.tags.find((modelTag) => modelTag[0] == groupTag[0] ) if not matched? return false if compareTags(groupTag, matched) == mode return true return false ) if (results.length) if cumulative return bonus * results.length else return bonus return 0 filters = {} filters.mismatchFilter = () -> return mismatchFilterSub filters.partialBonus = partial(bonusCompare, TAG_COMPARISON.PARTIAL) filters.fullBonus = partial(bonusCompare, TAG_COMPARISON.TOTAL) filters.dryness = () -> return (group) -> that = this newPhrases = [] for phrase in group.phrases if (that.history.indexOf(phrase) == -1 && phrase != null) newPhrases.push(phrase) newGroup = Object.create(group) newGroup.phrases = newPhrases return [0, newGroup] filters.unmentioned = (bonus = 1) -> return (group) -> if (!Array.isArray(group.tags)) return 0 if (group.tags.length == 0) return 0 that = this for t in group.tags # Return true if the tag is "novel". for u in that.tagHistory if u[0] == t[0] return bonus return 0 Improv.filters = filters