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 - $('