2014-01-03 13:32:13 -05:00
module.exports.clone = (obj) ->
2014-06-30 22:16:26 -04:00
return obj if obj is null or typeof ( obj ) isnt ' object '
2014-01-03 13:32:13 -05:00
temp = obj . constructor ( )
for key of obj
temp [ key ] = module . exports . clone ( obj [ key ] )
temp
module.exports.combineAncestralObject = (obj, propertyName) ->
combined = { }
while obj ? [ propertyName ]
for key , value of obj [ propertyName ]
continue if combined [ key ]
combined [ key ] = value
if obj . __proto__
obj = obj . __proto__
else
# IE has no __proto__. TODO: does this even work? At most it doesn't crash.
obj = Object . getPrototypeOf ( obj )
combined
module.exports.normalizeFunc = (func_thing, object) ->
# func could be a string to a function in this class
# or a function in its own right
object ? = { }
if _ . isString ( func_thing )
func = object [ func_thing ]
if not func
2014-08-14 13:28:50 -04:00
console . error " Could not find method #{ func_thing } in object " , object
2014-01-03 13:32:13 -05:00
return => null # always return a func, or Mediator will go boom
func_thing = func
2014-03-13 11:39:53 -04:00
return func_thing
2016-02-06 17:02:44 -05:00
module.exports.objectIdToDate = (objectID) ->
new Date ( parseInt ( objectID . toString ( ) . slice ( 0 , 8 ) , 16 ) * 1000 )
2014-01-10 19:48:28 -05:00
module.exports.hexToHSL = (hex) ->
rgbToHsl ( hexToR ( hex ) , hexToG ( hex ) , hexToB ( hex ) )
hexToR = (h) -> parseInt ( cutHex ( h ) ) . substring ( 0 , 2 ) , 16
hexToG = (h) -> parseInt ( cutHex ( h ) ) . substring ( 2 , 4 ) , 16
hexToB = (h) -> parseInt ( cutHex ( h ) ) . substring ( 4 , 6 ) , 16
2014-06-30 22:16:26 -04:00
cutHex = (h) -> ( if ( h . charAt ( 0 ) is ' # ' ) then h . substring ( 1 , 7 ) else h )
2014-03-13 11:39:53 -04:00
2014-01-10 19:48:28 -05:00
module.exports.hslToHex = (hsl) ->
' # ' + ( toHex ( n ) for n in hslToRgb ( hsl . . . ) ) . join ( ' ' )
2014-03-13 11:39:53 -04:00
2014-01-10 19:48:28 -05:00
toHex = (n) ->
h = Math . floor ( n ) . toString ( 16 )
h = ' 0 ' + h if h . length is 1
2014-03-12 15:30:43 -04:00
h
2014-08-23 18:51:59 -04:00
module.exports.i18n = ( say , target , language = me . get ( ' preferredLanguage ' , true ) , fallback = ' en ' ) ->
2014-03-12 15:30:43 -04:00
generalResult = null
2016-03-07 11:13:49 -05:00
fallBackResult = null
fallForwardResult = null # If a general language isn't available, the first specific one will do.
fallSidewaysResult = null # If a specific language isn't available, its sibling specific language will do.
2014-03-12 15:30:43 -04:00
matches = ( /\w+/gi ) . exec ( language )
generalName = matches [ 0 ] if matches
2014-03-12 19:47:08 -04:00
for localeName , locale of say . i18n
2014-07-13 13:05:58 -04:00
continue if localeName is ' - '
2014-03-12 19:47:08 -04:00
if target of locale
result = locale [ target ]
2014-03-12 15:30:43 -04:00
else continue
2014-07-04 10:23:47 -04:00
return result if localeName is language
generalResult = result if localeName is generalName
2016-03-07 11:13:49 -05:00
fallBackResult = result if localeName is fallback
fallForwardResult = result if localeName . indexOf ( language ) is 0 and not fallForwardResult ?
fallSidewaysResult = result if localeName . indexOf ( generalName ) is 0 and not fallSidewaysResult ?
2014-03-12 15:30:43 -04:00
return generalResult if generalResult ?
2016-03-07 11:13:49 -05:00
return fallForwardResult if fallForwardResult ?
return fallSidewaysResult if fallSidewaysResult ?
return fallBackResult if fallBackResult ?
2014-03-12 20:07:36 -04:00
return say [ target ] if target of say
2014-03-13 11:39:53 -04:00
null
2014-05-18 15:14:22 -04:00
module.exports.getByPath = (target, path) ->
2014-06-14 14:12:17 -04:00
throw new Error ' Expected an object to match a query against, instead got null ' unless target
2014-05-18 15:14:22 -04:00
pieces = path . split ( ' . ' )
obj = target
for piece in pieces
return undefined unless piece of obj
obj = obj [ piece ]
obj
2014-06-03 06:40:47 -04:00
2014-07-09 14:23:05 -04:00
module.exports.isID = (id) -> _ . isString ( id ) and id . length is 24 and id . match ( /[a-f0-9]/gi ) ? . length is 24
2014-06-03 06:40:47 -04:00
module.exports.round = _ . curry (digits, n) ->
n = + n . toFixed ( digits )
2014-06-14 14:12:17 -04:00
positify = (func) -> (params) -> (x) -> if x > 0 then func ( params ) ( x ) else 0
2014-06-12 13:39:45 -04:00
# f(x) = ax + b
createLinearFunc = (params) ->
2014-06-03 06:40:47 -04:00
(x) -> ( params . a or 1 ) * x + ( params . b or 0 )
2014-06-12 13:39:45 -04:00
# f(x) = ax² + bx + c
createQuadraticFunc = (params) ->
(x) -> ( params . a or 1 ) * x * x + ( params . b or 1 ) * x + ( params . c or 0 )
# f(x) = a log(b (x + c)) + d
createLogFunc = (params) ->
(x) -> if x > 0 then ( params . a or 1 ) * Math . log ( ( params . b or 1 ) * ( x + ( params . c or 0 ) ) ) + ( params . d or 0 ) else 0
2015-02-18 12:06:32 -05:00
# f(x) = ax^b + c
createPowFunc = (params) ->
(x) -> ( params . a or 1 ) * Math . pow ( x , params . b or 1 ) + ( params . c or 0 )
2014-06-12 13:39:45 -04:00
module.exports.functionCreators =
linear: positify ( createLinearFunc )
quadratic: positify ( createQuadraticFunc )
logarithmic: positify ( createLogFunc )
2015-02-18 12:06:32 -05:00
pow: positify ( createPowFunc )
2014-06-17 15:42:27 -04:00
# Call done with true to satisfy the 'until' goal and stop repeating func
module.exports.keepDoingUntil = (func, wait=100, totalWait=5000) ->
waitSoFar = 0
( done = (success) ->
2014-07-07 13:56:30 -04:00
if ( waitSoFar += wait ) <= totalWait and not success
2014-06-17 15:42:27 -04:00
_ . delay ( -> func done ) , wait ) false
2014-08-04 09:26:21 -04:00
module.exports.grayscale = (imageData) ->
d = imageData . data
for i in [ 0 . . d . length ] by 4
r = d [ i ]
g = d [ i + 1 ]
b = d [ i + 2 ]
v = 0.2126 * r + 0.7152 * g + 0.0722 * b
d [ i ] = d [ i + 1 ] = d [ i + 2 ] = v
imageData
2014-08-08 07:08:13 -04:00
# Deep compares l with r, with the exception that undefined values are considered equal to missing values
# Very practical for comparing Mongoose documents where undefined is not allowed, instead fields get deleted
module.exports.kindaEqual = compare = (l, r) ->
if _ . isObject ( l ) and _ . isObject ( r )
for key in _ . union Object . keys ( l ) , Object . keys ( r )
return false unless compare l [ key ] , r [ key ]
return true
else if l is r
return true
else
return false
2014-11-29 16:09:38 -05:00
2015-01-14 14:09:01 -05:00
# Return UTC string "YYYYMMDD" for today + offset
2015-01-07 17:49:21 -05:00
module.exports.getUTCDay = (offset=0) ->
day = new Date ( )
day . setDate ( day . getUTCDate ( ) + offset )
partYear = day . getUTCFullYear ( )
partMonth = ( day . getUTCMonth ( ) + 1 )
partMonth = " 0 " + partMonth if partMonth < 10
partDay = day . getUTCDate ( )
partDay = " 0 " + partDay if partDay < 10
2015-01-14 14:09:01 -05:00
" #{ partYear } #{ partMonth } #{ partDay } "
2015-01-07 17:49:21 -05:00
2014-11-29 16:09:38 -05:00
# Fast, basic way to replace text in an element when you don't need much.
# http://stackoverflow.com/a/4962398/540620
2015-02-12 19:06:27 -05:00
if document ? . createElement
2014-11-29 16:09:38 -05:00
dummy = document . createElement ' div '
dummy.innerHTML = ' text '
TEXT = if dummy . textContent is ' text ' then ' textContent ' else ' innerText '
module.exports.replaceText = (elems, text) ->
elem [ TEXT ] = text for elem in elems
null
2014-12-28 16:25:20 -05:00
# Add a stylesheet rule
# http://stackoverflow.com/questions/524696/how-to-create-a-style-tag-with-javascript/26230472#26230472
# Don't use wantonly, or we'll have to implement a simple mechanism for clearing out old rules.
2015-02-12 19:06:27 -05:00
if document ? . createElement
2014-12-28 16:25:20 -05:00
module.exports.injectCSS = ( (doc) ->
# wrapper for all injected styles and temp el to create them
wrap = doc . createElement ( " div " )
temp = doc . createElement ( " div " )
# rules like "a {color: red}" etc.
return (cssRules) ->
# append wrapper to the body on the first call
unless wrap . id
wrap.id = " injected-css "
wrap.style.display = " none "
doc . body . appendChild wrap
# <br> for IE: http://goo.gl/vLY4x7
temp.innerHTML = " <br><style> " + cssRules + " </style> "
wrap . appendChild temp . children [ 1 ]
return
) ( document )
2015-03-03 12:04:53 -05:00
module.exports.getQueryVariable = getQueryVariable = (param, defaultValue) ->
query = document . location . search . substring 1
pairs = ( pair . split ( ' = ' ) for pair in query . split ' & ' )
for pair in pairs when pair [ 0 ] is param
return { ' true ' : true , ' false ' : false } [ pair [ 1 ] ] ? decodeURIComponent ( pair [ 1 ] )
defaultValue
2015-03-13 18:19:20 -04:00
module.exports.getSponsoredSubsAmount = getSponsoredSubsAmount = (price=999, subCount=0, personalSub=false) ->
# 1 100%
# 2-11 80%
# 12+ 60%
# TODO: make this less confusing
return 0 unless subCount > 0
offset = if personalSub then 1 else 0
if subCount <= 1 - offset
price
else if subCount <= 11 - offset
Math . round ( ( 1 - offset ) * price + ( subCount - 1 + offset ) * price * 0.8 )
else
Math . round ( ( 1 - offset ) * price + 10 * price * 0.8 + ( subCount - 11 + offset ) * price * 0.6 )
2015-09-03 14:04:40 -04:00
2015-10-01 18:23:20 -04:00
module.exports.getCourseBundlePrice = getCourseBundlePrice = (coursePrices, seats=20) ->
totalPricePerSeat = coursePrices . reduce ( (a, b) -> a + b ) , 0
if coursePrices . length > 2
2015-09-03 14:04:40 -04:00
pricePerSeat = Math . round ( totalPricePerSeat / 2.0 )
else
pricePerSeat = parseInt ( totalPricePerSeat )
seats * pricePerSeat
2015-09-10 13:37:32 -04:00
module.exports.getCoursePraise = getCoursePraise = ->
praise = [
{
quote: " The kids love it. "
source: " Leo Joseph Tran, Athlos Leadership Academy "
} ,
{
quote: " My students have been using the site for a couple of weeks and they love it. "
source: " Scott Hatfield, Computer Applications Teacher, School Technology Coordinator, Eastside Middle School "
} ,
{
quote: " Thanks for the captivating site. My eighth graders love it. "
source: " Janet Cook, Ansbach Middle/High School "
} ,
{
quote: " My students have started working on CodeCombat and love it! I love that they are learning coding and problem solving skills without them even knowing it!! "
source: " Kristin Huff, Special Education Teacher, Webb City School District "
} ,
{
quote: " I recently introduced Code Combat to a few of my fifth graders and they are loving it! "
source: " Shauna Hamman, Fifth Grade Teacher, Four Peaks Elementary School "
} ,
{
quote: " Overall I think it ' s a fantastic service. Variables, arrays, loops, all covered in very fun and imaginative ways. Every kid who has tried it is a fan. "
source: " Aibinder Andrew, Technology Teacher "
} ,
{
quote: " I love what you have created. The kids are so engaged. "
source: " Desmond Smith, 4KS Academy "
} ,
{
quote: " My students love the website and I hope on having content structured around it in the near future. "
source: " Michael Leonard, Science Teacher, Clearwater Central Catholic High School "
}
]
praise [ _ . random ( 0 , praise . length - 1 ) ]
2015-09-25 13:03:44 -04:00
2015-12-14 14:10:37 -05:00
module.exports.getPrepaidCodeAmount = getPrepaidCodeAmount = (price=0, users=0, months=0) ->
2015-09-25 13:03:44 -04:00
return 0 unless users > 0 and months > 0
total = price * users * months
total
2015-11-10 18:22:09 -05:00
2016-06-24 12:36:18 -04:00
startsWithVowel = (s) -> s [ 0 ] in ' aeiouAEIOU '
2016-02-19 13:55:42 -05:00
module.exports.filterMarkdownCodeLanguages = (text, language) ->
2015-12-10 12:05:34 -05:00
return ' ' unless text
2016-02-19 13:55:42 -05:00
currentLanguage = language or me . get ( ' aceConfig ' ) ? . language or ' python '
2015-12-23 13:38:00 -05:00
excludedLanguages = _ . without [ ' javascript ' , ' python ' , ' coffeescript ' , ' clojure ' , ' lua ' , ' java ' , ' io ' ] , currentLanguage
2016-05-31 13:48:16 -04:00
# Exclude language-specific code blocks like ```python (... code ...)``` for each non-target language.
codeBlockExclusionRegex = new RegExp " ```( #{ excludedLanguages . join ( ' | ' ) } ) \n [^`]+``` \n ? " , ' gm '
# Exclude language-specific images like ![python - image description](image url) for each non-target language.
imageExclusionRegex = new RegExp " ! \\ [( #{ excludedLanguages . join ( ' | ' ) } ) - .+? \\ ] \\ (.+? \\ ) \n ? " , ' gm '
2016-06-24 12:36:18 -04:00
text = text . replace ( codeBlockExclusionRegex , ' ' ) . replace ( imageExclusionRegex , ' ' )
commonLanguageReplacements =
python: [
[ ' true ' , ' True ' ] , [ ' false ' , ' False ' ] , [ ' null ' , ' None ' ] ,
[ ' object ' , ' dictionary ' ] , [ ' Object ' , ' Dictionary ' ] ,
[ ' array ' , ' list ' ] , [ ' Array ' , ' List ' ] ,
]
lua: [
[ ' null ' , ' nil ' ] ,
[ ' object ' , ' table ' ] , [ ' Object ' , ' Table ' ] ,
[ ' array ' , ' table ' ] , [ ' Array ' , ' Table ' ] ,
]
for [ from , to ] in commonLanguageReplacements [ currentLanguage ] ? [ ]
# Convert JS-specific keywords and types to Python ones, if in simple `code` tags.
# This won't cover it when it's not in an inline code tag by itself or when it's not in English.
text = text . replace / / /`#{from}`/ / / g , " ` #{ to } ` "
# Now change "An `dictionary`" to "A `dictionary`", etc.
if startsWithVowel ( from ) and not startsWithVowel ( to )
text = text . replace / / /(\ a|A)n(\ `#{to}`)/ / / g , " $1$2 "
if not startsWithVowel ( from ) and startsWithVowel ( to )
text = text . replace / / /(\ a|A)(\ `#{to}`)/ / / g , " $1n$2 "
return text
2015-11-10 18:22:09 -05:00
module.exports.aceEditModes = aceEditModes =
' javascript ' : ' ace/mode/javascript '
' coffeescript ' : ' ace/mode/coffee '
' python ' : ' ace/mode/python '
2015-12-23 13:38:00 -05:00
' java ' : ' ace/mode/java '
2015-11-10 18:22:09 -05:00
' lua ' : ' ace/mode/lua '
2016-01-04 19:41:24 -05:00
' java ' : ' ace/mode/java '
2015-11-10 18:22:09 -05:00
module.exports.initializeACE = (el, codeLanguage) ->
contents = $ ( el ) . text ( ) . trim ( )
editor = ace . edit el
editor . setOptions maxLines: Infinity
editor . setReadOnly true
editor . setTheme ' ace/theme/textmate '
editor . setShowPrintMargin false
editor . setShowFoldWidgets false
editor . setHighlightActiveLine false
editor . setHighlightActiveLine false
editor . setBehavioursEnabled false
editor . renderer . setShowGutter false
editor . setValue contents
editor . clearSelection ( )
session = editor . getSession ( )
session . setUseWorker false
session . setMode aceEditModes [ codeLanguage ]
session . setWrapLimitRange null
session . setUseWrapMode true
session . setNewLineMode ' unix '
return editor
2016-03-30 16:57:19 -04:00
2016-05-24 15:00:04 -04:00
module.exports.capitalLanguages = capitalLanguages =
2016-03-30 16:57:19 -04:00
' javascript ' : ' JavaScript '
' coffeescript ' : ' CoffeeScript '
' python ' : ' Python '
' java ' : ' Java '
' lua ' : ' Lua '