diff --git a/app/assets/fonts/glyphicons-halflings-regular.eot b/app/assets/fonts/glyphicons-halflings-regular.eot
new file mode 100644
index 000000000..4a4ca865d
Binary files /dev/null and b/app/assets/fonts/glyphicons-halflings-regular.eot differ
diff --git a/app/assets/fonts/glyphicons-halflings-regular.svg b/app/assets/fonts/glyphicons-halflings-regular.svg
new file mode 100644
index 000000000..e3e2dc739
--- /dev/null
+++ b/app/assets/fonts/glyphicons-halflings-regular.svg
@@ -0,0 +1,229 @@
+
+
+
\ No newline at end of file
diff --git a/app/assets/fonts/glyphicons-halflings-regular.ttf b/app/assets/fonts/glyphicons-halflings-regular.ttf
new file mode 100644
index 000000000..67fa00bf8
Binary files /dev/null and b/app/assets/fonts/glyphicons-halflings-regular.ttf differ
diff --git a/app/assets/fonts/glyphicons-halflings-regular.woff b/app/assets/fonts/glyphicons-halflings-regular.woff
new file mode 100644
index 000000000..8c54182aa
Binary files /dev/null and b/app/assets/fonts/glyphicons-halflings-regular.woff differ
diff --git a/app/assets/images/level/loading_left_wing.png b/app/assets/images/level/loading_left_wing.png
index 5b7ba04a7..42ec0336d 100644
Binary files a/app/assets/images/level/loading_left_wing.png and b/app/assets/images/level/loading_left_wing.png differ
diff --git a/app/assets/images/level/loading_right_wing.png b/app/assets/images/level/loading_right_wing.png
index 7f7ff29da..e9ace047f 100644
Binary files a/app/assets/images/level/loading_right_wing.png and b/app/assets/images/level/loading_right_wing.png differ
diff --git a/app/assets/images/pages/account/profile/education.png b/app/assets/images/pages/account/profile/education.png
new file mode 100644
index 000000000..dad4914c6
Binary files /dev/null and b/app/assets/images/pages/account/profile/education.png differ
diff --git a/app/assets/images/pages/account/profile/icon_facebook.png b/app/assets/images/pages/account/profile/icon_facebook.png
new file mode 100644
index 000000000..b775c18fa
Binary files /dev/null and b/app/assets/images/pages/account/profile/icon_facebook.png differ
diff --git a/app/assets/images/pages/account/profile/icon_github.png b/app/assets/images/pages/account/profile/icon_github.png
new file mode 100644
index 000000000..fc1801abc
Binary files /dev/null and b/app/assets/images/pages/account/profile/icon_github.png differ
diff --git a/app/assets/images/pages/account/profile/icon_gplus.png b/app/assets/images/pages/account/profile/icon_gplus.png
new file mode 100644
index 000000000..c2343eb50
Binary files /dev/null and b/app/assets/images/pages/account/profile/icon_gplus.png differ
diff --git a/app/assets/images/pages/account/profile/icon_linkedin.png b/app/assets/images/pages/account/profile/icon_linkedin.png
new file mode 100644
index 000000000..cdd0ff6c2
Binary files /dev/null and b/app/assets/images/pages/account/profile/icon_linkedin.png differ
diff --git a/app/assets/images/pages/account/profile/icon_twitter.png b/app/assets/images/pages/account/profile/icon_twitter.png
new file mode 100644
index 000000000..1280ad6df
Binary files /dev/null and b/app/assets/images/pages/account/profile/icon_twitter.png differ
diff --git a/app/assets/images/pages/account/profile/work.png b/app/assets/images/pages/account/profile/work.png
new file mode 100644
index 000000000..72e659071
Binary files /dev/null and b/app/assets/images/pages/account/profile/work.png differ
diff --git a/app/assets/images/pages/play/easy_button.png b/app/assets/images/pages/play/easy_button.png
new file mode 100644
index 000000000..c75dc83f9
Binary files /dev/null and b/app/assets/images/pages/play/easy_button.png differ
diff --git a/app/assets/images/pages/play/hard_button.png b/app/assets/images/pages/play/hard_button.png
new file mode 100644
index 000000000..880b42442
Binary files /dev/null and b/app/assets/images/pages/play/hard_button.png differ
diff --git a/app/assets/images/pages/play/ladder/humans_ladder_easy.png b/app/assets/images/pages/play/ladder/humans_ladder_easy.png
index ea34dcc5b..095b57688 100644
Binary files a/app/assets/images/pages/play/ladder/humans_ladder_easy.png and b/app/assets/images/pages/play/ladder/humans_ladder_easy.png differ
diff --git a/app/assets/images/pages/play/ladder/humans_ladder_hard.png b/app/assets/images/pages/play/ladder/humans_ladder_hard.png
index 8cd03225d..90afbd048 100644
Binary files a/app/assets/images/pages/play/ladder/humans_ladder_hard.png and b/app/assets/images/pages/play/ladder/humans_ladder_hard.png differ
diff --git a/app/assets/images/pages/play/ladder/humans_ladder_medium.png b/app/assets/images/pages/play/ladder/humans_ladder_medium.png
index f4b5fdf94..4d5570ebf 100644
Binary files a/app/assets/images/pages/play/ladder/humans_ladder_medium.png and b/app/assets/images/pages/play/ladder/humans_ladder_medium.png differ
diff --git a/app/assets/images/pages/play/ladder/humans_ladder_tutorial.png b/app/assets/images/pages/play/ladder/humans_ladder_tutorial.png
index 8e34fc924..d4f1ffffe 100644
Binary files a/app/assets/images/pages/play/ladder/humans_ladder_tutorial.png and b/app/assets/images/pages/play/ladder/humans_ladder_tutorial.png differ
diff --git a/app/assets/images/pages/play/ladder/ogres_ladder_easy.png b/app/assets/images/pages/play/ladder/ogres_ladder_easy.png
index d5e4695ff..ae82c36ca 100644
Binary files a/app/assets/images/pages/play/ladder/ogres_ladder_easy.png and b/app/assets/images/pages/play/ladder/ogres_ladder_easy.png differ
diff --git a/app/assets/images/pages/play/ladder/ogres_ladder_medium.png b/app/assets/images/pages/play/ladder/ogres_ladder_medium.png
index 5e327d74b..a86ac9585 100644
Binary files a/app/assets/images/pages/play/ladder/ogres_ladder_medium.png and b/app/assets/images/pages/play/ladder/ogres_ladder_medium.png differ
diff --git a/app/assets/images/pages/play/ladder/ogres_ladder_tutorial.png b/app/assets/images/pages/play/ladder/ogres_ladder_tutorial.png
index 16e952728..939a534c7 100644
Binary files a/app/assets/images/pages/play/ladder/ogres_ladder_tutorial.png and b/app/assets/images/pages/play/ladder/ogres_ladder_tutorial.png differ
diff --git a/app/assets/images/pages/play/medium_button.png b/app/assets/images/pages/play/medium_button.png
new file mode 100644
index 000000000..41f87572e
Binary files /dev/null and b/app/assets/images/pages/play/medium_button.png differ
diff --git a/app/assets/images/pages/play/warmup_button.png b/app/assets/images/pages/play/warmup_button.png
new file mode 100644
index 000000000..ccc6503b2
Binary files /dev/null and b/app/assets/images/pages/play/warmup_button.png differ
diff --git a/app/assets/main.html b/app/assets/main.html
index 96306649e..761eac10c 100644
--- a/app/assets/main.html
+++ b/app/assets/main.html
@@ -72,7 +72,7 @@
-
+
@@ -117,16 +117,9 @@
-
-
+
-
+
")
- $('head').append(script)
- window[functionName] = (profile) =>
- @gravatarProfile = profile
- @trigger('change', @)
-
- func = => @gravatarProfile = null unless @gravatarProfile
- setTimeout(func, 1000)
-
displayName: ->
- @get('name') or @gravatarName() or "Anoner"
+ @get('name') or "Anoner"
lang: ->
@get('preferredLanguage') or "en-US"
- gravatarName: ->
- @gravatarProfile?.entry[0]?.name?.formatted or ''
-
- gravatarPhotoURLs: ->
- photos = @gravatarProfile?.entry[0]?.photos
- return if not photos
- (photo.value for photo in photos)
-
- getPhotoURL: ->
- photoURL = @get('photoURL')
- validURLs = @gravatarPhotoURLs()
- return @gravatarAvatarURL() unless validURLs and validURLs.length
- return validURLs[0] unless photoURL in validURLs
- return photoURL
+ getPhotoURL: (size=80, useJobProfilePhoto=false) ->
+ photoURL = if useJobProfilePhoto then @get('jobProfile')?.photoURL else null
+ photoURL ||= @get('photoURL')
+ if photoURL
+ prefix = if photoURL.search(/\?/) is -1 then "?" else "&"
+ return "#{photoURL}#{prefix}s=#{size}" if photoURL.search('http') isnt -1 # legacy
+ return "/file/#{photoURL}#{prefix}s=#{size}"
+ return "/db/user/#{@id}/avatar?s=#{size}"
@getByID = (id, properties, force) ->
{me} = require('lib/auth')
@@ -66,7 +38,7 @@ module.exports = class User extends CocoModel
success: ->
user.loading = false
Backbone.Mediator.publish('user:fetched')
- user.loadGravatarProfile()
+ #user.trigger 'sync' # needed?
)
cache[id] = user
user
diff --git a/app/schemas/definitions/bus.coffee b/app/schemas/definitions/bus.coffee
new file mode 100644
index 000000000..b5625025e
--- /dev/null
+++ b/app/schemas/definitions/bus.coffee
@@ -0,0 +1,14 @@
+module.exports =
+ bus:
+ title: "Bus"
+ id: "bus"
+ $schema: "http://json-schema.org/draft-04/schema#"
+ description: "Bus" # TODO
+ type: "object"
+ properties: # TODO
+ joined:
+ type: "boolean"
+ players:
+ type: "object"
+ required: ["joined", "players"]
+ additionalProperties: false
\ No newline at end of file
diff --git a/app/schemas/definitions/misc.coffee b/app/schemas/definitions/misc.coffee
new file mode 100644
index 000000000..bbf9f5c02
--- /dev/null
+++ b/app/schemas/definitions/misc.coffee
@@ -0,0 +1,12 @@
+module.exports =
+ jQueryEvent:
+ title: "jQuery Event"
+ id: "jQueryEvent"
+ $schema: "http://json-schema.org/draft-04/schema#"
+ description: "A standard jQuery Event"
+ type: "object"
+ properties: # TODO schema complete
+ altKey:
+ type: "boolean"
+ required: []
+ additionalProperties: true
diff --git a/server/commons/i18n_schema.coffee b/app/schemas/i18n_schema.coffee
similarity index 100%
rename from server/commons/i18n_schema.coffee
rename to app/schemas/i18n_schema.coffee
diff --git a/app/schemas/languages.coffee b/app/schemas/languages.coffee
new file mode 100644
index 000000000..053a89f2a
--- /dev/null
+++ b/app/schemas/languages.coffee
@@ -0,0 +1,34 @@
+locale = require '../locale/locale' # requiring from app; will break if we stop serving from where app lives
+
+languages = []
+for code, localeInfo of locale
+ languages.push code: code, nativeDescription: localeInfo.nativeDescription, englishDescription: localeInfo.englishDescription
+
+module.exports.languages = languages
+module.exports.languageCodes = languageCodes = (language.code for language in languages)
+module.exports.languageCodesLower = languageCodesLower = (code.toLowerCase() for code in languageCodes)
+
+# Keep keys lower-case for matching and values with second subtag uppercase like i18next expects
+languageAliases =
+ 'en': 'en-US'
+
+ 'zh-cn': 'zh-HANS'
+ 'zh-hans-cn': 'zh-HANS'
+ 'zh-sg': 'zh-HANS'
+ 'zh-hans-sg': 'zh-HANS'
+
+ 'zh-tw': 'zh-HANT'
+ 'zh-hant-tw': 'zh-HANT'
+ 'zh-hk': 'zh-HANT'
+ 'zh-hant-hk': 'zh-HANT'
+ 'zh-mo': 'zh-HANT'
+ 'zh-hant-mo': 'zh-HANT'
+
+module.exports.languageCodeFromAcceptedLanguages = languageCodeFromAcceptedLanguages = (acceptedLanguages) ->
+ for lang in acceptedLanguages ? []
+ code = languageAliases[lang.toLowerCase()]
+ return code if code
+ codeIndex = _.indexOf languageCodesLower, lang
+ if codeIndex isnt -1
+ return languageCodes[codeIndex]
+ return 'en-US'
diff --git a/server/commons/metaschema.coffee b/app/schemas/metaschema.coffee
similarity index 100%
rename from server/commons/metaschema.coffee
rename to app/schemas/metaschema.coffee
diff --git a/server/articles/article_schema.coffee b/app/schemas/models/article.coffee
similarity index 50%
rename from server/articles/article_schema.coffee
rename to app/schemas/models/article.coffee
index 1fd4769f7..60f65640f 100644
--- a/server/articles/article_schema.coffee
+++ b/app/schemas/models/article.coffee
@@ -1,13 +1,14 @@
-c = require '../commons/schemas'
+c = require './../schemas'
ArticleSchema = c.object()
c.extendNamedProperties ArticleSchema # name first
ArticleSchema.properties.body = { type: 'string', title: 'Content', format: 'markdown' }
-ArticleSchema.properties.i18n = { type: 'object', title: 'i18n', format: 'i18n', props: ['body'] }
+ArticleSchema.properties.i18n = { type: 'object', title: 'i18n', format: 'i18n', props: ['name', 'body'] }
-c.extendBasicProperties(ArticleSchema, 'article')
-c.extendSearchableProperties(ArticleSchema)
-c.extendVersionedProperties(ArticleSchema, 'article')
+c.extendBasicProperties ArticleSchema, 'article'
+c.extendSearchableProperties ArticleSchema
+c.extendVersionedProperties ArticleSchema, 'article'
+c.extendPatchableProperties ArticleSchema
module.exports = ArticleSchema
diff --git a/server/levels/level_schema.coffee b/app/schemas/models/level.coffee
similarity index 97%
rename from server/levels/level_schema.coffee
rename to app/schemas/models/level.coffee
index 8d2d60cd3..7180c6a67 100644
--- a/server/levels/level_schema.coffee
+++ b/app/schemas/models/level.coffee
@@ -1,5 +1,5 @@
-c = require '../commons/schemas'
-ThangComponentSchema = require './thangs/thang_component_schema'
+c = require './../schemas'
+ThangComponentSchema = require './../models/thang_component'
SpecificArticleSchema = c.object()
c.extendNamedProperties SpecificArticleSchema # name first
@@ -108,9 +108,9 @@ NoteGroupSchema = c.object {title: "Note Group", description: "A group of notes
lock: {title: "Lock", description: "Whether the interface should be locked so that the player's focus is on the script, or specific areas to lock.", type: ['boolean', 'array'], items: {type: 'string', enum: ['surface', 'editor', 'palette', 'hud', 'playback', 'playback-hover', 'level', ]}}
letterbox: {type: 'boolean', title: 'Letterbox', description:'Turn letterbox mode on or off. Disables surface and playback controls.'}
- goals: c.object {title: "Goals", description: "Add or remove goals for the player to complete in the level."},
- add: c.array {title: "Add", description: "Add these goals."}, GoalSchema
- remove: c.array {title: "Remove", description: "Remove these goals."}, GoalSchema
+ goals: c.object {title: "Goals (Old)", description: "Deprecated. Goals added here have no effect. Add goals in the level settings instead."},
+ add: c.array {title: "Add", description: "Deprecated. Goals added here have no effect. Add goals in the level settings instead."}, GoalSchema
+ remove: c.array {title: "Remove", description: "Deprecated. Goals removed here have no effect. Adjust goals in the level settings instead."}, GoalSchema
playback: c.object {title: "Playback", description: "Control the playback of the level."},
playing: {type: 'boolean', title: "Set Playing", description: "Set whether playback is playing or paused."}
@@ -243,6 +243,7 @@ c.extendBasicProperties LevelSchema, 'level'
c.extendSearchableProperties LevelSchema
c.extendVersionedProperties LevelSchema, 'level'
c.extendPermissionsProperties LevelSchema, 'level'
+c.extendPatchableProperties LevelSchema
module.exports = LevelSchema
diff --git a/server/levels/components/level_component_schema.coffee b/app/schemas/models/level_component.coffee
similarity index 97%
rename from server/levels/components/level_component_schema.coffee
rename to app/schemas/models/level_component.coffee
index ac399da2c..8552979ee 100644
--- a/server/levels/components/level_component_schema.coffee
+++ b/app/schemas/models/level_component.coffee
@@ -1,5 +1,5 @@
-c = require '../../commons/schemas'
-metaschema = require '../../commons/metaschema'
+c = require './../schemas'
+metaschema = require './../metaschema'
attackSelfCode = """
class AttacksSelf extends Component
@@ -115,5 +115,6 @@ c.extendBasicProperties LevelComponentSchema, 'level.component'
c.extendSearchableProperties LevelComponentSchema
c.extendVersionedProperties LevelComponentSchema, 'level.component'
c.extendPermissionsProperties LevelComponentSchema, 'level.component'
+c.extendPatchableProperties LevelComponentSchema
module.exports = LevelComponentSchema
diff --git a/server/levels/feedbacks/level_feedback_schema.coffee b/app/schemas/models/level_feedback.coffee
similarity index 95%
rename from server/levels/feedbacks/level_feedback_schema.coffee
rename to app/schemas/models/level_feedback.coffee
index 54d9e84e1..f8bb6a73c 100644
--- a/server/levels/feedbacks/level_feedback_schema.coffee
+++ b/app/schemas/models/level_feedback.coffee
@@ -1,4 +1,4 @@
-c = require '../../commons/schemas'
+c = require './../schemas'
LevelFeedbackLevelSchema = c.object {required: ['original', 'majorVersion']}, {
original: c.objectId({})
diff --git a/server/levels/sessions/level_session_schema.coffee b/app/schemas/models/level_session.coffee
similarity index 99%
rename from server/levels/sessions/level_session_schema.coffee
rename to app/schemas/models/level_session.coffee
index d798a9d88..670dc9ad4 100644
--- a/server/levels/sessions/level_session_schema.coffee
+++ b/app/schemas/models/level_session.coffee
@@ -1,4 +1,4 @@
-c = require '../../commons/schemas'
+c = require './../schemas'
LevelSessionPlayerSchema = c.object
id: c.objectId
diff --git a/server/levels/systems/level_system_schema.coffee b/app/schemas/models/level_system.coffee
similarity index 96%
rename from server/levels/systems/level_system_schema.coffee
rename to app/schemas/models/level_system.coffee
index cc4bc7891..1804de363 100644
--- a/server/levels/systems/level_system_schema.coffee
+++ b/app/schemas/models/level_system.coffee
@@ -1,5 +1,5 @@
-c = require '../../commons/schemas'
-metaschema = require '../../commons/metaschema'
+c = require './../schemas'
+metaschema = require './../metaschema'
jitterSystemCode = """
class Jitter extends System
@@ -101,6 +101,7 @@ _.extend LevelSystemSchema.properties,
c.extendBasicProperties LevelSystemSchema, 'level.system'
c.extendSearchableProperties LevelSystemSchema
c.extendVersionedProperties LevelSystemSchema, 'level.system'
-c.extendPermissionsProperties LevelSystemSchema, 'level.system'
+c.extendPermissionsProperties LevelSystemSchema
+c.extendPatchableProperties LevelSystemSchema
module.exports = LevelSystemSchema
diff --git a/app/schemas/models/patch.coffee b/app/schemas/models/patch.coffee
new file mode 100644
index 000000000..e14423371
--- /dev/null
+++ b/app/schemas/models/patch.coffee
@@ -0,0 +1,27 @@
+c = require './../schemas'
+
+patchables = ['level', 'thang_type', 'level_system', 'level_component', 'article']
+
+PatchSchema = c.object({title:'Patch', required:['target', 'delta', 'commitMessage']}, {
+ delta: { title: 'Delta', type:['array', 'object'] }
+ commitMessage: c.shortString({maxLength: 500, minLength: 1})
+ creator: c.objectId(links: [{rel: 'extra', href: "/db/user/{($)}"}])
+ created: c.date( { title: 'Created', readOnly: true })
+ status: { enum: ['pending', 'accepted', 'rejected', 'withdrawn']}
+
+ target: c.object({title: 'Target', required:['collection', 'id']}, {
+ collection: { enum: patchables }
+ id: c.objectId(title: 'Target ID') # search by this if not versioned
+
+ # if target is versioned, want to know that info too
+ original: c.objectId(title: 'Target Original') # search by this if versioned
+ version:
+ properties:
+ major: { type: 'number', minimum: 0 }
+ minor: { type: 'number', minimum: 0 }
+ })
+})
+
+c.extendBasicProperties(PatchSchema, 'patch')
+
+module.exports = PatchSchema
diff --git a/server/levels/thangs/thang_component_schema.coffee b/app/schemas/models/thang_component.coffee
similarity index 95%
rename from server/levels/thangs/thang_component_schema.coffee
rename to app/schemas/models/thang_component.coffee
index 0118d3a4c..eebcf155b 100644
--- a/server/levels/thangs/thang_component_schema.coffee
+++ b/app/schemas/models/thang_component.coffee
@@ -1,4 +1,4 @@
-c = require '../../commons/schemas'
+c = require './../schemas'
module.exports = ThangComponentSchema = c.object {
title: "Component"
diff --git a/server/levels/thangs/thang_type_schema.coffee b/app/schemas/models/thang_type.coffee
similarity index 96%
rename from server/levels/thangs/thang_type_schema.coffee
rename to app/schemas/models/thang_type.coffee
index 8b70ccbaf..eb78c1c11 100644
--- a/server/levels/thangs/thang_type_schema.coffee
+++ b/app/schemas/models/thang_type.coffee
@@ -1,5 +1,5 @@
-c = require '../../commons/schemas'
-ThangComponentSchema = require './thang_component_schema'
+c = require './../schemas'
+ThangComponentSchema = require './thang_component'
ThangTypeSchema = c.object()
c.extendNamedProperties ThangTypeSchema # name first
@@ -146,8 +146,9 @@ ThangTypeSchema.definitions =
action: ActionSchema
sound: SoundSchema
-c.extendBasicProperties(ThangTypeSchema, 'thang.type')
-c.extendSearchableProperties(ThangTypeSchema)
-c.extendVersionedProperties(ThangTypeSchema, 'thang.type')
+c.extendBasicProperties ThangTypeSchema, 'thang.type'
+c.extendSearchableProperties ThangTypeSchema
+c.extendVersionedProperties ThangTypeSchema, 'thang.type'
+c.extendPatchableProperties ThangTypeSchema
module.exports = ThangTypeSchema
diff --git a/app/schemas/models/user.coffee b/app/schemas/models/user.coffee
new file mode 100644
index 000000000..6bb3939e6
--- /dev/null
+++ b/app/schemas/models/user.coffee
@@ -0,0 +1,98 @@
+c = require './../schemas'
+emailSubscriptions = ['announcement', 'tester', 'level_creator', 'developer', 'article_editor', 'translator', 'support', 'notification']
+
+UserSchema = c.object {},
+ name: c.shortString({title: 'Display Name', default:''})
+ email: c.shortString({title: 'Email', format: 'email'})
+ firstName: c.shortString({title: 'First Name'})
+ lastName: c.shortString({title: 'Last Name'})
+ gender: {type: 'string', 'enum': ['male', 'female']}
+ password: {type: 'string', maxLength: 256, minLength: 2, title:'Password'}
+ passwordReset: {type: 'string'}
+ photoURL: {type: 'string', format: 'image-file', title: 'Profile Picture', description: 'Upload a 256x256px or larger image to serve as your profile picture.'}
+
+ facebookID: c.shortString({title: 'Facebook ID'})
+ gplusID: c.shortString({title: 'G+ ID'})
+
+ wizardColor1: c.pct({title: 'Wizard Clothes Color'})
+ volume: c.pct({title: 'Volume'})
+ music: {type: 'boolean', default: true}
+ autocastDelay: {type: 'integer', 'default': 5000 }
+ lastLevel: { type: 'string' }
+
+ emailSubscriptions: c.array {uniqueItems: true, 'default': ['announcement', 'notification']}, {'enum': emailSubscriptions}
+
+ # server controlled
+ permissions: c.array {'default': []}, c.shortString()
+ dateCreated: c.date({title: 'Date Joined'})
+ anonymous: {type: 'boolean', 'default': true}
+ testGroupNumber: {type: 'integer', minimum: 0, maximum: 256, exclusiveMaximum: true}
+ mailChimp: {type: 'object'}
+ hourOfCode: {type: 'boolean'}
+ hourOfCodeComplete: {type: 'boolean'}
+
+ emailLower: c.shortString()
+ nameLower: c.shortString()
+ passwordHash: {type: 'string', maxLength: 256}
+
+ # client side
+ emailHash: {type: 'string'}
+
+ #Internationalization stuff
+ preferredLanguage: {type: 'string', default: 'en', 'enum': c.getLanguageCodeArray()}
+
+ signedCLA: c.date({title: 'Date Signed the CLA'})
+ wizard: c.object {},
+ colorConfig: c.object {additionalProperties: c.colorConfig()}
+
+ aceConfig: c.object {},
+ language: {type: 'string', 'default': 'javascript', 'enum': ['javascript', 'coffeescript']}
+ keyBindings: {type: 'string', 'default': 'default', 'enum': ['default', 'vim', 'emacs']}
+ invisibles: {type: 'boolean', 'default': false}
+ indentGuides: {type: 'boolean', 'default': false}
+ behaviors: {type: 'boolean', 'default': false}
+
+ simulatedBy: {type: 'integer', minimum: 0, default: 0}
+ simulatedFor: {type: 'integer', minimum: 0, default: 0}
+
+ jobProfile: c.object {title: 'Job Profile', required: ['lookingFor', 'jobTitle', 'active', 'name', 'city', 'country', 'skills', 'experience', 'shortDescription', 'longDescription', 'visa', 'work', 'education', 'projects', 'links']},
+ lookingFor: {title: 'Looking For', type: 'string', enum: ['Full-time', 'Part-time', 'Remote', 'Contracting', 'Internship'], default: 'Full-time', description: 'What kind of developer position do you want?'}
+ jobTitle: {type: 'string', maxLength: 50, title: 'Desired Job Title', description: 'What role are you looking for? Ex.: "Full Stack Engineer", "Front-End Developer", "iOS Developer"', default: 'Software Developer'}
+ active: {title: 'Active', type: 'boolean', description: 'Want interview offers right now?'}
+ updated: c.date {title: 'Last Updated', description: 'How fresh your profile appears to employers. The fresher, the better. Profiles go inactive after 30 days.'}
+ name: c.shortString {title: 'Name', description: 'Name you want employers to see, like "Nick Winter".'}
+ city: c.shortString {title: 'City', description: 'City you want to work in (or live in now), like "San Francisco" or "Lubbock, TX".', default: 'Defaultsville, CA', format: 'city'}
+ country: c.shortString {title: 'Country', description: 'Country you want to work in (or live in now), like "USA" or "France".', default: 'USA', format: 'country'}
+ skills: c.array {title: 'Skills', description: 'Tag relevant developer skills in order of proficiency. Employers will see the first five at a glance.', default: ['javascript'], minItems: 1, maxItems: 30, uniqueItems: true},
+ {type: 'string', minLength: 1, maxLength: 20, description: 'Ex.: "objective-c", "mongodb", "rails", "android", "javascript"', format: 'skill'}
+ experience: {type: 'integer', title: 'Years of Experience', minimum: 0, description: 'How many years of professional experience (getting paid) developing software do you have?'}
+ shortDescription: {type: 'string', maxLength: 140, title: 'Short Description', description: 'Who are you, and what are you looking for? 140 characters max.', default: 'Programmer seeking to build great software.'}
+ longDescription: {type: 'string', maxLength: 600, title: 'Description', description: 'Describe yourself to potential employers. Keep it short and to the point. We recommend outlining the position that would most interest you. Tasteful markdown okay; 600 characters max.', format: 'markdown', default: '* I write great code.\n* You need great code?\n* Great!'}
+ visa: c.shortString {title: 'US Work Status', description: 'Are you authorized to work in the US, or do you need visa sponsorship?', enum: ['Authorized to work in the US', 'Need visa sponsorship'], default: 'Authorized to work in the US'}
+ work: c.array {title: 'Work Experience', description: 'List your relevant work experience, most recent first.'},
+ c.object {title: 'Job', description: 'Some work experience you had.', required: ['employer', 'role', 'duration']},
+ employer: c.shortString {title: 'Employer', description: 'Name of your employer.'}
+ role: c.shortString {title: 'Job Title', description: 'What was your job title or role?'}
+ duration: c.shortString {title: 'Duration', description: 'When did you hold this gig? Ex.: "Feb 2013 - present".'}
+ education: c.array {title: 'Education', description: 'List your academic ordeals.'},
+ c.object {title: 'Ordeal', description: 'Some education that befell you.', required: ['school', 'degree', 'duration']},
+ school: c.shortString {title: 'School', description: 'Name of your school.'}
+ degree: c.shortString {title: 'Degree', description: 'What was your degree and field of study? Ex. Ph.D. Human-Computer Interaction (incomplete)'}
+ duration: c.shortString {title: 'Dates', description: 'When? Ex.: "Aug 2004 - May 2008".'}
+ projects: c.array {title: 'Projects', description: 'Highlight your projects to amaze employers.'},
+ c.object {title: 'Project', description: 'A project you created.', required: ['name', 'description', 'picture'], default: {name: 'My Project', description: 'A project I worked on.', link: 'http://example.com', picture: ''}},
+ name: c.shortString {title: 'Project Name', description: 'What was the project called?', default: 'My Project'}
+ description: {type: 'string', title: 'Description', description: 'Briefly describe the project.', maxLength: 400, default: 'A project I worked on.', format: 'markdown'}
+ picture: {type: 'string', title: 'Picture', format: 'image-file', description: 'Upload a 230x115px or larger image showing off the project.'}
+ link: c.url {title: 'Link', description: 'Link to the project.', default: 'http://example.com'}
+ links: c.array {title: 'Personal and Social Links', description: 'Link any other sites or profiles you want to highlight, like your GitHub, your LinkedIn, or your blog.'},
+ c.object {title: 'Link', description: 'A link to another site you want to highlight, like your GitHub, your LinkedIn, or your blog.', required: ['name', 'link']},
+ name: {type: 'string', maxLength: 30, title: 'Link Name', description: 'What are you linking to? Ex: "Personal Website", "Twitter"', format: 'link-name'}
+ link: c.url {title: 'Link', description: 'The URL.', default: 'http://example.com'}
+ photoURL: {type: 'string', format: 'image-file', title: 'Profile Picture', description: 'Upload a 256x256px or larger image if you want to show a different profile picture to employers than your normal avatar.'}
+
+ jobProfileApproved: {title: 'Job Profile Approved', type: 'boolean', description: 'Whether your profile has been approved by CodeCombat.'}
+ jobProfileNotes: {type: 'string', maxLength: 1000, title: 'Our Notes', description: "CodeCombat's notes on the candidate.", format: 'markdown', default: ''}
+c.extendBasicProperties UserSchema, 'user'
+
+module.exports = UserSchema
diff --git a/server/commons/schemas.coffee b/app/schemas/schemas.coffee
similarity index 86%
rename from server/commons/schemas.coffee
rename to app/schemas/schemas.coffee
index 060ff8348..2d7ae0603 100644
--- a/server/commons/schemas.coffee
+++ b/app/schemas/schemas.coffee
@@ -1,5 +1,5 @@
#language imports
-Language = require '../routes/languages'
+Language = require './languages'
# schema helper methods
me = module.exports
@@ -8,14 +8,17 @@ combine = (base, ext) ->
return base unless ext?
return _.extend(base, ext)
+urlPattern = '^(ht|f)tp(s?)\:\/\/[0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*(:(0-9)*)*(\/?)([a-zA-Z0-9\-‌​\.\?\,\'\/\\\+&%\$#_=]*)?$'
+
# Common schema properties
me.object = (ext, props) -> combine {type: 'object', additionalProperties: false, properties: props or {}}, ext
me.array = (ext, items) -> combine {type: 'array', items: items or {}}, ext
me.shortString = (ext) -> combine({type: 'string', maxLength: 100}, ext)
me.pct = (ext) -> combine({type: 'number', maximum: 1.0, minimum: 0.0}, ext)
-me.date = (ext) -> combine({type: 'string', format: 'date-time'}, ext)
+me.date = (ext) -> combine({type: ['object', 'string'], format: 'date-time'}, ext)
# should just be string (Mongo ID), but sometimes mongoose turns them into objects representing those, so we are lenient
me.objectId = (ext) -> schema = combine({type: ['object', 'string'] }, ext)
+me.url = (ext) -> combine({type: 'string', format: 'url', pattern: urlPattern}, ext)
PointSchema = me.object {title: "Point", description: "An {x, y} coordinate point.", format: "point2d", required: ["x", "y"]},
x: {title: "x", description: "The x coordinate.", type: "number", "default": 15}
@@ -51,7 +54,21 @@ basicProps = (linkFragment) ->
me.extendBasicProperties = (schema, linkFragment) ->
schema.properties = {} unless schema.properties?
_.extend(schema.properties, basicProps(linkFragment))
+
+# PATCHABLE
+patchableProps = ->
+ patches: me.array({title:'Patches'}, {
+ _id: me.objectId(links: [{rel: "db", href: "/db/patch/{($)}"}], title: "Patch ID", description: "A reference to the patch.")
+ status: { enum: ['pending', 'accepted', 'rejected', 'cancelled']}
+ })
+ allowPatches: { type: 'boolean' }
+ listeners: me.array({title:'Listeners'},
+ me.objectId(links: [{rel: 'extra', href: "/db/user/{($)}"}]))
+
+me.extendPatchableProperties = (schema) ->
+ schema.properties = {} unless schema.properties?
+ _.extend(schema.properties, patchableProps())
# NAMED
diff --git a/app/schemas/subscriptions/app.coffee b/app/schemas/subscriptions/app.coffee
new file mode 100644
index 000000000..7d0673751
--- /dev/null
+++ b/app/schemas/subscriptions/app.coffee
@@ -0,0 +1,18 @@
+module.exports =
+ "application:idle-changed":
+ {} # TODO schema
+
+ "logging-in-with-facebook":
+ {} # TODO schema
+
+ "facebook-logged-in":
+ {} # TODO schema
+
+ "gapi-loaded":
+ {} # TODO schema
+
+ "logging-in-with-gplus":
+ {} # TODO schema
+
+ "gplus-logged-in":
+ {} # TODO schema
diff --git a/app/schemas/subscriptions/bus.coffee b/app/schemas/subscriptions/bus.coffee
new file mode 100644
index 000000000..549793f3b
--- /dev/null
+++ b/app/schemas/subscriptions/bus.coffee
@@ -0,0 +1,27 @@
+module.exports =
+ "bus:connecting":
+ title: "Bus Connecting"
+ $schema: "http://json-schema.org/draft-04/schema#"
+ description: "Published when a Bus starts connecting"
+ type: "object"
+ properties:
+ bus:
+ $ref: "bus"
+
+ "bus:connected":
+ {} # TODO schema
+
+ "bus:disconnected":
+ {} # TODO schema
+
+ "bus:new-message":
+ {} # TODO schema
+
+ "bus:player-joined":
+ {} # TODO schema
+
+ "bus:player-left":
+ {} # TODO schema
+
+ "bus:player-states-changed":
+ {} # TODO schema
\ No newline at end of file
diff --git a/app/schemas/subscriptions/editor.coffee b/app/schemas/subscriptions/editor.coffee
new file mode 100644
index 000000000..eba61f772
--- /dev/null
+++ b/app/schemas/subscriptions/editor.coffee
@@ -0,0 +1,78 @@
+module.exports =
+ "save-new-version":
+ title: "Save New Version"
+ $schema: "http://json-schema.org/draft-04/schema#"
+ description: "Published when a version gets saved"
+ type: "object"
+ properties:
+ major:
+ type: "boolean"
+ commitMessage:
+ type: "string"
+ required: ["major", "commitMessage"]
+ additionalProperties: false
+
+ # TODO all these events starting with 'level:' should have 'editor' in their name
+ # to avoid confusion with level play events
+
+ "level:view-switched":
+ title: "Level View Switched"
+ $schema: "http://json-schema.org/draft-04/schema#"
+ description: "Published whenever the view switches"
+ $ref: "jQueryEvent"
+
+ "level-components-changed":
+ {} # TODO schema
+
+ "edit-level-component":
+ {} # TODO schema
+
+ "level-component-edited":
+ {} # TODO schema
+
+ "level-component-editing-ended":
+ {} # TODO schema
+
+ "level-systems-changed":
+ {} # TODO schema
+
+ "edit-level-system":
+ {} # TODO schema
+
+ "level-system-added":
+ {} # TODO schema
+
+ "level-system-edited":
+ {} # TODO schema
+
+ "level-system-editing-ended":
+ {} # TODO schema
+
+ "level-thangs-changed":
+ title: "Level Thangs Changed"
+ $schema: "http://json-schema.org/draft-04/schema#"
+ description: "Published when a Thang changes"
+ type: "object"
+ properties:
+ thangsData:
+ type: "array"
+ required: ["thangsData"]
+ additionalProperties: false
+
+ "edit-level-thang":
+ {} # TODO schema
+
+ "level-thang-edited":
+ {} # TODO schema
+
+ "level-thang-done-editing":
+ {} # TODO schema
+
+ "level-loaded":
+ {} # TODO schema
+
+ "level-reload-from-data":
+ {} # TODO schema
+
+ "save-new-version":
+ {} # TODO schema
diff --git a/app/schemas/subscriptions/errors.coffee b/app/schemas/subscriptions/errors.coffee
new file mode 100644
index 000000000..4fa0e33ef
--- /dev/null
+++ b/app/schemas/subscriptions/errors.coffee
@@ -0,0 +1,5 @@
+module.exports =
+ # app/lib/errors
+ "server-error":
+ {} # TODO schema
+
diff --git a/app/schemas/subscriptions/misc.coffee b/app/schemas/subscriptions/misc.coffee
new file mode 100644
index 000000000..5834aaff8
--- /dev/null
+++ b/app/schemas/subscriptions/misc.coffee
@@ -0,0 +1,20 @@
+module.exports =
+ "audio-played:loaded":
+ {} # TODO schema
+
+ # TODO location is debatable
+ "note-group-started":
+ {} # TODO schema
+
+ "note-group-ended":
+ {} # TODO schema
+
+ "modal-closed":
+ {} # TODO schema
+
+ # TODO I propose prepending 'modal:'
+ "save-new-version":
+ {} # TODO schema
+
+ "router:navigate":
+ {} # TODO schema
diff --git a/app/schemas/subscriptions/play.coffee b/app/schemas/subscriptions/play.coffee
new file mode 100644
index 000000000..356f06a36
--- /dev/null
+++ b/app/schemas/subscriptions/play.coffee
@@ -0,0 +1,118 @@
+module.exports =
+ # TODO There should be a better way to divide these channels into smaller ones
+
+ # TODO location is debatable
+ "echo-self-wizard-sprite":
+ {} # TODO schema
+
+ "level:session-will-save":
+ {} # TODO schema
+
+ "level-loader:progress-changed":
+ {} # TODO schema
+
+ "level:shift-space-pressed":
+ {} # TODO schema
+
+ "level:escape-pressed":
+ {} # TODO schema
+
+ "level-enable-controls":
+ {} # TODO schema
+
+ "level-set-letterbox":
+ {} # TODO schema
+
+ "level:started":
+ {} # TODO schema
+
+ "level-set-debug":
+ {} # TODO schema
+
+ "level-set-grid":
+ {} # TODO schema
+
+ "tome:cast-spell":
+ {} # TODO schema
+
+ "level:restarted":
+ {} # TODO schema
+
+ "level-set-volume":
+ {} # TODO schema
+
+ "level-set-time":
+ {} # TODO schema
+
+ "level-select-sprite":
+ {} # TODO schema
+
+ "level-set-playing":
+ {} # TODO schema
+
+ "level:team-set":
+ {} # TODO schema
+
+ "level:docs-hidden":
+ {} # TODO schema
+
+ "level:victory-hidden":
+ {} # TODO schema
+
+ "next-game-pressed":
+ {} # TODO schema
+
+ "focus-editor":
+ {} # TODO schema
+
+ "end-current-script":
+ {} # TODO schema
+
+ "script:reset":
+ {} # TODO schema
+
+ "script:ended":
+ {} # TODO schema
+
+ "script:state-changed":
+ {} # TODO schema
+
+ "play-sound":
+ {} # TODO schema
+
+ # TODO refactor name
+ "onLoadingViewUnveiled":
+ {} # TODO schema
+
+ "playback:manually-scrubbed":
+ {} # TODO schema
+
+ "change:editor-config":
+ {} # TODO schema
+
+ "restart-level":
+ {} # TODO schema
+
+ "play-next-level":
+ {} # TODO schema
+
+ "level-select-sprite":
+ {} # TODO schema
+
+ "level-toggle-grid":
+ {} # TODO schema
+
+ "level-toggle-debug":
+ {} # TODO schema
+
+ "level-toggle-pathfinding":
+ {} # TODO schema
+
+ "level-scrub-forward":
+ {} # TODO schema
+
+ "level-scrub-back":
+ {} # TODO schema
+
+ "goal-manager:new-goal-states":
+ {} # TODO schema
diff --git a/app/schemas/subscriptions/surface.coffee b/app/schemas/subscriptions/surface.coffee
new file mode 100644
index 000000000..6fa5f2415
--- /dev/null
+++ b/app/schemas/subscriptions/surface.coffee
@@ -0,0 +1,96 @@
+module.exports = # /app/lib/surface
+ "camera-dragged":
+ {} # TODO schema
+
+ "camera-zoom-in":
+ {} # TODO schema
+
+ "camera-zoom-out":
+ {} # TODO schema
+
+ "camera-zoom-to":
+ {} # TODO schema
+
+ "camera:zoom-updated":
+ {} # TODO schema
+
+ "sprite:speech-updated":
+ {} # TODO schema
+
+ "dialogue-sound-completed":
+ {} # TODO schema
+
+ "surface:gold-changed":
+ {} # TODO schema
+
+ "surface:coordinate-selected":
+ {} # TODO schema
+
+ "surface:coordinates-shown":
+ {} # TODO schema
+
+ "level-sprite-clear-dialogue":
+ {} # TODO schema
+
+ "sprite:loaded":
+ {} # TODO schema
+
+ "choose-point":
+ {} # TODO schema
+
+ "choose-region":
+ {} # TODO schema
+
+ "surface:new-thang-added":
+ {} # TODO schema
+
+ "surface:sprite-selected":
+ {} # TODO schema
+
+ "thang-began-talking":
+ {} # TODO schema
+
+ "thang-finished-talking":
+ {} # TODO schema
+
+ "surface:world-set-up":
+ {} # TODO schema
+
+ "surface:frame-changed":
+ {} # TODO schema
+
+ "surface:playback-ended":
+ {} # TODO schema
+
+ "surface:playback-restarted":
+ {} # TODO schema
+
+ "level-set-playing":
+ {} # TODO schema
+
+ "registrar-echo-states":
+ {} # TODO schema
+
+ "surface:mouse-moved":
+ {} # TODO schema
+
+ "surface:stage-mouse-down":
+ {} # TODO schema
+
+ "surface:mouse-scrolled":
+ {} # TODO schema
+
+ "surface:ticked":
+ {} # TODO schema
+
+ "surface:mouse-over":
+ {} # TODO schema
+
+ "surface:mouse-out":
+ {} # TODO schema
+
+ "self-wizard:target-changed":
+ {} # TODO schema
+
+ "echo-all-wizard-sprites":
+ {} # TODO schema
diff --git a/app/schemas/subscriptions/tome.coffee b/app/schemas/subscriptions/tome.coffee
new file mode 100644
index 000000000..7c6a5a11f
--- /dev/null
+++ b/app/schemas/subscriptions/tome.coffee
@@ -0,0 +1,73 @@
+module.exports =
+ "tome:cast-spell":
+ {} # TODO schema
+
+ # TODO do we really need both 'cast-spell' and 'cast-spells'?
+ "tome:cast-spells":
+ {} # TODO schema
+
+ "tome:manual-cast":
+ {} # TODO schema
+
+ "tome:spell-created":
+ {} # TODO schema
+
+ "tome:spell-debug-property-hovered":
+ {} # TODO schema
+
+ "tome:toggle-spell-list":
+ {} # TODO schema
+
+ "tome:reload-code":
+ {} # TODO schema
+
+ "tome:palette-hovered":
+ {} # TODO schema
+
+ "tome:palette-pin-toggled":
+ {} # TODO schema
+
+ "tome:palette-clicked":
+ {} # TODO schema
+
+ "tome:spell-statement-index-updated":
+ {} # TODO schema
+
+ # TODO proposition: refactor 'tome' into spell events
+ "spell-beautify":
+ {} # TODO schema
+
+ "spell-step-forward":
+ {} # TODO schema
+
+ "spell-step-backward":
+ {} # TODO schema
+
+ "tome:spell-loaded":
+ {} # TODO schema
+
+ "tome:cast-spell":
+ {} # TODO schema
+
+ "tome:spell-changed":
+ {} # TODO schema
+
+ "tome:editing-ended":
+ {} # TODO schema
+
+ "tome:editing-began":
+ {} # TODO schema
+
+ "tome:problems-updated":
+ {} # TODO schema
+
+ "tome:thang-list-entry-popover-shown":
+ {} # TODO schema
+
+ "tome:spell-shown":
+ {} # TODO schema
+
+ # TODO proposition: add tome to name
+ "focus-editor":
+ {} # TODO schema
+
diff --git a/app/schemas/subscriptions/user.coffee b/app/schemas/subscriptions/user.coffee
new file mode 100644
index 000000000..44e713777
--- /dev/null
+++ b/app/schemas/subscriptions/user.coffee
@@ -0,0 +1,9 @@
+module.exports =
+ "me:synced":
+ {} # TODO schema
+
+ "user-fetched":
+ {} # TODO schema
+
+ "edit-wizard-settings":
+ {} # TODO schema
diff --git a/app/schemas/subscriptions/world.coffee b/app/schemas/subscriptions/world.coffee
new file mode 100644
index 000000000..d5e953de4
--- /dev/null
+++ b/app/schemas/subscriptions/world.coffee
@@ -0,0 +1,15 @@
+module.exports =
+ "god:user-code-problem":
+ {} # TODO schema
+
+ "god:infinite-loop":
+ {} # TODO schema
+
+ "god:user-code-problem":
+ {} # TODO schema
+
+ "god:new-world-created":
+ {} # TODO schema
+
+ "god:world-load-progress-changed":
+ {} # TODO schema
\ No newline at end of file
diff --git a/app/styles/account/profile.sass b/app/styles/account/profile.sass
index 2edec8f24..0d0f4e450 100644
--- a/app/styles/account/profile.sass
+++ b/app/styles/account/profile.sass
@@ -1,15 +1,195 @@
#profile-view
- button
- float: right
- i
- margin-right: 5px
-
- img.img-thumbnail
- margin: 20px 0
+ .profile-control-bar
+ background-color: rgb(78, 78, 78)
+ width: 100%
+ text-align: center
+
+ button.edit-settings-button
+ margin: 2px
+ i
+ margin-right: 5px
- li
- list-style: none
-
- ul
- margin: 0
+ .approved, .not-approved
+ display: none
+
+ .main-content-area
padding: 0
+
+ .flat-button
+ width: 100%
+ margin-bottom: 10px
+ background: rgb(78, 78, 78)
+ border: 0
+ border-radius: 0
+ padding: 10px
+
+ .public-profile-container
+ padding: 20px
+
+ img.profile-photo
+ width: 256px
+ border-radius: 6px
+
+ .job-profile-container
+ width: 100%
+ height: 100%
+ padding: 0
+ display: table
+
+ h1, h2, h3, h4, h5, h6
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif
+ color: #555
+
+ ul.links, ul.projects
+ margin: 0
+ padding: 0
+
+ li
+ list-style: none
+
+ .job-profile-row
+ height: 100%
+ display: table-row
+
+ .full-height-column
+ height: 100%
+ padding: 5px
+ display: table-cell
+ vertical-align: top
+
+ h3:first-child
+ margin: 5px 0 5px 0
+
+ .left-column
+ width: 250px
+ padding: 5px
+ background-color: rgb(220, 220, 220)
+
+ .profile-photo-container
+ position: relative
+ margin-bottom: 10px
+
+ img.profile-photo
+ width: 240px
+ border-radius: 6px
+
+ .profile-caption
+ background-color: rgba(0, 0, 0, 0.5)
+ color: white
+ border-bottom-right-radius: 6px
+ border-bottom-left-radius: 6px
+ position: absolute
+ width: 100%
+ bottom: 0px
+ text-align: center
+
+ ul.links
+ li.has-icon
+ display: inline-block
+ img
+ margin: 0 0 10px 0
+ li.has-icon:not(:nth-child(5))
+ img
+ margin: 0 10px 10px 0
+
+ #contact-candidate
+ margin-top: 20px
+ background-color: rgb(177, 55, 25)
+ padding: 15px
+ font-size: 20px
+
+ .middle-column
+ width: 524px
+ background-color: white
+ padding-left: 20px
+ padding-right: 20px
+
+ &.double-column
+ width: 524px + 250px
+ padding-left: 30px
+ padding-right: 30px
+
+ code
+ background-color: rgb(220, 220, 220)
+ color: #555
+ margin: 2px 0
+ display: inline-block
+ text-transform: lowercase
+
+ .long-description
+ margin-top: 10px
+ img
+ max-width: 524px - 60px
+ max-height: 200px
+
+ .experience-header
+ margin-top: 25px
+
+ .header-icon
+ margin-right: 10px
+ width: 32px
+ height: 32px
+
+ .duration
+ margin-left: 10px
+ margin-bottom: 10px
+
+ #job-profile-notes
+ width: 100%
+ height: 100px
+
+ .right-column
+ width: 250px
+ background-color: rgb(220, 220, 220)
+
+ > h3:first-child
+ background-color: white
+ padding: 5px 5px
+ margin: 5px 2px 5px 2px
+
+ ul.projects
+ li
+ margin-bottom: 10px
+ padding: 5px 5px
+ border: 2px solid rgb(220, 220, 220)
+ transition: .5s ease-in-out
+ position: relative
+ background-color: white
+
+ &:hover
+ border-color: rgb(100, 130, 255)
+
+ a
+ position: relative
+ z-index: 2
+
+ > a
+ position: absolute
+ width: 100%
+ height: 100%
+ top: 0
+ left: 0
+ z-index: 1
+
+ .project-image
+ width: 230px
+ height: 115px
+ background-size: cover
+ background-repeat: no-repeat
+ background-position: center
+
+ -webkit-filter: grayscale(100%)
+ -webkit-transition: .5s ease-in-out
+ -moz-filter: grayscale(100%)
+ -moz-transition: .5s ease-in-out
+ -o-filter: grayscale(100%)
+ -o-transition: .5s ease-in-out
+ filter: grayscale(100%)
+ transition: .5s ease-in-out
+
+ li:hover
+ .project-image
+ -webkit-filter: grayscale(0%)
+ -moz-filter: grayscale(0%)
+ -o-filter: grayscale(0%)
+ filter: grayscale(0%)
diff --git a/app/styles/account/settings.sass b/app/styles/account/settings.sass
index 8751e59ef..b768d69a2 100644
--- a/app/styles/account/settings.sass
+++ b/app/styles/account/settings.sass
@@ -8,15 +8,20 @@
background: #eee
border-radius: 5px
- #save-button
- float: right
+ #save-button-container
+ position: fixed
+ top: 100px
+ width: 1000px
+ z-index: 10
- .thumbnails
- text-align: center
- .thumbnail
- margin-bottom: 30px
- margin-right: 20px
- float: left
+ #save-button
+ float: right
+
+ &.btn-info, &.btn-danger
+ opacity: 1.0
+
+ .gravatar-fallback
+ margin-top: 10px
input.range
position: relative
@@ -37,4 +42,15 @@
font-size: 12px
.form
- max-width: 600px
\ No newline at end of file
+ max-width: 600px
+
+ #job-profile-treema
+ background-color: white
+
+ input
+ width: 790px
+
+ .treema-description
+ font-size: 14px
+ line-height: 22px
+ opacity: 1
diff --git a/app/styles/base.sass b/app/styles/base.sass
index 30980c1fb..0425ba2d6 100644
--- a/app/styles/base.sass
+++ b/app/styles/base.sass
@@ -5,9 +5,8 @@
html
background-color: #2f261d
-html, body
- // For level loading view wings
- overflow-x: hidden
+body
+ position: absolute !important
// https://github.com/twbs/bootstrap/issues/9237 -- need a version that's not !important
.secret
@@ -49,7 +48,6 @@ h1 h2 h3 h4
margin: 0 auto
.footer
- height: 75px
border-top: 1px solid black
background-color: #2f261d
p
@@ -103,10 +101,19 @@ a[data-toggle="modal"]
.modal-dialog
padding: 5px
- background: transparent url(/images/pages/base/modal_background.png)
- background-size: 100% 100%
- border: 0
- @include box-shadow(0 0 0 #000)
+ margin-top: 0px !important
+ margin-bottom: 0px !important
+ padding-top: 30px
+ .background-wrapper
+ background: url("/images/pages/base/modal_background.png")
+ background-size: 100% 100%
+ border: 0
+ @include box-shadow(0 0 0 #000)
+ //position: absolute
+ width: 99%
+
+ .background-wrapper.plain
+ background: white
.modal-content
@include box-shadow(none)
@@ -178,6 +185,7 @@ a[data-toggle="modal"]
margin-left: 10px
.modal
+ overflow-y: auto !important
.wait
h3
text-align: center
@@ -207,7 +215,7 @@ table.table
.header-font
font-family: $headings-font-family
-body[lang='ru'], body[lang|='zh'], body[lang='ja'], body[lang='pl'], body[lang='tr'], body[lang='cs'], body[lang='el'], body[lang='ro'], body[lang='vi'], body[lang='th'], body[lang='ko'], body[lang='sk'], body[lang='sl'], body[lang='bg'], body[lang='he'], body[lang='lt'], body[lang='sr'], body[lang='uk'], body[lang='hi'], body[lang='ur'],
+body[lang='ru'], body[lang|='zh'], body[lang='pl'], body[lang='tr'], body[lang='cs'], body[lang='el'], body[lang='ro'], body[lang='vi'], body[lang='th'], body[lang='ko'], body[lang='sk'], body[lang='sl'], body[lang='bg'], body[lang='he'], body[lang='lt'], body[lang='sr'], body[lang='uk'], body[lang='hi'], body[lang='ur'], body[lang='hu']
h1, h2, h3, h4, h5, h6
font-family: 'Open Sans Condensed', Impact, "Arial Narrow", "Arial", sans-serif
text-transform: uppercase
@@ -217,6 +225,23 @@ body[lang='ru'], body[lang|='zh'], body[lang='ja'], body[lang='pl'], body[lang='
font-family: 'Open Sans Condensed', Impact, "Arial Narrow", "Arial", sans-serif !important
text-transform: uppercase
letter-spacing: -1px !important
+
+body[lang='ja']
+ h1, h2, h3, h4, h5, h6
+ font-family: "ヒラギノ角ゴ Pro W3", "Hiragino Kaku Gothic Pro", Osaka, "メイリオ", Meiryo, "ï¼ï¼³ Pゴシック", "MS PGothic", 'Open Sans Condensed', sans-serif
+ text-transform: uppercase
+ letter-spacing: -1px !important
+
+ .header-font
+ font-family: "ヒラギノ角ゴ Pro W3", "Hiragino Kaku Gothic Pro", Osaka, "メイリオ", Meiryo, "ï¼ï¼³ Pゴシック", "MS PGothic", 'Open Sans Condensed', sans-serif
+ text-transform: uppercase
+ letter-spacing: -1px !important
+
+ #top-nav
+ .navbar-nav
+ li
+ a.header-font
+ font-size: 16px
@media only screen and (max-width: 800px)
.main-content-area
@@ -233,3 +258,10 @@ body[lang='ru'], body[lang|='zh'], body[lang='ja'], body[lang='pl'], body[lang='
margin-bottom: 20px
.partner-badges
display: none
+
+// point the new glyphicons to the fonts in public
+
+@font-face
+ font-family: 'Glyphicons Halflings'
+ src: url("/fonts/glyphicons-halflings-regular.eot")
+ src: url("/fonts/glyphicons-halflings-regular.eot?#iefix") format("embedded-opentype"), url("/fonts/glyphicons-halflings-regular.woff") format("woff"), url("/fonts/glyphicons-halflings-regular.ttf") format("truetype"), url("/fonts/glyphicons-halflings-regular.svg#glyphicons-halflingsregular") format("svg")
diff --git a/app/styles/common/top_nav.sass b/app/styles/common/top_nav.sass
index 9a41771fe..f5c61685e 100644
--- a/app/styles/common/top_nav.sass
+++ b/app/styles/common/top_nav.sass
@@ -11,7 +11,7 @@
letter-spacing: 1px
.navbuttontext-user-name
- max-width: 125px
+ max-width: 110px
overflow: hidden
text-overflow: ellipsis
white-space: nowrap
diff --git a/app/styles/editor/delta.sass b/app/styles/editor/delta.sass
new file mode 100644
index 000000000..013478efb
--- /dev/null
+++ b/app/styles/editor/delta.sass
@@ -0,0 +1,43 @@
+.delta-view
+ .panel-heading
+ font-size: 13px
+ padding: 4px
+ .row
+ padding: 5px 10px
+
+ .delta-added
+ border-color: green
+ > .panel-heading
+ background-color: lighten(green, 70%)
+ strong
+ color: green
+
+ .delta-modified
+ border-color: darkgoldenrod
+ > .panel-heading
+ background-color: lighten(darkgoldenrod, 40%)
+ strong
+ color: darkgoldenrod
+
+ .delta-text-diff
+ border-color: blue
+ > .panel-heading
+ background-color: lighten(blue, 45%)
+ strong
+ color: blue
+ table
+ width: 100%
+
+ .delta-deleted
+ border-color: red
+ > .panel-heading
+ background-color: lighten(red, 42%)
+ strong
+ color: red
+
+ .delta-moved-index
+ border-color: darkslategray
+ > .panel-heading
+ background-color: lighten(darkslategray, 60%)
+ strong
+ color: darkslategray
diff --git a/app/styles/editor/patch.sass b/app/styles/editor/patch.sass
new file mode 100644
index 000000000..3296d946c
--- /dev/null
+++ b/app/styles/editor/patch.sass
@@ -0,0 +1,3 @@
+#patch-modal
+ .modal-body
+ padding: 10px
\ No newline at end of file
diff --git a/app/styles/editor/patches.sass b/app/styles/editor/patches.sass
new file mode 100644
index 000000000..87c22728e
--- /dev/null
+++ b/app/styles/editor/patches.sass
@@ -0,0 +1,3 @@
+.patches-view
+ .status-buttons
+ margin: 10px 0
diff --git a/app/styles/employers.sass b/app/styles/employers.sass
new file mode 100644
index 000000000..6e64b44a0
--- /dev/null
+++ b/app/styles/employers.sass
@@ -0,0 +1,29 @@
+#employers-view
+ .tablesorter
+ //img
+ // display: none
+
+ .tablesorter-header
+ cursor: pointer
+ &:hover
+ color: black
+
+ &:first-child
+ // Make sure that "Developer #56" doesn't wrap onto second row
+ min-width: 110px
+
+ .tablesorter-headerAsc
+ background-color: #cfc
+
+ .tablesorter-headerDesc
+ background-color: #ccf
+
+ tr
+ cursor: pointer
+
+ code
+ background-color: rgb(220, 220, 220)
+ color: #555
+ margin: 2px 0
+ display: inline-block
+ text-transform: lowercase
diff --git a/app/styles/modal/login.sass b/app/styles/modal/login.sass
index e89a92118..7d85ad900 100644
--- a/app/styles/modal/login.sass
+++ b/app/styles/modal/login.sass
@@ -10,3 +10,16 @@
a[data-toggle="coco-modal"]
cursor: pointer
+
+#signup-modal
+ .modal-footer
+ padding-top: 0
+ div
+ text-align: center
+ .social-login-text
+ padding-top: 20px
+ .network-logins
+ width: 263px
+ margin: 0 auto
+ div:last-of-type
+ margin-right: 0px
diff --git a/app/styles/modal/save_version.sass b/app/styles/modal/save_version.sass
index 9af4225dc..66de28a29 100644
--- a/app/styles/modal/save_version.sass
+++ b/app/styles/modal/save_version.sass
@@ -1,4 +1,12 @@
#save-version-modal
+ .modal-body
+ padding: 10px 50px 30px 20px
+
+ .modal-footer
+ text-align: left
+ .buttons
+ text-align: right
+
#cla-link
cursor: pointer
text-decoration: underline
@@ -25,3 +33,23 @@
font-size: 0.9em
font-style: italic
+ .delta-view
+ overflow-y: auto
+ padding: 10px
+ border: 1px solid black
+ background: lighten(#add8e6, 17%)
+ margin-bottom: 10px
+ ul
+ padding-left: 20px
+
+ form
+ width: 100%
+
+ .commit-message
+ display: block
+ width: 100%
+
+ .checkbox
+ margin: 10px 10px 0
+ input
+ margin-right: 5px
\ No newline at end of file
diff --git a/app/styles/play/level.sass b/app/styles/play/level.sass
index d051e9f43..756e56af7 100644
--- a/app/styles/play/level.sass
+++ b/app/styles/play/level.sass
@@ -1,6 +1,9 @@
@import "app/styles/bootstrap/mixins"
@import "app/styles/mixins"
+body.is-playing
+ background-color: black
+
#level-view
margin: 0 auto
@include user-select(none)
diff --git a/app/styles/play/level/loading.sass b/app/styles/play/level/loading.sass
index 177334f72..3dac90ec8 100644
--- a/app/styles/play/level/loading.sass
+++ b/app/styles/play/level/loading.sass
@@ -1,16 +1,9 @@
@import "app/styles/bootstrap/mixins"
@import "app/styles/mixins"
-@mixin sky-background($url: '', $backgroundPosition: left)
- $top: #95D9EF
- $mid: #FFFFFF
- $bot: #8EC643
- $stop: 99.6%
- background: $mid
- background-image: url($url) // fallback
- background-image: url($url), -webkit-linear-gradient(top, $top, $mid $stop, $bot)
- background-image: url($url), -ms-linear-gradient(top, $top, $mid $stop, $bot)
- background-image: url($url), linear-gradient(to bottom, $top, $mid $stop, $bot)
+@mixin wing-background($url: '', $backgroundPosition: left)
+ background: black
+ background-image: url($url)
background-repeat: no-repeat
background-position: top $backgroundPosition
background-size: contain
@@ -22,7 +15,9 @@
position: absolute
z-index: 20
$UNVEIL_TIME: 1.2s
- pointer-events: none
+
+ &.unveiled
+ pointer-events: none
.loading-details
position: absolute
@@ -67,11 +62,11 @@
position: absolute
.left-wing
- @include sky-background('/images/level/loading_left_wing.png', right)
+ @include wing-background('/images/level/loading_left_wing.png', right)
left: -50%
transition: all $UNVEIL_TIME ease
.right-wing
- @include sky-background('/images/level/loading_right_wing.png', left)
+ @include wing-background('/images/level/loading_right_wing.png', left)
right: -50%
transition: all $UNVEIL_TIME ease
diff --git a/app/styles/play/level/tome/spell_palette.sass b/app/styles/play/level/tome/spell_palette.sass
index e941f8def..52ce37658 100644
--- a/app/styles/play/level/tome/spell_palette.sass
+++ b/app/styles/play/level/tome/spell_palette.sass
@@ -15,7 +15,7 @@
background-color: transparent
background-size: 100% 100%
z-index: 0
- overflow-y: auto
+ //overflow-y: auto
img
position: absolute
@@ -47,6 +47,12 @@
&.multiple-tabs li:not(.active) a
cursor: pointer
+ .tab-content
+ height: 80px
+ .nano-pane
+ width: 7px
+ right: 5px
+
//.nav-pills > li.active > a, .nav-pills > li.active > a:hover, .nav-pills > li.active > a:focus
// background-color: lighten(rgb(230, 212, 146), 10%)
diff --git a/app/styles/play/spectate.sass b/app/styles/play/spectate.sass
index f04075b79..2fd8cf684 100644
--- a/app/styles/play/spectate.sass
+++ b/app/styles/play/spectate.sass
@@ -31,6 +31,7 @@
max-height: 1284px
.level-content
+ //max-width: 1920px
position: relative
margin: 0px auto
diff --git a/app/templates/account/job_profile.jade b/app/templates/account/job_profile.jade
new file mode 100644
index 000000000..491ea8b9c
--- /dev/null
+++ b/app/templates/account/job_profile.jade
@@ -0,0 +1,8 @@
+h3(data-i18n="account_settings.job_profile") Job Profile
+
+if me.get('jobProfileApproved')
+ p.lead(data-i18n="account_settings.job_profile_approved") Your job profile has been approved by CodeCombat. Employers will be able to see it until you either mark it inactive or it has not been changed for four weeks.
+else
+ p.lead(data-i18n="account_settings.job_profile_explanation") Hi! Fill this out, and we will get in touch about finding you a software developer job.
+
+#job-profile-treema
\ No newline at end of file
diff --git a/app/templates/account/profile.jade b/app/templates/account/profile.jade
index 65dc9786b..6fee4c8af 100644
--- a/app/templates/account/profile.jade
+++ b/app/templates/account/profile.jade
@@ -1,72 +1,106 @@
extends /templates/base
block content
+
+ if myProfile || (me.isAdmin() && user.get('jobProfile'))
+ .profile-control-bar
+ if myProfile
+ a(href="/account/settings")
+ button.btn.edit-settings-button
+ i.icon-cog
+ span(data-i18n="account_profile.edit_settings") Edit Settings
+ if me.isAdmin() && user.get('jobProfile')
+ button.btn.edit-settings-button#toggle-job-profile-approved
+ i.icon-cog
+ span(data-i18n='account_profile.approved').approved Approved
+ span(data-i18n='account_profile.approved').not-approved Not Approved
- if myProfile
- a(href="/account/settings")
- button.btn
- i.icon-cog
- span(data-i18n="account_profile.edit_settings") Edit Settings
-
- h2
- if grav && grav.name && grav.name.formatted
- span(data-i18n="account_profile.profile_for_prefix") Profile for
- span= grav.name.formatted
- span(data-i18n="account_profile.profile_for_suffix")
- else
- span(data-i18n="account_profile.profile") Profile
+ if user.get('jobProfile')
+ - var profile = user.get('jobProfile');
+ .job-profile-container
+ .job-profile-row
+ .left-column.full-height-column
+ .profile-photo-container
+ img.profile-photo(src=user.getPhotoURL(240, true))
+ .profile-caption= profile.jobTitle || 'Software Developer'
- if loadingProfile
- p(data-i18n="common.loading") Loading...
+ if profileLinks.length
+ ul.links
+ each link in profileLinks
+ li(title=profile.name + " on " + link.name, class=link.icon ? "has-icon" : "")
+ a(href=link.link)
+ if link.icon
+ img(src=link.icon.url, alt=link.icon.name)
+ else
+ button.btn.btn-large.btn-inverse.flat-button= link.name
- else if !user.get('emailHash')
- p(data-i18n="account_profile.user_not_found") No user found. Check the URL?
+ div= profile.city + ', ' + profile.country
+ div= profile.visa
+ div
+ span(data-i18n="account_profile.looking_for") Looking for:
+ | #{profile.lookingFor}
+ div
+ span(data-i18n="account_profile.last_updated") Last updated:
+ | #{moment(profile.updated).fromNow()}
- else if !user.gravatarProfile
- if myProfile
- p
- span(data-i18n="account_profile.gravatar_not_found_mine") We couldn't find your profile associated with:
- strong "#{me.get('email')}"
- span(data-i18n="account_profile.gravatar_not_found_email_suffix") .
- span
- span(data-i18n="account_profile.gravatar_signup_prefix") Sign up at
- a(href="http://en.gravatar.com/") Gravatar
- span(data-i18n="account_profile.gravatar_signup_suffix") to get set up!
- else
- p(data-i18n="account_profile.gravatar_not_found_other")
- | Alas, there's no profile associated with this person's email address.
+ button#contact-candidate.btn.btn-large.btn-inverse.flat-button
+ span(data-i18n="account_profile.contact") Contact
+ | #{profile.name.split(' ')[0]}
+
+ .middle-column.full-height-column
+ h3= profile.name
+ p= profile.shortDescription
+
+ each skill in profile.skills
+ code= skill
+ span
+ div.long-description!= marked(profile.longDescription)
+
+ if profile.work.length
+ h3.experience-header
+ img.header-icon(src="/images/pages/account/profile/work.png", alt="")
+ span(data-i18n="account_profile.work_experience") Work Experience
+ each job in profile.work
+ div.duration.pull-right= job.duration
+ | #{job.role} at #{job.employer}
+ .clearfix
+
+ if profile.education.length
+ h3.experience-header
+ img.header-icon(src="/images/pages/account/profile/education.png", alt="")
+ span(data-i18n="account_profile.work_experience") Education
+ each school in profile.education
+ div.duration.pull-right= school.duration
+ | #{school.degree} at #{school.school}
+ .clearfix
+
+ if user.get('jobProfileNotes') || me.isAdmin()
+ h3.experience-header(data-i18n="account_profile.our_notes") Our Notes
+ - var notes = user.get('jobProfileNotes') || '';
+ if me.isAdmin()
+ textarea#job-profile-notes!= notes
+ else
+ div!= marked(notes)
+
+ .right-column.full-height-column
+ if profile.projects.length
+ h3(data-i18n="account_profile.projects") Projects
+ ul.projects
+ each project in profile.projects
+ li
+ a(href=project.link)
+ .project-image(style="background-image: url(/file/" + project.picture + ")")
+ p= project.name
+ div!= marked(project.description)
else
- .container
- div.row
- div.col-xs-3
- img(src=photoURL).img-thumbnail
-
- p.about-me #{grav.aboutMe}
+ .public-profile-container
+ h2
+ span(data-i18n="account_profile.profile_for_prefix") Profile for
+ span= user.get('name')
+ span(data-i18n="account_profile.profile_for_suffix")
- if grav.emails
- div.col-xs-3
- h3(data-i18n="account_profile.gravatar_contact") Contact
- ul
- each email in grav.emails
- li #{email.value}
-
- if grav.urls && grav.urls.length
- div.col-xs-3
- h3(data-i18n="account_profile.gravatar_websites") Websites
- ul
- each url in grav.urls
- li
- a(href="#{url.value}") #{url.title}
-
- if grav.accounts
- div.col-xs-3
- h3(data-i18n="account_profile.gravatar_accounts") As Seen On
- ul
- each account in grav.accounts
- li
- a(href="#{account.url}") #{account.domain}
-
- hr
- p
- a(href="#{grav.profileUrl}", data-i18n="account_profile.gravatar_profile_link") Full Gravatar Profile
+ img.profile-photo(src=user.getPhotoURL(256))
+
+ h2 TODO
+ p Public user profiles are not ready yet. If you are seeing this, we probably have a bug leading to a broken link.
\ No newline at end of file
diff --git a/app/templates/account/settings.jade b/app/templates/account/settings.jade
index 91b533b1b..a1e829b7e 100644
--- a/app/templates/account/settings.jade
+++ b/app/templates/account/settings.jade
@@ -8,7 +8,8 @@ block content
p(data-i18n="account_settings.not_logged_in") Log in or create an account to change your settings.
else
- button.btn#save-button.disabled.secret(data-i18n="account_settings.autosave") Changes Save Automatically
+ #save-button-container
+ button.btn#save-button.disabled.secret(data-i18n="account_settings.autosave") Changes Save Automatically
ul.nav.nav-pills#settings-tabs
li
@@ -21,6 +22,9 @@ block content
a(href="#password-pane", data-toggle="tab", data-i18n="account_settings.password_tab") Password
li
a(href="#email-pane", data-toggle="tab", data-i18n="account_settings.emails_tab") Emails
+ if showsJobProfileTab
+ li
+ a(href="#job-profile-pane", data-toggle="tab", data-i18n="account_settings.job_profile_tab") Job Profile
.tab-content#settings-panes
#general-pane.tab-pane
@@ -28,7 +32,7 @@ block content
.form
.form-group
label.control-label(for="name", data-i18n="general.name") Name
- input#name.form-control(name="name", type="text", value="#{me.get('name')||''}", placeholder="#{gravatarName}")
+ input#name.form-control(name="name", type="text", value="#{me.get('name') || ''}")
.form-group
label.control-label(for="email", data-i18n="general.email") Email
input#email.form-control(name="email", type="text", value="#{me.get('email')}")
@@ -39,23 +43,11 @@ block content
#picture-pane.tab-pane
- h3(data-i18n="account_settings.gravatar_select") Select which Gravatar photo to use
- p
- if !photos
- span(data-i18n="account_settings.gravatar_add_photos") Add thumbnails and photos to a Gravatar account for your email to choose an image.
-
- else
- .thumbnails
- each photo, i in photos
- .thumbnail
- label(for="photo-#{i}")
- img(src=photo)
- br
- input(type="radio", name="photoURL", value="#{photo}", id="photo-#{i}", checked=photo==chosenPhoto)
- .clearfix
- p
- a(href="http://en.gravatar.com/profiles/edit/?noclose#your-images", target="_blank", data-i18n="account_settings.gravatar_add_more_photos") Add more photos to your Gravatar account to access them here.
-
+ h3(data-i18n="account_settings.upload_picture") Upload a picture
+ #picture-treema
+ .gravatar-fallback
+ img(src=me.getPhotoURL(256), alt="Gravatar", title="Gravatar fallback image")
+
#wizard-pane.tab-pane
#wizard-settings-view
@@ -153,3 +145,6 @@ block content
span(data-i18n="contribute.ambassador_subscribe_desc").help-block Get emails on support updates and multiplayer developments.
button.btn#toggle-all-button(data-i18n="account_settings.email_toggle") Toggle All
+
+ #job-profile-pane.tab-pane
+ #job-profile-view
diff --git a/app/templates/account/wizard_settings.jade b/app/templates/account/wizard_settings.jade
index 95684394c..f0c95410e 100644
--- a/app/templates/account/wizard_settings.jade
+++ b/app/templates/account/wizard_settings.jade
@@ -1,9 +1,9 @@
#color-settings
table.table.table-bordered
tr
- th
- th Color
- th Group
+ th(data-i18n="wizard_settings.active") Active
+ th(data-i18n="wizard_settings.color") Color
+ th(data-i18n="wizard_settings.group") Group
for group in colorGroups
tr.color-group(data-name=group.name)
td.enabled-cell
diff --git a/app/templates/base.jade b/app/templates/base.jade
index 3aec9624e..96d184876 100644
--- a/app/templates/base.jade
+++ b/app/templates/base.jade
@@ -33,7 +33,7 @@ body
if me.get('anonymous') === false
button.btn.btn-primary.navbuttontext.header-font#logout-button(data-i18n="login.log_out") Log Out
- a.btn.btn-primary.navbuttontext.header-font(href="/account/profile/#{me.id}")
+ a.btn.btn-primary.navbuttontext.header-font(href=me.get('jobProfile') ? "/account/profile/#{me.id}" : "/account/settings")
div.navbuttontext-user-name
| #{me.displayName()}
i.icon-cog.icon-white.big
@@ -51,12 +51,6 @@ body
a.header-font(href='http://blog.codecombat.com/', data-i18n="nav.blog") Blog
li.forum
a.header-font(href='http://discourse.codecombat.com/', data-i18n="nav.forum") Forum
- if me.isAdmin()
- li.admin
- a.header-font(href='/admin', data-i18n="nav.admin") Admin
-
-
-
block outer_content
@@ -68,7 +62,7 @@ body
p If this is showing, you dun goofed
block footer
- .footer
+ .footer.clearfix
.content
p.footer-link-text
if pathname == "/"
@@ -79,6 +73,8 @@ body
a(href='/legal', title='Legal', tabindex=-1, data-i18n="nav.legal") Legal
a(href='/about', title='About', tabindex=-1, data-i18n="nav.about") About
a(title='Contact', tabindex=-1, data-toggle="coco-modal", data-target="modal/contact", data-i18n="nav.contact") Contact
+ if me.isAdmin()
+ a(href='/admin', data-i18n="nav.admin") Admin
.share-buttons
.g-plusone(data-href="http://codecombat.com", data-size="medium")
diff --git a/app/templates/editor/article/edit.jade b/app/templates/editor/article/edit.jade
index 4969e30e9..d61460e60 100644
--- a/app/templates/editor/article/edit.jade
+++ b/app/templates/editor/article/edit.jade
@@ -10,10 +10,10 @@ block content
li.active
| #{article.attributes.name}
- button(data-i18n="general.history").btn.btn-primary#history-button History
+ button(data-i18n="general.version_history").btn.btn-primary#history-button Version History
button(data-toggle="coco-modal", data-target="modal/revert", data-i18n="editor.revert", disabled=authorized === true ? undefined : "true").btn.btn-primary#revert-button Revert
button(data-i18n="article.edit_btn_preview", disabled=authorized === true ? undefined : "true").btn.btn-primary#preview-button Preview
- button(data-toggle="coco-modal", data-target="modal/save_version", data-i18n="common.save", disabled=authorized === true ? undefined : "true").btn.btn-primary#save-button Save
+ button(data-i18n="common.save", disabled=authorized === true ? undefined : "true").btn.btn-primary#save-button Save
h3(data-i18n="article.edit_article_title") Edit Article
span
diff --git a/app/templates/editor/delta.jade b/app/templates/editor/delta.jade
new file mode 100644
index 000000000..480e4ef01
--- /dev/null
+++ b/app/templates/editor/delta.jade
@@ -0,0 +1,46 @@
+- var i = 0
+
+mixin deltaPanel(delta, conflict)
+ - delta.index = i++
+ .delta.panel.panel-default(class='delta-'+delta.action, data-index=i)
+ .panel-heading
+ if delta.action === 'added'
+ strong(data-i18n="delta.added") Added
+ if delta.action === 'modified'
+ strong(data-i18n="delta.modified") Modified
+ if delta.action === 'deleted'
+ strong(data-i18n="delta.deleted") Deleted
+ if delta.action === 'moved-index'
+ strong(data-i18n="delta.modified_array") Moved Index
+ if delta.action === 'text-diff'
+ strong(data-i18n="delta.text_diff") Text Diff
+ span
+ a(data-toggle="collapse" data-parent="#delta-accordion"+(counter) href="#collapse-"+(i+counter))
+ span= delta.humanPath
+
+ .panel-collapse.collapse(id="collapse-"+(i+counter))
+ .panel-body.row(class=conflict ? "conflict-details" : "details")
+ if delta.action === 'added'
+ .new-value.col-md-12= delta.right
+ if delta.action === 'modified'
+ .old-value.col-md-6= delta.left
+ .new-value.col-md-6= delta.right
+ if delta.action === 'deleted'
+ .col-md-12
+ div.old-value= delta.left
+ if delta.action === 'text-diff'
+ .col-md-12
+ div.text-diff
+ if delta.action === 'moved-index'
+ .col-md-12
+ span Moved array value #{JSON.stringify(delta.left)} to index #{delta.destinationIndex}
+
+ if delta.conflict && !conflict
+ .panel-body
+ strong MERGE CONFLICT WITH
+ +deltaPanel(delta.conflict, true)
+
+.panel-group(id='delta-accordion-'+(counter))
+ for delta in deltas
+ +deltaPanel(delta)
+
\ No newline at end of file
diff --git a/app/templates/editor/level/component/edit.jade b/app/templates/editor/level/component/edit.jade
index 44d368080..23c24cf6c 100644
--- a/app/templates/editor/level/component/edit.jade
+++ b/app/templates/editor/level/component/edit.jade
@@ -15,7 +15,7 @@ nav.navbar.navbar-default(role='navigation')
li
a(href="#component-settings" data-toggle="tab" data-i18n="editor.level_component_settings") Settings
ul.nav.navbar-nav.navbar-left
- li(data-i18n="general.history").btn.btn-primary.navbar-btn#history-button History
+ li(data-i18n="general.version_history").btn.btn-primary.navbar-btn#history-button Version History
ul.nav.navbar-nav.navbar-right
li(data-i18n="editor.level_component_btn_new").btn.btn-primary.navbar-btn#create-new-component-button Create New Component
diff --git a/app/templates/editor/level/edit.jade b/app/templates/editor/level/edit.jade
index f90485b49..ba69b27fd 100644
--- a/app/templates/editor/level/edit.jade
+++ b/app/templates/editor/level/edit.jade
@@ -7,7 +7,7 @@ block outer_content
.container-fluid
ul.nav.navbar-nav
li
- a(href="/editor/level") Back
+ a(href="/editor/level", data-i18n="editor.back") Back
.navbar-header
span.navbar-brand
span(data-i18n="editor.level_title") Level Editor
@@ -26,29 +26,34 @@ block outer_content
a(href="#editor-level-components-tab-view", data-toggle="tab", data-i18n="editor.level_tab_components") Components
li
a(href="#editor-level-systems-tab-view", data-toggle="tab", data-i18n="editor.level_tab_systems") Systems
-
-
+ li
+ a(href="#editor-level-patches", data-toggle="tab", data-i18n="resources.patches")#patches-tab Patches
+
+
ul.nav.navbar-nav.navbar-right
li(data-toggle="coco-modal", data-target="modal/revert", data-i18n="editor.revert", disabled=authorized === true ? undefined : "true").btn.btn-primary.navbar-btn#revert-button Revert
- li(data-i18n="common.save", disabled=authorized === true ? undefined : "true").btn.btn-primary.navbar-btn#commit-level-start-button Save
+ if authorized
+ li(data-i18n="common.save").btn.btn-primary.navbar-btn#commit-level-start-button Save
+ else
+ li(data-i18n="common.patch").btn.btn-primary.navbar-btn#commit-level-patch-button Patch
li(data-i18n="common.fork", disabled=anonymous ? "true": undefined).btn.btn-primary.navbar-btn#fork-level-start-button Fork
li(title="⌃↩ or ⌘↩: Play preview of current level", data-i18n="common.play")#play-button.btn.btn-inverse.banner.navbar-btn Play!
li.divider
li.dropdown
- a.dropdown-toggle(href='#', data-toggle='dropdown')
+ a.dropdown-toggle(href='#', data-toggle='dropdown', data-i18n="editor.more")
| More
b.caret
ul.dropdown-menu
li#history-button
a(href='#', data-i18n="general.version_history") Version History
li
- a(href='https://github.com/codecombat/codecombat/wiki/Artisan-Home') Wiki
+ a(href='https://github.com/codecombat/codecombat/wiki/Artisan-Home', data-i18n="editor.wiki") Wiki
li
- a(href='http://www.hipchat.com/g3plnOKqa') Live Chat
+ a(href='http://www.hipchat.com/g3plnOKqa', data-i18n="editor.live_chat") Live Chat
li
- a(href='http://discourse.codecombat.com/category/artisan') Forum
+ a(href='http://discourse.codecombat.com/category/artisan', data-i18n="nav.forum") Forum
li
a(data-toggle="coco-modal", data-target="modal/contact", data-i18n="nav.contact") Email
@@ -74,6 +79,9 @@ block outer_content
div.tab-pane#editor-level-components-tab-view
div.tab-pane#editor-level-systems-tab-view
+
+ div.tab-pane#editor-level-patches
+ .patches-view
div#error-view
diff --git a/app/templates/editor/level/fork.jade b/app/templates/editor/level/fork.jade
index 255fc8d80..6c4f43553 100644
--- a/app/templates/editor/level/fork.jade
+++ b/app/templates/editor/level/fork.jade
@@ -6,12 +6,12 @@ block modal-header-content
block modal-body-content
form#save-level-form.form
.form-group
- label(for="level-name") Name
+ label(for="level-name", data-i18n="general.name") Name
input#level-name(name="name", type="text").form-control
block modal-footer-content
- button.btn(data-dismiss="modal") Cancel
- button.btn.btn-primary#fork-level-confirm-button Save
+ button.btn(data-dismiss="modal", data-i18n="common.cancel") Cancel
+ button.btn.btn-primary#fork-level-confirm-button(data-i18n="common.save") Save
block modal-body-wait-content
- h3 Creating Fork...
+ h3(data-i18n="editor.fork_creating") Creating Fork...
diff --git a/app/templates/editor/level/save.jade b/app/templates/editor/level/save.jade
index 8ada52b23..eccef370a 100644
--- a/app/templates/editor/level/save.jade
+++ b/app/templates/editor/level/save.jade
@@ -3,19 +3,20 @@ extends /templates/modal/save_version
block modal-body-content
h3= "Level: " + level.get('name') + " - " + (levelNeedsSave ? "Modified" : "Not Modified")
if levelNeedsSave
- form#save-level-form.form
- .form-group
- label.control-label(for="level-commit-message") Commit Message
- textarea.form-control#level-commit-message(name="commit-message", type="text")
+ .changes-stub
+ form#save-level-form.form-inline
+ .form-group.commit-message
+ input.form-control#level-commit-message(name="commit-message", type="text")
if level.isPublished()
- .form-group.checkbox
- label.control-label(for="level-version-is-major") Major Changes?
- input#level-version-is-major(name="version-is-major", type="checkbox")
- span.help-block (Could this update break old solutions of the level?)
+ .checkbox
+ label
+ input#level-version-is-major(name="version-is-major", type="checkbox")
+ span(data-i18n="versions.new_major_version") New Major Version
if !level.isPublished()
- .form-group.checkbox
- label.control-label(for="level-publish") Publish This Level (irreversible)?
- input#level-publish(name="publish", type="checkbox")
+ .checkbox
+ label
+ input#level-publish(name="publish", type="checkbox")
+ span(data-i18n="common.publish") Publish
if modifiedComponents.length
hr
@@ -23,17 +24,17 @@ block modal-body-content
each component in modifiedComponents
- var id = component.get('_id')
h4= "Component: " + component.get('system') + '.' + component.get('name')
- form.component-form(id="save-component-" + id + "-form")
+ .changes-stub
+ form.form-inline.component-form(id="save-component-" + id + "-form")
input(name="component-original", type="hidden", value=component.get('original'))
input(name="component-parent-major-version", type="hidden", value=component.get('version').major)
- .form-group
- label.control-label(for=id + "-commit-message") Commit Message
- textarea.form-control(id=id + "-commit-message", name="commit-message", type="text")
+ .form-group.commit-message
+ input.form-control(id=id + "-commit-message", name="commit-message", type="text")
if component.isPublished()
- .form-group.checkbox
- label.control-label(for=id + "-version-is-major") Major Changes?
- input(id=id + "-version-is-major", name="version-is-major", type="checkbox")
- span.help-block (Could this update break anything depending on this Component?)
+ .checkbox
+ label
+ input(id=id + "-version-is-major", name="version-is-major", type="checkbox")
+ span(data-i18n="versions.new_major_version") New Major Version
if modifiedSystems.length
hr
@@ -41,14 +42,14 @@ block modal-body-content
each system in modifiedSystems
- var id = system.get('_id')
h4= "System: " + system.get('name')
- form.system-form(id="save-system-" + id + "-form")
+ .changes-stub
+ form.form-inline.system-form(id="save-system-" + id + "-form")
input(name="system-original", type="hidden", value=system.get('original'))
input(name="system-parent-major-version", type="hidden", value=system.get('version').major)
- .form-group
- label.control-label(for=id + "-commit-message") Commit Message
- textarea.form-control(id=id + "-commit-message", name="commit-message", type="text")
+ .form-group.commit-message
+ input.form-control(id=id + "-commit-message", name="commit-message", type="text", placeholder="Commit Message")
if system.isPublished()
- .form-group.checkbox
- label.control-label(for=id + "-version-is-major") Major Changes?
- input(id=id + "-version-is-major", name="version-is-major", type="checkbox")
- span.help-block (Could this update break anything depending on this System?)
+ .checkbox
+ label
+ input(id=id + "-version-is-major", name="version-is-major", type="checkbox")
+ span(data-i18n="versions.new_major_version") New Major Version
diff --git a/app/templates/editor/level/thangs_tab.jade b/app/templates/editor/level/thangs_tab.jade
index b0b86868c..c41c727b8 100644
--- a/app/templates/editor/level/thangs_tab.jade
+++ b/app/templates/editor/level/thangs_tab.jade
@@ -1,7 +1,7 @@
.thangs-container.thangs-column
h3(data-i18n="editor.level_tab_thangs_title") Current Thangs
.btn-group(data-toggle="buttons-radio")#extant-thangs-filter
- button.btn.btn-primary All
+ button.btn.btn-primary(data-i18n="editor.level_tab_thangs_all") All
button.btn.btn-primary(value="Unit", title="Unit")
i.icon-user
button.btn.btn-primary(value="Wall", title="Wall")
@@ -19,9 +19,9 @@
#canvas-wrapper
ul.dropdown-menu#contextmenu
li#delete
- a Delete
+ a(data-i18n="editor.delete") Delete
li#duplicate
- a Duplicate
+ a(data-i18n="editor.duplicate") Duplicate
canvas(width=1848, height=1178)#surface
#canvas-left-gradient.gradient
#canvas-top-gradient.gradient
diff --git a/app/templates/editor/patch_modal.jade b/app/templates/editor/patch_modal.jade
new file mode 100644
index 000000000..68fed43f7
--- /dev/null
+++ b/app/templates/editor/patch_modal.jade
@@ -0,0 +1,23 @@
+extends /templates/modal/modal_base
+
+block modal-header-content
+ .modal-header-content
+ h3 Patch
+
+block modal-body-content
+ .modal-body
+ .changes-stub
+
+
+block modal-footer
+ .modal-footer
+ button(data-dismiss="modal", data-i18n="common.cancel").btn Cancel
+ if isPatchCreator
+ if status != 'withdrawn'
+ button.btn.btn-danger#withdraw-button Withdraw
+ if isPatchRecipient
+ if status != 'accepted'
+ button.btn.btn-primary#accept-button Accept
+ if status != 'rejected'
+ button.btn.btn-danger#reject-button Reject
+
\ No newline at end of file
diff --git a/app/templates/editor/patches.jade b/app/templates/editor/patches.jade
new file mode 100644
index 000000000..872788e7d
--- /dev/null
+++ b/app/templates/editor/patches.jade
@@ -0,0 +1,30 @@
+.btn-group(data-toggle="buttons").status-buttons
+ label.btn.btn-default.pending
+ input(type="radio", name="status", value="pending")
+ | Pending
+ label.btn.btn-default.accepted
+ input(type="radio", name="status", value="accepted")
+ | Accepted
+ label.btn.btn-default.rejected
+ input(type="radio", name="status", value="rejected")
+ | Rejected
+ label.btn.btn-default.withdrawn
+ input(type="radio", name="status", value="withdrawn")
+ | Withdrawn
+
+if patches.loading
+ p Loading
+else
+ table.table.table-condensed.table-bordered
+ tr
+ th Submitter
+ th Submitted
+ th Commit Message
+ th Review
+ for patch in patches
+ tr
+ td= patch.userName
+ td= moment(patch.get('created')).format('llll')
+ td= patch.get('commitMessage')
+ td
+ span.glyphicon.glyphicon-wrench(data-patch-id=patch.id).patch-icon
diff --git a/app/templates/editor/thang/edit.jade b/app/templates/editor/thang/edit.jade
index 1e8ce462d..b1924f439 100644
--- a/app/templates/editor/thang/edit.jade
+++ b/app/templates/editor/thang/edit.jade
@@ -12,8 +12,8 @@ block content
img#portrait.img-thumbnail
- button.btn.btn-secondary#history-button(data-i18n="general.history") History
- button.btn.btn-primary#save-button(data-toggle="coco-modal", data-target="modal/save_version", data-i18n="common.save", disabled=authorized === true ? undefined : "true") Save
+ button.btn.btn-secondary#history-button(data-i18n="general.version_history") Version History
+ button.btn.btn-primary#save-button(data-i18n="common.save", disabled=authorized === true ? undefined : "true") Save
button.btn.btn-primary#revert-button(data-toggle="coco-modal", data-target="modal/revert", data-i18n="editor.revert", disabled=authorized === true ? undefined : "true") Revert
h3 Edit Thang Type: "#{thangType.attributes.name}"
@@ -27,11 +27,13 @@ block content
a(href="#editor-thang-spritesheets-view", data-toggle="tab") Spritesheets
li
a(href="#editor-thang-colors-tab-view", data-toggle="tab")#color-tab Colors
+ li
+ a(href="#editor-thang-patches-view", data-toggle="tab")#patches-tab Patches
div.tab-content
div.tab-pane#editor-thang-colors-tab-view
- div.tab-pane.active#editor-thang-main-tab-view
+ div.tab-pane#editor-thang-main-tab-view.active
div.main-area.well
div.file-controls
@@ -83,6 +85,10 @@ block content
div.tab-pane#editor-thang-spritesheets-view
div#spritesheets
+
+ div.tab-pane#editor-thang-patches-view
+
+ div.patches-view
div#error-view
diff --git a/app/templates/employers.jade b/app/templates/employers.jade
index 485d21622..efeb59429 100644
--- a/app/templates/employers.jade
+++ b/app/templates/employers.jade
@@ -2,33 +2,59 @@ extends /templates/base
block content
- .row
+ h1(data-i18n="employers.want_to_hire_our_players") Want to hire expert CodeCombat players?
- .col-md-6
-
- h2 CodeCombat for Employers
-
- p.lead Want to hire expert CodeCombat players?
-
- p
- | CodeCombat doesn't just have beginners. We also have expert software developers who play our
- a(href="http://blog.codecombat.com/beat-this-level-get-a-programming-job") developer challenge levels
- | . If your company is seeking technical talent, then we'd be happy to help place candidates with you.
-
- p We were actually overwhelmed by how many talented developers rushed to site, crushed our version of the algorithm in the Gridmancer challenge, and were looking for job opportunities, especially in the SF Bay Area where CodeCombat is located. So if you're an employer, now's a great time to get in touch and meet some amazing programmers.
-
- p If this sounds interesting, then let's get in touch, find out what you're looking for, talk about recruitment terms, and see what we can do for you. Don't worry–we are not your traditional recruiter. We're a tech company like you who happens to have a ton of great programmers looking to us for help with the job search.
-
- h3
- a(title='Contact', tabindex=-1, data-toggle="coco-modal", data-target="modal/contact") Contact Us
+ p
+ span(data-i18n="employers.candidates_count_prefix") We currently have
+ if candidates.length
+ | #{candidates.length}
+ else
+ span(data-i18n="employers.candidates_count_many") many
+ |
+ span(data-i18n="employers.candidates_count_suffix") highly skilled and vetted developers looking for work.
+ h3
+ a(title='Contact', tabindex=-1, data-toggle="coco-modal", data-target="modal/employer_signup", data-i18n="employers.contact_george") Contact George to see our candidates
- .span5
-
- h2 Candidate Statistics
-
- h4 Resumes: 46
- h4 Ages: 16 - 45
- h4 Experience: 0 - 30 years
- h4 Skill: from interns and entry level to senior developers and management
- h4 Technologies: just about everything
- h4 Countries: USA, Canada, Australia, and many more
+ if candidates.length
+ table.table.table-condensed.table-hover.table-responsive.tablesorter
+ thead
+ tr
+ th(data-i18n="general.name") Name
+ th(data-i18n="employers.candidate_location") Location
+ th(data-i18n="employers.candidate_looking_for") Looking For
+ th(data-i18n="employers.candidate_role") Role
+ th(data-i18n="employers.candidate_top_skills") Top Skills
+ th(data-i18n="employers.candidate_years_experience") Yrs Exp
+ th(data-i18n="employers.candidate_last_updated") Last Updated
+ if me.isAdmin()
+ th ✓?
+
+ tbody
+ for candidate, index in candidates
+ - var profile = candidate.get('jobProfile');
+ - var authorized = candidate.id; // If we have the id, then we are authorized.
+ tr(data-candidate-id=candidate.id)
+ td
+ if authorized
+ img(src=candidate.getPhotoURL(50), alt=profile.name, title=profile.name, width=50)
+ p= profile.name
+ else
+ img(src="/images/pages/contribute/archmage.png", alt="", title="Sign up as an employer to see our candidates", width=50)
+ p Developer ##{index + 1}
+ if profile.country == 'USA'
+ td= profile.city
+ else
+ td= profile.country
+ td= profile.lookingFor
+ td= profile.jobTitle
+ td
+ each skill in profile.skills.slice(0, 10)
+ code= skill
+ span
+ td= profile.experience
+ td= moment(profile.updated).fromNow()
+ if me.isAdmin()
+ if candidate.get('jobProfileApproved')
+ td ✓
+ else
+ td ✗
\ No newline at end of file
diff --git a/app/templates/home.jade b/app/templates/home.jade
index 376ea27fb..dfd523f0c 100644
--- a/app/templates/home.jade
+++ b/app/templates/home.jade
@@ -4,11 +4,20 @@ block content
h1#site-slogan(data-i18n="home.slogan") Learn to Code JavaScript by Playing a Game
- #trailer-wrapper
-
- img(src="/images/pages/home/video_border.png")
- #mobile-trailer-wrapper
-
+ //- if language is Chinese, we use youku, because China can't visit youtube.
+ //- otherwise, we use youtube.
+ if languageName == "zh-HANS"
+ #trailer-wrapper
+
+ img(src="/images/pages/home/video_border.png")
+ #mobile-trailer-wrapper
+
+ else
+ #trailer-wrapper
+
+ img(src="/images/pages/home/video_border.png")
+ #mobile-trailer-wrapper
+
hr
.alert.alert-danger.lt-ie10
diff --git a/app/templates/kinds/search.jade b/app/templates/kinds/search.jade
index b7babdc95..77dce78db 100644
--- a/app/templates/kinds/search.jade
+++ b/app/templates/kinds/search.jade
@@ -9,9 +9,9 @@ block content
| #{currentEditor}
if me.get('anonymous')
- a.btn.btn-primary.open-modal-button(data-toggle="coco-modal", data-target="modal/signup", role="button") Sign Up to Create a New #{modelLabel}
+ a.btn.btn-primary.open-modal-button(data-toggle="coco-modal", data-target="modal/signup", role="button", data-i18n="editor.signup_to_create") Sign Up to Create a New Content
else
- a.btn.btn-primary.open-modal-button(href='#new-model-modal', role="button", data-toggle="modal" data-i18n="#{currentNew}") Create a New Something
+ a.btn.btn-primary.open-modal-button(href='#new-model-modal', role="button", data-toggle="modal", data-i18n="#{currentNew}") Create a New Something
input#search(data-i18n="[placeholder]#{currentSearch}")
hr
div.results
@@ -20,18 +20,19 @@ block content
// TODO: make this into a ModalView subview
div.modal.fade#new-model-modal
.modal-dialog
- .modal-content
- .modal-header
- h3 Create New #{modelLabel}
- .modal-body
- form.form
- .form-group
- label.control-label(for="name") Name
- input#name.form-control(name="name", type="text")
- .modal-footer
- button.btn(data-dismiss="modal") Cancel
- button.btn.btn-primary.new-model-submit Create
- .modal-body.wait.secret
- h3 Reticulating Splines...
- .progress.progress-striped.active
- .progress-bar
+ .background-wrapper
+ .modal-content
+ .modal-header
+ h3(data-i18n="#{currentNew}") Create New #{modelLabel}
+ .modal-body
+ form.form
+ .form-group
+ label.control-label(for="name", data-i18n="general.name") Name
+ input#name.form-control(name="name", type="text")
+ .modal-footer
+ button.btn(data-dismiss="modal", data-i18n="common.cancel") Cancel
+ button.btn.btn-primary.new-model-submit(data-i18n="common.create") Create
+ .modal-body.wait.secret
+ h3(data-i18n="play_level.tip_reticulating") Reticulating Splines...
+ .progress.progress-striped.active
+ .progress-bar
diff --git a/app/templates/modal/diplomat_suggestion.jade b/app/templates/modal/diplomat_suggestion.jade
index b3d29cb6b..56d98f33a 100644
--- a/app/templates/modal/diplomat_suggestion.jade
+++ b/app/templates/modal/diplomat_suggestion.jade
@@ -1,7 +1,7 @@
extends /templates/modal/modal_base
block modal-header-content
- h3(data-i18n="diplomat_suggestion.title")
+ h3(data-i18n="diplomat_suggestion.title") Help translate CodeCombat!
block modal-body-content
h4(data-i18n="diplomat_suggestion.sub_heading") We need your language skills.
diff --git a/app/templates/modal/employer_signup_modal.jade b/app/templates/modal/employer_signup_modal.jade
new file mode 100644
index 000000000..809b26452
--- /dev/null
+++ b/app/templates/modal/employer_signup_modal.jade
@@ -0,0 +1,9 @@
+extends /templates/modal/modal_base
+
+block modal-header-content
+ h3(data-i18n="employer_signup.title") Hire CodeCombat Players
+
+block modal-body-content
+ h4(data-i18n="employer_signup.sub_heading") Let us find your next brilliant developers.
+
+ p(data-i18n="employer_signup.pitch_body") When you hire one of our players, you will pay CodeCombat 18% of her first-year salary, payable within 30 days of when she starts working. We will fully refund our placement fee if she leaves or is fired within 90 days. Cool? Email george@codecombat.com to get set up with employer permissions to see our candidates.
diff --git a/app/templates/modal/job_profile_contact.jade b/app/templates/modal/job_profile_contact.jade
new file mode 100644
index 000000000..33a02d34d
--- /dev/null
+++ b/app/templates/modal/job_profile_contact.jade
@@ -0,0 +1,22 @@
+extends /templates/modal/contact
+
+block modal-header-content
+ h3(data-i18n="contact.contact_candidate") Contact Candidate
+
+block modal-body-content
+ p(data-i18n="contact.recruitment_reminder") Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 18% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns.
+ .form
+ .form-group
+ label.control-label(for="contact-email", data-i18n="general.email") Email
+ input#contact-email.form-control(name="email", type="email", value=me.get('email'), placeholder="Where should the candidate reply?")
+ .form-group
+ label.control-label(for="contact-subject", data-i18n="general.subject") Subject
+ input#contact-subject.form-control(name="subject", type="text", value="Job interest", placeholder="Subject of the email the candidate will receive.")
+ .form-group
+ label.control-label(for="contact-message", data-i18n="general.message") Message
+ textarea#contact-message.form-control(name="message", rows=8)
+
+block modal-footer-content
+ span.sending-indicator.pull-left.secret(data-i18n="common.sending") Sending...
+ a(href='#', data-dismiss="modal", aria-hidden="true", data-i18n="common.cancel").btn Cancel
+ button.btn.btn-primary#contact-submit-button(data-i18n="common.send") Send
diff --git a/app/templates/modal/modal_base.jade b/app/templates/modal/modal_base.jade
index caebe847c..e2c2d527f 100644
--- a/app/templates/modal/modal_base.jade
+++ b/app/templates/modal/modal_base.jade
@@ -1,26 +1,27 @@
.modal-dialog
- .modal-content
- block modal-header
- .modal-header
- if closeButton
- .button.close(type="button", data-dismiss="modal", aria-hidden="true") ×
- block modal-header-content
- h3 man bites God
-
- block modal-body
- .modal-body
- block modal-body-content
- p Man Bites God are the bad boys of the Melbourne live music and comedy scene. It is like being drowned in a bathtub of harmony.
- img(src="http://www.manbitesgod.com/images/picturecoupleb.jpg")
- img(src="http://www.manbitesgod.com/images/manrantb.jpg")
+ .background-wrapper
+ .modal-content
+ block modal-header
+ .modal-header
+ if closeButton
+ .button.close(type="button", data-dismiss="modal", aria-hidden="true") ×
+ block modal-header-content
+ h3 man bites God
+
+ block modal-body
+ .modal-body
+ block modal-body-content
+ p Man Bites God are the bad boys of the Melbourne live music and comedy scene. It is like being drowned in a bathtub of harmony.
+ img(src="http://www.manbitesgod.com/images/picturecoupleb.jpg")
+ img(src="http://www.manbitesgod.com/images/manrantb.jpg")
- .modal-body.wait.secret
- block modal-body-wait-content
- h3 Reticulating Splines...
- .progress.progress-striped.active
- .progress-bar
+ .modal-body.wait.secret
+ block modal-body-wait-content
+ h3 Reticulating Splines...
+ .progress.progress-striped.active
+ .progress-bar
- block modal-footer
- .modal-footer
- block modal-footer-content
- button.btn.btn-primary(type="button", data-dismiss="modal", aria-hidden="true", data-i18n="modal.okay") Okay
\ No newline at end of file
+ block modal-footer
+ .modal-footer
+ block modal-footer-content
+ button.btn.btn-primary(type="button", data-dismiss="modal", aria-hidden="true", data-i18n="modal.okay") Okay
\ No newline at end of file
diff --git a/app/templates/modal/revert.jade b/app/templates/modal/revert.jade
index f20edd7d2..28b111337 100644
--- a/app/templates/modal/revert.jade
+++ b/app/templates/modal/revert.jade
@@ -10,4 +10,4 @@ block modal-body-content
td
| #{model.type()}: #{model.get('name')}
td
- button(value=model.id) Revert
\ No newline at end of file
+ button(value=model.id, data-i18n="editor.revert") Revert
diff --git a/app/templates/modal/save_version.jade b/app/templates/modal/save_version.jade
index d1f8fc219..748c541a9 100644
--- a/app/templates/modal/save_version.jade
+++ b/app/templates/modal/save_version.jade
@@ -1,30 +1,46 @@
extends /templates/modal/modal_base
block modal-header-content
- h3(data-i18n="versions.save_version_title") Save New Version
+ if isPatch
+ h3(data-i18n="versions.submit_patch_title") Submit Patch
+ else
+ h3(data-i18n="versions.save_version_title") Save New Version
block modal-body-content
- form.form
- .form-group
- label.control-label(for="commitMessage", data-i18n="general.commit_msg") Commit Message
- textarea#commit-message.input-large.form-control(name="commitMessage", type="text")
- .form-group
- label.control-label(for="level-version-is-major", data-i18n="versions.new_major_version") New Major Version
- input#major-version.input-large.form-control(name="version-is-major", type="checkbox")
- span.help-block
+ if hasChanges
+ .changes-stub
+ form.form-inline
+ .form-group.commit-message
+ input.form-control#commit-message(name="commitMessage", type="text")
+ if !isPatch
+ .checkbox
+ label
+ input#major-version(name="version-is-major", type="checkbox")
+ span(data-i18n="versions.new_major_version") New Major Version
+ else
+ .alert.alert-danger No changes
block modal-body-wait-content
- h3(data-i18n="common.saving") Saving...
+ if hasChanges
+ if isPatch
+ h3(data-i18n="versions.submitting_patch") Submitting Patch...
+ else
+ h3(data-i18n="common.saving") Saving...
block modal-footer-content
- if !noSaveButton
+ if hasChanges
#accept-cla-wrapper.alert.alert-info
span(data-i18n="versions.cla_prefix") To save changes, first you must agree to our
|
strong#cla-link(data-i18n="versions.cla_url") CLA
span(data-i18n="versions.cla_suffix") .
- button.btn#agreement-button(data-i18n="versions.cla_agree") I AGREE
+ button.btn.btn-sm#agreement-button(data-i18n="versions.cla_agree") I AGREE
+ if isPatch
+ .alert.alert-info An owner will need to approve it before your changes will become visible.
- button.btn(data-dismiss="modal", data-i18n="common.cancel") Cancel
- if !noSaveButton
- button.btn.btn-primary#save-version-button(data-i18n="common.save") Save
+ .buttons
+ button.btn(data-dismiss="modal", data-i18n="common.cancel") Cancel
+ if hasChanges && !isPatch
+ button.btn.btn-primary#save-version-button(data-i18n="common.save") Save
+ if hasChanges && isPatch
+ button.btn.btn-primary#submit-patch-button(data-i18n="versions.submit_patch") Submit Patch
\ No newline at end of file
diff --git a/app/templates/modal/signup.jade b/app/templates/modal/signup.jade
index c603d019b..db46c7e03 100644
--- a/app/templates/modal/signup.jade
+++ b/app/templates/modal/signup.jade
@@ -30,5 +30,14 @@ block modal-body-content
block modal-body-wait-content
h3(data-i18n="signup.creating") Creating Account...
-block modal-footer-content
- button.btn.btn-primary.btn-large#signup-button(data-i18n="signup.sign_up") Sign Up
+block modal-footer
+ .modal-footer
+ div
+ button.btn.btn-primary.btn-large#signup-button(data-i18n="signup.sign_up") Sign Up
+ div.social-login-text(data-i18n="signup.social_signup") Or, you can sign up through Facebook or G+:
+
+ .modal-footer.network-logins
+ div
+ .fb-login-button(data-show-faces="false", data-width="200", data-max-rows="1", data-scope="email")
+ div
+ .gplus-login-button#gplus-login-button
diff --git a/app/templates/play/level.jade b/app/templates/play/level.jade
index fe4170d1c..d5386952d 100644
--- a/app/templates/play/level.jade
+++ b/app/templates/play/level.jade
@@ -1,7 +1,6 @@
#level-loading-view
.level-content
-
#control-bar-view
#code-area
diff --git a/app/templates/play/level/hud.jade b/app/templates/play/level/hud.jade
index f3a67ff9b..5153f1379 100644
--- a/app/templates/play/level/hud.jade
+++ b/app/templates/play/level/hud.jade
@@ -9,12 +9,14 @@
.thang-name
.thang-actions.thang-elem
- .action-header(data-i18n="play_level.action_timeline") Action Timeline
- .table-container
- .progress-arrow.progress-indicator
- .progress-line.progress-indicator
- table
- tbody
+ .nano
+ .nano-content
+ .action-header(data-i18n="play_level.action_timeline") Action Timeline
+ .table-container
+ .progress-arrow.progress-indicator
+ .progress-line.progress-indicator
+ table
+ tbody
.dialogue-area
p.bubble.dialogue-bubble
diff --git a/app/templates/play/level/tome/spell_palette.jade b/app/templates/play/level/tome/spell_palette.jade
index d587da8c5..f87259d77 100644
--- a/app/templates/play/level/tome/spell_palette.jade
+++ b/app/templates/play/level/tome/spell_palette.jade
@@ -6,5 +6,5 @@ ul(class="nav nav-pills" + (tabbed ? ' multiple-tabs' : ''))
h4= group
.tab-content
each slug, group in entryGroupSlugs
- div(id="palette-tab-" + slug, class="tab-pane" + (group == "this" || slug == defaultGroupSlug ? " active" : ""))
- div(class="properties properties-" + slug)
+ div(id="palette-tab-" + slug, class="tab-pane nano" + (group == "this" || slug == defaultGroupSlug ? " active" : ""))
+ div(class="properties properties-" + slug + " nano-content")
diff --git a/app/treema-ext.coffee b/app/treema-ext.coffee
index 6d2b0c3ed..62fb12644 100644
--- a/app/treema-ext.coffee
+++ b/app/treema-ext.coffee
@@ -55,16 +55,16 @@ class LiveEditingMarkup extends TreemaNode.nodeMap.ace
buildValueForDisplay: (valEl) ->
@editor?.destroy()
valEl.html(marked(@data))
-
+
class SoundFileTreema extends TreemaNode.nodeMap.string
valueClass: 'treema-sound-file'
editable: false
soundCollection: 'files'
-
+
onClick: (e) ->
return if $(e.target).closest('.btn').length
super(arguments...)
-
+
getFiles: ->
@settings[@soundCollection]?.models or []
@@ -76,7 +76,7 @@ class SoundFileTreema extends TreemaNode.nodeMap.string
.click(@playFile)
stopButton = $('')
.click(@stopFile)
-
+
dropdown = $('')
dropdownButton = $('')
@@ -84,9 +84,9 @@ class SoundFileTreema extends TreemaNode.nodeMap.string
.attr('href', '#')
.append($(''))
.dropdown()
-
+
dropdown.append dropdownButton
-
+
menu = $('')
files = @getFiles()
for file in files
@@ -102,7 +102,7 @@ class SoundFileTreema extends TreemaNode.nodeMap.string
@data = $(e.target).data('fullPath') or @data
@reset()
dropdown.append(menu)
-
+
valEl.append(pickButton)
if @data
valEl.append(playButton)
@@ -112,12 +112,12 @@ class SoundFileTreema extends TreemaNode.nodeMap.string
path = @data.split('/')
name = path[path.length-1]
valEl.append($('').text(name))
-
+
reset: ->
@instance = null
@flushChanges()
@refreshDisplay()
-
+
playFile: =>
@src = "/file/#{@data}"
@@ -129,27 +129,27 @@ class SoundFileTreema extends TreemaNode.nodeMap.string
registered = createjs.Sound.registerSound(@src)
if registered is true
@instance = createjs.Sound.play(@src)
-
+
else
f = (event) =>
@instance = createjs.Sound.play(event.src) if event.src is @src
createjs.Sound.removeEventListener('fileload', f)
createjs.Sound.addEventListener('fileload', f)
-
+
stopFile: => @instance?.stop()
-
+
onFileChosen: (InkBlob) =>
if not @settings.filePath
console.error('Need to specify a filePath for this treema', @getRoot())
throw Error('cannot upload file')
-
+
body =
url: InkBlob.url
filename: InkBlob.filename
mimetype: InkBlob.mimetype
path: @settings.filePath
force: true
-
+
@uploadingPath = [@settings.filePath, InkBlob.filename].join('/')
$.ajax('/file', { type: 'POST', data: body, success: @onFileUploaded })
@@ -279,13 +279,13 @@ class LatestVersionReferenceNode extends TreemaNode
search: =>
term = @getValEl().find('input').val()
return if term is @lastTerm
-
+
# HACK while search is broken
if @collection
@lastTerm = term
@searchCallback()
return
-
+
@getSearchResultsEl().empty() if @lastTerm and not term
return unless term
@lastTerm = term
@@ -295,9 +295,9 @@ class LatestVersionReferenceNode extends TreemaNode
# HACK while search is broken
# @collection.url = "#{@url}?term=#{term}&project=true"
@collection.url = "#{@url}?term=#{''}&project=true"
-
+
@collection.fetch()
- @listenTo(@collection, 'sync', @searchCallback)
+ @collection.once 'sync', @searchCallback, @
searchCallback: ->
container = @getSearchResultsEl().detach().empty()
@@ -306,10 +306,10 @@ class LatestVersionReferenceNode extends TreemaNode
row = $('').addClass('treema-search-result-row')
text = @formatDocument(model)
continue unless text?
-
+
# HACK while search is broken
continue unless text.toLowerCase().indexOf(@lastTerm.toLowerCase()) >= 0
-
+
row.addClass('treema-search-selected') if first
first = false
row.text(text)
diff --git a/app/views/account/job_profile_view.coffee b/app/views/account/job_profile_view.coffee
new file mode 100644
index 000000000..940d52ffe
--- /dev/null
+++ b/app/views/account/job_profile_view.coffee
@@ -0,0 +1,89 @@
+CocoView = require 'views/kinds/CocoView'
+template = require 'templates/account/job_profile'
+{me} = require('lib/auth')
+
+module.exports = class JobProfileView extends CocoView
+ id: 'job-profile-view'
+ template: template
+
+ editableSettings: [
+ 'lookingFor', 'active', 'name', 'city', 'country', 'skills', 'experience', 'shortDescription', 'longDescription',
+ 'work', 'education', 'visa', 'projects', 'links', 'jobTitle', 'photoURL'
+ ]
+ readOnlySettings: [
+ 'updated'
+ ]
+
+ afterRender: ->
+ super()
+ return if @loading()
+ @buildJobProfileTreema()
+
+ buildJobProfileTreema: ->
+ visibleSettings = @editableSettings.concat @readOnlySettings
+ data = _.pick (me.get('jobProfile') ? {}), (value, key) => key in visibleSettings
+ data.name ?= (me.get('firstName') + ' ' + me.get('lastName')).trim() if me.get('firstName')
+ console.log 'schema?', me.schema()
+ schema = _.cloneDeep me.schema().properties.jobProfile
+ schema.properties = _.pick schema.properties, (value, key) => key in visibleSettings
+ schema.required = _.intersection schema.required, visibleSettings
+ for prop in @readOnlySettings
+ schema.properties[prop].readOnly = true
+ treemaOptions =
+ filePath: "db/user/#{me.id}"
+ schema: schema
+ data: data
+ aceUseWrapMode: true
+ callbacks: {change: @onJobProfileChanged}
+ nodeClasses:
+ 'skill': SkillTagNode
+ 'link-name': LinkNameNode
+ 'city': CityNode
+ 'country': CountryNode
+
+ @jobProfileTreema = @$el.find('#job-profile-treema').treema treemaOptions
+ @jobProfileTreema.build()
+ @jobProfileTreema.open()
+
+ onJobProfileChanged: (e) =>
+ @hasEditedProfile = true
+ @trigger 'change'
+
+ getData: ->
+ return {} unless me.get('jobProfile') or @hasEditedProfile
+ _.pick @jobProfileTreema.data, (value, key) => key in @editableSettings
+
+
+commonSkills = ['c#', 'java', 'javascript', 'php', 'android', 'jquery', 'python', 'c++', 'html', 'mysql', 'ios', 'asp.net', 'css', 'sql', 'iphone', '.net', 'objective-c', 'ruby-on-rails', 'c', 'ruby', 'sql-server', 'ajax', 'wpf', 'linux', 'database', 'django', 'vb.net', 'windows', 'facebook', 'r', 'html5', 'multithreading', 'ruby-on-rails-3', 'wordpress', 'winforms', 'node.js', 'spring', 'osx', 'performance', 'visual-studio-2010', 'oracle', 'swing', 'algorithm', 'git', 'linq', 'apache', 'web-services', 'perl', 'wcf', 'entity-framework', 'bash', 'visual-studio', 'sql-server-2008', 'hibernate', 'actionscript-3', 'angularjs', 'matlab', 'qt', 'ipad', 'sqlite', 'cocoa-touch', 'cocoa', 'flash', 'mongodb', 'codeigniter', 'jquery-ui', 'css3', 'tsql', 'google-maps', 'silverlight', 'security', 'delphi', 'vba', 'postgresql', 'jsp', 'shell', 'internet-explorer', 'google-app-engine', 'sockets', 'validation', 'scala', 'oop', 'unit-testing', 'xaml', 'parsing', 'twitter-bootstrap', 'google-chrome', 'http', 'magento', 'email', 'android-layout', 'flex', 'rest', 'maven', 'jsf', 'listview', 'date', 'winapi', 'windows-phone-7', 'facebook-graph-api', 'unix', 'url', 'c#-4.0', 'jquery-ajax', 'svn', 'symfony2', 'table', 'cakephp', 'firefox', 'ms-access', 'java-ee', 'jquery-mobile', 'python-2.7', 'tomcat', 'zend-framework', 'opencv', 'visual-c++', 'opengl', 'spring-mvc', 'sql-server-2005', 'authentication', 'search', 'xslt', 'servlets', 'pdf', 'animation', 'math', 'batch-file', 'excel-vba', 'iis', 'mod-rewrite', 'sharepoint', 'gwt', 'powershell', 'visual-studio-2012', 'haskell', 'grails', 'ubuntu', 'networking', 'nhibernate', 'design-patterns', 'testing', 'jpa', 'visual-studio-2008', 'core-data', 'user-interface', 'audio', 'backbone.js', 'gcc', 'mobile', 'design', 'activerecord', 'extjs', 'video', 'stored-procedures', 'optimization', 'drupal', 'image-processing', 'android-intent', 'logging', 'web-applications', 'razor', 'database-design', 'azure', 'vim', 'memory-management', 'model-view-controller', 'cordova', 'c++11', 'selenium', 'ssl', 'assembly', 'soap', 'boost', 'canvas', 'google-maps-api-3', 'netbeans', 'heroku', 'jsf-2', 'encryption', 'hadoop', 'linq-to-sql', 'dll', 'xpath', 'data-binding', 'windows-phone-8', 'phonegap', 'jdbc', 'python-3.x', 'twitter', 'mvvm', 'gui', 'web', 'jquery-plugins', 'numpy', 'deployment', 'ios7', 'emacs', 'knockout.js', 'graphics', 'joomla', 'unicode', 'windows-8', 'android-fragments', 'ant', 'command-line', 'version-control', 'yii', 'github', 'amazon-web-services', 'macros', 'ember.js', 'svg', 'opengl-es', 'django-models', 'solr', 'orm', 'blackberry', 'windows-7', 'ruby-on-rails-4', 'compiler', 'tcp', 'pdo', 'architecture', 'groovy', 'nginx', 'concurrency', 'paypal', 'iis-7', 'express', 'vbscript', 'google-chrome-extension', 'memory-leaks', 'rspec', 'actionscript', 'interface', 'fonts', 'oauth', 'ssh', 'tfs', 'junit', 'struts2', 'd3.js', 'coldfusion', '.net-4.0', 'jqgrid', 'asp-classic', 'https', 'plsql', 'stl', 'sharepoint-2010', 'asp.net-web-api', 'mysqli', 'sed', 'awk', 'internet-explorer-8', 'jboss', 'charts', 'scripting', 'matplotlib', 'laravel', 'clojure', 'entity-framework-4', 'intellij-idea', 'xml-parsing', 'sqlite3', '3d', 'io', 'mfc', 'devise', 'playframework', 'youtube', 'amazon-ec2', 'localization', 'cuda', 'jenkins', 'ssis', 'safari', 'doctrine2', 'vb6', 'amazon-s3', 'dojo', 'air', 'eclipse-plugin', 'android-asynctask', 'crystal-reports', 'cocos2d-iphone', 'dns', 'highcharts', 'ruby-on-rails-3.2', 'ado.net', 'sql-server-2008-r2', 'android-emulator', 'spring-security', 'cross-browser', 'oracle11g', 'bluetooth', 'f#', 'msbuild', 'drupal-7', 'google-apps-script', 'mercurial', 'xna', 'google-analytics', 'lua', 'parallel-processing', 'internationalization', 'java-me', 'mono', 'monotouch', 'android-ndk', 'lucene', 'kendo-ui', 'linux-kernel', 'terminal', 'phpmyadmin', 'makefile', 'ffmpeg', 'applet', 'active-directory', 'coffeescript', 'pandas', 'responsive-design', 'xhtml', 'silverlight-4.0', '.net-3.5', 'jaxb', 'ruby-on-rails-3.1', 'gps', 'geolocation', 'network-programming', 'windows-services', 'laravel-4', 'ggplot2', 'rss', 'webkit', 'functional-programming', 'wsdl', 'telerik', 'maven-2', 'cron', 'mapreduce', 'websocket', 'automation', 'windows-runtime', 'django-forms', 'tkinter', 'android-widget', 'android-activity', 'rubygems', 'content-management-system', 'doctrine', 'django-templates', 'gem', 'fluent-nhibernate', 'seo', 'meteor', 'serial-port', 'glassfish', 'documentation', 'cryptography', 'ef-code-first', 'extjs4', 'x86', 'wordpress-plugin', 'go', 'wix', 'linq-to-entities', 'oracle10g', 'cocos2d', 'selenium-webdriver', 'open-source', 'jtable', 'qt4', 'smtp', 'redis', 'jvm', 'openssl', 'timezone', 'nosql', 'erlang', 'playframework-2.0', 'machine-learning', 'mocking', 'unity3d', 'thread-safety', 'android-actionbar', 'jni', 'udp', 'jasper-reports', 'zend-framework2', 'apache2', 'internet-explorer-7', 'sqlalchemy', 'neo4j', 'ldap', 'jframe', 'youtube-api', 'filesystems', 'make', 'flask', 'gdb', 'cassandra', 'sms', 'g++', 'django-admin', 'push-notification', 'statistics', 'tinymce', 'locking', 'javafx', 'firefox-addon', 'fancybox', 'windows-phone', 'log4j', 'uikit', 'prolog', 'socket.io', 'icons', 'oauth-2.0', 'refactoring', 'sencha-touch', 'elasticsearch', 'symfony1', 'google-api', 'webserver', 'wpf-controls', 'microsoft-metro', 'gtk', 'flex4', 'three.js', 'gradle', 'centos', 'angularjs-directive', 'internet-explorer-9', 'sass', 'html5-canvas', 'interface-builder', 'programming-languages', 'gmail', 'jersey', 'twitter-bootstrap-3', 'arduino', 'requirejs', 'cmake', 'web-development', 'software-engineering', 'startups', 'entrepreneurship', 'social-media-marketing', 'writing', 'marketing', 'web-design', 'graphic-design', 'game-development', 'game-design', 'photoshop', 'illustrator', 'robotics', 'aws', 'devops', 'mathematica', 'bioinformatics', 'data-vis', 'ui', 'embedded-systems', 'codecombat']
+
+commonLinkNames = ['GitHub', 'Facebook', 'Twitter', 'G+', 'LinkedIn', 'Personal Website', 'Blog']
+
+countries = ['Afghanistan', 'Albania', 'Algeria', 'American Samoa', 'Andorra', 'Angola', 'Anguilla', 'Antarctica', 'Antigua and Barbuda', 'Argentina', 'Armenia', 'Aruba', 'Australia', 'Austria', 'Azerbaijan', 'Bahamas', 'Bahrain', 'Bangladesh', 'Barbados', 'Belarus', 'Belgium', 'Belize', 'Benin', 'Bermuda', 'Bhutan', 'Bolivia', 'Bosnia and Herzegovina', 'Botswana', 'Brazil', 'Brunei Darussalam', 'Bulgaria', 'Burkina Faso', 'Burundi', 'Cambodia', 'Cameroon', 'Canada', 'Cape Verde', 'Cayman Islands', 'Central African Republic', 'Chad', 'Chile', 'China', 'Christmas Island', 'Cocos (Keeling) Islands', 'Colombia', 'Comoros', 'Democratic Republic of the Congo (Kinshasa)', 'Congo, Republic of (Brazzaville)', 'Cook Islands', 'Costa Rica', 'Ivory Coast', 'Croatia', 'Cuba', 'Cyprus', 'Czech Republic', 'Denmark', 'Djibouti', 'Dominica', 'Dominican Republic', 'East Timor', 'Ecuador', 'Egypt', 'El Salvador', 'Equatorial Guinea', 'Eritrea', 'Estonia', 'Ethiopia', 'Falkland Islands', 'Faroe Islands', 'Fiji', 'Finland', 'France', 'French Guiana', 'French Polynesia', 'French Southern Territories', 'Gabon', 'Gambia', 'Georgia', 'Germany', 'Ghana', 'Gibraltar', 'Great Britain', 'Greece', 'Greenland', 'Grenada', 'Guadeloupe', 'Guam', 'Guatemala', 'Guinea', 'Guinea-Bissau', 'Guyana', 'Haiti', 'Holy See', 'Honduras', 'Hong Kong', 'Hungary', 'Iceland', 'India', 'Indonesia', 'Iran', 'Iraq', 'Ireland', 'Israel', 'Italy', 'Jamaica', 'Japan', 'Jordan', 'Kazakhstan', 'Kenya', 'Kiribati', 'North Korea', 'South Korea', 'Kosovo', 'Kuwait', 'Kyrgyzstan', 'Lao, People\'s Democratic Republic', 'Latvia', 'Lebanon', 'Lesotho', 'Liberia', 'Libya', 'Liechtenstein', 'Lithuania', 'Luxembourg', 'Macau', 'Macedonia, Rep. of', 'Madagascar', 'Malawi', 'Malaysia', 'Maldives', 'Mali', 'Malta', 'Marshall Islands', 'Martinique', 'Mauritania', 'Mauritius', 'Mayotte', 'Mexico', 'Micronesia, Federal States of', 'Moldova, Republic of', 'Monaco', 'Mongolia', 'Montenegro', 'Montserrat', 'Morocco', 'Mozambique', 'Myanmar, Burma', 'Namibia', 'Nauru', 'Nepal', 'Netherlands', 'Netherlands Antilles', 'New Caledonia', 'New Zealand', 'Nicaragua', 'Niger', 'Nigeria', 'Niue', 'Northern Mariana Islands', 'Norway', 'Oman', 'Pakistan', 'Palau', 'Palestinian territories', 'Panama', 'Papua New Guinea', 'Paraguay', 'Peru', 'Philippines', 'Pitcairn Island', 'Poland', 'Portugal', 'Puerto Rico', 'Qatar', 'Reunion Island', 'Romania', 'Russian Federation', 'Rwanda', 'Saint Kitts and Nevis', 'Saint Lucia', 'Saint Vincent and the Grenadines', 'Samoa', 'San Marino', 'Sao Tome and Principe', 'Saudi Arabia', 'Senegal', 'Serbia', 'Seychelles', 'Sierra Leone', 'Singapore', 'Slovakia', 'Slovenia', 'Solomon Islands', 'Somalia', 'South Africa', 'South Sudan', 'Spain', 'Sri Lanka', 'Sudan', 'Suriname', 'Swaziland', 'Sweden', 'Switzerland', 'Syria, Syrian Arab Republic', 'Taiwan', 'Tajikistan', 'Tanzania; officially the United Republic of Tanzania', 'Thailand', 'Tibet', 'Timor-Leste', 'Togo', 'Tokelau', 'Tonga', 'Trinidad and Tobago', 'Tunisia', 'Turkey', 'Turkmenistan', 'Turks and Caicos Islands', 'Tuvalu', 'Uganda', 'Ukraine', 'United Arab Emirates', 'United Kingdom', 'USA', 'Uruguay', 'Uzbekistan', 'Vanuatu', 'Vatican City State', 'Venezuela', 'Vietnam', 'Virgin Islands (British)', 'Virgin Islands (U.S.)', 'Wallis and Futuna Islands', 'Western Sahara', 'Yemen', 'Zambia', 'Zimbabwe']
+
+commonCities = ['Tokyo', 'Jakarta', 'Seoul', 'Delhi', 'Shanghai', 'Manila', 'Karachi', 'New York', 'Sao Paulo', 'Mexico City', 'Cairo', 'Beijing', 'Osaka', 'Mumbai (Bombay)', 'Guangzhou', 'Moscow', 'Los Angeles', 'Calcutta', 'Dhaka', 'Buenos Aires', 'Istanbul', 'Rio de Janeiro', 'Shenzhen', 'Lagos', 'Paris', 'Nagoya', 'Lima', 'Chicago', 'Kinshasa', 'Tianjin', 'Chennai', 'Bogota', 'Bengaluru', 'London', 'Taipei', 'Ho Chi Minh City (Saigon)', 'Dongguan', 'Hyderabad', 'Chengdu', 'Lahore', 'Johannesburg', 'Tehran', 'Essen', 'Bangkok', 'Hong Kong', 'Wuhan', 'Ahmedabad', 'Chongqung', 'Baghdad', 'Hangzhou', 'Toronto', 'Kuala Lumpur', 'Santiago', 'Dallas-Fort Worth', 'Quanzhou', 'Miami', 'Shenyang', 'Belo Horizonte', 'Philadelphia', 'Nanjing', 'Madrid', 'Houston', 'Xi\'an-Xianyang', 'Milan', 'Luanda', 'Pune', 'Singapore', 'Riyadh', 'Khartoum', 'Saint Petersburg', 'Atlanta', 'Surat', 'Washington', 'Bandung', 'Surabaya', 'Yangoon', 'Alexandria', 'Guadalajara', 'Harbin', 'Boston', 'Zhengzhou', 'Qingdao', 'Abidjan', 'Barcelona', 'Monterrey', 'Ankara', 'Suzhou', 'Phoenix-Mesa', 'Salvador', 'Porto Alegre', 'Rome', 'Accra', 'Sydney', 'Recife', 'Naples', 'Detroit', 'Dalian', 'Fuzhou', 'Medellin', 'San Francisco', 'Silicon Valley', 'Portland', 'Seattle', 'Austin', 'Denver', 'Boulder']
+
+autoFocus = true # Not working right now, possibly a Treema bower thing.
+
+class SkillTagNode extends TreemaNode.nodeMap.string
+ buildValueForEditing: (valEl) ->
+ super(valEl)
+ valEl.find('input').autocomplete(source: commonSkills, minLength: 1, delay: 0, autoFocus: autoFocus)
+ valEl
+
+class LinkNameNode extends TreemaNode.nodeMap.string
+ buildValueForEditing: (valEl) ->
+ super(valEl)
+ valEl.find('input').autocomplete(source: commonLinkNames, minLength: 0, delay: 0, autoFocus: autoFocus)
+ valEl
+
+class CityNode extends TreemaNode.nodeMap.string
+ buildValueForEditing: (valEl) ->
+ super(valEl)
+ valEl.find('input').autocomplete(source: commonCities, minLength: 1, delay: 0, autoFocus: autoFocus)
+ valEl
+
+class CountryNode extends TreemaNode.nodeMap.string
+ buildValueForEditing: (valEl) ->
+ super(valEl)
+ valEl.find('input').autocomplete(source: countries, minLength: 1, delay: 0, autoFocus: autoFocus)
+ valEl
diff --git a/app/views/account/profile_view.coffee b/app/views/account/profile_view.coffee
index f61f7f1e2..854f9e985 100644
--- a/app/views/account/profile_view.coffee
+++ b/app/views/account/profile_view.coffee
@@ -1,36 +1,75 @@
View = require 'views/kinds/RootView'
template = require 'templates/account/profile'
User = require 'models/User'
+JobProfileContactView = require 'views/modal/job_profile_contact_modal'
module.exports = class ProfileView extends View
id: "profile-view"
template: template
- loadingProfile: true
+
+ events:
+ 'click #toggle-job-profile-approved': 'toggleJobProfileApproved'
+ 'keyup #job-profile-notes': 'onJobProfileNotesChanged'
+ 'click #contact-candidate': 'onContactCandidate'
constructor: (options, @userID) ->
+ @onJobProfileNotesChanged = _.debounce @onJobProfileNotesChanged, 1000
super options
- @user = User.getByID(@userID)
- @loadingProfile = false if 'gravatarProfile' of @user
- @listenTo(@user, 'change', @userChanged)
- @listenTo(@user, 'error', @userError)
-
- userChanged: (user) ->
- @loadingProfile = false if 'gravatarProfile' of user
- @render()
-
- userError: (user) ->
- @loadingProfile = false
- @render()
+ if @userID is me.id
+ @user = me
+ else
+ @user = User.getByID(@userID)
+ @addResourceToLoad @user, 'user_profile'
getRenderData: ->
context = super()
- grav = @user.gravatarProfile
- grav = grav.entry[0] if grav
- addedContext =
- user: @user
- loadingProfile: @loadingProfile
- myProfile: @user.id is context.me.id
- grav: grav
- photoURL: @user.getPhotoURL()
- context[key] = addedContext[key] for key of addedContext
+ context.user = @user
+ context.myProfile = @user.id is context.me.id
+ context.marked = marked
+ context.moment = moment
+ context.iconForLink = @iconForLink
+ if links = @user.get('jobProfile')?.links
+ links = ($.extend(true, {}, link) for link in links)
+ link.icon = @iconForLink link for link in links
+ context.profileLinks = _.sortBy links, (link) -> not link.icon # icons first
context
+
+ afterRender: ->
+ super()
+ @updateProfileApproval() if me.isAdmin()
+ unless @user.get('jobProfile')?.projects?.length
+ @$el.find('.right-column').hide()
+ @$el.find('.middle-column').addClass('double-column')
+
+ updateProfileApproval: ->
+ approved = @user.get 'jobProfileApproved'
+ @$el.find('.approved').toggle Boolean(approved)
+ @$el.find('.not-approved').toggle not approved
+
+ toggleJobProfileApproved: ->
+ approved = not @user.get 'jobProfileApproved'
+ @user.set 'jobProfileApproved', approved
+ @user.save()
+ @updateProfileApproval()
+
+ onJobProfileNotesChanged: (e) =>
+ notes = @$el.find("#job-profile-notes").val()
+ @user.set 'jobProfileNotes', notes
+ @user.save()
+
+ iconForLink: (link) ->
+ icons = [
+ {icon: 'facebook', name: 'Facebook', domain: 'facebook.com', match: /facebook/i}
+ {icon: 'twitter', name: 'Twitter', domain: 'twitter.com', match: /twitter/i}
+ {icon: 'github', name: 'GitHub', domain: 'github.com', match: /github/i}
+ {icon: 'gplus', name: 'Google Plus', domain: 'plus.google.com', match: /(google|^g).?(\+|plus)/i}
+ {icon: 'linkedin', name: 'LinkedIn', domain: 'linkedin.com', match: /(google|^g).?(\+|plus)/i}
+ ]
+ for icon in icons
+ if (link.name.search(icon.match) isnt -1) or (link.link.search(icon.domain) isnt -1)
+ icon.url = "/images/pages/account/profile/icon_#{icon.icon}.png"
+ return icon
+ null
+
+ onContactCandidate: (e) ->
+ @openModalView new JobProfileContactView recipientID: @user.id
diff --git a/app/views/account/settings_view.coffee b/app/views/account/settings_view.coffee
index df815b3cb..0665a7c9c 100644
--- a/app/views/account/settings_view.coffee
+++ b/app/views/account/settings_view.coffee
@@ -5,6 +5,7 @@ forms = require('lib/forms')
User = require('models/User')
WizardSettingsView = require './wizard_settings_view'
+JobProfileView = require './job_profile_view'
module.exports = class SettingsView extends View
id: 'account-settings-view'
@@ -19,18 +20,7 @@ module.exports = class SettingsView extends View
@save = _.debounce(@save, 200)
super options
return unless me
- @listenTo(me, 'change', @refreshPicturePane) # depends on gravatar load
@listenTo(me, 'invalid', (errors) -> forms.applyErrorsToForm(@$el, me.validationError))
- window.f = @getSubscriptions
-
- refreshPicturePane: ->
- h = $(@template(@getRenderData()))
- newPane = $('#picture-pane', h)
- oldPane = $('#picture-pane')
- active = oldPane.hasClass('active')
- oldPane.replaceWith(newPane)
- newPane.i18n()
- newPane.addClass('active') if active
afterRender: ->
super()
@@ -45,9 +35,15 @@ module.exports = class SettingsView extends View
)
@chooseTab(location.hash.replace('#',''))
- WizardSettingsView = new WizardSettingsView()
- @listenTo(WizardSettingsView, 'change', @save)
- @insertSubView WizardSettingsView
+
+ wizardSettingsView = new WizardSettingsView()
+ @listenTo wizardSettingsView, 'change', @save
+ @insertSubView wizardSettingsView
+
+ @jobProfileView = new JobProfileView()
+ @listenTo @jobProfileView, 'change', @save
+ @insertSubView @jobProfileView
+ @buildPictureTreema()
chooseTab: (category) ->
id = "##{category}-pane"
@@ -62,11 +58,9 @@ module.exports = class SettingsView extends View
getRenderData: ->
c = super()
return c unless me
- c.gravatarName = c.me?.gravatarName()
- c.photos = me.gravatarPhotoURLs()
- c.chosenPhoto = me.getPhotoURL()
c.subs = {}
c.subs[sub] = 1 for sub in c.me.get('emailSubscriptions') or ['announcement', 'notification', 'tester', 'level_creator', 'developer']
+ c.showsJobProfileTab = me.isAdmin() or me.get('jobProfile') or location.hash.search('job-profile-') isnt -1
c
getSubscriptions: ->
@@ -81,6 +75,27 @@ module.exports = class SettingsView extends View
$('#email-pane input[type="checkbox"]', @$el).prop('checked', not Boolean(subs.length))
@save()
+ buildPictureTreema: ->
+ data = photoURL: me.get('photoURL')
+ data.photoURL = null if data.photoURL?.search('gravatar') isnt -1 # Old style
+ schema = _.cloneDeep me.schema()
+ schema.properties = _.pick me.schema().properties, 'photoURL'
+ schema.required = ['photoURL']
+ treemaOptions =
+ filePath: "db/user/#{me.id}"
+ schema: schema
+ data: data
+ callbacks: {change: @onPictureChanged}
+
+ @pictureTreema = @$el.find('#picture-treema').treema treemaOptions
+ @pictureTreema.build()
+ @pictureTreema.open()
+ @$el.find('.gravatar-fallback').toggle not me.get 'photoURL'
+
+ onPictureChanged: (e) =>
+ @trigger 'change'
+ @$el.find('.gravatar-fallback').toggle not me.get 'photoURL'
+
save: ->
forms.clearFormAlerts(@$el)
@grabData()
@@ -94,14 +109,14 @@ module.exports = class SettingsView extends View
res = me.save()
return unless res
save = $('#save-button', @$el).text($.i18n.t('common.saving', defaultValue: 'Saving...'))
- .addClass('btn-info').show().removeClass('btn-danger')
+ .removeClass('btn-danger').addClass('btn-success').show()
res.error ->
errors = JSON.parse(res.responseText)
forms.applyErrorsToForm(@$el, errors)
- save.text($.i18n.t('account_settings.error_saving', defaultValue: 'Error Saving')).removeClass('btn-info').addClass('btn-danger')
+ save.text($.i18n.t('account_settings.error_saving', defaultValue: 'Error Saving')).removeClass('btn-success').addClass('btn-danger', 500)
res.success (model, response, options) ->
- save.text($.i18n.t('account_settings.saved', defaultValue: 'Changes Saved')).removeClass('btn-info')
+ save.text($.i18n.t('account_settings.saved', defaultValue: 'Changes Saved')).removeClass('btn-success', 500)
grabData: ->
@grabPasswordData()
@@ -120,12 +135,22 @@ module.exports = class SettingsView extends View
me.set('password', password1)
grabOtherData: ->
- me.set('name', $('#name', @$el).val())
- me.set('email', $('#email', @$el).val())
- me.set('emailSubscriptions', @getSubscriptions())
+ me.set 'name', $('#name', @$el).val()
+ me.set 'email', $('#email', @$el).val()
+ me.set 'emailSubscriptions', @getSubscriptions()
+ me.set 'photoURL', @pictureTreema.get('/photoURL')
adminCheckbox = @$el.find('#admin')
if adminCheckbox.length
permissions = []
permissions.push 'admin' if adminCheckbox.prop('checked')
me.set('permissions', permissions)
+
+ jobProfile = me.get('jobProfile') ? {}
+ updated = false
+ for key, val of @jobProfileView.getData()
+ updated = updated or jobProfile[key] isnt val
+ jobProfile[key] = val
+ if updated
+ jobProfile.updated = (new Date()).toISOString()
+ me.set 'jobProfile', jobProfile
diff --git a/app/views/admin/base_view.coffee b/app/views/admin/base_view.coffee
index 7fe8c09c1..5db653086 100644
--- a/app/views/admin/base_view.coffee
+++ b/app/views/admin/base_view.coffee
@@ -1,6 +1,6 @@
-View = require 'views/kinds/RootView'
+RootView = require 'views/kinds/RootView'
template = require 'templates/base'
-module.exports = class BaseView extends View
+module.exports = class BaseView extends RootView
id: "base-view"
template: template
diff --git a/app/views/admin/level_sessions_view.coffee b/app/views/admin/level_sessions_view.coffee
index e66fefc2f..c00fcc2fe 100644
--- a/app/views/admin/level_sessions_view.coffee
+++ b/app/views/admin/level_sessions_view.coffee
@@ -16,12 +16,12 @@ module.exports = class LevelSessionsView extends View
@getLevelSessions()
getLevelSessions: ->
- @sessions = new LevelSessionCollection
+ @sessions = new LevelSessionCollection()
@sessions.fetch()
- @listenTo(@sessions, 'all', @render)
+ @listenToOnce @sessions, 'all', @render
getRenderData: =>
c = super()
c.sessions = @sessions.models
c.moment = moment
- c
\ No newline at end of file
+ c
diff --git a/app/views/admin/users_view.coffee b/app/views/admin/users_view.coffee
index c19c7bd37..acc9a8152 100644
--- a/app/views/admin/users_view.coffee
+++ b/app/views/admin/users_view.coffee
@@ -38,8 +38,7 @@ module.exports = class UsersView extends View
@users.fetch()
@listenTo(@users, 'all', @render)
- getRenderData: =>
+ getRenderData: ->
c = super()
c.users = (user.attributes for user in @users.models)
- console.log('our render data', c)
c
\ No newline at end of file
diff --git a/app/views/editor/article/edit.coffee b/app/views/editor/article/edit.coffee
index 875dc6113..0123546e0 100644
--- a/app/views/editor/article/edit.coffee
+++ b/app/views/editor/article/edit.coffee
@@ -3,6 +3,7 @@ VersionHistoryView = require './versions_view'
ErrorView = require '../../error_view'
template = require 'templates/editor/article/edit'
Article = require 'models/Article'
+SaveVersionModal = require 'views/modal/save_version_modal'
module.exports = class ArticleEditView extends View
id: "editor-article-edit-view"
@@ -12,6 +13,7 @@ module.exports = class ArticleEditView extends View
events:
'click #preview-button': 'openPreview'
'click #history-button': 'showVersionHistory'
+ 'click #save-button': 'openSaveModal'
subscriptions:
'save-new-version': 'saveNewArticle'
@@ -33,17 +35,11 @@ module.exports = class ArticleEditView extends View
)
@article.fetch()
- @article.loadSchema()
- @listenToOnce(@article, 'sync', @onArticleSync)
- @listenToOnce(@article, 'schema-loaded', @buildTreema)
+ @listenToOnce(@article, 'sync', @buildTreema)
@pushChangesToPreview = _.throttle(@pushChangesToPreview, 500)
- onArticleSync: ->
- @article.loaded = true
- @buildTreema()
-
buildTreema: ->
- return if @treema? or (not @article.loaded) or (not Article.hasSchema())
+ return if @treema? or (not @article.loaded)
unless @article.attributes.body
@article.set('body', '')
@startsLoading = false
@@ -52,7 +48,7 @@ module.exports = class ArticleEditView extends View
options =
data: data
filePath: "db/thang.type/#{@article.get('original')}"
- schema: Article.schema.attributes
+ schema: Article.schema
readOnly: true unless me.isAdmin() or @article.hasWriteAccess(me)
callbacks:
change: @pushChangesToPreview
@@ -78,13 +74,16 @@ module.exports = class ArticleEditView extends View
afterRender: ->
super()
return if @startsLoading
- @showReadOnly() unless me.isAdmin() or @article.hasWriteAccess(me)
+ @showReadOnly() if me.get('anonymous')
- openPreview: =>
+ openPreview: ->
@preview = window.open('/editor/article/x/preview', 'preview', 'height=800,width=600')
@preview.focus() if window.focus
@preview.onload = => @pushChangesToPreview()
return false
+
+ openSaveModal: ->
+ @openModalView(new SaveVersionModal({model: @article}))
saveNewArticle: (e) ->
@treema.endExistingEdits()
diff --git a/app/views/editor/components/main.coffee b/app/views/editor/components/main.coffee
index 7b813595b..0104aa5d9 100644
--- a/app/views/editor/components/main.coffee
+++ b/app/views/editor/components/main.coffee
@@ -20,9 +20,6 @@ module.exports = class ThangComponentEditView extends CocoView
render: =>
return if @destroyed
- for model in [Level, LevelComponent]
- temp = new model()
- @listenToOnce temp, 'schema-loaded', @render unless model.schema?.loaded
if not @componentCollection
@componentCollection = @supermodel.getCollection new ComponentsCollection()
unless @componentCollection.loaded
@@ -32,7 +29,7 @@ module.exports = class ThangComponentEditView extends CocoView
afterRender: ->
super()
- return @showLoading() unless @componentCollection?.loaded and Level.schema.loaded and LevelComponent.schema.loaded
+ return @showLoading() unless @componentCollection?.loaded
@hideLoading()
@buildExtantComponentTreema()
@buildAddComponentTreema()
@@ -45,7 +42,7 @@ module.exports = class ThangComponentEditView extends CocoView
buildExtantComponentTreema: ->
treemaOptions =
supermodel: @supermodel
- schema: Level.schema.get('properties').thangs.items.properties.components
+ schema: Level.schema.properties.thangs.items.properties.components
data: _.cloneDeep @components
callbacks: {select: @onSelectExtantComponent, change:@onChangeExtantComponents}
noSortable: true
@@ -69,7 +66,7 @@ module.exports = class ThangComponentEditView extends CocoView
treemaOptions =
supermodel: @supermodel
- schema: { type: 'array', items: LevelComponent.schema.attributes }
+ schema: { type: 'array', items: LevelComponent.schema }
data: ($.extend(true, {}, c) for c in components)
callbacks: {select: @onSelectAddableComponent, enter: @onAddComponentEnterPressed }
readOnly: true
diff --git a/app/views/editor/delta.coffee b/app/views/editor/delta.coffee
new file mode 100644
index 000000000..09c0981a6
--- /dev/null
+++ b/app/views/editor/delta.coffee
@@ -0,0 +1,70 @@
+CocoView = require 'views/kinds/CocoView'
+template = require 'templates/editor/delta'
+deltasLib = require 'lib/deltas'
+
+TEXTDIFF_OPTIONS =
+ baseTextName: "Old"
+ newTextName: "New"
+ contextSize: 5
+ viewType: 1
+
+module.exports = class DeltaView extends CocoView
+ @deltaCounter: 0
+ className: "delta-view"
+ template: template
+
+ constructor: (options) ->
+ super(options)
+ @model = options.model
+ @headModel = options.headModel
+ @expandedDeltas = @model.getExpandedDelta()
+ if @headModel
+ @headDeltas = @headModel.getExpandedDelta()
+ @conflicts = deltasLib.getConflicts(@headDeltas, @expandedDeltas)
+ DeltaView.deltaCounter += @expandedDeltas.length
+
+ getRenderData: ->
+ c = super()
+ c.deltas = @expandedDeltas
+ c.counter = DeltaView.deltaCounter
+ c
+
+ afterRender: ->
+ deltas = @$el.find('.details')
+ for delta, i in deltas
+ deltaEl = $(delta)
+ deltaData = @expandedDeltas[i]
+ @expandDetails(deltaEl, deltaData)
+
+ conflictDeltas = @$el.find('.conflict-details')
+ conflicts = (delta.conflict for delta in @expandedDeltas when delta.conflict)
+ for delta, i in conflictDeltas
+ deltaEl = $(delta)
+ deltaData = conflicts[i]
+ @expandDetails(deltaEl, deltaData)
+
+ expandDetails: (deltaEl, deltaData) ->
+ treemaOptions = { schema: deltaData.schema, readOnly: true }
+
+ if _.isObject(deltaData.left) and leftEl = deltaEl.find('.old-value')
+ options = _.defaults {data: deltaData.left}, treemaOptions
+ TreemaNode.make(leftEl, options).build()
+
+ if _.isObject(deltaData.right) and rightEl = deltaEl.find('.new-value')
+ options = _.defaults {data: deltaData.right}, treemaOptions
+ TreemaNode.make(rightEl, options).build()
+
+ if deltaData.action is 'text-diff'
+ left = difflib.stringAsLines deltaData.left
+ right = difflib.stringAsLines deltaData.right
+ sm = new difflib.SequenceMatcher(left, right)
+ opcodes = sm.get_opcodes()
+ el = deltaEl.find('.text-diff')
+ options = {baseTextLines: left, newTextLines: right, opcodes: opcodes}
+ args = _.defaults options, TEXTDIFF_OPTIONS
+ el.append(diffview.buildView(args))
+
+ getApplicableDelta: ->
+ delta = @model.getDelta()
+ delta = deltasLib.pruneConflictsFromDelta delta, @conflicts if @conflicts
+ delta
\ No newline at end of file
diff --git a/app/views/editor/level/component/edit.coffee b/app/views/editor/level/component/edit.coffee
index 7571e9a80..3f91f1467 100644
--- a/app/views/editor/level/component/edit.coffee
+++ b/app/views/editor/level/component/edit.coffee
@@ -31,7 +31,7 @@ module.exports = class LevelComponentEditView extends View
buildSettingsTreema: ->
data = _.pick @levelComponent.attributes, (value, key) => key in @editableSettings
- schema = _.cloneDeep LevelComponent.schema.attributes
+ schema = _.cloneDeep LevelComponent.schema
schema.properties = _.pick schema.properties, (value, key) => key in @editableSettings
schema.required = _.intersection schema.required, @editableSettings
@@ -55,7 +55,7 @@ module.exports = class LevelComponentEditView extends View
buildConfigSchemaTreema: ->
treemaOptions =
supermodel: @supermodel
- schema: LevelComponent.schema.get('properties').configSchema
+ schema: LevelComponent.schema.properties.configSchema
data: @levelComponent.get 'configSchema'
callbacks: {change: @onConfigSchemaEdited}
treemaOptions.readOnly = true unless me.isAdmin()
@@ -63,7 +63,7 @@ module.exports = class LevelComponentEditView extends View
@configSchemaTreema.build()
@configSchemaTreema.open()
# TODO: schema is not loaded for the first one here?
- @configSchemaTreema.tv4.addSchema('metaschema', LevelComponent.schema.get('properties').configSchema)
+ @configSchemaTreema.tv4.addSchema('metaschema', LevelComponent.schema.properties.configSchema)
onConfigSchemaEdited: =>
@levelComponent.set 'configSchema', @configSchemaTreema.data
diff --git a/app/views/editor/level/edit.coffee b/app/views/editor/level/edit.coffee
index b685d457f..e28f87247 100644
--- a/app/views/editor/level/edit.coffee
+++ b/app/views/editor/level/edit.coffee
@@ -12,6 +12,8 @@ ComponentsTabView = require './components_tab_view'
SystemsTabView = require './systems_tab_view'
LevelSaveView = require './save_view'
LevelForkView = require './fork_view'
+SaveVersionModal = require 'views/modal/save_version_modal'
+PatchesView = require 'views/editor/patches_view'
VersionHistoryView = require './versions_view'
ErrorView = require '../../error_view'
@@ -26,6 +28,8 @@ module.exports = class EditorLevelView extends View
'click #commit-level-start-button': 'startCommittingLevel'
'click #fork-level-start-button': 'startForkingLevel'
'click #history-button': 'showVersionHistory'
+ 'click #patches-tab': -> @patchesView.load()
+ 'click #commit-level-patch-button': 'startPatchingLevel'
constructor: (options, @levelID) ->
super options
@@ -88,7 +92,8 @@ module.exports = class EditorLevelView extends View
@componentsTab = @insertSubView new ComponentsTabView supermodel: @supermodel
@systemsTab = @insertSubView new SystemsTabView supermodel: @supermodel
Backbone.Mediator.publish 'level-loaded', level: @level
- @showReadOnly() unless me.isAdmin() or @level.hasWriteAccess(me)
+ @showReadOnly() if me.get('anonymous')
+ @patchesView = @insertSubView(new PatchesView(@level), @$el.find('.patches-view'))
onPlayLevel: (e) ->
sendLevel = =>
@@ -103,9 +108,12 @@ module.exports = class EditorLevelView extends View
@childWindow.onPlayLevelViewLoaded = (e) => sendLevel() # still a hack
@childWindow.focus()
+ startPatchingLevel: (e) ->
+ @openModalView new SaveVersionModal({model:@level})
+ Backbone.Mediator.publish 'level:view-switched', e
+
startCommittingLevel: (e) ->
- levelSaveView = new LevelSaveView level: @level, supermodel: @supermodel
- @openModalView levelSaveView
+ @openModalView new LevelSaveView level: @level, supermodel: @supermodel
Backbone.Mediator.publish 'level:view-switched', e
startForkingLevel: (e) ->
diff --git a/app/views/editor/level/home.coffee b/app/views/editor/level/home.coffee
index ffb1a5ac9..247c9d3ff 100644
--- a/app/views/editor/level/home.coffee
+++ b/app/views/editor/level/home.coffee
@@ -1,8 +1,8 @@
SearchView = require 'views/kinds/SearchView'
-module.exports = class ThangTypeHomeView extends SearchView
+module.exports = class EditorSearchView extends SearchView
id: "editor-level-home-view"
modelLabel: 'Level'
model: require 'models/Level'
modelURL: '/db/level'
- tableTemplate: require 'templates/editor/level/table'
\ No newline at end of file
+ tableTemplate: require 'templates/editor/level/table'
diff --git a/app/views/editor/level/save_view.coffee b/app/views/editor/level/save_view.coffee
index e3e5ad25c..33d94370b 100644
--- a/app/views/editor/level/save_view.coffee
+++ b/app/views/editor/level/save_view.coffee
@@ -3,11 +3,13 @@ template = require 'templates/editor/level/save'
forms = require 'lib/forms'
LevelComponent = require 'models/LevelComponent'
LevelSystem = require 'models/LevelSystem'
+DeltaView = require 'views/editor/delta'
module.exports = class LevelSaveView extends SaveVersionModal
template: template
instant: false
modalWidthPercent: 60
+ plain: true
events:
'click #save-version-button': 'commitLevel'
@@ -23,8 +25,20 @@ module.exports = class LevelSaveView extends SaveVersionModal
context.levelNeedsSave = @level.hasLocalChanges()
context.modifiedComponents = _.filter @supermodel.getModels(LevelComponent), @shouldSaveEntity
context.modifiedSystems = _.filter @supermodel.getModels(LevelSystem), @shouldSaveEntity
- context.noSaveButton = not (context.levelNeedsSave or context.modifiedComponents.length or context.modifiedSystems.length)
+ context.hasChanges = (context.levelNeedsSave or context.modifiedComponents.length or context.modifiedSystems.length)
+ @lastContext = context
context
+
+ afterRender: ->
+ super()
+ changeEls = @$el.find('.changes-stub')
+ models = if @lastContext.levelNeedsSave then [@level] else []
+ models = models.concat @lastContext.modifiedComponents
+ models = models.concat @lastContext.modifiedSystems
+ for changeEl, i in changeEls
+ model = models[i]
+ deltaView = new DeltaView({model:model})
+ @insertSubView(deltaView, $(changeEl))
shouldSaveEntity: (m) ->
return true if m.hasLocalChanges()
diff --git a/app/views/editor/level/scripts_tab_view.coffee b/app/views/editor/level/scripts_tab_view.coffee
index 8e06b8a58..03855662d 100644
--- a/app/views/editor/level/scripts_tab_view.coffee
+++ b/app/views/editor/level/scripts_tab_view.coffee
@@ -3,7 +3,6 @@ template = require 'templates/editor/level/scripts_tab'
Level = require 'models/Level'
Surface = require 'lib/surface/Surface'
nodes = require './treema_nodes'
-defaultScripts = require 'lib/scripts/defaultScripts'
module.exports = class ScriptsTabView extends View
id: "editor-level-scripts-tab-view"
@@ -22,9 +21,8 @@ module.exports = class ScriptsTabView extends View
@level = e.level
@dimensions = @level.dimensions()
scripts = $.extend(true, [], @level.get('scripts') ? [])
- scripts = _.cloneDeep defaultScripts unless scripts.length
treemaOptions =
- schema: Level.schema.get('properties').scripts
+ schema: Level.schema.properties.scripts
data: scripts
callbacks:
change: @onScriptsChanged
@@ -54,7 +52,7 @@ module.exports = class ScriptsTabView extends View
filePath: "db/level/#{@level.get('original')}"
files: @files
view: @
- schema: Level.schema.get('properties').scripts.items
+ schema: Level.schema.properties.scripts.items
data: selected.data
thangIDs: thangIDs
dimensions: @dimensions
diff --git a/app/views/editor/level/settings_tab_view.coffee b/app/views/editor/level/settings_tab_view.coffee
index 7556c4a4f..6f6822885 100644
--- a/app/views/editor/level/settings_tab_view.coffee
+++ b/app/views/editor/level/settings_tab_view.coffee
@@ -25,7 +25,7 @@ module.exports = class SettingsTabView extends View
onLevelLoaded: (e) ->
@level = e.level
data = _.pick @level.attributes, (value, key) => key in @editableSettings
- schema = _.cloneDeep Level.schema.attributes
+ schema = _.cloneDeep Level.schema
schema.properties = _.pick schema.properties, (value, key) => key in @editableSettings
schema.required = _.intersection schema.required, @editableSettings
thangIDs = @getThangIDs()
diff --git a/app/views/editor/level/system/edit.coffee b/app/views/editor/level/system/edit.coffee
index c92894cdd..338ede1e5 100644
--- a/app/views/editor/level/system/edit.coffee
+++ b/app/views/editor/level/system/edit.coffee
@@ -29,7 +29,7 @@ module.exports = class LevelSystemEditView extends View
buildSettingsTreema: ->
data = _.pick @levelSystem.attributes, (value, key) => key in @editableSettings
- schema = _.cloneDeep LevelSystem.schema.attributes
+ schema = _.cloneDeep LevelSystem.schema
schema.properties = _.pick schema.properties, (value, key) => key in @editableSettings
schema.required = _.intersection schema.required, @editableSettings
@@ -53,7 +53,7 @@ module.exports = class LevelSystemEditView extends View
buildConfigSchemaTreema: ->
treemaOptions =
supermodel: @supermodel
- schema: LevelSystem.schema.get('properties').configSchema
+ schema: LevelSystem.schema.properties.configSchema
data: @levelSystem.get 'configSchema'
callbacks: {change: @onConfigSchemaEdited}
treemaOptions.readOnly = true unless me.isAdmin()
@@ -61,7 +61,7 @@ module.exports = class LevelSystemEditView extends View
@configSchemaTreema.build()
@configSchemaTreema.open()
# TODO: schema is not loaded for the first one here?
- @configSchemaTreema.tv4.addSchema('metaschema', LevelSystem.schema.get('properties').configSchema)
+ @configSchemaTreema.tv4.addSchema('metaschema', LevelSystem.schema.properties.configSchema)
onConfigSchemaEdited: =>
@levelSystem.set 'configSchema', @configSchemaTreema.data
diff --git a/app/views/editor/level/systems_tab_view.coffee b/app/views/editor/level/systems_tab_view.coffee
index 52129e7b8..eb4747b0e 100644
--- a/app/views/editor/level/systems_tab_view.coffee
+++ b/app/views/editor/level/systems_tab_view.coffee
@@ -34,7 +34,7 @@ module.exports = class SystemsTabView extends View
do (url) -> ls.url = -> url
continue if @supermodel.getModelByURL ls.url
ls.fetch()
- @listenTo(ls, 'sync', @onSystemLoaded)
+ @listenToOnce ls, 'sync', @onSystemLoaded
++@toLoad
@onDefaultSystemsLoaded() unless @toLoad
@@ -63,11 +63,11 @@ module.exports = class SystemsTabView extends View
systemModelMap = {}
systemModelMap[sys.get('original')] = sys.get('name') for sys in systemModels
systems = _.sortBy systems, (sys) -> systemModelMap[sys.original]
-
+
treemaOptions =
# TODO: somehow get rid of the + button, or repurpose it to open the LevelSystemAddView instead
supermodel: @supermodel
- schema: Level.schema.get('properties').systems
+ schema: Level.schema.properties.systems
data: systems
readOnly: true unless me.isAdmin() or @level.hasWriteAccess(me)
callbacks:
@@ -143,6 +143,8 @@ class LevelSystemNode extends TreemaObjectNode
@collection = @system?.attributes?.configSchema?.properties?
grabDBComponent: ->
+ unless _.isString @data.original
+ return alert('Press the "Add System" button at the bottom instead of the "+". Sorry.')
@system = @settings.supermodel.getModelByOriginalAndMajorVersion LevelSystem, @data.original, @data.majorVersion
#@system = _.find @settings.supermodel.getModels(LevelSystem), (m) =>
# m.get('original') is @data.original and m.get('version').major is @data.majorVersion
@@ -157,11 +159,12 @@ class LevelSystemNode extends TreemaObjectNode
name = "#{@system.get('name')} v#{@system.get('version').major}"
@buildValueForDisplaySimply valEl, "#{name}"
- onEnterPressed: ->
+ onEnterPressed: (e) ->
+ super e
Backbone.Mediator.publish 'edit-level-system', original: @data.original, majorVersion: @data.majorVersion
- open: ->
- super()
+ open: (depth) ->
+ super depth
cTreema = @childrenTreemas.config
if cTreema? and (cTreema.getChildren().length or cTreema.canAddChild())
cTreema.open()
diff --git a/app/views/editor/level/thangs_tab_view.coffee b/app/views/editor/level/thangs_tab_view.coffee
index 243b1e540..8a6c4cfe5 100644
--- a/app/views/editor/level/thangs_tab_view.coffee
+++ b/app/views/editor/level/thangs_tab_view.coffee
@@ -140,7 +140,7 @@ module.exports = class ThangsTabView extends View
return if @startsLoading
data = $.extend(true, {}, @level.attributes)
treemaOptions =
- schema: Level.schema.get('properties').thangs
+ schema: Level.schema.properties.thangs
data: data.thangs
supermodel: @supermodel
callbacks:
diff --git a/app/views/editor/patch_modal.coffee b/app/views/editor/patch_modal.coffee
new file mode 100644
index 000000000..19f3ebfe6
--- /dev/null
+++ b/app/views/editor/patch_modal.coffee
@@ -0,0 +1,60 @@
+ModalView = require 'views/kinds/ModalView'
+template = require 'templates/editor/patch_modal'
+DeltaView = require 'views/editor/delta'
+auth = require 'lib/auth'
+
+module.exports = class PatchModal extends ModalView
+ id: "patch-modal"
+ template: template
+ plain: true
+
+ events:
+ 'click #withdraw-button': 'withdrawPatch'
+ 'click #reject-button': 'rejectPatch'
+ 'click #accept-button': 'acceptPatch'
+
+ constructor: (@patch, @targetModel, options) ->
+ super(options)
+ targetID = @patch.get('target').id
+ if false
+ @originalSource = targetModel.clone(false)
+ @onOriginalLoaded()
+ else
+ @originalSource = new targetModel.constructor({_id:targetID})
+ @originalSource.fetch()
+ @listenToOnce @originalSource, 'sync', @onOriginalLoaded
+ @addResourceToLoad(@originalSource)
+
+ getRenderData: ->
+ c = super()
+ c.isPatchCreator = @patch.get('creator') is auth.me.id
+ c.isPatchRecipient = @targetModel.hasWriteAccess()
+ c.status = @patch.get 'status'
+ c
+
+ afterRender: ->
+ return if @originalSource.loading
+ headModel = @originalSource.clone(false)
+ headModel.set(@targetModel.attributes)
+
+ pendingModel = @originalSource.clone(false)
+ pendingModel.applyDelta(@patch.get('delta'))
+
+ @deltaView = new DeltaView({model:pendingModel, headModel:headModel})
+ changeEl = @$el.find('.changes-stub')
+ @insertSubView(@deltaView, changeEl)
+ super()
+
+ acceptPatch: ->
+ delta = @deltaView.getApplicableDelta()
+ @targetModel.applyDelta(delta)
+ @targetModel.addPatchToAcceptOnSave(@patch)
+ @hide()
+
+ rejectPatch: ->
+ @patch.setStatus('rejected')
+ @hide()
+
+ withdrawPatch: ->
+ @patch.setStatus('withdrawn')
+ @hide()
\ No newline at end of file
diff --git a/app/views/editor/patches_view.coffee b/app/views/editor/patches_view.coffee
new file mode 100644
index 000000000..f8dd4fa15
--- /dev/null
+++ b/app/views/editor/patches_view.coffee
@@ -0,0 +1,56 @@
+CocoView = require 'views/kinds/CocoView'
+template = require 'templates/editor/patches'
+PatchesCollection = require 'collections/PatchesCollection'
+nameLoader = require 'lib/NameLoader'
+PatchModal = require './patch_modal'
+
+module.exports = class PatchesView extends CocoView
+ template: template
+ className: 'patches-view'
+ status: 'pending'
+
+ events:
+ 'change .status-buttons': 'onStatusButtonsChanged'
+ 'click .patch-icon': 'openPatchModal'
+
+ constructor: (@model, options) ->
+ super(options)
+ @initPatches()
+
+ initPatches: ->
+ @startedLoading = false
+ @patches = new PatchesCollection([], {}, @model, @status)
+ @listenToOnce @patches, 'sync', @gotPatches
+ @addResourceToLoad @patches, 'patches'
+
+ gotPatches: ->
+ ids = (p.get('creator') for p in @patches.models)
+ jqxhr = nameLoader.loadNames ids
+ if jqxhr then @addRequestToLoad(jqxhr, 'user_names', 'gotPatches') else @render()
+
+ load: ->
+ return if @startedLoading
+ @patches.fetch()
+ @startedLoading = true
+
+ getRenderData: ->
+ c = super()
+ patch.userName = nameLoader.getName(patch.get('creator')) for patch in @patches.models
+ c.patches = @patches.models
+ c.status
+ c
+
+ afterRender: ->
+ @$el.find(".#{@status}").addClass 'active'
+
+ onStatusButtonsChanged: (e) ->
+ @loaded = false
+ @status = $(e.target).val()
+ @initPatches()
+ @load()
+ @render()
+
+ openPatchModal: (e) ->
+ patch = _.find @patches.models, {id:$(e.target).data('patch-id')}
+ modal = new PatchModal(patch, @model)
+ @openModalView(modal)
\ No newline at end of file
diff --git a/app/views/editor/thang/colors_tab_view.coffee b/app/views/editor/thang/colors_tab_view.coffee
index a858f4385..87c887522 100644
--- a/app/views/editor/thang/colors_tab_view.coffee
+++ b/app/views/editor/thang/colors_tab_view.coffee
@@ -12,7 +12,7 @@ module.exports = class ColorsTabView extends CocoView
constructor: (@thangType, options) ->
@listenToOnce(@thangType, 'sync', @tryToBuild)
- @listenToOnce(@thangType.schema(), 'sync', @tryToBuild)
+ # @listenToOnce(@thangType.schema(), 'sync', @tryToBuild)
@colorConfig = { hue: 0, saturation: 0.5, lightness: 0.5 }
@spriteBuilder = new SpriteBuilder(@thangType)
f = =>
@@ -115,7 +115,7 @@ module.exports = class ColorsTabView extends CocoView
return unless @thangType.loaded and @thangType.schema().loaded
data = @thangType.get('colorGroups')
data ?= {}
- schema = @thangType.schema().attributes.properties?.colorGroups
+ schema = @thangType.schema().properties?.colorGroups
treemaOptions =
data: data
schema: schema
diff --git a/app/views/editor/thang/edit.coffee b/app/views/editor/thang/edit.coffee
index 660280e54..c5b08d7c0 100644
--- a/app/views/editor/thang/edit.coffee
+++ b/app/views/editor/thang/edit.coffee
@@ -9,6 +9,8 @@ View = require 'views/kinds/RootView'
ThangComponentEditView = require 'views/editor/components/main'
VersionHistoryView = require './versions_view'
ColorsTabView = require './colors_tab_view'
+PatchesView = require 'views/editor/patches_view'
+SaveVersionModal = require 'views/modal/save_version_modal'
ErrorView = require '../../error_view'
template = require 'templates/editor/thang/edit'
@@ -33,6 +35,8 @@ module.exports = class ThangTypeEditView extends View
'click #marker-button': 'toggleDots'
'click #end-button': 'endAnimation'
'click #history-button': 'showVersionHistory'
+ 'click #save-button': 'openSaveModal'
+ 'click #patches-tab': -> @patchesView.load()
subscriptions:
'save-new-version': 'saveNewThangType'
@@ -57,13 +61,11 @@ module.exports = class ThangTypeEditView extends View
)
@thangType.fetch()
- @thangType.loadSchema()
- @listenToOnce(@thangType.schema(), 'sync', @onThangTypeSync)
@listenToOnce(@thangType, 'sync', @onThangTypeSync)
@refreshAnimation = _.debounce @refreshAnimation, 500
onThangTypeSync: ->
- return unless @thangType.loaded and ThangType.hasSchema()
+ return unless @thangType.loaded
@startsLoading = false
@files = new DocumentFiles(@thangType)
@files.fetch()
@@ -90,7 +92,8 @@ module.exports = class ThangTypeEditView extends View
@initSliders()
@initComponents()
@insertSubView(new ColorsTabView(@thangType))
- @showReadOnly() unless me.isAdmin() or @thangType.hasWriteAccess(me)
+ @patchesView = @insertSubView(new PatchesView(@thangType), @$el.find('.patches-view'))
+ @showReadOnly() if me.get('anonymous')
initComponents: =>
options =
@@ -339,7 +342,7 @@ module.exports = class ThangTypeEditView extends View
buildTreema: ->
data = @getThangData()
- schema = _.cloneDeep ThangType.schema.attributes
+ schema = _.cloneDeep ThangType.schema
schema.properties = _.pick schema.properties, (value, key) => not (key in ['components'])
options =
data: data
@@ -396,11 +399,14 @@ module.exports = class ThangTypeEditView extends View
@showAnimation()
@showingSelectedNode = false
- destroy: ->
- @camera?.destroy()
- super()
-
showVersionHistory: (e) ->
versionHistoryView = new VersionHistoryView thangType:@thangType, @thangTypeID
@openModalView versionHistoryView
Backbone.Mediator.publish 'level:view-switched', e
+
+ openSaveModal: ->
+ @openModalView(new SaveVersionModal({model: @thangType}))
+
+ destroy: ->
+ @camera?.destroy()
+ super()
diff --git a/app/views/employers_view.coffee b/app/views/employers_view.coffee
index d5e2eeb2a..cdbf8f074 100644
--- a/app/views/employers_view.coffee
+++ b/app/views/employers_view.coffee
@@ -1,6 +1,87 @@
View = require 'views/kinds/RootView'
template = require 'templates/employers'
+app = require 'application'
+User = require 'models/User'
+CocoCollection = require 'models/CocoCollection'
+EmployerSignupView = require 'views/modal/employer_signup_modal'
+
+class CandidatesCollection extends CocoCollection
+ url: '/db/user/x/candidates'
+ model: User
module.exports = class EmployersView extends View
id: "employers-view"
template: template
+
+ events:
+ 'click tbody tr': 'onCandidateClicked'
+
+ constructor: (options) ->
+ super options
+ @getCandidates()
+
+ afterRender: ->
+ super()
+ @sortTable() if @candidates.models.length
+
+ getRenderData: ->
+ c = super()
+ c.candidates = @candidates.models
+ c.moment = moment
+ c
+
+ getCandidates: ->
+ @candidates = new CandidatesCollection()
+ @candidates.fetch()
+ # Re-render when we have fetched them, but don't wait and show a progress bar while loading.
+ @listenToOnce @candidates, 'all', @render
+
+ sortTable: ->
+ # http://mottie.github.io/tablesorter/docs/example-widget-bootstrap-theme.html
+ $.extend $.tablesorter.themes.bootstrap,
+ # these classes are added to the table. To see other table classes available,
+ # look here: http://twitter.github.com/bootstrap/base-css.html#tables
+ table: "table table-bordered"
+ caption: "caption"
+ header: "bootstrap-header" # give the header a gradient background
+ footerRow: ""
+ footerCells: ""
+ icons: "" # add "icon-white" to make them white; this icon class is added to the in the header
+ sortNone: "bootstrap-icon-unsorted"
+ sortAsc: "icon-chevron-up" # glyphicon glyphicon-chevron-up" # we are still using v2 icons
+ sortDesc: "icon-chevron-down" # glyphicon-chevron-down" # we are still using v2 icons
+ active: "" # applied when column is sorted
+ hover: "" # use custom css here - bootstrap class may not override it
+ filterRow: "" # filter row class
+ even: "" # odd row zebra striping
+ odd: "" # even row zebra striping
+
+ # call the tablesorter plugin and apply the uitheme widget
+ @$el.find(".tablesorter").tablesorter(
+ theme: "bootstrap"
+ widthFixed: true
+ headerTemplate: "{content} {icon}"
+ # widget code contained in the jquery.tablesorter.widgets.js file
+ # use the zebra stripe widget if you plan on hiding any rows (filter widget)
+ widgets: [
+ "uitheme"
+ "zebra"
+ ]
+ widgetOptions:
+ # using the default zebra striping class name, so it actually isn't included in the theme variable above
+ # this is ONLY needed for bootstrap theming if you are using the filter widget, because rows are hidden
+ zebra: [
+ "even"
+ "odd"
+ ]
+ # reset filters button
+ filter_reset: ".reset"
+ )
+
+ onCandidateClicked: (e) ->
+ id = $(e.target).closest('tr').data('candidate-id')
+ if id
+ url = "/account/profile/#{id}"
+ app.router.navigate url, {trigger: true}
+ else
+ @openModalView new EmployerSignupView
diff --git a/app/views/home_view.coffee b/app/views/home_view.coffee
index 7c0eea234..d3f5494a9 100644
--- a/app/views/home_view.coffee
+++ b/app/views/home_view.coffee
@@ -23,6 +23,7 @@ module.exports = class HomeView extends View
else
console.warn 'no more jquery browser version...'
c.isEnglish = (me.get('preferredLanguage') or 'en').startsWith 'en'
+ c.languageName = me.get('preferredLanguage')
c
afterRender: ->
@@ -39,4 +40,4 @@ module.exports = class HomeView extends View
href = playLink.attr("href").split("/")
href[href.length-1] = lastLevel if href.length isnt 0
href = href.join("/")
- playLink.attr("href", href)
+ playLink.attr("href", href)
\ No newline at end of file
diff --git a/app/views/kinds/CocoView.coffee b/app/views/kinds/CocoView.coffee
index 768072a26..6d295bdb2 100644
--- a/app/views/kinds/CocoView.coffee
+++ b/app/views/kinds/CocoView.coffee
@@ -10,7 +10,7 @@ classCount = 0
makeScopeName = -> "view-scope-#{classCount++}"
doNothing = ->
-module.exports = class CocoView extends Backbone.View
+class CocoView extends Backbone.View
startsLoading: false
cache: false # signals to the router to keep this view around
template: -> ''
@@ -101,18 +101,19 @@ module.exports = class CocoView extends Backbone.View
context.fbRef = context.pathname.replace(/[^a-zA-Z0-9+/=\-.:_]/g, '').slice(0, 40) or 'home'
context.isMobile = @isMobile()
context.isIE = @isIE()
+ context.moment = moment
context
afterRender: ->
-
+
# Resource and request loading management for any given view
-
+
addResourceToLoad: (modelOrCollection, name, value=1) ->
@loadProgress.resources.push {resource:modelOrCollection, value:value, name:name}
@listenToOnce modelOrCollection, 'sync', @updateProgress
@listenTo modelOrCollection, 'error', @onResourceLoadFailed
@updateProgress()
-
+
addRequestToLoad: (jqxhr, name, retryFunc, value=1) ->
@loadProgress.requests.push {request:jqxhr, value:value, name: name, retryFunc: retryFunc}
jqxhr.done @updateProgress
@@ -152,7 +153,7 @@ module.exports = class CocoView extends Backbone.View
num += r.value for r in @loadProgress.requests when r.request.status
num += r.value for r in @loadProgress.somethings when r.loaded
#console.log 'update progress', @, num, denom, arguments
-
+
progress = if denom then num / denom else 0
# sometimes the denominator isn't known from the outset, so make sure the overall progress only goes up
@loadProgress.progress = progress if progress > @loadProgress.progress
@@ -160,7 +161,7 @@ module.exports = class CocoView extends Backbone.View
if num is denom and not @loaded
@loaded = true
@onLoaded()
-
+
updateProgressBar: =>
prog = "#{parseInt(@loadProgress.progress*100)}%"
@$el.find('.loading-screen .progress-bar').css('width', prog)
@@ -169,7 +170,7 @@ module.exports = class CocoView extends Backbone.View
@render()
# Error handling for loading
-
+
onResourceLoadFailed: (resource, jqxhr) ->
for r, index in @loadProgress.resources
break if r.resource is resource
@@ -179,12 +180,12 @@ module.exports = class CocoView extends Backbone.View
resourceIndex: index,
responseText: jqxhr.responseText
})).i18n()
-
+
onRetryResource: (e) ->
r = @loadProgress.resources[$(e.target).data('resource-index')]
r.resource.fetch()
$(e.target).closest('.loading-error-alert').remove()
-
+
onRequestLoadFailed: (jqxhr) =>
for r, index in @loadProgress.requests
break if r.request is jqxhr
@@ -194,7 +195,7 @@ module.exports = class CocoView extends Backbone.View
requestIndex: index,
responseText: jqxhr.responseText
}))
-
+
onRetryRequest: (e) ->
r = @loadProgress.requests[$(e.target).data('request-index')]
@[r.retryFunc]?()
@@ -227,6 +228,9 @@ module.exports = class CocoView extends Backbone.View
$('#modal-wrapper .modal').modal(modalOptions).on 'hidden.bs.modal', @modalClosed
window.currentModal = modalView
@getRootView().stopListeningToShortcuts(true)
+ # setTimeout ->
+ # $('.modal').nanoScroller({contentClass:'modal-dialog'})
+ # , 1000
modalClosed: =>
visibleModal.willDisappear() if visibleModal
@@ -257,7 +261,7 @@ module.exports = class CocoView extends Backbone.View
showReadOnly: ->
return if me.isAdmin()
- warning = $.i18n.t 'editor.read_only_warning', defaultValue: "Note: you can't save any edits here, because you're not logged in as an admin."
+ warning = $.i18n.t 'editor.read_only_warning2', defaultValue: "Note: you can't save any edits here, because you're not logged in."
noty text: warning, layout: 'center', type: 'information', killer: true, timeout: 5000
# Loading ModalViews
@@ -296,18 +300,23 @@ module.exports = class CocoView extends Backbone.View
# Subviews
- insertSubView: (view) ->
- @subviews[view.id].destroy() if view.id of @subviews
- @$el.find('#'+view.id).after(view.el).remove()
+ insertSubView: (view, elToReplace=null) ->
+ key = view.id or (view.constructor.name+classCount++)
+ key = _.string.underscored(key)
+ @subviews[key].destroy() if key of @subviews
+ elToReplace ?= @$el.find('#'+view.id)
+ elToReplace.after(view.el).remove()
view.parent = @
view.render()
view.afterInsert()
- @subviews[view.id] = view
+ view.parentKey = key
+ @subviews[key] = view
+ view
removeSubView: (view) ->
view.$el.empty()
+ delete @subviews[view.parentKey]
view.destroy()
- delete @subviews[view.id]
# Utilities
@@ -339,6 +348,7 @@ module.exports = class CocoView extends Backbone.View
slider
-
-mobileRELong = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i
+ mobileRELong = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i
mobileREShort = /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i
+
+module.exports = CocoView
diff --git a/app/views/kinds/ModalView.coffee b/app/views/kinds/ModalView.coffee
index 5222df067..2bf6ee8db 100644
--- a/app/views/kinds/ModalView.coffee
+++ b/app/views/kinds/ModalView.coffee
@@ -5,6 +5,7 @@ module.exports = class ModalView extends CocoView
closeButton: true
closesOnClickOutside: true
modalWidthPercent: null
+ plain: false
shortcuts:
'esc': 'hide'
@@ -31,6 +32,7 @@ module.exports = class ModalView extends CocoView
@$el.on 'hide.bs.modal', =>
@onHidden() unless @hidden
@hidden = true
+ @$el.find('.background-wrapper').addClass('plain') if @plain
afterInsert: ->
super()
diff --git a/app/views/kinds/RootView.coffee b/app/views/kinds/RootView.coffee
index 2cbcf098c..7ef3e7221 100644
--- a/app/views/kinds/RootView.coffee
+++ b/app/views/kinds/RootView.coffee
@@ -30,6 +30,10 @@ module.exports = class RootView extends CocoView
$el ?= @$el.find('.main-content-area')
super($el)
+ renderScrollbar: ->
+ $('.nano-pane').css('display','none')
+ $ -> $('.nano').nanoScroller()
+
afterInsert: ->
# force the browser to scroll to the hash
# also messes with the browser history, so perhaps come up with a better solution
@@ -37,12 +41,14 @@ module.exports = class RootView extends CocoView
hash = location.hash
location.hash = ''
location.hash = hash
- @buildLanguages()
+ @renderScrollbar()
#@$('.antiscroll-wrap').antiscroll() # not yet, buggy
afterRender: ->
super(arguments...)
@chooseTab(location.hash.replace('#','')) if location.hash
+ @buildLanguages()
+ $('body').removeClass('is-playing')
chooseTab: (category) ->
$("a[href='##{category}']", @$el).tab('show')
@@ -52,7 +58,7 @@ module.exports = class RootView extends CocoView
buildLanguages: ->
$select = @$el.find(".language-dropdown").empty()
if $select.hasClass("fancified")
- $select.parent().find('.options,.trigger').remove()
+ $select.parent().find('.options, .trigger').remove()
$select.unwrap().removeClass("fancified")
preferred = me.lang()
codes = _.keys(locale)
@@ -70,10 +76,8 @@ module.exports = class RootView extends CocoView
$.i18n.setLng(newLang, {})
@saveLanguage(newLang)
@render()
- @buildLanguages()
unless newLang.split('-')[0] is "en"
@openModalView(application.router.getView("modal/diplomat_suggestion", "_modal"))
- $('body').attr('lang', newLang)
saveLanguage: (newLang) ->
me.set('preferredLanguage', newLang)
diff --git a/app/views/kinds/SearchView.coffee b/app/views/kinds/SearchView.coffee
index f49eb4994..9ce303b7c 100644
--- a/app/views/kinds/SearchView.coffee
+++ b/app/views/kinds/SearchView.coffee
@@ -8,7 +8,7 @@ class SearchCollection extends Backbone.Collection
@url = "#{modelURL}/search?project=true"
@url += "&term=#{term}" if @term
-module.exports = class ThangTypeHomeView extends View
+module.exports = class SearchView extends View
template: template
className: 'search-view'
@@ -96,7 +96,7 @@ module.exports = class ThangTypeHomeView extends View
name = @$el.find('#name').val()
model = new @model()
model.set('name', name)
- if @model.schema.get('properties').permissions
+ if @model.schema.properties.permissions
model.set 'permissions', [{access: 'owner', target: me.id}]
res = model.save()
return unless res
diff --git a/app/views/modal/contact_modal.coffee b/app/views/modal/contact_modal.coffee
index dd3f0c40e..2e313d44a 100644
--- a/app/views/modal/contact_modal.coffee
+++ b/app/views/modal/contact_modal.coffee
@@ -6,16 +6,15 @@ forms = require 'lib/forms'
contactSchema =
additionalProperties: false
+ required: ['email', 'message']
properties:
email:
- required: true
type: 'string'
maxLength: 100
minLength: 1
format: 'email'
message:
- required: true
type: 'string'
minLength: 1
diff --git a/app/views/modal/employer_signup_modal.coffee b/app/views/modal/employer_signup_modal.coffee
new file mode 100644
index 000000000..de66c007d
--- /dev/null
+++ b/app/views/modal/employer_signup_modal.coffee
@@ -0,0 +1,7 @@
+View = require 'views/kinds/ModalView'
+template = require 'templates/modal/employer_signup_modal'
+
+module.exports = class EmployerSignupView extends View
+ id: "employer-signup"
+ template: template
+ closeButton: true
diff --git a/app/views/modal/job_profile_contact_modal.coffee b/app/views/modal/job_profile_contact_modal.coffee
new file mode 100644
index 000000000..236301454
--- /dev/null
+++ b/app/views/modal/job_profile_contact_modal.coffee
@@ -0,0 +1,41 @@
+ContactView = require 'views/modal/contact_modal'
+template = require 'templates/modal/job_profile_contact'
+
+forms = require 'lib/forms'
+{sendContactMessage} = require 'lib/contact'
+
+contactSchema =
+ additionalProperties: false
+ required: ['email', 'message']
+ properties:
+ email:
+ type: 'string'
+ maxLength: 100
+ minLength: 1
+ format: 'email'
+
+ subject:
+ type: 'string'
+ minLength: 1
+
+ message:
+ type: 'string'
+ minLength: 1
+
+ recipientID:
+ type: 'string'
+ minLength: 1
+
+module.exports = class JobProfileContactView extends ContactView
+ id: "job-profile-contact-modal"
+ template: template
+
+ contact: ->
+ forms.clearFormAlerts @$el
+ contactMessage = forms.formToObject @$el
+ contactMessage.recipientID = @options.recipientID
+ res = tv4.validateMultiple contactMessage, contactSchema
+ return forms.applyErrorsToForm @$el, res.errors unless res.valid
+ contactMessage.message += '\n\n\n\n[CodeCombat says: please let us know if you end up accepting this job. Thanks!]'
+ window.tracker?.trackEvent 'Sent Job Profile Message', message: contactMessage
+ sendContactMessage contactMessage, @$el
diff --git a/app/views/modal/login_modal.coffee b/app/views/modal/login_modal.coffee
index 8a433dd28..68306a03e 100644
--- a/app/views/modal/login_modal.coffee
+++ b/app/views/modal/login_modal.coffee
@@ -36,7 +36,7 @@ module.exports = class LoginModalView extends View
loginAccount: (e) =>
forms.clearFormAlerts(@$el)
userObject = forms.formToObject @$el
- res = tv4.validateMultiple userObject, User.schema.attributes
+ res = tv4.validateMultiple userObject, User.schema
return forms.applyErrorsToForm(@$el, res.errors) unless res.valid
@enableModalInProgress(@$el) # TODO: part of forms
loginUser(userObject)
diff --git a/app/views/modal/revert_modal.coffee b/app/views/modal/revert_modal.coffee
index 91358988c..68094c371 100644
--- a/app/views/modal/revert_modal.coffee
+++ b/app/views/modal/revert_modal.coffee
@@ -5,16 +5,16 @@ CocoModel = require 'models/CocoModel'
module.exports = class RevertModal extends ModalView
id: 'revert-modal'
template: template
-
+
events:
'click #changed-models button': 'onRevertModel'
-
+
onRevertModel: (e) ->
id = $(e.target).val()
CocoModel.backedUp[id].revert()
$(e.target).closest('tr').remove()
@reloadOnClose = true
-
+
getRenderData: ->
c = super()
models = _.values CocoModel.backedUp
@@ -23,5 +23,4 @@ module.exports = class RevertModal extends ModalView
c
onHidden: ->
- console.log 'reload?', @reloadOnClose
location.reload() if @reloadOnClose
diff --git a/app/views/modal/save_version_modal.coffee b/app/views/modal/save_version_modal.coffee
index 86e1ea96b..8c49327f5 100644
--- a/app/views/modal/save_version_modal.coffee
+++ b/app/views/modal/save_version_modal.coffee
@@ -1,18 +1,39 @@
ModalView = require 'views/kinds/ModalView'
template = require 'templates/modal/save_version'
+DeltaView = require 'views/editor/delta'
+Patch = require 'models/Patch'
+forms = require 'lib/forms'
module.exports = class SaveVersionModal extends ModalView
id: 'save-version-modal'
template: template
+ plain: true
events:
'click #save-version-button': 'onClickSaveButton'
'click #cla-link': 'onClickCLALink'
'click #agreement-button': 'onAgreedToCLA'
-
+ 'click #submit-patch-button': 'onClickPatchButton'
+
+ constructor: (options) ->
+ super options
+ @model = options.model or options.level
+ new Patch() # hack to get the schema to load, delete this later
+ @isPatch = not @model.hasWriteAccess()
+
+ getRenderData: ->
+ c = super()
+ c.isPatch = @isPatch
+ c.hasChanges = @model.hasLocalChanges()
+ c
+
afterRender: ->
super()
@$el.find(if me.get('signedCLA') then '#accept-cla-wrapper' else '#save-version-button').hide()
+ changeEl = @$el.find('.changes-stub')
+ deltaView = new DeltaView({model:@model})
+ @insertSubView(deltaView, changeEl)
+ @$el.find('.commit-message input').attr('placeholder', $.i18n.t('general.commit_msg'))
onClickSaveButton: ->
Backbone.Mediator.publish 'save-new-version', {
@@ -20,6 +41,27 @@ module.exports = class SaveVersionModal extends ModalView
commitMessage: @$el.find('#commit-message').val()
}
+ onClickPatchButton: ->
+ forms.clearFormAlerts @$el
+ patch = new Patch()
+ patch.set 'delta', @model.getDelta()
+ patch.set 'commitMessage', @$el.find('#commit-message').val()
+ patch.set 'target', {
+ 'collection': _.string.underscored @model.constructor.className
+ 'id': @model.id
+ }
+ errors = patch.validate()
+ forms.applyErrorsToForm(@$el, errors) if errors
+ res = patch.save()
+ return unless res
+ @enableModalInProgress(@$el)
+
+ res.error =>
+ @disableModalInProgress(@$el)
+
+ res.success =>
+ @hide()
+
onClickCLALink: ->
window.open('/cla', 'cla', 'height=800,width=900')
diff --git a/app/views/modal/signup_modal.coffee b/app/views/modal/signup_modal.coffee
index 5ecbc07c5..63174261f 100644
--- a/app/views/modal/signup_modal.coffee
+++ b/app/views/modal/signup_modal.coffee
@@ -57,8 +57,12 @@ module.exports = class SignupModalView extends View
userObject.emailSubscriptions.push 'notification' unless 'notification' in userObject.emailSubscriptions
else
userObject.emailSubscriptions = _.without (userObject.emailSubscriptions ? []), 'announcement', 'notification'
- res = tv4.validateMultiple userObject, User.schema.attributes
+ res = tv4.validateMultiple userObject, User.schema
return forms.applyErrorsToForm(@$el, res.errors) unless res.valid
window.tracker?.trackEvent 'Finished Signup'
@enableModalInProgress(@$el)
createUser userObject, null, window.nextLevelURL
+
+ afterInsert: ->
+ super()
+ application.router.renderLoginButtons()
diff --git a/app/views/modal/wizard_settings_modal.coffee b/app/views/modal/wizard_settings_modal.coffee
index 0223187bd..5715a4c1f 100644
--- a/app/views/modal/wizard_settings_modal.coffee
+++ b/app/views/modal/wizard_settings_modal.coffee
@@ -22,6 +22,7 @@ module.exports = class WizardSettingsModal extends View
WizardSettingsView = require 'views/account/wizard_settings_view'
view = new WizardSettingsView()
@insertSubView view
+ super()
checkNameExists: =>
forms.clearFormAlerts(@$el)
@@ -31,7 +32,7 @@ module.exports = class WizardSettingsModal extends View
forms.applyErrorsToForm(@$el, {property:'name', message:'is already taken'}) if id and id isnt me.id
$.ajax("/db/user/#{name}/nameToID", {success: success})
- onWizardSettingsDone: =>
+ onWizardSettingsDone: ->
me.set('name', $('#wizard-settings-name').val())
forms.clearFormAlerts(@$el)
res = me.validate()
@@ -42,10 +43,11 @@ module.exports = class WizardSettingsModal extends View
res = me.save()
return unless res
save = $('#save-button', @$el).text($.i18n.t('common.saving', defaultValue: 'Saving...'))
- .addClass('btn-info').show().removeClass('btn-danger')
+ .addClass('btn-info').show().removeClass('btn-danger')
res.error =>
errors = JSON.parse(res.responseText)
+ console.warn "Got errors saving user:", errors
forms.applyErrorsToForm(@$el, errors)
@disableModalInProgress(@$el)
@@ -53,4 +55,3 @@ module.exports = class WizardSettingsModal extends View
@hide()
@enableModalInProgress(@$el)
- me.save()
diff --git a/app/views/play/ladder/play_modal.coffee b/app/views/play/ladder/play_modal.coffee
index 2272be191..43286b1d0 100644
--- a/app/views/play/ladder/play_modal.coffee
+++ b/app/views/play/ladder/play_modal.coffee
@@ -11,6 +11,7 @@ module.exports = class LadderPlayModal extends View
closeButton: true
startsLoading: true
@shownTutorialButton: false
+ tutorialLevelExists: null
events:
'click #skip-tutorial-button': 'hideTutorialButtons'
@@ -21,7 +22,7 @@ module.exports = class LadderPlayModal extends View
@otherTeam = if team is 'ogres' then 'humans' else 'ogres'
@startLoadingChallengersMaybe()
@wizardType = ThangType.loadUniversalWizard()
-
+
# PART 1: Load challengers from the db unless some are in the matches
startLoadingChallengersMaybe: ->
@@ -58,9 +59,11 @@ module.exports = class LadderPlayModal extends View
# PART 4: Render
finishRendering: ->
- @startsLoading = false
- @render()
- @maybeShowTutorialButtons()
+ @checkTutorialLevelExists (exists) =>
+ @tutorialLevelExists = exists
+ @startsLoading = false
+ @render()
+ @maybeShowTutorialButtons()
getRenderData: ->
ctx = super()
@@ -94,7 +97,7 @@ module.exports = class LadderPlayModal extends View
ctx
maybeShowTutorialButtons: ->
- return if @session or LadderPlayModal.shownTutorialButton
+ return if @session or LadderPlayModal.shownTutorialButton or not @tutorialLevelExists
@$el.find('#normal-view').addClass('secret')
@$el.find('.modal-header').addClass('secret')
@$el.find('#noob-view').removeClass('secret')
@@ -105,6 +108,17 @@ module.exports = class LadderPlayModal extends View
@$el.find('.modal-header').removeClass('secret')
@$el.find('#noob-view').addClass('secret')
+ checkTutorialLevelExists: (cb) ->
+ levelID = @level.get('slug') or @level.id
+ tutorialLevelID = "#{levelID}-tutorial"
+ success = => cb true
+ failure = => cb false
+ $.ajax
+ type: "GET"
+ url: "/db/level/#{tutorialLevelID}/exists"
+ success: success
+ error: failure
+
# Choosing challengers
getChallengers: ->
diff --git a/app/views/play/level/level_loading_view.coffee b/app/views/play/level/level_loading_view.coffee
index 32da3377e..8035c5d14 100644
--- a/app/views/play/level/level_loading_view.coffee
+++ b/app/views/play/level/level_loading_view.coffee
@@ -8,7 +8,7 @@ module.exports = class LevelLoadingView extends View
subscriptions:
'level-loader:progress-changed': 'onLevelLoaderProgressChanged'
-
+
afterRender: ->
@$el.find('.tip.rare').remove() if _.random(1, 10) < 9
tips = @$el.find('.tip').addClass('to-remove')
@@ -34,6 +34,7 @@ module.exports = class LevelLoadingView extends View
reallyUnveil: =>
return if @destroyed
+ @$el.addClass 'unveiled'
loadingDetails = @$el.find('.loading-details')
duration = parseFloat loadingDetails.css 'transition-duration'
loadingDetails.css 'top', -loadingDetails.outerHeight(true)
diff --git a/app/views/play/level/playback_view.coffee b/app/views/play/level/playback_view.coffee
index 4a3e4d359..191a00c22 100644
--- a/app/views/play/level/playback_view.coffee
+++ b/app/views/play/level/playback_view.coffee
@@ -107,6 +107,9 @@ module.exports = class PlaybackView extends View
@hookUpScrubber()
@updateMusicButton()
$(window).on('resize', @onWindowResize)
+ ua = navigator.userAgent.toLowerCase()
+ if /safari/.test(ua) and not /chrome/.test(ua)
+ @$el.find('.toggle-fullscreen').hide()
updatePopupContent: ->
@timePopup.updateContent "#{@timeToString @newTime}
#{@formatTime(@current, @currentTime)}
#{@formatTime(@total, @totalTime)}"
@@ -151,7 +154,7 @@ module.exports = class PlaybackView extends View
@newTime = 0
@currentTime = 0
- @timePopup = new HoverPopup unless @timePopup?
+ @timePopup ?= new HoverPopup
#TODO: Why do we need defaultValues here at all? Fallback language has been set to 'en'... oO
@@ -192,7 +195,7 @@ module.exports = class PlaybackView extends View
@$progressScrubber.slider('disable', true)
catch e
#console.warn('error disabling scrubber')
- @timePopup.disable()
+ @timePopup?.disable()
$('#volume-button', @$el).removeClass('disabled')
onEnableControls: (e) ->
@@ -203,7 +206,7 @@ module.exports = class PlaybackView extends View
@$progressScrubber.slider('enable', true)
catch e
#console.warn('error enabling scrubber')
- @timePopup.enable()
+ @timePopup?.enable()
onSetPlaying: (e) ->
@playing = (e ? {}).playing ? true
@@ -242,21 +245,21 @@ module.exports = class PlaybackView extends View
@lastProgress = e.progress
onProgressEnter: (e) ->
- #Why it needs itself as parameter you ask? Ask Twitter instead..
- @timePopup.enter @timePopup
+ # Why it needs itself as parameter you ask? Ask Twitter instead.
+ @timePopup?.enter @timePopup
onProgressLeave: (e) ->
- @timePopup.leave @timePopup
+ @timePopup?.leave @timePopup
onProgressHover: (e) ->
timeRatio = @$progressScrubber.width() / @totalTime
@newTime = e.offsetX / timeRatio
@updatePopupContent()
- @timePopup.onHover e
+ @timePopup?.onHover e
- #Show it instantaniously if close enough to current time.
- if Math.abs(@currentTime - @newTime) < 1 and not @timePopup.shown
- @timePopup.show() unless @timePopup.shown
+ # Show it instantaneously if close enough to current time.
+ if @timePopup and Math.abs(@currentTime - @newTime) < 1 and not @timePopup.shown
+ @timePopup.show()
updateProgress: (progress) ->
$('.scrubber .progress-bar', @$el).css('width', "#{progress*100}%")
diff --git a/app/views/play/level/tome/spell.coffee b/app/views/play/level/tome/spell.coffee
index a0cb680cb..832d59623 100644
--- a/app/views/play/level/tome/spell.coffee
+++ b/app/views/play/level/tome/spell.coffee
@@ -90,6 +90,8 @@ module.exports = class Spell
problems:
jshint_W040: {level: "ignore"}
jshint_W030: {level: "ignore"} # aether_NoEffect instead
+ jshint_W038: {level: "ignore"} #eliminates hoisting problems
+ jshint_W091: {level: "ignore"} #eliminates more hoisting problems
aether_MissingThis: {level: (if thang.requiresThis then 'error' else 'warning')}
language: aceConfig.language ? 'javascript'
functionName: @name
diff --git a/app/views/play/level/tome/spell_palette_view.coffee b/app/views/play/level/tome/spell_palette_view.coffee
index 3a1056733..776c28f53 100644
--- a/app/views/play/level/tome/spell_palette_view.coffee
+++ b/app/views/play/level/tome/spell_palette_view.coffee
@@ -39,6 +39,7 @@ module.exports = class SpellPaletteView extends View
for entry in entryColumn
col.append entry.el
entry.render() # Render after appending so that we can access parent container for popover
+ $('.nano').nanoScroller()
createPalette: ->
lcs = @supermodel.getModels LevelComponent
diff --git a/app/views/play/level/tome/spell_view.coffee b/app/views/play/level/tome/spell_view.coffee
index 4951b6915..5a8dd6a57 100644
--- a/app/views/play/level/tome/spell_view.coffee
+++ b/app/views/play/level/tome/spell_view.coffee
@@ -82,7 +82,7 @@ module.exports = class SpellView extends View
@ace.setShowPrintMargin false
@ace.setShowInvisibles aceConfig.invisibles
@ace.setBehavioursEnabled aceConfig.behaviors
- @ace.setAnimatedScroll true
+ @ace.setAnimatedScroll true
@ace.setKeyboardHandler @keyBindings[aceConfig.keyBindings ? 'default']
@toggleControls null, @writable
@aceSession.selection.on 'changeCursor', @onCursorActivity
diff --git a/app/views/play/level_view.coffee b/app/views/play/level_view.coffee
index 083d99cb0..ea5270c2e 100644
--- a/app/views/play/level_view.coffee
+++ b/app/views/play/level_view.coffee
@@ -126,6 +126,7 @@ module.exports = class PlayLevelView extends View
@insertSubView @loadingView = new LoadingView {}
@$el.find('#level-done-button').hide()
super()
+ $('body').addClass('is-playing')
onLevelLoaderProgressChanged: ->
return if @seenDocs
@@ -413,7 +414,7 @@ module.exports = class PlayLevelView extends View
return if @alreadyLoadedState
@alreadyLoadedState = true
state = @originalSessionState
- if state.frame
+ if state.frame and @level.get('type') isnt 'ladder' # https://github.com/codecombat/codecombat/issues/714
Backbone.Mediator.publish 'level-set-time', { time: 0, frameOffset: state.frame }
if state.selected
# TODO: Should also restore selected spell here by saving spellName
diff --git a/app/views/play/spectate_view.coffee b/app/views/play/spectate_view.coffee
index da6f5e611..7a3058212 100644
--- a/app/views/play/spectate_view.coffee
+++ b/app/views/play/spectate_view.coffee
@@ -119,6 +119,7 @@ module.exports = class SpectateLevelView extends View
@insertSubView @loadingView = new LoadingView {}
@$el.find('#level-done-button').hide()
super()
+ $('body').addClass('is-playing')
onLevelLoaderProgressChanged: ->
return if @seenDocs
diff --git a/bower.json b/bower.json
index e73834bc5..2c7d15146 100644
--- a/bower.json
+++ b/bower.json
@@ -24,7 +24,7 @@
"test"
],
"dependencies": {
- "jquery": "~2.0.3",
+ "jquery": "~2.1.0",
"lodash": "~2.4.1",
"backbone": "1.1.0",
"jquery-mousewheel": "~3.1.9",
@@ -36,7 +36,13 @@
"underscore.string": "~2.3.3",
"firebase": "~1.0.2",
"catiline": "~2.9.3",
- "d3": "~3.4.4"
+ "d3": "~3.4.4",
+ "jsondiffpatch": "~0.1.5",
+ "nanoscroller": "~0.8.0",
+ "jquery.tablesorter": "~2.15.13",
+ "treema": ">=0.0.1",
+ "bootstrap": "~3.1.1",
+ "validated-backbone-mediator": "~0.1.3"
},
"overrides": {
"backbone": {
@@ -50,6 +56,25 @@
},
"underscore.string": {
"main": "lib/underscore.string.js"
+ },
+ "jsondiffpatch": {
+ "main": ["build/bundle-full.js", "build/formatters.js", "src/formatters/html.css"]
+ },
+ "jquery.tablesorter": {
+ "main": [
+ "js/jquery.tablesorter.js",
+ "js/jquery.tablesorter.widgets.js",
+ "css/theme.bootstrap.css"
+ ]
+ },
+ "bootstrap": {
+ "main": [
+ "./dist/js/bootstrap.js",
+ "./dist/fonts/glyphicons-halflings-regular.eot",
+ "./dist/fonts/glyphicons-halflings-regular.svg",
+ "./dist/fonts/glyphicons-halflings-regular.ttf",
+ "./dist/fonts/glyphicons-halflings-regular.woff"
+ ]
}
}
}
diff --git a/config.coffee b/config.coffee
index 1a860cb76..27c476980 100644
--- a/config.coffee
+++ b/config.coffee
@@ -41,27 +41,19 @@ exports.config =
'test/javascripts/test-vendor.js': /^test[\/\\](?=vendor)/
order:
before: [
- 'bower_components/jquery/jquery.js'
+ 'bower_components/jquery/dist/jquery.js'
'bower_components/lodash/dist/lodash.js'
'bower_components/backbone/backbone.js'
# Twitter Bootstrap jquery plugins
- 'vendor/scripts/bootstrap/transition.js'
- 'vendor/scripts/bootstrap/affix.js'
- 'vendor/scripts/bootstrap/alert.js'
- 'vendor/scripts/bootstrap/button.js'
- 'vendor/scripts/bootstrap/carousel.js'
- 'vendor/scripts/bootstrap/collapse.js'
- 'vendor/scripts/bootstrap/dropdown.js'
- 'vendor/scripts/bootstrap/modal.js'
- 'vendor/scripts/bootstrap/scrollspy.js'
- 'vendor/scripts/bootstrap/tab.js'
- 'vendor/scripts/bootstrap/tooltip.js'
+ 'bower_components/bootstrap/dist/bootstrap.js'
# CreateJS dependencies
'vendor/scripts/easeljs-NEXT.combined.js'
'vendor/scripts/preloadjs-NEXT.combined.js'
'vendor/scripts/soundjs-NEXT.combined.js'
'vendor/scripts/tweenjs-NEXT.combined.js'
'vendor/scripts/movieclip-NEXT.min.js'
+ # Validated Backbone Mediator dependencies
+ 'bower_components/tv4/tv4.js'
# Aether before box2d for some strange Object.defineProperty thing
'bower_components/aether/build/aether.js'
@@ -70,9 +62,12 @@ exports.config =
stylesheets:
defaultExtension: 'sass'
joinTo:
- 'stylesheets/app.css': /^(app|vendor)/
+ 'stylesheets/app.css': /^(app|vendor|bower_components)/
order:
- before: ['app/styles/bootstrap.scss']
+ before: [
+ 'app/styles/bootstrap.scss'
+ 'vendor/styles/nanoscroller.scss'
+ ]
templates:
defaultExtension: 'jade'
joinTo: 'javascripts/app.js'
diff --git a/package.json b/package.json
index 665ac0131..ee0dcc201 100644
--- a/package.json
+++ b/package.json
@@ -46,7 +46,7 @@
"mongoose": "3.8.x",
"mongoose-text-search": "~0.0.2",
"request": "2.12.x",
- "tv4": "1.0.11",
+ "tv4": "1.0.x",
"lodash": "~2.0.0",
"underscore.string": "2.3.x",
"async": "0.2.x",
@@ -56,7 +56,7 @@
"graceful-fs": "~2.0.1",
"node-force-domain": "~0.1.0",
"mailchimp-api": "2.0.x",
- "express-useragent": "~0.0.9",
+ "express-useragent": "~0.0.9",
"gridfs-stream": "0.4.x",
"stream-buffers": "0.2.x",
"sendwithus": "2.0.x",
@@ -73,7 +73,6 @@
"css-brunch": "> 1.0 < 1.8",
"jade-brunch": "> 1.0 < 1.8",
"uglify-js-brunch": "~1.7.4",
- "clean-css-brunch": "> 1.0 < 1.8",
"auto-reload-brunch": "> 1.0 < 1.8",
"brunch": "~1.7.4",
"jasmine-node": "1.13.x",
diff --git a/scripts/devSetup/directoryController.py b/scripts/devSetup/directoryController.py
index 6585ff5f8..5087db888 100644
--- a/scripts/devSetup/directoryController.py
+++ b/scripts/devSetup/directoryController.py
@@ -20,23 +20,30 @@ class DirectoryController(object):
def bin_directory(self):
return self.root_install_directory
+ def mkdir(self, path):
+ if os.path.exists(path):
+ print(u"Skipping creation of " + path + " because it exists.")
+ else:
+ os.mkdir(path)
+
def create_directory_in_tmp(self,subdirectory):
- os.mkdir(self.generate_path_for_directory_in_tmp(subdirectory))
+ path = self.generate_path_for_directory_in_tmp(subdirectory)
+ self.mkdir(path)
def generate_path_for_directory_in_tmp(self,subdirectory):
return self.tmp_directory + os.sep + subdirectory
def create_directory_in_bin(self,subdirectory):
full_path = self.bin_directory + os.sep + subdirectory
- os.mkdir(full_path)
+ self.mkdir(full_path)
def create_base_directories(self):
shutil.rmtree(self.root_dir + os.sep + "coco" + os.sep + "node_modules",ignore_errors=True) #just in case
try:
- if os.path.exists(self.tmp_directory):
- self.remove_tmp_directory()
- os.mkdir(self.tmp_directory)
+ if os.path.exists(self.tmp_directory):
+ self.remove_tmp_directory()
+ os.mkdir(self.tmp_directory)
except:
- raise errors.CoCoError(u"There was an error creating the directory structure, do you have correct permissions? Please remove all and start over.")
+ raise errors.CoCoError(u"There was an error creating the directory structure, do you have correct permissions? Please remove all and start over.")
def remove_directories(self):
shutil.rmtree(self.bin_directory + os.sep + "node",ignore_errors=True)
diff --git a/scripts/devSetup/factories.py b/scripts/devSetup/factories.py
index f14f922ec..1eab847bb 100644
--- a/scripts/devSetup/factories.py
+++ b/scripts/devSetup/factories.py
@@ -37,10 +37,12 @@ class SetupFactory(object):
try:
mongo_version_string = subprocess.check_output("mongod --version",shell=True)
mongo_version_string = mongo_version_string.decode(encoding='UTF-8')
- except:
- print("Mongod not found.")
+ except Exception as e:
+ print("Mongod not found: %s"%e)
if "v2.6." not in mongo_version_string:
- print("MongoDB not found, so installing...")
+ if mongo_version_string:
+ print("Had MongoDB version: %s"%mongo_version_string)
+ print("MongoDB not found, so installing a local copy...")
self.mongo.download_dependencies()
self.mongo.install_dependencies()
self.node.download_dependencies()
diff --git a/scripts/devSetup/mongo.py b/scripts/devSetup/mongo.py
index 88de736af..eb57ea452 100644
--- a/scripts/devSetup/mongo.py
+++ b/scripts/devSetup/mongo.py
@@ -8,7 +8,7 @@ import os
from configuration import Configuration
from dependency import Dependency
import sys
-
+import shutil
class MongoDB(Dependency):
def __init__(self,configuration):
@@ -32,13 +32,20 @@ class MongoDB(Dependency):
def bashrc_string(self):
return "COCO_MONGOD_PATH=" + self.config.directory.bin_directory + os.sep + u"mongo" + os.sep +"bin" + os.sep + "mongod"
+
def download_dependencies(self):
- self.downloader.download()
- self.downloader.decompress()
+ install_directory = self.config.directory.bin_directory + os.sep + u"mongo"
+ if os.path.exists(install_directory):
+ print(u"Skipping MongoDB download because " + install_directory + " exists.")
+ else:
+ self.downloader.download()
+ self.downloader.decompress()
def install_dependencies(self):
install_directory = self.config.directory.bin_directory + os.sep + u"mongo"
- import shutil
- shutil.copytree(self.findUnzippedMongoBinPath(),install_directory)
+ if os.path.exists(install_directory):
+ print(u"Skipping creation of " + install_directory + " because it exists.")
+ else:
+ shutil.copytree(self.findUnzippedMongoBinPath(),install_directory)
def findUnzippedMongoBinPath(self):
return self.downloader.download_directory + os.sep + \
diff --git a/scripts/devSetup/node.py b/scripts/devSetup/node.py
index 065634aad..8fb1265d8 100644
--- a/scripts/devSetup/node.py
+++ b/scripts/devSetup/node.py
@@ -37,19 +37,27 @@ class Node(Dependency):
return self.config.directory.bin_directory
def download_dependencies(self):
- self.downloader.download()
- self.downloader.decompress()
+ install_directory = self.config.directory.bin_directory + os.sep + u"node"
+ if os.path.exists(install_directory):
+ print(u"Skipping Node download because " + install_directory + " exists.")
+ else:
+ self.downloader.download()
+ self.downloader.decompress()
def bashrc_string(self):
return "COCO_NODE_PATH=" + self.config.directory.bin_directory + os.sep + u"node" + os.sep + "bin" + os.sep +"node"
def install_dependencies(self):
install_directory = self.config.directory.bin_directory + os.sep + u"node"
#check for node here
- unzipped_node_path = self.findUnzippedNodePath()
if self.config.system.operating_system in ["mac","linux"] and not which("node"):
+ unzipped_node_path = self.findUnzippedNodePath()
print("Copying node into /usr/local/bin/...")
shutil.copy(unzipped_node_path + os.sep + "bin" + os.sep + "node","/usr/local/bin/")
os.chmod("/usr/local/bin/node",S_IRWXG|S_IRWXO|S_IRWXU)
- shutil.copytree(self.findUnzippedNodePath(),install_directory)
+ if os.path.exists(install_directory):
+ print(u"Skipping creation of " + install_directory + " because it exists.")
+ else:
+ unzipped_node_path = self.findUnzippedNodePath()
+ shutil.copytree(self.findUnzippedNodePath(),install_directory)
wants_to_upgrade = True
if self.check_if_executable_installed(u"npm"):
warning_string = u"A previous version of npm has been found. \nYou may experience problems if you have a version of npm that's too old.Would you like to upgrade?(y/n) "
diff --git a/scripts/windows/coco-dev-setup/batch/config/config.coco b/scripts/windows/coco-dev-setup/batch/config/config.coco
index eba46b0f4..da381689f 100755
--- a/scripts/windows/coco-dev-setup/batch/config/config.coco
+++ b/scripts/windows/coco-dev-setup/batch/config/config.coco
@@ -1,8 +1,9 @@
- 1.2
+ 3.5
GlenDC
CodeCombat.com © 2013-2014
https://github.com/codecombat/codecombat.git
git@github.com:codecombat/codecombat.git
+ http://23.21.59.137/dump.tar.gz
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/config/downloads.coco b/scripts/windows/coco-dev-setup/batch/config/downloads.coco
index 771189954..f8906cbb4 100755
--- a/scripts/windows/coco-dev-setup/batch/config/downloads.coco
+++ b/scripts/windows/coco-dev-setup/batch/config/downloads.coco
@@ -11,7 +11,6 @@
http://nodejs.org/dist/v0.10.25/x64/node-v0.10.25-x64.msi
http://dl.bintray.com/oneclick/rubyinstaller/rubyinstaller-2.0.0-p353-x64.exe?direct
http://s3.amazonaws.com/CodeCombatLargeFiles/python-64.msi
- http://download.microsoft.com/download/A/6/A/A6AC035D-DA3F-4F0C-ADA4-37C8E5D34E3D/winsdk_web.exe
http://download.microsoft.com/download/1/6/B/16B06F60-3B20-4FF2-B699-5E9B7962F9AE/VSU_4/vcredist_x64.exe
@@ -19,20 +18,28 @@
http://download.microsoft.com/download/C/6/D/C6D0FD4E-9E53-4897-9B91-836EBA2AACD3/vcredist_x86.exe
-
+
- http://fastdl.mongodb.org/win32/mongodb-win32-i386-2.5.4.zip
+ https://fastdl.mongodb.org/win32/mongodb-win32-i386-2.6.0.zip
- http://fastdl.mongodb.org/win32/mongodb-win32-x86_64-2008plus-2.5.4.zip
+ https://fastdl.mongodb.org/win32/mongodb-win32-x86_64-2008plus-2.6.0.zip
+
+
+
+
+ https://fastdl.mongodb.org/win32/mongodb-win32-i386-2.6.0.zip
+
+
+ https://fastdl.mongodb.org/win32/mongodb-win32-x86_64-2008plus-2.6.0.zip
- http://fastdl.mongodb.org/win32/mongodb-win32-i386-2.5.4.zip
+ https://fastdl.mongodb.org/win32/mongodb-win32-i386-2.6.0.zip
- http://fastdl.mongodb.org/win32/mongodb-win32-x86_64-2.5.4.zip
+ https://fastdl.mongodb.org/win32/mongodb-win32-x86_64-2.6.0.zip
-
\ No newline at end of file
+
diff --git a/scripts/windows/coco-dev-setup/batch/config/license.coco b/scripts/windows/coco-dev-setup/batch/config/localized/license-nl.coco
similarity index 100%
rename from scripts/windows/coco-dev-setup/batch/config/license.coco
rename to scripts/windows/coco-dev-setup/batch/config/localized/license-nl.coco
diff --git a/scripts/windows/coco-dev-setup/batch/config/localized/license.coco b/scripts/windows/coco-dev-setup/batch/config/localized/license.coco
new file mode 100755
index 000000000..9b753bf10
--- /dev/null
+++ b/scripts/windows/coco-dev-setup/batch/config/localized/license.coco
@@ -0,0 +1,10 @@
+
+The MIT License (MIT)
+
+Copyright (c) 2014 CodeCombat Inc. and other contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in allcopies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN sCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THESOFTWARE.
diff --git a/scripts/windows/coco-dev-setup/batch/config/localized/readme-nl.coco b/scripts/windows/coco-dev-setup/batch/config/localized/readme-nl.coco
new file mode 100755
index 000000000..40665c28c
--- /dev/null
+++ b/scripts/windows/coco-dev-setup/batch/config/localized/readme-nl.coco
@@ -0,0 +1,29 @@
+ _____ _ _____ _ _
+ / __ \ | | / __ \ | | | |
+ | / \/ ___ __| | ___ | / \/ ___ _ __ ___ | |__ __ _| |_
+ | | / _ \ / _` |/ _ \ | | / _ \| '_ ` _ \| '_ \ / _` | __|
+ | \__/\ (_) | (_| | __/ | \__/\ (_) | | | | | | |_) | (_| | |_
+ \____/\___/ \__,_|\___| \____/\___/|_| |_| |_|_.__/ \__,_|\__|
+
+=============================================================================
+
+Congratulations, you are now part of the CodeCombat community.
+Now that your Develop Environment has been setup, you are ready to start
+contributing and help us make this world a better place.
+
+Do you have questions or would you like to meet us?
+Talk with us on hipchat @ https://www.hipchat.com/g3plnOKqa
+
+Another way to reach is, is by visiting our forum.
+You can find it @ http://discourse.codecombat.com/
+
+You can read about the latest developments on our blog site.
+This one can be found @ http://blog.codecombat.com/
+
+Last but not least, you can find most of our documentation
+and information on our wiki @ https://github.com/codecombat/codecombat/wiki
+
+We hope you'll enjoy yourself within our community, just as much as us.
+
+
+ - Nick, George, Scott, Michael, Jeremy and Glen
diff --git a/scripts/windows/coco-dev-setup/batch/config/readme.coco b/scripts/windows/coco-dev-setup/batch/config/localized/readme.coco
old mode 100644
new mode 100755
similarity index 97%
rename from scripts/windows/coco-dev-setup/batch/config/readme.coco
rename to scripts/windows/coco-dev-setup/batch/config/localized/readme.coco
index ccce0d398..40665c28c
--- a/scripts/windows/coco-dev-setup/batch/config/readme.coco
+++ b/scripts/windows/coco-dev-setup/batch/config/localized/readme.coco
@@ -1,29 +1,29 @@
- _____ _ _____ _ _
- / __ \ | | / __ \ | | | |
- | / \/ ___ __| | ___ | / \/ ___ _ __ ___ | |__ __ _| |_
- | | / _ \ / _` |/ _ \ | | / _ \| '_ ` _ \| '_ \ / _` | __|
- | \__/\ (_) | (_| | __/ | \__/\ (_) | | | | | | |_) | (_| | |_
- \____/\___/ \__,_|\___| \____/\___/|_| |_| |_|_.__/ \__,_|\__|
-
-=============================================================================
-
-Congratulations, you are now part of the CodeCombat community.
-Now that your Develop Environment has been setup, you are ready to start
-contributing and help us make this world a better place.
-
-Do you have questions or would you like to meet us?
-Talk with us on hipchat @ https://www.hipchat.com/g3plnOKqa
-
-Another way to reach is, is by visiting our forum.
-You can find it @ http://discourse.codecombat.com/
-
-You can read about the latest developments on our blog site.
-This one can be found @ http://blog.codecombat.com/
-
-Last but not least, you can find most of our documentation
-and information on our wiki @ https://github.com/codecombat/codecombat/wiki
-
-We hope you'll enjoy yourself within our community, just as much as us.
-
-
- - Nick, George, Scott, Michael, Jeremy and Glen
+ _____ _ _____ _ _
+ / __ \ | | / __ \ | | | |
+ | / \/ ___ __| | ___ | / \/ ___ _ __ ___ | |__ __ _| |_
+ | | / _ \ / _` |/ _ \ | | / _ \| '_ ` _ \| '_ \ / _` | __|
+ | \__/\ (_) | (_| | __/ | \__/\ (_) | | | | | | |_) | (_| | |_
+ \____/\___/ \__,_|\___| \____/\___/|_| |_| |_|_.__/ \__,_|\__|
+
+=============================================================================
+
+Congratulations, you are now part of the CodeCombat community.
+Now that your Develop Environment has been setup, you are ready to start
+contributing and help us make this world a better place.
+
+Do you have questions or would you like to meet us?
+Talk with us on hipchat @ https://www.hipchat.com/g3plnOKqa
+
+Another way to reach is, is by visiting our forum.
+You can find it @ http://discourse.codecombat.com/
+
+You can read about the latest developments on our blog site.
+This one can be found @ http://blog.codecombat.com/
+
+Last but not least, you can find most of our documentation
+and information on our wiki @ https://github.com/codecombat/codecombat/wiki
+
+We hope you'll enjoy yourself within our community, just as much as us.
+
+
+ - Nick, George, Scott, Michael, Jeremy and Glen
diff --git a/scripts/windows/coco-dev-setup/batch/config/localized/tips-nl.coco b/scripts/windows/coco-dev-setup/batch/config/localized/tips-nl.coco
new file mode 100755
index 000000000..bc12d3bf5
--- /dev/null
+++ b/scripts/windows/coco-dev-setup/batch/config/localized/tips-nl.coco
@@ -0,0 +1,8 @@
+ 1) Antwoord voorzichtig en juist, indien er een vraag gesteld wordt.
+ 2) Deze installatie is nog steeds in beta en kan bugs bevatten.
+ 3) Rapporteer bugs op 'https://github.com/codecombat/codecombat/issues'
+ 4) Heb je vragen of suggesties? Praat met ons op HipChat via CodeCombat.com
+
+ Je kan een Engelstalige stappengids
+ voor deze installatie vinden op onze wiki:
+ github.com/codecombat/codecombat/wiki/Setup-on-Windows:-a-step-by-step-guide
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/config/tips.coco b/scripts/windows/coco-dev-setup/batch/config/localized/tips.coco
similarity index 100%
rename from scripts/windows/coco-dev-setup/batch/config/tips.coco
rename to scripts/windows/coco-dev-setup/batch/config/localized/tips.coco
diff --git a/scripts/windows/coco-dev-setup/batch/configuration.exe b/scripts/windows/coco-dev-setup/batch/configuration.exe
new file mode 100755
index 000000000..28177aeab
Binary files /dev/null and b/scripts/windows/coco-dev-setup/batch/configuration.exe differ
diff --git a/scripts/windows/coco-dev-setup/batch/localisation/de.coco b/scripts/windows/coco-dev-setup/batch/localisation/de.coco
deleted file mode 100644
index 6332041f4..000000000
--- a/scripts/windows/coco-dev-setup/batch/localisation/de.coco
+++ /dev/null
@@ -1,82 +0,0 @@
-
-
-
- Deutsch
- Ab jetzt senden wir unser Feedback in Englisch!
-
-
-
- -Bit System erkannt.
- Es wurde das Betriebssystem
- erkannt.
- Windows XP wird nicht unterstützt. Installation abgebrochen.
-
-
- Sind die für CodeCombat benötigten Programme bereits installiert?
- Wir empfehlen Ihnen, mit „Nein“ zu antorten, falls Sie unsicher sind.
- Ãœberspringe Installation der Programme...
- Ohne Software von Drittanbietern könnte CodeCombat nicht entwickelt werden.
- Aus diesem Grund müssen Sie diese Software installieren,
- um sich in der Community zu engagieren.
- Wenn Sie ein Programm bereits installiert haben, brechen Sie die Installation bitte ab.
- Make sure to select the option that adds the application to your Windows Path, if the option is available.
- Haben Sie bereits die aktuellste Version von
- installiert?
- wird heruntergeladen...
- wird installiert...
- wird entpackt...
- wird aufgeräumt...
- Bitte geben Sie den kompletten Pfad an, an dem MongoDB installiert werden soll
-
-
-
-
- Wie Du bereits weißt, ist CodeCombat Open Source.
- Unser Quellcode ist komplett auf Github.
- Wenn Du möchtest, kannst du das komplette Git Repository selbst herunterladen und nach deinen wünschen einrichten.
- Allerdings empfehlen wir, dass du den Prozess statt dessen uns überlässt.
-
-
- Willst du das lokale Git Setup selbst vornehmen?
- Bit vergewissere dich, dass das Repository korrekt heruntergeladen wurde, bevor du fortfährst.
- Bitte schließe dieses Fenster nicht.
- Wenn du fertig bist, drücke eine beliebige Taste zum Fortfahren...
-
-
- Gebe bitte den kompletten Pfad zu deinem CodeCombat Git Repository ein:
- Bitte gib den kompletten Pfad ein, an dem du die CodeCombat Umgebung einrichten willst
- Diese Installation benötigt die Git Bash.
- Die Git Bash ist standardmäßig in 'C:\Program Files (x86)\Git' installiert.
- Die Git Bash ist standardmäßig in 'C:\Program Files\Git' installiert.
- Bitte gebe den kompletten Pfad zur Git Bash ein, oder drücke Enter, um den Standardpfad zu verwenden
- Willst du das Repository via SSH auschecken?
-
-
-
- Installing bower, brunch, nodemon and sendwithus...
- Installing bower packages...
- Installing sass...
- Installing npm...
- Starting brunch....
- Setting up a MongoDB database for you...
- Downloading the last version of the CodeCombat database...
-
-
-
- Dieser Pfad existiert bereits. Willst du ihn wirklich überschreiben?
- Dieser Pfad exisitert nicht. Bitte versuche es erneut...
-
-
- Die CodeCombat Entwicklungsumgebung wurde erfoglreich installiert.
- Vielen Dank für die Unterstützung und bis bald.
- Willst du das README lesen, um weitere Informationen zu erhalten?
-
-
- Von nun an kannst du die Entwicklungsumgebung starten unter
- einmal mit der Maus klicken.
- 1) Einfach Doppelklicken
- und warten bis die Entwicklungsumgebung fertig geladen hat.
- 2) Jetzt 'localhost:3000' in deinem bevorzugten Browser aufrufen.
- Fertig. Du bist nun bereit, bei CodeCombat mitzuarbeiten!
-
-
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/localisation/nl.coco b/scripts/windows/coco-dev-setup/batch/localisation/nl.coco
deleted file mode 100755
index 654d45c97..000000000
--- a/scripts/windows/coco-dev-setup/batch/localisation/nl.coco
+++ /dev/null
@@ -1,82 +0,0 @@
-
-
-
- Nederlands
- Vanaf nu geven we onze feedback in het Nederlands!
-
-
-
- -bit computer gedetecteerd.
- Het besturingsysteem
- is gedetecteerd.
- Wij ondersteunen Windows XP niet, installatie geanulleerd.
-
-
- Heb je alle benodige software al geinstalleerd?
- We raden aan dat je negatief antwoord indien je niet zeker bent.
- De installatie van software wordt geanulleerd...
- CodeCombat kon niet worden ontwikkeld zonder third-party software.
- Dat is waarom je deze software moet installeren,
- zodat je je kan beginnen met het bijdragen tot onze gemeenschap.
- Annuleer de installatie als je de applicatie al hebt.
- Zorg er zeker voor dat je de optie selecteert dat de applicatie aan je Windows Path toevoegt, als de optie beschikbaar is.
- Heb je al de laatste versie van
- geinstalleerd?
- is aan het downloaden...
- is aan het installeren...
- is aan het uitpakken...
- is aan het opkuisen...
- Geef het volledige pad op, waar mongodb mag worden geinstalleerd
-
-
-
-
- CodeCombat is opensource, zoals je waarschijnlijk wel al weet.
- Je kan al onze sourcecode vinden op Github.
- Indien je wil, kan je de Git setup manueel doen.
- Maar wij raden aan dat je ons dit automatisch laat afhandellen.
-
-
- Wil je de lokale Git setup manueel doen?
- Zorg er zeker voor dat jouw git repository correct is.
- Sluit dit venster niet alsjeblieft.
- Wanneer je klaar bent, druk dan eender welke toets om verder te gaan...
-
-
- Geef alsjeblieft het volledige pad van je CodeCombat git repository:
- Geef alsjeblieft het volledige pad waar je de CodeCombat Ontwikkelings omgeving will installeren
- Deze installatie maakt gebruik van Git Bash.
- Git bash is normaal geinstalleerd in 'C:\Program Files (x86)\Git'.
- Git bash is normaal geinstalleerd in 'C:\Program Files\Git'.
- Geef alsjeblieft het volledige pad op van Git Bash of druk gewoon op enter indien je het pad niet gewijzigd heeft
- Wil je het git project downloaden via ssh?
-
-
-
- Installing bower, brunch, nodemon and sendwithus...
- Installing bower packages...
- Installing sass...
- Installing npm...
- Starting brunch....
- Setting up a MongoDB database for you...
- Downloading the last version of the CodeCombat database...
-
-
-
- Dat pad bestaat al, ben je zeker dat je het wil overschrijven?
- Dat pad bestaat niet, probeer alsjeblieft opnieuw...
-
-
- De installatie van de CodeCombat-Ontwikkelings omgeving was succesvol.
- Alvast bedankt voor al je werk en tot binnenkort.
- Wil je de LEESMIJ lezen voor meer informatie?
-
-
- Vanaf nu kan je de ontwikkelings omgeving opstarten
- met het gemak van een enkele muisklik.
- 1) Dubbelklik op
- en laat de omgeving opstarten.
- 2) Nu kan je 'localhost:3000' openen in je browser naar voorkeur.
- Dat is het, je bent nu klaar om te starten met je werk aan CodeCombat.
-
-
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/localisation/zh-HANS.coco b/scripts/windows/coco-dev-setup/batch/localisation/zh-HANS.coco
deleted file mode 100644
index 24f6b8057..000000000
--- a/scripts/windows/coco-dev-setup/batch/localisation/zh-HANS.coco
+++ /dev/null
@@ -1,82 +0,0 @@
-
-
-
- 简体ä¸æ–‡
- ç›®å‰æˆ‘们åªèƒ½ç”¨è‹±æ–‡ç»™ä½ å馈ï¼
-
-
-
- -ä½ç³»ç»Ÿ.
- æ“作系统
- 被侦测到.
- 我们ä¸æ”¯æŒ Windows XP, 安装å–消.
-
-
- ä½ æ˜¯å¦å·²ç»å®‰è£…好è¿è¡Œ CodeCombat 所需的所有软件?
- å¦‚æžœä½ ä¸ç¡®å®šçš„è¯è¯·å›žç” No.
- æ£åœ¨è·³è¿‡æ¤è½¯ä»¶çš„安装...
- CodeCombat æ— æ³•åœ¨ä¸ä½¿ç”¨ç¬¬ä¸‰æ–¹æœåŠ¡çš„情况下开å‘.
- è¿™å°±æ˜¯ä¸ºä»€ä¹ˆä½ éœ€è¦å®‰è£…这些软件,
- 为了开始给我们的开æºç¤¾åŒºåšè´¡çŒ®.
- å¦‚æžœä½ å·²ç»æœ‰äº†è¿™äº›è½¯ä»¶ 请å–消安装.
- Make sure to select the option that adds the application to your Windows Path, if the option is available.
- ä½ æ˜¯å¦å·²ç»å®‰è£…了最新版本的
- ?
- æ£åœ¨ä¸‹è½½...
- æ£åœ¨å®‰è£…...
- æ£åœ¨è§£åŽ‹...
- æ£åœ¨æ¸…ç†...
- è¯·è¾“å…¥ä½ å¸Œæœ›å®‰è£… mongodb 的文件夹的全路径
-
-
-
-
- CodeCombat 是开æºçš„.
- 我们的所有æºä»£ç 都放在了 Github.
- ä½ å¯ä»¥é€‰æ‹©è‡ªå·±æ‰‹å·¥å®‰è£… Git.
- 但我们ä»ç„¶å»ºè®®è®©ç¨‹åºè‡ªåŠ¨æ›¿ä½ 完æˆ.
-
-
- ä½ æ˜¯å¦æƒ³è‡ªå·±æ‰‹å·¥å®‰è£…本地 Git 安装?
- 请确ä¿åœ¨å¼€å§‹å¤„ç†å‰, ä½ æœ‰æ£ç¡®è®¾ç½®å¥½ä½ 的库.
- 请ä¸è¦å…³é—æ¤çª—å£.
- å¦‚æžœä½ å‡†å¤‡å¥½äº†, 请按任æ„键继ç»...
-
-
- è¯·è¾“å…¥ä½ CodeCombat git库的全路径:
- è¯·è¾“å…¥ä½ æƒ³å®‰è£… CodeCombat 环境的全路径
- è¿™é¡¹å®‰è£…éœ€è¦ Git Bash.
- Git bash 默认安装在 'C:\Program Files (x86)\Git'.
- Git bash 默认安装在 'C:\Program Files\Git'.
- 请输入 git bash 的安装全路径, å¦‚æžœä½ å®‰è£…çš„æ˜¯é»˜è®¤è·¯å¾„, 那么直接输入回车å³å¯
- ä½ æ˜¯å¦æƒ³ä½¿ç”¨ ssh æ¥æ£€å‡º(checkout)库(repository)?
-
-
-
- æ£åœ¨å®‰è£… bower, brunch, nodemon å’Œ sendwithus...
- æ£åœ¨ç”¨ bower 安装ä¾èµ–包...
- æ£åœ¨å®‰è£… sass...
- æ£åœ¨å®‰è£… npm...
- æ£åœ¨å¼€å¯ brunch....
- æ£åœ¨ä¸ºä½ 设置 MongoDB æ•°æ®åº“...
- æ£åœ¨ä¸‹è½½ CodeCombat æ•°æ®åº“的最新版本...
-
-
-
- 这个路径已ç»å˜åœ¨, ä½ æƒ³è¦è¦†ç›–它å—?
- 这个路径ä¸å˜åœ¨, 请å†æ¬¡å°è¯•...
-
-
- CodeCombat å¼€å‘环境的æ建已æˆåŠŸ.
- æ„Ÿè°¢~ 我们会很快å†æ¬¡è§é¢çš„ :)
- ä½ æ˜¯å¦æƒ³é˜…读 README 文件以了解更多信æ¯?
-
-
- From now on you can start the dev. environment at
- the touch of a single mouse click.
- 1) åŒå‡»æ–‡ä»¶
- å¯åŠ¨å¼€å‘环境.
- 2) 在æµè§ˆå™¨é‡Œè®¿é—® 'localhost:3000'
- å¥½äº†ï¼Œä½ çŽ°åœ¨å¯ä»¥å¼€å§‹å¼€å‘ CodeCombat 了!
-
-
diff --git a/scripts/windows/coco-dev-setup/batch/localisation/zh-HANT.coco b/scripts/windows/coco-dev-setup/batch/localisation/zh-HANT.coco
deleted file mode 100644
index 3ef2d22d7..000000000
--- a/scripts/windows/coco-dev-setup/batch/localisation/zh-HANT.coco
+++ /dev/null
@@ -1,82 +0,0 @@
-
-
-
- ç¹ä½“ä¸æ–‡
- From now on we'll send our feedback in English!
-
-
-
- -bit computer detected.
- The operating system
- was detected.
- We don't support Windows XP, installation cancelled.
-
-
- Have you already installed all the software needed for CodeCombat?
- We recommand that you reply negative in case you're not sure.
- Skipping the installation of the software...
- CodeCombat couldn't be developed without third-party software.
- That's why you'll need to install this software,
- in order to start contributing to our community.
- Cancel the installation if you already have the application.
- Make sure to select the option that adds the application to your Windows Path, if the option is available.
- Do you already have the latest version of
- installed?
- is downloading...
- is installing...
- is unzipping...
- is cleaning...
- Please define the full path where mongodb should be installed
-
-
-
-
- CodeCombat is opensource, like you already know.
- All our sourcecode can be found online at Github.
- You can choose to do the entire Git setup yourself.
- However we recommend that you instead let us handle it instead.
-
-
- Do you want to do the Local Git setup manually yourself?
- Make sure you have correctly setup your repository before processing.
- Do not close this window please.
- When you're ready, press any key to continue...
-
-
- Please give the full path of your CodeCombat git repository:
- Please enter the full path where you want to install your CodeCombat environment
- This installation requires Git Bash.
- Git bash is by default installed at 'C:\Program Files (x86)\Git'.
- Git bash is by default installed at 'C:\Program Files\Git'.
- Please enter the full path where git bash is installed or just press enter if it's in the default location
- Do you want to checkout the repository via ssh?
-
-
-
- Installing bower, brunch, nodemon and sendwithus...
- Installing bower packages...
- Installing sass...
- Installing npm...
- Starting brunch....
- Setting up a MongoDB database for you...
- Downloading the last version of the CodeCombat database...
-
-
-
- That path already exists, are you sure you want to overwrite it?
- That path doesn't exist. Please try again...
-
-
- The setup of the CodeCombat Dev. Environment was succesfull.
- Thank you already for your contribution and see you soon.
- Do you want to read the README for more information?
-
-
- From now on you can start the dev. environment at
- the touch of a single mouse click.
- 1) Just double click
- and let the environment start up.
- 2) Now just open 'localhost:3000' in your prefered browser.
- That's it, you're now ready to start working on CodeCombat!
-
-
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/localization/de.coco b/scripts/windows/coco-dev-setup/batch/localization/de.coco
new file mode 100644
index 000000000..2b793068f
--- /dev/null
+++ b/scripts/windows/coco-dev-setup/batch/localization/de.coco
@@ -0,0 +1,192 @@
+<<<<<<< HEAD:scripts/windows/coco-dev-setup/batch/localisation/de.coco
+
+
+
+ Deutsch
+ Ab jetzt senden wir unser Feedback in Englisch!
+
+
+
+ -Bit System erkannt.
+ Es wurde das Betriebssystem
+ erkannt.
+ Windows XP wird nicht unterstützt. Installation abgebrochen.
+
+
+ Sind die für CodeCombat benötigten Programme bereits installiert?
+ Wir empfehlen Ihnen, mit „Nein“ zu antorten, falls Sie unsicher sind.
+ Ãœberspringe Installation der Programme...
+ Ohne Software von Drittanbietern könnte CodeCombat nicht entwickelt werden.
+ Aus diesem Grund müssen Sie diese Software installieren,
+ um sich in der Community zu engagieren.
+ Wenn Sie ein Programm bereits installiert haben, brechen Sie die Installation bitte ab.
+ Make sure to select the option that adds the application to your Windows Path, if the option is available.
+ Haben Sie bereits die aktuellste Version von
+ installiert?
+ wird heruntergeladen...
+ wird installiert...
+ wird entpackt...
+ wird aufgeräumt...
+ Bitte geben Sie den kompletten Pfad an, an dem MongoDB installiert werden soll
+
+
+
+
+ Wie Du bereits weißt, ist CodeCombat Open Source.
+ Unser Quellcode ist komplett auf Github.
+ Wenn Du möchtest, kannst du das komplette Git Repository selbst herunterladen und nach deinen wünschen einrichten.
+ Allerdings empfehlen wir, dass du den Prozess statt dessen uns überlässt.
+
+
+ Willst du das lokale Git Setup selbst vornehmen?
+ Bit vergewissere dich, dass das Repository korrekt heruntergeladen wurde, bevor du fortfährst.
+ Bitte schließe dieses Fenster nicht.
+ Wenn du fertig bist, drücke eine beliebige Taste zum Fortfahren...
+
+
+ Gebe bitte den kompletten Pfad zu deinem CodeCombat Git Repository ein:
+ Bitte gib den kompletten Pfad ein, an dem du die CodeCombat Umgebung einrichten willst
+ Diese Installation benötigt die Git Bash.
+ Die Git Bash ist standardmäßig in 'C:\Program Files (x86)\Git' installiert.
+ Die Git Bash ist standardmäßig in 'C:\Program Files\Git' installiert.
+ Bitte gebe den kompletten Pfad zur Git Bash ein, oder drücke Enter, um den Standardpfad zu verwenden
+ Willst du das Repository via SSH auschecken?
+
+
+
+ Installing bower, brunch, nodemon and sendwithus...
+ Installing bower packages...
+ Installing sass...
+ Installing npm...
+ Starting brunch....
+ Setting up a MongoDB database for you...
+ Downloading the last version of the CodeCombat database...
+
+
+
+ Dieser Pfad existiert bereits. Willst du ihn wirklich überschreiben?
+ Dieser Pfad exisitert nicht. Bitte versuche es erneut...
+
+
+ Die CodeCombat Entwicklungsumgebung wurde erfoglreich installiert.
+ Vielen Dank für die Unterstützung und bis bald.
+ Willst du das README lesen, um weitere Informationen zu erhalten?
+
+
+ Von nun an kannst du die Entwicklungsumgebung starten unter
+ einmal mit der Maus klicken.
+ 1) Einfach Doppelklicken
+ und warten bis die Entwicklungsumgebung fertig geladen hat.
+ 2) Jetzt 'localhost:3000' in deinem bevorzugten Browser aufrufen.
+ Fertig. Du bist nun bereit, bei CodeCombat mitzuarbeiten!
+
+=======
+
+
+
+ Deutsch
+ German
+ Before we start the installation, here are some tips:
+ Press any key to exit...
+
+
+ You have choosen Deutsch as your language.
+ Ab jetzt senden wir unser Feedback in Deutsch.
+
+
+ In order to continue the installation of the developers environment
+ you will have to read and agree with the following license:
+ Have you read the license and do you agree with it?
+ This setup can't happen without an agreement.
+ Installation and Setup of the CodeCombat environment is cancelled.
+
+
+
+ -Bit System erkannt.
+ Es wurde das Betriebssystem
+ erkannt.
+ Windows XP wird nicht unterstützt. Installation abgebrochen.
+
+
+ Sind die für CodeCombat benötigten Programme bereits installiert?
+ Wir empfehlen Ihnen, mit „Nein“ zu antorten, falls Sie unsicher sind.
+ Ãœberspringe Installation der Programme...
+ Ohne Software von Drittanbietern könnte CodeCombat nicht entwickelt werden.
+ Aus diesem Grund müssen Sie diese Software installieren,
+ um sich in der Community zu engagieren.
+ Wenn Sie ein Programm bereits installiert haben, brechen Sie die Installation bitte ab.
+ Make sure to select the option that adds the application to your Windows Path, if the option is available.
+ Haben Sie bereits die aktuellste Version von
+ installiert?
+ wird heruntergeladen...
+ wird installiert...
+ wird entpackt...
+ wird aufgeräumt...
+ Bitte geben Sie den kompletten Pfad an, an dem MongoDB installiert werden soll
+
+
+
+
+ Wie Du bereits weißt, ist CodeCombat Open Source.
+ Unser Quellcode ist komplett auf Github.
+ Wenn Du möchtest, kannst du das komplette Git Repository selbst herunterladen und nach deinen wünschen einrichten.
+ Allerdings empfehlen wir, dass du den Prozess statt dessen uns überlässt.
+
+
+ Willst du das lokale Git Setup selbst vornehmen?
+ Bit vergewissere dich, dass das Repository korrekt heruntergeladen wurde, bevor du fortfährst.
+ Bitte schließe dieses Fenster nicht.
+ Wenn du fertig bist, drücke eine beliebige Taste zum Fortfahren...
+
+
+ Gebe bitte den kompletten Pfad zu deinem CodeCombat Git Repository ein:
+ Bitte gib den kompletten Pfad ein, an dem du die CodeCombat Umgebung einrichten willst
+ Diese Installation benötigt die Git Bash.
+ Die Git Bash ist standardmäßig in 'C:\Program Files (x86)\Git' installiert.
+ Die Git Bash ist standardmäßig in 'C:\Program Files\Git' installiert.
+ Bitte gebe den kompletten Pfad zur Git Bash ein, oder drücke Enter, um den Standardpfad zu verwenden
+ Willst du das Repository via SSH auschecken?
+
+
+ You should have forked CodeCombat to your own GitHub Account by now...
+ Please enter your github information, to configure your local repository.
+ Username:
+ Password:
+ Thank you... Configuring your local repistory right now...
+
+
+
+ The installation of your local environment was succesfull!
+ You can now close this setup.
+ After that, you should open the configuration setup to automaticly configure your environment...
+
+
+ Installing bower, brunch, nodemon and sendwithus...
+ Installing bower packages...
+ Installing sass...
+ Installing npm...
+ Starting brunch....
+ Setting up a MongoDB database for you...
+ Downloading the last version of the CodeCombat database...
+
+ Don't close!
+
+
+ Dieser Pfad existiert bereits. Willst du ihn wirklich überschreiben?
+ Dieser Pfad exisitert nicht. Bitte versuche es erneut...
+
+
+ Die CodeCombat Entwicklungsumgebung wurde erfoglreich installiert.
+ Vielen Dank für die Unterstützung und bis bald.
+ Willst du das README lesen, um weitere Informationen zu erhalten?
+
+
+ Von nun an kannst du die Entwicklungsumgebung starten unter
+ einmal mit der Maus klicken.
+ 1) Einfach Doppelklicken
+ und warten bis die Entwicklungsumgebung fertig geladen hat.
+ 2) Jetzt 'localhost:3000' in deinem bevorzugten Browser aufrufen.
+ Fertig. Du bist nun bereit, bei CodeCombat mitzuarbeiten!
+
+>>>>>>> 072729acc34123c42250d361955438cfd8c210d7:scripts/windows/coco-dev-setup/batch/localization/de.coco
+
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/localisation/en.coco b/scripts/windows/coco-dev-setup/batch/localization/en.coco
similarity index 72%
rename from scripts/windows/coco-dev-setup/batch/localisation/en.coco
rename to scripts/windows/coco-dev-setup/batch/localization/en.coco
index 947890ee8..bef7c9ae6 100755
--- a/scripts/windows/coco-dev-setup/batch/localisation/en.coco
+++ b/scripts/windows/coco-dev-setup/batch/localization/en.coco
@@ -2,8 +2,21 @@
English
- From now on we'll send our feedback in English!
+ English
+ Before we start the installation, here are some tips:
+ Press any key to exit...
+
+ You have choosen English as your language.
+ From now on we'll send our feedback in English.
+
+
+ In order to continue the installation of the developers environment
+ you will have to read and agree with the following license:
+ Have you read the license and do you agree with it?
+ This setup can't happen without an agreement.
+ Installation and Setup of the CodeCombat environment is cancelled.
+
-bit computer detected.
@@ -51,7 +64,19 @@
Please enter the full path where git bash is installed or just press enter if it's in the default location
Do you want to checkout the repository via ssh?
+
+ You should have forked CodeCombat to your own GitHub Account by now...
+ Please enter your github information, to configure your local repository.
+ Username:
+ Password:
+ Thank you... Configuring your local repistory right now...
+
+
+ The installation of your local environment was succesfull!
+ You can now close this setup.
+ After that, you should open the configuration setup to automaticly configure your environment...
+
Installing bower, brunch, nodemon and sendwithus...
Installing bower packages...
@@ -61,6 +86,7 @@
Setting up a MongoDB database for you...
Downloading the last version of the CodeCombat database...
+ Don't close!
That path already exists, are you sure you want to overwrite it?
diff --git a/scripts/windows/coco-dev-setup/batch/localization/languages.coco b/scripts/windows/coco-dev-setup/batch/localization/languages.coco
new file mode 100755
index 000000000..2f3e2fe0d
--- /dev/null
+++ b/scripts/windows/coco-dev-setup/batch/localization/languages.coco
@@ -0,0 +1,6 @@
+en
+ru
+nl
+de
+zh-HANT
+zh-HANS
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/localization/nl.coco b/scripts/windows/coco-dev-setup/batch/localization/nl.coco
new file mode 100755
index 000000000..a969efb31
--- /dev/null
+++ b/scripts/windows/coco-dev-setup/batch/localization/nl.coco
@@ -0,0 +1,108 @@
+
+
+
+ Nederlands
+ Dutch
+ Voor we verder gaan met de installatie hier volgen enkele tips:
+ Druk een willekeurige toets in om af te sluiten...
+
+
+ Je hebt Nederlands gekozen als jouw taal naar keuze.
+ Vanaf nu geven we onze feedback in het Nederlands.
+
+
+ Om verder te gaan met de installatie van jouw CodeCombat omgeving
+ moet je de licentieovereenkomst lezen en ermee akkoord gaan.
+ Heb je de licentieovereenkomst gelezen en ga je ermee akkoord?
+ Deze installatie kan niet doorgaan zonder jouw akkoord.
+ De installatie van jouw Developers omgeving is nu geannulleerd.
+
+
+
+ -bit computer gedetecteerd.
+ Het besturingssysteem
+ is gedetecteerd.
+ Wij ondersteunen Windows XP niet, installatie geannuleerd.
+
+
+ Heb je alle benodige software al geïnstalleerd?
+ We raden aan dat je negatief antwoord indien je niet zeker bent.
+ De installatie van software wordt geannuleerd...
+ CodeCombat kon niet worden ontwikkeld zonder third-party software.
+ Dat is waarom je deze software moet installeren,
+ zodat je kan beginnen met het bijdragen tot onze gemeenschap.
+ Annuleer de installatie als je de applicatie al hebt.
+ Zorg er zeker voor dat je de optie selecteert die de applicatie aan je Windows Path toevoegt, als deze optie beschikbaar is.
+ Heb je al de laatste versie van
+ geïnstalleerd?
+ is aan het downloaden...
+ is aan het installeren...
+ is aan het uitpakken...
+ is aan het opkuisen...
+ Geef het volledige pad op waar mongodb mag worden geïnstalleerd
+
+
+
+
+ CodeCombat is open-source, zoals je waarschijnlijk wel al weet.
+ Je kunt al onze source code vinden op Github.
+ Indien je wil, kan je de Git setup ook manueel doen.
+ Maar wij raden aan dat je ons dit automatisch laat afhandelen.
+
+
+ Wil je de lokale Git setup manueel doen?
+ Zorg er zeker voor dat jouw git repository correct is.
+ Sluit dit venster alsjeblieft niet.
+ Wanneer je klaar bent, druk dan op eender welke toets om verder te gaan...
+
+
+ Geef alsjeblieft het volledige pad in van je CodeCombat git repository:
+ Geef alsjeblieft het volledige pad in waar je de CodeCombat ontwikkelingsomgeving wilt installeren
+ Deze installatie maakt gebruik van Git Bash.
+ Git bash is normaal gezien geïnstalleerd in 'C:\Program Files (x86)\Git'.
+ Git bash is normaal gezien geïnstalleerd in 'C:\Program Files\Git'.
+ Geef alsjeblieft het volledige pad op van Git Bash of druk gewoon op enter indien je het pad niet gewijzigd hebt.
+ Wil je het git project downloaden via ssh?
+
+
+ Je zou nu al een eigen CodeCombat-fork moeten hebben gekoppeld aan jouw GitHub account...
+ Geef jou GitHub informatie alstublieft, zodat wij jou lokale repositorie kunnen configureren.
+ Gebruikersnaam:
+ Wachtwoord:
+ Dank u, jouw lokaal project wordt nu geconfigureerd...
+
+
+
+ De installatie van jouw lokale omgeving was een succes!
+ Je kan nu deze setup sluiten.
+ Nadien, kan je de 'configuration' setup openen om jouw omgeving automatisch te configureren...
+
+
+ Bezig met het installeren van bower, brunch, nodemon en sendwithus...
+ Bower packages worden geïnstalleerd...
+ Sass wordt geïnstalleerd...
+ Npm wordt geïnstalleerd...
+ Brunch wordt gestart...
+ De MongoDB database wordt voor je klaargemaakt...
+ De laatste versie van de CodeCombat database wordt gedownload...
+
+ Niet sluiten!
+
+
+ Dat pad bestaat al, ben je zeker dat je het wil overschrijven?
+ Dat pad bestaat niet, probeer alsjeblieft opnieuw...
+
+
+ De installatie van de CodeCombat ontwikkelingsomgeving was succesvol.
+ Alvast bedankt voor al je werk en tot binnenkort.
+ Wil je de LEESMIJ lezen voor meer informatie?
+
+
+ Vanaf nu kan je de ontwikkelingsomgeving opstarten
+ met het gemak van een enkele muisklik.
+ 1) Dubbelklik op
+ en laat de omgeving opstarten.
+ 2) Nu kan je 'localhost:3000' openen in je browser naar voorkeur.
+ Dat is het, je bent nu klaar om te starten met je werk aan CodeCombat.
+
+
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/localization/ru.coco b/scripts/windows/coco-dev-setup/batch/localization/ru.coco
new file mode 100644
index 000000000..150391711
--- /dev/null
+++ b/scripts/windows/coco-dev-setup/batch/localization/ru.coco
@@ -0,0 +1,108 @@
+
+
+
+ ðóññêèé
+ Russian
+ Before we start the installation, here are some tips:
+ Press any key to exit...
+
+
+ You have choosen ðóññêèé as your language.
+ C äàííîãî ìîìåíòà ìû áóäåì îáùàòüñÿ íà ðóññêîì.
+
+
+ In order to continue the installation of the developers environment
+ you will have to read and agree with the following license:
+ Have you read the license and do you agree with it?
+ This setup can't happen without an agreement.
+ Installation and Setup of the CodeCombat environment is cancelled.
+
+
+
+ -áèòíûé êîìïüþòåð îáíàðóæåí.
+ Îáíàðóæåíà îïåðàöèîííàÿ ñèñòåìà
+ .
+ Ìû íå ïîääåðæèâàåì Windows XP, óñòàíîâêà îòìåíåíà.
+
+
+ Âû óæå óñòàíîâèëè âñ¸ ïðîãðàììíîå îáåñïå÷åíèå, íåîáõîäèìîå äëÿ CodeCombat?
+ Ìû ðåêóìåíäóåì îòâåòèòü îòðèöàòåëüíî, åñëè âû íå óâåðåíû.
+ Ïðîïóñê óñòàíîâêè ïðîãðàììíîãî îáåñïå÷åíèÿ...
+ CodeCombat íå ìîã áû áûòü ðàçðàáîòàí áåç ñòîðîííåãî ïðîãðàììíîãî îáåñïå÷åíèÿ.
+ Âîò ïî÷åìó âû äîëæíû áóäåòå óñòàíîâèòü ýòî ïðîãðàììíîå îáåñïå÷åíèå
+ äëÿ òîãî, ÷òîáû íà÷àòü âíîñèòü âêëàä â íàøå ñîîáùåñòâî.
+ Îòìåíèòå óñòàíîâêó, åñëè ó âàñ óæå åñòü ïðèëîæåíèå.
+ Óáåäèòåñü â âûáîðå îïöèè, êîòîðàÿ äîáàâëÿåò ïðèëîæåíèå â Windows PATH, åñëè îïöèÿ äîñòóïíà.
+ Ó âàñ óæå åñòü ïîñëåäíÿÿ âåðñèÿ
+ ?
+ çàãðóæàåòñÿ...
+ óñòàíàâëèâàåòñÿ...
+ ðàñïàêîâûâàåòñÿ...
+ óáèðàåòñÿ...
+ Ïîæàëóéñòà, îïðåäåëèòå ïîëíûé ïóòü, êóäà äîëæåí áûòü óñòàíîâëåí MongoDB
+
+
+
+
+ Èñõîäíûé êîä CodeCombat îòêðûò, êàê âû óæå çíàåòå.
+ Âåñü íàø èñõîäíûé êîä ìîæåò áûòü íàéäåí îíëàéí â Github.
+ Âû ìîæåòå âûáðàòü öåëèêîì ñàìîñòîÿòåëüíóþ óñòàíîâêó Git.
+ Îäíàêî ìû ðåêîìåíäóåì, âìåñòî ýòîãî, ïåðåäàòü óïðàâëåíèå íàì.
+
+
+ Âû õîòèòå ïðîâåñòè óñòàíîâêó Local Git âðó÷íóþ ñàìîñòîÿòåëüíî?
+ Óáåäèòåñü, ÷òî âû ïðàâèëüíî íàñòðîèëè ðåïîçèòîðèé ïåðåä âûïîëíåíèåì.
+ Íå çàêðûâàéòå ýòî îêíî, ïîæàëóéñòà.
+ Êîãäà âû áóäåòå ãîòîâû, íàæìèòå ëþáóþ êëàâèøó äëÿ ïðîäîëæåíèÿ...
+
+
+ Ïîæàëóéñòà, óêàæèòå ïîëíûé ïóòü äî âàøåãî CodeCombat ðåïîçèòîðèÿ git:
+ Ïîæàëóéñòà, ââåäèòå ïîëíûé ïóòü, êóäà âû õîòèòå óñòàíîâèòü ñðåäó CodeCombat
+ Äàííàÿ óñòàíîâêà òðåáóåò Git Bash.
+ Git bash ïî óìîë÷àíèþ óñòàíîâëåí â 'C:\Program Files (x86)\Git'.
+ Git bash ïî óìîë÷àíèþ óñòàíîâëåí â 'C:\Program Files\Git'.
+ Ïîæàëóéñòà, ââåäèòå ïîëíûé ïóòü, êóäà óñòàíîâëåí git bash èëè ïðîñòî íàæìèòå Enter, åñëè îí íàõîäèòñÿ â ïàïêå ïî óìîë÷àíèþ
+ Âû õîòèòå ïðîâåðÿòü ðåïîçèòîðèé ÷åðåç ssh?
+
+
+ Âû äîëæíû áûëè ñäåëàòü ôîðê CodeCombat íà âàøåì àêêàóíòå GitHub...
+ Ïîæàëóéñòà, ââåäèòå âàøè äàííûå github, ÷òîáû íàñòðîèòü ëîêàëüíûé ðåïîçèòîðèé.
+ Èìÿ ïîëüçîâàòåëÿ:
+ Ïàðîëü:
+ Ñïàñèáî... Èä¸ò íàñòðîéêà âàøåãî ëîêàëüíîãî ðåïîçèòîðèÿ...
+
+
+
+ Óñòàíîâêà âàøåé ëîêàëüíîé ñðåäû óñïåøíî çàâåðøåíà!
+ Òåïåðü âû ìîæåòå çàêðûòü äàííûé óñòàíîâùèê.
+ Ïîñëå ýòîãî âû äîëæíû îòêðûòü óñòàíîâùèê íàñòðîåê äëÿ àâòîìàòè÷åñêîé êîíôèãóðàöèè âàøåé ñðåäû...
+
+
+ Óñòàíîâêà bower, brunch, nodemon è sendwithus...
+ Óñòàíîâêà ïàêåòîâ bower...
+ Óñòàíîâêà sass...
+ Óñòàíîâêà npm...
+ Çàïóñê brunch....
+ Óñòàíîâêà áàçû äàííûõ MongoDB...
+ Ñêà÷èâàíèå ïîñëåäíåé âåðñèè áàçû äàííûõ CodeCombat...
+
+ Íå çàêðûâàéòå!
+
+
+ Ýòîò ïóòü óæå ñóùåñòâóåò, âû óâåðåíû, ÷òî õîòèòå ïåðåçàïèñàòü åãî?
+ Ýòîò ïóòü íå ñóùåñòâóåò. Ïîæàëóéñòà, ïîïðîáóéòå åù¸ ðàç...
+
+
+ Óñòàíîâêà ñðåäû ðàçðàáîò÷èêà CodeCombat óñïåøíî çàâåðøåíà.
+ Çàðàíåå ñïàñèáî çà âàø âêëàä è äî ñêîðîé âñòðå÷è.
+ Âû õîòèòå ïðî÷èòàòü README äëÿ ïîëó÷åíèÿ äîïîëíèòåëüíîé èíôîðìàöèè?
+
+
+ Ñ ýòîãî ìîìåíòà âû ìîæåòå çàïóñêàòü ñðåäó ðàçðàáîò÷èêà
+ ñ ïîìîùüþ ùåë÷êà ìûøè.
+ 1) Äâàæäû ù¸ëêíèòå
+ è äàéòå ñðåäå çàïóñòèòüñÿ.
+ 2) Òåïåðü ïðîñòî îòêðîéòå 'localhost:3000' â âàøåì ëþáèìîì áðàóçåðå.
+ Âîò è âñ¸, òåïåðü âû ãîòîâû ïðèñòóïèòü ê ðàáîòå íàä CodeCombat!
+
+
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/localization/zh-HANS.coco b/scripts/windows/coco-dev-setup/batch/localization/zh-HANS.coco
new file mode 100644
index 000000000..febc98c56
--- /dev/null
+++ b/scripts/windows/coco-dev-setup/batch/localization/zh-HANS.coco
@@ -0,0 +1,193 @@
+<<<<<<< HEAD:scripts/windows/coco-dev-setup/batch/localisation/zh-HANS.coco
+
+
+
+ 简体ä¸æ–‡
+ ç›®å‰æˆ‘们åªèƒ½ç”¨è‹±æ–‡ç»™ä½ å馈ï¼
+
+
+
+ -ä½ç³»ç»Ÿ.
+ æ“作系统
+ 被侦测到.
+ 我们ä¸æ”¯æŒ Windows XP, 安装å–消.
+
+
+ ä½ æ˜¯å¦å·²ç»å®‰è£…好è¿è¡Œ CodeCombat 所需的所有软件?
+ å¦‚æžœä½ ä¸ç¡®å®šçš„è¯è¯·å›žç” No.
+ æ£åœ¨è·³è¿‡æ¤è½¯ä»¶çš„安装...
+ CodeCombat æ— æ³•åœ¨ä¸ä½¿ç”¨ç¬¬ä¸‰æ–¹æœåŠ¡çš„情况下开å‘.
+ è¿™å°±æ˜¯ä¸ºä»€ä¹ˆä½ éœ€è¦å®‰è£…这些软件,
+ 为了开始给我们的开æºç¤¾åŒºåšè´¡çŒ®.
+ å¦‚æžœä½ å·²ç»æœ‰äº†è¿™äº›è½¯ä»¶ 请å–消安装.
+ Make sure to select the option that adds the application to your Windows Path, if the option is available.
+ ä½ æ˜¯å¦å·²ç»å®‰è£…了最新版本的
+ ?
+ æ£åœ¨ä¸‹è½½...
+ æ£åœ¨å®‰è£…...
+ æ£åœ¨è§£åŽ‹...
+ æ£åœ¨æ¸…ç†...
+ è¯·è¾“å…¥ä½ å¸Œæœ›å®‰è£… mongodb 的文件夹的全路径
+
+
+
+
+ CodeCombat 是开æºçš„.
+ 我们的所有æºä»£ç 都放在了 Github.
+ ä½ å¯ä»¥é€‰æ‹©è‡ªå·±æ‰‹å·¥å®‰è£… Git.
+ 但我们ä»ç„¶å»ºè®®è®©ç¨‹åºè‡ªåŠ¨æ›¿ä½ 完æˆ.
+
+
+ ä½ æ˜¯å¦æƒ³è‡ªå·±æ‰‹å·¥å®‰è£…本地 Git 安装?
+ 请确ä¿åœ¨å¼€å§‹å¤„ç†å‰, ä½ æœ‰æ£ç¡®è®¾ç½®å¥½ä½ 的库.
+ 请ä¸è¦å…³é—æ¤çª—å£.
+ å¦‚æžœä½ å‡†å¤‡å¥½äº†, 请按任æ„键继ç»...
+
+
+ è¯·è¾“å…¥ä½ CodeCombat git库的全路径:
+ è¯·è¾“å…¥ä½ æƒ³å®‰è£… CodeCombat 环境的全路径
+ è¿™é¡¹å®‰è£…éœ€è¦ Git Bash.
+ Git bash 默认安装在 'C:\Program Files (x86)\Git'.
+ Git bash 默认安装在 'C:\Program Files\Git'.
+ 请输入 git bash 的安装全路径, å¦‚æžœä½ å®‰è£…çš„æ˜¯é»˜è®¤è·¯å¾„, 那么直接输入回车å³å¯
+ ä½ æ˜¯å¦æƒ³ä½¿ç”¨ ssh æ¥æ£€å‡º(checkout)库(repository)?
+
+
+
+ æ£åœ¨å®‰è£… bower, brunch, nodemon å’Œ sendwithus...
+ æ£åœ¨ç”¨ bower 安装ä¾èµ–包...
+ æ£åœ¨å®‰è£… sass...
+ æ£åœ¨å®‰è£… npm...
+ æ£åœ¨å¼€å¯ brunch....
+ æ£åœ¨ä¸ºä½ 设置 MongoDB æ•°æ®åº“...
+ æ£åœ¨ä¸‹è½½ CodeCombat æ•°æ®åº“的最新版本...
+
+
+
+ 这个路径已ç»å˜åœ¨, ä½ æƒ³è¦è¦†ç›–它å—?
+ 这个路径ä¸å˜åœ¨, 请å†æ¬¡å°è¯•...
+
+
+ CodeCombat å¼€å‘环境的æ建已æˆåŠŸ.
+ æ„Ÿè°¢~ 我们会很快å†æ¬¡è§é¢çš„ :)
+ ä½ æ˜¯å¦æƒ³é˜…读 README 文件以了解更多信æ¯?
+
+
+ From now on you can start the dev. environment at
+ the touch of a single mouse click.
+ 1) åŒå‡»æ–‡ä»¶
+ å¯åŠ¨å¼€å‘环境.
+ 2) 在æµè§ˆå™¨é‡Œè®¿é—® 'localhost:3000'
+ å¥½äº†ï¼Œä½ çŽ°åœ¨å¯ä»¥å¼€å§‹å¼€å‘ CodeCombat 了!
+
+
+=======
+
+
+
+ 简体ä¸æ–‡
+ Traditional Chinese
+ Before we start the installation, here are some tips:
+ Press any key to exit...
+
+
+ You have choosen 简体ä¸æ–‡ as your language.
+ ç›®å‰æˆ‘们åªèƒ½ç”¨è‹±æ–‡ç»™ä½ å馈
+
+
+ In order to continue the installation of the developers environment
+ you will have to read and agree with the following license:
+ Have you read the license and do you agree with it?
+ This setup can't happen without an agreement.
+ Installation and Setup of the CodeCombat environment is cancelled.
+
+
+
+ -ä½ç³»ç»Ÿ.
+ æ“作系统
+ 被侦测到.
+ 我们ä¸æ”¯æŒ Windows XP, 安装å–消.
+
+
+ ä½ æ˜¯å¦å·²ç»å®‰è£…好è¿è¡Œ CodeCombat 所需的所有软件?
+ å¦‚æžœä½ ä¸ç¡®å®šçš„è¯è¯·å›žç” No.
+ æ£åœ¨è·³è¿‡æ¤è½¯ä»¶çš„安装...
+ CodeCombat æ— æ³•åœ¨ä¸ä½¿ç”¨ç¬¬ä¸‰æ–¹æœåŠ¡çš„情况下开å‘.
+ è¿™å°±æ˜¯ä¸ºä»€ä¹ˆä½ éœ€è¦å®‰è£…这些软件,
+ 为了开始给我们的开æºç¤¾åŒºåšè´¡çŒ®.
+ å¦‚æžœä½ å·²ç»æœ‰äº†è¿™äº›è½¯ä»¶ 请å–消安装.
+ Make sure to select the option that adds the application to your Windows Path, if the option is available.
+ ä½ æ˜¯å¦å·²ç»å®‰è£…了最新版本的
+ ?
+ æ£åœ¨ä¸‹è½½...
+ æ£åœ¨å®‰è£…...
+ æ£åœ¨è§£åŽ‹...
+ æ£åœ¨æ¸…ç†...
+ è¯·è¾“å…¥ä½ å¸Œæœ›å®‰è£… mongodb 的文件夹的全路径
+
+
+
+
+ CodeCombat 是开æºçš„.
+ 我们的所有æºä»£ç 都放在了 Github.
+ ä½ å¯ä»¥é€‰æ‹©è‡ªå·±æ‰‹å·¥å®‰è£… Git.
+ 但我们ä»ç„¶å»ºè®®è®©ç¨‹åºè‡ªåŠ¨æ›¿ä½ 完æˆ.
+
+
+ ä½ æ˜¯å¦æƒ³è‡ªå·±æ‰‹å·¥å®‰è£…本地 Git 安装?
+ 请确ä¿åœ¨å¼€å§‹å¤„ç†å‰, ä½ æœ‰æ£ç¡®è®¾ç½®å¥½ä½ 的库.
+ 请ä¸è¦å…³é—æ¤çª—å£.
+ å¦‚æžœä½ å‡†å¤‡å¥½äº†, 请按任æ„键继ç»...
+
+
+ è¯·è¾“å…¥ä½ CodeCombat git库的全路径:
+ è¯·è¾“å…¥ä½ æƒ³å®‰è£… CodeCombat 环境的全路径
+ è¿™é¡¹å®‰è£…éœ€è¦ Git Bash.
+ Git bash 默认安装在 'C:\Program Files (x86)\Git'.
+ Git bash 默认安装在 'C:\Program Files\Git'.
+ 请输入 git bash 的安装全路径, å¦‚æžœä½ å®‰è£…çš„æ˜¯é»˜è®¤è·¯å¾„, 那么直接输入回车å³å¯
+ ä½ æ˜¯å¦æƒ³ä½¿ç”¨ ssh æ¥æ£€å‡º(checkout)库(repository)?
+
+
+ You should have forked CodeCombat to your own GitHub Account by now...
+ Please enter your github information, to configure your local repository.
+ Username:
+ Password:
+ Thank you... Configuring your local repistory right now...
+
+
+
+ The installation of your local environment was succesfull!
+ You can now close this setup.
+ After that, you should open the configuration setup to automaticly configure your environment...
+
+
+ æ£åœ¨å®‰è£… bower, brunch, nodemon å’Œ sendwithus...
+ æ£åœ¨ç”¨ bower 安装ä¾èµ–包...
+ æ£åœ¨å®‰è£… sass...
+ æ£åœ¨å®‰è£… npm...
+ æ£åœ¨å¼€å¯ brunch....
+ æ£åœ¨ä¸ºä½ 设置 MongoDB æ•°æ®åº“...
+ æ£åœ¨ä¸‹è½½ CodeCombat æ•°æ®åº“的最新版本...
+
+ Don't close!
+
+
+ 这个路径已ç»å˜åœ¨, ä½ æƒ³è¦è¦†ç›–它å—?
+ 这个路径ä¸å˜åœ¨, 请å†æ¬¡å°è¯•...
+
+
+ CodeCombat å¼€å‘环境的æ建已æˆåŠŸ.
+ æ„Ÿè°¢~ 我们会很快å†æ¬¡è§é¢çš„ :)
+ ä½ æ˜¯å¦æƒ³é˜…读 README 文件以了解更多信æ¯?
+
+
+ From now on you can start the dev. environment at
+ the touch of a single mouse click.
+ 1) åŒå‡»æ–‡ä»¶
+ å¯åŠ¨å¼€å‘环境.
+ 2) 在æµè§ˆå™¨é‡Œè®¿é—® 'localhost:3000'
+ å¥½äº†ï¼Œä½ çŽ°åœ¨å¯ä»¥å¼€å§‹å¼€å‘ CodeCombat 了!
+
+
+>>>>>>> 072729acc34123c42250d361955438cfd8c210d7:scripts/windows/coco-dev-setup/batch/localization/zh-HANS.coco
diff --git a/scripts/windows/coco-dev-setup/batch/localization/zh-HANT.coco b/scripts/windows/coco-dev-setup/batch/localization/zh-HANT.coco
new file mode 100644
index 000000000..b54a269e5
--- /dev/null
+++ b/scripts/windows/coco-dev-setup/batch/localization/zh-HANT.coco
@@ -0,0 +1,192 @@
+<<<<<<< HEAD:scripts/windows/coco-dev-setup/batch/localisation/zh-HANT.coco
+
+
+
+ ç¹ä½“ä¸æ–‡
+ From now on we'll send our feedback in English!
+
+
+
+ -bit computer detected.
+ The operating system
+ was detected.
+ We don't support Windows XP, installation cancelled.
+
+
+ Have you already installed all the software needed for CodeCombat?
+ We recommand that you reply negative in case you're not sure.
+ Skipping the installation of the software...
+ CodeCombat couldn't be developed without third-party software.
+ That's why you'll need to install this software,
+ in order to start contributing to our community.
+ Cancel the installation if you already have the application.
+ Make sure to select the option that adds the application to your Windows Path, if the option is available.
+ Do you already have the latest version of
+ installed?
+ is downloading...
+ is installing...
+ is unzipping...
+ is cleaning...
+ Please define the full path where mongodb should be installed
+
+
+
+
+ CodeCombat is opensource, like you already know.
+ All our sourcecode can be found online at Github.
+ You can choose to do the entire Git setup yourself.
+ However we recommend that you instead let us handle it instead.
+
+
+ Do you want to do the Local Git setup manually yourself?
+ Make sure you have correctly setup your repository before processing.
+ Do not close this window please.
+ When you're ready, press any key to continue...
+
+
+ Please give the full path of your CodeCombat git repository:
+ Please enter the full path where you want to install your CodeCombat environment
+ This installation requires Git Bash.
+ Git bash is by default installed at 'C:\Program Files (x86)\Git'.
+ Git bash is by default installed at 'C:\Program Files\Git'.
+ Please enter the full path where git bash is installed or just press enter if it's in the default location
+ Do you want to checkout the repository via ssh?
+
+
+
+ Installing bower, brunch, nodemon and sendwithus...
+ Installing bower packages...
+ Installing sass...
+ Installing npm...
+ Starting brunch....
+ Setting up a MongoDB database for you...
+ Downloading the last version of the CodeCombat database...
+
+
+
+ That path already exists, are you sure you want to overwrite it?
+ That path doesn't exist. Please try again...
+
+
+ The setup of the CodeCombat Dev. Environment was succesfull.
+ Thank you already for your contribution and see you soon.
+ Do you want to read the README for more information?
+
+
+ From now on you can start the dev. environment at
+ the touch of a single mouse click.
+ 1) Just double click
+ and let the environment start up.
+ 2) Now just open 'localhost:3000' in your prefered browser.
+ That's it, you're now ready to start working on CodeCombat!
+
+=======
+
+
+
+ ç¹ä½“ä¸æ–‡
+ Simplified Chinese
+ Before we start the installation, here are some tips:
+ Press any key to exit...
+
+
+ You have choosen ç¹ä½“ä¸æ–‡ as your language.
+ From now on we'll send our feedback in ç¹ä½“ä¸æ–‡.
+
+
+ In order to continue the installation of the developers environment
+ you will have to read and agree with the following license:
+ Have you read the license and do you agree with it?
+ This setup can't happen without an agreement.
+ Installation and Setup of the CodeCombat environment is cancelled.
+
+
+
+ -bit computer detected.
+ The operating system
+ was detected.
+ We don't support Windows XP, installation cancelled.
+
+
+ Have you already installed all the software needed for CodeCombat?
+ We recommand that you reply negative in case you're not sure.
+ Skipping the installation of the software...
+ CodeCombat couldn't be developed without third-party software.
+ That's why you'll need to install this software,
+ in order to start contributing to our community.
+ Cancel the installation if you already have the application.
+ Make sure to select the option that adds the application to your Windows Path, if the option is available.
+ Do you already have the latest version of
+ installed?
+ is downloading...
+ is installing...
+ is unzipping...
+ is cleaning...
+ Please define the full path where mongodb should be installed
+
+
+
+
+ CodeCombat is opensource, like you already know.
+ All our sourcecode can be found online at Github.
+ You can choose to do the entire Git setup yourself.
+ However we recommend that you instead let us handle it instead.
+
+
+ Do you want to do the Local Git setup manually yourself?
+ Make sure you have correctly setup your repository before processing.
+ Do not close this window please.
+ When you're ready, press any key to continue...
+
+
+ Please give the full path of your CodeCombat git repository:
+ Please enter the full path where you want to install your CodeCombat environment
+ This installation requires Git Bash.
+ Git bash is by default installed at 'C:\Program Files (x86)\Git'.
+ Git bash is by default installed at 'C:\Program Files\Git'.
+ Please enter the full path where git bash is installed or just press enter if it's in the default location
+ Do you want to checkout the repository via ssh?
+
+
+ You should have forked CodeCombat to your own GitHub Account by now...
+ Please enter your github information, to configure your local repository.
+ Username:
+ Password:
+ Thank you... Configuring your local repistory right now...
+
+
+
+ The installation of your local environment was succesfull!
+ You can now close this setup.
+ After that, you should open the configuration setup to automaticly configure your environment...
+
+
+ Installing bower, brunch, nodemon and sendwithus...
+ Installing bower packages...
+ Installing sass...
+ Installing npm...
+ Starting brunch....
+ Setting up a MongoDB database for you...
+ Downloading the last version of the CodeCombat database...
+
+ Don't close!
+
+
+ That path already exists, are you sure you want to overwrite it?
+ That path doesn't exist. Please try again...
+
+
+ The setup of the CodeCombat Dev. Environment was succesfull.
+ Thank you already for your contribution and see you soon.
+ Do you want to read the README for more information?
+
+
+ From now on you can start the dev. environment at
+ the touch of a single mouse click.
+ 1) Just double click
+ and let the environment start up.
+ 2) Now just open 'localhost:3000' in your prefered browser.
+ That's it, you're now ready to start working on CodeCombat!
+
+>>>>>>> 072729acc34123c42250d361955438cfd8c210d7:scripts/windows/coco-dev-setup/batch/localization/zh-HANT.coco
+
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/scripts/configuration.bat b/scripts/windows/coco-dev-setup/batch/scripts/configuration.bat
new file mode 100755
index 000000000..f70748a7a
--- /dev/null
+++ b/scripts/windows/coco-dev-setup/batch/scripts/configuration.bat
@@ -0,0 +1,47 @@
+@echo off
+setlocal EnableDelayedExpansion
+
+call read_cache
+
+call configuration_cmd
+
+call npm_and_brunch_setup
+
+call print_finished_header
+call print_dashed_seperator
+
+call get_local_text end_succesfull end succesfull
+call get_local_text end_thankyou end thankyou
+echo %end_succesfull%
+echo %end_thankyou%
+
+call print_dashed_seperator
+
+call get_local_text start_s1 start s1
+call get_local_text start_s2 start s2
+call get_local_text start_s3 start s3
+call get_local_text start_s4 start s4
+call get_local_text start_s5 start s5
+call get_local_text start_s6 start s6
+
+echo !start_s1!
+echo !start_s2!
+echo.
+echo !start_s3! '!repository_path!\coco\SCOCODE.bat'
+echo !start_s4!
+echo !start_s5!
+echo.
+echo !start_s6!
+
+call print_dashed_seperator
+
+call get_local_text end_readme end readme
+call ask_question "!end_readme!"
+
+if "%result%"=="true" (
+ call open_readme
+)
+
+exit
+
+endlocal
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/scripts/configuration_cmd.bat b/scripts/windows/coco-dev-setup/batch/scripts/configuration_cmd.bat
new file mode 100755
index 000000000..3c614e671
--- /dev/null
+++ b/scripts/windows/coco-dev-setup/batch/scripts/configuration_cmd.bat
@@ -0,0 +1,4 @@
+Color 0A
+mode con: cols=79 lines=55
+
+TITLE CodeCombat.com - Development Environment Installation
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/scripts/download_and_install_app.bat b/scripts/windows/coco-dev-setup/batch/scripts/download_and_install_app.bat
index a81c90363..760b40889 100644
--- a/scripts/windows/coco-dev-setup/batch/scripts/download_and_install_app.bat
+++ b/scripts/windows/coco-dev-setup/batch/scripts/download_and_install_app.bat
@@ -1,3 +1,4 @@
+<<<<<<< HEAD
set "temp_directory=c:\.coco\"
set "curl_app=..\utilities\curl.exe"
set "zu_app=..\utilities\7za.exe"
@@ -65,4 +66,76 @@ goto:clean_up
goto:exit_installation
:exit_installation
+=======
+set "temp_directory=c:\.coco\"
+set "curl_app=..\utilities\curl.exe"
+set "zu_app=..\utilities\7za.exe"
+
+if NOT exist "%temp_directory%" (
+ md %temp_directory%
+)
+
+call get_local_text install_process_prefix install process prefix
+call get_local_text install_process_sufix install process sufix
+
+call ask_question "!install_process_prefix! %1 !install_process_sufix!"
+
+if "%result%"=="true" (
+ goto:exit_installation
+)
+
+call print_dashed_seperator
+
+call get_extension %2 download_extension
+call get_local_text install_process_downloading install process downloading
+echo %1 !install_process_downloading!
+set "install_file=!temp_directory!%1.!download_extension!"
+start /wait cmd.exe /c "TITLE %1 !install_process_downloading! && %curl_app% -k -m 10800 --retry 100 -o !install_file! %2"
+
+if "%download_extension%"=="zip" (
+ set "package_path=!temp_directory!%1\"
+
+ %zu_app% x !install_file! -o!package_path! -y
+
+ for /f "delims=" %%a in ('dir !package_path! /on /ad /b') do @set mongodb_original_directory=%%a
+
+ call print_dashed_seperator
+ goto:get_mongodb_path
+
+ :get_mongodb_path
+ call get_local_text install_process_mongodbpath install process mongodbpath
+ set /p "mongodb_path=!install_process_mongodbpath!: "
+ if exist "%mongodb_path%" (
+ call get_local_text error_path error path
+ call ask_question "!error_path!"
+ if "!result!"=="false" (
+ call print_dashed_seperator
+ goto:get_mongodb_path
+ ) else (
+ rmdir /s /q %mongodb_path%
+ )
+ )
+ md %mongodb_path%
+
+ %systemroot%\System32\xcopy !package_path!!mongodb_original_directory! !mongodb_path! /r /h /s /e /y
+
+ call set_environment_var "!mongodb_path!\bin"
+
+ goto:clean_up
+)
+
+call get_local_text install_process_installing install process installing
+echo %1 !install_process_installing!
+echo.
+start /WAIT !install_file!
+goto:clean_up
+
+:clean_up
+ call get_local_text install_process_cleaning install process cleaning
+ echo %1 !install_process_cleaning!
+ rmdir /s /q "!temp_directory!"
+ goto:exit_installation
+
+:exit_installation
+>>>>>>> 072729acc34123c42250d361955438cfd8c210d7
call print_dashed_seperator
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/scripts/download_and_install_applications.bat b/scripts/windows/coco-dev-setup/batch/scripts/download_and_install_applications.bat
index defdc10f8..78974d4b2 100644
--- a/scripts/windows/coco-dev-setup/batch/scripts/download_and_install_applications.bat
+++ b/scripts/windows/coco-dev-setup/batch/scripts/download_and_install_applications.bat
@@ -1,3 +1,4 @@
+<<<<<<< HEAD
call print_install_header
call print_dashed_seperator
@@ -50,4 +51,59 @@ for /l %%i in (1, 1, !downloads_count!) do (
goto:exit_setup
+=======
+call print_install_header
+call print_dashed_seperator
+
+call get_local_text install_process_sks install process sks
+echo !install_process_sks!
+
+call get_local_text install_process_skq install process skq
+call ask_question "!install_process_skq!"
+
+call print_dashed_seperator
+
+if "%result%"=="true" (
+ call get_local_text install_process_skc install process skc
+ echo !install_process_skc!
+ call print_dashed_seperator
+ goto:exit_setup
+)
+
+call get_system_information
+call print_dashed_seperator
+
+if %system_info_os% == XP (
+ call get_local_text install_system_xp install system xp
+ echo !install_system_xp!
+ call print_exit
+)
+
+call get_variables ..\\config\\downloads.coco downloads download_names downloads_count 0 general general
+call get_variables ..\\config\\downloads.coco downloads download_names downloads_count 2 %system_info_os% b%system_info_bit%
+call get_variables ..\\config\\downloads.coco downloads download_names downloads_count 3 general b%system_info_bit%
+
+call get_local_text install_process_s1 install process s1
+call get_local_text install_process_s2 install process s2
+call get_local_text install_process_s3 install process s3
+call get_local_text install_process_s4 install process s4
+call get_local_text install_process_winpath install process winpath
+
+echo !install_process_s1!
+echo !install_process_s2!
+echo !install_process_s3!
+echo.
+echo !install_process_s4!
+echo.
+echo !install_process_winpath!
+
+call print_dashed_seperator
+
+for /l %%i in (1, 1, !downloads_count!) do (
+ call download_and_install_app !download_names[%%i]! !downloads[%%i]!
+)
+
+goto:exit_setup
+
+>>>>>>> 072729acc34123c42250d361955438cfd8c210d7
:exit_setup
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/scripts/get_cache_var.bat b/scripts/windows/coco-dev-setup/batch/scripts/get_cache_var.bat
new file mode 100755
index 000000000..d7eebbcfe
--- /dev/null
+++ b/scripts/windows/coco-dev-setup/batch/scripts/get_cache_var.bat
@@ -0,0 +1,3 @@
+for /F "delims=" %%F in ('call run_script .\\get_var.ps1 ..\\config\\cache.coco %1') do (
+ set "%1=%%F"
+)
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/scripts/get_language.bat b/scripts/windows/coco-dev-setup/batch/scripts/get_language.bat
index ce3446b5e..99e4851af 100644
--- a/scripts/windows/coco-dev-setup/batch/scripts/get_language.bat
+++ b/scripts/windows/coco-dev-setup/batch/scripts/get_language.bat
@@ -1,3 +1,4 @@
+<<<<<<< HEAD
echo Some feedback is sent in your system's language
echo but most feedback is sent and localised by us.
echo Here is a list of languages:
@@ -33,4 +34,44 @@ goto:get_localisation_id
call get_text !language_id! global_intro global intro
echo !global_intro!
call print_seperator
+=======
+echo Some feedback is sent in your system's language
+echo but most feedback is sent and localised by us.
+echo Here is a list of languages:
+call print_dashed_seperator
+
+call get_array ..\\localization\\languages.coco languages language_count
+for /l %%i in (1,1,%language_count%) do (
+ call get_text !languages[%%i]! global_description global description
+ echo [%%i] !global_description!
+)
+
+goto:get_localization_id
+
+:get_localization_id
+ call print_dashed_seperator
+ set /p "localization_id=Enter the language ID of your preference and press : "
+ goto:validation_check
+
+:validation_check
+ set "localization_is_false="
+ set /a local_id = %localization_id%
+ if !local_id! EQU 0 set localization_is_false=1
+ if !local_id! LSS 1 set localization_is_false=1
+ if !local_id! GTR !language_count! set localization_is_false=1
+ if defined localization_is_false (
+ echo The id you entered is invalid, please try again...
+ goto:get_localization_id
+ ) else (
+ set language_id=!languages[%local_id%]!
+ call print_dashed_seperator
+
+ call get_local_text language_choosen language choosen
+ echo !language_choosen!
+
+ call get_local_text language_feedback language feedback
+ echo !language_feedback!
+
+ call print_seperator
+>>>>>>> 072729acc34123c42250d361955438cfd8c210d7
)
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/scripts/get_local_text.bat b/scripts/windows/coco-dev-setup/batch/scripts/get_local_text.bat
index 9a54a78c5..aae9bf110 100644
--- a/scripts/windows/coco-dev-setup/batch/scripts/get_local_text.bat
+++ b/scripts/windows/coco-dev-setup/batch/scripts/get_local_text.bat
@@ -1 +1 @@
-call get_text !language_id! %1 %2 %3 %4 %5
\ No newline at end of file
+call get_text %language_id% %1 %2 %3 %4 %5
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/scripts/get_system_information.bat b/scripts/windows/coco-dev-setup/batch/scripts/get_system_information.bat
index 6921cb56a..ee68e3274 100644
--- a/scripts/windows/coco-dev-setup/batch/scripts/get_system_information.bat
+++ b/scripts/windows/coco-dev-setup/batch/scripts/get_system_information.bat
@@ -1,3 +1,4 @@
+<<<<<<< HEAD
if exist "%PROGRAMFILES(X86)%" (
call:set_bit 64
) else (
@@ -29,3 +30,34 @@ goto:eof
goto:eof
:end
+=======
+if exist "%PROGRAMFILES(X86)%" (
+ call:set_bit 64
+) else (
+ call:set_bit 32
+)
+
+for /f "tokens=4-5 delims=. " %%i in ('ver') do set VERSION=%%i.%%j
+if "%version%" == "5.2" ( call:set_os XP )
+if "%version%" == "6.0" ( call:set_os Vista )
+if "%version%" == "6.1" ( call:set_os Win7 )
+if "%version%" == "6.2" ( call:set_os Win8 )
+if "%version%" == "6.3" ( call:set_os Win8 )
+
+goto:end
+
+:set_bit
+ call get_local_text install_system_bit install system bit
+ set system_info_bit=%~1
+ echo %system_info_bit%%install_system_bit%
+goto:eof
+
+:set_os
+ set system_info_os=%~1
+ call get_local_text install_system_prefix install system prefix
+ call get_local_text install_system_sufix install system sufix
+ echo %install_system_prefix% %system_info_os% %install_system_sufix%
+goto:eof
+
+:end
+>>>>>>> 072729acc34123c42250d361955438cfd8c210d7
diff --git a/scripts/windows/coco-dev-setup/batch/scripts/get_text.bat b/scripts/windows/coco-dev-setup/batch/scripts/get_text.bat
index 9bd888e0e..c36bd06fb 100644
--- a/scripts/windows/coco-dev-setup/batch/scripts/get_text.bat
+++ b/scripts/windows/coco-dev-setup/batch/scripts/get_text.bat
@@ -1,3 +1,8 @@
+<<<<<<< HEAD
for /F "delims=" %%F in ('call run_script .\\get_var.ps1 ..\\localisation\\%1.coco %3 %4 %5 %6') do (
set "%2=%%F"
+=======
+for /F "delims=" %%F in ('call run_script .\\get_var.ps1 ..\\localization\\%1.coco %3 %4 %5 %6') do (
+ set "%2=%%F"
+>>>>>>> 072729acc34123c42250d361955438cfd8c210d7
)
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/scripts/github_setup.bat b/scripts/windows/coco-dev-setup/batch/scripts/github_setup.bat
index deb1334cc..2bdf32529 100644
--- a/scripts/windows/coco-dev-setup/batch/scripts/github_setup.bat
+++ b/scripts/windows/coco-dev-setup/batch/scripts/github_setup.bat
@@ -1,3 +1,4 @@
+<<<<<<< HEAD
call print_github_header
call print_dashed_seperator
@@ -112,4 +113,154 @@ goto:eof
:exit_git_setup
call print_dashed_seperator
+=======
+call print_github_header
+call print_dashed_seperator
+
+call get_local_text github_intro_opensource github intro opensource
+call get_local_text github_intro_online github intro online
+call get_local_text github_intro_manual github intro manual
+call get_local_text github_intro_norec github intro norec
+
+echo !github_intro_opensource!
+echo !github_intro_online!
+echo !github_intro_manual!
+echo !github_intro_norec!
+
+call print_dashed_seperator
+
+call get_local_text github_skip_question github skip question
+call ask_question "!github_skip_question!"
+call print_dashed_seperator
+
+if "%result%"=="true" (
+ call get_local_text github_skip_consequence github skip consequence
+ echo !github_skip_consequence!
+
+ call get_local_text github_skip_donotclose github skip donotclose
+ echo !github_skip_donotclose!
+
+ call get_local_text github_skip_wait github skip wait
+ set /p "github_skip_wait=!github_skip_wait!"
+
+ call print_dashed_seperator
+
+ call get_local_text github_process_path github process path
+ call get_path_safe "!github_process_path!"
+ set "repository_path=!tmp_safe_path!"
+
+ goto:exit_git_setup
+)
+
+goto:get_bash_path
+
+:get_bash_path
+ call get_local_text github_process_bashi github process bashi
+ echo !github_process_bashi!
+
+ if not defined install_system_bit (
+ call print_dashed_seperator
+ call get_system_information
+ call print_dashed_seperator
+ )
+
+ if "%system_info_bit%"=="64" (
+ call get_local_text github_process_bashp64 github process bashp64
+ echo !github_process_bashp64!
+ ) else (
+ call get_local_text github_process_bashp32 github process bashp32
+ echo !github_process_bashp32!
+ )
+
+ call get_local_text github_process_bashq github process bashq
+ set /p "git_bash_path=!github_process_bashq!: "
+
+ if not defined git_bash_path (
+ if "%system_info_bit%"=="64" (
+ set "git_bash_path=C:\Program Files (x86)\Git"
+ ) else (
+ set "git_bash_path=C:\Program Files\Git"
+ )
+ goto:get_git_path
+ )
+
+ if not exist "%git_bash_path%" (
+ call get_local_text error_exist error exist
+ echo !error_exist!
+ call print_dashed_seperator
+ goto:get_bash_path
+ ) else (
+ goto:get_git_path
+ )
+goto:eof
+
+:get_git_path
+ call print_dashed_seperator
+ call get_local_text github_process_checkout github process checkout
+ set /p "repository_path=!github_process_checkout!: "
+ if exist !repository_path! (
+ call get_local_text error_path error path
+ call ask_question "!error_path!"
+ if "!result!"=="false" (
+ call print_dashed_seperator
+ goto:get_git_path
+ ) else (
+ rmdir /s /q %repository_path%
+ goto:git_checkout
+ )
+ ) else (
+ goto:git_checkout
+ )
+goto:eof
+
+:git_checkout
+ md "%repository_path%"
+ set "repository_path=%repository_path%"
+
+ call print_dashed_seperator
+ set "git_app_path=%git_bash_path%\bin\git.exe"
+
+ call get_config github_url
+ "%git_app_path%" clone "!github_url!" "%repository_path%\coco"
+
+ goto:git_configuration
+goto:eof
+
+:git_configuration
+ call print_dashed_seperator
+
+ call get_local_text github_config_intro github config intro
+ echo !github_config_intro!
+
+ call get_local_text github_config_info github config info
+ echo !github_config_info!
+
+ call print_dashed_seperator
+
+ call get_local_text github_config_username github config username
+ set /p "git_username=!github_config_username!"
+
+ call get_local_text github_config_password github config password
+
+ set /p "git_password=!github_config_password!"
+
+ call print_dashed_seperator
+
+ call get_local_text github_config_process github config process
+ echo !github_config_process!
+
+ set cur_dir=%CD%
+ cd !repository_path!\coco
+
+ "%git_app_path%" remote rm origin
+ "%git_app_path%" remote add origin https://!git_username!:!git_password!@github.com/!git_username!/codecombat.git
+
+ cd !cur_dir!
+
+ goto:exit_git_setup
+goto:eof
+
+:exit_git_setup
+ call print_dashed_seperator
+>>>>>>> 072729acc34123c42250d361955438cfd8c210d7
goto:eof
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/scripts/nab_automatic_script.bat b/scripts/windows/coco-dev-setup/batch/scripts/nab_automatic_script.bat
new file mode 100755
index 000000000..7048f0180
--- /dev/null
+++ b/scripts/windows/coco-dev-setup/batch/scripts/nab_automatic_script.bat
@@ -0,0 +1,7 @@
+call print_dashed_seperator
+call get_local_text npm_script npm script
+echo %npm_script%
+
+echo start cmd.exe cmd /c "TITLE CodeCombat.com - mongodb database & mongod --setParameter textSearchEnabled=true --dbpath %~2">%~1\SCOCODE.bat
+echo start cmd.exe cmd /c "TITLE CodeCombat.com - nodemon server & nodemon index.js">>%~1\SCOCODE.bat
+echo start cmd.exe cmd /c "TITLE CodeCombat.com - brunch - live compiler & brunch w">>%~1\SCOCODE.bat
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/scripts/nab_install_bower.bat b/scripts/windows/coco-dev-setup/batch/scripts/nab_install_bower.bat
new file mode 100755
index 000000000..283d5e522
--- /dev/null
+++ b/scripts/windows/coco-dev-setup/batch/scripts/nab_install_bower.bat
@@ -0,0 +1,7 @@
+call print_dashed_seperator
+call get_local_text npm_binstall npm binstall
+echo %npm_binstall%
+
+cd /D %~1
+start /wait cmd /c "echo %npm_binstall% & bower cache clean & bower install"
+cd /D %work_directory%
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/scripts/nab_install_mongodb.bat b/scripts/windows/coco-dev-setup/batch/scripts/nab_install_mongodb.bat
new file mode 100755
index 000000000..84d9a3691
--- /dev/null
+++ b/scripts/windows/coco-dev-setup/batch/scripts/nab_install_mongodb.bat
@@ -0,0 +1,37 @@
+call print_dashed_seperator
+call get_local_text npm_mongodb npm mongodb
+echo %npm_mongodb%
+
+if exist %~1 (
+ rmdir /s /q %~1
+)
+
+md %~1
+
+call print_dashed_seperator
+call get_local_text npm_db npm db
+echo %npm_db%
+
+call get_config database_backup
+
+call get_local_text npm_close npm close
+
+cd /D %~1
+
+start cmd /c "TITLE MongoDB - %npm_close% & mongod --setParameter textSearchEnabled=true --dbpath %~1"
+
+start /wait cmd.exe /c "TITLE downloading database backup... && %work_directory%\%curl_app% -k -m 10800 --retry 100 -o dump.tar.gz %database_backup%"
+
+start /wait cmd /c "%work_directory%\%zu_app% e dump.tar.gz && del dump.tar.gz && %work_directory%\%zu_app% x dump.tar && del dump.tar"
+
+start /wait cmd /c "mongorestore dump"
+
+rmdir /s /q dump
+
+call %work_directory%\print_dashed_seperator
+
+taskkill /F /fi "IMAGENAME eq mongod.exe"
+
+del /F %~1\mongod.lock
+
+cd /D %work_directory%
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/scripts/nab_install_npm.bat b/scripts/windows/coco-dev-setup/batch/scripts/nab_install_npm.bat
new file mode 100755
index 000000000..6c6e5d3c2
--- /dev/null
+++ b/scripts/windows/coco-dev-setup/batch/scripts/nab_install_npm.bat
@@ -0,0 +1,6 @@
+call get_local_text npm_install npm install
+echo %npm_install%
+
+cd /D %~1
+start /wait cmd /c "echo %npm_install% & npm install -g bower brunch nodemon sendwithus"
+cd /D %work_directory%
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/scripts/nab_install_npm_all.bat b/scripts/windows/coco-dev-setup/batch/scripts/nab_install_npm_all.bat
new file mode 100755
index 000000000..cb858c029
--- /dev/null
+++ b/scripts/windows/coco-dev-setup/batch/scripts/nab_install_npm_all.bat
@@ -0,0 +1,7 @@
+call print_dashed_seperator
+call get_local_text npm_npm npm npm
+echo %npm_npm%
+
+cd /D %~1
+start /wait cmd /c "echo %npm_npm% & npm install"
+cd /D %work_directory%
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/scripts/nab_install_sass.bat b/scripts/windows/coco-dev-setup/batch/scripts/nab_install_sass.bat
new file mode 100755
index 000000000..6cece7ced
--- /dev/null
+++ b/scripts/windows/coco-dev-setup/batch/scripts/nab_install_sass.bat
@@ -0,0 +1,7 @@
+call print_dashed_seperator
+call get_local_text npm_sass npm sass
+echo %npm_sass%
+
+cd /D %~1
+start /wait cmd /c "echo %npm_sass% & gem install sass"
+cd /D %work_directory%
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/scripts/npm_and_brunch_setup.bat b/scripts/windows/coco-dev-setup/batch/scripts/npm_and_brunch_setup.bat
index c54b2d205..8a3819228 100644
--- a/scripts/windows/coco-dev-setup/batch/scripts/npm_and_brunch_setup.bat
+++ b/scripts/windows/coco-dev-setup/batch/scripts/npm_and_brunch_setup.bat
@@ -1,3 +1,4 @@
+<<<<<<< HEAD
call print_npm_and_brunch_header
call print_dashed_seperator
@@ -86,4 +87,30 @@ echo !npm_script!
call print_dashed_seperator
-pause
\ No newline at end of file
+pause
+=======
+call print_npm_and_brunch_header
+call print_dashed_seperator
+
+set work_directory=%CD%
+
+set "curl_app=..\utilities\curl.exe"
+set "zu_app=..\utilities\7za.exe"
+
+set coco_root=%repository_path%\coco
+set coco_db=%repository_path%\cocodb
+
+call nab_install_npm %coco_root%
+
+call nab_install_bower %coco_root%
+
+call nab_install_sass %coco_root%
+
+call nab_install_npm_all %coco_root%
+
+call nab_install_mongodb %coco_db%
+
+call nab_automatic_script.bat %coco_root% %coco_db%
+
+call print_dashed_seperator
+>>>>>>> 072729acc34123c42250d361955438cfd8c210d7
diff --git a/scripts/windows/coco-dev-setup/batch/scripts/open_localized_text_file.bat b/scripts/windows/coco-dev-setup/batch/scripts/open_localized_text_file.bat
new file mode 100755
index 000000000..bee9f5dd6
--- /dev/null
+++ b/scripts/windows/coco-dev-setup/batch/scripts/open_localized_text_file.bat
@@ -0,0 +1,6 @@
+set "LFTP=%1-%language_id%.coco"
+if not exist "%LFTP%" (
+ call open_text_file %1.coco
+) else (
+ call open_text_file %LFTP%
+)
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/scripts/open_readme.bat b/scripts/windows/coco-dev-setup/batch/scripts/open_readme.bat
index 484f3dd75..730a3f577 100644
--- a/scripts/windows/coco-dev-setup/batch/scripts/open_readme.bat
+++ b/scripts/windows/coco-dev-setup/batch/scripts/open_readme.bat
@@ -1 +1 @@
-call open_text_file ..\\config\\readme.coco
\ No newline at end of file
+call open_localized_text_file ..\\config\\localized\\readme
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/scripts/print_dashed_seperator.bat b/scripts/windows/coco-dev-setup/batch/scripts/print_dashed_seperator.bat
index cd9f23c7d..b79e5154b 100644
--- a/scripts/windows/coco-dev-setup/batch/scripts/print_dashed_seperator.bat
+++ b/scripts/windows/coco-dev-setup/batch/scripts/print_dashed_seperator.bat
@@ -1,3 +1,8 @@
+<<<<<<< HEAD
echo.
echo - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+=======
+echo.
+echo - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+>>>>>>> 072729acc34123c42250d361955438cfd8c210d7
echo.
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/scripts/print_exit.bat b/scripts/windows/coco-dev-setup/batch/scripts/print_exit.bat
index 1e6a5e14f..afe7bbf61 100644
--- a/scripts/windows/coco-dev-setup/batch/scripts/print_exit.bat
+++ b/scripts/windows/coco-dev-setup/batch/scripts/print_exit.bat
@@ -1,2 +1,7 @@
+<<<<<<< HEAD
set /p res="Press any key to exit..."
+=======
+call get_local_text global_exit global exit
+set /p res="%global_exit%"
+>>>>>>> 072729acc34123c42250d361955438cfd8c210d7
exit
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/scripts/print_license.bat b/scripts/windows/coco-dev-setup/batch/scripts/print_license.bat
index a208ca559..3acee4bcc 100644
--- a/scripts/windows/coco-dev-setup/batch/scripts/print_license.bat
+++ b/scripts/windows/coco-dev-setup/batch/scripts/print_license.bat
@@ -1 +1 @@
-print_file ..\\config\\license.coco
\ No newline at end of file
+call print_localized_file ..\\config\\localized\\license
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/scripts/print_localized_file.bat b/scripts/windows/coco-dev-setup/batch/scripts/print_localized_file.bat
new file mode 100755
index 000000000..e71fe3364
--- /dev/null
+++ b/scripts/windows/coco-dev-setup/batch/scripts/print_localized_file.bat
@@ -0,0 +1,6 @@
+set "LFTP=%1-%language_id%.coco"
+if not exist "%LFTP%" (
+ call print_file %1.coco
+) else (
+ call print_file %LFTP%
+)
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/scripts/print_seperator.bat b/scripts/windows/coco-dev-setup/batch/scripts/print_seperator.bat
index cf145cb6d..aae85a7d8 100644
--- a/scripts/windows/coco-dev-setup/batch/scripts/print_seperator.bat
+++ b/scripts/windows/coco-dev-setup/batch/scripts/print_seperator.bat
@@ -1,3 +1,8 @@
+<<<<<<< HEAD
echo.
echo -----------------------------------------------------------------------------
+=======
+echo.
+echo -------------------------------------------------------------------------------
+>>>>>>> 072729acc34123c42250d361955438cfd8c210d7
echo.
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/scripts/print_tips.bat b/scripts/windows/coco-dev-setup/batch/scripts/print_tips.bat
index c00833574..0a2e3033a 100644
--- a/scripts/windows/coco-dev-setup/batch/scripts/print_tips.bat
+++ b/scripts/windows/coco-dev-setup/batch/scripts/print_tips.bat
@@ -1 +1 @@
-print_file ..\\config\\tips.coco
\ No newline at end of file
+call print_localized_file ..\\config\\localized\\tips
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/scripts/read_cache.bat b/scripts/windows/coco-dev-setup/batch/scripts/read_cache.bat
new file mode 100755
index 000000000..13f96330d
--- /dev/null
+++ b/scripts/windows/coco-dev-setup/batch/scripts/read_cache.bat
@@ -0,0 +1,2 @@
+call get_cache_var language_id
+call get_cache_var repository_path
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/scripts/set_environment_var.bat b/scripts/windows/coco-dev-setup/batch/scripts/set_environment_var.bat
new file mode 100755
index 000000000..05d5362a5
--- /dev/null
+++ b/scripts/windows/coco-dev-setup/batch/scripts/set_environment_var.bat
@@ -0,0 +1 @@
+setx path ";%~1"
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/scripts/setup.bat b/scripts/windows/coco-dev-setup/batch/scripts/setup.bat
index df03a45d7..0fb896ea7 100644
--- a/scripts/windows/coco-dev-setup/batch/scripts/setup.bat
+++ b/scripts/windows/coco-dev-setup/batch/scripts/setup.bat
@@ -1,3 +1,4 @@
+<<<<<<< HEAD
@echo off
setlocal EnableDelayedExpansion
@@ -64,4 +65,34 @@ if "%result%"=="true" (
call open_readme
)
+=======
+@echo off
+setlocal EnableDelayedExpansion
+
+call configuration_cmd
+
+call print_header
+call print_dashed_seperator
+
+call get_config.bat version
+call get_config.bat author
+call get_config.bat copyright
+echo Welcome to the automated Installation of the CodeCombat Dev. Environment!
+echo v%version% authored by %author% and published by %copyright%.
+call print_seperator
+
+call get_language
+
+call get_local_text global_tips global tips
+echo !global_tips!
+call print_tips
+call print_seperator
+
+call sign_license
+
+call download_and_install_applications
+
+start cmd /c "setup_p2.bat"
+
+>>>>>>> 072729acc34123c42250d361955438cfd8c210d7
endlocal
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/scripts/setup_p2.bat b/scripts/windows/coco-dev-setup/batch/scripts/setup_p2.bat
new file mode 100755
index 000000000..f6e0d3b21
--- /dev/null
+++ b/scripts/windows/coco-dev-setup/batch/scripts/setup_p2.bat
@@ -0,0 +1,20 @@
+@echo off
+setlocal EnableDelayedExpansion
+
+call configuration_cmd
+
+call github_setup
+
+call write_cache
+
+call get_local_text switch_install switch install
+call get_local_text switch_close switch close
+call get_local_text switch_open switch open
+
+echo %switch_install%
+echo %switch_close%
+echo.
+
+set /p "dummy=%switch_open%"
+
+endlocal
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/scripts/sign_license.bat b/scripts/windows/coco-dev-setup/batch/scripts/sign_license.bat
index e1c2c2c47..db0b51e4b 100644
--- a/scripts/windows/coco-dev-setup/batch/scripts/sign_license.bat
+++ b/scripts/windows/coco-dev-setup/batch/scripts/sign_license.bat
@@ -1,3 +1,4 @@
+<<<<<<< HEAD
echo In order to continue the installation of the developers environment
echo you will have to read and agree with the following license:
call print_dashed_seperator
@@ -12,4 +13,32 @@ if "%result%"=="false" (
echo This setup can't happen without an agreement.
echo Installation and Setup of the CodeCombat environment is cancelled.
call print_exit
+=======
+call get_local_text license_s1 license s1
+echo !license_s1!
+
+call get_local_text license_s2 license s2
+echo !license_s2!
+
+call print_dashed_seperator
+
+call print_license
+call print_dashed_seperator
+
+call get_local_text license_q1 license q1
+call ask_question "%license_q1%"
+
+call print_dashed_seperator
+
+if "%result%"=="false" (
+ call get_local_text license_a1 license a1
+ echo !license_a1!
+
+ call get_local_text license_a2 license a2
+ echo !license_a2!
+
+ echo.
+
+ call print_exit
+>>>>>>> 072729acc34123c42250d361955438cfd8c210d7
)
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/scripts/write_cache.bat b/scripts/windows/coco-dev-setup/batch/scripts/write_cache.bat
new file mode 100755
index 000000000..2e46ca4c3
--- /dev/null
+++ b/scripts/windows/coco-dev-setup/batch/scripts/write_cache.bat
@@ -0,0 +1,10 @@
+set "cache=..\\config\\cache.coco"
+
+echo ^>%cache%
+
+echo ^>>%cache%
+
+echo ^%language_id%^>>%cache%
+echo ^%repository_path%^>>%cache%
+
+echo ^>>%cache%
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/setup.exe b/scripts/windows/coco-dev-setup/batch/setup.exe
new file mode 100755
index 000000000..f36e70dfa
Binary files /dev/null and b/scripts/windows/coco-dev-setup/batch/setup.exe differ
diff --git a/scripts/windows/coco-dev-setup/last_step_succesfull/config.coco b/scripts/windows/coco-dev-setup/last_step_succesfull/config.coco
deleted file mode 100755
index ae8c66f56..000000000
--- a/scripts/windows/coco-dev-setup/last_step_succesfull/config.coco
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
- 1.0
- GlenDC
- CodeCombat.com © 2013-2014
-
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/last_step_succesfull/downloads.coco b/scripts/windows/coco-dev-setup/last_step_succesfull/downloads.coco
deleted file mode 100755
index 2a0472c41..000000000
--- a/scripts/windows/coco-dev-setup/last_step_succesfull/downloads.coco
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
- http://nodejs.org/dist/v0.10.25/node-v0.10.25-x86.msi
- http://dl.bintray.com/oneclick/rubyinstaller/rubyinstaller-2.0.0-p353.exe?direct
- http://www.python.org/ftp/python/2.7.6/python-2.7.6.msi
-
-
- http://nodejs.org/dist/v0.10.25/x64/node-v0.10.25-x64.msi
- http://dl.bintray.com/oneclick/rubyinstaller/rubyinstaller-2.0.0-p353-x64.exe?direct
- http://www.python.org/ftp/python/2.7.6/python-2.7.6.amd64.msi
-
- https://msysgit.googlecode.com/files/Git-1.8.5.2-preview20131230.exe
-
-
- mongodb=http://fastdl.mongodb.org/win32/mongodb-win32-i386-2.5.4.zip
- mongodb=http://fastdl.mongodb.org/win32/mongodb-win32-x86_64-2008plus-2.5.4.zip
-
-
- mongodb=http://fastdl.mongodb.org/win32/mongodb-win32-i386-2.5.4.zip
- mongodb=http://fastdl.mongodb.org/win32/mongodb-win32-x86_64-2.5.4.zip
-
-
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/last_step_succesfull/en.coco b/scripts/windows/coco-dev-setup/last_step_succesfull/en.coco
deleted file mode 100755
index a2e1f9fca..000000000
--- a/scripts/windows/coco-dev-setup/last_step_succesfull/en.coco
+++ /dev/null
@@ -1,53 +0,0 @@
-
-
-
- English
- Bye Bye!
-
-
- Installation has begun, this can take a while... Please stay tuned...
- Don't close any windows please, unless specified explicitly.
-
-
- [DOWNLOADING AND INSTALLING 3RD PARTY SOFTWARE]
- downloading:
- installing:
- Download and Installation cancelled...
- Software has been installed...
- Installation of the Developers Environment is complete!
- Installation has been stopped...
- unpacking and moving:
- Installing bower, brunch, nodemon and sendwithus...
-
-
- CodeCombat is safely stored on a git repository.
- Therefore you need a git command-line application (Git-bash).
- Examples: git-bash, CygWin, ...
- Do you already have git-bash?
- Enter the path to where you installed Git-bash
- Checking out the Git Repository...
- Please enter your github username:
-
-
- Do you already have the latest version of node-js installed?
- Please enter the full path of the location you installed nodejs to:
-
-
- Do you already have the latest version of ruby installed?
-
-
- Do you already have the latest version of mongo-db installed?
- Enter the path where you would like to install MongoDB:
-
-
- Do you already have the latest version of python installed?
-
-
- Sadly we can't support Windows XP... Please upgrade your OS!
- Machine OS cannot be determined...
- Report your OS to the developers @ CodeCombat.com...
- ... Cleaning up has been disabled... Terminating Script!
- The path to your git application is incorrect, please try again...
- The path you entered is invalid, please try again...
-
-
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/last_step_succesfull/get_config.bat b/scripts/windows/coco-dev-setup/last_step_succesfull/get_config.bat
deleted file mode 100755
index 3849e22c2..000000000
--- a/scripts/windows/coco-dev-setup/last_step_succesfull/get_config.bat
+++ /dev/null
@@ -1,3 +0,0 @@
-powershell .\get_var.ps1 config.coco %1 > var.tmp
-set /p %1= < var.tmp
-del /q var.tmp
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/last_step_succesfull/get_download.bat b/scripts/windows/coco-dev-setup/last_step_succesfull/get_download.bat
deleted file mode 100755
index fde3799e3..000000000
--- a/scripts/windows/coco-dev-setup/last_step_succesfull/get_download.bat
+++ /dev/null
@@ -1,4 +0,0 @@
-@ECHO off
-powershell .\get_var.ps1 downloads.coco %2 %3 %4 %5 %6 > var.tmp
-set /p %1= < var.tmp
-del /q var.tmp
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/last_step_succesfull/get_text.bat b/scripts/windows/coco-dev-setup/last_step_succesfull/get_text.bat
deleted file mode 100755
index 5cae1d431..000000000
--- a/scripts/windows/coco-dev-setup/last_step_succesfull/get_text.bat
+++ /dev/null
@@ -1,4 +0,0 @@
-@ECHO off
-powershell .\get_var.ps1 %1.coco %3 %4 %5 %6 %7 > var.tmp
-set /p %2= < var.tmp
-del /q var.tmp
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/last_step_succesfull/get_var.ps1 b/scripts/windows/coco-dev-setup/last_step_succesfull/get_var.ps1
deleted file mode 100755
index 77573929f..000000000
--- a/scripts/windows/coco-dev-setup/last_step_succesfull/get_var.ps1
+++ /dev/null
@@ -1,17 +0,0 @@
-$xml_file = [xml](get-content $args[0])
-if($args.count -eq 2)
-{
- $xml_file.variables.($args[1])
-}
-elseif($args.count -eq 3)
-{
- $xml_file.variables.($args[1]).($args[2])
-}
-elseif($args.count -eq 4)
-{
- $xml_file.variables.($args[1]).($args[2]).($args[3])
-}
-elseif($args.count -eq 5)
-{
- $xml_file.variables.($args[1]).($args[2]).($args[3]).($args[4])
-}
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/last_step_succesfull/run_script.bat b/scripts/windows/coco-dev-setup/last_step_succesfull/run_script.bat
deleted file mode 100755
index dfc6e6cc0..000000000
--- a/scripts/windows/coco-dev-setup/last_step_succesfull/run_script.bat
+++ /dev/null
@@ -1,2 +0,0 @@
-@echo off
-powershell "& "%*"
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/src/get_category.cpp b/scripts/windows/coco-dev-setup/src/get_category.cpp
deleted file mode 100755
index 971c3aefb..000000000
--- a/scripts/windows/coco-dev-setup/src/get_category.cpp
+++ /dev/null
@@ -1,146 +0,0 @@
-#include "stdafx.h"
-#include
-#include
-#include
-#include
-#include
-
-#define tstring std::wstring
-#define tcout std::wcout
-
-static const tstring DEF_URL = L"http://www.google.com";
-
-int ErrorReport(const tstring & str, int value = 0)
-{
- tcout << str.c_str();
- return value;
-}
-
-void GetHashInfo(tstring id, std::vector & info) {
- while(id.size() > 0)
- {
- size_t pos = id.find(L'-');
-
- tstring substr =
- id.substr(0, pos == tstring::npos ? id.length() : pos);
- info.push_back(substr);
-
- if(pos == tstring::npos) id = L"";
- else
- {
- ++pos;
- id = id.substr(pos, id.length() - pos);
- }
- }
-}
-
-void SetArrayVariable(
- const tstring & name,
- int id,
- const tstring & line
- )
-{
- tcout << L"set \"";
- tcout << name;
- tcout << L"[" << id << "]";
- tcout << L"=" << line;
- tcout << L"\"" << std::endl;
-}
-
-void FillArray(
- const std::vector & info,
- const tstring & name,
- const tstring & id_array_name,
- const tstring & file,
- int & id
- )
-{
- if(info.size() == 0) return;
-
- auto it = info.begin();
- size_t indention = 0;
- unsigned int nlc = 0;
-
- std::wifstream infile(file.c_str(), std::ifstream::in);
-
- if(!infile)
- {
- #ifdef _DEBUG
- tcout << file.c_str() << std::endl;
- tcout << strerror(errno) << std::endl;
- #endif
- return;
- }
-
- tstring line;
- int counter = 1;
- while (std::getline(infile, line))
- {
- size_t cpos = line.find('[');
- if(cpos == tstring::npos)
- {
- cpos = line.find_first_not_of(L" \t\r\n");
- }
- if(nlc++ == 0 || cpos == indention)
- {
- indention = cpos;
- if(it == info.end())
- {
- size_t pos = line.find(L'=') + 1;
- SetArrayVariable(
- name, id,
- line.substr(pos, line.size() - pos)
- );
- SetArrayVariable(
- id_array_name, id++,
- line.substr(cpos, pos - 3)
- );
- ++counter;
- }
- else if(line.find(*it) != tstring::npos)
- {
- ++it;
- nlc = 0;
- }
- }
- else if(counter > 1)
- {
- return;
- }
- }
-
- infile.close();
- return;
-}
-
-int _tmain(int argc, _TCHAR* argv[])
-{
- if(argc == 1)
- return ErrorReport(L"Please specify a localisation file.");
- else if(argc == 2)
- return ErrorReport(L"Please specify the name of the array.");
- else if(argc == 3)
- return ErrorReport(L"Please specify the name of the name-array.");
- else if(argc == 4)
- return ErrorReport(L"Please specify the counter parameter.");
- else if(argc == 5)
- return ErrorReport(L"Please specify one or more categories you are looking for.");
-
- tstring file, name, counter_name, id_array_name;
- file = argv[1];
- name = argv[2];
- id_array_name = argv[3];
- counter_name = argv[4];
- int id = 1;
-
- for(int i = 5 ; i < argc ; ++i)
- {
- std::vector information;
- GetHashInfo(argv[i], information);
- FillArray(information, name, id_array_name, file, id);
- }
-
- tcout << L"set \"" << counter_name << L"=" << (id - 1) << L"\"";
-
- return 0;
-}
diff --git a/scripts/windows/coco-dev-setup/src/get_extension.cpp b/scripts/windows/coco-dev-setup/src/get_extension.cpp
deleted file mode 100755
index f311ac93f..000000000
--- a/scripts/windows/coco-dev-setup/src/get_extension.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-#include "stdafx.h"
-#include
-#include
-#include
-#include
-#include
-
-#define tstring std::wstring
-#define tcout std::wcout
-
-int ErrorReport(const tstring & str, int value = 0)
-{
- tcout << str.c_str();
- return value;
-}
-
-int _tmain(int argc, _TCHAR* argv[])
-{
- if(argc == 1)
- return ErrorReport(L"Please specify a download URL.");
- if(argc == 2)
- return ErrorReport(L"Please specify a name for your variable.");
-
- tstring url, name, extension;
- url = argv[1];
- name = argv[2];
-
- if(url.find(L"exe") != tstring::npos) extension = L"exe";
- else if(url.find(L"msi") != tstring::npos) extension = L"msi";
- else if(url.find(L"zip") != tstring::npos) extension = L"zip";
-
- tcout << L"set \"" << name << L"=";
- tcout << extension << L"\"";
-
- return 0;
-}
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/src/get_var.cpp b/scripts/windows/coco-dev-setup/src/get_var.cpp
deleted file mode 100755
index e69b0be1c..000000000
--- a/scripts/windows/coco-dev-setup/src/get_var.cpp
+++ /dev/null
@@ -1,108 +0,0 @@
-#include "stdafx.h"
-#include
-#include
-#include
-#include
-#include
-
-#define tstring std::wstring
-#define tcout std::wcout
-
-static const tstring DEF_URL = L"http://www.google.com";
-
-int ErrorReport(const tstring & str, int value = 0)
-{
- tcout << str.c_str();
- return value;
-}
-
-void GetHashInfo(tstring id, std::vector & info) {
- while(id.size() > 0)
- {
- size_t pos = id.find(L'-');
-
- tstring substr =
- id.substr(0, pos == tstring::npos ? id.length() : pos);
- info.push_back(substr);
-
- if(pos == tstring::npos) id = L"";
- else
- {
- ++pos;
- id = id.substr(pos, id.length() - pos);
- }
- }
-}
-
-std::wstring GetText(const std::vector & info, const tstring & file)
-{
- if(info.size() == 0) return L"Info Size is 0.";
-
- auto it = info.begin();
- auto last = info.end() - 1;
- size_t indention = 0;
- unsigned int nlc = 0;
-
- std::wifstream infile(file.c_str(), std::ifstream::in);
-
- if(!infile)
- {
- #ifdef _DEBUG
- tcout << file.c_str() << std::endl;
- tcout << strerror(errno) << std::endl;
- #endif
- return L"File couldn't be opened.";
- }
-
- tstring line;
- while (std::getline(infile, line))
- {
- size_t cpos = line.find('[');
- if(nlc++ == 0 || cpos == indention)
- {
- indention = cpos;
- if(line.find(*it) != tstring::npos)
- {
- if(it == last)
- {
- size_t pos = line.find(L'=') + 1;
- infile.close();
- return line.substr(pos, line.size() - pos);
- }
- else
- {
- ++it;
- nlc = 0;
- }
- }
- }
- }
-
- infile.close();
- return L"Var couldn't be found.";
-}
-
-int _tmain(int argc, _TCHAR* argv[])
-{
- if(argc == 1)
- return ErrorReport(L"Please specify a localisation file.");
- else if(argc == 2)
- return ErrorReport(L"Please specify the ID you are looking for.");
-
- tstring file, hash;
- file = argv[1];
- hash = argv[2];
-
- std::vector information;
- GetHashInfo(hash, information);
-
- size_t size = information.size();
- for(unsigned int i = 0 ; i < size ; ++i)
- {
- tcout << information[i];
- if(i != size - 1) tcout << L"_";
- }
- tcout << L"=" << GetText(information, file);
-
- return 0;
-}
diff --git a/server/articles/Article.coffee b/server/articles/Article.coffee
index 19a1e3253..626fc779c 100644
--- a/server/articles/Article.coffee
+++ b/server/articles/Article.coffee
@@ -1,12 +1,11 @@
mongoose = require('mongoose')
plugins = require('../plugins/plugins')
-ArticleSchema = new mongoose.Schema(
- body: String,
-)
+ArticleSchema = new mongoose.Schema(body: String, {strict:false})
ArticleSchema.plugin(plugins.NamedPlugin)
ArticleSchema.plugin(plugins.VersionedPlugin)
ArticleSchema.plugin(plugins.SearchablePlugin, {searchable: ['body', 'name']})
+ArticleSchema.plugin(plugins.PatchablePlugin)
module.exports = mongoose.model('article', ArticleSchema)
diff --git a/server/articles/article_handler.coffee b/server/articles/article_handler.coffee
index b519b8b9f..8aa2d26dc 100644
--- a/server/articles/article_handler.coffee
+++ b/server/articles/article_handler.coffee
@@ -4,8 +4,13 @@ Handler = require('../commons/Handler')
ArticleHandler = class ArticleHandler extends Handler
modelClass: Article
editableProperties: ['body', 'name', 'i18n']
+ jsonSchema: require '../../app/schemas/models/article'
hasAccess: (req) ->
req.method is 'GET' or req.user?.isAdmin()
+ hasAccessToDocument: (req, document, method=null) ->
+ return true if req.method is 'GET' or method is 'get' or req.user?.isAdmin()
+ return false
+
module.exports = new ArticleHandler()
diff --git a/server/commons/Handler.coffee b/server/commons/Handler.coffee
index f38885fd9..23909b6a2 100644
--- a/server/commons/Handler.coffee
+++ b/server/commons/Handler.coffee
@@ -3,6 +3,7 @@ mongoose = require('mongoose')
Grid = require 'gridfs-stream'
errors = require './errors'
log = require 'winston'
+Patch = require '../patches/Patch'
PROJECT = {original:1, name:1, version:1, description: 1, slug:1, kind: 1}
FETCH_LIMIT = 200
@@ -27,8 +28,7 @@ module.exports = class Handler
getEditableProperties: (req, document) ->
props = @editableProperties.slice()
isBrandNew = req.method is 'POST' and not req.body.original
- if isBrandNew
- props = props.concat @postEditableProperties
+ props = props.concat @postEditableProperties if isBrandNew
if @modelClass.schema.uses_coco_permissions
# can only edit permissions if this is a brand new property,
@@ -37,8 +37,8 @@ module.exports = class Handler
if isBrandNew or isOwner or req.user?.isAdmin()
props.push 'permissions'
- if @modelClass.schema.uses_coco_versions
- props.push 'commitMessage'
+ props.push 'commitMessage' if @modelClass.schema.uses_coco_versions
+ props.push 'allowPatches' if @modelClass.schema.is_patchable
props
@@ -48,6 +48,7 @@ module.exports = class Handler
sendMethodNotAllowed: (res) -> errors.badMethod(res)
sendBadInputError: (res, message) -> errors.badInput(res, message)
sendDatabaseError: (res, err) ->
+ return @sendError(res, err.code, err.response) if err.response and err.code
log.error "Database error, #{err}"
errors.serverError(res, 'Database error, ' + err)
@@ -92,8 +93,32 @@ module.exports = class Handler
getByRelationship: (req, res, args...) ->
# this handler should be overwritten by subclasses
+ if @modelClass.schema.is_patchable
+ return @getPatchesFor(req, res, args[0]) if req.route.method is 'get' and args[1] is 'patches'
+ return @setListening(req, res, args[0]) if req.route.method is 'put' and args[1] is 'listen'
return @sendNotFoundError(res)
+ getPatchesFor: (req, res, id) ->
+ query = { 'target.original': mongoose.Types.ObjectId(id), status: req.query.status or 'pending' }
+ Patch.find(query).sort('-created').exec (err, patches) =>
+ return @sendDatabaseError(res, err) if err
+ patches = (patch.toObject() for patch in patches)
+ @sendSuccess(res, patches)
+
+ setListening: (req, res, id) ->
+ @getDocumentForIdOrSlug id, (err, document) =>
+ return @sendUnauthorizedError(res) unless @hasAccessToDocument(req, document, 'get')
+ return @sendDatabaseError(res, err) if err
+ return @sendNotFoundError(res) unless document?
+ listeners = document.get('listeners') or []
+ me = req.user.get('_id')
+ listeners = (l for l in listeners when not l.equals(me))
+ listeners.push me if req.body.on
+ document.set 'listeners', listeners
+ document.save (err, document) =>
+ return @sendDatabaseError(res, err) if err
+ @sendSuccess(res, @formatEntity(req, document))
+
search: (req, res) ->
unless @modelClass.schema.uses_coco_search
return @sendNotFoundError(res)
@@ -203,10 +228,9 @@ module.exports = class Handler
return @sendBadInputError(res, 'No input.') if _.isEmpty(req.body)
return @sendBadInputError(res, 'id should not be included.') if req.body._id
return @sendUnauthorizedError(res) unless @hasAccess(req)
- validation = @validateDocumentInput(req.body)
- return @sendBadInputError(res, validation.errors) unless validation.valid
document = @makeNewInstance(req)
@saveChangesToDocument req, document, (err) =>
+ return @sendBadInputError(res, err.errors) if err?.valid is false
return @sendDatabaseError(res, err) if err
@sendSuccess(res, @formatEntity(req, document))
@@ -220,13 +244,12 @@ module.exports = class Handler
return @sendBadInputError(res, 'No input.') if _.isEmpty(req.body)
return @sendBadInputError(res, 'id should not be included.') if req.body._id
return @sendUnauthorizedError(res) unless @hasAccess(req)
- validation = @validateDocumentInput(req.body)
- return @sendBadInputError(res, validation.errors) unless validation.valid
document = @makeNewInstance(req)
document.set('original', document._id)
document.set('creator', req.user._id)
@saveChangesToDocument req, document, (err) =>
- return @sendBadInputError(res, err.response) if err?.response
+ console.log 'saved new version', document.toObject()
+ return @sendBadInputError(res, err.errors) if err?.valid is false
return @sendDatabaseError(res, err) if err
@sendSuccess(res, @formatEntity(req, document))
@@ -245,8 +268,6 @@ module.exports = class Handler
return @sendBadInputError(res, 'This entity is not versioned') unless @modelClass.schema.uses_coco_versions
return @sendBadInputError(res, 'No input.') if _.isEmpty(req.body)
return @sendUnauthorizedError(res) unless @hasAccess(req)
- validation = @validateDocumentInput(req.body)
- return @sendBadInputError(res, validation.errors) unless validation.valid
@getDocumentForIdOrSlug req.body._id, (err, parentDocument) =>
return @sendBadInputError(res, 'Bad id.') if err and err.name is 'CastError'
return @sendDatabaseError(res, err) if err
@@ -261,6 +282,8 @@ module.exports = class Handler
delete updatedObject[prop]
delete updatedObject._id
major = req.body.version?.major
+ validation = @validateDocumentInput(updatedObject)
+ return @sendBadInputError(res, validation.errors) unless validation.valid
done = (err, newDocument) =>
return @sendDatabaseError(res, err) if err
diff --git a/server/commons/mapping.coffee b/server/commons/mapping.coffee
index 2f659811b..3cfcc2164 100644
--- a/server/commons/mapping.coffee
+++ b/server/commons/mapping.coffee
@@ -6,23 +6,10 @@ module.exports.handlers =
'level_feedback': 'levels/feedbacks/level_feedback_handler'
'level_session': 'levels/sessions/level_session_handler'
'level_system': 'levels/systems/level_system_handler'
+ 'patch': 'patches/patch_handler'
'thang_type': 'levels/thangs/thang_type_handler'
'user': 'users/user_handler'
-module.exports.schemas =
- 'article': 'articles/article_schema'
- 'common': 'commons/schemas'
- 'i18n': 'commons/i18n_schema'
- 'level': 'levels/level_schema'
- 'level_component': 'levels/components/level_component_schema'
- 'level_feedback': 'levels/feedbacks/level_feedback_schema'
- 'level_session': 'levels/sessions/level_session_schema'
- 'level_system': 'levels/systems/level_system_schema'
- 'metaschema': 'commons/metaschema'
- 'thang_component': 'levels/thangs/thang_component_schema'
- 'thang_type': 'levels/thangs/thang_type_schema'
- 'user': 'users/user_schema'
-
module.exports.routes =
[
'routes/auth'
diff --git a/server/levels/Level.coffee b/server/levels/Level.coffee
index c61245ed5..9cadeac7b 100644
--- a/server/levels/Level.coffee
+++ b/server/levels/Level.coffee
@@ -1,6 +1,6 @@
mongoose = require('mongoose')
plugins = require('../plugins/plugins')
-jsonschema = require('./level_schema')
+jsonschema = require('../../app/schemas/models/level')
LevelSchema = new mongoose.Schema({
description: String
@@ -10,6 +10,7 @@ LevelSchema.plugin(plugins.NamedPlugin)
LevelSchema.plugin(plugins.PermissionsPlugin)
LevelSchema.plugin(plugins.VersionedPlugin)
LevelSchema.plugin(plugins.SearchablePlugin, {searchable: ['name', 'description']})
+LevelSchema.plugin(plugins.PatchablePlugin)
LevelSchema.pre 'init', (next) ->
return next() unless jsonschema.properties?
diff --git a/server/levels/components/LevelComponent.coffee b/server/levels/components/LevelComponent.coffee
index 3dc373be1..6c1a58370 100644
--- a/server/levels/components/LevelComponent.coffee
+++ b/server/levels/components/LevelComponent.coffee
@@ -1,16 +1,17 @@
mongoose = require('mongoose')
plugins = require('../../plugins/plugins')
-jsonschema = require('./level_component_schema')
+jsonschema = require('../../../app/schemas/models/level_component')
LevelComponentSchema = new mongoose.Schema {
description: String
system: String
}, {strict: false}
-LevelComponentSchema.plugin(plugins.NamedPlugin)
-LevelComponentSchema.plugin(plugins.PermissionsPlugin)
-LevelComponentSchema.plugin(plugins.VersionedPlugin)
-LevelComponentSchema.plugin(plugins.SearchablePlugin, {searchable: ['name', 'description', 'system']})
+LevelComponentSchema.plugin plugins.NamedPlugin
+LevelComponentSchema.plugin plugins.PermissionsPlugin
+LevelComponentSchema.plugin plugins.VersionedPlugin
+LevelComponentSchema.plugin plugins.SearchablePlugin, {searchable: ['name', 'description', 'system']}
+LevelComponentSchema.plugin plugins.PatchablePlugin
LevelComponentSchema.pre 'init', (next) ->
return next() unless jsonschema.properties?
diff --git a/server/levels/components/level_component_handler.coffee b/server/levels/components/level_component_handler.coffee
index 89a3ea21c..3bcc572d0 100644
--- a/server/levels/components/level_component_handler.coffee
+++ b/server/levels/components/level_component_handler.coffee
@@ -3,6 +3,7 @@ Handler = require('../../commons/Handler')
LevelComponentHandler = class LevelComponentHandler extends Handler
modelClass: LevelComponent
+ jsonSchema: require '../../../app/schemas/models/level_component'
editableProperties: [
'system'
'description'
diff --git a/server/levels/feedbacks/LevelFeedback.coffee b/server/levels/feedbacks/LevelFeedback.coffee
index 0eecdec32..5fef6a567 100644
--- a/server/levels/feedbacks/LevelFeedback.coffee
+++ b/server/levels/feedbacks/LevelFeedback.coffee
@@ -2,7 +2,7 @@
mongoose = require('mongoose')
plugins = require('../../plugins/plugins')
-jsonschema = require('./level_feedback_schema')
+jsonschema = require('../../../app/schemas/models/level_feedback')
LevelFeedbackSchema = new mongoose.Schema({
created:
diff --git a/server/levels/feedbacks/level_feedback_handler.coffee b/server/levels/feedbacks/level_feedback_handler.coffee
index 5cb8be50b..58d268db1 100644
--- a/server/levels/feedbacks/level_feedback_handler.coffee
+++ b/server/levels/feedbacks/level_feedback_handler.coffee
@@ -4,6 +4,7 @@ Handler = require('../../commons/Handler')
class LevelFeedbackHandler extends Handler
modelClass: LevelFeedback
editableProperties: ['rating', 'review', 'level', 'levelID', 'levelName']
+ jsonSchema: require '../../../app/schemas/models/level_feedback'
makeNewInstance: (req) ->
feedback = super(req)
diff --git a/server/levels/level_handler.coffee b/server/levels/level_handler.coffee
index ad26fe0e1..f0c6d225b 100644
--- a/server/levels/level_handler.coffee
+++ b/server/levels/level_handler.coffee
@@ -8,6 +8,7 @@ mongoose = require('mongoose')
LevelHandler = class LevelHandler extends Handler
modelClass: Level
+ jsonSchema: require '../../app/schemas/models/level'
editableProperties: [
'description'
'documentation'
@@ -37,8 +38,8 @@ LevelHandler = class LevelHandler extends Handler
return @getLeaderboardFacebookFriends(req, res, args[0]) if args[1] is 'leaderboard_facebook_friends'
return @getLeaderboardGPlusFriends(req, res, args[0]) if args[1] is 'leaderboard_gplus_friends'
return @getHistogramData(req, res, args[0]) if args[1] is 'histogram_data'
-
- return @sendNotFoundError(res)
+ return @checkExistence(req, res, args[0]) if args[1] is 'exists'
+ super(arguments...)
fetchLevelByIDAndHandleErrors: (id, req, res, callback) ->
@getDocumentForIdOrSlug id, (err, level) =>
@@ -130,7 +131,23 @@ LevelHandler = class LevelHandler extends Handler
if err? then return @sendDatabaseError res, err
valueArray = _.pluck data, "totalScore"
@sendSuccess res, valueArray
-
+
+ checkExistence: (req, res, slugOrID) ->
+ findParameters = {}
+ if Handler.isID slugOrID
+ findParameters["_id"] = slugOrID
+ else
+ findParameters["slug"] = slugOrID
+ selectString = 'original version.major permissions'
+ query = Level.findOne(findParameters)
+ .select(selectString)
+ .lean()
+
+ query.exec (err, level) =>
+ return @sendDatabaseError(res, err) if err
+ return @sendNotFoundError(res) unless level?
+ res.send({"exists":true})
+ res.end()
getLeaderboard: (req, res, id) ->
sessionsQueryParameters = @makeLeaderboardQueryParameters(req, id)
@@ -224,9 +241,6 @@ LevelHandler = class LevelHandler extends Handler
original: level.original.toString()
majorVersion: level.version.major
submitted:true
-
- console.log sessionsQueryParameters
-
query = Session
.find(sessionsQueryParameters)
@@ -237,7 +251,6 @@ LevelHandler = class LevelHandler extends Handler
return @sendDatabaseError res, err if err? or not resultSessions
teamSessions = _.groupBy resultSessions, 'team'
- console.log teamSessions
sessions = []
numberOfTeams = 0
for team of teamSessions
diff --git a/server/levels/sessions/LevelSession.coffee b/server/levels/sessions/LevelSession.coffee
index 952782f1b..c30519ba0 100644
--- a/server/levels/sessions/LevelSession.coffee
+++ b/server/levels/sessions/LevelSession.coffee
@@ -2,7 +2,7 @@
mongoose = require('mongoose')
plugins = require('../../plugins/plugins')
-jsonschema = require('./level_session_schema')
+jsonschema = require('../../../app/schemas/models/level_session')
LevelSessionSchema = new mongoose.Schema({
created:
diff --git a/server/levels/sessions/level_session_handler.coffee b/server/levels/sessions/level_session_handler.coffee
index ca8680a17..5771711f2 100644
--- a/server/levels/sessions/level_session_handler.coffee
+++ b/server/levels/sessions/level_session_handler.coffee
@@ -9,10 +9,11 @@ class LevelSessionHandler extends Handler
editableProperties: ['multiplayer', 'players', 'code', 'completed', 'state',
'levelName', 'creatorName', 'levelID', 'screenshot',
'chat', 'teamSpells', 'submitted', 'unsubscribed']
+ jsonSchema: require '../../../app/schemas/models/level_session'
getByRelationship: (req, res, args...) ->
return @getActiveSessions req, res if args.length is 2 and args[1] is 'active'
- return @sendNotFoundError(res)
+ super(arguments...)
getActiveSessions: (req, res) ->
return @sendUnauthorizedError(res) unless req.user.isAdmin()
diff --git a/server/levels/systems/LevelSystem.coffee b/server/levels/systems/LevelSystem.coffee
index cf21f7355..f945aaa95 100644
--- a/server/levels/systems/LevelSystem.coffee
+++ b/server/levels/systems/LevelSystem.coffee
@@ -1,6 +1,6 @@
mongoose = require('mongoose')
plugins = require('../../plugins/plugins')
-jsonschema = require('./level_system_schema')
+jsonschema = require('../../../app/schemas/models/level_system')
LevelSystemSchema = new mongoose.Schema {
description: String
@@ -10,6 +10,7 @@ LevelSystemSchema.plugin(plugins.NamedPlugin)
LevelSystemSchema.plugin(plugins.PermissionsPlugin)
LevelSystemSchema.plugin(plugins.VersionedPlugin)
LevelSystemSchema.plugin(plugins.SearchablePlugin, {searchable: ['name', 'description']})
+LevelSystemSchema.plugin(plugins.PatchablePlugin)
LevelSystemSchema.pre 'init', (next) ->
return next() unless jsonschema.properties?
diff --git a/server/levels/systems/level_system_handler.coffee b/server/levels/systems/level_system_handler.coffee
index 1b1e511c1..bf1bb39d5 100644
--- a/server/levels/systems/level_system_handler.coffee
+++ b/server/levels/systems/level_system_handler.coffee
@@ -13,6 +13,7 @@ LevelSystemHandler = class LevelSystemHandler extends Handler
'configSchema'
]
postEditableProperties: ['name']
+ jsonSchema: require '../../../app/schemas/models/level_system'
getEditableProperties: (req, document) ->
props = super(req, document)
diff --git a/server/levels/thangs/ThangType.coffee b/server/levels/thangs/ThangType.coffee
index 92915e8d0..292597719 100644
--- a/server/levels/thangs/ThangType.coffee
+++ b/server/levels/thangs/ThangType.coffee
@@ -5,8 +5,9 @@ ThangTypeSchema = new mongoose.Schema({
body: String,
}, {strict: false})
-ThangTypeSchema.plugin(plugins.NamedPlugin)
-ThangTypeSchema.plugin(plugins.VersionedPlugin)
-ThangTypeSchema.plugin(plugins.SearchablePlugin, {searchable: ['name']})
+ThangTypeSchema.plugin plugins.NamedPlugin
+ThangTypeSchema.plugin plugins.VersionedPlugin
+ThangTypeSchema.plugin plugins.SearchablePlugin, {searchable: ['name']}
+ThangTypeSchema.plugin plugins.PatchablePlugin
module.exports = mongoose.model('thang.type', ThangTypeSchema)
diff --git a/server/levels/thangs/thang_type_handler.coffee b/server/levels/thangs/thang_type_handler.coffee
index a446b56be..abdecd529 100644
--- a/server/levels/thangs/thang_type_handler.coffee
+++ b/server/levels/thangs/thang_type_handler.coffee
@@ -3,6 +3,7 @@ Handler = require('../../commons/Handler')
ThangTypeHandler = class ThangTypeHandler extends Handler
modelClass: ThangType
+ jsonSchema: require '../../../app/schemas/models/thang_type'
editableProperties: [
'name',
'raw',
diff --git a/server/patches/Patch.coffee b/server/patches/Patch.coffee
new file mode 100644
index 000000000..df621f2a4
--- /dev/null
+++ b/server/patches/Patch.coffee
@@ -0,0 +1,46 @@
+mongoose = require('mongoose')
+{handlers} = require '../commons/mapping'
+
+PatchSchema = new mongoose.Schema({}, {strict: false})
+
+PatchSchema.pre 'save', (next) ->
+ return next() unless @isNew # patch can't be altered after creation, so only need to check data once
+ target = @get('target')
+ targetID = target.id
+ Handler = require '../commons/Handler'
+ if not Handler.isID(targetID)
+ err = new Error('Invalid input.')
+ err.response = {message:"isn't a MongoDB id.", property:'target.id'}
+ err.code = 422
+ return next(err)
+
+ collection = target.collection
+ handler = require('../' + handlers[collection])
+ handler.getDocumentForIdOrSlug targetID, (err, document) =>
+ if err
+ err = new Error('Server error.')
+ err.response = {message:'', property:'target.id'}
+ err.code = 500
+ return next(err)
+
+ if not document
+ err = new Error('Target of patch not found.')
+ err.response = {message:'was not found.', property:'target.id'}
+ err.code = 404
+ return next(err)
+
+ target.id = document.get('_id')
+ if handler.modelClass.schema.uses_coco_versions
+ target.original = document.get('original')
+ version = document.get('version')
+ target.version = _.pick document.get('version'), 'major', 'minor'
+ @set('target', target)
+ else
+ target.original = targetID
+
+ patches = document.get('patches') or []
+ patches.push @_id
+ document.set 'patches', patches
+ document.save (err) -> next(err)
+
+module.exports = mongoose.model('patch', PatchSchema)
diff --git a/server/patches/patch_handler.coffee b/server/patches/patch_handler.coffee
new file mode 100644
index 000000000..12a68ed9a
--- /dev/null
+++ b/server/patches/patch_handler.coffee
@@ -0,0 +1,55 @@
+Patch = require('./Patch')
+Handler = require('../commons/Handler')
+schema = require '../../app/schemas/models/patch'
+{handlers} = require '../commons/mapping'
+mongoose = require('mongoose')
+
+PatchHandler = class PatchHandler extends Handler
+ modelClass: Patch
+ editableProperties: []
+ postEditableProperties: ['delta', 'target', 'commitMessage']
+ jsonSchema: require '../../app/schemas/models/patch'
+
+ makeNewInstance: (req) ->
+ patch = super(req)
+ patch.set 'creator', req.user._id
+ patch.set 'created', new Date().toISOString()
+ patch.set 'status', 'pending'
+ patch
+
+ getByRelationship: (req, res, args...) ->
+ return @setStatus(req, res, args[0]) if req.route.method is 'put' and args[1] is 'status'
+ super(arguments...)
+
+ setStatus: (req, res, id) ->
+ newStatus = req.body.status
+ unless newStatus in ['rejected', 'accepted', 'withdrawn']
+ return @sendBadInputError(res, "Status must be 'rejected', 'accepted', or 'withdrawn'")
+
+ @getDocumentForIdOrSlug id, (err, patch) =>
+ return @sendDatabaseError(res, err) if err
+ return @sendNotFoundError(res) unless patch?
+ targetInfo = patch.get('target')
+ targetHandler = require('../' + handlers[targetInfo.collection])
+ targetModel = targetHandler.modelClass
+
+ query = { 'original': targetInfo.original }
+ sort = { 'version.major': -1, 'version.minor': -1 }
+ targetModel.findOne(query).sort(sort).exec (err, target) =>
+ return @sendDatabaseError(res, err) if err
+ return @sendNotFoundError(res) unless target?
+ return @sendUnauthorizedError(res) unless targetHandler.hasAccessToDocument(req, target, 'get')
+
+ if newStatus in ['rejected', 'accepted']
+ return @sendUnauthorizedError(res) unless targetHandler.hasAccessToDocument(req, target, 'put')
+
+ if newStatus is 'withdrawn'
+ return @sendUnauthorizedError(res) unless req.user.get('_id').equals patch.get('creator')
+
+ # these require callbacks
+ patch.update {$set:{status:newStatus}}, {}, ->
+ target.update {$pull:{patches:patch.get('_id')}}, {}, ->
+ @sendSuccess(res, null)
+
+
+module.exports = new PatchHandler()
diff --git a/server/plugins/plugins.coffee b/server/plugins/plugins.coffee
index f1f224b82..5e2e9d3c5 100644
--- a/server/plugins/plugins.coffee
+++ b/server/plugins/plugins.coffee
@@ -2,6 +2,10 @@ mongoose = require('mongoose')
User = require('../users/User')
textSearch = require('mongoose-text-search')
+module.exports.PatchablePlugin = (schema) ->
+ schema.is_patchable = true
+ schema.index({'target.original':1, 'status':'1', 'created':-1})
+
module.exports.NamedPlugin = (schema) ->
schema.add({name: String, slug: String})
schema.index({'slug': 1}, {unique: true, sparse: true, name: 'slug index'})
@@ -22,13 +26,11 @@ module.exports.NamedPlugin = (schema) ->
schema.methods.checkSlugConflicts = (done) ->
slug = @get('slug')
- try
- id = mongoose.Types.ObjectId.createFromHexString(slug)
+ if slug.length is 24 and slug.match(/[a-f0-9]/gi)?.length is 24
err = new Error('Bad name.')
- err.response = {message:'cannot be like a MondoDB id, Mr Hacker.', property:'name'}
+ err.response = {message: 'cannot be like a MongoDB ID, Mr. Hacker.', property: 'name'}
err.code = 422
done(err)
- catch e
query = { slug:slug }
diff --git a/server/queues/scoring.coffee b/server/queues/scoring.coffee
index cd4670708..db4dd1609 100644
--- a/server/queues/scoring.coffee
+++ b/server/queues/scoring.coffee
@@ -36,7 +36,7 @@ module.exports.messagesInQueueCount = (req, res) ->
module.exports.addPairwiseTaskToQueueFromRequest = (req, res) ->
taskPair = req.body.sessions
- addPairwiseTaskToQueue req.body.sessions (err, success) ->
+ addPairwiseTaskToQueue req.body.sessions, (err, success) ->
if err? then return errors.serverError res, "There was an error adding pairwise tasks: #{err}"
sendResponseObject req, res, {"message":"All task pairs were succesfully sent to the queue"}
@@ -113,7 +113,6 @@ module.exports.createNewTask = (req, res) ->
updateSessionToSubmit
fetchInitialSessionsToRankAgainst.bind(@, requestLevelMajorVersion, originalLevelID)
generateAndSendTaskPairsToTheQueue
-
], (err, successMessageObject) ->
if err? then return errors.serverError res, "There was an error submitting the game to the queue:#{err}"
sendResponseObject req, res, successMessageObject
@@ -188,15 +187,16 @@ fetchInitialSessionsToRankAgainst = (levelMajorVersion, levelID, submittedSessio
submittedCode:
$exists: true
team: opposingTeam
-
+
sortParameters =
totalScore: 1
limitNumber = 1
-
- query = LevelSession.find(findParameters)
- .sort(sortParameters)
- .limit(limitNumber)
+ query = LevelSession.aggregate [
+ {$match: findParameters}
+ {$sort: sortParameters}
+ {$limit: limitNumber}
+ ]
query.exec (err, sessionToRankAgainst) ->
callback err, sessionToRankAgainst, submittedSession
@@ -206,6 +206,8 @@ generateAndSendTaskPairsToTheQueue = (sessionToRankAgainst,submittedSession, cal
taskPairs = generateTaskPairs(sessionToRankAgainst, submittedSession)
sendEachTaskPairToTheQueue taskPairs, (taskPairError) ->
if taskPairError? then return callback taskPairError
+ console.log "Sent task pairs to the queue!"
+ console.log taskPairs
callback null, {"message": "All task pairs were succesfully sent to the queue"}
@@ -580,7 +582,8 @@ sendEachTaskPairToTheQueue = (taskPairs, callback) -> async.each taskPairs, send
generateTaskPairs = (submittedSessions, sessionToScore) ->
taskPairs = []
for session in submittedSessions
- session = session.toObject()
+ if session.toObject?
+ session = session.toObject()
teams = ['ogres','humans']
opposingTeams = _.pull teams, sessionToScore.team
if String(session._id) isnt String(sessionToScore._id) and session.team in opposingTeams
diff --git a/server/routes/contact.coffee b/server/routes/contact.coffee
index 51aaa78fc..1ceb8d196 100644
--- a/server/routes/contact.coffee
+++ b/server/routes/contact.coffee
@@ -1,26 +1,38 @@
config = require '../../server_config'
log = require 'winston'
mail = require '../commons/mail'
+User = require '../users/User'
module.exports.setup = (app) ->
app.post '/contact', (req, res) ->
return res.end() unless req.user
log.info "Sending mail from #{req.body.email} saying #{req.body.message}"
if config.isProduction
- options = createMailOptions req.body.email, req.body.message, req.user
- mail.transport.sendMail options, (error, response) ->
- if error
- log.error "Error sending mail: #{error.message or error}"
- else
- log.info "Mail sent successfully. Response: #{response.message}"
+ createMailOptions req.body.email, req.body.message, req.user, req.body.recipientID, req.body.subject, (options) ->
+ mail.transport.sendMail options, (error, response) ->
+ if error
+ log.error "Error sending mail: #{error.message or error}"
+ else
+ log.info "Mail sent successfully. Response: #{response.message}"
return res.end()
-createMailOptions = (sender, message, user) ->
+createMailOptions = (sender, message, user, recipientID, subject, done) ->
# TODO: use email templates here
options =
from: config.mail.username
to: config.mail.username
replyTo: sender
- subject: "[CodeCombat] Feedback - #{sender}"
+ subject: "[CodeCombat] #{subject ? ('Feedback - ' + sender)}"
text: "#{message}\n\nUsername: #{user.get('name') or 'Anonymous'}\nID: #{user._id}"
- #html: message.replace '\n', '
\n'
\ No newline at end of file
+ #html: message.replace '\n', '
\n'
+
+ if recipientID and (user.isAdmin() or ('employer' in (user.permissions ? [])))
+ User.findById(recipientID, 'email').exec (err, document) ->
+ if err
+ log.error "Error looking up recipient to email from #{recipientID}: #{err}" if err
+ else
+ options.bcc = options.to
+ options.to = document.get('email')
+ done options
+ else
+ done options
diff --git a/server/routes/db.coffee b/server/routes/db.coffee
index 723e15b90..beb120573 100644
--- a/server/routes/db.coffee
+++ b/server/routes/db.coffee
@@ -1,7 +1,6 @@
log = require 'winston'
errors = require '../commons/errors'
handlers = require('../commons/mapping').handlers
-schemas = require('../commons/mapping').schemas
mongoose = require 'mongoose'
module.exports.setup = (app) ->
@@ -42,14 +41,15 @@ module.exports.setup = (app) ->
catch error
log.error("Error trying db method #{req.route.method} route #{parts} from #{name}: #{error}")
log.error(error)
+ log.error(error.stack)
errors.notFound(res, "Route #{req.path} not found.")
getSchema = (req, res, moduleName) ->
try
- name = schemas[moduleName.replace '.', '_']
- schema = require('../' + name)
+ name = moduleName.replace '.', '_'
+ schema = require('../../app/schemas/models/' + name)
- res.send(schema)
+ res.send(JSON.stringify(schema, null, '\t'))
res.end()
catch error
diff --git a/server/routes/file.coffee b/server/routes/file.coffee
index 7a16c3709..f01f635e3 100644
--- a/server/routes/file.coffee
+++ b/server/routes/file.coffee
@@ -19,7 +19,7 @@ fileGet = (req, res) ->
objectId = mongoose.Types.ObjectId(path)
query = objectId
catch e
- path = path.split('/')
+ path = path.split('/')
filename = path[path.length-1]
path = path[...path.length-1].join('/')
query =
@@ -34,7 +34,7 @@ fileGet = (req, res) ->
res.setHeader('Content-Type', 'text/json')
res.send(results)
res.end()
-
+
else
Grid.gfs.collection('media').findOne query, (err, filedata) =>
return errors.notFound(res) if not filedata
@@ -42,7 +42,7 @@ fileGet = (req, res) ->
if req.headers['if-modified-since'] is filedata.uploadDate
res.status(304)
return res.end()
-
+
res.setHeader('Content-Type', filedata.contentType)
res.setHeader('Last-Modified', filedata.uploadDate)
res.setHeader('Cache-Control', 'public')
@@ -70,7 +70,7 @@ postFileSchema =
required: ['filename', 'mimetype', 'path']
filePost = (req, res) ->
- return errors.forbidden(res) unless req.user?.isAdmin()
+ return errors.forbidden(res) unless req.user
options = req.body
tv4 = require('tv4').tv4
valid = tv4.validate(options, postFileSchema)
@@ -83,7 +83,8 @@ filePost = (req, res) ->
saveURL = (req, res) ->
options = createPostOptions(req)
- checkExistence options, res, req.body.force, (err) ->
+ force = req.user.isAdmin() and req.body.force
+ checkExistence options, res, force, (err) ->
return errors.serverError(res) if err
writestream = Grid.gfs.createWriteStream(options)
request(req.body.url).pipe(writestream)
@@ -91,7 +92,8 @@ saveURL = (req, res) ->
saveFile = (req, res) ->
options = createPostOptions(req)
- checkExistence options, res, req.body.force, (err) ->
+ force = req.user.isAdmin() and req.body.force
+ checkExistence options, res, force, (err) ->
return if err
writestream = Grid.gfs.createWriteStream(options)
f = req.files[req.body.postName]
@@ -101,7 +103,8 @@ saveFile = (req, res) ->
savePNG = (req, res) ->
options = createPostOptions(req)
- checkExistence options, res, req.body.force, (err) ->
+ force = req.user.isAdmin() and req.body.force
+ checkExistence options, res, force, (err) ->
return errors.serverError(res) if err
writestream = Grid.gfs.createWriteStream(options)
img = new Buffer(req.body.b64png, 'base64')
@@ -143,11 +146,11 @@ createPostOptions = (req) ->
unless req.body.name
name = req.body.filename.split('.')[0]
req.body.name = _.str.humanize(name)
-
+
path = req.body.path or ''
path = path[1...] if path and path[0] is '/'
path = path[...path.length-2] if path and path[path.length-1] is '/'
-
+
options =
mode: 'w'
filename: req.body.filename
@@ -158,6 +161,6 @@ createPostOptions = (req) ->
name: req.body.name
path: path
creator: ''+req.user._id
- options.metadata.description = req.body.description if req.body.description?
+ options.metadata.description = req.body.description if req.body.description?
options
diff --git a/server/routes/mail.coffee b/server/routes/mail.coffee
index 6527e4a26..9023209d2 100644
--- a/server/routes/mail.coffee
+++ b/server/routes/mail.coffee
@@ -37,8 +37,9 @@ getTimeFromDaysAgo = (now, daysAgo) ->
t = now - 86400 * 1000 * daysAgo - LADDER_PREGAME_INTERVAL
isRequestFromDesignatedCronHandler = (req, res) ->
- if req.ip isnt config.mail.cronHandlerPublicIP and req.ip isnt config.mail.cronHandlerPrivateIP
- console.log "RECEIVED REQUEST FROM IP #{req.ip}(headers indicate #{req.headers['x-forwarded-for']}"
+ requestIP = req.headers['x-forwarded-for']?.replace(" ","").split(",")[0]
+ if requestIP isnt config.mail.cronHandlerPublicIP and requestIP isnt config.mail.cronHandlerPrivateIP
+ console.log "RECEIVED REQUEST FROM IP #{requestIP}(headers indicate #{req.headers['x-forwarded-for']}"
console.log "UNAUTHORIZED ATTEMPT TO SEND TRANSACTIONAL LADDER EMAIL THROUGH CRON MAIL HANDLER"
res.send("You aren't authorized to perform that action. Only the specified Cron handler may perform that action.")
res.end()
@@ -53,7 +54,7 @@ handleLadderUpdate = (req, res) ->
res.send('Great work, Captain Cron! I can take it from here.')
res.end()
# TODO: somehow fetch the histograms
- emailDays = [1, 2, 4, 7, 30]
+ emailDays = [1, 2, 4, 7, 14, 30]
now = new Date()
for daysAgo in emailDays
# Get every session that was submitted in a 5-minute window after the time.
diff --git a/server/sendwithus.coffee b/server/sendwithus.coffee
index ad7a07500..bda58d896 100644
--- a/server/sendwithus.coffee
+++ b/server/sendwithus.coffee
@@ -9,6 +9,8 @@ module.exports.setupRoutes = (app) ->
debug = not config.isProduction
module.exports.api = new sendwithusAPI swuAPIKey, debug
+if config.unittest
+ module.exports.api.send = ->
module.exports.templates =
welcome_email: 'utnGaBHuSU4Hmsi7qrAypU'
ladder_update_email: 'JzaZxf39A4cKMxpPZUfWy4'
diff --git a/server/users/User.coffee b/server/users/User.coffee
index 28009e610..0d3c42a92 100644
--- a/server/users/User.coffee
+++ b/server/users/User.coffee
@@ -1,5 +1,5 @@
mongoose = require('mongoose')
-jsonschema = require('./user_schema')
+jsonschema = require('../../app/schemas/models/user')
crypto = require('crypto')
{salt, isProduction} = require('../../server_config')
mail = require '../commons/mail'
diff --git a/server/users/user_handler.coffee b/server/users/user_handler.coffee
index 168f10d91..9023e3d94 100644
--- a/server/users/user_handler.coffee
+++ b/server/users/user_handler.coffee
@@ -1,4 +1,4 @@
-schema = require './user_schema'
+schema = require '../../app/schemas/models/user'
crypto = require 'crypto'
request = require 'request'
User = require './User'
@@ -9,12 +9,16 @@ errors = require '../commons/errors'
async = require 'async'
log = require 'winston'
LevelSession = require('../levels/sessions/LevelSession')
+LevelSessionHandler = require '../levels/sessions/level_session_handler'
serverProperties = ['passwordHash', 'emailLower', 'nameLower', 'passwordReset']
privateProperties = [
'permissions', 'email', 'firstName', 'lastName', 'gender', 'facebookID',
'gplusID', 'music', 'volume', 'aceConfig'
]
+candidateProperties = [
+ 'jobProfile', 'jobProfileApproved', 'jobProfileNotes'
+]
UserHandler = class UserHandler extends Handler
modelClass: User
@@ -23,7 +27,7 @@ UserHandler = class UserHandler extends Handler
'name', 'photoURL', 'password', 'anonymous', 'wizardColor1', 'volume',
'firstName', 'lastName', 'gender', 'facebookID', 'gplusID', 'emailSubscriptions',
'testGroupNumber', 'music', 'hourOfCode', 'hourOfCodeComplete', 'preferredLanguage',
- 'wizard', 'aceConfig', 'autocastDelay', 'lastLevel'
+ 'wizard', 'aceConfig', 'autocastDelay', 'lastLevel', 'jobProfile'
]
jsonSchema: schema
@@ -32,21 +36,19 @@ UserHandler = class UserHandler extends Handler
super(arguments...)
@editableProperties.push('permissions') unless config.isProduction
+ getEditableProperties: (req, document) ->
+ props = super req, document
+ props.push 'jobProfileApproved', 'jobProfileNotes' if req.user.isAdmin()
+ props
+
formatEntity: (req, document) ->
return null unless document?
obj = document.toObject()
delete obj[prop] for prop in serverProperties
- includePrivates = req.user and (req.user?.isAdmin() or req.user?._id.equals(document._id))
+ includePrivates = req.user and (req.user.isAdmin() or req.user._id.equals(document._id))
delete obj[prop] for prop in privateProperties unless includePrivates
-
- # emailHash is used by gravatar
- hash = crypto.createHash('md5')
- if document.get('email')
- hash.update(_.trim(document.get('email')).toLowerCase())
- else
- hash.update(@_id+'')
- obj.emailHash = hash.digest('hex')
-
+ includeCandidate = includePrivates or (obj.jobProfileApproved and req.user and ('employer' in (req.user.permissions ? [])))
+ delete obj[prop] for prop in candidateProperties unless includeCandidate
return obj
waterfallFunctions: [
@@ -115,7 +117,7 @@ UserHandler = class UserHandler extends Handler
getById: (req, res, id) ->
if req.user?._id.equals(id)
- return @sendSuccess(res, @formatEntity(req, req.user))
+ return @sendSuccess(res, @formatEntity(req, req.user, 256))
super(req, res, id)
getNamesByIds: (req, res) ->
@@ -171,7 +173,9 @@ UserHandler = class UserHandler extends Handler
return @getNamesByIds(req, res) if args[1] is 'names'
return @nameToID(req, res, args[0]) if args[1] is 'nameToID'
return @getLevelSessions(req, res, args[0]) if args[1] is 'level.sessions'
+ return @getCandidates(req, res) if args[1] is 'candidates'
return @sendNotFoundError(res)
+ super(arguments...)
agreeToCLA: (req, res) ->
return @sendUnauthorizedError(res) unless req.user
@@ -191,9 +195,11 @@ UserHandler = class UserHandler extends Handler
@sendSuccess(res, {result:'success'})
avatar: (req, res, id) ->
- @modelClass.findById(id).exec (err, document) ->
+ @modelClass.findById(id).exec (err, document) =>
return @sendDatabaseError(res, err) if err
- res.redirect(document?.get('photoURL') or '/images/generic-wizard-icon.png')
+ photoURL = document?.get('photoURL')
+ photoURL ||= @buildGravatarURL document
+ res.redirect photoURL
res.end()
getLevelSessions: (req, res, userID) ->
@@ -205,8 +211,46 @@ UserHandler = class UserHandler extends Handler
projection[field] = 1 for field in req.query.project.split(',')
LevelSession.find(query).select(projection).exec (err, documents) =>
return @sendDatabaseError(res, err) if err
- documents = (@formatEntity(req, doc) for doc in documents)
+ documents = (LevelSessionHandler.formatEntity(req, doc) for doc in documents)
@sendSuccess(res, documents)
+ getCandidates: (req, res) ->
+ authorized = req.user.isAdmin() or ('employer' in req.user.get('permissions'))
+ since = (new Date((new Date()) - 2 * 30.4 * 86400 * 1000)).toISOString()
+ #query = {'jobProfileApproved': true, 'jobProfile.active': true, 'jobProfile.updated': {$gt: since}}
+ query = {'jobProfile.active': true, 'jobProfile.updated': {$gt: since}} # testing
+ query.jobProfileApproved = true unless req.user.isAdmin()
+ selection = 'jobProfile'
+ selection += ' email' if authorized
+ selection += ' jobProfileApproved' if req.user.isAdmin()
+ User.find(query).select(selection).exec (err, documents) =>
+ return @sendDatabaseError(res, err) if err
+ candidates = (@formatCandidate(authorized, doc) for doc in documents)
+ @sendSuccess(res, candidates)
+
+ formatCandidate: (authorized, document) ->
+ fields = if authorized then ['jobProfile', 'jobProfileApproved', 'photoURL', '_id'] else ['jobProfile']
+ obj = _.pick document.toObject(), fields
+ obj.photoURL ||= obj.jobProfile.photoURL if authorized
+ obj.photoURL ||= @buildGravatarURL document if authorized
+ subfields = ['country', 'city', 'lookingFor', 'jobTitle', 'skills', 'experience', 'updated']
+ if authorized
+ subfields = subfields.concat ['name']
+ obj.jobProfile = _.pick obj.jobProfile, subfields
+ obj
+
+ buildGravatarURL: (user) ->
+ emailHash = @buildEmailHash user
+ defaultAvatar = "http://codecombat.com/file/db/thang.type/52a00d55cf1818f2be00000b/portrait.png"
+ "https://www.gravatar.com/avatar/#{emailHash}?default=#{defaultAvatar}"
+
+ buildEmailHash: (user) ->
+ # emailHash is used by gravatar
+ hash = crypto.createHash('md5')
+ if user.get('email')
+ hash.update(_.trim(user.get('email')).toLowerCase())
+ else
+ hash.update(user.get('_id') + '')
+ hash.digest('hex')
module.exports = new UserHandler()
diff --git a/server/users/user_schema.coffee b/server/users/user_schema.coffee
deleted file mode 100644
index 18d526de5..000000000
--- a/server/users/user_schema.coffee
+++ /dev/null
@@ -1,61 +0,0 @@
-c = require '../commons/schemas'
-emailSubscriptions = ['announcement', 'tester', 'level_creator', 'developer', 'article_editor', 'translator', 'support', 'notification']
-
-UserSchema = c.object {},
- name: c.shortString({title: 'Display Name', default:''})
- email: c.shortString({title: 'Email', format: 'email'})
- firstName: c.shortString({title: 'First Name'})
- lastName: c.shortString({title: 'Last Name'})
- gender: {type: 'string', 'enum': ['male', 'female']}
- password: {type: 'string', maxLength: 256, minLength: 2, title:'Password'}
- passwordReset: {type: 'string'}
- photoURL: {type: 'string', format: 'url', required: false}
-
- facebookID: c.shortString({title: 'Facebook ID'})
- gplusID: c.shortString({title: 'G+ ID'})
-
- wizardColor1: c.pct({title: 'Wizard Clothes Color'})
- volume: c.pct({title: 'Volume'})
- music: {type: 'boolean', default: true}
- autocastDelay: {type: 'integer', 'default': 5000 }
- lastLevel: { type: 'string' }
-
- emailSubscriptions: c.array {uniqueItems: true, 'default': ['announcement', 'notification']}, {'enum': emailSubscriptions}
-
- # server controlled
- permissions: c.array {'default': []}, c.shortString()
- dateCreated: c.date({title: 'Date Joined'})
- anonymous: {type: 'boolean', 'default': true}
- testGroupNumber: {type: 'integer', minimum: 0, maximum: 256, exclusiveMaximum: true}
- mailChimp: {type: 'object'}
- hourOfCode: {type: 'boolean'}
- hourOfCodeComplete: {type: 'boolean'}
-
- emailLower: c.shortString()
- nameLower: c.shortString()
- passwordHash: {type: 'string', maxLength: 256}
-
- # client side
- #gravatarProfile: {} (should only ever be kept locally)
- emailHash: {type: 'string'}
-
- #Internationalization stuff
- preferredLanguage: {type: 'string', default: 'en', 'enum': c.getLanguageCodeArray()}
-
- signedCLA: c.date({title: 'Date Signed the CLA'})
- wizard: c.object {},
- colorConfig: c.object {additionalProperties: c.colorConfig()}
-
- aceConfig: c.object {},
- language: {type: 'string', 'default': 'javascript', 'enum': ['javascript', 'coffeescript']}
- keyBindings: {type: 'string', 'default': 'default', 'enum': ['default', 'vim', 'emacs']}
- invisibles: {type: 'boolean', 'default': false}
- indentGuides: {type: 'boolean', 'default': false}
- behaviors: {type: 'boolean', 'default': false}
-
- simulatedBy: {type: 'integer', minimum: 0, default: 0}
- simulatedFor: {type: 'integer', minimum: 0, default: 0}
-
-c.extendBasicProperties UserSchema, 'user'
-
-module.exports = UserSchema
diff --git a/server_setup.coffee b/server_setup.coffee
index c06482a85..d8dcd1c03 100644
--- a/server_setup.coffee
+++ b/server_setup.coffee
@@ -3,6 +3,7 @@ path = require 'path'
authentication = require 'passport'
useragent = require 'express-useragent'
fs = require 'graceful-fs'
+log = require('winston')
database = require './server/commons/database'
baseRoute = require './server/routes/base'
@@ -96,7 +97,8 @@ setupFallbackRouteToIndex = (app) ->
auth.loginUser(req, res, user, false, next)
sendMain = (req, res) ->
- fs.readFile path.join(__dirname, 'public', 'main.html'), 'utf8', (err,data) ->
+ fs.readFile path.join(__dirname, 'public', 'main.html'), 'utf8', (err, data) ->
+ log.error "Error modifying main.html: #{err}" if err
# insert the user object directly into the html so the application can have it immediately
data = data.replace('"userObjectTag"', JSON.stringify(UserHandler.formatEntity(req, req.user)))
res.send data
diff --git a/test/server/common.coffee b/test/server/common.coffee
index d88fa21e8..e68c27b72 100644
--- a/test/server/common.coffee
+++ b/test/server/common.coffee
@@ -10,6 +10,7 @@ _.mixin(_.str.exports())
GLOBAL.mongoose = require 'mongoose'
mongoose.connect('mongodb://localhost/coco_unittest')
path = require('path')
+GLOBAL.testing = true
models_path = [
'../../server/articles/Article'
@@ -19,6 +20,7 @@ models_path = [
'../../server/levels/sessions/LevelSession'
'../../server/levels/thangs/LevelThangType'
'../../server/users/User'
+ '../../server/patches/Patch'
]
for m in models_path
@@ -78,11 +80,8 @@ unittest.getUser = (email, password, done, force) ->
req = request.post(getURL('/db/user'), (err, response, body) ->
throw err if err
User.findOne({email:email}).exec((err, user) ->
- if password is '80yqxpb38j'
- user.set('permissions', [ 'admin' ])
- user.save (err) ->
- wrapUpGetUser(email, user, done)
- else
+ user.set('permissions', if password is '80yqxpb38j' then [ 'admin' ] else [])
+ user.save (err) ->
wrapUpGetUser(email, user, done)
)
)
diff --git a/test/server/functional/auth.spec.coffee b/test/server/functional/auth.spec.coffee
index 18c3c7fc8..750f4997e 100644
--- a/test/server/functional/auth.spec.coffee
+++ b/test/server/functional/auth.spec.coffee
@@ -55,7 +55,7 @@ describe '/auth/login', ->
it 'rejects wrong passwords', (done) ->
req = request.post(urlLogin, (error, response) ->
expect(response.statusCode).toBe(401)
- expect(response.body.indexOf("wrong, wrong")).toBeGreaterThan(-1)
+ expect(response.body.indexOf("wrong")).toBeGreaterThan(-1)
done()
)
form = req.form()
@@ -96,7 +96,6 @@ describe '/auth/reset', ->
it 'resets user password', (done) ->
req = request.post(urlReset, (error, response) ->
expect(response).toBeDefined()
- console.log 'status code is', response.statusCode
expect(response.statusCode).toBe(200)
expect(response.body).toBeDefined()
passwordReset = response.body
diff --git a/test/server/functional/level.spec.coffee b/test/server/functional/level.spec.coffee
index 13dc6425a..edd163d0d 100644
--- a/test/server/functional/level.spec.coffee
+++ b/test/server/functional/level.spec.coffee
@@ -6,6 +6,9 @@ describe 'Level', ->
name: "King's Peak 3"
description: 'Climb a mountain.'
permissions: simplePermissions
+ scripts: []
+ thangs: []
+ documentation: {specificArticles:[], generalArticles:[]}
urlLevel = '/db/level'
diff --git a/test/server/functional/level_component.spec.coffee b/test/server/functional/level_component.spec.coffee
index 4850d834c..9127ccefd 100644
--- a/test/server/functional/level_component.spec.coffee
+++ b/test/server/functional/level_component.spec.coffee
@@ -3,11 +3,14 @@ require '../common'
describe 'LevelComponent', ->
component =
- name:'Bashes Everything'
+ name:'BashesEverything'
description:'Makes the unit uncontrollably bash anything bashable, using the bash system.'
code: 'bash();'
- language: 'javascript'
+ language: 'coffeescript'
permissions:simplePermissions
+ propertyDocumentation: []
+ system: 'ai'
+ dependencies: []
components = {}
@@ -45,7 +48,7 @@ describe 'LevelComponent', ->
it 'have a unique name.', (done) ->
loginAdmin ->
request.post {uri:url, json:component}, (err, res, body) ->
- expect(res.statusCode).toBe(422)
+ expect(res.statusCode).toBe(409)
done()
it 'can be read by an admin.', (done) ->
diff --git a/test/server/functional/level_system.spec.coffee b/test/server/functional/level_system.spec.coffee
index 32ca61df1..229c3a39d 100644
--- a/test/server/functional/level_system.spec.coffee
+++ b/test/server/functional/level_system.spec.coffee
@@ -11,6 +11,8 @@ describe 'LevelSystem', ->
"""
language: 'coffeescript'
permissions:simplePermissions
+ dependencies: []
+ propertyDocumentation: []
systems = {}
@@ -48,7 +50,7 @@ describe 'LevelSystem', ->
it 'have a unique name.', (done) ->
loginAdmin ->
request.post {uri:url, json:system}, (err, res, body) ->
- expect(res.statusCode).toBe(422)
+ expect(res.statusCode).toBe(409)
done()
it 'can be read by an admin.', (done) ->
diff --git a/test/server/functional/patch.spec.coffee b/test/server/functional/patch.spec.coffee
new file mode 100644
index 000000000..d8694baf0
--- /dev/null
+++ b/test/server/functional/patch.spec.coffee
@@ -0,0 +1,118 @@
+require '../common'
+
+describe '/db/patch', ->
+ request = require 'request'
+ it 'clears the db first', (done) ->
+ clearModels [User, Article, Patch], (err) ->
+ throw err if err
+ done()
+
+ article = {name: 'Yo', body:'yo ma'}
+ articleURL = getURL('/db/article')
+ articles = {}
+
+ patchURL = getURL('/db/patch')
+ patches = {}
+ patch =
+ commitMessage: 'Accept this patch!'
+ delta: {name:['test']}
+ target:
+ id:null
+ collection: 'article'
+
+ it 'creates an Article to patch', (done) ->
+ loginAdmin ->
+ request.post {uri:articleURL, json:patch}, (err, res, body) ->
+ articles[0] = body
+ patch.target.id = articles[0]._id
+ done()
+
+ it "allows someone to submit a patch to something they don't control", (done) ->
+ loginJoe (joe) ->
+ request.post {uri: patchURL, json: patch}, (err, res, body) ->
+ expect(res.statusCode).toBe(200)
+ expect(body.target.original).toBeDefined()
+ expect(body.target.version.major).toBeDefined()
+ expect(body.target.version.minor).toBeDefined()
+ expect(body.status).toBe('pending')
+ expect(body.created).toBeDefined()
+ expect(body.creator).toBe(joe.id)
+ patches[0] = body
+ done()
+
+ it 'adds a patch to the target document', (done) ->
+ Article.findOne({}).exec (err, article) ->
+ expect(article.toObject().patches[0]).toBeDefined()
+ done()
+
+ it 'shows up in patch requests', (done) ->
+ patchesURL = getURL("/db/article/#{articles[0]._id}/patches")
+ request.get {uri: patchesURL}, (err, res, body) ->
+ body = JSON.parse(body)
+ expect(res.statusCode).toBe(200)
+ expect(body.length).toBe(1)
+ done()
+
+ it 'allows you to set yourself as listening', (done) ->
+ listeningURL = getURL("/db/article/#{articles[0]._id}/listen")
+ request.put {uri: listeningURL, json: {on:true}}, (err, res, body) ->
+ expect(body.listeners[0]).toBeDefined()
+ done()
+
+ it 'added the listener to the target document', (done) ->
+ Article.findOne({}).exec (err, article) ->
+ expect(article.toObject().listeners[0]).toBeDefined()
+ done()
+
+ it 'does not add duplicate listeners', (done) ->
+ listeningURL = getURL("/db/article/#{articles[0]._id}/listen")
+ request.put {uri: listeningURL, json: {on:true}}, (err, res, body) ->
+ expect(body.listeners.length).toBe(1)
+ done()
+
+ it 'allows removing yourself', (done) ->
+ listeningURL = getURL("/db/article/#{articles[0]._id}/listen")
+ request.put {uri: listeningURL, json: {on:false}}, (err, res, body) ->
+ expect(body.listeners.length).toBe(0)
+ done()
+
+ it 'allows the submitter to withdraw the pull request', (done) ->
+ statusURL = getURL("/db/patch/#{patches[0]._id}/status")
+ request.put {uri: statusURL, json: {status:'withdrawn'}}, (err, res, body) ->
+ expect(res.statusCode).toBe(200)
+ Patch.findOne({}).exec (err, article) ->
+ expect(article.get('status')).toBe 'withdrawn'
+ Article.findOne({}).exec (err, article) ->
+ expect(article.toObject().patches.length).toBe(0)
+ done()
+
+ it 'does not allow the submitter to reject or accept the pull request', (done) ->
+ statusURL = getURL("/db/patch/#{patches[0]._id}/status")
+ request.put {uri: statusURL, json: {status:'rejected'}}, (err, res, body) ->
+ expect(res.statusCode).toBe(403)
+ request.put {uri: statusURL, json: {status:'accepted'}}, (err, res, body) ->
+ expect(res.statusCode).toBe(403)
+ Patch.findOne({}).exec (err, article) ->
+ expect(article.get('status')).toBe 'withdrawn'
+ done()
+
+ it 'allows the recipient to accept or reject the pull request', (done) ->
+ statusURL = getURL("/db/patch/#{patches[0]._id}/status")
+ loginAdmin ->
+ request.put {uri: statusURL, json: {status:'rejected'}}, (err, res, body) ->
+ expect(res.statusCode).toBe(200)
+ Patch.findOne({}).exec (err, article) ->
+ expect(article.get('status')).toBe 'rejected'
+ request.put {uri: statusURL, json: {status:'accepted'}}, (err, res, body) ->
+ expect(res.statusCode).toBe(200)
+ Patch.findOne({}).exec (err, article) ->
+ expect(article.get('status')).toBe 'accepted'
+ done()
+
+ it 'does not allow the recipient to withdraw the pull request', (done) ->
+ statusURL = getURL("/db/patch/#{patches[0]._id}/status")
+ request.put {uri: statusURL, json: {status:'withdrawn'}}, (err, res, body) ->
+ expect(res.statusCode).toBe(403)
+ Patch.findOne({}).exec (err, article) ->
+ expect(article.get('status')).toBe 'accepted'
+ done()
\ No newline at end of file
diff --git a/vendor/scripts/antiscroll.js b/vendor/scripts/antiscroll.js
deleted file mode 100644
index 2de1812d6..000000000
--- a/vendor/scripts/antiscroll.js
+++ /dev/null
@@ -1,471 +0,0 @@
-(function ($) {
-
- /**
- * Augment jQuery prototype.
- */
-
- $.fn.antiscroll = function (options) {
- return this.each(function () {
- if ($(this).data('antiscroll')) {
- $(this).data('antiscroll').destroy();
- }
-
- $(this).data('antiscroll', new $.Antiscroll(this, options));
- });
- };
-
- /**
- * Expose constructor.
- */
-
- $.Antiscroll = Antiscroll;
-
- /**
- * Antiscroll pane constructor.
- *
- * @param {Element|jQuery} main pane
- * @parma {Object} options
- * @api public
- */
-
- function Antiscroll (el, opts) {
- this.el = $(el);
- this.options = opts || {};
-
- this.x = (false !== this.options.x) || this.options.forceHorizontal;
- this.y = (false !== this.options.y) || this.options.forceVertical;
- this.autoHide = false !== this.options.autoHide;
- this.padding = undefined == this.options.padding ? 2 : this.options.padding;
-
- this.inner = this.el.find('.antiscroll-inner');
- this.inner.css({
- 'width': '+=' + (this.y ? scrollbarSize() : 0)
- , 'height': '+=' + (this.x ? scrollbarSize() : 0)
- });
-
- this.refresh();
- };
-
- /**
- * refresh scrollbars
- *
- * @api public
- */
-
- Antiscroll.prototype.refresh = function() {
- var needHScroll = this.inner.get(0).scrollWidth > this.el.width() + (this.y ? scrollbarSize() : 0),
- needVScroll = this.inner.get(0).scrollHeight > this.el.height() + (this.x ? scrollbarSize() : 0);
-
- if (this.x) {
- if (!this.horizontal && needHScroll) {
- this.horizontal = new Scrollbar.Horizontal(this);
- } else if (this.horizontal && !needHScroll) {
- this.horizontal.destroy();
- this.horizontal = null;
- } else if (this.horizontal) {
- this.horizontal.update();
- }
- }
-
- if (this.y) {
- if (!this.vertical && needVScroll) {
- this.vertical = new Scrollbar.Vertical(this);
- } else if (this.vertical && !needVScroll) {
- this.vertical.destroy();
- this.vertical = null;
- } else if (this.vertical) {
- this.vertical.update();
- }
- }
- };
-
- /**
- * Cleans up.
- *
- * @return {Antiscroll} for chaining
- * @api public
- */
-
- Antiscroll.prototype.destroy = function () {
- if (this.horizontal) {
- this.horizontal.destroy();
- this.horizontal = null
- }
- if (this.vertical) {
- this.vertical.destroy();
- this.vertical = null
- }
- return this;
- };
-
- /**
- * Rebuild Antiscroll.
- *
- * @return {Antiscroll} for chaining
- * @api public
- */
-
- Antiscroll.prototype.rebuild = function () {
- this.destroy();
- this.inner.attr('style', '');
- Antiscroll.call(this, this.el, this.options);
- return this;
- };
-
- /**
- * Scrollbar constructor.
- *
- * @param {Element|jQuery} element
- * @api public
- */
-
- function Scrollbar (pane) {
- this.pane = pane;
- this.pane.el.append(this.el);
- this.innerEl = this.pane.inner.get(0);
-
- this.dragging = false;
- this.enter = false;
- this.shown = false;
-
- // hovering
- this.pane.el.mouseenter($.proxy(this, 'mouseenter'));
- this.pane.el.mouseleave($.proxy(this, 'mouseleave'));
-
- // dragging
- this.el.mousedown($.proxy(this, 'mousedown'));
-
- // scrolling
- this.innerPaneScrollListener = $.proxy(this, 'scroll');
- this.pane.inner.scroll(this.innerPaneScrollListener);
-
- // wheel -optional-
- this.innerPaneMouseWheelListener = $.proxy(this, 'mousewheel');
- this.pane.inner.bind('mousewheel', this.innerPaneMouseWheelListener);
-
- // show
- var initialDisplay = this.pane.options.initialDisplay;
-
- if (initialDisplay !== false) {
- this.show();
- if (this.pane.autoHide) {
- this.hiding = setTimeout($.proxy(this, 'hide'), parseInt(initialDisplay, 10) || 3000);
- }
- }
- };
-
- /**
- * Cleans up.
- *
- * @return {Scrollbar} for chaining
- * @api public
- */
-
- Scrollbar.prototype.destroy = function () {
- this.el.remove();
- this.pane.inner.unbind('scroll', this.innerPaneScrollListener);
- this.pane.inner.unbind('mousewheel', this.innerPaneMouseWheelListener);
- return this;
- };
-
- /**
- * Called upon mouseenter.
- *
- * @api private
- */
-
- Scrollbar.prototype.mouseenter = function () {
- this.enter = true;
- this.show();
- };
-
- /**
- * Called upon mouseleave.
- *
- * @api private
- */
-
- Scrollbar.prototype.mouseleave = function () {
- this.enter = false;
-
- if (!this.dragging) {
- if (this.pane.autoHide) {
- this.hide();
- }
- }
- };
-
- /**
- * Called upon wrap scroll.
- *
- * @api private
- */
-
- Scrollbar.prototype.scroll = function () {
- if (!this.shown) {
- this.show();
- if (!this.enter && !this.dragging) {
- if (this.pane.autoHide) {
- this.hiding = setTimeout($.proxy(this, 'hide'), 1500);
- }
- }
- }
-
- this.update();
- };
-
- /**
- * Called upon scrollbar mousedown.
- *
- * @api private
- */
-
- Scrollbar.prototype.mousedown = function (ev) {
- ev.preventDefault();
-
- this.dragging = true;
-
- this.startPageY = ev.pageY - parseInt(this.el.css('top'), 10);
- this.startPageX = ev.pageX - parseInt(this.el.css('left'), 10);
-
- // prevent crazy selections on IE
- this.el[0].ownerDocument.onselectstart = function () { return false; };
-
- var pane = this.pane,
- move = $.proxy(this, 'mousemove'),
- self = this
-
- $(this.el[0].ownerDocument)
- .mousemove(move)
- .mouseup(function () {
- self.dragging = false;
- this.onselectstart = null;
-
- $(this).unbind('mousemove', move);
-
- if (!self.enter) {
- self.hide();
- }
- });
- };
-
- /**
- * Show scrollbar.
- *
- * @api private
- */
-
- Scrollbar.prototype.show = function (duration) {
- if (!this.shown && this.update()) {
- this.el.addClass('antiscroll-scrollbar-shown');
- if (this.hiding) {
- clearTimeout(this.hiding);
- this.hiding = null;
- }
- this.shown = true;
- }
- };
-
- /**
- * Hide scrollbar.
- *
- * @api private
- */
-
- Scrollbar.prototype.hide = function () {
- if (this.pane.autoHide !== false && this.shown) {
- // check for dragging
- this.el.removeClass('antiscroll-scrollbar-shown');
- this.shown = false;
- }
- };
-
- /**
- * Horizontal scrollbar constructor
- *
- * @api private
- */
-
- Scrollbar.Horizontal = function (pane) {
- this.el = $('', pane.el);
- Scrollbar.call(this, pane);
- };
-
- /**
- * Inherits from Scrollbar.
- */
-
- inherits(Scrollbar.Horizontal, Scrollbar);
-
- /**
- * Updates size/position of scrollbar.
- *
- * @api private
- */
-
- Scrollbar.Horizontal.prototype.update = function () {
- var paneWidth = this.pane.el.width(),
- trackWidth = paneWidth - this.pane.padding * 2,
- innerEl = this.pane.inner.get(0)
-
- this.el
- .css('width', trackWidth * paneWidth / innerEl.scrollWidth)
- .css('left', trackWidth * innerEl.scrollLeft / innerEl.scrollWidth);
-
- return paneWidth < innerEl.scrollWidth;
- };
-
- /**
- * Called upon drag.
- *
- * @api private
- */
-
- Scrollbar.Horizontal.prototype.mousemove = function (ev) {
- var trackWidth = this.pane.el.width() - this.pane.padding * 2,
- pos = ev.pageX - this.startPageX,
- barWidth = this.el.width(),
- innerEl = this.pane.inner.get(0)
-
- // minimum top is 0, maximum is the track height
- var y = Math.min(Math.max(pos, 0), trackWidth - barWidth);
-
- innerEl.scrollLeft = (innerEl.scrollWidth - this.pane.el.width())
- * y / (trackWidth - barWidth);
- };
-
- /**
- * Called upon container mousewheel.
- *
- * @api private
- */
-
- Scrollbar.Horizontal.prototype.mousewheel = function (ev, delta, x, y) {
- if ((x < 0 && 0 == this.pane.inner.get(0).scrollLeft) ||
- (x > 0 && (this.innerEl.scrollLeft + Math.ceil(this.pane.el.width())
- == this.innerEl.scrollWidth))) {
- ev.preventDefault();
- return false;
- }
- };
-
- /**
- * Vertical scrollbar constructor
- *
- * @api private
- */
-
- Scrollbar.Vertical = function (pane) {
- this.el = $('', pane.el);
- Scrollbar.call(this, pane);
- };
-
- /**
- * Inherits from Scrollbar.
- */
-
- inherits(Scrollbar.Vertical, Scrollbar);
-
- /**
- * Updates size/position of scrollbar.
- *
- * @api private
- */
-
- Scrollbar.Vertical.prototype.update = function () {
- var paneHeight = this.pane.el.height(),
- trackHeight = paneHeight - this.pane.padding * 2,
- innerEl = this.innerEl;
-
- var scrollbarHeight = trackHeight * paneHeight / innerEl.scrollHeight;
- scrollbarHeight = scrollbarHeight < 20 ? 20 : scrollbarHeight;
-
- var topPos = trackHeight * innerEl.scrollTop / innerEl.scrollHeight;
-
- if((topPos + scrollbarHeight) > trackHeight) {
- var diff = (topPos + scrollbarHeight) - trackHeight;
- topPos = topPos - diff - 3;
- }
-
- this.el
- .css('height', scrollbarHeight)
- .css('top', topPos);
-
- return paneHeight < innerEl.scrollHeight;
- };
-
- /**
- * Called upon drag.
- *
- * @api private
- */
-
- Scrollbar.Vertical.prototype.mousemove = function (ev) {
- var paneHeight = this.pane.el.height(),
- trackHeight = paneHeight - this.pane.padding * 2,
- pos = ev.pageY - this.startPageY,
- barHeight = this.el.height(),
- innerEl = this.innerEl
-
- // minimum top is 0, maximum is the track height
- var y = Math.min(Math.max(pos, 0), trackHeight - barHeight);
-
- innerEl.scrollTop = (innerEl.scrollHeight - paneHeight)
- * y / (trackHeight - barHeight);
- };
-
- /**
- * Called upon container mousewheel.
- *
- * @api private
- */
-
- Scrollbar.Vertical.prototype.mousewheel = function (ev, delta, x, y) {
- if ((y > 0 && 0 == this.innerEl.scrollTop) ||
- (y < 0 && (this.innerEl.scrollTop + Math.ceil(this.pane.el.height())
- == this.innerEl.scrollHeight))) {
- ev.preventDefault();
- return false;
- }
- };
-
- /**
- * Cross-browser inheritance.
- *
- * @param {Function} constructor
- * @param {Function} constructor we inherit from
- * @api private
- */
-
- function inherits (ctorA, ctorB) {
- function f() {};
- f.prototype = ctorB.prototype;
- ctorA.prototype = new f;
- };
-
- /**
- * Scrollbar size detection.
- */
-
- var size;
-
- function scrollbarSize () {
- if (size === undefined) {
- var div = $(
- ''
- );
-
- $('body').append(div);
- var w1 = $(div).innerWidth();
- var w2 = $('div', div).innerWidth();
- $(div).remove();
-
- size = w1 - w2;
- }
-
- return size;
- };
-
-})(jQuery);
diff --git a/vendor/scripts/backbone-mediator.js b/vendor/scripts/backbone-mediator.js
deleted file mode 100644
index 8e216e5f6..000000000
--- a/vendor/scripts/backbone-mediator.js
+++ /dev/null
@@ -1,203 +0,0 @@
-/**
- * |-------------------|
- * | Backbone-Mediator |
- * |-------------------|
- * Backbone-Mediator is freely distributable under the MIT license.
- *
- * More details & documentation
- *
- * @author Nicolas Gilbert
- *
- * @requires _
- * @requires Backbone
- */
-(function(factory){
- 'use strict';
-
- if (typeof define === 'function' && define.amd) {
- define(['underscore', 'backbone'], factory);
- } else {
- factory(_, Backbone);
- }
-
-})(function (_, Backbone){
- 'use strict';
-
- /**
- * @static
- */
- var channels = {},
- Subscriber,
- /** @borrows Backbone.View#delegateEvents */
- delegateEvents = Backbone.View.prototype.delegateEvents,
- /** @borrows Backbone.View#delegateEvents */
- undelegateEvents = Backbone.View.prototype.undelegateEvents;
-
- /**
- * @class
- */
- Backbone.Mediator = {
-
- /**
- * Subscribe to a channel
- *
- * @param channel
- */
- subscribe: function(channel, subscription, context, once) {
- if (!channels[channel]) channels[channel] = [];
- channels[channel].push({fn: subscription, context: context || this, once: once});
- },
-
- /**
- * Trigger all callbacks for a channel
- *
- * @param channel
- * @params N Extra parametter to pass to handler
- */
- publish: function(channel) {
- if (!channels[channel]) return;
-
- var args = [].slice.call(arguments, 1),
- subscription;
-
- for (var i = 0; i < channels[channel].length; i++) {
- subscription = channels[channel][i];
- subscription.fn.apply(subscription.context, args);
- if (subscription.once) {
- Backbone.Mediator.unsubscribe(channel, subscription.fn, subscription.context);
- i--;
- }
- }
- },
-
- /**
- * Cancel subscription
- *
- * @param channel
- * @param fn
- * @param context
- */
-
- unsubscribe: function(channel, fn, context){
- if (!channels[channel]) return;
-
- var subscription;
- for (var i = 0; i < channels[channel].length; i++) {
- subscription = channels[channel][i];
- if (subscription.fn === fn && subscription.context === context) {
- channels[channel].splice(i, 1);
- i--;
- }
- }
- },
-
- /**
- * Subscribing to one event only
- *
- * @param channel
- * @param subscription
- * @param context
- */
- subscribeOnce: function (channel, subscription, context) {
- Backbone.Mediator.subscribe(channel, subscription, context, true);
- }
-
- };
-
- Backbone.Mediator.channels = channels;
-
- /**
- * Allow to define convention-based subscriptions
- * as an 'subscriptions' hash on a view. Subscriptions
- * can then be easily setup and cleaned.
- *
- * @class
- */
-
-
- Subscriber = {
-
- /**
- * Extend delegateEvents() to set subscriptions
- */
- delegateEvents: function(){
- delegateEvents.apply(this, arguments);
- this.setSubscriptions();
- },
-
- /**
- * Extend undelegateEvents() to unset subscriptions
- */
- undelegateEvents: function(){
- undelegateEvents.apply(this, arguments);
- this.unsetSubscriptions();
- },
-
- /** @property {Object} List of subscriptions, to be defined */
- subscriptions: {},
-
- /**
- * Subscribe to each subscription
- * @param {Object} [subscriptions] An optional hash of subscription to add
- */
-
- setSubscriptions: function(subscriptions){
- if (subscriptions) _.extend(this.subscriptions || {}, subscriptions);
- subscriptions = subscriptions || this.subscriptions;
- if (!subscriptions || _.isEmpty(subscriptions)) return;
- // Just to be sure we don't set duplicate
- this.unsetSubscriptions(subscriptions);
-
- _.each(subscriptions, function(subscription, channel){
- var once;
- if (subscription.$once) {
- subscription = subscription.$once;
- once = true;
- }
- if (_.isString(subscription)) {
- subscription = this[subscription];
- }
- Backbone.Mediator.subscribe(channel, subscription, this, once);
- }, this);
- },
-
- /**
- * Unsubscribe to each subscription
- * @param {Object} [subscriptions] An optional hash of subscription to remove
- */
- unsetSubscriptions: function(subscriptions){
- subscriptions = subscriptions || this.subscriptions;
- if (!subscriptions || _.isEmpty(subscriptions)) return;
- _.each(subscriptions, function(subscription, channel){
- if (_.isString(subscription)) {
- subscription = this[subscription];
- }
- Backbone.Mediator.unsubscribe(channel, subscription.$once || subscription, this);
- }, this);
- }
- };
-
- /**
- * @lends Backbone.View.prototype
- */
- _.extend(Backbone.View.prototype, Subscriber);
-
- /**
- * @lends Backbone.Mediator
- */
- _.extend(Backbone.Mediator, {
- /**
- * Shortcut for publish
- * @function
- */
- pub: Backbone.Mediator.publish,
- /**
- * Shortcut for subscribe
- * @function
- */
- sub: Backbone.Mediator.subscribe
- });
-
- return Backbone;
-
-});
\ No newline at end of file
diff --git a/vendor/scripts/bootstrap/affix.js b/vendor/scripts/bootstrap/affix.js
deleted file mode 100644
index 552bffa3f..000000000
--- a/vendor/scripts/bootstrap/affix.js
+++ /dev/null
@@ -1,126 +0,0 @@
-/* ========================================================================
- * Bootstrap: affix.js v3.0.3
- * http://getbootstrap.com/javascript/#affix
- * ========================================================================
- * Copyright 2013 Twitter, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ======================================================================== */
-
-
-+function ($) { "use strict";
-
- // AFFIX CLASS DEFINITION
- // ======================
-
- var Affix = function (element, options) {
- this.options = $.extend({}, Affix.DEFAULTS, options)
- this.$window = $(window)
- .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))
- .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this))
-
- this.$element = $(element)
- this.affixed =
- this.unpin = null
-
- this.checkPosition()
- }
-
- Affix.RESET = 'affix affix-top affix-bottom'
-
- Affix.DEFAULTS = {
- offset: 0
- }
-
- Affix.prototype.checkPositionWithEventLoop = function () {
- setTimeout($.proxy(this.checkPosition, this), 1)
- }
-
- Affix.prototype.checkPosition = function () {
- if (!this.$element.is(':visible')) return
-
- var scrollHeight = $(document).height()
- var scrollTop = this.$window.scrollTop()
- var position = this.$element.offset()
- var offset = this.options.offset
- var offsetTop = offset.top
- var offsetBottom = offset.bottom
-
- if (typeof offset != 'object') offsetBottom = offsetTop = offset
- if (typeof offsetTop == 'function') offsetTop = offset.top()
- if (typeof offsetBottom == 'function') offsetBottom = offset.bottom()
-
- var affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ? false :
- offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ? 'bottom' :
- offsetTop != null && (scrollTop <= offsetTop) ? 'top' : false
-
- if (this.affixed === affix) return
- if (this.unpin) this.$element.css('top', '')
-
- this.affixed = affix
- this.unpin = affix == 'bottom' ? position.top - scrollTop : null
-
- this.$element.removeClass(Affix.RESET).addClass('affix' + (affix ? '-' + affix : ''))
-
- if (affix == 'bottom') {
- this.$element.offset({ top: document.body.offsetHeight - offsetBottom - this.$element.height() })
- }
- }
-
-
- // AFFIX PLUGIN DEFINITION
- // =======================
-
- var old = $.fn.affix
-
- $.fn.affix = function (option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.affix')
- var options = typeof option == 'object' && option
-
- if (!data) $this.data('bs.affix', (data = new Affix(this, options)))
- if (typeof option == 'string') data[option]()
- })
- }
-
- $.fn.affix.Constructor = Affix
-
-
- // AFFIX NO CONFLICT
- // =================
-
- $.fn.affix.noConflict = function () {
- $.fn.affix = old
- return this
- }
-
-
- // AFFIX DATA-API
- // ==============
-
- $(window).on('load', function () {
- $('[data-spy="affix"]').each(function () {
- var $spy = $(this)
- var data = $spy.data()
-
- data.offset = data.offset || {}
-
- if (data.offsetBottom) data.offset.bottom = data.offsetBottom
- if (data.offsetTop) data.offset.top = data.offsetTop
-
- $spy.affix(data)
- })
- })
-
-}(jQuery);
diff --git a/vendor/scripts/bootstrap/alert.js b/vendor/scripts/bootstrap/alert.js
deleted file mode 100644
index 695ad74d0..000000000
--- a/vendor/scripts/bootstrap/alert.js
+++ /dev/null
@@ -1,98 +0,0 @@
-/* ========================================================================
- * Bootstrap: alert.js v3.0.3
- * http://getbootstrap.com/javascript/#alerts
- * ========================================================================
- * Copyright 2013 Twitter, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ======================================================================== */
-
-
-+function ($) { "use strict";
-
- // ALERT CLASS DEFINITION
- // ======================
-
- var dismiss = '[data-dismiss="alert"]'
- var Alert = function (el) {
- $(el).on('click', dismiss, this.close)
- }
-
- Alert.prototype.close = function (e) {
- var $this = $(this)
- var selector = $this.attr('data-target')
-
- if (!selector) {
- selector = $this.attr('href')
- selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
- }
-
- var $parent = $(selector)
-
- if (e) e.preventDefault()
-
- if (!$parent.length) {
- $parent = $this.hasClass('alert') ? $this : $this.parent()
- }
-
- $parent.trigger(e = $.Event('close.bs.alert'))
-
- if (e.isDefaultPrevented()) return
-
- $parent.removeClass('in')
-
- function removeElement() {
- $parent.trigger('closed.bs.alert').remove()
- }
-
- $.support.transition && $parent.hasClass('fade') ?
- $parent
- .one($.support.transition.end, removeElement)
- .emulateTransitionEnd(150) :
- removeElement()
- }
-
-
- // ALERT PLUGIN DEFINITION
- // =======================
-
- var old = $.fn.alert
-
- $.fn.alert = function (option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.alert')
-
- if (!data) $this.data('bs.alert', (data = new Alert(this)))
- if (typeof option == 'string') data[option].call($this)
- })
- }
-
- $.fn.alert.Constructor = Alert
-
-
- // ALERT NO CONFLICT
- // =================
-
- $.fn.alert.noConflict = function () {
- $.fn.alert = old
- return this
- }
-
-
- // ALERT DATA-API
- // ==============
-
- $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close)
-
-}(jQuery);
diff --git a/vendor/scripts/bootstrap/bootstrap.js b/vendor/scripts/bootstrap/bootstrap.js
deleted file mode 100644
index fee1bda9d..000000000
--- a/vendor/scripts/bootstrap/bootstrap.js
+++ /dev/null
@@ -1,12 +0,0 @@
-//= require affix
-//= require alert
-//= require button
-//= require carousel
-//= require collapse
-//= require dropdown
-//= require tab
-//= require transition
-//= require scrollspy
-//= require modal
-//= require tooltip
-//= require popover
diff --git a/vendor/scripts/bootstrap/button.js b/vendor/scripts/bootstrap/button.js
deleted file mode 100644
index c9fdde5e4..000000000
--- a/vendor/scripts/bootstrap/button.js
+++ /dev/null
@@ -1,115 +0,0 @@
-/* ========================================================================
- * Bootstrap: button.js v3.0.3
- * http://getbootstrap.com/javascript/#buttons
- * ========================================================================
- * Copyright 2013 Twitter, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ======================================================================== */
-
-
-+function ($) { "use strict";
-
- // BUTTON PUBLIC CLASS DEFINITION
- // ==============================
-
- var Button = function (element, options) {
- this.$element = $(element)
- this.options = $.extend({}, Button.DEFAULTS, options)
- }
-
- Button.DEFAULTS = {
- loadingText: 'loading...'
- }
-
- Button.prototype.setState = function (state) {
- var d = 'disabled'
- var $el = this.$element
- var val = $el.is('input') ? 'val' : 'html'
- var data = $el.data()
-
- state = state + 'Text'
-
- if (!data.resetText) $el.data('resetText', $el[val]())
-
- $el[val](data[state] || this.options[state])
-
- // push to event loop to allow forms to submit
- setTimeout(function () {
- state == 'loadingText' ?
- $el.addClass(d).attr(d, d) :
- $el.removeClass(d).removeAttr(d);
- }, 0)
- }
-
- Button.prototype.toggle = function () {
- var $parent = this.$element.closest('[data-toggle="buttons"]')
- var changed = true
-
- if ($parent.length) {
- var $input = this.$element.find('input')
- if ($input.prop('type') === 'radio') {
- // see if clicking on current one
- if ($input.prop('checked') && this.$element.hasClass('active'))
- changed = false
- else
- $parent.find('.active').removeClass('active')
- }
- if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change')
- }
-
- if (changed) this.$element.toggleClass('active')
- }
-
-
- // BUTTON PLUGIN DEFINITION
- // ========================
-
- var old = $.fn.button
-
- $.fn.button = function (option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.button')
- var options = typeof option == 'object' && option
-
- if (!data) $this.data('bs.button', (data = new Button(this, options)))
-
- if (option == 'toggle') data.toggle()
- else if (option) data.setState(option)
- })
- }
-
- $.fn.button.Constructor = Button
-
-
- // BUTTON NO CONFLICT
- // ==================
-
- $.fn.button.noConflict = function () {
- $.fn.button = old
- return this
- }
-
-
- // BUTTON DATA-API
- // ===============
-
- $(document).on('click.bs.button.data-api', '[data-toggle^=button]', function (e) {
- var $btn = $(e.target)
- if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
- $btn.button('toggle')
- e.preventDefault()
- })
-
-}(jQuery);
diff --git a/vendor/scripts/bootstrap/carousel.js b/vendor/scripts/bootstrap/carousel.js
deleted file mode 100644
index 6391a36df..000000000
--- a/vendor/scripts/bootstrap/carousel.js
+++ /dev/null
@@ -1,217 +0,0 @@
-/* ========================================================================
- * Bootstrap: carousel.js v3.0.3
- * http://getbootstrap.com/javascript/#carousel
- * ========================================================================
- * Copyright 2013 Twitter, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ======================================================================== */
-
-
-+function ($) { "use strict";
-
- // CAROUSEL CLASS DEFINITION
- // =========================
-
- var Carousel = function (element, options) {
- this.$element = $(element)
- this.$indicators = this.$element.find('.carousel-indicators')
- this.options = options
- this.paused =
- this.sliding =
- this.interval =
- this.$active =
- this.$items = null
-
- this.options.pause == 'hover' && this.$element
- .on('mouseenter', $.proxy(this.pause, this))
- .on('mouseleave', $.proxy(this.cycle, this))
- }
-
- Carousel.DEFAULTS = {
- interval: 5000
- , pause: 'hover'
- , wrap: true
- }
-
- Carousel.prototype.cycle = function (e) {
- e || (this.paused = false)
-
- this.interval && clearInterval(this.interval)
-
- this.options.interval
- && !this.paused
- && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
-
- return this
- }
-
- Carousel.prototype.getActiveIndex = function () {
- this.$active = this.$element.find('.item.active')
- this.$items = this.$active.parent().children()
-
- return this.$items.index(this.$active)
- }
-
- Carousel.prototype.to = function (pos) {
- var that = this
- var activeIndex = this.getActiveIndex()
-
- if (pos > (this.$items.length - 1) || pos < 0) return
-
- if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) })
- if (activeIndex == pos) return this.pause().cycle()
-
- return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos]))
- }
-
- Carousel.prototype.pause = function (e) {
- e || (this.paused = true)
-
- if (this.$element.find('.next, .prev').length && $.support.transition.end) {
- this.$element.trigger($.support.transition.end)
- this.cycle(true)
- }
-
- this.interval = clearInterval(this.interval)
-
- return this
- }
-
- Carousel.prototype.next = function () {
- if (this.sliding) return
- return this.slide('next')
- }
-
- Carousel.prototype.prev = function () {
- if (this.sliding) return
- return this.slide('prev')
- }
-
- Carousel.prototype.slide = function (type, next) {
- var $active = this.$element.find('.item.active')
- var $next = next || $active[type]()
- var isCycling = this.interval
- var direction = type == 'next' ? 'left' : 'right'
- var fallback = type == 'next' ? 'first' : 'last'
- var that = this
-
- if (!$next.length) {
- if (!this.options.wrap) return
- $next = this.$element.find('.item')[fallback]()
- }
-
- this.sliding = true
-
- isCycling && this.pause()
-
- var e = $.Event('slide.bs.carousel', { relatedTarget: $next[0], direction: direction })
-
- if ($next.hasClass('active')) return
-
- if (this.$indicators.length) {
- this.$indicators.find('.active').removeClass('active')
- this.$element.one('slid.bs.carousel', function () {
- var $nextIndicator = $(that.$indicators.children()[that.getActiveIndex()])
- $nextIndicator && $nextIndicator.addClass('active')
- })
- }
-
- if ($.support.transition && this.$element.hasClass('slide')) {
- this.$element.trigger(e)
- if (e.isDefaultPrevented()) return
- $next.addClass(type)
- $next[0].offsetWidth // force reflow
- $active.addClass(direction)
- $next.addClass(direction)
- $active
- .one($.support.transition.end, function () {
- $next.removeClass([type, direction].join(' ')).addClass('active')
- $active.removeClass(['active', direction].join(' '))
- that.sliding = false
- setTimeout(function () { that.$element.trigger('slid.bs.carousel') }, 0)
- })
- .emulateTransitionEnd(600)
- } else {
- this.$element.trigger(e)
- if (e.isDefaultPrevented()) return
- $active.removeClass('active')
- $next.addClass('active')
- this.sliding = false
- this.$element.trigger('slid.bs.carousel')
- }
-
- isCycling && this.cycle()
-
- return this
- }
-
-
- // CAROUSEL PLUGIN DEFINITION
- // ==========================
-
- var old = $.fn.carousel
-
- $.fn.carousel = function (option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.carousel')
- var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option)
- var action = typeof option == 'string' ? option : options.slide
-
- if (!data) $this.data('bs.carousel', (data = new Carousel(this, options)))
- if (typeof option == 'number') data.to(option)
- else if (action) data[action]()
- else if (options.interval) data.pause().cycle()
- })
- }
-
- $.fn.carousel.Constructor = Carousel
-
-
- // CAROUSEL NO CONFLICT
- // ====================
-
- $.fn.carousel.noConflict = function () {
- $.fn.carousel = old
- return this
- }
-
-
- // CAROUSEL DATA-API
- // =================
-
- $(document).on('click.bs.carousel.data-api', '[data-slide], [data-slide-to]', function (e) {
- var $this = $(this), href
- var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
- var options = $.extend({}, $target.data(), $this.data())
- var slideIndex = $this.attr('data-slide-to')
- if (slideIndex) options.interval = false
-
- $target.carousel(options)
-
- if (slideIndex = $this.attr('data-slide-to')) {
- $target.data('bs.carousel').to(slideIndex)
- }
-
- e.preventDefault()
- })
-
- $(window).on('load', function () {
- $('[data-ride="carousel"]').each(function () {
- var $carousel = $(this)
- $carousel.carousel($carousel.data())
- })
- })
-
-}(jQuery);
diff --git a/vendor/scripts/bootstrap/collapse.js b/vendor/scripts/bootstrap/collapse.js
deleted file mode 100644
index 1a079938e..000000000
--- a/vendor/scripts/bootstrap/collapse.js
+++ /dev/null
@@ -1,179 +0,0 @@
-/* ========================================================================
- * Bootstrap: collapse.js v3.0.3
- * http://getbootstrap.com/javascript/#collapse
- * ========================================================================
- * Copyright 2013 Twitter, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ======================================================================== */
-
-
-+function ($) { "use strict";
-
- // COLLAPSE PUBLIC CLASS DEFINITION
- // ================================
-
- var Collapse = function (element, options) {
- this.$element = $(element)
- this.options = $.extend({}, Collapse.DEFAULTS, options)
- this.transitioning = null
-
- if (this.options.parent) this.$parent = $(this.options.parent)
- if (this.options.toggle) this.toggle()
- }
-
- Collapse.DEFAULTS = {
- toggle: true
- }
-
- Collapse.prototype.dimension = function () {
- var hasWidth = this.$element.hasClass('width')
- return hasWidth ? 'width' : 'height'
- }
-
- Collapse.prototype.show = function () {
- if (this.transitioning || this.$element.hasClass('in')) return
-
- var startEvent = $.Event('show.bs.collapse')
- this.$element.trigger(startEvent)
- if (startEvent.isDefaultPrevented()) return
-
- var actives = this.$parent && this.$parent.find('> .panel > .in')
-
- if (actives && actives.length) {
- var hasData = actives.data('bs.collapse')
- if (hasData && hasData.transitioning) return
- actives.collapse('hide')
- hasData || actives.data('bs.collapse', null)
- }
-
- var dimension = this.dimension()
-
- this.$element
- .removeClass('collapse')
- .addClass('collapsing')
- [dimension](0)
-
- this.transitioning = 1
-
- var complete = function () {
- this.$element
- .removeClass('collapsing')
- .addClass('in')
- [dimension]('auto')
- this.transitioning = 0
- this.$element.trigger('shown.bs.collapse')
- }
-
- if (!$.support.transition) return complete.call(this)
-
- var scrollSize = $.camelCase(['scroll', dimension].join('-'))
-
- this.$element
- .one($.support.transition.end, $.proxy(complete, this))
- .emulateTransitionEnd(350)
- [dimension](this.$element[0][scrollSize])
- }
-
- Collapse.prototype.hide = function () {
- if (this.transitioning || !this.$element.hasClass('in')) return
-
- var startEvent = $.Event('hide.bs.collapse')
- this.$element.trigger(startEvent)
- if (startEvent.isDefaultPrevented()) return
-
- var dimension = this.dimension()
-
- this.$element
- [dimension](this.$element[dimension]())
- [0].offsetHeight
-
- this.$element
- .addClass('collapsing')
- .removeClass('collapse')
- .removeClass('in')
-
- this.transitioning = 1
-
- var complete = function () {
- this.transitioning = 0
- this.$element
- .trigger('hidden.bs.collapse')
- .removeClass('collapsing')
- .addClass('collapse')
- }
-
- if (!$.support.transition) return complete.call(this)
-
- this.$element
- [dimension](0)
- .one($.support.transition.end, $.proxy(complete, this))
- .emulateTransitionEnd(350)
- }
-
- Collapse.prototype.toggle = function () {
- this[this.$element.hasClass('in') ? 'hide' : 'show']()
- }
-
-
- // COLLAPSE PLUGIN DEFINITION
- // ==========================
-
- var old = $.fn.collapse
-
- $.fn.collapse = function (option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.collapse')
- var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)
-
- if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))
- if (typeof option == 'string') data[option]()
- })
- }
-
- $.fn.collapse.Constructor = Collapse
-
-
- // COLLAPSE NO CONFLICT
- // ====================
-
- $.fn.collapse.noConflict = function () {
- $.fn.collapse = old
- return this
- }
-
-
- // COLLAPSE DATA-API
- // =================
-
- $(document).on('click.bs.collapse.data-api', '[data-toggle=collapse]', function (e) {
- var $this = $(this), href
- var target = $this.attr('data-target')
- || e.preventDefault()
- || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
- var $target = $(target)
- var data = $target.data('bs.collapse')
- var option = data ? 'toggle' : $this.data()
- var parent = $this.attr('data-parent')
- var $parent = parent && $(parent)
-
- if (!data || !data.transitioning) {
- if ($parent) $parent.find('[data-toggle=collapse][data-parent="' + parent + '"]').not($this).addClass('collapsed')
- $this[$target.hasClass('in') ? 'addClass' : 'removeClass']('collapsed')
- }
-
- $target.collapse(option)
- })
-
-}(jQuery);
diff --git a/vendor/scripts/bootstrap/dropdown.js b/vendor/scripts/bootstrap/dropdown.js
deleted file mode 100644
index 13352ef7c..000000000
--- a/vendor/scripts/bootstrap/dropdown.js
+++ /dev/null
@@ -1,154 +0,0 @@
-/* ========================================================================
- * Bootstrap: dropdown.js v3.0.3
- * http://getbootstrap.com/javascript/#dropdowns
- * ========================================================================
- * Copyright 2013 Twitter, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ======================================================================== */
-
-
-+function ($) { "use strict";
-
- // DROPDOWN CLASS DEFINITION
- // =========================
-
- var backdrop = '.dropdown-backdrop'
- var toggle = '[data-toggle=dropdown]'
- var Dropdown = function (element) {
- $(element).on('click.bs.dropdown', this.toggle)
- }
-
- Dropdown.prototype.toggle = function (e) {
- var $this = $(this)
-
- if ($this.is('.disabled, :disabled')) return
-
- var $parent = getParent($this)
- var isActive = $parent.hasClass('open')
-
- clearMenus()
-
- if (!isActive) {
- if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
- // if mobile we use a backdrop because click events don't delegate
- $('').insertAfter($(this)).on('click', clearMenus)
- }
-
- $parent.trigger(e = $.Event('show.bs.dropdown'))
-
- if (e.isDefaultPrevented()) return
-
- $parent
- .toggleClass('open')
- .trigger('shown.bs.dropdown')
-
- $this.focus()
- }
-
- return false
- }
-
- Dropdown.prototype.keydown = function (e) {
- if (!/(38|40|27)/.test(e.keyCode)) return
-
- var $this = $(this)
-
- e.preventDefault()
- e.stopPropagation()
-
- if ($this.is('.disabled, :disabled')) return
-
- var $parent = getParent($this)
- var isActive = $parent.hasClass('open')
-
- if (!isActive || (isActive && e.keyCode == 27)) {
- if (e.which == 27) $parent.find(toggle).focus()
- return $this.click()
- }
-
- var $items = $('[role=menu] li:not(.divider):visible a', $parent)
-
- if (!$items.length) return
-
- var index = $items.index($items.filter(':focus'))
-
- if (e.keyCode == 38 && index > 0) index-- // up
- if (e.keyCode == 40 && index < $items.length - 1) index++ // down
- if (!~index) index=0
-
- $items.eq(index).focus()
- }
-
- function clearMenus() {
- $(backdrop).remove()
- $(toggle).each(function (e) {
- var $parent = getParent($(this))
- if (!$parent.hasClass('open')) return
- $parent.trigger(e = $.Event('hide.bs.dropdown'))
- if (e.isDefaultPrevented()) return
- $parent.removeClass('open').trigger('hidden.bs.dropdown')
- })
- }
-
- function getParent($this) {
- var selector = $this.attr('data-target')
-
- if (!selector) {
- selector = $this.attr('href')
- selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
- }
-
- var $parent = selector && $(selector)
-
- return $parent && $parent.length ? $parent : $this.parent()
- }
-
-
- // DROPDOWN PLUGIN DEFINITION
- // ==========================
-
- var old = $.fn.dropdown
-
- $.fn.dropdown = function (option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.dropdown')
-
- if (!data) $this.data('bs.dropdown', (data = new Dropdown(this)))
- if (typeof option == 'string') data[option].call($this)
- })
- }
-
- $.fn.dropdown.Constructor = Dropdown
-
-
- // DROPDOWN NO CONFLICT
- // ====================
-
- $.fn.dropdown.noConflict = function () {
- $.fn.dropdown = old
- return this
- }
-
-
- // APPLY TO STANDARD DROPDOWN ELEMENTS
- // ===================================
-
- $(document)
- .on('click.bs.dropdown.data-api', clearMenus)
- .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
- .on('click.bs.dropdown.data-api' , toggle, Dropdown.prototype.toggle)
- .on('keydown.bs.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown)
-
-}(jQuery);
diff --git a/vendor/scripts/bootstrap/modal.js b/vendor/scripts/bootstrap/modal.js
deleted file mode 100644
index 3ead5ee88..000000000
--- a/vendor/scripts/bootstrap/modal.js
+++ /dev/null
@@ -1,246 +0,0 @@
-/* ========================================================================
- * Bootstrap: modal.js v3.0.3
- * http://getbootstrap.com/javascript/#modals
- * ========================================================================
- * Copyright 2013 Twitter, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ======================================================================== */
-
-
-+function ($) { "use strict";
-
- // MODAL CLASS DEFINITION
- // ======================
-
- var Modal = function (element, options) {
- this.options = options
- this.$element = $(element)
- this.$backdrop =
- this.isShown = null
-
- if (this.options.remote) this.$element.load(this.options.remote)
- }
-
- Modal.DEFAULTS = {
- backdrop: true
- , keyboard: true
- , show: true
- }
-
- Modal.prototype.toggle = function (_relatedTarget) {
- return this[!this.isShown ? 'show' : 'hide'](_relatedTarget)
- }
-
- Modal.prototype.show = function (_relatedTarget) {
- var that = this
- var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
-
- this.$element.trigger(e)
-
- if (this.isShown || e.isDefaultPrevented()) return
-
- this.isShown = true
-
- this.escape()
-
- this.$element.on('click.dismiss.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
-
- this.backdrop(function () {
- var transition = $.support.transition && that.$element.hasClass('fade')
-
- if (!that.$element.parent().length) {
- that.$element.appendTo(document.body) // don't move modals dom position
- }
-
- that.$element.show()
-
- if (transition) {
- that.$element[0].offsetWidth // force reflow
- }
-
- that.$element
- .addClass('in')
- .attr('aria-hidden', false)
-
- that.enforceFocus()
-
- var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
-
- transition ?
- that.$element.find('.modal-dialog') // wait for modal to slide in
- .one($.support.transition.end, function () {
- that.$element.focus().trigger(e)
- })
- .emulateTransitionEnd(300) :
- that.$element.focus().trigger(e)
- })
- }
-
- Modal.prototype.hide = function (e) {
- if (e) e.preventDefault()
-
- e = $.Event('hide.bs.modal')
-
- this.$element.trigger(e)
-
- if (!this.isShown || e.isDefaultPrevented()) return
-
- this.isShown = false
-
- this.escape()
-
- $(document).off('focusin.bs.modal')
-
- this.$element
- .removeClass('in')
- .attr('aria-hidden', true)
- .off('click.dismiss.modal')
-
- $.support.transition && this.$element.hasClass('fade') ?
- this.$element
- .one($.support.transition.end, $.proxy(this.hideModal, this))
- .emulateTransitionEnd(300) :
- this.hideModal()
- }
-
- Modal.prototype.enforceFocus = function () {
- $(document)
- .off('focusin.bs.modal') // guard against infinite focus loop
- .on('focusin.bs.modal', $.proxy(function (e) {
- if (this.$element[0] !== e.target && !this.$element.has(e.target).length) {
- this.$element.focus()
- }
- }, this))
- }
-
- Modal.prototype.escape = function () {
- if (this.isShown && this.options.keyboard) {
- this.$element.on('keyup.dismiss.bs.modal', $.proxy(function (e) {
- e.which == 27 && this.hide()
- }, this))
- } else if (!this.isShown) {
- this.$element.off('keyup.dismiss.bs.modal')
- }
- }
-
- Modal.prototype.hideModal = function () {
- var that = this
- this.$element.hide()
- this.backdrop(function () {
- that.removeBackdrop()
- that.$element.trigger('hidden.bs.modal')
- })
- }
-
- Modal.prototype.removeBackdrop = function () {
- this.$backdrop && this.$backdrop.remove()
- this.$backdrop = null
- }
-
- Modal.prototype.backdrop = function (callback) {
- var that = this
- var animate = this.$element.hasClass('fade') ? 'fade' : ''
-
- if (this.isShown && this.options.backdrop) {
- var doAnimate = $.support.transition && animate
-
- this.$backdrop = $('')
- .appendTo(document.body)
-
- this.$element.on('click.dismiss.modal', $.proxy(function (e) {
- if (e.target !== e.currentTarget) return
- this.options.backdrop == 'static'
- ? this.$element[0].focus.call(this.$element[0])
- : this.hide.call(this)
- }, this))
-
- if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
-
- this.$backdrop.addClass('in')
-
- if (!callback) return
-
- doAnimate ?
- this.$backdrop
- .one($.support.transition.end, callback)
- .emulateTransitionEnd(150) :
- callback()
-
- } else if (!this.isShown && this.$backdrop) {
- this.$backdrop.removeClass('in')
-
- $.support.transition && this.$element.hasClass('fade')?
- this.$backdrop
- .one($.support.transition.end, callback)
- .emulateTransitionEnd(150) :
- callback()
-
- } else if (callback) {
- callback()
- }
- }
-
-
- // MODAL PLUGIN DEFINITION
- // =======================
-
- var old = $.fn.modal
-
- $.fn.modal = function (option, _relatedTarget) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.modal')
- var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
-
- if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
- if (typeof option == 'string') data[option](_relatedTarget)
- else if (options.show) data.show(_relatedTarget)
- })
- }
-
- $.fn.modal.Constructor = Modal
-
-
- // MODAL NO CONFLICT
- // =================
-
- $.fn.modal.noConflict = function () {
- $.fn.modal = old
- return this
- }
-
-
- // MODAL DATA-API
- // ==============
-
- $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
- var $this = $(this)
- var href = $this.attr('href')
- var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) //strip for ie7
- var option = $target.data('modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
-
- e.preventDefault()
-
- $target
- .modal(option, this)
- .one('hide', function () {
- $this.is(':visible') && $this.focus()
- })
- })
-
- $(document)
- .on('show.bs.modal', '.modal', function () { $(document.body).addClass('modal-open') })
- .on('hidden.bs.modal', '.modal', function () { $(document.body).removeClass('modal-open') })
-
-}(jQuery);
diff --git a/vendor/scripts/bootstrap/popover.js b/vendor/scripts/bootstrap/popover.js
deleted file mode 100644
index 996962aa2..000000000
--- a/vendor/scripts/bootstrap/popover.js
+++ /dev/null
@@ -1,117 +0,0 @@
-/* ========================================================================
- * Bootstrap: popover.js v3.0.3
- * http://getbootstrap.com/javascript/#popovers
- * ========================================================================
- * Copyright 2013 Twitter, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ======================================================================== */
-
-
-+function ($) { "use strict";
-
- // POPOVER PUBLIC CLASS DEFINITION
- // ===============================
-
- var Popover = function (element, options) {
- this.init('popover', element, options)
- }
-
- if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')
-
- Popover.DEFAULTS = $.extend({} , $.fn.tooltip.Constructor.DEFAULTS, {
- placement: 'right'
- , trigger: 'click'
- , content: ''
- , template: ''
- })
-
-
- // NOTE: POPOVER EXTENDS tooltip.js
- // ================================
-
- Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype)
-
- Popover.prototype.constructor = Popover
-
- Popover.prototype.getDefaults = function () {
- return Popover.DEFAULTS
- }
-
- Popover.prototype.setContent = function () {
- var $tip = this.tip()
- var title = this.getTitle()
- var content = this.getContent()
-
- $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
- $tip.find('.popover-content')[this.options.html ? 'html' : 'text'](content)
-
- $tip.removeClass('fade top bottom left right in')
-
- // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do
- // this manually by checking the contents.
- if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide()
- }
-
- Popover.prototype.hasContent = function () {
- return this.getTitle() || this.getContent()
- }
-
- Popover.prototype.getContent = function () {
- var $e = this.$element
- var o = this.options
-
- return $e.attr('data-content')
- || (typeof o.content == 'function' ?
- o.content.call($e[0]) :
- o.content)
- }
-
- Popover.prototype.arrow = function () {
- return this.$arrow = this.$arrow || this.tip().find('.arrow')
- }
-
- Popover.prototype.tip = function () {
- if (!this.$tip) this.$tip = $(this.options.template)
- return this.$tip
- }
-
-
- // POPOVER PLUGIN DEFINITION
- // =========================
-
- var old = $.fn.popover
-
- $.fn.popover = function (option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.popover')
- var options = typeof option == 'object' && option
-
- if (!data) $this.data('bs.popover', (data = new Popover(this, options)))
- if (typeof option == 'string') data[option]()
- })
- }
-
- $.fn.popover.Constructor = Popover
-
-
- // POPOVER NO CONFLICT
- // ===================
-
- $.fn.popover.noConflict = function () {
- $.fn.popover = old
- return this
- }
-
-}(jQuery);
diff --git a/vendor/scripts/bootstrap/scrollspy.js b/vendor/scripts/bootstrap/scrollspy.js
deleted file mode 100644
index 2efe14fdd..000000000
--- a/vendor/scripts/bootstrap/scrollspy.js
+++ /dev/null
@@ -1,158 +0,0 @@
-/* ========================================================================
- * Bootstrap: scrollspy.js v3.0.3
- * http://getbootstrap.com/javascript/#scrollspy
- * ========================================================================
- * Copyright 2013 Twitter, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ======================================================================== */
-
-
-+function ($) { "use strict";
-
- // SCROLLSPY CLASS DEFINITION
- // ==========================
-
- function ScrollSpy(element, options) {
- var href
- var process = $.proxy(this.process, this)
-
- this.$element = $(element).is('body') ? $(window) : $(element)
- this.$body = $('body')
- this.$scrollElement = this.$element.on('scroll.bs.scroll-spy.data-api', process)
- this.options = $.extend({}, ScrollSpy.DEFAULTS, options)
- this.selector = (this.options.target
- || ((href = $(element).attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
- || '') + ' .nav li > a'
- this.offsets = $([])
- this.targets = $([])
- this.activeTarget = null
-
- this.refresh()
- this.process()
- }
-
- ScrollSpy.DEFAULTS = {
- offset: 10
- }
-
- ScrollSpy.prototype.refresh = function () {
- var offsetMethod = this.$element[0] == window ? 'offset' : 'position'
-
- this.offsets = $([])
- this.targets = $([])
-
- var self = this
- var $targets = this.$body
- .find(this.selector)
- .map(function () {
- var $el = $(this)
- var href = $el.data('target') || $el.attr('href')
- var $href = /^#\w/.test(href) && $(href)
-
- return ($href
- && $href.length
- && [[ $href[offsetMethod]().top + (!$.isWindow(self.$scrollElement.get(0)) && self.$scrollElement.scrollTop()), href ]]) || null
- })
- .sort(function (a, b) { return a[0] - b[0] })
- .each(function () {
- self.offsets.push(this[0])
- self.targets.push(this[1])
- })
- }
-
- ScrollSpy.prototype.process = function () {
- var scrollTop = this.$scrollElement.scrollTop() + this.options.offset
- var scrollHeight = this.$scrollElement[0].scrollHeight || this.$body[0].scrollHeight
- var maxScroll = scrollHeight - this.$scrollElement.height()
- var offsets = this.offsets
- var targets = this.targets
- var activeTarget = this.activeTarget
- var i
-
- if (scrollTop >= maxScroll) {
- return activeTarget != (i = targets.last()[0]) && this.activate(i)
- }
-
- for (i = offsets.length; i--;) {
- activeTarget != targets[i]
- && scrollTop >= offsets[i]
- && (!offsets[i + 1] || scrollTop <= offsets[i + 1])
- && this.activate( targets[i] )
- }
- }
-
- ScrollSpy.prototype.activate = function (target) {
- this.activeTarget = target
-
- $(this.selector)
- .parents('.active')
- .removeClass('active')
-
- var selector = this.selector
- + '[data-target="' + target + '"],'
- + this.selector + '[href="' + target + '"]'
-
- var active = $(selector)
- .parents('li')
- .addClass('active')
-
- if (active.parent('.dropdown-menu').length) {
- active = active
- .closest('li.dropdown')
- .addClass('active')
- }
-
- active.trigger('activate.bs.scrollspy')
- }
-
-
- // SCROLLSPY PLUGIN DEFINITION
- // ===========================
-
- var old = $.fn.scrollspy
-
- $.fn.scrollspy = function (option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.scrollspy')
- var options = typeof option == 'object' && option
-
- if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options)))
- if (typeof option == 'string') data[option]()
- })
- }
-
- $.fn.scrollspy.Constructor = ScrollSpy
-
-
- // SCROLLSPY NO CONFLICT
- // =====================
-
- $.fn.scrollspy.noConflict = function () {
- $.fn.scrollspy = old
- return this
- }
-
-
- // SCROLLSPY DATA-API
- // ==================
-
- $(window).on('load', function () {
- $('[data-spy="scroll"]').each(function () {
- var $spy = $(this)
- $spy.scrollspy($spy.data())
- })
- })
-
-}(jQuery);
diff --git a/vendor/scripts/bootstrap/tab.js b/vendor/scripts/bootstrap/tab.js
deleted file mode 100644
index 6b0f5f672..000000000
--- a/vendor/scripts/bootstrap/tab.js
+++ /dev/null
@@ -1,135 +0,0 @@
-/* ========================================================================
- * Bootstrap: tab.js v3.0.3
- * http://getbootstrap.com/javascript/#tabs
- * ========================================================================
- * Copyright 2013 Twitter, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ======================================================================== */
-
-
-+function ($) { "use strict";
-
- // TAB CLASS DEFINITION
- // ====================
-
- var Tab = function (element) {
- this.element = $(element)
- }
-
- Tab.prototype.show = function () {
- var $this = this.element
- var $ul = $this.closest('ul:not(.dropdown-menu)')
- var selector = $this.data('target')
-
- if (!selector) {
- selector = $this.attr('href')
- selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
- }
-
- if ($this.parent('li').hasClass('active')) return
-
- var previous = $ul.find('.active:last a')[0]
- var e = $.Event('show.bs.tab', {
- relatedTarget: previous
- })
-
- $this.trigger(e)
-
- if (e.isDefaultPrevented()) return
-
- var $target = $(selector)
-
- this.activate($this.parent('li'), $ul)
- this.activate($target, $target.parent(), function () {
- $this.trigger({
- type: 'shown.bs.tab'
- , relatedTarget: previous
- })
- })
- }
-
- Tab.prototype.activate = function (element, container, callback) {
- var $active = container.find('> .active')
- var transition = callback
- && $.support.transition
- && $active.hasClass('fade')
-
- function next() {
- $active
- .removeClass('active')
- .find('> .dropdown-menu > .active')
- .removeClass('active')
-
- element.addClass('active')
-
- if (transition) {
- element[0].offsetWidth // reflow for transition
- element.addClass('in')
- } else {
- element.removeClass('fade')
- }
-
- if (element.parent('.dropdown-menu')) {
- element.closest('li.dropdown').addClass('active')
- }
-
- callback && callback()
- }
-
- transition ?
- $active
- .one($.support.transition.end, next)
- .emulateTransitionEnd(150) :
- next()
-
- $active.removeClass('in')
- }
-
-
- // TAB PLUGIN DEFINITION
- // =====================
-
- var old = $.fn.tab
-
- $.fn.tab = function ( option ) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.tab')
-
- if (!data) $this.data('bs.tab', (data = new Tab(this)))
- if (typeof option == 'string') data[option]()
- })
- }
-
- $.fn.tab.Constructor = Tab
-
-
- // TAB NO CONFLICT
- // ===============
-
- $.fn.tab.noConflict = function () {
- $.fn.tab = old
- return this
- }
-
-
- // TAB DATA-API
- // ============
-
- $(document).on('click.bs.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
- e.preventDefault()
- $(this).tab('show')
- })
-
-}(jQuery);
diff --git a/vendor/scripts/bootstrap/tooltip.js b/vendor/scripts/bootstrap/tooltip.js
deleted file mode 100644
index 4c848f0e2..000000000
--- a/vendor/scripts/bootstrap/tooltip.js
+++ /dev/null
@@ -1,386 +0,0 @@
-/* ========================================================================
- * Bootstrap: tooltip.js v3.0.3
- * http://getbootstrap.com/javascript/#tooltip
- * Inspired by the original jQuery.tipsy by Jason Frame
- * ========================================================================
- * Copyright 2013 Twitter, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ======================================================================== */
-
-
-+function ($) { "use strict";
-
- // TOOLTIP PUBLIC CLASS DEFINITION
- // ===============================
-
- var Tooltip = function (element, options) {
- this.type =
- this.options =
- this.enabled =
- this.timeout =
- this.hoverState =
- this.$element = null
-
- this.init('tooltip', element, options)
- }
-
- Tooltip.DEFAULTS = {
- animation: true
- , placement: 'top'
- , selector: false
- , template: ''
- , trigger: 'hover focus'
- , title: ''
- , delay: 0
- , html: false
- , container: false
- }
-
- Tooltip.prototype.init = function (type, element, options) {
- this.enabled = true
- this.type = type
- this.$element = $(element)
- this.options = this.getOptions(options)
-
- var triggers = this.options.trigger.split(' ')
-
- for (var i = triggers.length; i--;) {
- var trigger = triggers[i]
-
- if (trigger == 'click') {
- this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
- } else if (trigger != 'manual') {
- var eventIn = trigger == 'hover' ? 'mouseenter' : 'focus'
- var eventOut = trigger == 'hover' ? 'mouseleave' : 'blur'
-
- this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
- this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
- }
- }
-
- this.options.selector ?
- (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
- this.fixTitle()
- }
-
- Tooltip.prototype.getDefaults = function () {
- return Tooltip.DEFAULTS
- }
-
- Tooltip.prototype.getOptions = function (options) {
- options = $.extend({}, this.getDefaults(), this.$element.data(), options)
-
- if (options.delay && typeof options.delay == 'number') {
- options.delay = {
- show: options.delay
- , hide: options.delay
- }
- }
-
- return options
- }
-
- Tooltip.prototype.getDelegateOptions = function () {
- var options = {}
- var defaults = this.getDefaults()
-
- this._options && $.each(this._options, function (key, value) {
- if (defaults[key] != value) options[key] = value
- })
-
- return options
- }
-
- Tooltip.prototype.enter = function (obj) {
- var self = obj instanceof this.constructor ?
- obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)
-
- clearTimeout(self.timeout)
-
- self.hoverState = 'in'
-
- if (!self.options.delay || !self.options.delay.show) return self.show()
-
- self.timeout = setTimeout(function () {
- if (self.hoverState == 'in') self.show()
- }, self.options.delay.show)
- }
-
- Tooltip.prototype.leave = function (obj) {
- var self = obj instanceof this.constructor ?
- obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)
-
- clearTimeout(self.timeout)
-
- self.hoverState = 'out'
-
- if (!self.options.delay || !self.options.delay.hide) return self.hide()
-
- self.timeout = setTimeout(function () {
- if (self.hoverState == 'out') self.hide()
- }, self.options.delay.hide)
- }
-
- Tooltip.prototype.show = function () {
- var e = $.Event('show.bs.'+ this.type)
-
- if (this.hasContent() && this.enabled) {
- this.$element.trigger(e)
-
- if (e.isDefaultPrevented()) return
-
- var $tip = this.tip()
-
- this.setContent()
-
- if (this.options.animation) $tip.addClass('fade')
-
- var placement = typeof this.options.placement == 'function' ?
- this.options.placement.call(this, $tip[0], this.$element[0]) :
- this.options.placement
-
- var autoToken = /\s?auto?\s?/i
- var autoPlace = autoToken.test(placement)
- if (autoPlace) placement = placement.replace(autoToken, '') || 'top'
-
- $tip
- .detach()
- .css({ top: 0, left: 0, display: 'block' })
- .addClass(placement)
-
- this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
-
- var pos = this.getPosition()
- var actualWidth = $tip[0].offsetWidth
- var actualHeight = $tip[0].offsetHeight
-
- if (autoPlace) {
- var $parent = this.$element.parent()
-
- var orgPlacement = placement
- var docScroll = document.documentElement.scrollTop || document.body.scrollTop
- var parentWidth = this.options.container == 'body' ? window.innerWidth : $parent.outerWidth()
- var parentHeight = this.options.container == 'body' ? window.innerHeight : $parent.outerHeight()
- var parentLeft = this.options.container == 'body' ? 0 : $parent.offset().left
-
- placement = placement == 'bottom' && pos.top + pos.height + actualHeight - docScroll > parentHeight ? 'top' :
- placement == 'top' && pos.top - docScroll - actualHeight < 0 ? 'bottom' :
- placement == 'right' && pos.right + actualWidth > parentWidth ? 'left' :
- placement == 'left' && pos.left - actualWidth < parentLeft ? 'right' :
- placement
-
- $tip
- .removeClass(orgPlacement)
- .addClass(placement)
- }
-
- var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
-
- this.applyPlacement(calculatedOffset, placement)
- this.$element.trigger('shown.bs.' + this.type)
- }
- }
-
- Tooltip.prototype.applyPlacement = function(offset, placement) {
- var replace
- var $tip = this.tip()
- var width = $tip[0].offsetWidth
- var height = $tip[0].offsetHeight
-
- // manually read margins because getBoundingClientRect includes difference
- var marginTop = parseInt($tip.css('margin-top'), 10)
- var marginLeft = parseInt($tip.css('margin-left'), 10)
-
- // we must check for NaN for ie 8/9
- if (isNaN(marginTop)) marginTop = 0
- if (isNaN(marginLeft)) marginLeft = 0
-
- offset.top = offset.top + marginTop
- offset.left = offset.left + marginLeft
-
- $tip
- .offset(offset)
- .addClass('in')
-
- // check to see if placing tip in new offset caused the tip to resize itself
- var actualWidth = $tip[0].offsetWidth
- var actualHeight = $tip[0].offsetHeight
-
- if (placement == 'top' && actualHeight != height) {
- replace = true
- offset.top = offset.top + height - actualHeight
- }
-
- if (/bottom|top/.test(placement)) {
- var delta = 0
-
- if (offset.left < 0) {
- delta = offset.left * -2
- offset.left = 0
-
- $tip.offset(offset)
-
- actualWidth = $tip[0].offsetWidth
- actualHeight = $tip[0].offsetHeight
- }
-
- this.replaceArrow(delta - width + actualWidth, actualWidth, 'left')
- } else {
- this.replaceArrow(actualHeight - height, actualHeight, 'top')
- }
-
- if (replace) $tip.offset(offset)
- }
-
- Tooltip.prototype.replaceArrow = function(delta, dimension, position) {
- this.arrow().css(position, delta ? (50 * (1 - delta / dimension) + "%") : '')
- }
-
- Tooltip.prototype.setContent = function () {
- var $tip = this.tip()
- var title = this.getTitle()
-
- $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
- $tip.removeClass('fade in top bottom left right')
- }
-
- Tooltip.prototype.hide = function () {
- var that = this
- var $tip = this.tip()
- var e = $.Event('hide.bs.' + this.type)
-
- function complete() {
- if (that.hoverState != 'in') $tip.detach()
- }
-
- this.$element.trigger(e)
-
- if (e.isDefaultPrevented()) return
-
- $tip.removeClass('in')
-
- $.support.transition && this.$tip.hasClass('fade') ?
- $tip
- .one($.support.transition.end, complete)
- .emulateTransitionEnd(150) :
- complete()
-
- this.$element.trigger('hidden.bs.' + this.type)
-
- return this
- }
-
- Tooltip.prototype.fixTitle = function () {
- var $e = this.$element
- if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
- $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
- }
- }
-
- Tooltip.prototype.hasContent = function () {
- return this.getTitle()
- }
-
- Tooltip.prototype.getPosition = function () {
- var el = this.$element[0]
- return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : {
- width: el.offsetWidth
- , height: el.offsetHeight
- }, this.$element.offset())
- }
-
- Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
- return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } :
- placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } :
- placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :
- /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width }
- }
-
- Tooltip.prototype.getTitle = function () {
- var title
- var $e = this.$element
- var o = this.options
-
- title = $e.attr('data-original-title')
- || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
-
- return title
- }
-
- Tooltip.prototype.tip = function () {
- return this.$tip = this.$tip || $(this.options.template)
- }
-
- Tooltip.prototype.arrow = function () {
- return this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')
- }
-
- Tooltip.prototype.validate = function () {
- if (!this.$element[0].parentNode) {
- this.hide()
- this.$element = null
- this.options = null
- }
- }
-
- Tooltip.prototype.enable = function () {
- this.enabled = true
- }
-
- Tooltip.prototype.disable = function () {
- this.enabled = false
- }
-
- Tooltip.prototype.toggleEnabled = function () {
- this.enabled = !this.enabled
- }
-
- Tooltip.prototype.toggle = function (e) {
- var self = e ? $(e.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type) : this
- self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
- }
-
- Tooltip.prototype.destroy = function () {
- this.hide().$element.off('.' + this.type).removeData('bs.' + this.type)
- }
-
-
- // TOOLTIP PLUGIN DEFINITION
- // =========================
-
- var old = $.fn.tooltip
-
- $.fn.tooltip = function (option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.tooltip')
- var options = typeof option == 'object' && option
-
- if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
- if (typeof option == 'string') data[option]()
- })
- }
-
- $.fn.tooltip.Constructor = Tooltip
-
-
- // TOOLTIP NO CONFLICT
- // ===================
-
- $.fn.tooltip.noConflict = function () {
- $.fn.tooltip = old
- return this
- }
-
-}(jQuery);
diff --git a/vendor/scripts/bootstrap/transition.js b/vendor/scripts/bootstrap/transition.js
deleted file mode 100644
index 773dbe693..000000000
--- a/vendor/scripts/bootstrap/transition.js
+++ /dev/null
@@ -1,56 +0,0 @@
-/* ========================================================================
- * Bootstrap: transition.js v3.0.3
- * http://getbootstrap.com/javascript/#transitions
- * ========================================================================
- * Copyright 2013 Twitter, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ======================================================================== */
-
-
-+function ($) { "use strict";
-
- // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
- // ============================================================
-
- function transitionEnd() {
- var el = document.createElement('bootstrap')
-
- var transEndEventNames = {
- 'WebkitTransition' : 'webkitTransitionEnd'
- , 'MozTransition' : 'transitionend'
- , 'OTransition' : 'oTransitionEnd otransitionend'
- , 'transition' : 'transitionend'
- }
-
- for (var name in transEndEventNames) {
- if (el.style[name] !== undefined) {
- return { end: transEndEventNames[name] }
- }
- }
- }
-
- // http://blog.alexmaccaw.com/css-transitions
- $.fn.emulateTransitionEnd = function (duration) {
- var called = false, $el = this
- $(this).one($.support.transition.end, function () { called = true })
- var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
- setTimeout(callback, duration)
- return this
- }
-
- $(function () {
- $.support.transition = transitionEnd()
- })
-
-}(jQuery);
diff --git a/vendor/scripts/difflib.js b/vendor/scripts/difflib.js
new file mode 100644
index 000000000..191fe4563
--- /dev/null
+++ b/vendor/scripts/difflib.js
@@ -0,0 +1,413 @@
+/***
+This is part of jsdifflib v1.0.
+
+Copyright (c) 2007, Snowtide Informatics Systems, Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * Neither the name of the Snowtide Informatics Systems nor the names of its
+ contributors may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+***/
+/* Author: Chas Emerick */
+var __whitespace = {" ":true, "\t":true, "\n":true, "\f":true, "\r":true};
+
+var difflib = {
+ defaultJunkFunction: function (c) {
+ return __whitespace.hasOwnProperty(c);
+ },
+
+ stripLinebreaks: function (str) { return str.replace(/^[\n\r]*|[\n\r]*$/g, ""); },
+
+ stringAsLines: function (str) {
+ var lfpos = str.indexOf("\n");
+ var crpos = str.indexOf("\r");
+ var linebreak = ((lfpos > -1 && crpos > -1) || crpos < 0) ? "\n" : "\r";
+
+ var lines = str.split(linebreak);
+ for (var i = 0; i < lines.length; i++) {
+ lines[i] = difflib.stripLinebreaks(lines[i]);
+ }
+
+ return lines;
+ },
+
+ // iteration-based reduce implementation
+ __reduce: function (func, list, initial) {
+ if (initial != null) {
+ var value = initial;
+ var idx = 0;
+ } else if (list) {
+ var value = list[0];
+ var idx = 1;
+ } else {
+ return null;
+ }
+
+ for (; idx < list.length; idx++) {
+ value = func(value, list[idx]);
+ }
+
+ return value;
+ },
+
+ // comparison function for sorting lists of numeric tuples
+ __ntuplecomp: function (a, b) {
+ var mlen = Math.max(a.length, b.length);
+ for (var i = 0; i < mlen; i++) {
+ if (a[i] < b[i]) return -1;
+ if (a[i] > b[i]) return 1;
+ }
+
+ return a.length == b.length ? 0 : (a.length < b.length ? -1 : 1);
+ },
+
+ __calculate_ratio: function (matches, length) {
+ return length ? 2.0 * matches / length : 1.0;
+ },
+
+ // returns a function that returns true if a key passed to the returned function
+ // is in the dict (js object) provided to this function; replaces being able to
+ // carry around dict.has_key in python...
+ __isindict: function (dict) {
+ return function (key) { return dict.hasOwnProperty(key); };
+ },
+
+ // replacement for python's dict.get function -- need easy default values
+ __dictget: function (dict, key, defaultValue) {
+ return dict.hasOwnProperty(key) ? dict[key] : defaultValue;
+ },
+
+ SequenceMatcher: function (a, b, isjunk) {
+ this.set_seqs = function (a, b) {
+ this.set_seq1(a);
+ this.set_seq2(b);
+ }
+
+ this.set_seq1 = function (a) {
+ if (a == this.a) return;
+ this.a = a;
+ this.matching_blocks = this.opcodes = null;
+ }
+
+ this.set_seq2 = function (b) {
+ if (b == this.b) return;
+ this.b = b;
+ this.matching_blocks = this.opcodes = this.fullbcount = null;
+ this.__chain_b();
+ }
+
+ this.__chain_b = function () {
+ var b = this.b;
+ var n = b.length;
+ var b2j = this.b2j = {};
+ var populardict = {};
+ for (var i = 0; i < b.length; i++) {
+ var elt = b[i];
+ if (b2j.hasOwnProperty(elt)) {
+ var indices = b2j[elt];
+ if (n >= 200 && indices.length * 100 > n) {
+ populardict[elt] = 1;
+ delete b2j[elt];
+ } else {
+ indices.push(i);
+ }
+ } else {
+ b2j[elt] = [i];
+ }
+ }
+
+ for (var elt in populardict) {
+ if (populardict.hasOwnProperty(elt)) {
+ delete b2j[elt];
+ }
+ }
+
+ var isjunk = this.isjunk;
+ var junkdict = {};
+ if (isjunk) {
+ for (var elt in populardict) {
+ if (populardict.hasOwnProperty(elt) && isjunk(elt)) {
+ junkdict[elt] = 1;
+ delete populardict[elt];
+ }
+ }
+ for (var elt in b2j) {
+ if (b2j.hasOwnProperty(elt) && isjunk(elt)) {
+ junkdict[elt] = 1;
+ delete b2j[elt];
+ }
+ }
+ }
+
+ this.isbjunk = difflib.__isindict(junkdict);
+ this.isbpopular = difflib.__isindict(populardict);
+ }
+
+ this.find_longest_match = function (alo, ahi, blo, bhi) {
+ var a = this.a;
+ var b = this.b;
+ var b2j = this.b2j;
+ var isbjunk = this.isbjunk;
+ var besti = alo;
+ var bestj = blo;
+ var bestsize = 0;
+ var j = null;
+ var k;
+
+ var j2len = {};
+ var nothing = [];
+ for (var i = alo; i < ahi; i++) {
+ var newj2len = {};
+ var jdict = difflib.__dictget(b2j, a[i], nothing);
+ for (var jkey in jdict) {
+ if (jdict.hasOwnProperty(jkey)) {
+ j = jdict[jkey];
+ if (j < blo) continue;
+ if (j >= bhi) break;
+ newj2len[j] = k = difflib.__dictget(j2len, j - 1, 0) + 1;
+ if (k > bestsize) {
+ besti = i - k + 1;
+ bestj = j - k + 1;
+ bestsize = k;
+ }
+ }
+ }
+ j2len = newj2len;
+ }
+
+ while (besti > alo && bestj > blo && !isbjunk(b[bestj - 1]) && a[besti - 1] == b[bestj - 1]) {
+ besti--;
+ bestj--;
+ bestsize++;
+ }
+
+ while (besti + bestsize < ahi && bestj + bestsize < bhi &&
+ !isbjunk(b[bestj + bestsize]) &&
+ a[besti + bestsize] == b[bestj + bestsize]) {
+ bestsize++;
+ }
+
+ while (besti > alo && bestj > blo && isbjunk(b[bestj - 1]) && a[besti - 1] == b[bestj - 1]) {
+ besti--;
+ bestj--;
+ bestsize++;
+ }
+
+ while (besti + bestsize < ahi && bestj + bestsize < bhi && isbjunk(b[bestj + bestsize]) &&
+ a[besti + bestsize] == b[bestj + bestsize]) {
+ bestsize++;
+ }
+
+ return [besti, bestj, bestsize];
+ }
+
+ this.get_matching_blocks = function () {
+ if (this.matching_blocks != null) return this.matching_blocks;
+ var la = this.a.length;
+ var lb = this.b.length;
+
+ var queue = [[0, la, 0, lb]];
+ var matching_blocks = [];
+ var alo, ahi, blo, bhi, qi, i, j, k, x;
+ while (queue.length) {
+ qi = queue.pop();
+ alo = qi[0];
+ ahi = qi[1];
+ blo = qi[2];
+ bhi = qi[3];
+ x = this.find_longest_match(alo, ahi, blo, bhi);
+ i = x[0];
+ j = x[1];
+ k = x[2];
+
+ if (k) {
+ matching_blocks.push(x);
+ if (alo < i && blo < j)
+ queue.push([alo, i, blo, j]);
+ if (i+k < ahi && j+k < bhi)
+ queue.push([i + k, ahi, j + k, bhi]);
+ }
+ }
+
+ matching_blocks.sort(difflib.__ntuplecomp);
+
+ var i1 = 0, j1 = 0, k1 = 0, block = 0;
+ var i2, j2, k2;
+ var non_adjacent = [];
+ for (var idx in matching_blocks) {
+ if (matching_blocks.hasOwnProperty(idx)) {
+ block = matching_blocks[idx];
+ i2 = block[0];
+ j2 = block[1];
+ k2 = block[2];
+ if (i1 + k1 == i2 && j1 + k1 == j2) {
+ k1 += k2;
+ } else {
+ if (k1) non_adjacent.push([i1, j1, k1]);
+ i1 = i2;
+ j1 = j2;
+ k1 = k2;
+ }
+ }
+ }
+
+ if (k1) non_adjacent.push([i1, j1, k1]);
+
+ non_adjacent.push([la, lb, 0]);
+ this.matching_blocks = non_adjacent;
+ return this.matching_blocks;
+ }
+
+ this.get_opcodes = function () {
+ if (this.opcodes != null) return this.opcodes;
+ var i = 0;
+ var j = 0;
+ var answer = [];
+ this.opcodes = answer;
+ var block, ai, bj, size, tag;
+ var blocks = this.get_matching_blocks();
+ for (var idx in blocks) {
+ if (blocks.hasOwnProperty(idx)) {
+ block = blocks[idx];
+ ai = block[0];
+ bj = block[1];
+ size = block[2];
+ tag = '';
+ if (i < ai && j < bj) {
+ tag = 'replace';
+ } else if (i < ai) {
+ tag = 'delete';
+ } else if (j < bj) {
+ tag = 'insert';
+ }
+ if (tag) answer.push([tag, i, ai, j, bj]);
+ i = ai + size;
+ j = bj + size;
+
+ if (size) answer.push(['equal', ai, i, bj, j]);
+ }
+ }
+
+ return answer;
+ }
+
+ // this is a generator function in the python lib, which of course is not supported in javascript
+ // the reimplementation builds up the grouped opcodes into a list in their entirety and returns that.
+ this.get_grouped_opcodes = function (n) {
+ if (!n) n = 3;
+ var codes = this.get_opcodes();
+ if (!codes) codes = [["equal", 0, 1, 0, 1]];
+ var code, tag, i1, i2, j1, j2;
+ if (codes[0][0] == 'equal') {
+ code = codes[0];
+ tag = code[0];
+ i1 = code[1];
+ i2 = code[2];
+ j1 = code[3];
+ j2 = code[4];
+ codes[0] = [tag, Math.max(i1, i2 - n), i2, Math.max(j1, j2 - n), j2];
+ }
+ if (codes[codes.length - 1][0] == 'equal') {
+ code = codes[codes.length - 1];
+ tag = code[0];
+ i1 = code[1];
+ i2 = code[2];
+ j1 = code[3];
+ j2 = code[4];
+ codes[codes.length - 1] = [tag, i1, Math.min(i2, i1 + n), j1, Math.min(j2, j1 + n)];
+ }
+
+ var nn = n + n;
+ var group = [];
+ var groups = [];
+ for (var idx in codes) {
+ if (codes.hasOwnProperty(idx)) {
+ code = codes[idx];
+ tag = code[0];
+ i1 = code[1];
+ i2 = code[2];
+ j1 = code[3];
+ j2 = code[4];
+ if (tag == 'equal' && i2 - i1 > nn) {
+ group.push([tag, i1, Math.min(i2, i1 + n), j1, Math.min(j2, j1 + n)]);
+ groups.push(group);
+ group = [];
+ i1 = Math.max(i1, i2-n);
+ j1 = Math.max(j1, j2-n);
+ }
+
+ group.push([tag, i1, i2, j1, j2]);
+ }
+ }
+
+ if (group && !(group.length == 1 && group[0][0] == 'equal')) groups.push(group)
+
+ return groups;
+ }
+
+ this.ratio = function () {
+ matches = difflib.__reduce(
+ function (sum, triple) { return sum + triple[triple.length - 1]; },
+ this.get_matching_blocks(), 0);
+ return difflib.__calculate_ratio(matches, this.a.length + this.b.length);
+ }
+
+ this.quick_ratio = function () {
+ var fullbcount, elt;
+ if (this.fullbcount == null) {
+ this.fullbcount = fullbcount = {};
+ for (var i = 0; i < this.b.length; i++) {
+ elt = this.b[i];
+ fullbcount[elt] = difflib.__dictget(fullbcount, elt, 0) + 1;
+ }
+ }
+ fullbcount = this.fullbcount;
+
+ var avail = {};
+ var availhas = difflib.__isindict(avail);
+ var matches = numb = 0;
+ for (var i = 0; i < this.a.length; i++) {
+ elt = this.a[i];
+ if (availhas(elt)) {
+ numb = avail[elt];
+ } else {
+ numb = difflib.__dictget(fullbcount, elt, 0);
+ }
+ avail[elt] = numb - 1;
+ if (numb > 0) matches++;
+ }
+
+ return difflib.__calculate_ratio(matches, this.a.length + this.b.length);
+ }
+
+ this.real_quick_ratio = function () {
+ var la = this.a.length;
+ var lb = this.b.length;
+ return _calculate_ratio(Math.min(la, lb), la + lb);
+ }
+
+ this.isjunk = isjunk ? isjunk : difflib.defaultJunkFunction;
+ this.a = this.b = null;
+ this.set_seqs(a, b);
+ }
+};
+
diff --git a/vendor/scripts/diffview.js b/vendor/scripts/diffview.js
new file mode 100644
index 000000000..372753d84
--- /dev/null
+++ b/vendor/scripts/diffview.js
@@ -0,0 +1,198 @@
+/*
+This is part of jsdifflib v1.0.
+
+Copyright 2007 - 2011 Chas Emerick . All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are
+permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this list of
+ conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ of conditions and the following disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY Chas Emerick ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Chas Emerick OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The views and conclusions contained in the software and documentation are those of the
+authors and should not be interpreted as representing official policies, either expressed
+or implied, of Chas Emerick.
+*/
+diffview = {
+ /**
+ * Builds and returns a visual diff view. The single parameter, `params', should contain
+ * the following values:
+ *
+ * - baseTextLines: the array of strings that was used as the base text input to SequenceMatcher
+ * - newTextLines: the array of strings that was used as the new text input to SequenceMatcher
+ * - opcodes: the array of arrays returned by SequenceMatcher.get_opcodes()
+ * - baseTextName: the title to be displayed above the base text listing in the diff view; defaults
+ * to "Base Text"
+ * - newTextName: the title to be displayed above the new text listing in the diff view; defaults
+ * to "New Text"
+ * - contextSize: the number of lines of context to show around differences; by default, all lines
+ * are shown
+ * - viewType: if 0, a side-by-side diff view is generated (default); if 1, an inline diff view is
+ * generated
+ */
+ buildView: function (params) {
+ var baseTextLines = params.baseTextLines;
+ var newTextLines = params.newTextLines;
+ var opcodes = params.opcodes;
+ var baseTextName = params.baseTextName ? params.baseTextName : "Base Text";
+ var newTextName = params.newTextName ? params.newTextName : "New Text";
+ var contextSize = params.contextSize;
+ var inline = (params.viewType == 0 || params.viewType == 1) ? params.viewType : 0;
+
+ if (baseTextLines == null)
+ throw "Cannot build diff view; baseTextLines is not defined.";
+ if (newTextLines == null)
+ throw "Cannot build diff view; newTextLines is not defined.";
+ if (!opcodes)
+ throw "Canno build diff view; opcodes is not defined.";
+
+ function celt (name, clazz) {
+ var e = document.createElement(name);
+ e.className = clazz;
+ return e;
+ }
+
+ function telt (name, text) {
+ var e = document.createElement(name);
+ e.appendChild(document.createTextNode(text));
+ return e;
+ }
+
+ function ctelt (name, clazz, text) {
+ var e = document.createElement(name);
+ e.className = clazz;
+ e.appendChild(document.createTextNode(text));
+ return e;
+ }
+
+ var tdata = document.createElement("thead");
+ var node = document.createElement("tr");
+ tdata.appendChild(node);
+ if (inline) {
+ node.appendChild(document.createElement("th"));
+ node.appendChild(document.createElement("th"));
+ node.appendChild(ctelt("th", "texttitle", baseTextName + " vs. " + newTextName));
+ } else {
+ node.appendChild(document.createElement("th"));
+ node.appendChild(ctelt("th", "texttitle", baseTextName));
+ node.appendChild(document.createElement("th"));
+ node.appendChild(ctelt("th", "texttitle", newTextName));
+ }
+ tdata = [tdata];
+
+ var rows = [];
+ var node2;
+
+ /**
+ * Adds two cells to the given row; if the given row corresponds to a real
+ * line number (based on the line index tidx and the endpoint of the
+ * range in question tend), then the cells will contain the line number
+ * and the line of text from textLines at position tidx (with the class of
+ * the second cell set to the name of the change represented), and tidx + 1 will
+ * be returned. Otherwise, tidx is returned, and two empty cells are added
+ * to the given row.
+ */
+ function addCells (row, tidx, tend, textLines, change) {
+ if (tidx < tend) {
+ row.appendChild(telt("th", (tidx + 1).toString()));
+ row.appendChild(ctelt("td", change, textLines[tidx].replace(/\t/g, "\u00a0\u00a0\u00a0\u00a0")));
+ return tidx + 1;
+ } else {
+ row.appendChild(document.createElement("th"));
+ row.appendChild(celt("td", "empty"));
+ return tidx;
+ }
+ }
+
+ function addCellsInline (row, tidx, tidx2, textLines, change) {
+ row.appendChild(telt("th", tidx == null ? "" : (tidx + 1).toString()));
+ row.appendChild(telt("th", tidx2 == null ? "" : (tidx2 + 1).toString()));
+ row.appendChild(ctelt("td", change, textLines[tidx != null ? tidx : tidx2].replace(/\t/g, "\u00a0\u00a0\u00a0\u00a0")));
+ }
+
+ for (var idx = 0; idx < opcodes.length; idx++) {
+ code = opcodes[idx];
+ change = code[0];
+ var b = code[1];
+ var be = code[2];
+ var n = code[3];
+ var ne = code[4];
+ var rowcnt = Math.max(be - b, ne - n);
+ var toprows = [];
+ var botrows = [];
+ for (var i = 0; i < rowcnt; i++) {
+ // jump ahead if we've alredy provided leading context or if this is the first range
+ if (contextSize && opcodes.length > 1 && ((idx > 0 && i == contextSize) || (idx == 0 && i == 0)) && change=="equal") {
+ var jump = rowcnt - ((idx == 0 ? 1 : 2) * contextSize);
+ if (jump > 1) {
+ toprows.push(node = document.createElement("tr"));
+
+ b += jump;
+ n += jump;
+ i += jump - 1;
+ node.appendChild(telt("th", "..."));
+ if (!inline) node.appendChild(ctelt("td", "skip", ""));
+ node.appendChild(telt("th", "..."));
+ node.appendChild(ctelt("td", "skip", ""));
+
+ // skip last lines if they're all equal
+ if (idx + 1 == opcodes.length) {
+ break;
+ } else {
+ continue;
+ }
+ }
+ }
+
+ toprows.push(node = document.createElement("tr"));
+ if (inline) {
+ if (change == "insert") {
+ addCellsInline(node, null, n++, newTextLines, change);
+ } else if (change == "replace") {
+ botrows.push(node2 = document.createElement("tr"));
+ if (b < be) addCellsInline(node, b++, null, baseTextLines, "delete");
+ if (n < ne) addCellsInline(node2, null, n++, newTextLines, "insert");
+ } else if (change == "delete") {
+ addCellsInline(node, b++, null, baseTextLines, change);
+ } else {
+ // equal
+ addCellsInline(node, b++, n++, baseTextLines, change);
+ }
+ } else {
+ b = addCells(node, b, be, baseTextLines, change);
+ n = addCells(node, n, ne, newTextLines, change);
+ }
+ }
+
+ for (var i = 0; i < toprows.length; i++) rows.push(toprows[i]);
+ for (var i = 0; i < botrows.length; i++) rows.push(botrows[i]);
+ }
+
+ rows.push(node = ctelt("th", "author", "diff view generated by "));
+ node.setAttribute("colspan", inline ? 3 : 4);
+ node.appendChild(node2 = telt("a", "jsdifflib"));
+ node2.setAttribute("href", "http://github.com/cemerick/jsdifflib");
+
+ tdata.push(node = document.createElement("tbody"));
+ for (var idx in rows) rows.hasOwnProperty(idx) && node.appendChild(rows[idx]);
+
+ node = celt("table", "diff" + (inline ? " inlinediff" : ""));
+ for (var idx in tdata) tdata.hasOwnProperty(idx) && node.appendChild(tdata[idx]);
+ return node;
+ }
+};
+
diff --git a/vendor/scripts/jquery-ui-1.10.3.custom.js b/vendor/scripts/jquery-ui-1.10.4.custom.js
similarity index 98%
rename from vendor/scripts/jquery-ui-1.10.3.custom.js
rename to vendor/scripts/jquery-ui-1.10.4.custom.js
index 991276d15..0aa0374c8 100644
--- a/vendor/scripts/jquery-ui-1.10.3.custom.js
+++ b/vendor/scripts/jquery-ui-1.10.4.custom.js
@@ -1,7 +1,7 @@
-/*! jQuery UI - v1.10.3 - 2013-09-04
+/*! jQuery UI - v1.10.4 - 2014-04-11
* http://jqueryui.com
* Includes: jquery.ui.core.js, jquery.ui.widget.js, jquery.ui.mouse.js, jquery.ui.position.js, jquery.ui.resizable.js, jquery.ui.sortable.js, jquery.ui.autocomplete.js, jquery.ui.menu.js, jquery.ui.slider.js, jquery.ui.effect.js, jquery.ui.effect-bounce.js, jquery.ui.effect-highlight.js, jquery.ui.effect-pulsate.js, jquery.ui.effect-shake.js, jquery.ui.effect-transfer.js
-* Copyright 2013 jQuery Foundation and other contributors; Licensed MIT */
+* Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */
(function( $, undefined ) {
@@ -12,7 +12,7 @@ var uuid = 0,
$.ui = $.ui || {};
$.extend( $.ui, {
- version: "1.10.3",
+ version: "1.10.4",
keyCode: {
BACKSPACE: 8,
@@ -411,7 +411,7 @@ $.widget = function( name, base, prototype ) {
// TODO: remove support for widgetEventPrefix
// always use the name + a colon as the prefix, e.g., draggable:start
// don't prefix for widgets that aren't DOM-based
- widgetEventPrefix: existingConstructor ? basePrototype.widgetEventPrefix : name
+ widgetEventPrefix: existingConstructor ? (basePrototype.widgetEventPrefix || name) : name
}, proxiedPrototype, {
constructor: constructor,
namespace: namespace,
@@ -620,12 +620,12 @@ $.Widget.prototype = {
curOption = curOption[ parts[ i ] ];
}
key = parts.pop();
- if ( value === undefined ) {
+ if ( arguments.length === 1 ) {
return curOption[ key ] === undefined ? null : curOption[ key ];
}
curOption[ key ] = value;
} else {
- if ( value === undefined ) {
+ if ( arguments.length === 1 ) {
return this.options[ key ] === undefined ? null : this.options[ key ];
}
options[ key ] = value;
@@ -832,7 +832,7 @@ $( document ).mouseup( function() {
});
$.widget("ui.mouse", {
- version: "1.10.3",
+ version: "1.10.4",
options: {
cancel: "input,textarea,button,select,option",
distance: 1,
@@ -1042,7 +1042,7 @@ $.position = {
return cachedScrollbarWidth;
}
var w1, w2,
- div = $( "" ),
+ div = $( "" ),
innerDiv = div.children()[0];
$( "body" ).append( div );
@@ -1060,8 +1060,10 @@ $.position = {
return (cachedScrollbarWidth = w1 - w2);
},
getScrollInfo: function( within ) {
- var overflowX = within.isWindow ? "" : within.element.css( "overflow-x" ),
- overflowY = within.isWindow ? "" : within.element.css( "overflow-y" ),
+ var overflowX = within.isWindow || within.isDocument ? "" :
+ within.element.css( "overflow-x" ),
+ overflowY = within.isWindow || within.isDocument ? "" :
+ within.element.css( "overflow-y" ),
hasOverflowX = overflowX === "scroll" ||
( overflowX === "auto" && within.width < within.element[0].scrollWidth ),
hasOverflowY = overflowY === "scroll" ||
@@ -1073,10 +1075,12 @@ $.position = {
},
getWithinInfo: function( element ) {
var withinElement = $( element || window ),
- isWindow = $.isWindow( withinElement[0] );
+ isWindow = $.isWindow( withinElement[0] ),
+ isDocument = !!withinElement[ 0 ] && withinElement[ 0 ].nodeType === 9;
return {
element: withinElement,
isWindow: isWindow,
+ isDocument: isDocument,
offset: withinElement.offset() || { left: 0, top: 0 },
scrollLeft: withinElement.scrollLeft(),
scrollTop: withinElement.scrollTop(),
@@ -1408,7 +1412,7 @@ $.ui.position = {
}
}
else if ( overBottom > 0 ) {
- newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + offset - offsetTop;
+ newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + offset - offsetTop;
if ( ( position.top + myOffset + atOffset + offset) > overBottom && ( newOverTop > 0 || abs( newOverTop ) < overBottom ) ) {
position.top += myOffset + atOffset + offset;
}
@@ -1478,7 +1482,7 @@ function isNumber(value) {
}
$.widget("ui.resizable", $.ui.mouse, {
- version: "1.10.3",
+ version: "1.10.4",
widgetEventPrefix: "resize",
options: {
alsoResize: false,
@@ -1747,7 +1751,7 @@ $.widget("ui.resizable", $.ui.mouse, {
//Store needed variables
this.offset = this.helper.offset();
this.position = { left: curleft, top: curtop };
- this.size = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
+ this.size = this._helper ? { width: this.helper.width(), height: this.helper.height() } : { width: el.width(), height: el.height() };
this.originalSize = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
this.originalPosition = { left: curleft, top: curtop };
this.sizeDiff = { width: el.outerWidth() - el.width(), height: el.outerHeight() - el.height() };
@@ -2228,8 +2232,8 @@ $.ui.plugin.add("resizable", "containment", {
isParent = that.containerElement.get(0) === that.element.parent().get(0);
isOffsetRelative = /relative|absolute/.test(that.containerElement.css("position"));
- if(isParent && isOffsetRelative) {
- woset -= that.parentData.left;
+ if ( isParent && isOffsetRelative ) {
+ woset -= Math.abs( that.parentData.left );
}
if (woset + that.size.width >= that.parentData.width) {
@@ -2410,10 +2414,20 @@ $.ui.plugin.add("resizable", "grid", {
that.size.height = newHeight;
that.position.left = op.left - ox;
} else {
- that.size.width = newWidth;
- that.size.height = newHeight;
- that.position.top = op.top - oy;
- that.position.left = op.left - ox;
+ if ( newHeight - gridY > 0 ) {
+ that.size.height = newHeight;
+ that.position.top = op.top - oy;
+ } else {
+ that.size.height = gridY;
+ that.position.top = op.top + os.height - gridY;
+ }
+ if ( newWidth - gridX > 0 ) {
+ that.size.width = newWidth;
+ that.position.left = op.left - ox;
+ } else {
+ that.size.width = gridX;
+ that.position.left = op.left + os.width - gridX;
+ }
}
}
@@ -2422,8 +2436,6 @@ $.ui.plugin.add("resizable", "grid", {
})(jQuery);
(function( $, undefined ) {
-/*jshint loopfunc: true */
-
function isOverAxis( x, reference, size ) {
return ( x > reference ) && ( x < ( reference + size ) );
}
@@ -2433,7 +2445,7 @@ function isFloating(item) {
}
$.widget("ui.sortable", $.ui.mouse, {
- version: "1.10.3",
+ version: "1.10.4",
widgetEventPrefix: "sort",
ready: false,
options: {
@@ -2774,12 +2786,12 @@ $.widget("ui.sortable", $.ui.mouse, {
}
// Only put the placeholder inside the current Container, skip all
- // items form other containers. This works because when moving
+ // items from other containers. This works because when moving
// an item from one container to another the
// currentContainer is switched before the placeholder is moved.
//
- // Without this moving items in "sub-sortables" can cause the placeholder to jitter
- // beetween the outer and inner container.
+ // Without this, moving items in "sub-sortables" can cause
+ // the placeholder to jitter beetween the outer and inner container.
if (item.instance !== this.currentContainer) {
continue;
}
@@ -3047,10 +3059,11 @@ $.widget("ui.sortable", $.ui.mouse, {
queries.push([$.isFunction(this.options.items) ? this.options.items.call(this.element, null, { options: this.options, item: this.currentItem }) : $(this.options.items, this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"), this]);
+ function addItems() {
+ items.push( this );
+ }
for (i = queries.length - 1; i >= 0; i--){
- queries[i][0].each(function() {
- items.push(this);
- });
+ queries[i][0].each( addItems );
}
return $(items);
@@ -3608,12 +3621,17 @@ $.widget("ui.sortable", $.ui.mouse, {
//Post events to containers
+ function delayEvent( type, instance, container ) {
+ return function( event ) {
+ container._trigger( type, event, instance._uiHash( instance ) );
+ };
+ }
for (i = this.containers.length - 1; i >= 0; i--){
- if(!noPropagation) {
- delayedTriggers.push((function(c) { return function(event) { c._trigger("deactivate", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
+ if (!noPropagation) {
+ delayedTriggers.push( delayEvent( "deactivate", this, this.containers[ i ] ) );
}
if(this.containers[i].containerCache.over) {
- delayedTriggers.push((function(c) { return function(event) { c._trigger("out", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
+ delayedTriggers.push( delayEvent( "out", this, this.containers[ i ] ) );
this.containers[i].containerCache.over = 0;
}
}
@@ -3692,11 +3710,8 @@ $.widget("ui.sortable", $.ui.mouse, {
})(jQuery);
(function( $, undefined ) {
-// used to prevent race conditions with remote data sources
-var requestIndex = 0;
-
$.widget( "ui.autocomplete", {
- version: "1.10.3",
+ version: "1.10.4",
defaultElement: "",
options: {
appendTo: null,
@@ -3720,6 +3735,7 @@ $.widget( "ui.autocomplete", {
select: null
},
+ requestIndex: 0,
pending: 0,
_create: function() {
@@ -3753,7 +3769,6 @@ $.widget( "ui.autocomplete", {
this._on( this.element, {
keydown: function( event ) {
- /*jshint maxcomplexity:15*/
if ( this.element.prop( "readOnly" ) ) {
suppressKeyPress = true;
suppressInput = true;
@@ -4096,19 +4111,18 @@ $.widget( "ui.autocomplete", {
},
_response: function() {
- var that = this,
- index = ++requestIndex;
+ var index = ++this.requestIndex;
- return function( content ) {
- if ( index === requestIndex ) {
- that.__response( content );
+ return $.proxy(function( content ) {
+ if ( index === this.requestIndex ) {
+ this.__response( content );
}
- that.pending--;
- if ( !that.pending ) {
- that.element.removeClass( "ui-autocomplete-loading" );
+ this.pending--;
+ if ( !this.pending ) {
+ this.element.removeClass( "ui-autocomplete-loading" );
}
- };
+ }, this );
},
__response: function( content ) {
@@ -4287,7 +4301,7 @@ $.widget( "ui.autocomplete", $.ui.autocomplete, {
(function( $, undefined ) {
$.widget( "ui.menu", {
- version: "1.10.3",
+ version: "1.10.4",
defaultElement: "",
delay: 300,
options: {
@@ -4346,13 +4360,18 @@ $.widget( "ui.menu", {
"click .ui-menu-item:has(a)": function( event ) {
var target = $( event.target ).closest( ".ui-menu-item" );
if ( !this.mouseHandled && target.not( ".ui-state-disabled" ).length ) {
- this.mouseHandled = true;
-
this.select( event );
+
+ // Only set the mouseHandled flag if the event will bubble, see #9469.
+ if ( !event.isPropagationStopped() ) {
+ this.mouseHandled = true;
+ }
+
// Open submenu on click
if ( target.has( ".ui-menu" ).length ) {
this.expand( event );
- } else if ( !this.element.is( ":focus" ) ) {
+ } else if ( !this.element.is( ":focus" ) && $( this.document[ 0 ].activeElement ).closest( ".ui-menu" ).length ) {
+
// Redirect focus to the menu
this.element.trigger( "focus", [ true ] );
@@ -4445,7 +4464,6 @@ $.widget( "ui.menu", {
},
_keydown: function( event ) {
- /*jshint maxcomplexity:20*/
var match, prev, character, skip, regex,
preventDefault = true;
@@ -4554,6 +4572,8 @@ $.widget( "ui.menu", {
icon = this.options.icons.submenu,
submenus = this.element.find( this.options.menus );
+ this.element.toggleClass( "ui-menu-icons", !!this.element.find( ".ui-icon" ).length );
+
// Initialize nested menus
submenus.filter( ":not(.ui-menu)" )
.addClass( "ui-menu ui-widget ui-widget-content ui-corner-all" )
@@ -4654,7 +4674,7 @@ $.widget( "ui.menu", {
}
nested = item.children( ".ui-menu" );
- if ( nested.length && ( /^mouse/.test( event.type ) ) ) {
+ if ( nested.length && event && ( /^mouse/.test( event.type ) ) ) {
this._startOpening(nested);
}
this.activeMenu = item.parent();
@@ -4897,7 +4917,7 @@ $.widget( "ui.menu", {
var numPages = 5;
$.widget( "ui.slider", $.ui.mouse, {
- version: "1.10.3",
+ version: "1.10.4",
widgetEventPrefix: "slide",
options: {
@@ -5008,7 +5028,10 @@ $.widget( "ui.slider", $.ui.mouse, {
this.range.addClass( classes +
( ( options.range === "min" || options.range === "max" ) ? " ui-slider-range-" + options.range : "" ) );
} else {
- this.range = $([]);
+ if ( this.range ) {
+ this.range.remove();
+ }
+ this.range = null;
}
},
@@ -5022,7 +5045,9 @@ $.widget( "ui.slider", $.ui.mouse, {
_destroy: function() {
this.handles.remove();
- this.range.remove();
+ if ( this.range ) {
+ this.range.remove();
+ }
this.element
.removeClass( "ui-slider" +
@@ -5194,7 +5219,7 @@ $.widget( "ui.slider", $.ui.mouse, {
} );
otherVal = this.values( index ? 0 : 1 );
if ( allowed !== false ) {
- this.values( index, newVal, true );
+ this.values( index, newVal );
}
}
} else {
@@ -5466,7 +5491,6 @@ $.widget( "ui.slider", $.ui.mouse, {
_handleEvents: {
keydown: function( event ) {
- /*jshint maxcomplexity:25*/
var allowed, curVal, newVal, step,
index = $( event.target ).data( "ui-slider-handle-index" );
@@ -6432,7 +6456,7 @@ $.fn.extend({
(function() {
$.extend( $.effects, {
- version: "1.10.3",
+ version: "1.10.4",
// Saves a set of properties in a data storage
save: function( element, set ) {
diff --git a/vendor/scripts/treema.js b/vendor/scripts/treema.js
deleted file mode 100644
index e02ce1980..000000000
--- a/vendor/scripts/treema.js
+++ /dev/null
@@ -1,3430 +0,0 @@
-(function() {
- var WebSocket = window.WebSocket || window.MozWebSocket;
- var br = window.brunch = (window.brunch || {});
- var ar = br['auto-reload'] = (br['auto-reload'] || {});
- if (!WebSocket || ar.disabled) return;
-
- var cacheBuster = function(url){
- var date = Math.round(Date.now() / 1000).toString();
- url = url.replace(/(\&|\\?)cacheBuster=\d*/, '');
- return url + (url.indexOf('?') >= 0 ? '&' : '?') +'cacheBuster=' + date;
- };
-
- var reloaders = {
- page: function(){
- window.location.reload(true);
- },
-
- stylesheet: function(){
- [].slice
- .call(document.querySelectorAll('link[rel="stylesheet"]'))
- .filter(function(link){
- return (link != null && link.href != null);
- })
- .forEach(function(link) {
- link.href = cacheBuster(link.href);
- });
- }
- };
- var port = ar.port || 9485;
- var host = (!br['server']) ? window.location.hostname : br['server'];
- var connection = new WebSocket('ws://' + host + ':' + port);
- connection.onmessage = function(event) {
- var message = event.data;
- if (ar.disabled) return;
- if (reloaders[message] != null) {
- reloaders[message]();
- } else {
- reloaders.page();
- }
- };
-})();
-
-;
-jade = (function(exports){
-/*!
- * Jade - runtime
- * Copyright(c) 2010 TJ Holowaychuk
- * MIT Licensed
- */
-
-/**
- * Lame Array.isArray() polyfill for now.
- */
-
-if (!Array.isArray) {
- Array.isArray = function(arr){
- return '[object Array]' == Object.prototype.toString.call(arr);
- };
-}
-
-/**
- * Lame Object.keys() polyfill for now.
- */
-
-if (!Object.keys) {
- Object.keys = function(obj){
- var arr = [];
- for (var key in obj) {
- if (obj.hasOwnProperty(key)) {
- arr.push(key);
- }
- }
- return arr;
- }
-}
-
-/**
- * Merge two attribute objects giving precedence
- * to values in object `b`. Classes are special-cased
- * allowing for arrays and merging/joining appropriately
- * resulting in a string.
- *
- * @param {Object} a
- * @param {Object} b
- * @return {Object} a
- * @api private
- */
-
-exports.merge = function merge(a, b) {
- var ac = a['class'];
- var bc = b['class'];
-
- if (ac || bc) {
- ac = ac || [];
- bc = bc || [];
- if (!Array.isArray(ac)) ac = [ac];
- if (!Array.isArray(bc)) bc = [bc];
- ac = ac.filter(nulls);
- bc = bc.filter(nulls);
- a['class'] = ac.concat(bc).join(' ');
- }
-
- for (var key in b) {
- if (key != 'class') {
- a[key] = b[key];
- }
- }
-
- return a;
-};
-
-/**
- * Filter null `val`s.
- *
- * @param {Mixed} val
- * @return {Mixed}
- * @api private
- */
-
-function nulls(val) {
- return val != null;
-}
-
-/**
- * Render the given attributes object.
- *
- * @param {Object} obj
- * @param {Object} escaped
- * @return {String}
- * @api private
- */
-
-exports.attrs = function attrs(obj, escaped){
- var buf = []
- , terse = obj.terse;
-
- delete obj.terse;
- var keys = Object.keys(obj)
- , len = keys.length;
-
- if (len) {
- buf.push('');
- for (var i = 0; i < len; ++i) {
- var key = keys[i]
- , val = obj[key];
-
- if ('boolean' == typeof val || null == val) {
- if (val) {
- terse
- ? buf.push(key)
- : buf.push(key + '="' + key + '"');
- }
- } else if (0 == key.indexOf('data') && 'string' != typeof val) {
- buf.push(key + "='" + JSON.stringify(val) + "'");
- } else if ('class' == key && Array.isArray(val)) {
- buf.push(key + '="' + exports.escape(val.join(' ')) + '"');
- } else if (escaped && escaped[key]) {
- buf.push(key + '="' + exports.escape(val) + '"');
- } else {
- buf.push(key + '="' + val + '"');
- }
- }
- }
-
- return buf.join(' ');
-};
-
-/**
- * Escape the given string of `html`.
- *
- * @param {String} html
- * @return {String}
- * @api private
- */
-
-exports.escape = function escape(html){
- return String(html)
- .replace(/&(?!(\w+|\#\d+);)/g, '&')
- .replace(//g, '>')
- .replace(/"/g, '"');
-};
-
-/**
- * Re-throw the given `err` in context to the
- * the jade in `filename` at the given `lineno`.
- *
- * @param {Error} err
- * @param {String} filename
- * @param {String} lineno
- * @api private
- */
-
-exports.rethrow = function rethrow(err, filename, lineno){
- if (!filename) throw err;
-
- var context = 3
- , str = require('fs').readFileSync(filename, 'utf8')
- , lines = str.split('\n')
- , start = Math.max(lineno - context, 0)
- , end = Math.min(lines.length, lineno + context);
-
- // Error context
- var context = lines.slice(start, end).map(function(line, i){
- var curr = i + start + 1;
- return (curr == lineno ? ' > ' : ' ')
- + curr
- + '| '
- + line;
- }).join('\n');
-
- // Alter exception message
- err.path = filename;
- err.message = (filename || 'Jade') + ':' + lineno
- + '\n' + context + '\n\n' + err.message;
- throw err;
-};
-
- return exports;
-
-})({});
-
-;var TreemaNode,
- __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
- __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
- __slice = [].slice;
-
-TreemaNode = (function() {
- var defaults;
-
- TreemaNode.prototype.schema = {};
-
- TreemaNode.prototype.$el = null;
-
- TreemaNode.prototype.data = null;
-
- TreemaNode.prototype.options = null;
-
- TreemaNode.prototype.parent = null;
-
- TreemaNode.prototype.nodeTemplate = '';
-
- TreemaNode.prototype.childrenTemplate = '';
-
- TreemaNode.prototype.addChildTemplate = '+
';
-
- TreemaNode.prototype.tempErrorTemplate = '';
-
- TreemaNode.prototype.toggleTemplate = '';
-
- TreemaNode.prototype.keyTemplate = '';
-
- TreemaNode.prototype.errorTemplate = '';
-
- TreemaNode.prototype.newPropertyTemplate = '';
-
- TreemaNode.prototype.collection = false;
-
- TreemaNode.prototype.ordered = false;
-
- TreemaNode.prototype.keyed = false;
-
- TreemaNode.prototype.editable = true;
-
- TreemaNode.prototype.directlyEditable = true;
-
- TreemaNode.prototype.skipTab = false;
-
- TreemaNode.prototype.valueClass = null;
-
- TreemaNode.prototype.removeOnEmptyDelete = true;
-
- TreemaNode.prototype.keyForParent = null;
-
- TreemaNode.prototype.childrenTreemas = null;
-
- TreemaNode.prototype.justCreated = true;
-
- TreemaNode.prototype.removed = false;
-
- TreemaNode.prototype.workingSchema = null;
-
- TreemaNode.prototype.isValid = function() {
- var errors;
- errors = this.getErrors();
- return errors.length === 0;
- };
-
- TreemaNode.prototype.getErrors = function() {
- var e, errors, moreErrors, my_path, root, _i, _len;
- if (!this.tv4) {
- return [];
- }
- if (this.isRoot()) {
- if (this.cachedErrors) {
- return this.cachedErrors;
- }
- this.cachedErrors = this.tv4.validateMultiple(this.data, this.schema)['errors'];
- return this.cachedErrors;
- }
- root = this.getRoot();
- errors = root.getErrors();
- my_path = this.getPath();
- errors = (function() {
- var _i, _len, _results;
- _results = [];
- for (_i = 0, _len = errors.length; _i < _len; _i++) {
- e = errors[_i];
- if (e.dataPath.slice(0, +my_path.length + 1 || 9e9) === my_path) {
- _results.push(e);
- }
- }
- return _results;
- })();
- for (_i = 0, _len = errors.length; _i < _len; _i++) {
- e = errors[_i];
- e.dataPath = e.dataPath.slice(0, +my_path.length + 1 || 9e9);
- }
- if (this.workingSchema) {
- moreErrors = this.tv4.validateMultiple(this.data, this.workingSchema).errors;
- errors = errors.concat(moreErrors);
- }
- return errors;
- };
-
- TreemaNode.prototype.setUpValidator = function() {
- var root, _ref;
- if (!this.parent) {
- this.tv4 = (_ref = window['tv4']) != null ? _ref.freshApi() : void 0;
- this.tv4.addSchema('#', this.schema);
- if (this.schema.id) {
- return this.tv4.addSchema(this.schema.id, this.schema);
- }
- } else {
- root = this.getRoot();
- return this.tv4 = root.tv4;
- }
- };
-
- TreemaNode.prototype.saveChanges = function() {
- return console.error('"saveChanges" has not been overridden.');
- };
-
- TreemaNode.prototype.getDefaultValue = function() {
- return null;
- };
-
- TreemaNode.prototype.buildValueForDisplay = function() {
- return console.error('"buildValueForDisplay" has not been overridden.');
- };
-
- TreemaNode.prototype.buildValueForEditing = function() {
- if (!this.editable) {
- return;
- }
- return console.error('"buildValueForEditing" has not been overridden.');
- };
-
- TreemaNode.prototype.getChildren = function() {
- return console.error('"getChildren" has not been overridden.');
- };
-
- TreemaNode.prototype.getChildSchema = function() {
- return console.error('"getChildSchema" has not been overridden.');
- };
-
- TreemaNode.prototype.canAddChild = function() {
- return this.collection && this.editable && !this.settings.readOnly;
- };
-
- TreemaNode.prototype.canAddProperty = function() {
- return true;
- };
-
- TreemaNode.prototype.addingNewProperty = function() {
- return false;
- };
-
- TreemaNode.prototype.addNewChild = function() {
- return false;
- };
-
- TreemaNode.prototype.buildValueForDisplaySimply = function(valEl, text) {
- if (text.length > 200) {
- text = text.slice(0, 200) + '...';
- }
- return valEl.append($("").addClass('treema-shortened').text(text));
- };
-
- TreemaNode.prototype.buildValueForEditingSimply = function(valEl, value, inputType) {
- var input;
- if (inputType == null) {
- inputType = null;
- }
- input = $('');
- if (inputType) {
- input.attr('type', inputType);
- }
- if (value !== null) {
- input.val(value);
- }
- valEl.append(input);
- input.focus().select();
- input.blur(this.onEditInputBlur);
- return input;
- };
-
- TreemaNode.prototype.onEditInputBlur = function(e) {
- var closest, input, shouldRemove;
- shouldRemove = this.shouldTryToRemoveFromParent();
- closest = $(e.relatedTarget).closest('.treema-node')[0];
- if (closest === this.$el[0]) {
- shouldRemove = false;
- }
- this.markAsChanged();
- this.saveChanges(this.getValEl());
- input = this.getValEl().find('input, textarea, select');
- if (this.isValid()) {
- if (this.isEditing()) {
- this.display();
- }
- } else {
- input.focus().select();
- }
- if (shouldRemove) {
- this.remove();
- } else {
- this.flushChanges();
- }
- return this.broadcastChanges();
- };
-
- TreemaNode.prototype.shouldTryToRemoveFromParent = function() {
- var input, inputs, val, _i, _len;
- return false;
- val = this.getValEl();
- if (val.find('select').length) {
- return;
- }
- inputs = val.find('input, textarea');
- for (_i = 0, _len = inputs.length; _i < _len; _i++) {
- input = inputs[_i];
- input = $(input);
- if (input.attr('type') === 'checkbox' || input.val()) {
- return false;
- }
- }
- return true;
- };
-
- TreemaNode.prototype.limitChoices = function(options) {
- var _this = this;
- this["enum"] = options;
- this.buildValueForEditing = function(valEl) {
- var index, input, option, _i, _len, _ref;
- input = $('');
- _ref = _this["enum"];
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
- option = _ref[_i];
- input.append($('').text(option));
- }
- index = _this["enum"].indexOf(_this.data);
- if (index >= 0) {
- input.prop('selectedIndex', index);
- }
- valEl.append(input);
- input.focus();
- input.blur(_this.onEditInputBlur);
- return input;
- };
- return this.saveChanges = function(valEl) {
- var index;
- index = valEl.find('select').prop('selectedIndex');
- _this.data = _this["enum"][index];
- TreemaNode.changedTreemas.push(_this);
- return _this.broadcastChanges();
- };
- };
-
- TreemaNode.pluginName = "treema";
-
- defaults = {
- schema: {},
- callbacks: {}
- };
-
- function TreemaNode($el, options, parent) {
- this.$el = $el;
- this.parent = parent;
- this.onSelectType = __bind(this.onSelectType, this);
- this.onSelectSchema = __bind(this.onSelectSchema, this);
- this.orderDataFromUI = __bind(this.orderDataFromUI, this);
- this.onMouseLeave = __bind(this.onMouseLeave, this);
- this.onMouseEnter = __bind(this.onMouseEnter, this);
- this.onEditInputBlur = __bind(this.onEditInputBlur, this);
- this.$el = this.$el || $('');
- this.settings = $.extend({}, defaults, options);
- this.schema = this.settings.schema;
- if (!(this.schema.id || this.parent)) {
- this.schema.id = '__base__';
- }
- this.data = options.data;
- this.patches = [];
- this.callbacks = this.settings.callbacks;
- this._defaults = defaults;
- this._name = TreemaNode.pluginName;
- this.setUpValidator();
- this.populateData();
- this.previousState = this.copyData();
- }
-
- TreemaNode.prototype.build = function() {
- var schema, valEl, _ref;
- this.$el.addClass('treema-node').addClass('treema-clearfix');
- this.$el.empty().append($(this.nodeTemplate));
- this.$el.data('instance', this);
- if (!this.parent) {
- this.$el.addClass('treema-root');
- }
- if (!this.parent) {
- this.$el.attr('tabindex', 9001);
- }
- if (!this.parent) {
- this.justCreated = false;
- }
- if (this.collection) {
- this.$el.append($(this.childrenTemplate)).addClass('treema-closed');
- }
- valEl = this.getValEl();
- if (this.valueClass) {
- valEl.addClass(this.valueClass);
- }
- if (this.directlyEditable) {
- valEl.addClass('treema-display');
- }
- this.buildValueForDisplay(valEl);
- if (this.collection && !this.parent) {
- this.open();
- }
- if (!this.parent) {
- this.setUpGlobalEvents();
- }
- if (this.parent) {
- this.setUpLocalEvents();
- }
- if (this.collection) {
- this.updateMyAddButton();
- }
- this.createTypeSelector();
- if (((_ref = this.workingSchemas) != null ? _ref.length : void 0) > 1) {
- this.createSchemaSelector();
- }
- schema = this.workingSchema || this.schema;
- if (schema["enum"]) {
- this.limitChoices(schema["enum"]);
- }
- return this.$el;
- };
-
- TreemaNode.prototype.populateData = function() {
- if (this.data !== void 0) {
- return;
- }
- this.data = this.schema["default"];
- if (this.data !== void 0) {
- return;
- }
- return this.data = this.getDefaultValue();
- };
-
- TreemaNode.prototype.setWorkingSchema = function(workingSchema, workingSchemas) {
- this.workingSchema = workingSchema;
- this.workingSchemas = workingSchemas;
- };
-
- TreemaNode.prototype.createSchemaSelector = function() {
- var button, div, i, label, option, schema, select, _i, _len, _ref;
- div = $('').addClass('treema-schema-select-container');
- select = $('').addClass('treema-schema-select');
- button = $('').addClass('treema-schema-select-button').text('...');
- _ref = this.workingSchemas;
- for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) {
- schema = _ref[i];
- label = this.makeWorkingSchemaLabel(schema);
- option = $('').attr('value', i).text(label);
- if (schema === this.workingSchema) {
- option.attr('selected', true);
- }
- select.append(option);
- }
- div.append(button).append(select);
- select.change(this.onSelectSchema);
- return this.$el.find('> .treema-row').prepend(div);
- };
-
- TreemaNode.prototype.makeWorkingSchemaLabel = function(schema) {
- if (schema.title != null) {
- return schema.title;
- }
- if (schema.type != null) {
- return schema.type;
- }
- return '???';
- };
-
- TreemaNode.prototype.getTypes = function() {
- var schema, types;
- schema = this.workingSchema || this.schema;
- types = schema.type || ["string", "number", "integer", "boolean", "null", "array", "object"];
- if (!$.isArray(types)) {
- types = [types];
- }
- return types;
- };
-
- TreemaNode.prototype.createTypeSelector = function() {
- var button, currentType, div, option, schema, select, type, types, _i, _len;
- types = this.getTypes();
- if (!(types.length > 1)) {
- return;
- }
- schema = this.workingSchema || this.schema;
- if (schema["enum"]) {
- return;
- }
- div = $('').addClass('treema-type-select-container');
- select = $('').addClass('treema-type-select');
- button = $('').addClass('treema-type-select-button');
- currentType = $.type(this.data);
- if (this.valueClass === 'treema-integer') {
- currentType = 'integer';
- }
- for (_i = 0, _len = types.length; _i < _len; _i++) {
- type = types[_i];
- option = $('').attr('value', type).text(type);
- if (type === currentType) {
- option.attr('selected', true);
- button.text(this.typeToLetter(type));
- }
- select.append(option);
- }
- div.append(button).append(select);
- select.change(this.onSelectType);
- return this.$el.find('> .treema-row').prepend(div);
- };
-
- TreemaNode.prototype.typeToLetter = function(type) {
- return {
- 'boolean': 'B',
- 'array': 'A',
- 'object': 'O',
- 'string': 'S',
- 'number': 'F',
- 'integer': 'I',
- 'null': 'N'
- }[type];
- };
-
- TreemaNode.prototype.setUpGlobalEvents = function() {
- var _this = this;
- this.$el.unbind();
- this.$el.dblclick(function(e) {
- var _ref;
- return (_ref = $(e.target).closest('.treema-node').data('instance')) != null ? _ref.onDoubleClick(e) : void 0;
- });
- this.$el.click(function(e) {
- var _ref;
- if ((_ref = $(e.target).closest('.treema-node').data('instance')) != null) {
- _ref.onClick(e);
- }
- return _this.broadcastChanges(e);
- });
- this.keysPreviouslyDown = {};
- this.$el.keydown(function(e) {
- var closest, lastSelected, _ref;
- e.heldDown = _this.keysPreviouslyDown[e.which] || false;
- closest = $(e.target).closest('.treema-node').data('instance');
- lastSelected = _this.getLastSelectedTreema();
- if ((_ref = lastSelected || closest) != null) {
- _ref.onKeyDown(e);
- }
- _this.broadcastChanges(e);
- _this.keysPreviouslyDown[e.which] = true;
- if (e.ctrlKey || e.metaKey) {
- return _this.manageCopyAndPaste(e);
- }
- });
- return this.$el.keyup(function(e) {
- return delete _this.keysPreviouslyDown[e.which];
- });
- };
-
- TreemaNode.prototype.manageCopyAndPaste = function(e) {
- var target, x, y, _ref, _ref1, _ref2, _ref3,
- _this = this;
- target = (_ref = this.getLastSelectedTreema()) != null ? _ref : this;
- if (e.which === 86 && $(e.target).hasClass('treema-clipboard')) {
- if (e.shiftKey && $(e.target).hasClass('treema-clipboard')) {
- _ref1 = [window.scrollX, window.scrollY], x = _ref1[0], y = _ref1[1];
- return setTimeout((function() {
- var newData, result;
- _this.keepFocus(x, y);
- if (!(newData = _this.$clipboard.val())) {
- return;
- }
- try {
- newData = JSON.parse(newData);
- } catch (_error) {
- e = _error;
- return;
- }
- result = target.tv4.validateMultiple(newData, target.schema);
- if (result.valid) {
- return target.set('/', newData);
- } else {
- return console.log("not pasting", newData, "because it's not valid:", result);
- }
- }), 10);
- } else {
- return e.preventDefault();
- }
- } else if (e.shiftKey) {
- return this.$clipboardContainer.find('.treema-clipboard').focus().select();
- } else if (!(((_ref2 = window.getSelection()) != null ? _ref2.toString() : void 0) || ((_ref3 = document.selection) != null ? _ref3.createRange().text : void 0))) {
- return setTimeout((function() {
- if (_this.$clipboardContainer == null) {
- _this.$clipboardContainer = $('').appendTo(_this.$el);
- }
- _this.$clipboardContainer.empty().show();
- return _this.$clipboard = $('').val(JSON.stringify(target.data)).appendTo(_this.$clipboardContainer).focus().select();
- }), 0);
- }
- };
-
- TreemaNode.prototype.broadcastChanges = function(e) {
- var changes, t, _base;
- if (this.callbacks.select && TreemaNode.didSelect) {
- TreemaNode.didSelect = false;
- this.callbacks.select(e, this.getSelectedTreemas());
- }
- if (TreemaNode.changedTreemas.length) {
- changes = (function() {
- var _i, _len, _ref, _results;
- _ref = TreemaNode.changedTreemas;
- _results = [];
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
- t = _ref[_i];
- if (!t.removed) {
- _results.push(t);
- }
- }
- return _results;
- })();
- if (typeof (_base = this.callbacks).change === "function") {
- _base.change(e, jQuery.unique(changes));
- }
- return TreemaNode.changedTreemas = [];
- }
- };
-
- TreemaNode.prototype.markAsChanged = function() {
- return TreemaNode.changedTreemas.push(this);
- };
-
- TreemaNode.prototype.setUpLocalEvents = function() {
- var row;
- row = this.$el.find('> .treema-row');
- if (this.callbacks.mouseenter != null) {
- row.mouseenter(this.onMouseEnter);
- }
- if (this.callbacks.mouseleave != null) {
- return row.mouseleave(this.onMouseLeave);
- }
- };
-
- TreemaNode.prototype.onMouseEnter = function(e) {
- return this.callbacks.mouseenter(e, this);
- };
-
- TreemaNode.prototype.onMouseLeave = function(e) {
- return this.callbacks.mouseleave(e, this);
- };
-
- TreemaNode.prototype.onClick = function(e) {
- var clickedToggle, clickedValue, usedModKey, _ref;
- if ((_ref = e.target.nodeName) === 'INPUT' || _ref === 'TEXTAREA') {
- return;
- }
- clickedValue = $(e.target).closest('.treema-value').length;
- clickedToggle = $(e.target).hasClass('treema-toggle') || $(e.target).hasClass('treema-toggle-hit-area');
- usedModKey = e.shiftKey || e.ctrlKey || e.metaKey;
- if (!(clickedValue && !this.collection)) {
- this.keepFocus();
- }
- if (this.isDisplaying() && clickedValue && this.canEdit() && !usedModKey) {
- return this.toggleEdit();
- }
- if (!usedModKey && (clickedToggle || (clickedValue && this.collection))) {
- if (!clickedToggle) {
- this.deselectAll();
- this.select();
- }
- return this.toggleOpen();
- }
- if ($(e.target).closest('.treema-add-child').length && this.collection) {
- return this.addNewChild();
- }
- if (this.isRoot() || this.isEditing()) {
- return;
- }
- if (e.shiftKey) {
- return this.shiftSelect();
- }
- if (e.ctrlKey || e.metaKey) {
- return this.toggleSelect();
- }
- return this.select();
- };
-
- TreemaNode.prototype.onDoubleClick = function(e) {
- var clickedKey, _base, _base1, _base2;
- if (!this.collection) {
- return typeof (_base = this.callbacks).dblclick === "function" ? _base.dblclick(e, this) : void 0;
- }
- clickedKey = $(e.target).hasClass('treema-key');
- if (!clickedKey) {
- return typeof (_base1 = this.callbacks).dblclick === "function" ? _base1.dblclick(e, this) : void 0;
- }
- if (this.isClosed()) {
- this.open();
- }
- this.addNewChild();
- return typeof (_base2 = this.callbacks).dblclick === "function" ? _base2.dblclick(e, this) : void 0;
- };
-
- TreemaNode.prototype.onKeyDown = function(e) {
- if (e.which === 27) {
- this.onEscapePressed(e);
- }
- if (e.which === 9) {
- this.onTabPressed(e);
- }
- if (e.which === 37) {
- this.onLeftArrowPressed(e);
- }
- if (e.which === 38) {
- this.onUpArrowPressed(e);
- }
- if (e.which === 39) {
- this.onRightArrowPressed(e);
- }
- if (e.which === 40) {
- this.onDownArrowPressed(e);
- }
- if (e.which === 13) {
- this.onEnterPressed(e);
- }
- if (e.which === 78) {
- this.onNPressed(e);
- }
- if (e.which === 32) {
- this.onSpacePressed(e);
- }
- if (e.which === 84) {
- this.onTPressed(e);
- }
- if (e.which === 70) {
- this.onFPressed(e);
- }
- if (e.which === 8 && !e.heldDown) {
- return this.onDeletePressed(e);
- }
- };
-
- TreemaNode.prototype.onLeftArrowPressed = function(e) {
- if (this.inputFocused()) {
- return;
- }
- this.navigateOut();
- return e.preventDefault();
- };
-
- TreemaNode.prototype.onRightArrowPressed = function(e) {
- if (this.inputFocused()) {
- return;
- }
- this.navigateIn();
- return e.preventDefault();
- };
-
- TreemaNode.prototype.onUpArrowPressed = function(e) {
- if (this.inputFocused()) {
- return;
- }
- this.navigateSelection(-1);
- return e.preventDefault();
- };
-
- TreemaNode.prototype.onDownArrowPressed = function(e) {
- if (this.inputFocused()) {
- return;
- }
- this.navigateSelection(1);
- return e.preventDefault();
- };
-
- TreemaNode.prototype.inputFocused = function() {
- var _ref;
- if ((_ref = document.activeElement.nodeName) === 'INPUT' || _ref === 'TEXTAREA' || _ref === 'SELECT') {
- return true;
- }
- };
-
- TreemaNode.prototype.onSpacePressed = function() {};
-
- TreemaNode.prototype.onTPressed = function() {};
-
- TreemaNode.prototype.onFPressed = function() {};
-
- TreemaNode.prototype.onDeletePressed = function(e) {
- var editing;
- editing = this.editingIsHappening();
- if (editing && !$(e.target).val() && this.removeOnEmptyDelete) {
- this.display();
- this.select();
- this.removeSelectedNodes();
- e.preventDefault();
- }
- if (editing) {
- return;
- }
- e.preventDefault();
- return this.removeSelectedNodes();
- };
-
- TreemaNode.prototype.onEscapePressed = function() {
- if (!this.isEditing()) {
- return;
- }
- if (this.justCreated) {
- return this.remove();
- }
- if (this.isEditing()) {
- this.display();
- }
- if (!this.isRoot()) {
- this.select();
- }
- return this.keepFocus();
- };
-
- TreemaNode.prototype.onEnterPressed = function(e) {
- var offset;
- offset = e.shiftKey ? -1 : 1;
- if (offset === 1 && $(e.target).hasClass('treema-add-child')) {
- return this.addNewChild();
- }
- return this.traverseWhileEditing(offset, true);
- };
-
- TreemaNode.prototype.onTabPressed = function(e) {
- var offset;
- offset = e.shiftKey ? -1 : 1;
- if (this.hasMoreInputs(offset)) {
- return;
- }
- e.preventDefault();
- return this.traverseWhileEditing(offset, false);
- };
-
- TreemaNode.prototype.hasMoreInputs = function(offset) {
- var input, inputs, passedFocusedEl, _i, _len;
- inputs = this.getInputs().toArray();
- if (offset < 0) {
- inputs = inputs.reverse();
- }
- passedFocusedEl = false;
- for (_i = 0, _len = inputs.length; _i < _len; _i++) {
- input = inputs[_i];
- if (input === document.activeElement) {
- passedFocusedEl = true;
- continue;
- }
- if (!passedFocusedEl) {
- continue;
- }
- return true;
- }
- return false;
- };
-
- TreemaNode.prototype.onNPressed = function(e) {
- var selected, success, target;
- if (this.editingIsHappening()) {
- return;
- }
- selected = this.getLastSelectedTreema();
- target = (selected != null ? selected.collection : void 0) ? selected : selected != null ? selected.parent : void 0;
- if (!target) {
- return;
- }
- success = target.addNewChild();
- if (success) {
- this.deselectAll();
- }
- return e.preventDefault();
- };
-
- TreemaNode.prototype.traverseWhileEditing = function(offset, aggressive) {
- var ctx, editing, selected, shouldRemove, targetEl, _ref;
- shouldRemove = false;
- selected = this.getLastSelectedTreema();
- editing = this.isEditing();
- if (!editing && (selected != null ? selected.canEdit() : void 0)) {
- return selected.edit();
- }
- if (editing) {
- shouldRemove = this.shouldTryToRemoveFromParent();
- this.saveChanges(this.getValEl());
- if (!shouldRemove) {
- this.flushChanges();
- }
- if (!(aggressive || this.isValid())) {
- this.refreshErrors();
- return;
- }
- if (shouldRemove && ((_ref = $(this.$el[0].nextSibling)) != null ? _ref.hasClass('treema-add-child') : void 0) && offset === 1) {
- offset = 2;
- }
- this.endExistingEdits();
- this.select();
- }
- ctx = this.traversalContext(offset);
- if (!ctx) {
- return this.getRoot().addChild();
- }
- if (!ctx.origin) {
- targetEl = offset > 0 ? ctx.first : ctx.last;
- this.selectOrActivateElement(targetEl);
- }
- selected = $(ctx.origin).data('instance');
- if (offset > 0 && aggressive && selected.collection && selected.isClosed()) {
- return selected.open();
- }
- targetEl = offset > 0 ? ctx.next : ctx.prev;
- if (!targetEl) {
- targetEl = offset > 0 ? ctx.first : ctx.last;
- }
- this.selectOrActivateElement(targetEl);
- if (shouldRemove) {
- return this.remove();
- } else {
- return this.refreshErrors();
- }
- };
-
- TreemaNode.prototype.selectOrActivateElement = function(el) {
- var treema;
- el = $(el);
- treema = el.data('instance');
- if (treema) {
- if (treema.canEdit()) {
- return treema.edit();
- } else {
- return treema.select();
- }
- }
- this.deselectAll();
- return el.focus();
- };
-
- TreemaNode.prototype.navigateSelection = function(offset) {
- var ctx, targetTreema;
- ctx = this.navigationContext();
- if (!ctx) {
- return;
- }
- if (!ctx.origin) {
- targetTreema = offset > 0 ? ctx.first : ctx.last;
- return targetTreema.select();
- }
- targetTreema = offset > 0 ? ctx.next : ctx.prev;
- if (!targetTreema) {
- targetTreema = offset > 0 ? ctx.first : ctx.last;
- }
- return targetTreema != null ? targetTreema.select() : void 0;
- };
-
- TreemaNode.prototype.navigateOut = function() {
- var selected;
- selected = this.getLastSelectedTreema();
- if (!selected) {
- return;
- }
- if (selected.isOpen()) {
- return selected.close();
- }
- if ((!selected.parent) || selected.parent.isRoot()) {
- return;
- }
- return selected.parent.select();
- };
-
- TreemaNode.prototype.navigateIn = function() {
- var treema, _i, _len, _ref, _results;
- _ref = this.getSelectedTreemas();
- _results = [];
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
- treema = _ref[_i];
- if (!treema.collection) {
- continue;
- }
- if (treema.isClosed()) {
- _results.push(treema.open());
- } else {
- _results.push(void 0);
- }
- }
- return _results;
- };
-
- TreemaNode.prototype.traversalContext = function(offset) {
- var list, origin, _ref;
- list = this.getNavigableElements(offset);
- origin = (_ref = this.getLastSelectedTreema()) != null ? _ref.$el[0] : void 0;
- if (!origin) {
- origin = this.getRootEl().find('.treema-add-child:focus')[0];
- }
- if (!origin) {
- origin = this.getRootEl().find('.treema-new-prop')[0];
- }
- return this.wrapContext(list, origin, offset);
- };
-
- TreemaNode.prototype.navigationContext = function() {
- var list, origin;
- list = this.getVisibleTreemas();
- origin = this.getLastSelectedTreema();
- return this.wrapContext(list, origin);
- };
-
- TreemaNode.prototype.wrapContext = function(list, origin, offset) {
- var c, originIndex;
- if (offset == null) {
- offset = 1;
- }
- if (!list.length) {
- return;
- }
- c = {
- first: list[0],
- last: list[list.length - 1],
- origin: origin
- };
- if (origin) {
- offset = Math.abs(offset);
- originIndex = list.indexOf(origin);
- c.next = list[originIndex + offset];
- c.prev = list[originIndex - offset];
- }
- return c;
- };
-
- TreemaNode.prototype.canEdit = function() {
- var _ref;
- if (this.schema.readOnly || ((_ref = this.parent) != null ? _ref.schema.readOnly : void 0)) {
- return false;
- }
- if (this.settings.readOnly) {
- return false;
- }
- if (!this.editable) {
- return false;
- }
- if (!this.directlyEditable) {
- return false;
- }
- if (this.collection && this.isOpen()) {
- return false;
- }
- return true;
- };
-
- TreemaNode.prototype.display = function() {
- return this.toggleEdit('treema-display');
- };
-
- TreemaNode.prototype.edit = function(options) {
- if (options == null) {
- options = {};
- }
- this.toggleEdit('treema-edit');
- if ((options.offset != null) && options.offset < 0) {
- return this.focusLastInput();
- }
- };
-
- TreemaNode.prototype.toggleEdit = function(toClass) {
- var valEl;
- if (toClass == null) {
- toClass = null;
- }
- if (!this.editable) {
- return;
- }
- valEl = this.getValEl();
- if (toClass && valEl.hasClass(toClass)) {
- return;
- }
- toClass = toClass || (valEl.hasClass('treema-display') ? 'treema-edit' : 'treema-display');
- if (toClass === 'treema-edit') {
- this.endExistingEdits();
- }
- valEl.removeClass('treema-display').removeClass('treema-edit').addClass(toClass);
- valEl.empty();
- if (this.isDisplaying()) {
- this.buildValueForDisplay(valEl);
- }
- if (this.isEditing()) {
- this.buildValueForEditing(valEl);
- return this.deselectAll();
- }
- };
-
- TreemaNode.prototype.endExistingEdits = function() {
- var editing, elem, treema, _i, _len, _results;
- editing = this.getRootEl().find('.treema-edit').closest('.treema-node');
- _results = [];
- for (_i = 0, _len = editing.length; _i < _len; _i++) {
- elem = editing[_i];
- treema = $(elem).data('instance');
- treema.saveChanges(treema.getValEl());
- treema.display();
- _results.push(this.markAsChanged());
- }
- return _results;
- };
-
- TreemaNode.prototype.flushChanges = function() {
- var parent, _results;
- if (this.parent && this.justCreated) {
- this.parent.integrateChildTreema(this);
- }
- this.getRoot().cachedErrors = null;
- this.justCreated = false;
- this.markAsChanged();
- if (!this.parent) {
- return this.refreshErrors();
- }
- this.parent.data[this.keyForParent] = this.data;
- this.parent.refreshErrors();
- parent = this.parent;
- _results = [];
- while (parent) {
- parent.buildValueForDisplay(parent.getValEl().empty());
- _results.push(parent = parent.parent);
- }
- return _results;
- };
-
- TreemaNode.prototype.focusLastInput = function() {
- var inputs, last;
- inputs = this.getInputs();
- last = inputs[inputs.length - 1];
- return $(last).focus().select();
- };
-
- TreemaNode.prototype.removeSelectedNodes = function() {
- var nextSibling, prevSibling, selected, toSelect, treema, _i, _len;
- selected = this.getSelectedTreemas();
- toSelect = null;
- if (selected.length === 1) {
- nextSibling = selected[0].$el.next('.treema-node').data('instance');
- prevSibling = selected[0].$el.prev('.treema-node').data('instance');
- toSelect = nextSibling || prevSibling || selected[0].parent;
- }
- for (_i = 0, _len = selected.length; _i < _len; _i++) {
- treema = selected[_i];
- treema.remove();
- }
- if (toSelect && !this.getSelectedTreemas().length) {
- return toSelect.select();
- }
- };
-
- TreemaNode.prototype.remove = function() {
- var readOnly, required, root, tempError, _ref, _ref1;
- required = this.parent && (this.parent.schema.required != null) && (_ref = this.keyForParent, __indexOf.call(this.parent.schema.required, _ref) >= 0);
- if (required) {
- tempError = this.createTemporaryError('required');
- this.$el.prepend(tempError);
- return false;
- }
- readOnly = this.schema.readOnly || ((_ref1 = this.parent) != null ? _ref1.schema.readOnly : void 0);
- if (readOnly) {
- tempError = this.createTemporaryError('read only');
- this.$el.prepend(tempError);
- return false;
- }
- root = this.getRootEl();
- this.$el.remove();
- this.removed = true;
- if (document.activeElement === $('body')[0]) {
- this.keepFocus();
- }
- if (this.parent == null) {
- return true;
- }
- delete this.parent.childrenTreemas[this.keyForParent];
- delete this.parent.data[this.keyForParent];
- if (this.parent.ordered) {
- this.parent.orderDataFromUI();
- }
- this.parent.refreshErrors();
- this.parent.updateMyAddButton();
- this.parent.markAsChanged();
- this.parent.buildValueForDisplay(this.parent.getValEl().empty());
- this.broadcastChanges();
- return true;
- };
-
- TreemaNode.prototype.toggleOpen = function() {
- if (this.isClosed()) {
- this.open();
- } else {
- this.close();
- }
- return this;
- };
-
- TreemaNode.prototype.open = function(depth) {
- var child, childIndex, childNode, childrenContainer, key, schema, treema, value, _i, _len, _ref, _ref1, _ref2, _ref3, _results;
- if (depth == null) {
- depth = 1;
- }
- if (this.isClosed()) {
- childrenContainer = this.$el.find('.treema-children').detach();
- childrenContainer.empty();
- this.childrenTreemas = {};
- _ref = this.getChildren();
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
- _ref1 = _ref[_i], key = _ref1[0], value = _ref1[1], schema = _ref1[2];
- if (schema.format === 'hidden') {
- continue;
- }
- treema = TreemaNode.make(null, {
- schema: schema,
- data: value
- }, this, key);
- this.integrateChildTreema(treema);
- childNode = this.createChildNode(treema);
- childrenContainer.append(childNode);
- }
- this.$el.append(childrenContainer).removeClass('treema-closed').addClass('treema-open');
- childrenContainer.append($(this.addChildTemplate));
- if (this.ordered && childrenContainer.sortable && !this.settings.noSortable) {
- if (typeof childrenContainer.sortable === "function") {
- childrenContainer.sortable({
- deactivate: this.orderDataFromUI
- });
- }
- }
- this.refreshErrors();
- }
- depth -= 1;
- if (depth) {
- _ref3 = (_ref2 = this.childrenTreemas) != null ? _ref2 : {};
- _results = [];
- for (childIndex in _ref3) {
- child = _ref3[childIndex];
- _results.push(child.open(depth));
- }
- return _results;
- }
- };
-
- TreemaNode.prototype.orderDataFromUI = function() {
- var child, children, index, treema, _i, _len;
- children = this.$el.find('> .treema-children > .treema-node');
- index = 0;
- this.childrenTreemas = {};
- this.data = $.isArray(this.data) ? [] : {};
- for (_i = 0, _len = children.length; _i < _len; _i++) {
- child = children[_i];
- treema = $(child).data('instance');
- if (!treema) {
- continue;
- }
- treema.keyForParent = index;
- this.childrenTreemas[index] = treema;
- this.data[index] = treema.data;
- index += 1;
- }
- return this.flushChanges();
- };
-
- TreemaNode.prototype.close = function(saveChildData) {
- var key, treema, _ref;
- if (saveChildData == null) {
- saveChildData = true;
- }
- if (!this.isOpen()) {
- return;
- }
- if (saveChildData) {
- _ref = this.childrenTreemas;
- for (key in _ref) {
- treema = _ref[key];
- this.data[key] = treema.data;
- }
- }
- this.$el.find('.treema-children').empty();
- this.$el.addClass('treema-closed').removeClass('treema-open');
- this.childrenTreemas = null;
- this.refreshErrors();
- return this.buildValueForDisplay(this.getValEl().empty());
- };
-
- TreemaNode.prototype.select = function() {
- var excludeSelf, numSelected;
- numSelected = this.getSelectedTreemas().length;
- excludeSelf = numSelected === 1;
- this.deselectAll(excludeSelf);
- this.toggleSelect();
- this.keepFocus();
- return TreemaNode.didSelect = true;
- };
-
- TreemaNode.prototype.deselectAll = function(excludeSelf) {
- var treema, _i, _len, _ref;
- if (excludeSelf == null) {
- excludeSelf = false;
- }
- _ref = this.getSelectedTreemas();
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
- treema = _ref[_i];
- if (excludeSelf && treema === this) {
- continue;
- }
- treema.$el.removeClass('treema-selected');
- }
- this.clearLastSelected();
- return TreemaNode.didSelect = true;
- };
-
- TreemaNode.prototype.toggleSelect = function() {
- this.clearLastSelected();
- if (!this.isRoot()) {
- this.$el.toggleClass('treema-selected');
- }
- if (this.isSelected()) {
- this.$el.addClass('treema-last-selected');
- }
- return TreemaNode.didSelect = true;
- };
-
- TreemaNode.prototype.clearLastSelected = function() {
- return this.getRootEl().find('.treema-last-selected').removeClass('treema-last-selected');
- };
-
- TreemaNode.prototype.shiftSelect = function() {
- var allNodes, lastSelected, node, started, _i, _len;
- lastSelected = this.getRootEl().find('.treema-last-selected');
- if (!lastSelected.length) {
- this.select();
- }
- this.deselectAll();
- allNodes = this.getRootEl().find('.treema-node');
- started = false;
- for (_i = 0, _len = allNodes.length; _i < _len; _i++) {
- node = allNodes[_i];
- node = $(node).data('instance');
- if (!started) {
- if (node === this || node.wasSelectedLast()) {
- started = true;
- }
- if (started) {
- node.$el.addClass('treema-selected');
- }
- continue;
- }
- if (started && (node === this || node.wasSelectedLast())) {
- break;
- }
- node.$el.addClass('treema-selected');
- }
- this.$el.addClass('treema-selected');
- lastSelected.addClass('treema-selected');
- lastSelected.removeClass('treema-last-selected');
- this.$el.addClass('treema-last-selected');
- return TreemaNode.didSelect = true;
- };
-
- TreemaNode.prototype.buildWorkingSchemas = function(originalSchema) {
- var allOf, anyOf, baseSchema, oneOf, s, schema, singularSchema, singularSchemas, workingSchemas, _i, _j, _len, _len1;
- baseSchema = this.resolveReference($.extend(true, {}, originalSchema || {}));
- allOf = baseSchema.allOf;
- anyOf = baseSchema.anyOf;
- oneOf = baseSchema.oneOf;
- if (baseSchema.allOf != null) {
- delete baseSchema.allOf;
- }
- if (baseSchema.anyOf != null) {
- delete baseSchema.anyOf;
- }
- if (baseSchema.oneOf != null) {
- delete baseSchema.oneOf;
- }
- if (allOf != null) {
- for (_i = 0, _len = allOf.length; _i < _len; _i++) {
- schema = allOf[_i];
- $.extend(null, baseSchema, this.resolveReference(schema));
- }
- }
- workingSchemas = [];
- singularSchemas = [];
- if (anyOf != null) {
- singularSchemas = singularSchemas.concat(anyOf);
- }
- if (oneOf != null) {
- singularSchemas = singularSchemas.concat(oneOf);
- }
- for (_j = 0, _len1 = singularSchemas.length; _j < _len1; _j++) {
- singularSchema = singularSchemas[_j];
- singularSchema = this.resolveReference(singularSchema);
- s = $.extend(true, {}, baseSchema);
- s = $.extend(true, s, singularSchema);
- workingSchemas.push(s);
- }
- if (workingSchemas.length === 0) {
- workingSchemas = [baseSchema];
- }
- return workingSchemas;
- };
-
- TreemaNode.prototype.resolveReference = function(schema, scrubTitle) {
- var resolved;
- if (scrubTitle == null) {
- scrubTitle = false;
- }
- if (schema.$ref == null) {
- return schema;
- }
- resolved = this.tv4.getSchema(schema.$ref);
- if (!resolved) {
- console.warn('could not resolve reference', schema.$ref, tv4.getMissingUris());
- }
- if (resolved == null) {
- resolved = {};
- }
- if (scrubTitle && (resolved.title != null)) {
- delete resolved.title;
- }
- return resolved;
- };
-
- TreemaNode.prototype.chooseWorkingSchema = function(workingSchemas, data) {
- var result, root, schema, _i, _len;
- if (workingSchemas.length === 1) {
- return workingSchemas[0];
- }
- root = this.getRoot();
- for (_i = 0, _len = workingSchemas.length; _i < _len; _i++) {
- schema = workingSchemas[_i];
- result = tv4.validateMultiple(data, schema, false, root.schema);
- if (result.valid) {
- return schema;
- }
- }
- return workingSchemas[0];
- };
-
- TreemaNode.prototype.onSelectSchema = function(e) {
- var NodeClass, defaultType, index, workingSchema;
- index = parseInt($(e.target).val());
- workingSchema = this.workingSchemas[index];
- defaultType = "null";
- if (workingSchema["default"] != null) {
- defaultType = $.type(workingSchema["default"]);
- }
- if (workingSchema.type != null) {
- defaultType = workingSchema.type;
- }
- if ($.isArray(defaultType)) {
- defaultType = defaultType[0];
- }
- NodeClass = TreemaNode.getNodeClassForSchema(workingSchema, defaultType, this.settings.nodeClasses);
- this.workingSchema = workingSchema;
- return this.replaceNode(NodeClass);
- };
-
- TreemaNode.prototype.onSelectType = function(e) {
- var NodeClass, newType;
- newType = $(e.target).val();
- NodeClass = TreemaNode.getNodeClassForSchema(this.workingSchema, newType, this.settings.nodeClasses);
- return this.replaceNode(NodeClass);
- };
-
- TreemaNode.prototype.replaceNode = function(NodeClass) {
- var newNode, oldData, settings;
- settings = $.extend(true, {}, this.settings);
- oldData = this.data;
- if (settings.data) {
- delete settings.data;
- }
- newNode = new NodeClass(null, settings, this.parent);
- newNode.data = newNode.getDefaultValue();
- if (this.workingSchema["default"] != null) {
- newNode.data = this.workingSchema["default"];
- }
- if ($.type(oldData) === 'string' && $.type(newNode.data) === 'number') {
- newNode.data = parseFloat(oldData) || 0;
- }
- if ($.type(oldData) === 'number' && $.type(newNode.data) === 'string') {
- newNode.data = oldData.toString();
- }
- if ($.type(oldData) === 'number' && $.type(newNode.data) === 'number') {
- newNode.data = oldData;
- if (newNode.valueClass === 'treema-integer') {
- newNode.data = parseInt(newNode.data);
- }
- }
- newNode.tv4 = this.tv4;
- if (this.keyForParent != null) {
- newNode.keyForParent = this.keyForParent;
- }
- newNode.setWorkingSchema(this.workingSchema, this.workingSchemas);
- this.parent.createChildNode(newNode);
- this.$el.replaceWith(newNode.$el);
- return newNode.flushChanges();
- };
-
- TreemaNode.prototype.integrateChildTreema = function(treema) {
- treema.justCreated = false;
- this.childrenTreemas[treema.keyForParent] = treema;
- treema.populateData();
- this.data[treema.keyForParent] = treema.data;
- return treema;
- };
-
- TreemaNode.prototype.createChildNode = function(treema) {
- var childNode, defnEl, keyEl, name, required, row, suffix, _ref;
- childNode = treema.build();
- row = childNode.find('.treema-row');
- if (this.collection && this.keyed) {
- name = treema.schema.title || treema.keyForParent;
- required = this.schema.required || [];
- suffix = ': ';
- if (_ref = treema.keyForParent, __indexOf.call(required, _ref) >= 0) {
- suffix = '*' + suffix;
- }
- keyEl = $(this.keyTemplate).text(name + suffix);
- row.prepend(keyEl);
- defnEl = $('').addClass('treema-description').text(treema.schema.description || '');
- row.append(defnEl);
- }
- if (treema.collection) {
- childNode.prepend($(this.toggleTemplate));
- }
- return childNode;
- };
-
- TreemaNode.prototype.refreshErrors = function() {
- this.clearErrors();
- return this.showErrors();
- };
-
- TreemaNode.prototype.showErrors = function() {
- var childErrors, deepestTreema, e, error, erroredTreemas, errors, message, messages, ownErrors, path, subpath, treema, _i, _j, _k, _len, _len1, _len2, _ref, _results;
- if (this.justCreated) {
- return;
- }
- errors = this.getErrors();
- erroredTreemas = [];
- for (_i = 0, _len = errors.length; _i < _len; _i++) {
- error = errors[_i];
- path = error.dataPath.slice(1);
- path = path ? path.split('/') : [];
- deepestTreema = this;
- for (_j = 0, _len1 = path.length; _j < _len1; _j++) {
- subpath = path[_j];
- if (!deepestTreema.childrenTreemas) {
- error.forChild = true;
- break;
- }
- if (deepestTreema.ordered) {
- subpath = parseInt(subpath);
- }
- deepestTreema = deepestTreema.childrenTreemas[subpath];
- if (!deepestTreema) {
- console.error('could not find treema down path', path, this, "so couldn't show error", error);
- return;
- }
- }
- if (!(deepestTreema._errors && __indexOf.call(erroredTreemas, deepestTreema) >= 0)) {
- deepestTreema._errors = [];
- }
- deepestTreema._errors.push(error);
- erroredTreemas.push(deepestTreema);
- }
- _ref = $.unique(erroredTreemas);
- _results = [];
- for (_k = 0, _len2 = _ref.length; _k < _len2; _k++) {
- treema = _ref[_k];
- childErrors = (function() {
- var _l, _len3, _ref1, _results1;
- _ref1 = treema._errors;
- _results1 = [];
- for (_l = 0, _len3 = _ref1.length; _l < _len3; _l++) {
- e = _ref1[_l];
- if (e.forChild) {
- _results1.push(e);
- }
- }
- return _results1;
- })();
- ownErrors = (function() {
- var _l, _len3, _ref1, _results1;
- _ref1 = treema._errors;
- _results1 = [];
- for (_l = 0, _len3 = _ref1.length; _l < _len3; _l++) {
- e = _ref1[_l];
- if (!e.forChild) {
- _results1.push(e);
- }
- }
- return _results1;
- })();
- messages = (function() {
- var _l, _len3, _results1;
- _results1 = [];
- for (_l = 0, _len3 = ownErrors.length; _l < _len3; _l++) {
- e = ownErrors[_l];
- _results1.push(e.message);
- }
- return _results1;
- })();
- if (childErrors.length > 0) {
- message = "[" + childErrors.length + "] error";
- if (childErrors.length > 1) {
- message = message + 's';
- }
- messages.push(message);
- }
- _results.push(treema.showError(messages.join('
')));
- }
- return _results;
- };
-
- TreemaNode.prototype.showError = function(message) {
- this.$el.prepend($(this.errorTemplate));
- this.$el.find('> .treema-error').html(message).show();
- return this.$el.addClass('treema-has-error');
- };
-
- TreemaNode.prototype.clearErrors = function() {
- this.$el.find('.treema-error').remove();
- this.$el.find('.treema-has-error').removeClass('treema-has-error');
- return this.$el.removeClass('treema-has-error');
- };
-
- TreemaNode.prototype.createTemporaryError = function(message, attachFunction) {
- if (attachFunction == null) {
- attachFunction = null;
- }
- if (!attachFunction) {
- attachFunction = this.$el.prepend;
- }
- this.clearTemporaryErrors();
- return $(this.tempErrorTemplate).text(message).delay(3000).fadeOut(1000, function() {
- return $(this).remove();
- });
- };
-
- TreemaNode.prototype.clearTemporaryErrors = function() {
- return this.getRootEl().find('.treema-temp-error').remove();
- };
-
- TreemaNode.prototype.get = function(path) {
- var data, seg, _i, _len;
- if (path == null) {
- path = '/';
- }
- path = this.normalizePath(path);
- if (path.length === 0) {
- return this.data;
- }
- if (this.childrenTreemas != null) {
- return this.digDeeper(path, 'get', void 0, []);
- }
- data = this.data;
- for (_i = 0, _len = path.length; _i < _len; _i++) {
- seg = path[_i];
- data = data[this.normalizeKey(seg, data)];
- if (data === void 0) {
- break;
- }
- }
- return data;
- };
-
- TreemaNode.prototype.set = function(path, newData) {
- var data, i, result, seg, _i, _len;
- path = this.normalizePath(path);
- if (path.length === 0) {
- this.data = newData;
- this.refreshDisplay();
- return true;
- }
- if (this.childrenTreemas != null) {
- result = this.digDeeper(path, 'set', false, [newData]);
- if (result === false && path.length === 1 && $.isPlainObject(this.data)) {
- this.data[path[0]] = newData;
- return true;
- }
- return result;
- }
- data = this.data;
- for (i = _i = 0, _len = path.length; _i < _len; i = ++_i) {
- seg = path[i];
- seg = this.normalizeKey(seg, data);
- if (path.length === i + 1) {
- data[seg] = newData;
- this.refreshDisplay();
- return true;
- } else {
- data = data[seg];
- if (data === void 0) {
- return false;
- }
- }
- }
- };
-
- TreemaNode.prototype["delete"] = function(path) {
- var data, i, seg, _i, _len;
- path = this.normalizePath(path);
- if (path.length === 0) {
- return this.remove();
- }
- if (this.childrenTreemas != null) {
- return this.digDeeper(path, 'delete', false, []);
- }
- data = this.data;
- for (i = _i = 0, _len = path.length; _i < _len; i = ++_i) {
- seg = path[i];
- seg = this.normalizeKey(seg, data);
- if (path.length === i + 1) {
- if ($.isArray(data)) {
- data.splice(seg, 1);
- } else {
- delete data[seg];
- }
- this.refreshDisplay();
- return true;
- } else {
- data = data[seg];
- if (data === void 0) {
- return false;
- }
- }
- }
- };
-
- TreemaNode.prototype.insert = function(path, newData) {
- var data, i, seg, _i, _len;
- path = this.normalizePath(path);
- if (path.length === 0) {
- if (!$.isArray(this.data)) {
- return false;
- }
- this.data.push(newData);
- this.refreshDisplay();
- this.flushChanges();
- return true;
- }
- if (this.childrenTreemas != null) {
- return this.digDeeper(path, 'insert', false, [newData]);
- }
- data = this.data;
- for (i = _i = 0, _len = path.length; _i < _len; i = ++_i) {
- seg = path[i];
- seg = this.normalizeKey(seg, data);
- data = data[seg];
- if (data === void 0) {
- return false;
- }
- }
- if (!$.isArray(data)) {
- return false;
- }
- data.push(newData);
- this.refreshDisplay();
- return true;
- };
-
- TreemaNode.prototype.normalizeKey = function(key, collection) {
- var i, parts, value, _i, _len;
- if ($.isArray(collection)) {
- if (__indexOf.call(key, '=') >= 0) {
- parts = key.split('=');
- for (i = _i = 0, _len = collection.length; _i < _len; i = ++_i) {
- value = collection[i];
- if (value[parts[0]] === parts[1]) {
- return i;
- }
- }
- } else {
- return parseInt(key);
- }
- }
- return key;
- };
-
- TreemaNode.prototype.normalizePath = function(path) {
- var s;
- if ($.type(path) === 'string') {
- path = path.split('/');
- path = (function() {
- var _i, _len, _results;
- _results = [];
- for (_i = 0, _len = path.length; _i < _len; _i++) {
- s = path[_i];
- if (s.length) {
- _results.push(s);
- }
- }
- return _results;
- })();
- }
- return path;
- };
-
- TreemaNode.prototype.digDeeper = function(path, func, def, args) {
- var childTreema, seg;
- seg = this.normalizeKey(path[0], this.data);
- childTreema = this.childrenTreemas[seg];
- if (childTreema === void 0) {
- return def;
- }
- return childTreema[func].apply(childTreema, [path.slice(1)].concat(__slice.call(args)));
- };
-
- TreemaNode.prototype.refreshDisplay = function() {
- var valEl;
- if (this.isDisplaying()) {
- valEl = this.getValEl();
- valEl.empty();
- this.buildValueForDisplay(valEl);
- } else {
- this.display();
- }
- if (this.collection && this.isOpen()) {
- this.close(false);
- this.open();
- }
- this.flushChanges();
- return this.broadcastChanges();
- };
-
- TreemaNode.prototype.getValEl = function() {
- return this.$el.find('> .treema-row .treema-value');
- };
-
- TreemaNode.prototype.getRootEl = function() {
- return this.$el.closest('.treema-root');
- };
-
- TreemaNode.prototype.getRoot = function() {
- var node;
- node = this;
- while (node.parent != null) {
- node = node.parent;
- }
- return node;
- };
-
- TreemaNode.prototype.getInputs = function() {
- return this.getValEl().find('input, textarea');
- };
-
- TreemaNode.prototype.getSelectedTreemas = function() {
- var el, _i, _len, _ref, _results;
- _ref = this.getRootEl().find('.treema-selected');
- _results = [];
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
- el = _ref[_i];
- _results.push($(el).data('instance'));
- }
- return _results;
- };
-
- TreemaNode.prototype.getLastSelectedTreema = function() {
- return this.getRootEl().find('.treema-last-selected').data('instance');
- };
-
- TreemaNode.prototype.getAddButtonEl = function() {
- return this.$el.find('> .treema-children > .treema-add-child');
- };
-
- TreemaNode.prototype.getVisibleTreemas = function() {
- var el, _i, _len, _ref, _results;
- _ref = this.getRootEl().find('.treema-node');
- _results = [];
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
- el = _ref[_i];
- _results.push($(el).data('instance'));
- }
- return _results;
- };
-
- TreemaNode.prototype.getNavigableElements = function() {
- return this.getRootEl().find('.treema-node, .treema-add-child:visible').toArray();
- };
-
- TreemaNode.prototype.getPath = function() {
- var pathPieces, pointer;
- pathPieces = [];
- pointer = this;
- while (pointer && (pointer.keyForParent != null)) {
- pathPieces.push(pointer.keyForParent + '');
- pointer = pointer.parent;
- }
- pathPieces.reverse();
- return '/' + pathPieces.join('/');
- };
-
- TreemaNode.prototype.isRoot = function() {
- return !this.parent;
- };
-
- TreemaNode.prototype.isEditing = function() {
- return this.getValEl().hasClass('treema-edit');
- };
-
- TreemaNode.prototype.isDisplaying = function() {
- return this.getValEl().hasClass('treema-display');
- };
-
- TreemaNode.prototype.isOpen = function() {
- return this.$el.hasClass('treema-open');
- };
-
- TreemaNode.prototype.isClosed = function() {
- return this.$el.hasClass('treema-closed');
- };
-
- TreemaNode.prototype.isSelected = function() {
- return this.$el.hasClass('treema-selected');
- };
-
- TreemaNode.prototype.wasSelectedLast = function() {
- return this.$el.hasClass('treema-last-selected');
- };
-
- TreemaNode.prototype.editingIsHappening = function() {
- return this.getRootEl().find('.treema-edit').length;
- };
-
- TreemaNode.prototype.rootSelected = function() {
- return $(document.activeElement).hasClass('treema-root');
- };
-
- TreemaNode.prototype.keepFocus = function(x, y) {
- var _ref;
- if (!((x != null) && (y != null))) {
- _ref = [window.scrollX, window.scrollY], x = _ref[0], y = _ref[1];
- }
- this.getRootEl().focus();
- return window.scrollTo(x, y);
- };
-
- TreemaNode.prototype.copyData = function() {
- return $.extend(null, {}, {
- 'd': this.data
- })['d'];
- };
-
- TreemaNode.prototype.updateMyAddButton = function() {
- this.$el.removeClass('treema-full');
- if (!this.canAddChild()) {
- return this.$el.addClass('treema-full');
- }
- };
-
- TreemaNode.nodeMap = {};
-
- TreemaNode.setNodeSubclass = function(key, NodeClass) {
- return this.nodeMap[key] = NodeClass;
- };
-
- TreemaNode.getNodeClassForSchema = function(schema, def, localClasses) {
- var NodeClass, type;
- if (def == null) {
- def = 'string';
- }
- if (localClasses == null) {
- localClasses = null;
- }
- NodeClass = null;
- localClasses = localClasses || {};
- if (schema.format) {
- NodeClass = localClasses[schema.format] || this.nodeMap[schema.format];
- }
- if (NodeClass) {
- return NodeClass;
- }
- type = schema.type || def;
- if ($.isArray(type)) {
- type = def;
- }
- NodeClass = localClasses[type] || this.nodeMap[type];
- if (NodeClass) {
- return NodeClass;
- }
- return this.nodeMap['any'];
- };
-
- TreemaNode.make = function(element, options, parent, keyForParent) {
- var NodeClass, combinedOps, d, data, localClasses, newNode, schemaTypes, type, workingSchema, workingSchemas;
- if (options.data === void 0 && (options.schema["default"] != null)) {
- d = options.schema["default"];
- options.data = $.extend(true, {}, {
- 'x': d
- })['x'];
- }
- workingSchemas = [];
- workingSchema = null;
- type = null;
- if (options.schema["default"] !== void 0) {
- type = $.type(options.schema["default"]);
- }
- if (options.data != null) {
- type = $.type(options.data);
- }
- if (type === 'number' && options.data % 1) {
- type = 'integer';
- }
- if (type == null) {
- schemaTypes = options.schema.type;
- if ($.isArray(schemaTypes)) {
- schemaTypes = schemaTypes[0];
- }
- if (schemaTypes == null) {
- schemaTypes = 'string';
- }
- type = schemaTypes;
- }
- localClasses = parent ? parent.settings.nodeClasses : options.nodeClasses;
- if (parent) {
- workingSchemas = parent.buildWorkingSchemas(options.schema);
- data = options.data;
- if (data === void 0) {
- data = options.schema["default"];
- }
- workingSchema = parent.chooseWorkingSchema(workingSchemas, data);
- NodeClass = this.getNodeClassForSchema(workingSchema, type, localClasses);
- } else {
- NodeClass = this.getNodeClassForSchema(options.schema, type, localClasses);
- }
- if (options.data === void 0) {
- type = options.schema.type;
- if (type == null) {
- type = workingSchema != null ? workingSchema.type : void 0;
- }
- if (type == null) {
- type = 'string';
- }
- if ($.isArray(type)) {
- type = type[0];
- }
- options.data = {
- 'string': '',
- 'number': 0,
- 'null': null,
- 'object': {},
- 'integer': 0,
- 'boolean': false,
- 'array': []
- }[type];
- }
- combinedOps = {};
- if (parent) {
- $.extend(combinedOps, parent.settings);
- }
- $.extend(combinedOps, options);
- newNode = new NodeClass(element, combinedOps, parent);
- if (parent != null) {
- newNode.tv4 = parent.tv4;
- }
- if (keyForParent != null) {
- newNode.keyForParent = keyForParent;
- }
- if (parent) {
- newNode.setWorkingSchema(workingSchema, workingSchemas);
- }
- return newNode;
- };
-
- TreemaNode.extend = function(child) {
- var ctor;
- ctor = function() {};
- ctor.prototype = this.prototype;
- child.prototype = new ctor();
- child.prototype.constructor = child;
- child.__super__ = this.prototype;
- child.prototype["super"] = function(method) {
- return this.constructor.__super__[method];
- };
- return child;
- };
-
- TreemaNode.didSelect = false;
-
- TreemaNode.changedTreemas = [];
-
- return TreemaNode;
-
-})();
-;var __init,
- __hasProp = {}.hasOwnProperty,
- __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
- __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
- __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
- __slice = [].slice;
-
-(__init = function() {
- var AnyNode, ArrayNode, BooleanNode, IntegerNode, NullNode, NumberNode, ObjectNode, StringNode, _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6;
- TreemaNode.setNodeSubclass('string', StringNode = (function(_super) {
- __extends(StringNode, _super);
-
- function StringNode() {
- _ref = StringNode.__super__.constructor.apply(this, arguments);
- return _ref;
- }
-
- StringNode.prototype.valueClass = 'treema-string';
-
- StringNode.prototype.getDefaultValue = function() {
- return '';
- };
-
- StringNode.inputTypes = ['color', 'date', 'datetime', 'datetime-local', 'email', 'month', 'range', 'search', 'tel', 'text', 'time', 'url', 'week'];
-
- StringNode.prototype.buildValueForDisplay = function(valEl) {
- return this.buildValueForDisplaySimply(valEl, "\"" + this.data + "\"");
- };
-
- StringNode.prototype.buildValueForEditing = function(valEl) {
- var input, _ref1;
- input = this.buildValueForEditingSimply(valEl, this.data);
- if (this.schema.maxLength) {
- input.attr('maxlength', this.schema.maxLength);
- }
- if (_ref1 = this.schema.format, __indexOf.call(StringNode.inputTypes, _ref1) >= 0) {
- return input.attr('type', this.schema.format);
- }
- };
-
- StringNode.prototype.saveChanges = function(valEl) {
- return this.data = $('input', valEl).val();
- };
-
- return StringNode;
-
- })(TreemaNode));
- TreemaNode.setNodeSubclass('number', NumberNode = (function(_super) {
- __extends(NumberNode, _super);
-
- function NumberNode() {
- _ref1 = NumberNode.__super__.constructor.apply(this, arguments);
- return _ref1;
- }
-
- NumberNode.prototype.valueClass = 'treema-number';
-
- NumberNode.prototype.getDefaultValue = function() {
- return 0;
- };
-
- NumberNode.prototype.buildValueForDisplay = function(valEl) {
- return this.buildValueForDisplaySimply(valEl, JSON.stringify(this.data));
- };
-
- NumberNode.prototype.buildValueForEditing = function(valEl) {
- var input;
- input = this.buildValueForEditingSimply(valEl, JSON.stringify(this.data), 'number');
- if (this.schema.maximum) {
- input.attr('max', this.schema.maximum);
- }
- if (this.schema.minimum) {
- return input.attr('min', this.schema.minimum);
- }
- };
-
- NumberNode.prototype.saveChanges = function(valEl) {
- return this.data = parseFloat($('input', valEl).val());
- };
-
- return NumberNode;
-
- })(TreemaNode));
- TreemaNode.setNodeSubclass('integer', IntegerNode = (function(_super) {
- __extends(IntegerNode, _super);
-
- function IntegerNode() {
- _ref2 = IntegerNode.__super__.constructor.apply(this, arguments);
- return _ref2;
- }
-
- IntegerNode.prototype.valueClass = 'treema-integer';
-
- IntegerNode.prototype.getDefaultValue = function() {
- return 0;
- };
-
- IntegerNode.prototype.buildValueForDisplay = function(valEl) {
- return this.buildValueForDisplaySimply(valEl, JSON.stringify(this.data));
- };
-
- IntegerNode.prototype.buildValueForEditing = function(valEl) {
- var input;
- input = this.buildValueForEditingSimply(valEl, JSON.stringify(this.data), 'number');
- if (this.schema.maximum) {
- input.attr('max', this.schema.maximum);
- }
- if (this.schema.minimum) {
- return input.attr('min', this.schema.minimum);
- }
- };
-
- IntegerNode.prototype.saveChanges = function(valEl) {
- return this.data = parseInt($('input', valEl).val());
- };
-
- return IntegerNode;
-
- })(TreemaNode));
- TreemaNode.setNodeSubclass('null', NullNode = NullNode = (function(_super) {
- __extends(NullNode, _super);
-
- function NullNode() {
- _ref3 = NullNode.__super__.constructor.apply(this, arguments);
- return _ref3;
- }
-
- NullNode.prototype.valueClass = 'treema-null';
-
- NullNode.prototype.editable = false;
-
- NullNode.prototype.buildValueForDisplay = function(valEl) {
- return this.buildValueForDisplaySimply(valEl, 'null');
- };
-
- return NullNode;
-
- })(TreemaNode));
- TreemaNode.setNodeSubclass('boolean', BooleanNode = (function(_super) {
- __extends(BooleanNode, _super);
-
- function BooleanNode() {
- _ref4 = BooleanNode.__super__.constructor.apply(this, arguments);
- return _ref4;
- }
-
- BooleanNode.prototype.valueClass = 'treema-boolean';
-
- BooleanNode.prototype.getDefaultValue = function() {
- return false;
- };
-
- BooleanNode.prototype.buildValueForDisplay = function(valEl) {
- return this.buildValueForDisplaySimply(valEl, JSON.stringify(this.data));
- };
-
- BooleanNode.prototype.buildValueForEditing = function(valEl) {
- var input;
- input = this.buildValueForEditingSimply(valEl, JSON.stringify(this.data));
- $('').text(JSON.stringify(this.data)).insertBefore(input);
- return input.focus();
- };
-
- BooleanNode.prototype.toggleValue = function(newValue) {
- var valEl;
- if (newValue == null) {
- newValue = null;
- }
- this.data = !this.data;
- if (newValue != null) {
- this.data = newValue;
- }
- valEl = this.getValEl().empty();
- if (this.isDisplaying()) {
- return this.buildValueForDisplay(valEl);
- } else {
- return this.buildValueForEditing(valEl);
- }
- };
-
- BooleanNode.prototype.onSpacePressed = function() {
- return this.toggleValue();
- };
-
- BooleanNode.prototype.onFPressed = function() {
- return this.toggleValue(false);
- };
-
- BooleanNode.prototype.onTPressed = function() {
- return this.toggleValue(true);
- };
-
- BooleanNode.prototype.saveChanges = function() {};
-
- BooleanNode.prototype.onClick = function(e) {
- var value;
- value = $(e.target).closest('.treema-value');
- if (!value.length) {
- return BooleanNode.__super__.onClick.call(this, e);
- }
- return this.toggleValue();
- };
-
- return BooleanNode;
-
- })(TreemaNode));
- TreemaNode.setNodeSubclass('array', ArrayNode = (function(_super) {
- __extends(ArrayNode, _super);
-
- function ArrayNode() {
- _ref5 = ArrayNode.__super__.constructor.apply(this, arguments);
- return _ref5;
- }
-
- ArrayNode.prototype.valueClass = 'treema-array';
-
- ArrayNode.prototype.getDefaultValue = function() {
- return [];
- };
-
- ArrayNode.prototype.collection = true;
-
- ArrayNode.prototype.ordered = true;
-
- ArrayNode.prototype.directlyEditable = false;
-
- ArrayNode.prototype.sort = false;
-
- ArrayNode.prototype.getChildren = function() {
- var key, value, _i, _len, _ref6, _results;
- _ref6 = this.data;
- _results = [];
- for (key = _i = 0, _len = _ref6.length; _i < _len; key = ++_i) {
- value = _ref6[key];
- _results.push([key, value, this.getChildSchema(key)]);
- }
- return _results;
- };
-
- ArrayNode.prototype.getChildSchema = function(index) {
- var schema;
- schema = this.workingSchema || this.schema;
- if (!((schema.items != null) || (schema.additionalItems != null))) {
- return {};
- }
- if ($.isPlainObject(schema.items)) {
- return this.resolveReference(schema.items, true);
- }
- if (index < schema.length) {
- return this.resolveReference(schema[index], true);
- }
- if ($.isPlainObject(schema.additionalItems)) {
- return this.resolveReference(schema.additionalItems, true);
- }
- return {};
- };
-
- ArrayNode.prototype.buildValueForDisplay = function(valEl) {
- var child, empty, helperTreema, index, text, val, _i, _len, _ref6;
- text = [];
- if (!this.data) {
- return;
- }
- _ref6 = this.data.slice(0, 3);
- for (index = _i = 0, _len = _ref6.length; _i < _len; index = ++_i) {
- child = _ref6[index];
- helperTreema = TreemaNode.make(null, {
- schema: this.getChildSchema(index),
- data: child
- }, this);
- val = $('');
- helperTreema.buildValueForDisplay(val);
- text.push(val.text());
- }
- if (this.data.length > 3) {
- text.push('...');
- }
- empty = this.schema.title != null ? "(empty " + this.schema.title + ")" : '(empty)';
- text = text.length ? text.join(' | ') : empty;
- return this.buildValueForDisplaySimply(valEl, text);
- };
-
- ArrayNode.prototype.buildValueForEditing = function(valEl) {
- return this.buildValueForEditingSimply(valEl, JSON.stringify(this.data));
- };
-
- ArrayNode.prototype.canAddChild = function() {
- if (this.settings.readOnly || this.schema.readOnly) {
- return false;
- }
- if (this.schema.additionalItems === false && this.data.length >= this.schema.items.length) {
- return false;
- }
- if ((this.schema.maxItems != null) && this.data.length >= this.schema.maxItems) {
- return false;
- }
- return true;
- };
-
- ArrayNode.prototype.addNewChild = function() {
- var childNode, newTreema, new_index, schema;
- if (!this.canAddChild()) {
- return;
- }
- if (this.isClosed()) {
- this.open();
- }
- new_index = Object.keys(this.childrenTreemas).length;
- schema = this.getChildSchema(new_index);
- newTreema = TreemaNode.make(void 0, {
- schema: schema
- }, this, new_index);
- newTreema.justCreated = true;
- newTreema.tv4 = this.tv4;
- childNode = this.createChildNode(newTreema);
- this.getAddButtonEl().before(childNode);
- if (newTreema.canEdit()) {
- newTreema.edit();
- } else {
- newTreema.select();
- this.integrateChildTreema(newTreema);
- newTreema.flushChanges();
- }
- return newTreema;
- };
-
- ArrayNode.prototype.open = function() {
- var shouldShorten, valEl;
- if (this.sort) {
- this.data.sort(this.sortFunction);
- }
- ArrayNode.__super__.open.apply(this, arguments);
- shouldShorten = this.buildValueForDisplay === ArrayNode.prototype.buildValueForDisplay;
- shouldShorten = false;
- if (shouldShorten) {
- valEl = this.getValEl().empty();
- if (shouldShorten) {
- return this.buildValueForDisplaySimply(valEl, '[...]');
- }
- }
- };
-
- ArrayNode.prototype.close = function() {
- var valEl;
- ArrayNode.__super__.close.apply(this, arguments);
- valEl = this.getValEl().empty();
- return this.buildValueForDisplay(valEl);
- };
-
- ArrayNode.prototype.sortFunction = function(a, b) {
- if (a > b) {
- return 1;
- }
- if (a < b) {
- return -1;
- }
- return 0;
- };
-
- return ArrayNode;
-
- })(TreemaNode));
- window.TreemaArrayNode = ArrayNode;
- TreemaNode.setNodeSubclass('object', ObjectNode = (function(_super) {
- __extends(ObjectNode, _super);
-
- function ObjectNode() {
- this.cleanupAddNewChild = __bind(this.cleanupAddNewChild, this);
- _ref6 = ObjectNode.__super__.constructor.apply(this, arguments);
- return _ref6;
- }
-
- ObjectNode.prototype.valueClass = 'treema-object';
-
- ObjectNode.prototype.getDefaultValue = function() {
- var childKey, childSchema, d, _ref7, _ref8;
- d = {};
- if (!((_ref7 = this.schema) != null ? _ref7.properties : void 0)) {
- return d;
- }
- _ref8 = this.schema.properties;
- for (childKey in _ref8) {
- childSchema = _ref8[childKey];
- if (childSchema["default"]) {
- d[childKey] = childSchema["default"];
- }
- }
- return d;
- };
-
- ObjectNode.prototype.collection = true;
-
- ObjectNode.prototype.keyed = true;
-
- ObjectNode.prototype.directlyEditable = false;
-
- ObjectNode.prototype.getChildren = function() {
- var children, key, keysAccountedFor, value, _ref7;
- children = [];
- keysAccountedFor = [];
- if (this.schema.properties) {
- for (key in this.schema.properties) {
- if (typeof this.data[key] === 'undefined') {
- continue;
- }
- keysAccountedFor.push(key);
- children.push([key, this.data[key], this.getChildSchema(key)]);
- }
- }
- _ref7 = this.data;
- for (key in _ref7) {
- value = _ref7[key];
- if (__indexOf.call(keysAccountedFor, key) >= 0) {
- continue;
- }
- children.push([key, value, this.getChildSchema(key)]);
- }
- return children;
- };
-
- ObjectNode.prototype.getChildSchema = function(key_or_title) {
- var child_schema, key, re, schema, _ref7, _ref8;
- schema = this.workingSchema || this.schema;
- _ref7 = schema.properties;
- for (key in _ref7) {
- child_schema = _ref7[key];
- if (key === key_or_title || child_schema.title === key_or_title) {
- return this.resolveReference(child_schema, true);
- }
- }
- _ref8 = schema.patternProperties;
- for (key in _ref8) {
- child_schema = _ref8[key];
- re = new RegExp(key);
- if (key.match(re)) {
- return this.resolveReference(child_schema, true);
- }
- }
- if ($.isPlainObject(schema.additionalProperties)) {
- return this.resolveReference(schema.additionalProperties, true);
- }
- return {};
- };
-
- ObjectNode.prototype.buildValueForDisplay = function(valEl) {
- var displayValue, empty, i, key, name, schema, text, value, valueString, _ref7;
- text = [];
- if (!this.data) {
- return;
- }
- displayValue = this.data[this.schema.displayProperty];
- if (displayValue) {
- text = displayValue;
- return this.buildValueForDisplaySimply(valEl, text);
- }
- i = 0;
- schema = this.workingSchema || this.schema;
- _ref7 = this.data;
- for (key in _ref7) {
- value = _ref7[key];
- if (i === 3) {
- text.push('...');
- break;
- }
- i += 1;
- name = this.getChildSchema(key).title || key;
- if ($.isPlainObject(value) || $.isArray(value)) {
- text.push("" + name);
- continue;
- }
- valueString = value;
- if ($.type(value) !== 'string') {
- valueString = JSON.stringify(value);
- }
- if (valueString.length > 20) {
- valueString = valueString.slice(0, 21) + ' ...';
- }
- text.push("" + name + "=" + valueString);
- }
- empty = this.schema.title != null ? "(empty " + this.schema.title + ")" : '(empty)';
- text = text.length ? text.join(', ') : empty;
- return this.buildValueForDisplaySimply(valEl, text);
- };
-
- ObjectNode.prototype.buildValueForEditing = function(valEl) {
- return this.buildValueForEditingSimply(valEl, JSON.stringify(this.data));
- };
-
- ObjectNode.prototype.populateData = function() {
- var helperTreema, key, _i, _len, _ref7, _results;
- ObjectNode.__super__.populateData.call(this);
- if (!this.schema.required) {
- return;
- }
- _ref7 = this.schema.required;
- _results = [];
- for (_i = 0, _len = _ref7.length; _i < _len; _i++) {
- key = _ref7[_i];
- if (this.data[key] != null) {
- continue;
- }
- helperTreema = TreemaNode.make(null, {
- schema: this.getChildSchema(key)
- }, this);
- helperTreema.populateData();
- _results.push(this.data[key] = helperTreema.data);
- }
- return _results;
- };
-
- ObjectNode.prototype.open = function() {
- var shouldShorten, valEl;
- ObjectNode.__super__.open.apply(this, arguments);
- shouldShorten = this.buildValueForDisplay === ObjectNode.prototype.buildValueForDisplay;
- shouldShorten = false;
- if (shouldShorten) {
- valEl = this.getValEl().empty();
- if (shouldShorten) {
- return this.buildValueForDisplaySimply(valEl, '{...}');
- }
- }
- };
-
- ObjectNode.prototype.close = function() {
- var valEl;
- ObjectNode.__super__.close.apply(this, arguments);
- valEl = this.getValEl().empty();
- return this.buildValueForDisplay(valEl);
- };
-
- ObjectNode.prototype.addNewChild = function() {
- var keyInput, properties,
- _this = this;
- if (!this.canAddChild()) {
- return;
- }
- if (!this.isRoot()) {
- this.open();
- }
- this.deselectAll();
- properties = this.childPropertiesAvailable();
- keyInput = $(this.newPropertyTemplate);
- keyInput.keydown(function(e) {
- return _this.originalTargetValue = $(e.target).val();
- });
- if (typeof keyInput.autocomplete === "function") {
- keyInput.autocomplete({
- source: properties,
- minLength: 0,
- delay: 0,
- autoFocus: true
- });
- }
- this.getAddButtonEl().before(keyInput);
- keyInput.focus();
- keyInput.autocomplete('search');
- return true;
- };
-
- ObjectNode.prototype.canAddChild = function() {
- if (this.settings.readOnly || this.schema.readOnly) {
- return false;
- }
- if ((this.schema.maxProperties != null) && Object.keys(this.data).length >= this.schema.maxProperties) {
- return false;
- }
- if (this.schema.additionalProperties !== false) {
- return true;
- }
- if (this.schema.patternProperties != null) {
- return true;
- }
- if (this.childPropertiesAvailable().length) {
- return true;
- }
- return false;
- };
-
- ObjectNode.prototype.childPropertiesAvailable = function() {
- var childSchema, properties, property, schema, _ref7;
- schema = this.workingSchema || this.schema;
- if (!schema.properties) {
- return [];
- }
- properties = [];
- _ref7 = schema.properties;
- for (property in _ref7) {
- childSchema = _ref7[property];
- if (this.data[property] != null) {
- continue;
- }
- if (childSchema.format === 'hidden') {
- continue;
- }
- if (childSchema.readOnly) {
- continue;
- }
- properties.push(childSchema.title || property);
- }
- return properties.sort();
- };
-
- ObjectNode.prototype.onDeletePressed = function(e) {
- if (!this.addingNewProperty()) {
- return ObjectNode.__super__.onDeletePressed.call(this, e);
- }
- if (!$(e.target).val()) {
- this.cleanupAddNewChild();
- e.preventDefault();
- return this.$el.find('.treema-add-child').focus();
- }
- };
-
- ObjectNode.prototype.onEscapePressed = function() {
- return this.cleanupAddNewChild();
- };
-
- ObjectNode.prototype.onTabPressed = function(e) {
- if (!this.addingNewProperty()) {
- return ObjectNode.__super__.onTabPressed.call(this, e);
- }
- e.preventDefault();
- return this.tryToAddNewChild(e, false);
- };
-
- ObjectNode.prototype.onEnterPressed = function(e) {
- if (!this.addingNewProperty()) {
- return ObjectNode.__super__.onEnterPressed.call(this, e);
- }
- return this.tryToAddNewChild(e, true);
- };
-
- ObjectNode.prototype.tryToAddNewChild = function(e, aggressive) {
- var key, keyInput, offset, treema;
- if ((!this.originalTargetValue) && (!aggressive)) {
- offset = e.shiftKey ? -1 : 1;
- this.cleanupAddNewChild();
- this.$el.find('.treema-add-child').focus();
- this.traverseWhileEditing(offset);
- return;
- }
- keyInput = $(e.target);
- key = this.getPropertyKey($(e.target));
- if (key.length && !this.canAddProperty(key)) {
- this.clearTemporaryErrors();
- this.showBadPropertyError(keyInput);
- return;
- }
- if (this.childrenTreemas[key] != null) {
- this.cleanupAddNewChild();
- treema = this.childrenTreemas[key];
- if (treema.canEdit()) {
- return treema.toggleEdit();
- } else {
- return treema.select();
- }
- }
- this.cleanupAddNewChild();
- return this.addNewChildForKey(key);
- };
-
- ObjectNode.prototype.getPropertyKey = function(keyInput) {
- var child_key, child_schema, key, _ref7;
- key = keyInput.val();
- if (this.schema.properties) {
- _ref7 = this.schema.properties;
- for (child_key in _ref7) {
- child_schema = _ref7[child_key];
- if (child_schema.title === key) {
- key = child_key;
- }
- }
- }
- return key;
- };
-
- ObjectNode.prototype.canAddProperty = function(key) {
- var pattern;
- if (this.schema.additionalProperties !== false) {
- return true;
- }
- if (this.schema.properties[key] != null) {
- return true;
- }
- if (this.schema.patternProperties != null) {
- if ((function() {
- var _results;
- _results = [];
- for (pattern in this.schema.patternProperties) {
- _results.push(RegExp(pattern).test(key));
- }
- return _results;
- }).call(this)) {
- return true;
- }
- }
- return false;
- };
-
- ObjectNode.prototype.showBadPropertyError = function(keyInput) {
- var tempError;
- keyInput.focus();
- tempError = this.createTemporaryError('Invalid property name.');
- tempError.insertAfter(keyInput);
- };
-
- ObjectNode.prototype.addNewChildForKey = function(key) {
- var child, childNode, children, newTreema, schema;
- schema = this.getChildSchema(key);
- newTreema = TreemaNode.make(null, {
- schema: schema
- }, this, key);
- childNode = this.createChildNode(newTreema);
- this.findObjectInsertionPoint(key).before(childNode);
- if (newTreema.canEdit()) {
- newTreema.edit();
- } else {
- this.integrateChildTreema(newTreema);
- children = newTreema.getChildren();
- if (children.length) {
- newTreema.open();
- child = newTreema.childrenTreemas[children[0][0]];
- child.select();
- } else {
- newTreema.addNewChild();
- }
- }
- return this.updateMyAddButton();
- };
-
- ObjectNode.prototype.findObjectInsertionPoint = function(key) {
- var afterKeys, allChildren, allProps, child, _i, _len, _ref7, _ref8;
- if (!((_ref7 = this.schema.properties) != null ? _ref7[key] : void 0)) {
- return this.getAddButtonEl();
- }
- allProps = Object.keys(this.schema.properties);
- afterKeys = allProps.slice(allProps.indexOf(key) + 1);
- allChildren = this.$el.find('> .treema-children > .treema-node');
- for (_i = 0, _len = allChildren.length; _i < _len; _i++) {
- child = allChildren[_i];
- if (_ref8 = $(child).data('instance').keyForParent, __indexOf.call(afterKeys, _ref8) >= 0) {
- return $(child);
- }
- }
- return this.getAddButtonEl();
- };
-
- ObjectNode.prototype.cleanupAddNewChild = function() {
- this.$el.find('.treema-new-prop').remove();
- return this.clearTemporaryErrors();
- };
-
- ObjectNode.prototype.addingNewProperty = function() {
- return document.activeElement === this.$el.find('.treema-new-prop')[0];
- };
-
- return ObjectNode;
-
- })(TreemaNode));
- window.TreemaObjectNode = ObjectNode;
- return TreemaNode.setNodeSubclass('any', AnyNode = (function(_super) {
- __extends(AnyNode, _super);
-
- AnyNode.prototype.helper = null;
-
- function AnyNode() {
- var splat;
- splat = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
- AnyNode.__super__.constructor.apply(this, splat);
- this.updateShadowMethods();
- }
-
- AnyNode.prototype.buildValueForEditing = function(valEl) {
- return this.buildValueForEditingSimply(valEl, JSON.stringify(this.data));
- };
-
- AnyNode.prototype.saveChanges = function(valEl) {
- var e;
- this.data = $('input', valEl).val();
- if (this.data[0] === "'" && this.data[this.data.length - 1] !== "'") {
- this.data = this.data.slice(1);
- } else if (this.data[0] === '"' && this.data[this.data.length - 1] !== '"') {
- this.data = this.data.slice(1);
- } else if (this.data.trim() === '[') {
- this.data = [];
- } else if (this.data.trim() === '{') {
- this.data = {};
- } else {
- try {
- this.data = JSON.parse(this.data);
- } catch (_error) {
- e = _error;
- console.log('could not parse data', this.data);
- }
- }
- this.updateShadowMethods();
- return this.rebuild();
- };
-
- AnyNode.prototype.updateShadowMethods = function() {
- var NodeClass, prop, _i, _len, _ref7, _results;
- NodeClass = TreemaNode.getNodeClassForSchema({
- type: $.type(this.data)
- });
- this.helper = new NodeClass(this.schema, {
- data: this.data,
- options: this.options
- }, this.parent);
- this.helper.tv4 = this.tv4;
- _ref7 = ['collection', 'ordered', 'keyed', 'getChildSchema', 'getChildren', 'getChildSchema', 'buildValueForDisplay', 'addNewChild', 'childPropertiesAvailable'];
- _results = [];
- for (_i = 0, _len = _ref7.length; _i < _len; _i++) {
- prop = _ref7[_i];
- _results.push(this[prop] = this.helper[prop]);
- }
- return _results;
- };
-
- AnyNode.prototype.rebuild = function() {
- var newNode, oldEl;
- oldEl = this.$el;
- if (this.parent) {
- newNode = this.parent.createChildNode(this);
- } else {
- newNode = this.build();
- }
- return this.$el = newNode;
- };
-
- AnyNode.prototype.onClick = function(e) {
- var clickedValue, usedModKey, _ref7;
- if ((_ref7 = e.target.nodeName) === 'INPUT' || _ref7 === 'TEXTAREA') {
- return;
- }
- clickedValue = $(e.target).closest('.treema-value').length;
- usedModKey = e.shiftKey || e.ctrlKey || e.metaKey;
- if (clickedValue && !usedModKey) {
- return this.toggleEdit();
- }
- return AnyNode.__super__.onClick.call(this, e);
- };
-
- return AnyNode;
-
- })(TreemaNode));
-})();
-;var __hasProp = {}.hasOwnProperty,
- __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
- __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
- __slice = [].slice;
-
-(function() {
- var AceNode, DatabaseSearchTreemaNode, LongStringNode, Point2DNode, Point3DNode, debounce, _ref, _ref1, _ref2, _ref3, _ref4;
- TreemaNode.setNodeSubclass('point2d', Point2DNode = (function(_super) {
- __extends(Point2DNode, _super);
-
- function Point2DNode() {
- _ref = Point2DNode.__super__.constructor.apply(this, arguments);
- return _ref;
- }
-
- Point2DNode.prototype.valueClass = 'treema-point2d';
-
- Point2DNode.prototype.getDefaultValue = function() {
- return {
- x: 0,
- y: 0
- };
- };
-
- Point2DNode.prototype.buildValueForDisplay = function(valEl) {
- return this.buildValueForDisplaySimply(valEl, "(" + this.data.x + ", " + this.data.y + ")");
- };
-
- Point2DNode.prototype.buildValueForEditing = function(valEl) {
- var xInput, yInput;
- xInput = $('').val(this.data.x).attr('placeholder', 'x');
- yInput = $('').val(this.data.y).attr('placeholder', 'y');
- valEl.append('(').append(xInput).append(', ').append(yInput).append(')');
- return valEl.find('input:first').focus().select();
- };
-
- Point2DNode.prototype.saveChanges = function(valEl) {
- this.data.x = parseFloat(valEl.find('input:first').val());
- return this.data.y = parseFloat(valEl.find('input:last').val());
- };
-
- return Point2DNode;
-
- })(TreemaNode));
- TreemaNode.setNodeSubclass('point3d', Point3DNode = (function(_super) {
- __extends(Point3DNode, _super);
-
- function Point3DNode() {
- _ref1 = Point3DNode.__super__.constructor.apply(this, arguments);
- return _ref1;
- }
-
- Point3DNode.prototype.valueClass = 'treema-point3d';
-
- Point3DNode.prototype.getDefaultValue = function() {
- return {
- x: 0,
- y: 0,
- z: 0
- };
- };
-
- Point3DNode.prototype.buildValueForDisplay = function(valEl) {
- return this.buildValueForDisplaySimply(valEl, "(" + this.data.x + ", " + this.data.y + ", " + this.data.z + ")");
- };
-
- Point3DNode.prototype.buildValueForEditing = function(valEl) {
- var xInput, yInput, zInput;
- xInput = $('').val(this.data.x).attr('placeholder', 'x');
- yInput = $('').val(this.data.y).attr('placeholder', 'y');
- zInput = $('').val(this.data.z).attr('placeholder', 'z');
- valEl.append('(').append(xInput).append(', ').append(yInput).append(', ').append(zInput).append(')');
- return valEl.find('input:first').focus().select();
- };
-
- Point3DNode.prototype.saveChanges = function() {
- var inputs;
- inputs = this.getInputs();
- this.data.x = parseFloat($(inputs[0]).val());
- this.data.y = parseFloat($(inputs[1]).val());
- return this.data.z = parseFloat($(inputs[2]).val());
- };
-
- return Point3DNode;
-
- })(TreemaNode));
- DatabaseSearchTreemaNode = (function(_super) {
- __extends(DatabaseSearchTreemaNode, _super);
-
- function DatabaseSearchTreemaNode() {
- this.searchCallback = __bind(this.searchCallback, this);
- this.search = __bind(this.search, this);
- _ref2 = DatabaseSearchTreemaNode.__super__.constructor.apply(this, arguments);
- return _ref2;
- }
-
- DatabaseSearchTreemaNode.prototype.valueClass = 'treema-search';
-
- DatabaseSearchTreemaNode.prototype.searchValueTemplate = '';
-
- DatabaseSearchTreemaNode.prototype.url = null;
-
- DatabaseSearchTreemaNode.prototype.lastTerm = null;
-
- DatabaseSearchTreemaNode.prototype.buildValueForDisplay = function(valEl) {
- var val;
- val = this.data ? this.formatDocument(this.data) : 'None';
- return this.buildValueForDisplaySimply(valEl, val);
- };
-
- DatabaseSearchTreemaNode.prototype.formatDocument = function(doc) {
- if ($.isString(doc)) {
- return doc;
- }
- return JSON.stringify(doc);
- };
-
- DatabaseSearchTreemaNode.prototype.buildValueForEditing = function(valEl) {
- var input;
- valEl.html(this.searchValueTemplate);
- input = valEl.find('input');
- input.focus().keyup(this.search);
- if (this.data) {
- return input.attr('placeholder', this.formatDocument(this.data));
- }
- };
-
- DatabaseSearchTreemaNode.prototype.search = function() {
- var term;
- term = this.getValEl().find('input').val();
- if (term === this.lastTerm) {
- return;
- }
- if (this.lastTerm && !term) {
- this.getSearchResultsEl().empty();
- }
- if (!term) {
- return;
- }
- this.lastTerm = term;
- this.getSearchResultsEl().empty().append('Searching');
- return $.ajax(this.url + '?term=' + term, {
- dataType: 'json',
- success: this.searchCallback
- });
- };
-
- DatabaseSearchTreemaNode.prototype.searchCallback = function(results) {
- var container, first, i, result, row, text, _i, _len;
- container = this.getSearchResultsEl().detach().empty();
- first = true;
- for (i = _i = 0, _len = results.length; _i < _len; i = ++_i) {
- result = results[i];
- row = $('').addClass('treema-search-result-row');
- text = this.formatDocument(result);
- if (text == null) {
- continue;
- }
- if (first) {
- row.addClass('treema-search-selected');
- }
- first = false;
- row.text(text);
- row.data('value', result);
- container.append(row);
- }
- if (!results.length) {
- container.append($('No results
'));
- }
- return this.getValEl().append(container);
- };
-
- DatabaseSearchTreemaNode.prototype.getSearchResultsEl = function() {
- return this.getValEl().find('.treema-search-results');
- };
-
- DatabaseSearchTreemaNode.prototype.getSelectedResultEl = function() {
- return this.getValEl().find('.treema-search-selected');
- };
-
- DatabaseSearchTreemaNode.prototype.saveChanges = function() {
- var selected;
- selected = this.getSelectedResultEl();
- if (!selected.length) {
- return;
- }
- return this.data = selected.data('value');
- };
-
- DatabaseSearchTreemaNode.prototype.onDownArrowPressed = function(e) {
- this.navigateSearch(1);
- return e.preventDefault();
- };
-
- DatabaseSearchTreemaNode.prototype.onUpArrowPressed = function(e) {
- e.preventDefault();
- return this.navigateSearch(-1);
- };
-
- DatabaseSearchTreemaNode.prototype.navigateSearch = function(offset) {
- var func, next, selected;
- selected = this.getSelectedResultEl();
- func = offset > 0 ? 'next' : 'prev';
- next = selected[func]('.treema-search-result-row');
- if (!next.length) {
- return;
- }
- selected.removeClass('treema-search-selected');
- return next.addClass('treema-search-selected');
- };
-
- DatabaseSearchTreemaNode.prototype.onClick = function(e) {
- var newSelection;
- newSelection = $(e.target).closest('.treema-search-result-row');
- if (!newSelection.length) {
- return DatabaseSearchTreemaNode.__super__.onClick.call(this, e);
- }
- this.getSelectedResultEl().removeClass('treema-search-selected');
- newSelection.addClass('treema-search-selected');
- this.saveChanges();
- return this.display();
- };
-
- DatabaseSearchTreemaNode.prototype.shouldTryToRemoveFromParent = function() {
- var selected;
- if (this.data != null) {
- return;
- }
- selected = this.getSelectedResultEl();
- return !selected.length;
- };
-
- return DatabaseSearchTreemaNode;
-
- })(TreemaNode);
- debounce = function(func, threshold, execAsap) {
- var timeout;
- timeout = null;
- return function() {
- var args, delayed, obj;
- args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
- obj = this;
- delayed = function() {
- if (!execAsap) {
- func.apply(obj, args);
- }
- return timeout = null;
- };
- if (timeout) {
- clearTimeout(timeout);
- } else if (execAsap) {
- func.apply(obj, args);
- }
- return timeout = setTimeout(delayed, threshold || 100);
- };
- };
- DatabaseSearchTreemaNode.prototype.search = debounce(DatabaseSearchTreemaNode.prototype.search, 200);
- window.DatabaseSearchTreemaNode = DatabaseSearchTreemaNode;
- TreemaNode.setNodeSubclass('ace', AceNode = (function(_super) {
- __extends(AceNode, _super);
-
- function AceNode() {
- _ref3 = AceNode.__super__.constructor.apply(this, arguments);
- return _ref3;
- }
-
- AceNode.prototype.valueClass = 'treema-ace treema-multiline';
-
- AceNode.prototype.getDefaultValue = function() {
- return '';
- };
-
- AceNode.prototype.buildValueForDisplay = function(valEl) {
- var pre, _ref4;
- if ((_ref4 = this.editor) != null) {
- _ref4.destroy();
- }
- pre = $('').text(this.data);
- return valEl.append(pre);
- };
-
- AceNode.prototype.buildValueForEditing = function(valEl) {
- var d;
- d = $('').text(this.data);
- valEl.append(d);
- this.editor = ace.edit(d[0]);
- this.editor.setReadOnly(false);
- if (this.schema.aceMode != null) {
- this.editor.getSession().setMode(this.schema.aceMode);
- }
- if (this.schema.aceTabSize != null) {
- this.editor.getSession().setTabSize(this.schema.aceTabSize);
- }
- if (this.schema.aceTheme != null) {
- this.editor.setTheme(this.schema.aceTheme);
- }
- return valEl.find('textarea').focus();
- };
-
- AceNode.prototype.saveChanges = function() {
- return this.data = this.editor.getValue();
- };
-
- AceNode.prototype.onTabPressed = function() {};
-
- AceNode.prototype.onEnterPressed = function() {};
-
- return AceNode;
-
- })(TreemaNode));
- return TreemaNode.setNodeSubclass('long-string', LongStringNode = (function(_super) {
- __extends(LongStringNode, _super);
-
- function LongStringNode() {
- _ref4 = LongStringNode.__super__.constructor.apply(this, arguments);
- return _ref4;
- }
-
- LongStringNode.prototype.valueClass = 'treema-long-string treema-multiline';
-
- LongStringNode.prototype.getDefaultValue = function() {
- return '';
- };
-
- LongStringNode.prototype.buildValueForDisplay = function(valEl) {
- var text;
- text = this.data;
- text = text.replace(/\n/g, '
');
- return valEl.append($("").html(text));
- };
-
- LongStringNode.prototype.buildValueForEditing = function(valEl) {
- var input;
- input = $('');
- if (this.data !== null) {
- input.val(this.data);
- }
- valEl.append(input);
- input.focus().select();
- input.blur(this.onEditInputBlur);
- return input;
- };
-
- LongStringNode.prototype.saveChanges = function(valEl) {
- var input;
- input = valEl.find('textarea');
- return this.data = input.val();
- };
-
- return LongStringNode;
-
- })(TreemaNode));
-})();
-;(function($) {
- return $.fn[TreemaNode.pluginName] = function(options) {
- var element;
- if (this.length === 0) {
- return null;
- }
- element = $(this[0]);
- return TreemaNode.make(element, options);
- };
-})(jQuery);
-;
-//@ sourceMappingURL=treema.js.map
\ No newline at end of file
diff --git a/vendor/scripts/tv4.js b/vendor/scripts/tv4.js
deleted file mode 100644
index bae299f94..000000000
--- a/vendor/scripts/tv4.js
+++ /dev/null
@@ -1,1150 +0,0 @@
-/*
-Author: Geraint Luff and others
-Year: 2013
-
-This code is released into the "public domain" by its author(s). Anybody may use, alter and distribute the code without restriction. The author makes no guarantees, and takes no liability of any kind for use of this code.
-
-If you find a bug or make an improvement, it would be courteous to let the author know, but it is not compulsory.
-*/
-(function (global) {
-'use strict';
-
-// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FObject%2Fkeys
-if (!Object.keys) {
- Object.keys = (function () {
- var hasOwnProperty = Object.prototype.hasOwnProperty,
- hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'),
- dontEnums = [
- 'toString',
- 'toLocaleString',
- 'valueOf',
- 'hasOwnProperty',
- 'isPrototypeOf',
- 'propertyIsEnumerable',
- 'constructor'
- ],
- dontEnumsLength = dontEnums.length;
-
- return function (obj) {
- if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) {
- throw new TypeError('Object.keys called on non-object');
- }
-
- var result = [];
-
- for (var prop in obj) {
- if (hasOwnProperty.call(obj, prop)) {
- result.push(prop);
- }
- }
-
- if (hasDontEnumBug) {
- for (var i=0; i < dontEnumsLength; i++) {
- if (hasOwnProperty.call(obj, dontEnums[i])) {
- result.push(dontEnums[i]);
- }
- }
- }
- return result;
- };
- })();
-}
-// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create
-if (!Object.create) {
- Object.create = (function(){
- function F(){}
-
- return function(o){
- if (arguments.length !== 1) {
- throw new Error('Object.create implementation only accepts one parameter.');
- }
- F.prototype = o;
- return new F();
- };
- })();
-}
-// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FArray%2FisArray
-if(!Array.isArray) {
- Array.isArray = function (vArg) {
- return Object.prototype.toString.call(vArg) === "[object Array]";
- };
-}
-// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FArray%2FindexOf
-if (!Array.prototype.indexOf) {
- Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
- if (this === null) {
- throw new TypeError();
- }
- var t = Object(this);
- var len = t.length >>> 0;
-
- if (len === 0) {
- return -1;
- }
- var n = 0;
- if (arguments.length > 1) {
- n = Number(arguments[1]);
- if (n !== n) { // shortcut for verifying if it's NaN
- n = 0;
- } else if (n !== 0 && n !== Infinity && n !== -Infinity) {
- n = (n > 0 || -1) * Math.floor(Math.abs(n));
- }
- }
- if (n >= len) {
- return -1;
- }
- var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
- for (; k < len; k++) {
- if (k in t && t[k] === searchElement) {
- return k;
- }
- }
- return -1;
- };
-}
-
-// Grungey Object.isFrozen hack
-if (!Object.isFrozen) {
- Object.isFrozen = function (obj) {
- var key = "tv4_test_frozen_key";
- while (obj.hasOwnProperty(key)) {
- key += Math.random();
- }
- try {
- obj[key] = true;
- delete obj[key];
- return false;
- } catch (e) {
- return true;
- }
- };
-}
-var ValidatorContext = function ValidatorContext(parent, collectMultiple, errorMessages, checkRecursive) {
- this.missing = [];
- this.missingMap = {};
- this.formatValidators = parent ? Object.create(parent.formatValidators) : {};
- this.schemas = parent ? Object.create(parent.schemas) : {};
- this.collectMultiple = collectMultiple;
- this.errors = [];
- this.handleError = collectMultiple ? this.collectError : this.returnError;
- if (checkRecursive) {
- this.checkRecursive = true;
- this.scanned = [];
- this.scannedFrozen = [];
- this.scannedFrozenSchemas = [];
- this.key = 'tv4_validation_id';
- }
- this.errorMessages = errorMessages;
-};
-ValidatorContext.prototype.createError = function (code, messageParams, dataPath, schemaPath, subErrors) {
- var messageTemplate = this.errorMessages[code] || ErrorMessagesDefault[code];
- if (typeof messageTemplate !== 'string') {
- return new ValidationError(code, "Unknown error code " + code + ": " + JSON.stringify(messageParams), dataPath, schemaPath, subErrors);
- }
- // Adapted from Crockford's supplant()
- var message = messageTemplate.replace(/\{([^{}]*)\}/g, function (whole, varName) {
- var subValue = messageParams[varName];
- return typeof subValue === 'string' || typeof subValue === 'number' ? subValue : whole;
- });
- return new ValidationError(code, message, dataPath, schemaPath, subErrors);
-};
-ValidatorContext.prototype.returnError = function (error) {
- return error;
-};
-ValidatorContext.prototype.collectError = function (error) {
- if (error) {
- this.errors.push(error);
- }
- return null;
-};
-ValidatorContext.prototype.prefixErrors = function (startIndex, dataPath, schemaPath) {
- for (var i = startIndex; i < this.errors.length; i++) {
- this.errors[i] = this.errors[i].prefixWith(dataPath, schemaPath);
- }
- return this;
-};
-
-ValidatorContext.prototype.addFormat = function (format, validator) {
- if (typeof format === 'object') {
- for (var key in format) {
- this.addFormat(key, format[key]);
- }
- return this;
- }
- this.formatValidators[format] = validator;
-};
-ValidatorContext.prototype.getSchema = function (url) {
- var schema;
- if (this.schemas[url] !== undefined) {
- schema = this.schemas[url];
- return schema;
- }
- var baseUrl = url;
- var fragment = "";
- if (url.indexOf('#') !== -1) {
- fragment = url.substring(url.indexOf("#") + 1);
- baseUrl = url.substring(0, url.indexOf("#"));
- }
- if (typeof this.schemas[baseUrl] === 'object') {
- schema = this.schemas[baseUrl];
- var pointerPath = decodeURIComponent(fragment);
- if (pointerPath === "") {
- return schema;
- } else if (pointerPath.charAt(0) !== "/") {
- return undefined;
- }
- var parts = pointerPath.split("/").slice(1);
- for (var i = 0; i < parts.length; i++) {
- var component = parts[i].replace("~1", "/").replace("~0", "~");
- if (schema[component] === undefined) {
- schema = undefined;
- break;
- }
- schema = schema[component];
- }
- if (schema !== undefined) {
- return schema;
- }
- }
- if (this.missing[baseUrl] === undefined) {
- this.missing.push(baseUrl);
- this.missing[baseUrl] = baseUrl;
- this.missingMap[baseUrl] = baseUrl;
- }
-};
-ValidatorContext.prototype.searchSchemas = function (schema, url) {
- if (schema === undefined || schema === null) { return; }
- if (typeof schema.id === "string") {
- if (isTrustedUrl(url, schema.id)) {
- if (this.schemas[schema.id] === undefined) {
- this.schemas[schema.id] = schema;
- }
- }
- }
- if (typeof schema === "object") {
- for (var key in schema) {
- if (key !== "enum") {
- if (typeof schema[key] === "object") {
- this.searchSchemas(schema[key], url);
- } else if (key === "$ref") {
- var uri = getDocumentUri(schema[key]);
- if (uri && this.schemas[uri] === undefined && this.missingMap[uri] === undefined) {
- this.missingMap[uri] = uri;
- }
- }
- }
- }
- }
-};
-ValidatorContext.prototype.addSchema = function (url, schema) {
- //overload
- if (typeof schema === 'undefined') {
- if (typeof url === 'object' && typeof url.id === 'string') {
- schema = url;
- url = schema.id;
- }
- else {
- return;
- }
- }
- if (url = getDocumentUri(url) + "#") {
- // Remove empty fragment
- url = getDocumentUri(url);
- }
- this.schemas[url] = schema;
- delete this.missingMap[url];
- normSchema(schema, url);
- this.searchSchemas(schema, url);
-};
-
-ValidatorContext.prototype.getSchemaMap = function () {
- var map = {};
- for (var key in this.schemas) {
- map[key] = this.schemas[key];
- }
- return map;
-};
-
-ValidatorContext.prototype.getSchemaUris = function (filterRegExp) {
- var list = [];
- for (var key in this.schemas) {
- if (!filterRegExp || filterRegExp.test(key)) {
- list.push(key);
- }
- }
- return list;
-};
-
-ValidatorContext.prototype.getMissingUris = function (filterRegExp) {
- var list = [];
- for (var key in this.missingMap) {
- if (!filterRegExp || filterRegExp.test(key)) {
- list.push(key);
- }
- }
- return list;
-};
-
-ValidatorContext.prototype.dropSchemas = function () {
- this.schemas = {};
- this.reset();
-};
-ValidatorContext.prototype.reset = function () {
- this.missing = [];
- this.missingMap = {};
- this.errors = [];
-};
-
-ValidatorContext.prototype.validateAll = function (data, schema, dataPathParts, schemaPathParts) {
- var topLevel;
- if (schema['$ref'] !== undefined) {
- schema = this.getSchema(schema['$ref']);
- if (!schema) {
- return null;
- }
- }
-
- if (this.checkRecursive && (typeof data) === 'object') {
- topLevel = !this.scanned.length;
- if (data[this.key] && data[this.key].indexOf(schema) !== -1) { return null; }
- var frozenIndex;
- if (Object.isFrozen(data)) {
- frozenIndex = this.scannedFrozen.indexOf(data);
- if (frozenIndex !== -1 && this.scannedFrozenSchemas[frozenIndex].indexOf(schema) !== -1) { return null; }
- }
- this.scanned.push(data);
- if (Object.isFrozen(data)) {
- if (frozenIndex === -1) {
- frozenIndex = this.scannedFrozen.length;
- this.scannedFrozen.push(data);
- this.scannedFrozenSchemas.push([]);
- }
- this.scannedFrozenSchemas[frozenIndex].push(schema);
- } else {
- if (!data[this.key]) {
- try {
- Object.defineProperty(data, this.key, {
- value: [],
- configurable: true
- });
- } catch (e) {
- //IE 7/8 workaround
- data[this.key] = [];
- }
- }
- data[this.key].push(schema);
- }
- }
-
- var errorCount = this.errors.length;
- var error = this.validateBasic(data, schema)
- || this.validateNumeric(data, schema)
- || this.validateString(data, schema)
- || this.validateArray(data, schema)
- || this.validateObject(data, schema)
- || this.validateCombinations(data, schema)
- || this.validateFormat(data, schema)
- || null;
-
- if (topLevel) {
- while (this.scanned.length) {
- var item = this.scanned.pop();
- delete item[this.key];
- }
- this.scannedFrozen = [];
- this.scannedFrozenSchemas = [];
- }
-
- if (error || errorCount !== this.errors.length) {
- while ((dataPathParts && dataPathParts.length) || (schemaPathParts && schemaPathParts.length)) {
- var dataPart = (dataPathParts && dataPathParts.length) ? "" + dataPathParts.pop() : null;
- var schemaPart = (schemaPathParts && schemaPathParts.length) ? "" + schemaPathParts.pop() : null;
- if (error) {
- error = error.prefixWith(dataPart, schemaPart);
- }
- this.prefixErrors(errorCount, dataPart, schemaPart);
- }
- }
-
- return this.handleError(error);
-};
-ValidatorContext.prototype.validateFormat = function (data, schema) {
- if (typeof schema.format !== 'string' || !this.formatValidators[schema.format]) {
- return null;
- }
- var errorMessage = this.formatValidators[schema.format].call(null, data, schema);
- if (typeof errorMessage === 'string' || typeof errorMessage === 'number') {
- return this.createError(ErrorCodes.FORMAT_CUSTOM, {message: errorMessage}).prefixWith(null, "format");
- } else if (errorMessage && typeof errorMessage === 'object') {
- return this.createError(ErrorCodes.FORMAT_CUSTOM, {message: errorMessage.message || "?"}, errorMessage.dataPath || null, errorMessage.schemaPath || "/format");
- }
- return null;
-};
-
-function recursiveCompare(A, B) {
- if (A === B) {
- return true;
- }
- if (typeof A === "object" && typeof B === "object") {
- if (Array.isArray(A) !== Array.isArray(B)) {
- return false;
- } else if (Array.isArray(A)) {
- if (A.length !== B.length) {
- return false;
- }
- for (var i = 0; i < A.length; i++) {
- if (!recursiveCompare(A[i], B[i])) {
- return false;
- }
- }
- } else {
- var key;
- for (key in A) {
- if (B[key] === undefined && A[key] !== undefined) {
- return false;
- }
- }
- for (key in B) {
- if (A[key] === undefined && B[key] !== undefined) {
- return false;
- }
- }
- for (key in A) {
- if (!recursiveCompare(A[key], B[key])) {
- return false;
- }
- }
- }
- return true;
- }
- return false;
-}
-
-ValidatorContext.prototype.validateBasic = function validateBasic(data, schema) {
- var error;
- if (error = this.validateType(data, schema)) {
- return error.prefixWith(null, "type");
- }
- if (error = this.validateEnum(data, schema)) {
- return error.prefixWith(null, "type");
- }
- return null;
-};
-
-ValidatorContext.prototype.validateType = function validateType(data, schema) {
- if (schema.type === undefined) {
- return null;
- }
- var dataType = typeof data;
- if (data === null) {
- dataType = "null";
- } else if (Array.isArray(data)) {
- dataType = "array";
- }
- var allowedTypes = schema.type;
- if (typeof allowedTypes !== "object") {
- allowedTypes = [allowedTypes];
- }
-
- for (var i = 0; i < allowedTypes.length; i++) {
- var type = allowedTypes[i];
- if (type === dataType || (type === "integer" && dataType === "number" && (data % 1 === 0))) {
- return null;
- }
- }
- return this.createError(ErrorCodes.INVALID_TYPE, {type: dataType, expected: allowedTypes.join("/")});
-};
-
-ValidatorContext.prototype.validateEnum = function validateEnum(data, schema) {
- if (schema["enum"] === undefined) {
- return null;
- }
- for (var i = 0; i < schema["enum"].length; i++) {
- var enumVal = schema["enum"][i];
- if (recursiveCompare(data, enumVal)) {
- return null;
- }
- }
- return this.createError(ErrorCodes.ENUM_MISMATCH, {value: (typeof JSON !== 'undefined') ? JSON.stringify(data) : data});
-};
-
-ValidatorContext.prototype.validateNumeric = function validateNumeric(data, schema) {
- return this.validateMultipleOf(data, schema)
- || this.validateMinMax(data, schema)
- || null;
-};
-
-ValidatorContext.prototype.validateMultipleOf = function validateMultipleOf(data, schema) {
- var multipleOf = schema.multipleOf || schema.divisibleBy;
- if (multipleOf === undefined) {
- return null;
- }
- if (typeof data === "number") {
- if (data % multipleOf !== 0) {
- return this.createError(ErrorCodes.NUMBER_MULTIPLE_OF, {value: data, multipleOf: multipleOf});
- }
- }
- return null;
-};
-
-ValidatorContext.prototype.validateMinMax = function validateMinMax(data, schema) {
- if (typeof data !== "number") {
- return null;
- }
- if (schema.minimum !== undefined) {
- if (data < schema.minimum) {
- return this.createError(ErrorCodes.NUMBER_MINIMUM, {value: data, minimum: schema.minimum}).prefixWith(null, "minimum");
- }
- if (schema.exclusiveMinimum && data === schema.minimum) {
- return this.createError(ErrorCodes.NUMBER_MINIMUM_EXCLUSIVE, {value: data, minimum: schema.minimum}).prefixWith(null, "exclusiveMinimum");
- }
- }
- if (schema.maximum !== undefined) {
- if (data > schema.maximum) {
- return this.createError(ErrorCodes.NUMBER_MAXIMUM, {value: data, maximum: schema.maximum}).prefixWith(null, "maximum");
- }
- if (schema.exclusiveMaximum && data === schema.maximum) {
- return this.createError(ErrorCodes.NUMBER_MAXIMUM_EXCLUSIVE, {value: data, maximum: schema.maximum}).prefixWith(null, "exclusiveMaximum");
- }
- }
- return null;
-};
-
-ValidatorContext.prototype.validateString = function validateString(data, schema) {
- return this.validateStringLength(data, schema)
- || this.validateStringPattern(data, schema)
- || null;
-};
-
-ValidatorContext.prototype.validateStringLength = function validateStringLength(data, schema) {
- if (typeof data !== "string") {
- return null;
- }
- if (schema.minLength !== undefined) {
- if (data.length < schema.minLength) {
- return this.createError(ErrorCodes.STRING_LENGTH_SHORT, {length: data.length, minimum: schema.minLength}).prefixWith(null, "minLength");
- }
- }
- if (schema.maxLength !== undefined) {
- if (data.length > schema.maxLength) {
- return this.createError(ErrorCodes.STRING_LENGTH_LONG, {length: data.length, maximum: schema.maxLength}).prefixWith(null, "maxLength");
- }
- }
- return null;
-};
-
-ValidatorContext.prototype.validateStringPattern = function validateStringPattern(data, schema) {
- if (typeof data !== "string" || schema.pattern === undefined) {
- return null;
- }
- var regexp = new RegExp(schema.pattern);
- if (!regexp.test(data)) {
- return this.createError(ErrorCodes.STRING_PATTERN, {pattern: schema.pattern}).prefixWith(null, "pattern");
- }
- return null;
-};
-ValidatorContext.prototype.validateArray = function validateArray(data, schema) {
- if (!Array.isArray(data)) {
- return null;
- }
- return this.validateArrayLength(data, schema)
- || this.validateArrayUniqueItems(data, schema)
- || this.validateArrayItems(data, schema)
- || null;
-};
-
-ValidatorContext.prototype.validateArrayLength = function validateArrayLength(data, schema) {
- var error;
- if (schema.minItems !== undefined) {
- if (data.length < schema.minItems) {
- error = (this.createError(ErrorCodes.ARRAY_LENGTH_SHORT, {length: data.length, minimum: schema.minItems})).prefixWith(null, "minItems");
- if (this.handleError(error)) {
- return error;
- }
- }
- }
- if (schema.maxItems !== undefined) {
- if (data.length > schema.maxItems) {
- error = (this.createError(ErrorCodes.ARRAY_LENGTH_LONG, {length: data.length, maximum: schema.maxItems})).prefixWith(null, "maxItems");
- if (this.handleError(error)) {
- return error;
- }
- }
- }
- return null;
-};
-
-ValidatorContext.prototype.validateArrayUniqueItems = function validateArrayUniqueItems(data, schema) {
- if (schema.uniqueItems) {
- for (var i = 0; i < data.length; i++) {
- for (var j = i + 1; j < data.length; j++) {
- if (recursiveCompare(data[i], data[j])) {
- var error = (this.createError(ErrorCodes.ARRAY_UNIQUE, {match1: i, match2: j})).prefixWith(null, "uniqueItems");
- if (this.handleError(error)) {
- return error;
- }
- }
- }
- }
- }
- return null;
-};
-
-ValidatorContext.prototype.validateArrayItems = function validateArrayItems(data, schema) {
- if (schema.items === undefined) {
- return null;
- }
- var error, i;
- if (Array.isArray(schema.items)) {
- for (i = 0; i < data.length; i++) {
- if (i < schema.items.length) {
- if (error = this.validateAll(data[i], schema.items[i], [i], ["items", i])) {
- return error;
- }
- } else if (schema.additionalItems !== undefined) {
- if (typeof schema.additionalItems === "boolean") {
- if (!schema.additionalItems) {
- error = (this.createError(ErrorCodes.ARRAY_ADDITIONAL_ITEMS, {})).prefixWith("" + i, "additionalItems");
- if (this.handleError(error)) {
- return error;
- }
- }
- } else if (error = this.validateAll(data[i], schema.additionalItems, [i], ["additionalItems"])) {
- return error;
- }
- }
- }
- } else {
- for (i = 0; i < data.length; i++) {
- if (error = this.validateAll(data[i], schema.items, [i], ["items"])) {
- return error;
- }
- }
- }
- return null;
-};
-
-ValidatorContext.prototype.validateObject = function validateObject(data, schema) {
- if (typeof data !== "object" || data === null || Array.isArray(data)) {
- return null;
- }
- return this.validateObjectMinMaxProperties(data, schema)
- || this.validateObjectRequiredProperties(data, schema)
- || this.validateObjectProperties(data, schema)
- || this.validateObjectDependencies(data, schema)
- || null;
-};
-
-ValidatorContext.prototype.validateObjectMinMaxProperties = function validateObjectMinMaxProperties(data, schema) {
- var keys = Object.keys(data);
- var error;
- if (schema.minProperties !== undefined) {
- if (keys.length < schema.minProperties) {
- error = this.createError(ErrorCodes.OBJECT_PROPERTIES_MINIMUM, {propertyCount: keys.length, minimum: schema.minProperties}).prefixWith(null, "minProperties");
- if (this.handleError(error)) {
- return error;
- }
- }
- }
- if (schema.maxProperties !== undefined) {
- if (keys.length > schema.maxProperties) {
- error = this.createError(ErrorCodes.OBJECT_PROPERTIES_MAXIMUM, {propertyCount: keys.length, maximum: schema.maxProperties}).prefixWith(null, "maxProperties");
- if (this.handleError(error)) {
- return error;
- }
- }
- }
- return null;
-};
-
-ValidatorContext.prototype.validateObjectRequiredProperties = function validateObjectRequiredProperties(data, schema) {
- if (schema.required !== undefined) {
- for (var i = 0; i < schema.required.length; i++) {
- var key = schema.required[i];
- if (data[key] === undefined) {
- var error = this.createError(ErrorCodes.OBJECT_REQUIRED, {key: key}).prefixWith(null, "" + i).prefixWith(null, "required");
- if (this.handleError(error)) {
- return error;
- }
- }
- }
- }
- return null;
-};
-
-ValidatorContext.prototype.validateObjectProperties = function validateObjectProperties(data, schema) {
- var error;
- for (var key in data) {
- var foundMatch = false;
- if (schema.properties !== undefined && schema.properties[key] !== undefined) {
- foundMatch = true;
- if (error = this.validateAll(data[key], schema.properties[key], [key], ["properties", key])) {
- return error;
- }
- }
- if (schema.patternProperties !== undefined) {
- for (var patternKey in schema.patternProperties) {
- var regexp = new RegExp(patternKey);
- if (regexp.test(key)) {
- foundMatch = true;
- if (error = this.validateAll(data[key], schema.patternProperties[patternKey], [key], ["patternProperties", patternKey])) {
- return error;
- }
- }
- }
- }
- if (!foundMatch && schema.additionalProperties !== undefined) {
- if (typeof schema.additionalProperties === "boolean") {
- if (!schema.additionalProperties) {
- error = this.createError(ErrorCodes.OBJECT_ADDITIONAL_PROPERTIES, {}).prefixWith(key, "additionalProperties");
- if (this.handleError(error)) {
- return error;
- }
- }
- } else {
- if (error = this.validateAll(data[key], schema.additionalProperties, [key], ["additionalProperties"])) {
- return error;
- }
- }
- }
- }
- return null;
-};
-
-ValidatorContext.prototype.validateObjectDependencies = function validateObjectDependencies(data, schema) {
- var error;
- if (schema.dependencies !== undefined) {
- for (var depKey in schema.dependencies) {
- if (data[depKey] !== undefined) {
- var dep = schema.dependencies[depKey];
- if (typeof dep === "string") {
- if (data[dep] === undefined) {
- error = this.createError(ErrorCodes.OBJECT_DEPENDENCY_KEY, {key: depKey, missing: dep}).prefixWith(null, depKey).prefixWith(null, "dependencies");
- if (this.handleError(error)) {
- return error;
- }
- }
- } else if (Array.isArray(dep)) {
- for (var i = 0; i < dep.length; i++) {
- var requiredKey = dep[i];
- if (data[requiredKey] === undefined) {
- error = this.createError(ErrorCodes.OBJECT_DEPENDENCY_KEY, {key: depKey, missing: requiredKey}).prefixWith(null, "" + i).prefixWith(null, depKey).prefixWith(null, "dependencies");
- if (this.handleError(error)) {
- return error;
- }
- }
- }
- } else {
- if (error = this.validateAll(data, dep, [], ["dependencies", depKey])) {
- return error;
- }
- }
- }
- }
- }
- return null;
-};
-
-ValidatorContext.prototype.validateCombinations = function validateCombinations(data, schema) {
- return this.validateAllOf(data, schema)
- || this.validateAnyOf(data, schema)
- || this.validateOneOf(data, schema)
- || this.validateNot(data, schema)
- || null;
-};
-
-ValidatorContext.prototype.validateAllOf = function validateAllOf(data, schema) {
- if (schema.allOf === undefined) {
- return null;
- }
- var error;
- for (var i = 0; i < schema.allOf.length; i++) {
- var subSchema = schema.allOf[i];
- if (error = this.validateAll(data, subSchema, [], ["allOf", i])) {
- return error;
- }
- }
- return null;
-};
-
-ValidatorContext.prototype.validateAnyOf = function validateAnyOf(data, schema) {
- if (schema.anyOf === undefined) {
- return null;
- }
- var errors = [];
- var startErrorCount = this.errors.length;
- for (var i = 0; i < schema.anyOf.length; i++) {
- var subSchema = schema.anyOf[i];
-
- var errorCount = this.errors.length;
- var error = this.validateAll(data, subSchema, [], ["anyOf", i]);
-
- if (error === null && errorCount === this.errors.length) {
- this.errors = this.errors.slice(0, startErrorCount);
- return null;
- }
- if (error) {
- errors.push(error.prefixWith(null, "" + i).prefixWith(null, "anyOf"));
- }
- }
- errors = errors.concat(this.errors.slice(startErrorCount));
- this.errors = this.errors.slice(0, startErrorCount);
- return this.createError(ErrorCodes.ANY_OF_MISSING, {}, "", "/anyOf", errors);
-};
-
-ValidatorContext.prototype.validateOneOf = function validateOneOf(data, schema) {
- if (schema.oneOf === undefined) {
- return null;
- }
- var validIndex = null;
- var errors = [];
- var startErrorCount = this.errors.length;
- for (var i = 0; i < schema.oneOf.length; i++) {
- var subSchema = schema.oneOf[i];
-
- var errorCount = this.errors.length;
- var error = this.validateAll(data, subSchema, [], ["oneOf", i]);
-
- if (error === null && errorCount === this.errors.length) {
- if (validIndex === null) {
- validIndex = i;
- } else {
- this.errors = this.errors.slice(0, startErrorCount);
- return this.createError(ErrorCodes.ONE_OF_MULTIPLE, {index1: validIndex, index2: i}, "", "/oneOf");
- }
- } else if (error) {
- errors.push(error.prefixWith(null, "" + i).prefixWith(null, "oneOf"));
- }
- }
- if (validIndex === null) {
- errors = errors.concat(this.errors.slice(startErrorCount));
- this.errors = this.errors.slice(0, startErrorCount);
- return this.createError(ErrorCodes.ONE_OF_MISSING, {}, "", "/oneOf", errors);
- } else {
- this.errors = this.errors.slice(0, startErrorCount);
- }
- return null;
-};
-
-ValidatorContext.prototype.validateNot = function validateNot(data, schema) {
- if (schema.not === undefined) {
- return null;
- }
- var oldErrorCount = this.errors.length;
- var error = this.validateAll(data, schema.not);
- var notErrors = this.errors.slice(oldErrorCount);
- this.errors = this.errors.slice(0, oldErrorCount);
- if (error === null && notErrors.length === 0) {
- return this.createError(ErrorCodes.NOT_PASSED, {}, "", "/not");
- }
- return null;
-};
-
-// parseURI() and resolveUrl() are from https://gist.github.com/1088850
-// - released as public domain by author ("Yaffle") - see comments on gist
-
-function parseURI(url) {
- var m = String(url).replace(/^\s+|\s+$/g, '').match(/^([^:\/?#]+:)?(\/\/(?:[^:@]*(?::[^:@]*)?@)?(([^:\/?#]*)(?::(\d*))?))?([^?#]*)(\?[^#]*)?(#[\s\S]*)?/);
- // authority = '//' + user + ':' + pass '@' + hostname + ':' port
- return (m ? {
- href : m[0] || '',
- protocol : m[1] || '',
- authority: m[2] || '',
- host : m[3] || '',
- hostname : m[4] || '',
- port : m[5] || '',
- pathname : m[6] || '',
- search : m[7] || '',
- hash : m[8] || ''
- } : null);
-}
-
-function resolveUrl(base, href) {// RFC 3986
-
- function removeDotSegments(input) {
- var output = [];
- input.replace(/^(\.\.?(\/|$))+/, '')
- .replace(/\/(\.(\/|$))+/g, '/')
- .replace(/\/\.\.$/, '/../')
- .replace(/\/?[^\/]*/g, function (p) {
- if (p === '/..') {
- output.pop();
- } else {
- output.push(p);
- }
- });
- return output.join('').replace(/^\//, input.charAt(0) === '/' ? '/' : '');
- }
-
- href = parseURI(href || '');
- base = parseURI(base || '');
-
- return !href || !base ? null : (href.protocol || base.protocol) +
- (href.protocol || href.authority ? href.authority : base.authority) +
- removeDotSegments(href.protocol || href.authority || href.pathname.charAt(0) === '/' ? href.pathname : (href.pathname ? ((base.authority && !base.pathname ? '/' : '') + base.pathname.slice(0, base.pathname.lastIndexOf('/') + 1) + href.pathname) : base.pathname)) +
- (href.protocol || href.authority || href.pathname ? href.search : (href.search || base.search)) +
- href.hash;
-}
-
-function getDocumentUri(uri) {
- return uri.split('#')[0];
-}
-function normSchema(schema, baseUri) {
- if (schema === undefined || schema === null) { return; }
- if (baseUri === undefined) {
- baseUri = schema.id;
- } else if (typeof schema.id === "string") {
- baseUri = resolveUrl(baseUri, schema.id);
- schema.id = baseUri;
- }
- if (typeof schema === "object") {
- if (Array.isArray(schema)) {
- for (var i = 0; i < schema.length; i++) {
- normSchema(schema[i], baseUri);
- }
- } else if (typeof schema['$ref'] === "string") {
- schema['$ref'] = resolveUrl(baseUri, schema['$ref']);
- } else {
- for (var key in schema) {
- if (key !== "enum") {
- normSchema(schema[key], baseUri);
- }
- }
- }
- }
-}
-
-var ErrorCodes = {
- INVALID_TYPE: 0,
- ENUM_MISMATCH: 1,
- ANY_OF_MISSING: 10,
- ONE_OF_MISSING: 11,
- ONE_OF_MULTIPLE: 12,
- NOT_PASSED: 13,
- // Numeric errors
- NUMBER_MULTIPLE_OF: 100,
- NUMBER_MINIMUM: 101,
- NUMBER_MINIMUM_EXCLUSIVE: 102,
- NUMBER_MAXIMUM: 103,
- NUMBER_MAXIMUM_EXCLUSIVE: 104,
- // String errors
- STRING_LENGTH_SHORT: 200,
- STRING_LENGTH_LONG: 201,
- STRING_PATTERN: 202,
- // Object errors
- OBJECT_PROPERTIES_MINIMUM: 300,
- OBJECT_PROPERTIES_MAXIMUM: 301,
- OBJECT_REQUIRED: 302,
- OBJECT_ADDITIONAL_PROPERTIES: 303,
- OBJECT_DEPENDENCY_KEY: 304,
- // Array errors
- ARRAY_LENGTH_SHORT: 400,
- ARRAY_LENGTH_LONG: 401,
- ARRAY_UNIQUE: 402,
- ARRAY_ADDITIONAL_ITEMS: 403,
- // Format errors
- FORMAT_CUSTOM: 500
-};
-var ErrorMessagesDefault = {
- INVALID_TYPE: "invalid type: {type} (expected {expected})",
- ENUM_MISMATCH: "No enum match for: {value}",
- ANY_OF_MISSING: "Data does not match any schemas from \"anyOf\"",
- ONE_OF_MISSING: "Data does not match any schemas from \"oneOf\"",
- ONE_OF_MULTIPLE: "Data is valid against more than one schema from \"oneOf\": indices {index1} and {index2}",
- NOT_PASSED: "Data matches schema from \"not\"",
- // Numeric errors
- NUMBER_MULTIPLE_OF: "Value {value} is not a multiple of {multipleOf}",
- NUMBER_MINIMUM: "Value {value} is less than minimum {minimum}",
- NUMBER_MINIMUM_EXCLUSIVE: "Value {value} is equal to exclusive minimum {minimum}",
- NUMBER_MAXIMUM: "Value {value} is greater than maximum {maximum}",
- NUMBER_MAXIMUM_EXCLUSIVE: "Value {value} is equal to exclusive maximum {maximum}",
- // String errors
- STRING_LENGTH_SHORT: "String is too short ({length} chars), minimum {minimum}",
- STRING_LENGTH_LONG: "String is too long ({length} chars), maximum {maximum}",
- STRING_PATTERN: "String does not match pattern: {pattern}",
- // Object errors
- OBJECT_PROPERTIES_MINIMUM: "Too few properties defined ({propertyCount}), minimum {minimum}",
- OBJECT_PROPERTIES_MAXIMUM: "Too many properties defined ({propertyCount}), maximum {maximum}",
- OBJECT_REQUIRED: "Missing required property: {key}",
- OBJECT_ADDITIONAL_PROPERTIES: "Additional properties not allowed",
- OBJECT_DEPENDENCY_KEY: "Dependency failed - key must exist: {missing} (due to key: {key})",
- // Array errors
- ARRAY_LENGTH_SHORT: "Array is too short ({length}), minimum {minimum}",
- ARRAY_LENGTH_LONG: "Array is too long ({length}), maximum {maximum}",
- ARRAY_UNIQUE: "Array items are not unique (indices {match1} and {match2})",
- ARRAY_ADDITIONAL_ITEMS: "Additional items not allowed",
- // Format errors
- FORMAT_CUSTOM: "Format validation failed ({message})"
-};
-
-function ValidationError(code, message, dataPath, schemaPath, subErrors) {
- if (code === undefined) {
- throw new Error ("No code supplied for error: "+ message);
- }
- this.code = code;
- this.message = message;
- this.dataPath = dataPath || "";
- this.schemaPath = schemaPath || "";
- this.subErrors = subErrors || null;
-}
-ValidationError.prototype = new Error();
-ValidationError.prototype.prefixWith = function (dataPrefix, schemaPrefix) {
- if (dataPrefix !== null) {
- dataPrefix = dataPrefix.replace("~", "~0").replace("/", "~1");
- this.dataPath = "/" + dataPrefix + this.dataPath;
- }
- if (schemaPrefix !== null) {
- schemaPrefix = schemaPrefix.replace("~", "~0").replace("/", "~1");
- this.schemaPath = "/" + schemaPrefix + this.schemaPath;
- }
- if (this.subErrors !== null) {
- for (var i = 0; i < this.subErrors.length; i++) {
- this.subErrors[i].prefixWith(dataPrefix, schemaPrefix);
- }
- }
- return this;
-};
-
-function isTrustedUrl(baseUrl, testUrl) {
- if(testUrl.substring(0, baseUrl.length) === baseUrl){
- var remainder = testUrl.substring(baseUrl.length);
- if ((testUrl.length > 0 && testUrl.charAt(baseUrl.length - 1) === "/")
- || remainder.charAt(0) === "#"
- || remainder.charAt(0) === "?") {
- return true;
- }
- }
- return false;
-}
-
-var languages = {};
-function createApi(language) {
- var globalContext = new ValidatorContext();
- var currentLanguage = language || 'en';
- var api = {
- addFormat: function () {
- globalContext.addFormat.apply(globalContext, arguments);
- },
- language: function (code) {
- if (!code) {
- return currentLanguage;
- }
- if (!languages[code]) {
- code = code.split('-')[0]; // fall back to base language
- }
- if (languages[code]) {
- currentLanguage = code;
- return code; // so you can tell if fall-back has happened
- }
- return false;
- },
- addLanguage: function (code, messageMap) {
- var key;
- for (key in ErrorCodes) {
- if (messageMap[key] && !messageMap[ErrorCodes[key]]) {
- messageMap[ErrorCodes[key]] = messageMap[key];
- }
- }
- var rootCode = code.split('-')[0];
- if (!languages[rootCode]) { // use for base language if not yet defined
- languages[code] = messageMap;
- languages[rootCode] = messageMap;
- } else {
- languages[code] = Object.create(languages[rootCode]);
- for (key in messageMap) {
- if (typeof languages[rootCode][key] === 'undefined') {
- languages[rootCode][key] = messageMap[key];
- }
- languages[code][key] = messageMap[key];
- }
- }
- return this;
- },
- freshApi: function (language) {
- var result = createApi();
- if (language) {
- result.language(language);
- }
- return result;
- },
- validate: function (data, schema, checkRecursive) {
- var context = new ValidatorContext(globalContext, false, languages[currentLanguage], checkRecursive);
- if (typeof schema === "string") {
- schema = {"$ref": schema};
- }
- context.addSchema("", schema);
- var error = context.validateAll(data, schema);
- this.error = error;
- this.missing = context.missing;
- this.valid = (error === null);
- return this.valid;
- },
- validateResult: function () {
- var result = {};
- this.validate.apply(result, arguments);
- return result;
- },
- validateMultiple: function (data, schema, checkRecursive) {
- var context = new ValidatorContext(globalContext, true, languages[currentLanguage], checkRecursive);
- if (typeof schema === "string") {
- schema = {"$ref": schema};
- }
- context.addSchema("", schema);
- context.validateAll(data, schema);
- var result = {};
- result.errors = context.errors;
- result.missing = context.missing;
- result.valid = (result.errors.length === 0);
- return result;
- },
- addSchema: function () {
- return globalContext.addSchema.apply(globalContext, arguments);
- },
- getSchema: function () {
- return globalContext.getSchema.apply(globalContext, arguments);
- },
- getSchemaMap: function () {
- return globalContext.getSchemaMap.apply(globalContext, arguments);
- },
- getSchemaUris: function () {
- return globalContext.getSchemaUris.apply(globalContext, arguments);
- },
- getMissingUris: function () {
- return globalContext.getMissingUris.apply(globalContext, arguments);
- },
- dropSchemas: function () {
- globalContext.dropSchemas.apply(globalContext, arguments);
- },
- reset: function () {
- globalContext.reset();
- this.error = null;
- this.missing = [];
- this.valid = true;
- },
- missing: [],
- error: null,
- valid: true,
- normSchema: normSchema,
- resolveUrl: resolveUrl,
- getDocumentUri: getDocumentUri,
- errorCodes: ErrorCodes
- };
- return api;
-}
-
-var tv4 = createApi();
-tv4.addLanguage('en-gb', ErrorMessagesDefault);
-
-//legacy property
-tv4.tv4 = tv4;
-
-if (typeof module !== 'undefined' && module.exports){
- module.exports = tv4;
-}
-else {
- global.tv4 = tv4;
-}
-
-})(this);
-
-//@ sourceMappingURL=tv4.js.map
\ No newline at end of file
diff --git a/vendor/styles/antiscroll.css b/vendor/styles/antiscroll.css
deleted file mode 100644
index 29626295a..000000000
--- a/vendor/styles/antiscroll.css
+++ /dev/null
@@ -1,63 +0,0 @@
-.antiscroll-wrap {
- display: inline-block;
- position: relative;
- overflow: hidden;
-}
-
-.antiscroll-scrollbar {
- background: gray;
- background: rgba(0, 0, 0, 0.5);
- -webkit-border-radius: 7px;
- -moz-border-radius: 7px;
- border-radius: 7px;
- -webkit-box-shadow: 0 0 1px #fff;
- -moz-box-shadow: 0 0 1px #fff;
- box-shadow: 0 0 1px #fff;
- position: absolute;
- opacity: 0;
- filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0);
- -webkit-transition: linear 300ms opacity;
- -moz-transition: linear 300ms opacity;
- -o-transition: linear 300ms opacity;
-}
-
-.antiscroll-scrollbar-shown {
- opacity: 1;
- filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100);
-}
-
-.antiscroll-scrollbar-horizontal {
- height: 7px;
- margin-left: 2px;
- bottom: 2px;
- left: 0;
-}
-
-.antiscroll-scrollbar-vertical {
- width: 7px;
- margin-top: 2px;
- right: 2px;
- top: 0;
-}
-
-.antiscroll-inner {
- overflow: scroll;
-}
-
-/** A bug in Chrome 25 on Lion requires each selector to have their own
- blocks. E.g. the following:
-
- .antiscroll-inner::-webkit-scrollbar, .antiscroll-inner::scrollbar {...}
-
- causes the width and height rules to be ignored by the browser resulting
- in both native and antiscroll scrollbars appearing at the same time.
- */
-.antiscroll-inner::-webkit-scrollbar {
- width: 0;
- height: 0;
-}
-
-.antiscroll-inner::scrollbar {
- width: 0;
- height: 0;
-}
diff --git a/vendor/styles/diffview.css b/vendor/styles/diffview.css
new file mode 100644
index 000000000..811a593b7
--- /dev/null
+++ b/vendor/styles/diffview.css
@@ -0,0 +1,83 @@
+/*
+This is part of jsdifflib v1.0.
+
+Copyright 2007 - 2011 Chas Emerick . All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are
+permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this list of
+ conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ of conditions and the following disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY Chas Emerick ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Chas Emerick OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The views and conclusions contained in the software and documentation are those of the
+authors and should not be interpreted as representing official policies, either expressed
+or implied, of Chas Emerick.
+*/
+table.diff {
+ border-collapse:collapse;
+ border:1px solid darkgray;
+ white-space:pre-wrap
+}
+table.diff tbody {
+ font-family:Courier, monospace
+}
+table.diff tbody th {
+ font-family:verdana,arial,'Bitstream Vera Sans',helvetica,sans-serif;
+ background:#EED;
+ font-size:11px;
+ font-weight:normal;
+ border:1px solid #BBC;
+ color:#886;
+ padding:.3em .5em .1em 2em;
+ text-align:right;
+ vertical-align:top
+}
+table.diff thead {
+ border-bottom:1px solid #BBC;
+ background:#EFEFEF;
+ font-family:Verdana
+}
+table.diff thead th.texttitle {
+ text-align:left
+}
+table.diff tbody td {
+ padding:0px .4em;
+ padding-top:.4em;
+ vertical-align:top;
+}
+table.diff .empty {
+ background-color:#DDD;
+}
+table.diff .replace {
+ background-color:#FD8
+}
+table.diff .delete {
+ background-color:#E99;
+}
+table.diff .skip {
+ background-color:#EFEFEF;
+ border:1px solid #AAA;
+ border-right:1px solid #BBC;
+}
+table.diff .insert {
+ background-color:#9E9
+}
+table.diff th.author {
+ text-align:right;
+ border-top:1px solid #BBC;
+ background:#EFEFEF
+}
\ No newline at end of file
diff --git a/vendor/styles/jquery-ui-1.10.3.custom.css b/vendor/styles/jquery-ui-1.10.4.custom.css
similarity index 85%
rename from vendor/styles/jquery-ui-1.10.3.custom.css
rename to vendor/styles/jquery-ui-1.10.4.custom.css
index f9710a7e0..c956584a0 100644
--- a/vendor/styles/jquery-ui-1.10.3.custom.css
+++ b/vendor/styles/jquery-ui-1.10.4.custom.css
@@ -1,8 +1,8 @@
-/*! jQuery UI - v1.10.3 - 2013-09-04
+/*! jQuery UI - v1.10.4 - 2014-04-11
* http://jqueryui.com
* Includes: jquery.ui.core.css, jquery.ui.resizable.css, jquery.ui.autocomplete.css, jquery.ui.menu.css, jquery.ui.slider.css, jquery.ui.theme.css
-* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Lucida%20Grande%2CLucida%20Sans%2CArial%2Csans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=5px&bgColorHeader=5c9ccc&bgTextureHeader=gloss_wave&bgImgOpacityHeader=55&borderColorHeader=4297d7&fcHeader=ffffff&iconColorHeader=d8e7f3&bgColorContent=fcfdfd&bgTextureContent=inset_hard&bgImgOpacityContent=100&borderColorContent=a6c9e2&fcContent=222222&iconColorContent=469bdd&bgColorDefault=dfeffc&bgTextureDefault=glass&bgImgOpacityDefault=85&borderColorDefault=c5dbec&fcDefault=2e6e9e&iconColorDefault=6da8d5&bgColorHover=d0e5f5&bgTextureHover=glass&bgImgOpacityHover=75&borderColorHover=79b7e7&fcHover=1d5987&iconColorHover=217bc0&bgColorActive=f5f8f9&bgTextureActive=inset_hard&bgImgOpacityActive=100&borderColorActive=79b7e7&fcActive=e17009&iconColorActive=f9bd01&bgColorHighlight=fbec88&bgTextureHighlight=flat&bgImgOpacityHighlight=55&borderColorHighlight=fad42e&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=glass&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=flat&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=flat&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px
-* Copyright 2013 jQuery Foundation and other contributors; Licensed MIT */
+* To view and modify this theme, visit http://jqueryui.com/themeroller/
+* Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */
/* Layout helpers
----------------------------------*/
@@ -296,7 +296,7 @@
/* Component containers
----------------------------------*/
.ui-widget {
- font-family: Lucida Grande,Lucida Sans,Arial,sans-serif;
+ font-family: Verdana,Arial,sans-serif;
font-size: 1.1em;
}
.ui-widget .ui-widget {
@@ -306,25 +306,25 @@
.ui-widget select,
.ui-widget textarea,
.ui-widget button {
- font-family: Lucida Grande,Lucida Sans,Arial,sans-serif;
+ font-family: Verdana,Arial,sans-serif;
font-size: 1em;
}
.ui-widget-content {
- border: 1px solid #a6c9e2;
- background: #fcfdfd url(images/ui-bg_inset-hard_100_fcfdfd_1x100.png) 50% bottom repeat-x;
+ border: 1px solid #aaaaaa;
+ background: #ffffff url("images/ui-bg_flat_75_ffffff_40x100.png") 50% 50% repeat-x;
color: #222222;
}
.ui-widget-content a {
color: #222222;
}
.ui-widget-header {
- border: 1px solid #4297d7;
- background: #5c9ccc url(images/ui-bg_gloss-wave_55_5c9ccc_500x100.png) 50% 50% repeat-x;
- color: #ffffff;
+ border: 1px solid #aaaaaa;
+ background: #cccccc url("images/ui-bg_highlight-soft_75_cccccc_1x100.png") 50% 50% repeat-x;
+ color: #222222;
font-weight: bold;
}
.ui-widget-header a {
- color: #ffffff;
+ color: #222222;
}
/* Interaction states
@@ -332,15 +332,15 @@
.ui-state-default,
.ui-widget-content .ui-state-default,
.ui-widget-header .ui-state-default {
- border: 1px solid #c5dbec;
- background: #dfeffc url(images/ui-bg_glass_85_dfeffc_1x400.png) 50% 50% repeat-x;
- font-weight: bold;
- color: #2e6e9e;
+ border: 1px solid #d3d3d3;
+ background: #e6e6e6 url("images/ui-bg_glass_75_e6e6e6_1x400.png") 50% 50% repeat-x;
+ font-weight: normal;
+ color: #555555;
}
.ui-state-default a,
.ui-state-default a:link,
.ui-state-default a:visited {
- color: #2e6e9e;
+ color: #555555;
text-decoration: none;
}
.ui-state-hover,
@@ -349,30 +349,34 @@
.ui-state-focus,
.ui-widget-content .ui-state-focus,
.ui-widget-header .ui-state-focus {
- border: 1px solid #79b7e7;
- background: #d0e5f5 url(images/ui-bg_glass_75_d0e5f5_1x400.png) 50% 50% repeat-x;
- font-weight: bold;
- color: #1d5987;
+ border: 1px solid #999999;
+ background: #dadada url("images/ui-bg_glass_75_dadada_1x400.png") 50% 50% repeat-x;
+ font-weight: normal;
+ color: #212121;
}
.ui-state-hover a,
.ui-state-hover a:hover,
.ui-state-hover a:link,
-.ui-state-hover a:visited {
- color: #1d5987;
+.ui-state-hover a:visited,
+.ui-state-focus a,
+.ui-state-focus a:hover,
+.ui-state-focus a:link,
+.ui-state-focus a:visited {
+ color: #212121;
text-decoration: none;
}
.ui-state-active,
.ui-widget-content .ui-state-active,
.ui-widget-header .ui-state-active {
- border: 1px solid #79b7e7;
- background: #f5f8f9 url(images/ui-bg_inset-hard_100_f5f8f9_1x100.png) 50% 50% repeat-x;
- font-weight: bold;
- color: #e17009;
+ border: 1px solid #aaaaaa;
+ background: #ffffff url("images/ui-bg_glass_65_ffffff_1x400.png") 50% 50% repeat-x;
+ font-weight: normal;
+ color: #212121;
}
.ui-state-active a,
.ui-state-active a:link,
.ui-state-active a:visited {
- color: #e17009;
+ color: #212121;
text-decoration: none;
}
@@ -381,8 +385,8 @@
.ui-state-highlight,
.ui-widget-content .ui-state-highlight,
.ui-widget-header .ui-state-highlight {
- border: 1px solid #fad42e;
- background: #fbec88 url(images/ui-bg_flat_55_fbec88_40x100.png) 50% 50% repeat-x;
+ border: 1px solid #fcefa1;
+ background: #fbf9ee url("images/ui-bg_glass_55_fbf9ee_1x400.png") 50% 50% repeat-x;
color: #363636;
}
.ui-state-highlight a,
@@ -394,7 +398,7 @@
.ui-widget-content .ui-state-error,
.ui-widget-header .ui-state-error {
border: 1px solid #cd0a0a;
- background: #fef1ec url(images/ui-bg_glass_95_fef1ec_1x400.png) 50% 50% repeat-x;
+ background: #fef1ec url("images/ui-bg_glass_95_fef1ec_1x400.png") 50% 50% repeat-x;
color: #cd0a0a;
}
.ui-state-error a,
@@ -440,27 +444,27 @@
}
.ui-icon,
.ui-widget-content .ui-icon {
- background-image: url(images/ui-icons_469bdd_256x240.png);
+ background-image: url("images/ui-icons_222222_256x240.png");
}
.ui-widget-header .ui-icon {
- background-image: url(images/ui-icons_d8e7f3_256x240.png);
+ background-image: url("images/ui-icons_222222_256x240.png");
}
.ui-state-default .ui-icon {
- background-image: url(images/ui-icons_6da8d5_256x240.png);
+ background-image: url("images/ui-icons_888888_256x240.png");
}
.ui-state-hover .ui-icon,
.ui-state-focus .ui-icon {
- background-image: url(images/ui-icons_217bc0_256x240.png);
+ background-image: url("images/ui-icons_454545_256x240.png");
}
.ui-state-active .ui-icon {
- background-image: url(images/ui-icons_f9bd01_256x240.png);
+ background-image: url("images/ui-icons_454545_256x240.png");
}
.ui-state-highlight .ui-icon {
- background-image: url(images/ui-icons_2e83ff_256x240.png);
+ background-image: url("images/ui-icons_2e83ff_256x240.png");
}
.ui-state-error .ui-icon,
.ui-state-error-text .ui-icon {
- background-image: url(images/ui-icons_cd0a0a_256x240.png);
+ background-image: url("images/ui-icons_cd0a0a_256x240.png");
}
/* positioning */
@@ -650,37 +654,37 @@
.ui-corner-top,
.ui-corner-left,
.ui-corner-tl {
- border-top-left-radius: 5px;
+ border-top-left-radius: 4px;
}
.ui-corner-all,
.ui-corner-top,
.ui-corner-right,
.ui-corner-tr {
- border-top-right-radius: 5px;
+ border-top-right-radius: 4px;
}
.ui-corner-all,
.ui-corner-bottom,
.ui-corner-left,
.ui-corner-bl {
- border-bottom-left-radius: 5px;
+ border-bottom-left-radius: 4px;
}
.ui-corner-all,
.ui-corner-bottom,
.ui-corner-right,
.ui-corner-br {
- border-bottom-right-radius: 5px;
+ border-bottom-right-radius: 4px;
}
/* Overlays */
.ui-widget-overlay {
- background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x;
+ background: #aaaaaa url("images/ui-bg_flat_0_aaaaaa_40x100.png") 50% 50% repeat-x;
opacity: .3;
filter: Alpha(Opacity=30);
}
.ui-widget-shadow {
margin: -8px 0 0 -8px;
padding: 8px;
- background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x;
+ background: #aaaaaa url("images/ui-bg_flat_0_aaaaaa_40x100.png") 50% 50% repeat-x;
opacity: .3;
filter: Alpha(Opacity=30);
border-radius: 8px;
diff --git a/vendor/styles/treema.css b/vendor/styles/treema.css
deleted file mode 100644
index d9cc2c630..000000000
--- a/vendor/styles/treema.css
+++ /dev/null
@@ -1,299 +0,0 @@
-@media -sass-debug-info{filename{}line{font-family:\000033}}
-.treema-node {
- user-select: none;
- -webkit-user-select: none;
- -moz-user-select: none;
- position: relative;
- font-family: Helvetica;
- clear: both;
- border-bottom: 1px solid #cccccc;
- font-size: 13px;
- cursor: pointer; }
-@media -sass-debug-info{filename{}line{font-family:\0000315}}
- .treema-node.treema-root > .treema-row .treema-value {
- display: none; }
-@media -sass-debug-info{filename{}line{font-family:\0000318}}
- .treema-node.treema-open > .treema-children {
- padding-top: 1px; }
-@media -sass-debug-info{filename{}line{font-family:\0000321}}
- .treema-node.treema-root {
- outline: none; }
-@media -sass-debug-info{filename{}line{font-family:\0000325}}
- .treema-node input, .treema-node select {
- font-size: 13px;
- font-family: Helvetica; }
-@media -sass-debug-info{filename{}line{font-family:\0000328}}
- .treema-node input {
- margin: -3px 0;
- width: 200px; }
-@media -sass-debug-info{filename{}line{font-family:\0000331}}
- .treema-node select {
- height: inherit;
- margin: 0;
- width: inherit; }
-
-@media -sass-debug-info{filename{}line{font-family:\0000336}}
-.treema-type-select-container, .treema-schema-select-container {
- display: block;
- position: relative;
- margin-right: 5px;
- top: 1px;
- float: left; }
-@media -sass-debug-info{filename{}line{font-family:\0000343}}
- .treema-type-select-container select, .treema-schema-select-container select {
- position: absolute;
- left: 0;
- right: 0;
- top: 0;
- bottom: 0;
- opacity: 0.01;
- width: inherit !important; }
-@media -sass-debug-info{filename{}line{font-family:\0000354}}
- .treema-type-select-container button, .treema-schema-select-container button {
- font-size: 10px;
- padding: 0 3px;
- width: 19px; }
-
-@media -sass-debug-info{filename{}line{font-family:\0000359}}
-.treema-children {
- margin-left: 15px;
- clear: both; }
-
-@media -sass-debug-info{filename{}line{font-family:\0000363}}
-.treema-add-child {
- background-color: #eeeeff;
- border: 1px solid #aaaaff;
- cursor: pointer;
- display: inline-block;
- margin: 3px 0 10px;
- padding: 0px 5px;
- font-weight: bold;
- position: relative;
- left: -10px; }
-@media -sass-debug-info{filename{}line{font-family:\0000374}}
- .treema-add-child:hover {
- background-color: #ccccff; }
-
-@media -sass-debug-info{filename{}line{font-family:\0000377}}
-.treema-full > .treema-children > .treema-add-child {
- display: none; }
-
-@media -sass-debug-info{filename{}line{font-family:\0000380}}
-.treema-full.treema-open > .treema-children {
- margin-bottom: 5px; }
-
-@media -sass-debug-info{filename{}line{font-family:\0000383}}
-.treema-row {
- padding: 2px 3px 2px 3px; }
-
-@media -sass-debug-info{filename{}line{font-family:\0000386}}
-.treema-value {
- cursor: text;
- display: block;
- float: left;
- max-width: 100%; }
-
-@media -sass-debug-info{filename{}line{font-family:\0000392}}
-.treema-key {
- color: #5353ac;
- float: left;
- display: block;
- cursor: pointer;
- margin-right: 5px; }
-
-@media -sass-debug-info{filename{}line{font-family:\0000399}}
-.treema-backdrop {
- position: absolute;
- top: 0;
- left: 0;
- height: 25px;
- background-color: rgba(64, 128, 255, 0);
- pointer-events: none;
- width: 100%;
- cursor: pointer; }
-
-@media -sass-debug-info{filename{}line{font-family:\00003109}}
-.treema-description {
- float: right;
- opacity: 0.8;
- font-size: 11px;
- line-height: 13px;
- min-width: 200px;
- text-align: right;
- display: none; }
-
-@media -sass-debug-info{filename{}line{font-family:\00003118}}
-.treema-selected > .treema-row > .treema-description {
- display: inline; }
-
-@media -sass-debug-info{filename{}line{font-family:\00003121}}
-.treema-edit + .treema-description {
- display: inline; }
-
-@media -sass-debug-info{filename{}line{font-family:\00003124}}
-.treema-selected > .treema-row {
- background-color: rgba(64, 128, 255, 0.25); }
-
-@media -sass-debug-info{filename{}line{font-family:\00003129}}
-.treema-error {
- float: right;
- color: darkred;
- margin: 2px 10px; }
-
-@media -sass-debug-info{filename{}line{font-family:\00003134}}
-.treema-has-error {
- background-color: lightpink;
- border: 1px solid darkred; }
-
-@media -sass-debug-info{filename{}line{font-family:\00003138}}
-.treema-temp-error {
- background-color: lightpink;
- padding: 2px 3px;
- color: darkred;
- margin: 0 5px;
- border: 1px solid darkred; }
-
-@media -sass-debug-info{filename{}line{font-family:\00003148}}
-.treema-toggle-hit-area {
- cursor: pointer;
- width: 15px;
- float: left;
- position: absolute;
- left: -15px;
- top: 0;
- bottom: 0; }
-@media -sass-debug-info{filename{}line{font-family:\00003157}}
- .treema-toggle-hit-area:hover {
- background-color: rgba(128, 128, 128, 0.1); }
-@media -sass-debug-info{filename{}line{font-family:\00003159}}
- .treema-toggle-hit-area:hover .treema-toggle {
- opacity: 1; }
-@media -sass-debug-info{filename{}line{font-family:\00003162}}
- .treema-toggle-hit-area .treema-toggle {
- width: 0;
- height: 0;
- opacity: 0.7;
- position: absolute; }
-@media -sass-debug-info{filename{}line{font-family:\00003168}}
- .treema-closed > .treema-toggle-hit-area .treema-toggle {
- border-top: 6px solid transparent;
- border-bottom: 6px solid transparent;
- border-left: 8px solid #666666;
- top: 5px;
- left: 5px; }
-@media -sass-debug-info{filename{}line{font-family:\00003175}}
- .treema-open > .treema-toggle-hit-area .treema-toggle {
- border-left: 6px solid transparent;
- border-right: 6px solid transparent;
- border-top: 8px solid #666666;
- top: 7px;
- left: 3px; }
-
-@media -sass-debug-info{filename{}line{font-family:\00003183}}
-.treema-clearfix:after {
- content: ".";
- display: block;
- height: 0;
- clear: both;
- visibility: hidden; }
-
-@media -sass-debug-info{filename{}line{font-family:\00003190}}
-.treema-shortened {
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap; }
-
-@media -sass-debug-info{filename{}line{font-family:\00003195}}
-.treema-multiline {
- width: inherit;
- margin-top: 20px;
- float: none !important; }
-
-@media -sass-debug-info{filename{}line{font-family:\00003201}}
-.treema-clipboard-container {
- position: fixed;
- left: 0px;
- top: 0px;
- width: 0px;
- height: 0px;
- z-index: 100;
- display: none;
- opacity: 0; }
-@media -sass-debug-info{filename{}line{font-family:\00003211}}
- .treema-clipboard-container .treema-clipboard {
- width: 1px;
- height: 1px;
- padding: 0px; }
-
-@media -sass-debug-info{filename{}line{font-family:\000033}}
-.treema-string {
- color: #998500; }
-
-@media -sass-debug-info{filename{}line{font-family:\000036}}
-.treema-number {
- color: #699900; }
-
-@media -sass-debug-info{filename{}line{font-family:\000039}}
-.treema-null {
- color: #524059;
- font-weight: bold; }
-
-@media -sass-debug-info{filename{}line{font-family:\0000313}}
-.treema-array {
- color: #009905;
- cursor: row-resize; }
-
-@media -sass-debug-info{filename{}line{font-family:\0000317}}
-.treema-object {
- color: #008799;
- cursor: row-resize; }
-
-@media -sass-debug-info{filename{}line{font-family:\0000321}}
-.treema-boolean {
- color: #140099;
- cursor: pointer; }
-@media -sass-debug-info{filename{}line{font-family:\0000324}}
- .treema-boolean.treema-edit {
- background-color: rgba(64, 128, 255, 0.25); }
-@media -sass-debug-info{filename{}line{font-family:\0000326}}
- .treema-boolean input {
- opacity: 0; }
-@media -sass-debug-info{filename{}line{font-family:\0000329}}
- .treema-boolean:hover + .treema-description {
- display: inline; }
-
-@media -sass-debug-info{filename{}line{font-family:\000034}}
-.treema-point2d input, .treema-point3d input {
- width: 40px; }
-
-@media -sass-debug-info{filename{}line{font-family:\000037}}
-.treema-search-results {
- width: 500px;
- margin-top: 10px;
- cursor: pointer; }
-
-@media -sass-debug-info{filename{}line{font-family:\0000312}}
-.treema-search-result-row:hover {
- background-color: rgba(166, 196, 255, 0.25); }
-
-@media -sass-debug-info{filename{}line{font-family:\0000315}}
-.treema-search-selected {
- background-color: rgba(64, 128, 255, 0.25) !important; }
-
-@media -sass-debug-info{filename{}line{font-family:\0000318}}
-.treema-ace.treema-display .treema-shortened {
- font-family: monospace; }
-
-@media -sass-debug-info{filename{}line{font-family:\0000321}}
-.treema-ace.treema-edit .ace_editor {
- width: 100%;
- height: 300px;
- border: 1px solid gray; }
-
-@media -sass-debug-info{filename{}line{font-family:\0000327}}
-.treema-long-string textarea {
- width: 100%;
- height: 300px; }
-
-
-/*@ sourceMappingURL=treema.css.map*/
\ No newline at end of file