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/lib/LevelBus.coffee b/app/lib/LevelBus.coffee
index 8b270808b..f4b24bc47 100644
--- a/app/lib/LevelBus.coffee
+++ b/app/lib/LevelBus.coffee
@@ -112,7 +112,7 @@ module.exports = class LevelBus extends Bus
@changedSessionProperties.teamSpells = true
@session.set({'teamSpells': @teamSpellMap})
@saveSession()
- if spellTeam is me.team
+ if spellTeam is me.team or spellTeam is "common"
@onSpellChanged e # Save the new spell to the session, too.
onScriptStateChanged: (e) ->
diff --git a/app/lib/auth.coffee b/app/lib/auth.coffee
index d46733089..4fa94e61f 100644
--- a/app/lib/auth.coffee
+++ b/app/lib/auth.coffee
@@ -11,7 +11,6 @@ init = ->
me.set 'testGroupNumber', Math.floor(Math.random() * 256)
me.save()
- me.loadGravatarProfile() if me.get('email')
Backbone.listenTo(me, 'sync', Backbone.Mediator.publish('me:synced', {me:me}))
module.exports.createUser = (userObject, failure=backboneFailure, nextURL=null) ->
@@ -52,4 +51,3 @@ trackFirstArrival = ->
storage.save(BEEN_HERE_BEFORE_KEY, true)
init()
-
diff --git a/app/lib/contact.coffee b/app/lib/contact.coffee
index fa38fbf69..ade94f2e9 100644
--- a/app/lib/contact.coffee
+++ b/app/lib/contact.coffee
@@ -1,16 +1,9 @@
-
-
module.exports.sendContactMessage = (contactMessageObject, modal) ->
modal.find('.sending-indicator').show()
- jqxhr = $.post '/contact',
- email: contactMessageObject.email
- message: contactMessageObject.message
- ,
- (response) ->
- console.log "Got contact response:", response
- modal.find('.sending-indicator').hide()
- modal.find('#contact-message').val("Thanks!")
- _.delay ->
- modal.find('#contact-message').val("")
- modal.modal 'hide'
- , 1000
+ jqxhr = $.post '/contact', contactMessageObject, (response) ->
+ modal.find('.sending-indicator').hide()
+ modal.find('#contact-message').val("Thanks!")
+ _.delay ->
+ modal.find('#contact-message').val("")
+ modal.modal 'hide'
+ , 1000
diff --git a/app/locale/en.coffee b/app/locale/en.coffee
index 7d35d42aa..36bbfd9e8 100644
--- a/app/locale/en.coffee
+++ b/app/locale/en.coffee
@@ -3,6 +3,7 @@ module.exports = nativeDescription: "English", englishDescription: "English", tr
loading: "Loading..."
saving: "Saving..."
sending: "Sending..."
+ send: "Send"
cancel: "Cancel"
save: "Save"
create: "Create"
@@ -111,6 +112,8 @@ module.exports = nativeDescription: "English", englishDescription: "English", tr
forum_page: "our forum"
forum_suffix: " instead."
send: "Send Feedback"
+ contact_candidate: "Contact Candidate"
+ recruitment_reminder: "Use this form to get in touch with candidates you are interested in interviewing. Remember that CodeCombat charges 18% of first-year salary for any full-time candidate you hire who stays 90 days, but that part-timers, remote employees, contractors, and interns are free."
diplomat_suggestion:
title: "Help translate CodeCombat!"
@@ -142,9 +145,6 @@ module.exports = nativeDescription: "English", englishDescription: "English", tr
password_tab: "Password"
emails_tab: "Emails"
admin: "Admin"
- gravatar_select: "Select which Gravatar photo to use"
- gravatar_add_photos: "Add thumbnails and photos to a Gravatar account for your email to choose an image."
- gravatar_add_more_photos: "Add more photos to your Gravatar account to access them here."
wizard_color: "Wizard Clothes Color"
new_password: "New Password"
new_password_verify: "Verify"
@@ -166,17 +166,6 @@ module.exports = nativeDescription: "English", englishDescription: "English", tr
edit_settings: "Edit Settings"
profile_for_prefix: "Profile for "
profile_for_suffix: ""
- profile: "Profile"
- user_not_found: "No user found. Check the URL?"
- gravatar_not_found_mine: "We couldn't find your profile associated with:"
- gravatar_not_found_email_suffix: "."
- gravatar_signup_prefix: "Sign up at "
- gravatar_signup_suffix: " to get set up!"
- gravatar_not_found_other: "Alas, there's no profile associated with this person's email address."
- gravatar_contact: "Contact"
- gravatar_websites: "Websites"
- gravatar_accounts: "As Seen On"
- gravatar_profile_link: "Full Gravatar Profile"
play_level:
level_load_error: "Level could not be loaded: "
@@ -345,6 +334,7 @@ module.exports = nativeDescription: "English", englishDescription: "English", tr
results: "Results"
description: "Description"
or: "or"
+ subject: "Subject"
email: "Email"
password: "Password"
message: "Message"
@@ -616,7 +606,7 @@ module.exports = nativeDescription: "English", englishDescription: "English", tr
bad_input: "Bad input."
server_error: "Server error."
unknown: "Unknown error."
-
+
resources:
your_sessions: "Your Sessions"
level: "Level"
@@ -626,4 +616,6 @@ module.exports = nativeDescription: "English", englishDescription: "English", tr
facebook_friend_sessions: "Facebook Friend Sessions"
gplus_friends: "G+ Friends"
gplus_friend_sessions: "G+ Friend Sessions"
- leaderboard: 'leaderboard'
\ No newline at end of file
+ leaderboard: "Leaderboard"
+ user_schema: "User Schema"
+ user_profile: "User Profile"
diff --git a/app/locale/ru.coffee b/app/locale/ru.coffee
index 45c920faa..5e51f04be 100644
--- a/app/locale/ru.coffee
+++ b/app/locale/ru.coffee
@@ -102,7 +102,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi
spectate: "Наблюдать"
contact:
- contact_us: "Связаться с CodeCombat"
+ contact_us: "Связаться с Нами"
welcome: "Мы рады вашему сообщению! Используйте эту форму, чтобы отправить нам email. "
contribute_prefix: "Если вы хотите внести свой вклад в проект, зайдите на нашу "
contribute_page: "страницу сотрудничества"
@@ -604,21 +604,21 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi
so_ready: "Я полностью готов(а) для этого"
# loading_error:
-# could_not_load: "Error loading from server"
-# connection_failure: "Connection failed."
+# could_not_load: "Ошибка соединения с сервером"
+# connection_failure: "Соединение потеряно."
# unauthorized: "You need to be signed in. Do you have cookies disabled?"
# forbidden: "You do not have the permissions."
-# not_found: "Not found."
+# not_found: "Не наидено."
# not_allowed: "Method not allowed."
# timeout: "Server timeout."
# conflict: "Resource conflict."
# bad_input: "Bad input."
-# server_error: "Server error."
-# unknown: "Unknown error."
+# server_error: "Ошибка сервера."
+# unknown: "Неизвестная ошибка."
# resources:
# your_sessions: "Your Sessions"
-# level: "Level"
+# level: "Уровень"
# social_network_apis: "Social Network APIs"
# facebook_status: "Facebook Status"
# facebook_friends: "Facebook Friends"
diff --git a/app/locale/tr.coffee b/app/locale/tr.coffee
index d99ed8dd3..6c0e9dcaa 100644
--- a/app/locale/tr.coffee
+++ b/app/locale/tr.coffee
@@ -5,7 +5,7 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t
sending: "Gönderiliyor..."
cancel: "İptal"
save: "Kaydet"
-# create: "Create"
+ create: "Oluştur"
delay_1_sec: "1 saniye"
delay_3_sec: "3 saniye"
delay_5_sec: "5 saniye"
@@ -16,11 +16,11 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t
units:
second: "saniye"
- seconds: "saniyeler"
+ seconds: "saniye"
minute: "dakika"
- minutes: "dakikalar"
+ minutes: "dakika"
hour: "saat"
- hours: "saatler"
+ hours: "saat"
modal:
close: "Kapat"
@@ -224,13 +224,13 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t
tome_available_spells: "Kullanılabilir Büyüler"
hud_continue: "Devam (ÜstKarakter+Boşluk)"
spell_saved: "Büyü Kaydedildi"
-# skip_tutorial: "Skip (esc)"
+ skip_tutorial: "Atla (esc)"
# editor_config: "Editor Config"
# editor_config_title: "Editor Configuration"
# editor_config_language_label: "Programming Language"
# editor_config_language_description: "Define the programming language you want to code in."
# editor_config_keybindings_label: "Key Bindings"
-# editor_config_keybindings_default: "Default (Ace)"
+ editor_config_keybindings_default: "Varsayılan (Ace)"
# editor_config_keybindings_description: "Adds additional shortcuts known from the common editors."
# editor_config_invisibles_label: "Show Invisibles"
# editor_config_invisibles_description: "Displays invisibles such as spaces or tabs."
@@ -238,7 +238,7 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t
# editor_config_indentguides_description: "Displays vertical lines to see indentation better."
# editor_config_behaviors_label: "Smart Behaviors"
# editor_config_behaviors_description: "Autocompletes brackets, braces, and quotes."
-# loading_ready: "Ready!"
+ loading_ready: "Hazır!"
# tip_insert_positions: "Shift+Click a point on the map to insert it into the spell editor."
# tip_toggle_play: "Toggle play/paused with Ctrl+P."
# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward."
@@ -267,9 +267,9 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t
# tip_impossible: "It always seems impossible until it's done. - Nelson Mandela"
# tip_talk_is_cheap: "Talk is cheap. Show me the code. - Linus Torvalds"
# tip_first_language: "The most disastrous thing that you can ever learn is your first programming language. - Alan Kay"
-# time_current: "Now:"
-# time_total: "Max:"
-# time_goto: "Go to:"
+ time_current: "Şimdi:"
+ time_total: "Max:"
+ time_goto: "Git:"
admin:
av_title: "Yönetici Görünümleri"
diff --git a/app/models/User.coffee b/app/models/User.coffee
index c3bf71146..9f688f46b 100644
--- a/app/models/User.coffee
+++ b/app/models/User.coffee
@@ -8,53 +8,25 @@ module.exports = class User extends CocoModel
initialize: ->
super()
- @on 'change:emailHash', ->
- @gravatarProfile = null
- @loadGravatarProfile()
isAdmin: ->
permissions = @attributes['permissions'] or []
return 'admin' in permissions
- gravatarAvatarURL: ->
- avatar_url = GRAVATAR_URL + 'avatar/'
- return avatar_url if not @emailHash
- return avatar_url + @emailHash
-
- loadGravatarProfile: ->
- emailHash = @get('emailHash')
- return if not emailHash
- functionName = 'gotProfile'+emailHash
- profileUrl = "#{GRAVATAR_URL}#{emailHash}.json?callback=#{functionName}"
- script = $("")
- $('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/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/employers.sass b/app/styles/employers.sass
new file mode 100644
index 000000000..2d61d81fa
--- /dev/null
+++ b/app/styles/employers.sass
@@ -0,0 +1,18 @@
+#employers-view
+ .tablesorter
+ //img
+ // display: none
+
+ .tablesorter-header
+ cursor: pointer
+ &:hover
+ color: black
+
+ .tablesorter-headerAsc
+ background-color: #cfc
+
+ .tablesorter-headerDesc
+ background-color: #ccf
+
+ tr
+ cursor: pointer
diff --git a/app/styles/play/level/loading.sass b/app/styles/play/level/loading.sass
index 177334f72..9570497f3 100644
--- a/app/styles/play/level/loading.sass
+++ b/app/styles/play/level/loading.sass
@@ -22,7 +22,9 @@
position: absolute
z-index: 20
$UNVEIL_TIME: 1.2s
- pointer-events: none
+
+ &.unveiled
+ pointer-events: none
.loading-details
position: absolute
diff --git a/app/templates/account/job_profile.jade b/app/templates/account/job_profile.jade
new file mode 100644
index 000000000..a5eff46d3
--- /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. Hungry employers will see it until you mark it inactive or it is stale for two months.
+else
+ p.lead(data-i18n="account_settings.job_profile_explanation") Hi! Fill this out, and if we think we can find you a software developer job, we will get in touch to approve your profile.
+
+#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..5ef74256e 100644
--- a/app/templates/account/profile.jade
+++ b/app/templates/account/profile.jade
@@ -1,72 +1,100 @@
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 Looking for: #{profile.lookingFor}
+ div 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 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="")
+ | 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="")
+ | 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 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 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.
\ 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/base.jade b/app/templates/base.jade
index 8212882af..f31bc2db3 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
diff --git a/app/templates/employers.jade b/app/templates/employers.jade
index 485d21622..d242a7d77 100644
--- a/app/templates/employers.jade
+++ b/app/templates/employers.jade
@@ -32,3 +32,54 @@ block content
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 Name
+ th Location
+ th Looking For
+ th Top 5 Skills
+ th Yrs Exp
+ th Last Updated
+ th Current Job
+ 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
+ each skill in profile.skills.slice(0, 5)
+ code= skill
+ span
+ td= profile.experience
+ td= moment(profile.updated).fromNow()
+ if authorized
+ if profile.work.length
+ td= profile.work[0].role + ' at ' + profile.work[0].employer
+ else
+ td
+ else
+ td
+ em Employer sign-up required.
+ if me.isAdmin()
+ if candidate.get('jobProfileApproved')
+ td ✓
+ else
+ td ✗
\ No newline at end of file
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..87120f033
--- /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 get in touch with candidates you are interested in interviewing. Remember that CodeCombat charges 18% of first-year salary for any full-time candidate you hire who stays 90 days, but that part-timers, remote employees, contractors, and interns are free.
+ .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/views/account/job_profile_view.coffee b/app/views/account/job_profile_view.coffee
new file mode 100644
index 000000000..a39fb6b16
--- /dev/null
+++ b/app/views/account/job_profile_view.coffee
@@ -0,0 +1,94 @@
+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'
+ ]
+
+ constructor: (options) ->
+ super options
+ unless me.schema().loaded
+ @addSomethingToLoad("user_schema")
+ @listenToOnce me, 'schema-loaded', => @somethingLoaded 'user_schema'
+
+ 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')
+ schema = _.cloneDeep me.schema().get('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..be4a79c59 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,19 @@ 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
+
+ if me.schema().loaded
+ @buildPictureTreema()
+ else
+ @listenToOnce me, 'schema-loaded', @buildPictureTreema
chooseTab: (category) ->
id = "##{category}-pane"
@@ -62,11 +62,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 +79,30 @@ 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')
+ if data.photoURL?.search('gravatar') isnt -1
+ # Old style
+ data.photoURL = null
+ schema = _.cloneDeep me.schema().attributes
+ schema.properties = _.pick me.schema().get('properties'), 'photoURL'
+ schema.required = ['photoURL']
+ console.log 'schema is', schema
+ 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 +116,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 +142,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/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/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/employers_view.coffee b/app/views/employers_view.coffee
index d5e2eeb2a..a43bbf70a 100644
--- a/app/views/employers_view.coffee
+++ b/app/views/employers_view.coffee
@@ -1,6 +1,90 @@
View = require 'views/kinds/RootView'
template = require 'templates/employers'
+app = require 'application'
+User = require 'models/User'
+CocoCollection = require 'models/CocoCollection'
+employerSignupTemplate = require 'templates/modal/employer_signup_modal'
+ModalView = require 'views/kinds/ModalView'
+
+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
+ employerSignupModal = new ModalView()
+ employerSignupModal.template = employerSignupTemplate
+ @openModalView employerSignupModal
diff --git a/app/views/kinds/CocoView.coffee b/app/views/kinds/CocoView.coffee
index 3ea8204a3..360913182 100644
--- a/app/views/kinds/CocoView.coffee
+++ b/app/views/kinds/CocoView.coffee
@@ -104,15 +104,15 @@ module.exports = class CocoView extends Backbone.View
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 +152,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 +160,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 +169,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 +179,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 +194,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]?()
diff --git a/app/views/kinds/SearchView.coffee b/app/views/kinds/SearchView.coffee
index f49eb4994..5f93924c3 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'
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/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/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/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/bower.json b/bower.json
index 84e10d876..5995b14c3 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",
@@ -37,8 +37,15 @@
"firebase": "~1.0.2",
"catiline": "~2.9.3",
"d3": "~3.4.4",
+<<<<<<< HEAD
"jsondiffpatch": "~0.1.5",
"nanoscroller": "~0.8.0"
+=======
+ "nanoscroller": "~0.8.0",
+ "jquery.tablesorter": "~2.15.13",
+ "treema": "~0.0.1",
+ "bootstrap": "~3.1.1"
+>>>>>>> master
},
"overrides": {
"backbone": {
@@ -53,8 +60,26 @@
"underscore.string": {
"main": "lib/underscore.string.js"
},
+<<<<<<< HEAD
"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"
+ ]
+>>>>>>> master
}
}
}
diff --git a/config.coffee b/config.coffee
index 10daa6b93..176701755 100644
--- a/config.coffee
+++ b/config.coffee
@@ -41,21 +41,11 @@ 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'
diff --git a/package.json b/package.json
index 3e2552f1c..ee0dcc201 100644
--- a/package.json
+++ b/package.json
@@ -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/windows/coco-dev-setup/batch/config/config.coco b/scripts/windows/coco-dev-setup/batch/config/config.coco
index b0bf33e15..e2d7570f5 100755
--- a/scripts/windows/coco-dev-setup/batch/config/config.coco
+++ b/scripts/windows/coco-dev-setup/batch/config/config.coco
@@ -1,6 +1,6 @@
- 2.1
+ 3.3
GlenDC
CodeCombat.com 2013-2014
https://github.com/codecombat/codecombat.git
diff --git a/scripts/windows/coco-dev-setup/batch/config/downloads.coco b/scripts/windows/coco-dev-setup/batch/config/downloads.coco
index e65d0f7ee..1d57fbb71 100755
--- a/scripts/windows/coco-dev-setup/batch/config/downloads.coco
+++ b/scripts/windows/coco-dev-setup/batch/config/downloads.coco
@@ -20,18 +20,18 @@
- 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
- 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/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
index 075c10991..e8af90621 100755
--- a/scripts/windows/coco-dev-setup/batch/localisation/de.coco
+++ b/scripts/windows/coco-dev-setup/batch/localisation/de.coco
@@ -53,6 +53,11 @@
Willst du das Repository via SSH auschecken?
+
+ 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...
diff --git a/scripts/windows/coco-dev-setup/batch/localisation/en.coco b/scripts/windows/coco-dev-setup/batch/localisation/en.coco
index 081f6672a..46464bb55 100755
--- a/scripts/windows/coco-dev-setup/batch/localisation/en.coco
+++ b/scripts/windows/coco-dev-setup/batch/localisation/en.coco
@@ -60,6 +60,11 @@
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...
diff --git a/scripts/windows/coco-dev-setup/batch/localisation/fr.coco b/scripts/windows/coco-dev-setup/batch/localisation/fr.coco
index 6c3902376..d2a0d67ae 100755
--- a/scripts/windows/coco-dev-setup/batch/localisation/fr.coco
+++ b/scripts/windows/coco-dev-setup/batch/localisation/fr.coco
@@ -53,6 +53,11 @@
Do you want to checkout the repository via ssh?
+
+ 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...
diff --git a/scripts/windows/coco-dev-setup/batch/localisation/languages.coco b/scripts/windows/coco-dev-setup/batch/localisation/languages.coco
index a267d65d0..a98092066 100755
--- a/scripts/windows/coco-dev-setup/batch/localisation/languages.coco
+++ b/scripts/windows/coco-dev-setup/batch/localisation/languages.coco
@@ -2,6 +2,5 @@ en
nl
de
fr
-zh
zh-HANT
zh-HANS
\ 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
index 294b1ae89..39b95ef9b 100755
--- a/scripts/windows/coco-dev-setup/batch/localisation/nl.coco
+++ b/scripts/windows/coco-dev-setup/batch/localisation/nl.coco
@@ -53,6 +53,11 @@
Wil je het git project downloaden via ssh?
+
+ 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...
diff --git a/scripts/windows/coco-dev-setup/batch/localisation/zh-HANS.coco b/scripts/windows/coco-dev-setup/batch/localisation/zh-HANS.coco
index 86711ea66..410d032f7 100755
--- a/scripts/windows/coco-dev-setup/batch/localisation/zh-HANS.coco
+++ b/scripts/windows/coco-dev-setup/batch/localisation/zh-HANS.coco
@@ -53,6 +53,11 @@
你是否想使用 ssh 来检出(checkout)库(repository)?
+
+ 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 安装依赖包...
diff --git a/scripts/windows/coco-dev-setup/batch/localisation/zh-HANT.coco b/scripts/windows/coco-dev-setup/batch/localisation/zh-HANT.coco
index 61e7ff3d6..8c242effa 100755
--- a/scripts/windows/coco-dev-setup/batch/localisation/zh-HANT.coco
+++ b/scripts/windows/coco-dev-setup/batch/localisation/zh-HANT.coco
@@ -53,6 +53,11 @@
Do you want to checkout the repository via ssh?
+
+ 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...
diff --git a/scripts/windows/coco-dev-setup/batch/localisation/zh.coco b/scripts/windows/coco-dev-setup/batch/localisation/zh.coco
deleted file mode 100755
index 6c7c8dd2f..000000000
--- a/scripts/windows/coco-dev-setup/batch/localisation/zh.coco
+++ /dev/null
@@ -1,84 +0,0 @@
-
-
-
- 中文
- Chinese
- 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...
-
- 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!
-
-
\ No newline at end of file
diff --git a/scripts/windows/coco-dev-setup/batch/scripts/setup_p3.bat b/scripts/windows/coco-dev-setup/batch/scripts/configuration.bat
similarity index 93%
rename from scripts/windows/coco-dev-setup/batch/scripts/setup_p3.bat
rename to scripts/windows/coco-dev-setup/batch/scripts/configuration.bat
index 6eee20e91..f70748a7a 100755
--- a/scripts/windows/coco-dev-setup/batch/scripts/setup_p3.bat
+++ b/scripts/windows/coco-dev-setup/batch/scripts/configuration.bat
@@ -1,6 +1,8 @@
@echo off
setlocal EnableDelayedExpansion
+call read_cache
+
call configuration_cmd
call npm_and_brunch_setup
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 5fbca9f4e..3408a13e6 100755
--- 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
@@ -21,7 +21,7 @@ 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!"
-%curl_app% -k %2 -o !install_file!
+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\"
@@ -49,9 +49,9 @@ if "%download_extension%"=="zip" (
md %mongodb_path%
%systemroot%\System32\xcopy !package_path!!mongodb_original_directory! !mongodb_path! /r /h /s /e /y
-
- setx path ";!mongodb_path!\bin"
-
+
+ call set_environment_var "!mongodb_path!\bin"
+
goto:clean_up
)
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/github_setup.bat b/scripts/windows/coco-dev-setup/batch/scripts/github_setup.bat
index ebe97a08b..6436aacb0 100755
--- a/scripts/windows/coco-dev-setup/batch/scripts/github_setup.bat
+++ b/scripts/windows/coco-dev-setup/batch/scripts/github_setup.bat
@@ -136,8 +136,8 @@ goto:eof
set cur_dir=%CD%
cd !repository_path!\coco
- git remote rm origin
- git remote add origin https://!git_username!:!git_password!@github.com/!git_username!/codecombat.git
+ "%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!
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
index b548cb6b8..7048f0180 100755
--- a/scripts/windows/coco-dev-setup/batch/scripts/nab_automatic_script.bat
+++ b/scripts/windows/coco-dev-setup/batch/scripts/nab_automatic_script.bat
@@ -2,6 +2,6 @@ call print_dashed_seperator
call get_local_text npm_script npm script
echo %npm_script%
-echo start cmd.exe cmd /c "TITLE CodeCombat.com - nodemon server & call nodemon -w server -w server_config.js">%~1\SCOCODE.bat
-echo start cmd.exe cmd /c "TITLE CodeCombat.com - brunch - live compiler & call brunch w">>%~1\SCOCODE.bat
-echo start cmd.exe cmd /c "TITLE CodeCombat.com - mongodb database & mongod --setParameter textSearchEnabled=true --dbpath %~2">>%~1\SCOCODE.bat
\ No newline at end of file
+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_mongodb.bat b/scripts/windows/coco-dev-setup/batch/scripts/nab_install_mongodb.bat
index 8f22c4dee..84d9a3691 100755
--- a/scripts/windows/coco-dev-setup/batch/scripts/nab_install_mongodb.bat
+++ b/scripts/windows/coco-dev-setup/batch/scripts/nab_install_mongodb.bat
@@ -20,7 +20,7 @@ cd /D %~1
start cmd /c "TITLE MongoDB - %npm_close% & mongod --setParameter textSearchEnabled=true --dbpath %~1"
-%work_directory%\%curl_app% -k %database_backup% -o dump.tar.gz
+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"
@@ -32,4 +32,6 @@ 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/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_p2.bat b/scripts/windows/coco-dev-setup/batch/scripts/setup_p2.bat
index c79fb2e61..f6e0d3b21 100755
--- a/scripts/windows/coco-dev-setup/batch/scripts/setup_p2.bat
+++ b/scripts/windows/coco-dev-setup/batch/scripts/setup_p2.bat
@@ -5,6 +5,16 @@ call configuration_cmd
call github_setup
-start cmd /c "setup_p3.bat"
+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/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/server/commons/schemas.coffee b/server/commons/schemas.coffee
index e81f49587..a98c4a9b5 100644
--- a/server/commons/schemas.coffee
+++ b/server/commons/schemas.coffee
@@ -8,6 +8,8 @@ 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
@@ -16,6 +18,7 @@ me.pct = (ext) -> combine({type: 'number', maximum: 1.0, minimum: 0.0}, 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}
diff --git a/server/levels/level_handler.coffee b/server/levels/level_handler.coffee
index af9392bf2..15c75b521 100644
--- a/server/levels/level_handler.coffee
+++ b/server/levels/level_handler.coffee
@@ -38,7 +38,12 @@ 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'
+<<<<<<< HEAD
super(arguments...)
+=======
+ return @checkExistence(req, res, args[0]) if args[1] is 'exists'
+ return @sendNotFoundError(res)
+>>>>>>> master
fetchLevelByIDAndHandleErrors: (id, req, res, callback) ->
@getDocumentForIdOrSlug id, (err, level) =>
@@ -130,7 +135,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)
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/mail.coffee b/server/routes/mail.coffee
index b1a4b6ba3..9023209d2 100644
--- a/server/routes/mail.coffee
+++ b/server/routes/mail.coffee
@@ -37,7 +37,7 @@ getTimeFromDaysAgo = (now, daysAgo) ->
t = now - 86400 * 1000 * daysAgo - LADDER_PREGAME_INTERVAL
isRequestFromDesignatedCronHandler = (req, res) ->
- requestIP = req.headers['x-forwarded-for'][0]
+ 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"
diff --git a/server/users/user_handler.coffee b/server/users/user_handler.coffee
index b43f3ed6d..0ef407690 100644
--- a/server/users/user_handler.coffee
+++ b/server/users/user_handler.coffee
@@ -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,12 @@ 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'
+<<<<<<< HEAD
super(arguments...)
+=======
+ return @getCandidates(req, res) if args[1] is 'candidates'
+ return @sendNotFoundError(res)
+>>>>>>> master
agreeToCLA: (req, res) ->
return @sendUnauthorizedError(res) unless req.user
@@ -191,9 +198,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 +214,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', 'skills', 'experience', 'updated']
+ if authorized
+ subfields = subfields.concat ['name', 'work']
+ 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
index 18d526de5..c7de194e3 100644
--- a/server/users/user_schema.coffee
+++ b/server/users/user_schema.coffee
@@ -9,7 +9,7 @@ UserSchema = c.object {},
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}
+ 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'})
@@ -36,7 +36,6 @@ UserSchema = c.object {},
passwordHash: {type: 'string', maxLength: 256}
# client side
- #gravatarProfile: {} (should only ever be kept locally)
emailHash: {type: 'string'}
#Internationalization stuff
@@ -56,6 +55,44 @@ UserSchema = c.object {},
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_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/vendor/scripts/bootstrap/affix.js b/vendor/scripts/bootstrap/affix.js
deleted file mode 100644
index 552bffa3f..000000000
--- a/vendor/scripts/bootstrap/affix.js
+++ /dev/null
@@ -1,126 +0,0 @@
-/* ========================================================================
- * Bootstrap: affix.js v3.0.3
- * http://getbootstrap.com/javascript/#affix
- * ========================================================================
- * Copyright 2013 Twitter, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ======================================================================== */
-
-
-+function ($) { "use strict";
-
- // AFFIX CLASS DEFINITION
- // ======================
-
- var Affix = function (element, options) {
- this.options = $.extend({}, Affix.DEFAULTS, options)
- this.$window = $(window)
- .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))
- .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this))
-
- this.$element = $(element)
- this.affixed =
- this.unpin = null
-
- this.checkPosition()
- }
-
- Affix.RESET = 'affix affix-top affix-bottom'
-
- Affix.DEFAULTS = {
- offset: 0
- }
-
- Affix.prototype.checkPositionWithEventLoop = function () {
- setTimeout($.proxy(this.checkPosition, this), 1)
- }
-
- Affix.prototype.checkPosition = function () {
- if (!this.$element.is(':visible')) return
-
- var scrollHeight = $(document).height()
- var scrollTop = this.$window.scrollTop()
- var position = this.$element.offset()
- var offset = this.options.offset
- var offsetTop = offset.top
- var offsetBottom = offset.bottom
-
- if (typeof offset != 'object') offsetBottom = offsetTop = offset
- if (typeof offsetTop == 'function') offsetTop = offset.top()
- if (typeof offsetBottom == 'function') offsetBottom = offset.bottom()
-
- var affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ? false :
- offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ? 'bottom' :
- offsetTop != null && (scrollTop <= offsetTop) ? 'top' : false
-
- if (this.affixed === affix) return
- if (this.unpin) this.$element.css('top', '')
-
- this.affixed = affix
- this.unpin = affix == 'bottom' ? position.top - scrollTop : null
-
- this.$element.removeClass(Affix.RESET).addClass('affix' + (affix ? '-' + affix : ''))
-
- if (affix == 'bottom') {
- this.$element.offset({ top: document.body.offsetHeight - offsetBottom - this.$element.height() })
- }
- }
-
-
- // AFFIX PLUGIN DEFINITION
- // =======================
-
- var old = $.fn.affix
-
- $.fn.affix = function (option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.affix')
- var options = typeof option == 'object' && option
-
- if (!data) $this.data('bs.affix', (data = new Affix(this, options)))
- if (typeof option == 'string') data[option]()
- })
- }
-
- $.fn.affix.Constructor = Affix
-
-
- // AFFIX NO CONFLICT
- // =================
-
- $.fn.affix.noConflict = function () {
- $.fn.affix = old
- return this
- }
-
-
- // AFFIX DATA-API
- // ==============
-
- $(window).on('load', function () {
- $('[data-spy="affix"]').each(function () {
- var $spy = $(this)
- var data = $spy.data()
-
- data.offset = data.offset || {}
-
- if (data.offsetBottom) data.offset.bottom = data.offsetBottom
- if (data.offsetTop) data.offset.top = data.offsetTop
-
- $spy.affix(data)
- })
- })
-
-}(jQuery);
diff --git a/vendor/scripts/bootstrap/alert.js b/vendor/scripts/bootstrap/alert.js
deleted file mode 100644
index 695ad74d0..000000000
--- a/vendor/scripts/bootstrap/alert.js
+++ /dev/null
@@ -1,98 +0,0 @@
-/* ========================================================================
- * Bootstrap: alert.js v3.0.3
- * http://getbootstrap.com/javascript/#alerts
- * ========================================================================
- * Copyright 2013 Twitter, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ======================================================================== */
-
-
-+function ($) { "use strict";
-
- // ALERT CLASS DEFINITION
- // ======================
-
- var dismiss = '[data-dismiss="alert"]'
- var Alert = function (el) {
- $(el).on('click', dismiss, this.close)
- }
-
- Alert.prototype.close = function (e) {
- var $this = $(this)
- var selector = $this.attr('data-target')
-
- if (!selector) {
- selector = $this.attr('href')
- selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
- }
-
- var $parent = $(selector)
-
- if (e) e.preventDefault()
-
- if (!$parent.length) {
- $parent = $this.hasClass('alert') ? $this : $this.parent()
- }
-
- $parent.trigger(e = $.Event('close.bs.alert'))
-
- if (e.isDefaultPrevented()) return
-
- $parent.removeClass('in')
-
- function removeElement() {
- $parent.trigger('closed.bs.alert').remove()
- }
-
- $.support.transition && $parent.hasClass('fade') ?
- $parent
- .one($.support.transition.end, removeElement)
- .emulateTransitionEnd(150) :
- removeElement()
- }
-
-
- // ALERT PLUGIN DEFINITION
- // =======================
-
- var old = $.fn.alert
-
- $.fn.alert = function (option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.alert')
-
- if (!data) $this.data('bs.alert', (data = new Alert(this)))
- if (typeof option == 'string') data[option].call($this)
- })
- }
-
- $.fn.alert.Constructor = Alert
-
-
- // ALERT NO CONFLICT
- // =================
-
- $.fn.alert.noConflict = function () {
- $.fn.alert = old
- return this
- }
-
-
- // ALERT DATA-API
- // ==============
-
- $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close)
-
-}(jQuery);
diff --git a/vendor/scripts/bootstrap/bootstrap.js b/vendor/scripts/bootstrap/bootstrap.js
deleted file mode 100644
index fee1bda9d..000000000
--- a/vendor/scripts/bootstrap/bootstrap.js
+++ /dev/null
@@ -1,12 +0,0 @@
-//= require affix
-//= require alert
-//= require button
-//= require carousel
-//= require collapse
-//= require dropdown
-//= require tab
-//= require transition
-//= require scrollspy
-//= require modal
-//= require tooltip
-//= require popover
diff --git a/vendor/scripts/bootstrap/button.js b/vendor/scripts/bootstrap/button.js
deleted file mode 100644
index c9fdde5e4..000000000
--- a/vendor/scripts/bootstrap/button.js
+++ /dev/null
@@ -1,115 +0,0 @@
-/* ========================================================================
- * Bootstrap: button.js v3.0.3
- * http://getbootstrap.com/javascript/#buttons
- * ========================================================================
- * Copyright 2013 Twitter, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ======================================================================== */
-
-
-+function ($) { "use strict";
-
- // BUTTON PUBLIC CLASS DEFINITION
- // ==============================
-
- var Button = function (element, options) {
- this.$element = $(element)
- this.options = $.extend({}, Button.DEFAULTS, options)
- }
-
- Button.DEFAULTS = {
- loadingText: 'loading...'
- }
-
- Button.prototype.setState = function (state) {
- var d = 'disabled'
- var $el = this.$element
- var val = $el.is('input') ? 'val' : 'html'
- var data = $el.data()
-
- state = state + 'Text'
-
- if (!data.resetText) $el.data('resetText', $el[val]())
-
- $el[val](data[state] || this.options[state])
-
- // push to event loop to allow forms to submit
- setTimeout(function () {
- state == 'loadingText' ?
- $el.addClass(d).attr(d, d) :
- $el.removeClass(d).removeAttr(d);
- }, 0)
- }
-
- Button.prototype.toggle = function () {
- var $parent = this.$element.closest('[data-toggle="buttons"]')
- var changed = true
-
- if ($parent.length) {
- var $input = this.$element.find('input')
- if ($input.prop('type') === 'radio') {
- // see if clicking on current one
- if ($input.prop('checked') && this.$element.hasClass('active'))
- changed = false
- else
- $parent.find('.active').removeClass('active')
- }
- if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change')
- }
-
- if (changed) this.$element.toggleClass('active')
- }
-
-
- // BUTTON PLUGIN DEFINITION
- // ========================
-
- var old = $.fn.button
-
- $.fn.button = function (option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.button')
- var options = typeof option == 'object' && option
-
- if (!data) $this.data('bs.button', (data = new Button(this, options)))
-
- if (option == 'toggle') data.toggle()
- else if (option) data.setState(option)
- })
- }
-
- $.fn.button.Constructor = Button
-
-
- // BUTTON NO CONFLICT
- // ==================
-
- $.fn.button.noConflict = function () {
- $.fn.button = old
- return this
- }
-
-
- // BUTTON DATA-API
- // ===============
-
- $(document).on('click.bs.button.data-api', '[data-toggle^=button]', function (e) {
- var $btn = $(e.target)
- if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
- $btn.button('toggle')
- e.preventDefault()
- })
-
-}(jQuery);
diff --git a/vendor/scripts/bootstrap/carousel.js b/vendor/scripts/bootstrap/carousel.js
deleted file mode 100644
index 6391a36df..000000000
--- a/vendor/scripts/bootstrap/carousel.js
+++ /dev/null
@@ -1,217 +0,0 @@
-/* ========================================================================
- * Bootstrap: carousel.js v3.0.3
- * http://getbootstrap.com/javascript/#carousel
- * ========================================================================
- * Copyright 2013 Twitter, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ======================================================================== */
-
-
-+function ($) { "use strict";
-
- // CAROUSEL CLASS DEFINITION
- // =========================
-
- var Carousel = function (element, options) {
- this.$element = $(element)
- this.$indicators = this.$element.find('.carousel-indicators')
- this.options = options
- this.paused =
- this.sliding =
- this.interval =
- this.$active =
- this.$items = null
-
- this.options.pause == 'hover' && this.$element
- .on('mouseenter', $.proxy(this.pause, this))
- .on('mouseleave', $.proxy(this.cycle, this))
- }
-
- Carousel.DEFAULTS = {
- interval: 5000
- , pause: 'hover'
- , wrap: true
- }
-
- Carousel.prototype.cycle = function (e) {
- e || (this.paused = false)
-
- this.interval && clearInterval(this.interval)
-
- this.options.interval
- && !this.paused
- && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
-
- return this
- }
-
- Carousel.prototype.getActiveIndex = function () {
- this.$active = this.$element.find('.item.active')
- this.$items = this.$active.parent().children()
-
- return this.$items.index(this.$active)
- }
-
- Carousel.prototype.to = function (pos) {
- var that = this
- var activeIndex = this.getActiveIndex()
-
- if (pos > (this.$items.length - 1) || pos < 0) return
-
- if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) })
- if (activeIndex == pos) return this.pause().cycle()
-
- return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos]))
- }
-
- Carousel.prototype.pause = function (e) {
- e || (this.paused = true)
-
- if (this.$element.find('.next, .prev').length && $.support.transition.end) {
- this.$element.trigger($.support.transition.end)
- this.cycle(true)
- }
-
- this.interval = clearInterval(this.interval)
-
- return this
- }
-
- Carousel.prototype.next = function () {
- if (this.sliding) return
- return this.slide('next')
- }
-
- Carousel.prototype.prev = function () {
- if (this.sliding) return
- return this.slide('prev')
- }
-
- Carousel.prototype.slide = function (type, next) {
- var $active = this.$element.find('.item.active')
- var $next = next || $active[type]()
- var isCycling = this.interval
- var direction = type == 'next' ? 'left' : 'right'
- var fallback = type == 'next' ? 'first' : 'last'
- var that = this
-
- if (!$next.length) {
- if (!this.options.wrap) return
- $next = this.$element.find('.item')[fallback]()
- }
-
- this.sliding = true
-
- isCycling && this.pause()
-
- var e = $.Event('slide.bs.carousel', { relatedTarget: $next[0], direction: direction })
-
- if ($next.hasClass('active')) return
-
- if (this.$indicators.length) {
- this.$indicators.find('.active').removeClass('active')
- this.$element.one('slid.bs.carousel', function () {
- var $nextIndicator = $(that.$indicators.children()[that.getActiveIndex()])
- $nextIndicator && $nextIndicator.addClass('active')
- })
- }
-
- if ($.support.transition && this.$element.hasClass('slide')) {
- this.$element.trigger(e)
- if (e.isDefaultPrevented()) return
- $next.addClass(type)
- $next[0].offsetWidth // force reflow
- $active.addClass(direction)
- $next.addClass(direction)
- $active
- .one($.support.transition.end, function () {
- $next.removeClass([type, direction].join(' ')).addClass('active')
- $active.removeClass(['active', direction].join(' '))
- that.sliding = false
- setTimeout(function () { that.$element.trigger('slid.bs.carousel') }, 0)
- })
- .emulateTransitionEnd(600)
- } else {
- this.$element.trigger(e)
- if (e.isDefaultPrevented()) return
- $active.removeClass('active')
- $next.addClass('active')
- this.sliding = false
- this.$element.trigger('slid.bs.carousel')
- }
-
- isCycling && this.cycle()
-
- return this
- }
-
-
- // CAROUSEL PLUGIN DEFINITION
- // ==========================
-
- var old = $.fn.carousel
-
- $.fn.carousel = function (option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.carousel')
- var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option)
- var action = typeof option == 'string' ? option : options.slide
-
- if (!data) $this.data('bs.carousel', (data = new Carousel(this, options)))
- if (typeof option == 'number') data.to(option)
- else if (action) data[action]()
- else if (options.interval) data.pause().cycle()
- })
- }
-
- $.fn.carousel.Constructor = Carousel
-
-
- // CAROUSEL NO CONFLICT
- // ====================
-
- $.fn.carousel.noConflict = function () {
- $.fn.carousel = old
- return this
- }
-
-
- // CAROUSEL DATA-API
- // =================
-
- $(document).on('click.bs.carousel.data-api', '[data-slide], [data-slide-to]', function (e) {
- var $this = $(this), href
- var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
- var options = $.extend({}, $target.data(), $this.data())
- var slideIndex = $this.attr('data-slide-to')
- if (slideIndex) options.interval = false
-
- $target.carousel(options)
-
- if (slideIndex = $this.attr('data-slide-to')) {
- $target.data('bs.carousel').to(slideIndex)
- }
-
- e.preventDefault()
- })
-
- $(window).on('load', function () {
- $('[data-ride="carousel"]').each(function () {
- var $carousel = $(this)
- $carousel.carousel($carousel.data())
- })
- })
-
-}(jQuery);
diff --git a/vendor/scripts/bootstrap/collapse.js b/vendor/scripts/bootstrap/collapse.js
deleted file mode 100644
index 1a079938e..000000000
--- a/vendor/scripts/bootstrap/collapse.js
+++ /dev/null
@@ -1,179 +0,0 @@
-/* ========================================================================
- * Bootstrap: collapse.js v3.0.3
- * http://getbootstrap.com/javascript/#collapse
- * ========================================================================
- * Copyright 2013 Twitter, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ======================================================================== */
-
-
-+function ($) { "use strict";
-
- // COLLAPSE PUBLIC CLASS DEFINITION
- // ================================
-
- var Collapse = function (element, options) {
- this.$element = $(element)
- this.options = $.extend({}, Collapse.DEFAULTS, options)
- this.transitioning = null
-
- if (this.options.parent) this.$parent = $(this.options.parent)
- if (this.options.toggle) this.toggle()
- }
-
- Collapse.DEFAULTS = {
- toggle: true
- }
-
- Collapse.prototype.dimension = function () {
- var hasWidth = this.$element.hasClass('width')
- return hasWidth ? 'width' : 'height'
- }
-
- Collapse.prototype.show = function () {
- if (this.transitioning || this.$element.hasClass('in')) return
-
- var startEvent = $.Event('show.bs.collapse')
- this.$element.trigger(startEvent)
- if (startEvent.isDefaultPrevented()) return
-
- var actives = this.$parent && this.$parent.find('> .panel > .in')
-
- if (actives && actives.length) {
- var hasData = actives.data('bs.collapse')
- if (hasData && hasData.transitioning) return
- actives.collapse('hide')
- hasData || actives.data('bs.collapse', null)
- }
-
- var dimension = this.dimension()
-
- this.$element
- .removeClass('collapse')
- .addClass('collapsing')
- [dimension](0)
-
- this.transitioning = 1
-
- var complete = function () {
- this.$element
- .removeClass('collapsing')
- .addClass('in')
- [dimension]('auto')
- this.transitioning = 0
- this.$element.trigger('shown.bs.collapse')
- }
-
- if (!$.support.transition) return complete.call(this)
-
- var scrollSize = $.camelCase(['scroll', dimension].join('-'))
-
- this.$element
- .one($.support.transition.end, $.proxy(complete, this))
- .emulateTransitionEnd(350)
- [dimension](this.$element[0][scrollSize])
- }
-
- Collapse.prototype.hide = function () {
- if (this.transitioning || !this.$element.hasClass('in')) return
-
- var startEvent = $.Event('hide.bs.collapse')
- this.$element.trigger(startEvent)
- if (startEvent.isDefaultPrevented()) return
-
- var dimension = this.dimension()
-
- this.$element
- [dimension](this.$element[dimension]())
- [0].offsetHeight
-
- this.$element
- .addClass('collapsing')
- .removeClass('collapse')
- .removeClass('in')
-
- this.transitioning = 1
-
- var complete = function () {
- this.transitioning = 0
- this.$element
- .trigger('hidden.bs.collapse')
- .removeClass('collapsing')
- .addClass('collapse')
- }
-
- if (!$.support.transition) return complete.call(this)
-
- this.$element
- [dimension](0)
- .one($.support.transition.end, $.proxy(complete, this))
- .emulateTransitionEnd(350)
- }
-
- Collapse.prototype.toggle = function () {
- this[this.$element.hasClass('in') ? 'hide' : 'show']()
- }
-
-
- // COLLAPSE PLUGIN DEFINITION
- // ==========================
-
- var old = $.fn.collapse
-
- $.fn.collapse = function (option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.collapse')
- var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)
-
- if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))
- if (typeof option == 'string') data[option]()
- })
- }
-
- $.fn.collapse.Constructor = Collapse
-
-
- // COLLAPSE NO CONFLICT
- // ====================
-
- $.fn.collapse.noConflict = function () {
- $.fn.collapse = old
- return this
- }
-
-
- // COLLAPSE DATA-API
- // =================
-
- $(document).on('click.bs.collapse.data-api', '[data-toggle=collapse]', function (e) {
- var $this = $(this), href
- var target = $this.attr('data-target')
- || e.preventDefault()
- || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
- var $target = $(target)
- var data = $target.data('bs.collapse')
- var option = data ? 'toggle' : $this.data()
- var parent = $this.attr('data-parent')
- var $parent = parent && $(parent)
-
- if (!data || !data.transitioning) {
- if ($parent) $parent.find('[data-toggle=collapse][data-parent="' + parent + '"]').not($this).addClass('collapsed')
- $this[$target.hasClass('in') ? 'addClass' : 'removeClass']('collapsed')
- }
-
- $target.collapse(option)
- })
-
-}(jQuery);
diff --git a/vendor/scripts/bootstrap/dropdown.js b/vendor/scripts/bootstrap/dropdown.js
deleted file mode 100644
index 13352ef7c..000000000
--- a/vendor/scripts/bootstrap/dropdown.js
+++ /dev/null
@@ -1,154 +0,0 @@
-/* ========================================================================
- * Bootstrap: dropdown.js v3.0.3
- * http://getbootstrap.com/javascript/#dropdowns
- * ========================================================================
- * Copyright 2013 Twitter, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ======================================================================== */
-
-
-+function ($) { "use strict";
-
- // DROPDOWN CLASS DEFINITION
- // =========================
-
- var backdrop = '.dropdown-backdrop'
- var toggle = '[data-toggle=dropdown]'
- var Dropdown = function (element) {
- $(element).on('click.bs.dropdown', this.toggle)
- }
-
- Dropdown.prototype.toggle = function (e) {
- var $this = $(this)
-
- if ($this.is('.disabled, :disabled')) return
-
- var $parent = getParent($this)
- var isActive = $parent.hasClass('open')
-
- clearMenus()
-
- if (!isActive) {
- if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
- // if mobile we use a backdrop because click events don't delegate
- $('').insertAfter($(this)).on('click', clearMenus)
- }
-
- $parent.trigger(e = $.Event('show.bs.dropdown'))
-
- if (e.isDefaultPrevented()) return
-
- $parent
- .toggleClass('open')
- .trigger('shown.bs.dropdown')
-
- $this.focus()
- }
-
- return false
- }
-
- Dropdown.prototype.keydown = function (e) {
- if (!/(38|40|27)/.test(e.keyCode)) return
-
- var $this = $(this)
-
- e.preventDefault()
- e.stopPropagation()
-
- if ($this.is('.disabled, :disabled')) return
-
- var $parent = getParent($this)
- var isActive = $parent.hasClass('open')
-
- if (!isActive || (isActive && e.keyCode == 27)) {
- if (e.which == 27) $parent.find(toggle).focus()
- return $this.click()
- }
-
- var $items = $('[role=menu] li:not(.divider):visible a', $parent)
-
- if (!$items.length) return
-
- var index = $items.index($items.filter(':focus'))
-
- if (e.keyCode == 38 && index > 0) index-- // up
- if (e.keyCode == 40 && index < $items.length - 1) index++ // down
- if (!~index) index=0
-
- $items.eq(index).focus()
- }
-
- function clearMenus() {
- $(backdrop).remove()
- $(toggle).each(function (e) {
- var $parent = getParent($(this))
- if (!$parent.hasClass('open')) return
- $parent.trigger(e = $.Event('hide.bs.dropdown'))
- if (e.isDefaultPrevented()) return
- $parent.removeClass('open').trigger('hidden.bs.dropdown')
- })
- }
-
- function getParent($this) {
- var selector = $this.attr('data-target')
-
- if (!selector) {
- selector = $this.attr('href')
- selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
- }
-
- var $parent = selector && $(selector)
-
- return $parent && $parent.length ? $parent : $this.parent()
- }
-
-
- // DROPDOWN PLUGIN DEFINITION
- // ==========================
-
- var old = $.fn.dropdown
-
- $.fn.dropdown = function (option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.dropdown')
-
- if (!data) $this.data('bs.dropdown', (data = new Dropdown(this)))
- if (typeof option == 'string') data[option].call($this)
- })
- }
-
- $.fn.dropdown.Constructor = Dropdown
-
-
- // DROPDOWN NO CONFLICT
- // ====================
-
- $.fn.dropdown.noConflict = function () {
- $.fn.dropdown = old
- return this
- }
-
-
- // APPLY TO STANDARD DROPDOWN ELEMENTS
- // ===================================
-
- $(document)
- .on('click.bs.dropdown.data-api', clearMenus)
- .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
- .on('click.bs.dropdown.data-api' , toggle, Dropdown.prototype.toggle)
- .on('keydown.bs.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown)
-
-}(jQuery);
diff --git a/vendor/scripts/bootstrap/modal.js b/vendor/scripts/bootstrap/modal.js
deleted file mode 100644
index 3ead5ee88..000000000
--- a/vendor/scripts/bootstrap/modal.js
+++ /dev/null
@@ -1,246 +0,0 @@
-/* ========================================================================
- * Bootstrap: modal.js v3.0.3
- * http://getbootstrap.com/javascript/#modals
- * ========================================================================
- * Copyright 2013 Twitter, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ======================================================================== */
-
-
-+function ($) { "use strict";
-
- // MODAL CLASS DEFINITION
- // ======================
-
- var Modal = function (element, options) {
- this.options = options
- this.$element = $(element)
- this.$backdrop =
- this.isShown = null
-
- if (this.options.remote) this.$element.load(this.options.remote)
- }
-
- Modal.DEFAULTS = {
- backdrop: true
- , keyboard: true
- , show: true
- }
-
- Modal.prototype.toggle = function (_relatedTarget) {
- return this[!this.isShown ? 'show' : 'hide'](_relatedTarget)
- }
-
- Modal.prototype.show = function (_relatedTarget) {
- var that = this
- var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
-
- this.$element.trigger(e)
-
- if (this.isShown || e.isDefaultPrevented()) return
-
- this.isShown = true
-
- this.escape()
-
- this.$element.on('click.dismiss.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
-
- this.backdrop(function () {
- var transition = $.support.transition && that.$element.hasClass('fade')
-
- if (!that.$element.parent().length) {
- that.$element.appendTo(document.body) // don't move modals dom position
- }
-
- that.$element.show()
-
- if (transition) {
- that.$element[0].offsetWidth // force reflow
- }
-
- that.$element
- .addClass('in')
- .attr('aria-hidden', false)
-
- that.enforceFocus()
-
- var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
-
- transition ?
- that.$element.find('.modal-dialog') // wait for modal to slide in
- .one($.support.transition.end, function () {
- that.$element.focus().trigger(e)
- })
- .emulateTransitionEnd(300) :
- that.$element.focus().trigger(e)
- })
- }
-
- Modal.prototype.hide = function (e) {
- if (e) e.preventDefault()
-
- e = $.Event('hide.bs.modal')
-
- this.$element.trigger(e)
-
- if (!this.isShown || e.isDefaultPrevented()) return
-
- this.isShown = false
-
- this.escape()
-
- $(document).off('focusin.bs.modal')
-
- this.$element
- .removeClass('in')
- .attr('aria-hidden', true)
- .off('click.dismiss.modal')
-
- $.support.transition && this.$element.hasClass('fade') ?
- this.$element
- .one($.support.transition.end, $.proxy(this.hideModal, this))
- .emulateTransitionEnd(300) :
- this.hideModal()
- }
-
- Modal.prototype.enforceFocus = function () {
- $(document)
- .off('focusin.bs.modal') // guard against infinite focus loop
- .on('focusin.bs.modal', $.proxy(function (e) {
- if (this.$element[0] !== e.target && !this.$element.has(e.target).length) {
- this.$element.focus()
- }
- }, this))
- }
-
- Modal.prototype.escape = function () {
- if (this.isShown && this.options.keyboard) {
- this.$element.on('keyup.dismiss.bs.modal', $.proxy(function (e) {
- e.which == 27 && this.hide()
- }, this))
- } else if (!this.isShown) {
- this.$element.off('keyup.dismiss.bs.modal')
- }
- }
-
- Modal.prototype.hideModal = function () {
- var that = this
- this.$element.hide()
- this.backdrop(function () {
- that.removeBackdrop()
- that.$element.trigger('hidden.bs.modal')
- })
- }
-
- Modal.prototype.removeBackdrop = function () {
- this.$backdrop && this.$backdrop.remove()
- this.$backdrop = null
- }
-
- Modal.prototype.backdrop = function (callback) {
- var that = this
- var animate = this.$element.hasClass('fade') ? 'fade' : ''
-
- if (this.isShown && this.options.backdrop) {
- var doAnimate = $.support.transition && animate
-
- this.$backdrop = $('')
- .appendTo(document.body)
-
- this.$element.on('click.dismiss.modal', $.proxy(function (e) {
- if (e.target !== e.currentTarget) return
- this.options.backdrop == 'static'
- ? this.$element[0].focus.call(this.$element[0])
- : this.hide.call(this)
- }, this))
-
- if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
-
- this.$backdrop.addClass('in')
-
- if (!callback) return
-
- doAnimate ?
- this.$backdrop
- .one($.support.transition.end, callback)
- .emulateTransitionEnd(150) :
- callback()
-
- } else if (!this.isShown && this.$backdrop) {
- this.$backdrop.removeClass('in')
-
- $.support.transition && this.$element.hasClass('fade')?
- this.$backdrop
- .one($.support.transition.end, callback)
- .emulateTransitionEnd(150) :
- callback()
-
- } else if (callback) {
- callback()
- }
- }
-
-
- // MODAL PLUGIN DEFINITION
- // =======================
-
- var old = $.fn.modal
-
- $.fn.modal = function (option, _relatedTarget) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.modal')
- var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
-
- if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
- if (typeof option == 'string') data[option](_relatedTarget)
- else if (options.show) data.show(_relatedTarget)
- })
- }
-
- $.fn.modal.Constructor = Modal
-
-
- // MODAL NO CONFLICT
- // =================
-
- $.fn.modal.noConflict = function () {
- $.fn.modal = old
- return this
- }
-
-
- // MODAL DATA-API
- // ==============
-
- $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
- var $this = $(this)
- var href = $this.attr('href')
- var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) //strip for ie7
- var option = $target.data('modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
-
- e.preventDefault()
-
- $target
- .modal(option, this)
- .one('hide', function () {
- $this.is(':visible') && $this.focus()
- })
- })
-
- $(document)
- .on('show.bs.modal', '.modal', function () { $(document.body).addClass('modal-open') })
- .on('hidden.bs.modal', '.modal', function () { $(document.body).removeClass('modal-open') })
-
-}(jQuery);
diff --git a/vendor/scripts/bootstrap/popover.js b/vendor/scripts/bootstrap/popover.js
deleted file mode 100644
index 996962aa2..000000000
--- a/vendor/scripts/bootstrap/popover.js
+++ /dev/null
@@ -1,117 +0,0 @@
-/* ========================================================================
- * Bootstrap: popover.js v3.0.3
- * http://getbootstrap.com/javascript/#popovers
- * ========================================================================
- * Copyright 2013 Twitter, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ======================================================================== */
-
-
-+function ($) { "use strict";
-
- // POPOVER PUBLIC CLASS DEFINITION
- // ===============================
-
- var Popover = function (element, options) {
- this.init('popover', element, options)
- }
-
- if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')
-
- Popover.DEFAULTS = $.extend({} , $.fn.tooltip.Constructor.DEFAULTS, {
- placement: 'right'
- , trigger: 'click'
- , content: ''
- , template: ''
- })
-
-
- // NOTE: POPOVER EXTENDS tooltip.js
- // ================================
-
- Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype)
-
- Popover.prototype.constructor = Popover
-
- Popover.prototype.getDefaults = function () {
- return Popover.DEFAULTS
- }
-
- Popover.prototype.setContent = function () {
- var $tip = this.tip()
- var title = this.getTitle()
- var content = this.getContent()
-
- $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
- $tip.find('.popover-content')[this.options.html ? 'html' : 'text'](content)
-
- $tip.removeClass('fade top bottom left right in')
-
- // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do
- // this manually by checking the contents.
- if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide()
- }
-
- Popover.prototype.hasContent = function () {
- return this.getTitle() || this.getContent()
- }
-
- Popover.prototype.getContent = function () {
- var $e = this.$element
- var o = this.options
-
- return $e.attr('data-content')
- || (typeof o.content == 'function' ?
- o.content.call($e[0]) :
- o.content)
- }
-
- Popover.prototype.arrow = function () {
- return this.$arrow = this.$arrow || this.tip().find('.arrow')
- }
-
- Popover.prototype.tip = function () {
- if (!this.$tip) this.$tip = $(this.options.template)
- return this.$tip
- }
-
-
- // POPOVER PLUGIN DEFINITION
- // =========================
-
- var old = $.fn.popover
-
- $.fn.popover = function (option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.popover')
- var options = typeof option == 'object' && option
-
- if (!data) $this.data('bs.popover', (data = new Popover(this, options)))
- if (typeof option == 'string') data[option]()
- })
- }
-
- $.fn.popover.Constructor = Popover
-
-
- // POPOVER NO CONFLICT
- // ===================
-
- $.fn.popover.noConflict = function () {
- $.fn.popover = old
- return this
- }
-
-}(jQuery);
diff --git a/vendor/scripts/bootstrap/scrollspy.js b/vendor/scripts/bootstrap/scrollspy.js
deleted file mode 100644
index 2efe14fdd..000000000
--- a/vendor/scripts/bootstrap/scrollspy.js
+++ /dev/null
@@ -1,158 +0,0 @@
-/* ========================================================================
- * Bootstrap: scrollspy.js v3.0.3
- * http://getbootstrap.com/javascript/#scrollspy
- * ========================================================================
- * Copyright 2013 Twitter, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ======================================================================== */
-
-
-+function ($) { "use strict";
-
- // SCROLLSPY CLASS DEFINITION
- // ==========================
-
- function ScrollSpy(element, options) {
- var href
- var process = $.proxy(this.process, this)
-
- this.$element = $(element).is('body') ? $(window) : $(element)
- this.$body = $('body')
- this.$scrollElement = this.$element.on('scroll.bs.scroll-spy.data-api', process)
- this.options = $.extend({}, ScrollSpy.DEFAULTS, options)
- this.selector = (this.options.target
- || ((href = $(element).attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
- || '') + ' .nav li > a'
- this.offsets = $([])
- this.targets = $([])
- this.activeTarget = null
-
- this.refresh()
- this.process()
- }
-
- ScrollSpy.DEFAULTS = {
- offset: 10
- }
-
- ScrollSpy.prototype.refresh = function () {
- var offsetMethod = this.$element[0] == window ? 'offset' : 'position'
-
- this.offsets = $([])
- this.targets = $([])
-
- var self = this
- var $targets = this.$body
- .find(this.selector)
- .map(function () {
- var $el = $(this)
- var href = $el.data('target') || $el.attr('href')
- var $href = /^#\w/.test(href) && $(href)
-
- return ($href
- && $href.length
- && [[ $href[offsetMethod]().top + (!$.isWindow(self.$scrollElement.get(0)) && self.$scrollElement.scrollTop()), href ]]) || null
- })
- .sort(function (a, b) { return a[0] - b[0] })
- .each(function () {
- self.offsets.push(this[0])
- self.targets.push(this[1])
- })
- }
-
- ScrollSpy.prototype.process = function () {
- var scrollTop = this.$scrollElement.scrollTop() + this.options.offset
- var scrollHeight = this.$scrollElement[0].scrollHeight || this.$body[0].scrollHeight
- var maxScroll = scrollHeight - this.$scrollElement.height()
- var offsets = this.offsets
- var targets = this.targets
- var activeTarget = this.activeTarget
- var i
-
- if (scrollTop >= maxScroll) {
- return activeTarget != (i = targets.last()[0]) && this.activate(i)
- }
-
- for (i = offsets.length; i--;) {
- activeTarget != targets[i]
- && scrollTop >= offsets[i]
- && (!offsets[i + 1] || scrollTop <= offsets[i + 1])
- && this.activate( targets[i] )
- }
- }
-
- ScrollSpy.prototype.activate = function (target) {
- this.activeTarget = target
-
- $(this.selector)
- .parents('.active')
- .removeClass('active')
-
- var selector = this.selector
- + '[data-target="' + target + '"],'
- + this.selector + '[href="' + target + '"]'
-
- var active = $(selector)
- .parents('li')
- .addClass('active')
-
- if (active.parent('.dropdown-menu').length) {
- active = active
- .closest('li.dropdown')
- .addClass('active')
- }
-
- active.trigger('activate.bs.scrollspy')
- }
-
-
- // SCROLLSPY PLUGIN DEFINITION
- // ===========================
-
- var old = $.fn.scrollspy
-
- $.fn.scrollspy = function (option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.scrollspy')
- var options = typeof option == 'object' && option
-
- if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options)))
- if (typeof option == 'string') data[option]()
- })
- }
-
- $.fn.scrollspy.Constructor = ScrollSpy
-
-
- // SCROLLSPY NO CONFLICT
- // =====================
-
- $.fn.scrollspy.noConflict = function () {
- $.fn.scrollspy = old
- return this
- }
-
-
- // SCROLLSPY DATA-API
- // ==================
-
- $(window).on('load', function () {
- $('[data-spy="scroll"]').each(function () {
- var $spy = $(this)
- $spy.scrollspy($spy.data())
- })
- })
-
-}(jQuery);
diff --git a/vendor/scripts/bootstrap/tab.js b/vendor/scripts/bootstrap/tab.js
deleted file mode 100644
index 6b0f5f672..000000000
--- a/vendor/scripts/bootstrap/tab.js
+++ /dev/null
@@ -1,135 +0,0 @@
-/* ========================================================================
- * Bootstrap: tab.js v3.0.3
- * http://getbootstrap.com/javascript/#tabs
- * ========================================================================
- * Copyright 2013 Twitter, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ======================================================================== */
-
-
-+function ($) { "use strict";
-
- // TAB CLASS DEFINITION
- // ====================
-
- var Tab = function (element) {
- this.element = $(element)
- }
-
- Tab.prototype.show = function () {
- var $this = this.element
- var $ul = $this.closest('ul:not(.dropdown-menu)')
- var selector = $this.data('target')
-
- if (!selector) {
- selector = $this.attr('href')
- selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
- }
-
- if ($this.parent('li').hasClass('active')) return
-
- var previous = $ul.find('.active:last a')[0]
- var e = $.Event('show.bs.tab', {
- relatedTarget: previous
- })
-
- $this.trigger(e)
-
- if (e.isDefaultPrevented()) return
-
- var $target = $(selector)
-
- this.activate($this.parent('li'), $ul)
- this.activate($target, $target.parent(), function () {
- $this.trigger({
- type: 'shown.bs.tab'
- , relatedTarget: previous
- })
- })
- }
-
- Tab.prototype.activate = function (element, container, callback) {
- var $active = container.find('> .active')
- var transition = callback
- && $.support.transition
- && $active.hasClass('fade')
-
- function next() {
- $active
- .removeClass('active')
- .find('> .dropdown-menu > .active')
- .removeClass('active')
-
- element.addClass('active')
-
- if (transition) {
- element[0].offsetWidth // reflow for transition
- element.addClass('in')
- } else {
- element.removeClass('fade')
- }
-
- if (element.parent('.dropdown-menu')) {
- element.closest('li.dropdown').addClass('active')
- }
-
- callback && callback()
- }
-
- transition ?
- $active
- .one($.support.transition.end, next)
- .emulateTransitionEnd(150) :
- next()
-
- $active.removeClass('in')
- }
-
-
- // TAB PLUGIN DEFINITION
- // =====================
-
- var old = $.fn.tab
-
- $.fn.tab = function ( option ) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.tab')
-
- if (!data) $this.data('bs.tab', (data = new Tab(this)))
- if (typeof option == 'string') data[option]()
- })
- }
-
- $.fn.tab.Constructor = Tab
-
-
- // TAB NO CONFLICT
- // ===============
-
- $.fn.tab.noConflict = function () {
- $.fn.tab = old
- return this
- }
-
-
- // TAB DATA-API
- // ============
-
- $(document).on('click.bs.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
- e.preventDefault()
- $(this).tab('show')
- })
-
-}(jQuery);
diff --git a/vendor/scripts/bootstrap/tooltip.js b/vendor/scripts/bootstrap/tooltip.js
deleted file mode 100644
index 4c848f0e2..000000000
--- a/vendor/scripts/bootstrap/tooltip.js
+++ /dev/null
@@ -1,386 +0,0 @@
-/* ========================================================================
- * Bootstrap: tooltip.js v3.0.3
- * http://getbootstrap.com/javascript/#tooltip
- * Inspired by the original jQuery.tipsy by Jason Frame
- * ========================================================================
- * Copyright 2013 Twitter, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ======================================================================== */
-
-
-+function ($) { "use strict";
-
- // TOOLTIP PUBLIC CLASS DEFINITION
- // ===============================
-
- var Tooltip = function (element, options) {
- this.type =
- this.options =
- this.enabled =
- this.timeout =
- this.hoverState =
- this.$element = null
-
- this.init('tooltip', element, options)
- }
-
- Tooltip.DEFAULTS = {
- animation: true
- , placement: 'top'
- , selector: false
- , template: ''
- , trigger: 'hover focus'
- , title: ''
- , delay: 0
- , html: false
- , container: false
- }
-
- Tooltip.prototype.init = function (type, element, options) {
- this.enabled = true
- this.type = type
- this.$element = $(element)
- this.options = this.getOptions(options)
-
- var triggers = this.options.trigger.split(' ')
-
- for (var i = triggers.length; i--;) {
- var trigger = triggers[i]
-
- if (trigger == 'click') {
- this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
- } else if (trigger != 'manual') {
- var eventIn = trigger == 'hover' ? 'mouseenter' : 'focus'
- var eventOut = trigger == 'hover' ? 'mouseleave' : 'blur'
-
- this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
- this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
- }
- }
-
- this.options.selector ?
- (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
- this.fixTitle()
- }
-
- Tooltip.prototype.getDefaults = function () {
- return Tooltip.DEFAULTS
- }
-
- Tooltip.prototype.getOptions = function (options) {
- options = $.extend({}, this.getDefaults(), this.$element.data(), options)
-
- if (options.delay && typeof options.delay == 'number') {
- options.delay = {
- show: options.delay
- , hide: options.delay
- }
- }
-
- return options
- }
-
- Tooltip.prototype.getDelegateOptions = function () {
- var options = {}
- var defaults = this.getDefaults()
-
- this._options && $.each(this._options, function (key, value) {
- if (defaults[key] != value) options[key] = value
- })
-
- return options
- }
-
- Tooltip.prototype.enter = function (obj) {
- var self = obj instanceof this.constructor ?
- obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)
-
- clearTimeout(self.timeout)
-
- self.hoverState = 'in'
-
- if (!self.options.delay || !self.options.delay.show) return self.show()
-
- self.timeout = setTimeout(function () {
- if (self.hoverState == 'in') self.show()
- }, self.options.delay.show)
- }
-
- Tooltip.prototype.leave = function (obj) {
- var self = obj instanceof this.constructor ?
- obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)
-
- clearTimeout(self.timeout)
-
- self.hoverState = 'out'
-
- if (!self.options.delay || !self.options.delay.hide) return self.hide()
-
- self.timeout = setTimeout(function () {
- if (self.hoverState == 'out') self.hide()
- }, self.options.delay.hide)
- }
-
- Tooltip.prototype.show = function () {
- var e = $.Event('show.bs.'+ this.type)
-
- if (this.hasContent() && this.enabled) {
- this.$element.trigger(e)
-
- if (e.isDefaultPrevented()) return
-
- var $tip = this.tip()
-
- this.setContent()
-
- if (this.options.animation) $tip.addClass('fade')
-
- var placement = typeof this.options.placement == 'function' ?
- this.options.placement.call(this, $tip[0], this.$element[0]) :
- this.options.placement
-
- var autoToken = /\s?auto?\s?/i
- var autoPlace = autoToken.test(placement)
- if (autoPlace) placement = placement.replace(autoToken, '') || 'top'
-
- $tip
- .detach()
- .css({ top: 0, left: 0, display: 'block' })
- .addClass(placement)
-
- this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
-
- var pos = this.getPosition()
- var actualWidth = $tip[0].offsetWidth
- var actualHeight = $tip[0].offsetHeight
-
- if (autoPlace) {
- var $parent = this.$element.parent()
-
- var orgPlacement = placement
- var docScroll = document.documentElement.scrollTop || document.body.scrollTop
- var parentWidth = this.options.container == 'body' ? window.innerWidth : $parent.outerWidth()
- var parentHeight = this.options.container == 'body' ? window.innerHeight : $parent.outerHeight()
- var parentLeft = this.options.container == 'body' ? 0 : $parent.offset().left
-
- placement = placement == 'bottom' && pos.top + pos.height + actualHeight - docScroll > parentHeight ? 'top' :
- placement == 'top' && pos.top - docScroll - actualHeight < 0 ? 'bottom' :
- placement == 'right' && pos.right + actualWidth > parentWidth ? 'left' :
- placement == 'left' && pos.left - actualWidth < parentLeft ? 'right' :
- placement
-
- $tip
- .removeClass(orgPlacement)
- .addClass(placement)
- }
-
- var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
-
- this.applyPlacement(calculatedOffset, placement)
- this.$element.trigger('shown.bs.' + this.type)
- }
- }
-
- Tooltip.prototype.applyPlacement = function(offset, placement) {
- var replace
- var $tip = this.tip()
- var width = $tip[0].offsetWidth
- var height = $tip[0].offsetHeight
-
- // manually read margins because getBoundingClientRect includes difference
- var marginTop = parseInt($tip.css('margin-top'), 10)
- var marginLeft = parseInt($tip.css('margin-left'), 10)
-
- // we must check for NaN for ie 8/9
- if (isNaN(marginTop)) marginTop = 0
- if (isNaN(marginLeft)) marginLeft = 0
-
- offset.top = offset.top + marginTop
- offset.left = offset.left + marginLeft
-
- $tip
- .offset(offset)
- .addClass('in')
-
- // check to see if placing tip in new offset caused the tip to resize itself
- var actualWidth = $tip[0].offsetWidth
- var actualHeight = $tip[0].offsetHeight
-
- if (placement == 'top' && actualHeight != height) {
- replace = true
- offset.top = offset.top + height - actualHeight
- }
-
- if (/bottom|top/.test(placement)) {
- var delta = 0
-
- if (offset.left < 0) {
- delta = offset.left * -2
- offset.left = 0
-
- $tip.offset(offset)
-
- actualWidth = $tip[0].offsetWidth
- actualHeight = $tip[0].offsetHeight
- }
-
- this.replaceArrow(delta - width + actualWidth, actualWidth, 'left')
- } else {
- this.replaceArrow(actualHeight - height, actualHeight, 'top')
- }
-
- if (replace) $tip.offset(offset)
- }
-
- Tooltip.prototype.replaceArrow = function(delta, dimension, position) {
- this.arrow().css(position, delta ? (50 * (1 - delta / dimension) + "%") : '')
- }
-
- Tooltip.prototype.setContent = function () {
- var $tip = this.tip()
- var title = this.getTitle()
-
- $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
- $tip.removeClass('fade in top bottom left right')
- }
-
- Tooltip.prototype.hide = function () {
- var that = this
- var $tip = this.tip()
- var e = $.Event('hide.bs.' + this.type)
-
- function complete() {
- if (that.hoverState != 'in') $tip.detach()
- }
-
- this.$element.trigger(e)
-
- if (e.isDefaultPrevented()) return
-
- $tip.removeClass('in')
-
- $.support.transition && this.$tip.hasClass('fade') ?
- $tip
- .one($.support.transition.end, complete)
- .emulateTransitionEnd(150) :
- complete()
-
- this.$element.trigger('hidden.bs.' + this.type)
-
- return this
- }
-
- Tooltip.prototype.fixTitle = function () {
- var $e = this.$element
- if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
- $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
- }
- }
-
- Tooltip.prototype.hasContent = function () {
- return this.getTitle()
- }
-
- Tooltip.prototype.getPosition = function () {
- var el = this.$element[0]
- return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : {
- width: el.offsetWidth
- , height: el.offsetHeight
- }, this.$element.offset())
- }
-
- Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
- return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } :
- placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } :
- placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :
- /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width }
- }
-
- Tooltip.prototype.getTitle = function () {
- var title
- var $e = this.$element
- var o = this.options
-
- title = $e.attr('data-original-title')
- || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
-
- return title
- }
-
- Tooltip.prototype.tip = function () {
- return this.$tip = this.$tip || $(this.options.template)
- }
-
- Tooltip.prototype.arrow = function () {
- return this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')
- }
-
- Tooltip.prototype.validate = function () {
- if (!this.$element[0].parentNode) {
- this.hide()
- this.$element = null
- this.options = null
- }
- }
-
- Tooltip.prototype.enable = function () {
- this.enabled = true
- }
-
- Tooltip.prototype.disable = function () {
- this.enabled = false
- }
-
- Tooltip.prototype.toggleEnabled = function () {
- this.enabled = !this.enabled
- }
-
- Tooltip.prototype.toggle = function (e) {
- var self = e ? $(e.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type) : this
- self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
- }
-
- Tooltip.prototype.destroy = function () {
- this.hide().$element.off('.' + this.type).removeData('bs.' + this.type)
- }
-
-
- // TOOLTIP PLUGIN DEFINITION
- // =========================
-
- var old = $.fn.tooltip
-
- $.fn.tooltip = function (option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.tooltip')
- var options = typeof option == 'object' && option
-
- if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
- if (typeof option == 'string') data[option]()
- })
- }
-
- $.fn.tooltip.Constructor = Tooltip
-
-
- // TOOLTIP NO CONFLICT
- // ===================
-
- $.fn.tooltip.noConflict = function () {
- $.fn.tooltip = old
- return this
- }
-
-}(jQuery);
diff --git a/vendor/scripts/bootstrap/transition.js b/vendor/scripts/bootstrap/transition.js
deleted file mode 100644
index 773dbe693..000000000
--- a/vendor/scripts/bootstrap/transition.js
+++ /dev/null
@@ -1,56 +0,0 @@
-/* ========================================================================
- * Bootstrap: transition.js v3.0.3
- * http://getbootstrap.com/javascript/#transitions
- * ========================================================================
- * Copyright 2013 Twitter, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ======================================================================== */
-
-
-+function ($) { "use strict";
-
- // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
- // ============================================================
-
- function transitionEnd() {
- var el = document.createElement('bootstrap')
-
- var transEndEventNames = {
- 'WebkitTransition' : 'webkitTransitionEnd'
- , 'MozTransition' : 'transitionend'
- , 'OTransition' : 'oTransitionEnd otransitionend'
- , 'transition' : 'transitionend'
- }
-
- for (var name in transEndEventNames) {
- if (el.style[name] !== undefined) {
- return { end: transEndEventNames[name] }
- }
- }
- }
-
- // http://blog.alexmaccaw.com/css-transitions
- $.fn.emulateTransitionEnd = function (duration) {
- var called = false, $el = this
- $(this).one($.support.transition.end, function () { called = true })
- var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
- setTimeout(callback, duration)
- return this
- }
-
- $(function () {
- $.support.transition = transitionEnd()
- })
-
-}(jQuery);
diff --git a/vendor/scripts/treema.js b/vendor/scripts/treema.js
deleted file mode 100644
index e02ce1980..000000000
--- a/vendor/scripts/treema.js
+++ /dev/null
@@ -1,3430 +0,0 @@
-(function() {
- var WebSocket = window.WebSocket || window.MozWebSocket;
- var br = window.brunch = (window.brunch || {});
- var ar = br['auto-reload'] = (br['auto-reload'] || {});
- if (!WebSocket || ar.disabled) return;
-
- var cacheBuster = function(url){
- var date = Math.round(Date.now() / 1000).toString();
- url = url.replace(/(\&|\\?)cacheBuster=\d*/, '');
- return url + (url.indexOf('?') >= 0 ? '&' : '?') +'cacheBuster=' + date;
- };
-
- var reloaders = {
- page: function(){
- window.location.reload(true);
- },
-
- stylesheet: function(){
- [].slice
- .call(document.querySelectorAll('link[rel="stylesheet"]'))
- .filter(function(link){
- return (link != null && link.href != null);
- })
- .forEach(function(link) {
- link.href = cacheBuster(link.href);
- });
- }
- };
- var port = ar.port || 9485;
- var host = (!br['server']) ? window.location.hostname : br['server'];
- var connection = new WebSocket('ws://' + host + ':' + port);
- connection.onmessage = function(event) {
- var message = event.data;
- if (ar.disabled) return;
- if (reloaders[message] != null) {
- reloaders[message]();
- } else {
- reloaders.page();
- }
- };
-})();
-
-;
-jade = (function(exports){
-/*!
- * Jade - runtime
- * Copyright(c) 2010 TJ Holowaychuk
- * MIT Licensed
- */
-
-/**
- * Lame Array.isArray() polyfill for now.
- */
-
-if (!Array.isArray) {
- Array.isArray = function(arr){
- return '[object Array]' == Object.prototype.toString.call(arr);
- };
-}
-
-/**
- * Lame Object.keys() polyfill for now.
- */
-
-if (!Object.keys) {
- Object.keys = function(obj){
- var arr = [];
- for (var key in obj) {
- if (obj.hasOwnProperty(key)) {
- arr.push(key);
- }
- }
- return arr;
- }
-}
-
-/**
- * Merge two attribute objects giving precedence
- * to values in object `b`. Classes are special-cased
- * allowing for arrays and merging/joining appropriately
- * resulting in a string.
- *
- * @param {Object} a
- * @param {Object} b
- * @return {Object} a
- * @api private
- */
-
-exports.merge = function merge(a, b) {
- var ac = a['class'];
- var bc = b['class'];
-
- if (ac || bc) {
- ac = ac || [];
- bc = bc || [];
- if (!Array.isArray(ac)) ac = [ac];
- if (!Array.isArray(bc)) bc = [bc];
- ac = ac.filter(nulls);
- bc = bc.filter(nulls);
- a['class'] = ac.concat(bc).join(' ');
- }
-
- for (var key in b) {
- if (key != 'class') {
- a[key] = b[key];
- }
- }
-
- return a;
-};
-
-/**
- * Filter null `val`s.
- *
- * @param {Mixed} val
- * @return {Mixed}
- * @api private
- */
-
-function nulls(val) {
- return val != null;
-}
-
-/**
- * Render the given attributes object.
- *
- * @param {Object} obj
- * @param {Object} escaped
- * @return {String}
- * @api private
- */
-
-exports.attrs = function attrs(obj, escaped){
- var buf = []
- , terse = obj.terse;
-
- delete obj.terse;
- var keys = Object.keys(obj)
- , len = keys.length;
-
- if (len) {
- buf.push('');
- for (var i = 0; i < len; ++i) {
- var key = keys[i]
- , val = obj[key];
-
- if ('boolean' == typeof val || null == val) {
- if (val) {
- terse
- ? buf.push(key)
- : buf.push(key + '="' + key + '"');
- }
- } else if (0 == key.indexOf('data') && 'string' != typeof val) {
- buf.push(key + "='" + JSON.stringify(val) + "'");
- } else if ('class' == key && Array.isArray(val)) {
- buf.push(key + '="' + exports.escape(val.join(' ')) + '"');
- } else if (escaped && escaped[key]) {
- buf.push(key + '="' + exports.escape(val) + '"');
- } else {
- buf.push(key + '="' + val + '"');
- }
- }
- }
-
- return buf.join(' ');
-};
-
-/**
- * Escape the given string of `html`.
- *
- * @param {String} html
- * @return {String}
- * @api private
- */
-
-exports.escape = function escape(html){
- return String(html)
- .replace(/&(?!(\w+|\#\d+);)/g, '&')
- .replace(//g, '>')
- .replace(/"/g, '"');
-};
-
-/**
- * Re-throw the given `err` in context to the
- * the jade in `filename` at the given `lineno`.
- *
- * @param {Error} err
- * @param {String} filename
- * @param {String} lineno
- * @api private
- */
-
-exports.rethrow = function rethrow(err, filename, lineno){
- if (!filename) throw err;
-
- var context = 3
- , str = require('fs').readFileSync(filename, 'utf8')
- , lines = str.split('\n')
- , start = Math.max(lineno - context, 0)
- , end = Math.min(lines.length, lineno + context);
-
- // Error context
- var context = lines.slice(start, end).map(function(line, i){
- var curr = i + start + 1;
- return (curr == lineno ? ' > ' : ' ')
- + curr
- + '| '
- + line;
- }).join('\n');
-
- // Alter exception message
- err.path = filename;
- err.message = (filename || 'Jade') + ':' + lineno
- + '\n' + context + '\n\n' + err.message;
- throw err;
-};
-
- return exports;
-
-})({});
-
-;var TreemaNode,
- __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
- __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
- __slice = [].slice;
-
-TreemaNode = (function() {
- var defaults;
-
- TreemaNode.prototype.schema = {};
-
- TreemaNode.prototype.$el = null;
-
- TreemaNode.prototype.data = null;
-
- TreemaNode.prototype.options = null;
-
- TreemaNode.prototype.parent = null;
-
- TreemaNode.prototype.nodeTemplate = '';
-
- TreemaNode.prototype.childrenTemplate = '';
-
- TreemaNode.prototype.addChildTemplate = '+
';
-
- TreemaNode.prototype.tempErrorTemplate = '';
-
- TreemaNode.prototype.toggleTemplate = '';
-
- TreemaNode.prototype.keyTemplate = '';
-
- TreemaNode.prototype.errorTemplate = '';
-
- TreemaNode.prototype.newPropertyTemplate = '';
-
- TreemaNode.prototype.collection = false;
-
- TreemaNode.prototype.ordered = false;
-
- TreemaNode.prototype.keyed = false;
-
- TreemaNode.prototype.editable = true;
-
- TreemaNode.prototype.directlyEditable = true;
-
- TreemaNode.prototype.skipTab = false;
-
- TreemaNode.prototype.valueClass = null;
-
- TreemaNode.prototype.removeOnEmptyDelete = true;
-
- TreemaNode.prototype.keyForParent = null;
-
- TreemaNode.prototype.childrenTreemas = null;
-
- TreemaNode.prototype.justCreated = true;
-
- TreemaNode.prototype.removed = false;
-
- TreemaNode.prototype.workingSchema = null;
-
- TreemaNode.prototype.isValid = function() {
- var errors;
- errors = this.getErrors();
- return errors.length === 0;
- };
-
- TreemaNode.prototype.getErrors = function() {
- var e, errors, moreErrors, my_path, root, _i, _len;
- if (!this.tv4) {
- return [];
- }
- if (this.isRoot()) {
- if (this.cachedErrors) {
- return this.cachedErrors;
- }
- this.cachedErrors = this.tv4.validateMultiple(this.data, this.schema)['errors'];
- return this.cachedErrors;
- }
- root = this.getRoot();
- errors = root.getErrors();
- my_path = this.getPath();
- errors = (function() {
- var _i, _len, _results;
- _results = [];
- for (_i = 0, _len = errors.length; _i < _len; _i++) {
- e = errors[_i];
- if (e.dataPath.slice(0, +my_path.length + 1 || 9e9) === my_path) {
- _results.push(e);
- }
- }
- return _results;
- })();
- for (_i = 0, _len = errors.length; _i < _len; _i++) {
- e = errors[_i];
- e.dataPath = e.dataPath.slice(0, +my_path.length + 1 || 9e9);
- }
- if (this.workingSchema) {
- moreErrors = this.tv4.validateMultiple(this.data, this.workingSchema).errors;
- errors = errors.concat(moreErrors);
- }
- return errors;
- };
-
- TreemaNode.prototype.setUpValidator = function() {
- var root, _ref;
- if (!this.parent) {
- this.tv4 = (_ref = window['tv4']) != null ? _ref.freshApi() : void 0;
- this.tv4.addSchema('#', this.schema);
- if (this.schema.id) {
- return this.tv4.addSchema(this.schema.id, this.schema);
- }
- } else {
- root = this.getRoot();
- return this.tv4 = root.tv4;
- }
- };
-
- TreemaNode.prototype.saveChanges = function() {
- return console.error('"saveChanges" has not been overridden.');
- };
-
- TreemaNode.prototype.getDefaultValue = function() {
- return null;
- };
-
- TreemaNode.prototype.buildValueForDisplay = function() {
- return console.error('"buildValueForDisplay" has not been overridden.');
- };
-
- TreemaNode.prototype.buildValueForEditing = function() {
- if (!this.editable) {
- return;
- }
- return console.error('"buildValueForEditing" has not been overridden.');
- };
-
- TreemaNode.prototype.getChildren = function() {
- return console.error('"getChildren" has not been overridden.');
- };
-
- TreemaNode.prototype.getChildSchema = function() {
- return console.error('"getChildSchema" has not been overridden.');
- };
-
- TreemaNode.prototype.canAddChild = function() {
- return this.collection && this.editable && !this.settings.readOnly;
- };
-
- TreemaNode.prototype.canAddProperty = function() {
- return true;
- };
-
- TreemaNode.prototype.addingNewProperty = function() {
- return false;
- };
-
- TreemaNode.prototype.addNewChild = function() {
- return false;
- };
-
- TreemaNode.prototype.buildValueForDisplaySimply = function(valEl, text) {
- if (text.length > 200) {
- text = text.slice(0, 200) + '...';
- }
- return valEl.append($("").addClass('treema-shortened').text(text));
- };
-
- TreemaNode.prototype.buildValueForEditingSimply = function(valEl, value, inputType) {
- var input;
- if (inputType == null) {
- inputType = null;
- }
- input = $('');
- if (inputType) {
- input.attr('type', inputType);
- }
- if (value !== null) {
- input.val(value);
- }
- valEl.append(input);
- input.focus().select();
- input.blur(this.onEditInputBlur);
- return input;
- };
-
- TreemaNode.prototype.onEditInputBlur = function(e) {
- var closest, input, shouldRemove;
- shouldRemove = this.shouldTryToRemoveFromParent();
- closest = $(e.relatedTarget).closest('.treema-node')[0];
- if (closest === this.$el[0]) {
- shouldRemove = false;
- }
- this.markAsChanged();
- this.saveChanges(this.getValEl());
- input = this.getValEl().find('input, textarea, select');
- if (this.isValid()) {
- if (this.isEditing()) {
- this.display();
- }
- } else {
- input.focus().select();
- }
- if (shouldRemove) {
- this.remove();
- } else {
- this.flushChanges();
- }
- return this.broadcastChanges();
- };
-
- TreemaNode.prototype.shouldTryToRemoveFromParent = function() {
- var input, inputs, val, _i, _len;
- return false;
- val = this.getValEl();
- if (val.find('select').length) {
- return;
- }
- inputs = val.find('input, textarea');
- for (_i = 0, _len = inputs.length; _i < _len; _i++) {
- input = inputs[_i];
- input = $(input);
- if (input.attr('type') === 'checkbox' || input.val()) {
- return false;
- }
- }
- return true;
- };
-
- TreemaNode.prototype.limitChoices = function(options) {
- var _this = this;
- this["enum"] = options;
- this.buildValueForEditing = function(valEl) {
- var index, input, option, _i, _len, _ref;
- input = $('');
- _ref = _this["enum"];
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
- option = _ref[_i];
- input.append($('').text(option));
- }
- index = _this["enum"].indexOf(_this.data);
- if (index >= 0) {
- input.prop('selectedIndex', index);
- }
- valEl.append(input);
- input.focus();
- input.blur(_this.onEditInputBlur);
- return input;
- };
- return this.saveChanges = function(valEl) {
- var index;
- index = valEl.find('select').prop('selectedIndex');
- _this.data = _this["enum"][index];
- TreemaNode.changedTreemas.push(_this);
- return _this.broadcastChanges();
- };
- };
-
- TreemaNode.pluginName = "treema";
-
- defaults = {
- schema: {},
- callbacks: {}
- };
-
- function TreemaNode($el, options, parent) {
- this.$el = $el;
- this.parent = parent;
- this.onSelectType = __bind(this.onSelectType, this);
- this.onSelectSchema = __bind(this.onSelectSchema, this);
- this.orderDataFromUI = __bind(this.orderDataFromUI, this);
- this.onMouseLeave = __bind(this.onMouseLeave, this);
- this.onMouseEnter = __bind(this.onMouseEnter, this);
- this.onEditInputBlur = __bind(this.onEditInputBlur, this);
- this.$el = this.$el || $('');
- this.settings = $.extend({}, defaults, options);
- this.schema = this.settings.schema;
- if (!(this.schema.id || this.parent)) {
- this.schema.id = '__base__';
- }
- this.data = options.data;
- this.patches = [];
- this.callbacks = this.settings.callbacks;
- this._defaults = defaults;
- this._name = TreemaNode.pluginName;
- this.setUpValidator();
- this.populateData();
- this.previousState = this.copyData();
- }
-
- TreemaNode.prototype.build = function() {
- var schema, valEl, _ref;
- this.$el.addClass('treema-node').addClass('treema-clearfix');
- this.$el.empty().append($(this.nodeTemplate));
- this.$el.data('instance', this);
- if (!this.parent) {
- this.$el.addClass('treema-root');
- }
- if (!this.parent) {
- this.$el.attr('tabindex', 9001);
- }
- if (!this.parent) {
- this.justCreated = false;
- }
- if (this.collection) {
- this.$el.append($(this.childrenTemplate)).addClass('treema-closed');
- }
- valEl = this.getValEl();
- if (this.valueClass) {
- valEl.addClass(this.valueClass);
- }
- if (this.directlyEditable) {
- valEl.addClass('treema-display');
- }
- this.buildValueForDisplay(valEl);
- if (this.collection && !this.parent) {
- this.open();
- }
- if (!this.parent) {
- this.setUpGlobalEvents();
- }
- if (this.parent) {
- this.setUpLocalEvents();
- }
- if (this.collection) {
- this.updateMyAddButton();
- }
- this.createTypeSelector();
- if (((_ref = this.workingSchemas) != null ? _ref.length : void 0) > 1) {
- this.createSchemaSelector();
- }
- schema = this.workingSchema || this.schema;
- if (schema["enum"]) {
- this.limitChoices(schema["enum"]);
- }
- return this.$el;
- };
-
- TreemaNode.prototype.populateData = function() {
- if (this.data !== void 0) {
- return;
- }
- this.data = this.schema["default"];
- if (this.data !== void 0) {
- return;
- }
- return this.data = this.getDefaultValue();
- };
-
- TreemaNode.prototype.setWorkingSchema = function(workingSchema, workingSchemas) {
- this.workingSchema = workingSchema;
- this.workingSchemas = workingSchemas;
- };
-
- TreemaNode.prototype.createSchemaSelector = function() {
- var button, div, i, label, option, schema, select, _i, _len, _ref;
- div = $('').addClass('treema-schema-select-container');
- select = $('').addClass('treema-schema-select');
- button = $('').addClass('treema-schema-select-button').text('...');
- _ref = this.workingSchemas;
- for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) {
- schema = _ref[i];
- label = this.makeWorkingSchemaLabel(schema);
- option = $('').attr('value', i).text(label);
- if (schema === this.workingSchema) {
- option.attr('selected', true);
- }
- select.append(option);
- }
- div.append(button).append(select);
- select.change(this.onSelectSchema);
- return this.$el.find('> .treema-row').prepend(div);
- };
-
- TreemaNode.prototype.makeWorkingSchemaLabel = function(schema) {
- if (schema.title != null) {
- return schema.title;
- }
- if (schema.type != null) {
- return schema.type;
- }
- return '???';
- };
-
- TreemaNode.prototype.getTypes = function() {
- var schema, types;
- schema = this.workingSchema || this.schema;
- types = schema.type || ["string", "number", "integer", "boolean", "null", "array", "object"];
- if (!$.isArray(types)) {
- types = [types];
- }
- return types;
- };
-
- TreemaNode.prototype.createTypeSelector = function() {
- var button, currentType, div, option, schema, select, type, types, _i, _len;
- types = this.getTypes();
- if (!(types.length > 1)) {
- return;
- }
- schema = this.workingSchema || this.schema;
- if (schema["enum"]) {
- return;
- }
- div = $('').addClass('treema-type-select-container');
- select = $('').addClass('treema-type-select');
- button = $('').addClass('treema-type-select-button');
- currentType = $.type(this.data);
- if (this.valueClass === 'treema-integer') {
- currentType = 'integer';
- }
- for (_i = 0, _len = types.length; _i < _len; _i++) {
- type = types[_i];
- option = $('').attr('value', type).text(type);
- if (type === currentType) {
- option.attr('selected', true);
- button.text(this.typeToLetter(type));
- }
- select.append(option);
- }
- div.append(button).append(select);
- select.change(this.onSelectType);
- return this.$el.find('> .treema-row').prepend(div);
- };
-
- TreemaNode.prototype.typeToLetter = function(type) {
- return {
- 'boolean': 'B',
- 'array': 'A',
- 'object': 'O',
- 'string': 'S',
- 'number': 'F',
- 'integer': 'I',
- 'null': 'N'
- }[type];
- };
-
- TreemaNode.prototype.setUpGlobalEvents = function() {
- var _this = this;
- this.$el.unbind();
- this.$el.dblclick(function(e) {
- var _ref;
- return (_ref = $(e.target).closest('.treema-node').data('instance')) != null ? _ref.onDoubleClick(e) : void 0;
- });
- this.$el.click(function(e) {
- var _ref;
- if ((_ref = $(e.target).closest('.treema-node').data('instance')) != null) {
- _ref.onClick(e);
- }
- return _this.broadcastChanges(e);
- });
- this.keysPreviouslyDown = {};
- this.$el.keydown(function(e) {
- var closest, lastSelected, _ref;
- e.heldDown = _this.keysPreviouslyDown[e.which] || false;
- closest = $(e.target).closest('.treema-node').data('instance');
- lastSelected = _this.getLastSelectedTreema();
- if ((_ref = lastSelected || closest) != null) {
- _ref.onKeyDown(e);
- }
- _this.broadcastChanges(e);
- _this.keysPreviouslyDown[e.which] = true;
- if (e.ctrlKey || e.metaKey) {
- return _this.manageCopyAndPaste(e);
- }
- });
- return this.$el.keyup(function(e) {
- return delete _this.keysPreviouslyDown[e.which];
- });
- };
-
- TreemaNode.prototype.manageCopyAndPaste = function(e) {
- var target, x, y, _ref, _ref1, _ref2, _ref3,
- _this = this;
- target = (_ref = this.getLastSelectedTreema()) != null ? _ref : this;
- if (e.which === 86 && $(e.target).hasClass('treema-clipboard')) {
- if (e.shiftKey && $(e.target).hasClass('treema-clipboard')) {
- _ref1 = [window.scrollX, window.scrollY], x = _ref1[0], y = _ref1[1];
- return setTimeout((function() {
- var newData, result;
- _this.keepFocus(x, y);
- if (!(newData = _this.$clipboard.val())) {
- return;
- }
- try {
- newData = JSON.parse(newData);
- } catch (_error) {
- e = _error;
- return;
- }
- result = target.tv4.validateMultiple(newData, target.schema);
- if (result.valid) {
- return target.set('/', newData);
- } else {
- return console.log("not pasting", newData, "because it's not valid:", result);
- }
- }), 10);
- } else {
- return e.preventDefault();
- }
- } else if (e.shiftKey) {
- return this.$clipboardContainer.find('.treema-clipboard').focus().select();
- } else if (!(((_ref2 = window.getSelection()) != null ? _ref2.toString() : void 0) || ((_ref3 = document.selection) != null ? _ref3.createRange().text : void 0))) {
- return setTimeout((function() {
- if (_this.$clipboardContainer == null) {
- _this.$clipboardContainer = $('').appendTo(_this.$el);
- }
- _this.$clipboardContainer.empty().show();
- return _this.$clipboard = $('').val(JSON.stringify(target.data)).appendTo(_this.$clipboardContainer).focus().select();
- }), 0);
- }
- };
-
- TreemaNode.prototype.broadcastChanges = function(e) {
- var changes, t, _base;
- if (this.callbacks.select && TreemaNode.didSelect) {
- TreemaNode.didSelect = false;
- this.callbacks.select(e, this.getSelectedTreemas());
- }
- if (TreemaNode.changedTreemas.length) {
- changes = (function() {
- var _i, _len, _ref, _results;
- _ref = TreemaNode.changedTreemas;
- _results = [];
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
- t = _ref[_i];
- if (!t.removed) {
- _results.push(t);
- }
- }
- return _results;
- })();
- if (typeof (_base = this.callbacks).change === "function") {
- _base.change(e, jQuery.unique(changes));
- }
- return TreemaNode.changedTreemas = [];
- }
- };
-
- TreemaNode.prototype.markAsChanged = function() {
- return TreemaNode.changedTreemas.push(this);
- };
-
- TreemaNode.prototype.setUpLocalEvents = function() {
- var row;
- row = this.$el.find('> .treema-row');
- if (this.callbacks.mouseenter != null) {
- row.mouseenter(this.onMouseEnter);
- }
- if (this.callbacks.mouseleave != null) {
- return row.mouseleave(this.onMouseLeave);
- }
- };
-
- TreemaNode.prototype.onMouseEnter = function(e) {
- return this.callbacks.mouseenter(e, this);
- };
-
- TreemaNode.prototype.onMouseLeave = function(e) {
- return this.callbacks.mouseleave(e, this);
- };
-
- TreemaNode.prototype.onClick = function(e) {
- var clickedToggle, clickedValue, usedModKey, _ref;
- if ((_ref = e.target.nodeName) === 'INPUT' || _ref === 'TEXTAREA') {
- return;
- }
- clickedValue = $(e.target).closest('.treema-value').length;
- clickedToggle = $(e.target).hasClass('treema-toggle') || $(e.target).hasClass('treema-toggle-hit-area');
- usedModKey = e.shiftKey || e.ctrlKey || e.metaKey;
- if (!(clickedValue && !this.collection)) {
- this.keepFocus();
- }
- if (this.isDisplaying() && clickedValue && this.canEdit() && !usedModKey) {
- return this.toggleEdit();
- }
- if (!usedModKey && (clickedToggle || (clickedValue && this.collection))) {
- if (!clickedToggle) {
- this.deselectAll();
- this.select();
- }
- return this.toggleOpen();
- }
- if ($(e.target).closest('.treema-add-child').length && this.collection) {
- return this.addNewChild();
- }
- if (this.isRoot() || this.isEditing()) {
- return;
- }
- if (e.shiftKey) {
- return this.shiftSelect();
- }
- if (e.ctrlKey || e.metaKey) {
- return this.toggleSelect();
- }
- return this.select();
- };
-
- TreemaNode.prototype.onDoubleClick = function(e) {
- var clickedKey, _base, _base1, _base2;
- if (!this.collection) {
- return typeof (_base = this.callbacks).dblclick === "function" ? _base.dblclick(e, this) : void 0;
- }
- clickedKey = $(e.target).hasClass('treema-key');
- if (!clickedKey) {
- return typeof (_base1 = this.callbacks).dblclick === "function" ? _base1.dblclick(e, this) : void 0;
- }
- if (this.isClosed()) {
- this.open();
- }
- this.addNewChild();
- return typeof (_base2 = this.callbacks).dblclick === "function" ? _base2.dblclick(e, this) : void 0;
- };
-
- TreemaNode.prototype.onKeyDown = function(e) {
- if (e.which === 27) {
- this.onEscapePressed(e);
- }
- if (e.which === 9) {
- this.onTabPressed(e);
- }
- if (e.which === 37) {
- this.onLeftArrowPressed(e);
- }
- if (e.which === 38) {
- this.onUpArrowPressed(e);
- }
- if (e.which === 39) {
- this.onRightArrowPressed(e);
- }
- if (e.which === 40) {
- this.onDownArrowPressed(e);
- }
- if (e.which === 13) {
- this.onEnterPressed(e);
- }
- if (e.which === 78) {
- this.onNPressed(e);
- }
- if (e.which === 32) {
- this.onSpacePressed(e);
- }
- if (e.which === 84) {
- this.onTPressed(e);
- }
- if (e.which === 70) {
- this.onFPressed(e);
- }
- if (e.which === 8 && !e.heldDown) {
- return this.onDeletePressed(e);
- }
- };
-
- TreemaNode.prototype.onLeftArrowPressed = function(e) {
- if (this.inputFocused()) {
- return;
- }
- this.navigateOut();
- return e.preventDefault();
- };
-
- TreemaNode.prototype.onRightArrowPressed = function(e) {
- if (this.inputFocused()) {
- return;
- }
- this.navigateIn();
- return e.preventDefault();
- };
-
- TreemaNode.prototype.onUpArrowPressed = function(e) {
- if (this.inputFocused()) {
- return;
- }
- this.navigateSelection(-1);
- return e.preventDefault();
- };
-
- TreemaNode.prototype.onDownArrowPressed = function(e) {
- if (this.inputFocused()) {
- return;
- }
- this.navigateSelection(1);
- return e.preventDefault();
- };
-
- TreemaNode.prototype.inputFocused = function() {
- var _ref;
- if ((_ref = document.activeElement.nodeName) === 'INPUT' || _ref === 'TEXTAREA' || _ref === 'SELECT') {
- return true;
- }
- };
-
- TreemaNode.prototype.onSpacePressed = function() {};
-
- TreemaNode.prototype.onTPressed = function() {};
-
- TreemaNode.prototype.onFPressed = function() {};
-
- TreemaNode.prototype.onDeletePressed = function(e) {
- var editing;
- editing = this.editingIsHappening();
- if (editing && !$(e.target).val() && this.removeOnEmptyDelete) {
- this.display();
- this.select();
- this.removeSelectedNodes();
- e.preventDefault();
- }
- if (editing) {
- return;
- }
- e.preventDefault();
- return this.removeSelectedNodes();
- };
-
- TreemaNode.prototype.onEscapePressed = function() {
- if (!this.isEditing()) {
- return;
- }
- if (this.justCreated) {
- return this.remove();
- }
- if (this.isEditing()) {
- this.display();
- }
- if (!this.isRoot()) {
- this.select();
- }
- return this.keepFocus();
- };
-
- TreemaNode.prototype.onEnterPressed = function(e) {
- var offset;
- offset = e.shiftKey ? -1 : 1;
- if (offset === 1 && $(e.target).hasClass('treema-add-child')) {
- return this.addNewChild();
- }
- return this.traverseWhileEditing(offset, true);
- };
-
- TreemaNode.prototype.onTabPressed = function(e) {
- var offset;
- offset = e.shiftKey ? -1 : 1;
- if (this.hasMoreInputs(offset)) {
- return;
- }
- e.preventDefault();
- return this.traverseWhileEditing(offset, false);
- };
-
- TreemaNode.prototype.hasMoreInputs = function(offset) {
- var input, inputs, passedFocusedEl, _i, _len;
- inputs = this.getInputs().toArray();
- if (offset < 0) {
- inputs = inputs.reverse();
- }
- passedFocusedEl = false;
- for (_i = 0, _len = inputs.length; _i < _len; _i++) {
- input = inputs[_i];
- if (input === document.activeElement) {
- passedFocusedEl = true;
- continue;
- }
- if (!passedFocusedEl) {
- continue;
- }
- return true;
- }
- return false;
- };
-
- TreemaNode.prototype.onNPressed = function(e) {
- var selected, success, target;
- if (this.editingIsHappening()) {
- return;
- }
- selected = this.getLastSelectedTreema();
- target = (selected != null ? selected.collection : void 0) ? selected : selected != null ? selected.parent : void 0;
- if (!target) {
- return;
- }
- success = target.addNewChild();
- if (success) {
- this.deselectAll();
- }
- return e.preventDefault();
- };
-
- TreemaNode.prototype.traverseWhileEditing = function(offset, aggressive) {
- var ctx, editing, selected, shouldRemove, targetEl, _ref;
- shouldRemove = false;
- selected = this.getLastSelectedTreema();
- editing = this.isEditing();
- if (!editing && (selected != null ? selected.canEdit() : void 0)) {
- return selected.edit();
- }
- if (editing) {
- shouldRemove = this.shouldTryToRemoveFromParent();
- this.saveChanges(this.getValEl());
- if (!shouldRemove) {
- this.flushChanges();
- }
- if (!(aggressive || this.isValid())) {
- this.refreshErrors();
- return;
- }
- if (shouldRemove && ((_ref = $(this.$el[0].nextSibling)) != null ? _ref.hasClass('treema-add-child') : void 0) && offset === 1) {
- offset = 2;
- }
- this.endExistingEdits();
- this.select();
- }
- ctx = this.traversalContext(offset);
- if (!ctx) {
- return this.getRoot().addChild();
- }
- if (!ctx.origin) {
- targetEl = offset > 0 ? ctx.first : ctx.last;
- this.selectOrActivateElement(targetEl);
- }
- selected = $(ctx.origin).data('instance');
- if (offset > 0 && aggressive && selected.collection && selected.isClosed()) {
- return selected.open();
- }
- targetEl = offset > 0 ? ctx.next : ctx.prev;
- if (!targetEl) {
- targetEl = offset > 0 ? ctx.first : ctx.last;
- }
- this.selectOrActivateElement(targetEl);
- if (shouldRemove) {
- return this.remove();
- } else {
- return this.refreshErrors();
- }
- };
-
- TreemaNode.prototype.selectOrActivateElement = function(el) {
- var treema;
- el = $(el);
- treema = el.data('instance');
- if (treema) {
- if (treema.canEdit()) {
- return treema.edit();
- } else {
- return treema.select();
- }
- }
- this.deselectAll();
- return el.focus();
- };
-
- TreemaNode.prototype.navigateSelection = function(offset) {
- var ctx, targetTreema;
- ctx = this.navigationContext();
- if (!ctx) {
- return;
- }
- if (!ctx.origin) {
- targetTreema = offset > 0 ? ctx.first : ctx.last;
- return targetTreema.select();
- }
- targetTreema = offset > 0 ? ctx.next : ctx.prev;
- if (!targetTreema) {
- targetTreema = offset > 0 ? ctx.first : ctx.last;
- }
- return targetTreema != null ? targetTreema.select() : void 0;
- };
-
- TreemaNode.prototype.navigateOut = function() {
- var selected;
- selected = this.getLastSelectedTreema();
- if (!selected) {
- return;
- }
- if (selected.isOpen()) {
- return selected.close();
- }
- if ((!selected.parent) || selected.parent.isRoot()) {
- return;
- }
- return selected.parent.select();
- };
-
- TreemaNode.prototype.navigateIn = function() {
- var treema, _i, _len, _ref, _results;
- _ref = this.getSelectedTreemas();
- _results = [];
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
- treema = _ref[_i];
- if (!treema.collection) {
- continue;
- }
- if (treema.isClosed()) {
- _results.push(treema.open());
- } else {
- _results.push(void 0);
- }
- }
- return _results;
- };
-
- TreemaNode.prototype.traversalContext = function(offset) {
- var list, origin, _ref;
- list = this.getNavigableElements(offset);
- origin = (_ref = this.getLastSelectedTreema()) != null ? _ref.$el[0] : void 0;
- if (!origin) {
- origin = this.getRootEl().find('.treema-add-child:focus')[0];
- }
- if (!origin) {
- origin = this.getRootEl().find('.treema-new-prop')[0];
- }
- return this.wrapContext(list, origin, offset);
- };
-
- TreemaNode.prototype.navigationContext = function() {
- var list, origin;
- list = this.getVisibleTreemas();
- origin = this.getLastSelectedTreema();
- return this.wrapContext(list, origin);
- };
-
- TreemaNode.prototype.wrapContext = function(list, origin, offset) {
- var c, originIndex;
- if (offset == null) {
- offset = 1;
- }
- if (!list.length) {
- return;
- }
- c = {
- first: list[0],
- last: list[list.length - 1],
- origin: origin
- };
- if (origin) {
- offset = Math.abs(offset);
- originIndex = list.indexOf(origin);
- c.next = list[originIndex + offset];
- c.prev = list[originIndex - offset];
- }
- return c;
- };
-
- TreemaNode.prototype.canEdit = function() {
- var _ref;
- if (this.schema.readOnly || ((_ref = this.parent) != null ? _ref.schema.readOnly : void 0)) {
- return false;
- }
- if (this.settings.readOnly) {
- return false;
- }
- if (!this.editable) {
- return false;
- }
- if (!this.directlyEditable) {
- return false;
- }
- if (this.collection && this.isOpen()) {
- return false;
- }
- return true;
- };
-
- TreemaNode.prototype.display = function() {
- return this.toggleEdit('treema-display');
- };
-
- TreemaNode.prototype.edit = function(options) {
- if (options == null) {
- options = {};
- }
- this.toggleEdit('treema-edit');
- if ((options.offset != null) && options.offset < 0) {
- return this.focusLastInput();
- }
- };
-
- TreemaNode.prototype.toggleEdit = function(toClass) {
- var valEl;
- if (toClass == null) {
- toClass = null;
- }
- if (!this.editable) {
- return;
- }
- valEl = this.getValEl();
- if (toClass && valEl.hasClass(toClass)) {
- return;
- }
- toClass = toClass || (valEl.hasClass('treema-display') ? 'treema-edit' : 'treema-display');
- if (toClass === 'treema-edit') {
- this.endExistingEdits();
- }
- valEl.removeClass('treema-display').removeClass('treema-edit').addClass(toClass);
- valEl.empty();
- if (this.isDisplaying()) {
- this.buildValueForDisplay(valEl);
- }
- if (this.isEditing()) {
- this.buildValueForEditing(valEl);
- return this.deselectAll();
- }
- };
-
- TreemaNode.prototype.endExistingEdits = function() {
- var editing, elem, treema, _i, _len, _results;
- editing = this.getRootEl().find('.treema-edit').closest('.treema-node');
- _results = [];
- for (_i = 0, _len = editing.length; _i < _len; _i++) {
- elem = editing[_i];
- treema = $(elem).data('instance');
- treema.saveChanges(treema.getValEl());
- treema.display();
- _results.push(this.markAsChanged());
- }
- return _results;
- };
-
- TreemaNode.prototype.flushChanges = function() {
- var parent, _results;
- if (this.parent && this.justCreated) {
- this.parent.integrateChildTreema(this);
- }
- this.getRoot().cachedErrors = null;
- this.justCreated = false;
- this.markAsChanged();
- if (!this.parent) {
- return this.refreshErrors();
- }
- this.parent.data[this.keyForParent] = this.data;
- this.parent.refreshErrors();
- parent = this.parent;
- _results = [];
- while (parent) {
- parent.buildValueForDisplay(parent.getValEl().empty());
- _results.push(parent = parent.parent);
- }
- return _results;
- };
-
- TreemaNode.prototype.focusLastInput = function() {
- var inputs, last;
- inputs = this.getInputs();
- last = inputs[inputs.length - 1];
- return $(last).focus().select();
- };
-
- TreemaNode.prototype.removeSelectedNodes = function() {
- var nextSibling, prevSibling, selected, toSelect, treema, _i, _len;
- selected = this.getSelectedTreemas();
- toSelect = null;
- if (selected.length === 1) {
- nextSibling = selected[0].$el.next('.treema-node').data('instance');
- prevSibling = selected[0].$el.prev('.treema-node').data('instance');
- toSelect = nextSibling || prevSibling || selected[0].parent;
- }
- for (_i = 0, _len = selected.length; _i < _len; _i++) {
- treema = selected[_i];
- treema.remove();
- }
- if (toSelect && !this.getSelectedTreemas().length) {
- return toSelect.select();
- }
- };
-
- TreemaNode.prototype.remove = function() {
- var readOnly, required, root, tempError, _ref, _ref1;
- required = this.parent && (this.parent.schema.required != null) && (_ref = this.keyForParent, __indexOf.call(this.parent.schema.required, _ref) >= 0);
- if (required) {
- tempError = this.createTemporaryError('required');
- this.$el.prepend(tempError);
- return false;
- }
- readOnly = this.schema.readOnly || ((_ref1 = this.parent) != null ? _ref1.schema.readOnly : void 0);
- if (readOnly) {
- tempError = this.createTemporaryError('read only');
- this.$el.prepend(tempError);
- return false;
- }
- root = this.getRootEl();
- this.$el.remove();
- this.removed = true;
- if (document.activeElement === $('body')[0]) {
- this.keepFocus();
- }
- if (this.parent == null) {
- return true;
- }
- delete this.parent.childrenTreemas[this.keyForParent];
- delete this.parent.data[this.keyForParent];
- if (this.parent.ordered) {
- this.parent.orderDataFromUI();
- }
- this.parent.refreshErrors();
- this.parent.updateMyAddButton();
- this.parent.markAsChanged();
- this.parent.buildValueForDisplay(this.parent.getValEl().empty());
- this.broadcastChanges();
- return true;
- };
-
- TreemaNode.prototype.toggleOpen = function() {
- if (this.isClosed()) {
- this.open();
- } else {
- this.close();
- }
- return this;
- };
-
- TreemaNode.prototype.open = function(depth) {
- var child, childIndex, childNode, childrenContainer, key, schema, treema, value, _i, _len, _ref, _ref1, _ref2, _ref3, _results;
- if (depth == null) {
- depth = 1;
- }
- if (this.isClosed()) {
- childrenContainer = this.$el.find('.treema-children').detach();
- childrenContainer.empty();
- this.childrenTreemas = {};
- _ref = this.getChildren();
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
- _ref1 = _ref[_i], key = _ref1[0], value = _ref1[1], schema = _ref1[2];
- if (schema.format === 'hidden') {
- continue;
- }
- treema = TreemaNode.make(null, {
- schema: schema,
- data: value
- }, this, key);
- this.integrateChildTreema(treema);
- childNode = this.createChildNode(treema);
- childrenContainer.append(childNode);
- }
- this.$el.append(childrenContainer).removeClass('treema-closed').addClass('treema-open');
- childrenContainer.append($(this.addChildTemplate));
- if (this.ordered && childrenContainer.sortable && !this.settings.noSortable) {
- if (typeof childrenContainer.sortable === "function") {
- childrenContainer.sortable({
- deactivate: this.orderDataFromUI
- });
- }
- }
- this.refreshErrors();
- }
- depth -= 1;
- if (depth) {
- _ref3 = (_ref2 = this.childrenTreemas) != null ? _ref2 : {};
- _results = [];
- for (childIndex in _ref3) {
- child = _ref3[childIndex];
- _results.push(child.open(depth));
- }
- return _results;
- }
- };
-
- TreemaNode.prototype.orderDataFromUI = function() {
- var child, children, index, treema, _i, _len;
- children = this.$el.find('> .treema-children > .treema-node');
- index = 0;
- this.childrenTreemas = {};
- this.data = $.isArray(this.data) ? [] : {};
- for (_i = 0, _len = children.length; _i < _len; _i++) {
- child = children[_i];
- treema = $(child).data('instance');
- if (!treema) {
- continue;
- }
- treema.keyForParent = index;
- this.childrenTreemas[index] = treema;
- this.data[index] = treema.data;
- index += 1;
- }
- return this.flushChanges();
- };
-
- TreemaNode.prototype.close = function(saveChildData) {
- var key, treema, _ref;
- if (saveChildData == null) {
- saveChildData = true;
- }
- if (!this.isOpen()) {
- return;
- }
- if (saveChildData) {
- _ref = this.childrenTreemas;
- for (key in _ref) {
- treema = _ref[key];
- this.data[key] = treema.data;
- }
- }
- this.$el.find('.treema-children').empty();
- this.$el.addClass('treema-closed').removeClass('treema-open');
- this.childrenTreemas = null;
- this.refreshErrors();
- return this.buildValueForDisplay(this.getValEl().empty());
- };
-
- TreemaNode.prototype.select = function() {
- var excludeSelf, numSelected;
- numSelected = this.getSelectedTreemas().length;
- excludeSelf = numSelected === 1;
- this.deselectAll(excludeSelf);
- this.toggleSelect();
- this.keepFocus();
- return TreemaNode.didSelect = true;
- };
-
- TreemaNode.prototype.deselectAll = function(excludeSelf) {
- var treema, _i, _len, _ref;
- if (excludeSelf == null) {
- excludeSelf = false;
- }
- _ref = this.getSelectedTreemas();
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
- treema = _ref[_i];
- if (excludeSelf && treema === this) {
- continue;
- }
- treema.$el.removeClass('treema-selected');
- }
- this.clearLastSelected();
- return TreemaNode.didSelect = true;
- };
-
- TreemaNode.prototype.toggleSelect = function() {
- this.clearLastSelected();
- if (!this.isRoot()) {
- this.$el.toggleClass('treema-selected');
- }
- if (this.isSelected()) {
- this.$el.addClass('treema-last-selected');
- }
- return TreemaNode.didSelect = true;
- };
-
- TreemaNode.prototype.clearLastSelected = function() {
- return this.getRootEl().find('.treema-last-selected').removeClass('treema-last-selected');
- };
-
- TreemaNode.prototype.shiftSelect = function() {
- var allNodes, lastSelected, node, started, _i, _len;
- lastSelected = this.getRootEl().find('.treema-last-selected');
- if (!lastSelected.length) {
- this.select();
- }
- this.deselectAll();
- allNodes = this.getRootEl().find('.treema-node');
- started = false;
- for (_i = 0, _len = allNodes.length; _i < _len; _i++) {
- node = allNodes[_i];
- node = $(node).data('instance');
- if (!started) {
- if (node === this || node.wasSelectedLast()) {
- started = true;
- }
- if (started) {
- node.$el.addClass('treema-selected');
- }
- continue;
- }
- if (started && (node === this || node.wasSelectedLast())) {
- break;
- }
- node.$el.addClass('treema-selected');
- }
- this.$el.addClass('treema-selected');
- lastSelected.addClass('treema-selected');
- lastSelected.removeClass('treema-last-selected');
- this.$el.addClass('treema-last-selected');
- return TreemaNode.didSelect = true;
- };
-
- TreemaNode.prototype.buildWorkingSchemas = function(originalSchema) {
- var allOf, anyOf, baseSchema, oneOf, s, schema, singularSchema, singularSchemas, workingSchemas, _i, _j, _len, _len1;
- baseSchema = this.resolveReference($.extend(true, {}, originalSchema || {}));
- allOf = baseSchema.allOf;
- anyOf = baseSchema.anyOf;
- oneOf = baseSchema.oneOf;
- if (baseSchema.allOf != null) {
- delete baseSchema.allOf;
- }
- if (baseSchema.anyOf != null) {
- delete baseSchema.anyOf;
- }
- if (baseSchema.oneOf != null) {
- delete baseSchema.oneOf;
- }
- if (allOf != null) {
- for (_i = 0, _len = allOf.length; _i < _len; _i++) {
- schema = allOf[_i];
- $.extend(null, baseSchema, this.resolveReference(schema));
- }
- }
- workingSchemas = [];
- singularSchemas = [];
- if (anyOf != null) {
- singularSchemas = singularSchemas.concat(anyOf);
- }
- if (oneOf != null) {
- singularSchemas = singularSchemas.concat(oneOf);
- }
- for (_j = 0, _len1 = singularSchemas.length; _j < _len1; _j++) {
- singularSchema = singularSchemas[_j];
- singularSchema = this.resolveReference(singularSchema);
- s = $.extend(true, {}, baseSchema);
- s = $.extend(true, s, singularSchema);
- workingSchemas.push(s);
- }
- if (workingSchemas.length === 0) {
- workingSchemas = [baseSchema];
- }
- return workingSchemas;
- };
-
- TreemaNode.prototype.resolveReference = function(schema, scrubTitle) {
- var resolved;
- if (scrubTitle == null) {
- scrubTitle = false;
- }
- if (schema.$ref == null) {
- return schema;
- }
- resolved = this.tv4.getSchema(schema.$ref);
- if (!resolved) {
- console.warn('could not resolve reference', schema.$ref, tv4.getMissingUris());
- }
- if (resolved == null) {
- resolved = {};
- }
- if (scrubTitle && (resolved.title != null)) {
- delete resolved.title;
- }
- return resolved;
- };
-
- TreemaNode.prototype.chooseWorkingSchema = function(workingSchemas, data) {
- var result, root, schema, _i, _len;
- if (workingSchemas.length === 1) {
- return workingSchemas[0];
- }
- root = this.getRoot();
- for (_i = 0, _len = workingSchemas.length; _i < _len; _i++) {
- schema = workingSchemas[_i];
- result = tv4.validateMultiple(data, schema, false, root.schema);
- if (result.valid) {
- return schema;
- }
- }
- return workingSchemas[0];
- };
-
- TreemaNode.prototype.onSelectSchema = function(e) {
- var NodeClass, defaultType, index, workingSchema;
- index = parseInt($(e.target).val());
- workingSchema = this.workingSchemas[index];
- defaultType = "null";
- if (workingSchema["default"] != null) {
- defaultType = $.type(workingSchema["default"]);
- }
- if (workingSchema.type != null) {
- defaultType = workingSchema.type;
- }
- if ($.isArray(defaultType)) {
- defaultType = defaultType[0];
- }
- NodeClass = TreemaNode.getNodeClassForSchema(workingSchema, defaultType, this.settings.nodeClasses);
- this.workingSchema = workingSchema;
- return this.replaceNode(NodeClass);
- };
-
- TreemaNode.prototype.onSelectType = function(e) {
- var NodeClass, newType;
- newType = $(e.target).val();
- NodeClass = TreemaNode.getNodeClassForSchema(this.workingSchema, newType, this.settings.nodeClasses);
- return this.replaceNode(NodeClass);
- };
-
- TreemaNode.prototype.replaceNode = function(NodeClass) {
- var newNode, oldData, settings;
- settings = $.extend(true, {}, this.settings);
- oldData = this.data;
- if (settings.data) {
- delete settings.data;
- }
- newNode = new NodeClass(null, settings, this.parent);
- newNode.data = newNode.getDefaultValue();
- if (this.workingSchema["default"] != null) {
- newNode.data = this.workingSchema["default"];
- }
- if ($.type(oldData) === 'string' && $.type(newNode.data) === 'number') {
- newNode.data = parseFloat(oldData) || 0;
- }
- if ($.type(oldData) === 'number' && $.type(newNode.data) === 'string') {
- newNode.data = oldData.toString();
- }
- if ($.type(oldData) === 'number' && $.type(newNode.data) === 'number') {
- newNode.data = oldData;
- if (newNode.valueClass === 'treema-integer') {
- newNode.data = parseInt(newNode.data);
- }
- }
- newNode.tv4 = this.tv4;
- if (this.keyForParent != null) {
- newNode.keyForParent = this.keyForParent;
- }
- newNode.setWorkingSchema(this.workingSchema, this.workingSchemas);
- this.parent.createChildNode(newNode);
- this.$el.replaceWith(newNode.$el);
- return newNode.flushChanges();
- };
-
- TreemaNode.prototype.integrateChildTreema = function(treema) {
- treema.justCreated = false;
- this.childrenTreemas[treema.keyForParent] = treema;
- treema.populateData();
- this.data[treema.keyForParent] = treema.data;
- return treema;
- };
-
- TreemaNode.prototype.createChildNode = function(treema) {
- var childNode, defnEl, keyEl, name, required, row, suffix, _ref;
- childNode = treema.build();
- row = childNode.find('.treema-row');
- if (this.collection && this.keyed) {
- name = treema.schema.title || treema.keyForParent;
- required = this.schema.required || [];
- suffix = ': ';
- if (_ref = treema.keyForParent, __indexOf.call(required, _ref) >= 0) {
- suffix = '*' + suffix;
- }
- keyEl = $(this.keyTemplate).text(name + suffix);
- row.prepend(keyEl);
- defnEl = $('').addClass('treema-description').text(treema.schema.description || '');
- row.append(defnEl);
- }
- if (treema.collection) {
- childNode.prepend($(this.toggleTemplate));
- }
- return childNode;
- };
-
- TreemaNode.prototype.refreshErrors = function() {
- this.clearErrors();
- return this.showErrors();
- };
-
- TreemaNode.prototype.showErrors = function() {
- var childErrors, deepestTreema, e, error, erroredTreemas, errors, message, messages, ownErrors, path, subpath, treema, _i, _j, _k, _len, _len1, _len2, _ref, _results;
- if (this.justCreated) {
- return;
- }
- errors = this.getErrors();
- erroredTreemas = [];
- for (_i = 0, _len = errors.length; _i < _len; _i++) {
- error = errors[_i];
- path = error.dataPath.slice(1);
- path = path ? path.split('/') : [];
- deepestTreema = this;
- for (_j = 0, _len1 = path.length; _j < _len1; _j++) {
- subpath = path[_j];
- if (!deepestTreema.childrenTreemas) {
- error.forChild = true;
- break;
- }
- if (deepestTreema.ordered) {
- subpath = parseInt(subpath);
- }
- deepestTreema = deepestTreema.childrenTreemas[subpath];
- if (!deepestTreema) {
- console.error('could not find treema down path', path, this, "so couldn't show error", error);
- return;
- }
- }
- if (!(deepestTreema._errors && __indexOf.call(erroredTreemas, deepestTreema) >= 0)) {
- deepestTreema._errors = [];
- }
- deepestTreema._errors.push(error);
- erroredTreemas.push(deepestTreema);
- }
- _ref = $.unique(erroredTreemas);
- _results = [];
- for (_k = 0, _len2 = _ref.length; _k < _len2; _k++) {
- treema = _ref[_k];
- childErrors = (function() {
- var _l, _len3, _ref1, _results1;
- _ref1 = treema._errors;
- _results1 = [];
- for (_l = 0, _len3 = _ref1.length; _l < _len3; _l++) {
- e = _ref1[_l];
- if (e.forChild) {
- _results1.push(e);
- }
- }
- return _results1;
- })();
- ownErrors = (function() {
- var _l, _len3, _ref1, _results1;
- _ref1 = treema._errors;
- _results1 = [];
- for (_l = 0, _len3 = _ref1.length; _l < _len3; _l++) {
- e = _ref1[_l];
- if (!e.forChild) {
- _results1.push(e);
- }
- }
- return _results1;
- })();
- messages = (function() {
- var _l, _len3, _results1;
- _results1 = [];
- for (_l = 0, _len3 = ownErrors.length; _l < _len3; _l++) {
- e = ownErrors[_l];
- _results1.push(e.message);
- }
- return _results1;
- })();
- if (childErrors.length > 0) {
- message = "[" + childErrors.length + "] error";
- if (childErrors.length > 1) {
- message = message + 's';
- }
- messages.push(message);
- }
- _results.push(treema.showError(messages.join('
')));
- }
- return _results;
- };
-
- TreemaNode.prototype.showError = function(message) {
- this.$el.prepend($(this.errorTemplate));
- this.$el.find('> .treema-error').html(message).show();
- return this.$el.addClass('treema-has-error');
- };
-
- TreemaNode.prototype.clearErrors = function() {
- this.$el.find('.treema-error').remove();
- this.$el.find('.treema-has-error').removeClass('treema-has-error');
- return this.$el.removeClass('treema-has-error');
- };
-
- TreemaNode.prototype.createTemporaryError = function(message, attachFunction) {
- if (attachFunction == null) {
- attachFunction = null;
- }
- if (!attachFunction) {
- attachFunction = this.$el.prepend;
- }
- this.clearTemporaryErrors();
- return $(this.tempErrorTemplate).text(message).delay(3000).fadeOut(1000, function() {
- return $(this).remove();
- });
- };
-
- TreemaNode.prototype.clearTemporaryErrors = function() {
- return this.getRootEl().find('.treema-temp-error').remove();
- };
-
- TreemaNode.prototype.get = function(path) {
- var data, seg, _i, _len;
- if (path == null) {
- path = '/';
- }
- path = this.normalizePath(path);
- if (path.length === 0) {
- return this.data;
- }
- if (this.childrenTreemas != null) {
- return this.digDeeper(path, 'get', void 0, []);
- }
- data = this.data;
- for (_i = 0, _len = path.length; _i < _len; _i++) {
- seg = path[_i];
- data = data[this.normalizeKey(seg, data)];
- if (data === void 0) {
- break;
- }
- }
- return data;
- };
-
- TreemaNode.prototype.set = function(path, newData) {
- var data, i, result, seg, _i, _len;
- path = this.normalizePath(path);
- if (path.length === 0) {
- this.data = newData;
- this.refreshDisplay();
- return true;
- }
- if (this.childrenTreemas != null) {
- result = this.digDeeper(path, 'set', false, [newData]);
- if (result === false && path.length === 1 && $.isPlainObject(this.data)) {
- this.data[path[0]] = newData;
- return true;
- }
- return result;
- }
- data = this.data;
- for (i = _i = 0, _len = path.length; _i < _len; i = ++_i) {
- seg = path[i];
- seg = this.normalizeKey(seg, data);
- if (path.length === i + 1) {
- data[seg] = newData;
- this.refreshDisplay();
- return true;
- } else {
- data = data[seg];
- if (data === void 0) {
- return false;
- }
- }
- }
- };
-
- TreemaNode.prototype["delete"] = function(path) {
- var data, i, seg, _i, _len;
- path = this.normalizePath(path);
- if (path.length === 0) {
- return this.remove();
- }
- if (this.childrenTreemas != null) {
- return this.digDeeper(path, 'delete', false, []);
- }
- data = this.data;
- for (i = _i = 0, _len = path.length; _i < _len; i = ++_i) {
- seg = path[i];
- seg = this.normalizeKey(seg, data);
- if (path.length === i + 1) {
- if ($.isArray(data)) {
- data.splice(seg, 1);
- } else {
- delete data[seg];
- }
- this.refreshDisplay();
- return true;
- } else {
- data = data[seg];
- if (data === void 0) {
- return false;
- }
- }
- }
- };
-
- TreemaNode.prototype.insert = function(path, newData) {
- var data, i, seg, _i, _len;
- path = this.normalizePath(path);
- if (path.length === 0) {
- if (!$.isArray(this.data)) {
- return false;
- }
- this.data.push(newData);
- this.refreshDisplay();
- this.flushChanges();
- return true;
- }
- if (this.childrenTreemas != null) {
- return this.digDeeper(path, 'insert', false, [newData]);
- }
- data = this.data;
- for (i = _i = 0, _len = path.length; _i < _len; i = ++_i) {
- seg = path[i];
- seg = this.normalizeKey(seg, data);
- data = data[seg];
- if (data === void 0) {
- return false;
- }
- }
- if (!$.isArray(data)) {
- return false;
- }
- data.push(newData);
- this.refreshDisplay();
- return true;
- };
-
- TreemaNode.prototype.normalizeKey = function(key, collection) {
- var i, parts, value, _i, _len;
- if ($.isArray(collection)) {
- if (__indexOf.call(key, '=') >= 0) {
- parts = key.split('=');
- for (i = _i = 0, _len = collection.length; _i < _len; i = ++_i) {
- value = collection[i];
- if (value[parts[0]] === parts[1]) {
- return i;
- }
- }
- } else {
- return parseInt(key);
- }
- }
- return key;
- };
-
- TreemaNode.prototype.normalizePath = function(path) {
- var s;
- if ($.type(path) === 'string') {
- path = path.split('/');
- path = (function() {
- var _i, _len, _results;
- _results = [];
- for (_i = 0, _len = path.length; _i < _len; _i++) {
- s = path[_i];
- if (s.length) {
- _results.push(s);
- }
- }
- return _results;
- })();
- }
- return path;
- };
-
- TreemaNode.prototype.digDeeper = function(path, func, def, args) {
- var childTreema, seg;
- seg = this.normalizeKey(path[0], this.data);
- childTreema = this.childrenTreemas[seg];
- if (childTreema === void 0) {
- return def;
- }
- return childTreema[func].apply(childTreema, [path.slice(1)].concat(__slice.call(args)));
- };
-
- TreemaNode.prototype.refreshDisplay = function() {
- var valEl;
- if (this.isDisplaying()) {
- valEl = this.getValEl();
- valEl.empty();
- this.buildValueForDisplay(valEl);
- } else {
- this.display();
- }
- if (this.collection && this.isOpen()) {
- this.close(false);
- this.open();
- }
- this.flushChanges();
- return this.broadcastChanges();
- };
-
- TreemaNode.prototype.getValEl = function() {
- return this.$el.find('> .treema-row .treema-value');
- };
-
- TreemaNode.prototype.getRootEl = function() {
- return this.$el.closest('.treema-root');
- };
-
- TreemaNode.prototype.getRoot = function() {
- var node;
- node = this;
- while (node.parent != null) {
- node = node.parent;
- }
- return node;
- };
-
- TreemaNode.prototype.getInputs = function() {
- return this.getValEl().find('input, textarea');
- };
-
- TreemaNode.prototype.getSelectedTreemas = function() {
- var el, _i, _len, _ref, _results;
- _ref = this.getRootEl().find('.treema-selected');
- _results = [];
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
- el = _ref[_i];
- _results.push($(el).data('instance'));
- }
- return _results;
- };
-
- TreemaNode.prototype.getLastSelectedTreema = function() {
- return this.getRootEl().find('.treema-last-selected').data('instance');
- };
-
- TreemaNode.prototype.getAddButtonEl = function() {
- return this.$el.find('> .treema-children > .treema-add-child');
- };
-
- TreemaNode.prototype.getVisibleTreemas = function() {
- var el, _i, _len, _ref, _results;
- _ref = this.getRootEl().find('.treema-node');
- _results = [];
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
- el = _ref[_i];
- _results.push($(el).data('instance'));
- }
- return _results;
- };
-
- TreemaNode.prototype.getNavigableElements = function() {
- return this.getRootEl().find('.treema-node, .treema-add-child:visible').toArray();
- };
-
- TreemaNode.prototype.getPath = function() {
- var pathPieces, pointer;
- pathPieces = [];
- pointer = this;
- while (pointer && (pointer.keyForParent != null)) {
- pathPieces.push(pointer.keyForParent + '');
- pointer = pointer.parent;
- }
- pathPieces.reverse();
- return '/' + pathPieces.join('/');
- };
-
- TreemaNode.prototype.isRoot = function() {
- return !this.parent;
- };
-
- TreemaNode.prototype.isEditing = function() {
- return this.getValEl().hasClass('treema-edit');
- };
-
- TreemaNode.prototype.isDisplaying = function() {
- return this.getValEl().hasClass('treema-display');
- };
-
- TreemaNode.prototype.isOpen = function() {
- return this.$el.hasClass('treema-open');
- };
-
- TreemaNode.prototype.isClosed = function() {
- return this.$el.hasClass('treema-closed');
- };
-
- TreemaNode.prototype.isSelected = function() {
- return this.$el.hasClass('treema-selected');
- };
-
- TreemaNode.prototype.wasSelectedLast = function() {
- return this.$el.hasClass('treema-last-selected');
- };
-
- TreemaNode.prototype.editingIsHappening = function() {
- return this.getRootEl().find('.treema-edit').length;
- };
-
- TreemaNode.prototype.rootSelected = function() {
- return $(document.activeElement).hasClass('treema-root');
- };
-
- TreemaNode.prototype.keepFocus = function(x, y) {
- var _ref;
- if (!((x != null) && (y != null))) {
- _ref = [window.scrollX, window.scrollY], x = _ref[0], y = _ref[1];
- }
- this.getRootEl().focus();
- return window.scrollTo(x, y);
- };
-
- TreemaNode.prototype.copyData = function() {
- return $.extend(null, {}, {
- 'd': this.data
- })['d'];
- };
-
- TreemaNode.prototype.updateMyAddButton = function() {
- this.$el.removeClass('treema-full');
- if (!this.canAddChild()) {
- return this.$el.addClass('treema-full');
- }
- };
-
- TreemaNode.nodeMap = {};
-
- TreemaNode.setNodeSubclass = function(key, NodeClass) {
- return this.nodeMap[key] = NodeClass;
- };
-
- TreemaNode.getNodeClassForSchema = function(schema, def, localClasses) {
- var NodeClass, type;
- if (def == null) {
- def = 'string';
- }
- if (localClasses == null) {
- localClasses = null;
- }
- NodeClass = null;
- localClasses = localClasses || {};
- if (schema.format) {
- NodeClass = localClasses[schema.format] || this.nodeMap[schema.format];
- }
- if (NodeClass) {
- return NodeClass;
- }
- type = schema.type || def;
- if ($.isArray(type)) {
- type = def;
- }
- NodeClass = localClasses[type] || this.nodeMap[type];
- if (NodeClass) {
- return NodeClass;
- }
- return this.nodeMap['any'];
- };
-
- TreemaNode.make = function(element, options, parent, keyForParent) {
- var NodeClass, combinedOps, d, data, localClasses, newNode, schemaTypes, type, workingSchema, workingSchemas;
- if (options.data === void 0 && (options.schema["default"] != null)) {
- d = options.schema["default"];
- options.data = $.extend(true, {}, {
- 'x': d
- })['x'];
- }
- workingSchemas = [];
- workingSchema = null;
- type = null;
- if (options.schema["default"] !== void 0) {
- type = $.type(options.schema["default"]);
- }
- if (options.data != null) {
- type = $.type(options.data);
- }
- if (type === 'number' && options.data % 1) {
- type = 'integer';
- }
- if (type == null) {
- schemaTypes = options.schema.type;
- if ($.isArray(schemaTypes)) {
- schemaTypes = schemaTypes[0];
- }
- if (schemaTypes == null) {
- schemaTypes = 'string';
- }
- type = schemaTypes;
- }
- localClasses = parent ? parent.settings.nodeClasses : options.nodeClasses;
- if (parent) {
- workingSchemas = parent.buildWorkingSchemas(options.schema);
- data = options.data;
- if (data === void 0) {
- data = options.schema["default"];
- }
- workingSchema = parent.chooseWorkingSchema(workingSchemas, data);
- NodeClass = this.getNodeClassForSchema(workingSchema, type, localClasses);
- } else {
- NodeClass = this.getNodeClassForSchema(options.schema, type, localClasses);
- }
- if (options.data === void 0) {
- type = options.schema.type;
- if (type == null) {
- type = workingSchema != null ? workingSchema.type : void 0;
- }
- if (type == null) {
- type = 'string';
- }
- if ($.isArray(type)) {
- type = type[0];
- }
- options.data = {
- 'string': '',
- 'number': 0,
- 'null': null,
- 'object': {},
- 'integer': 0,
- 'boolean': false,
- 'array': []
- }[type];
- }
- combinedOps = {};
- if (parent) {
- $.extend(combinedOps, parent.settings);
- }
- $.extend(combinedOps, options);
- newNode = new NodeClass(element, combinedOps, parent);
- if (parent != null) {
- newNode.tv4 = parent.tv4;
- }
- if (keyForParent != null) {
- newNode.keyForParent = keyForParent;
- }
- if (parent) {
- newNode.setWorkingSchema(workingSchema, workingSchemas);
- }
- return newNode;
- };
-
- TreemaNode.extend = function(child) {
- var ctor;
- ctor = function() {};
- ctor.prototype = this.prototype;
- child.prototype = new ctor();
- child.prototype.constructor = child;
- child.__super__ = this.prototype;
- child.prototype["super"] = function(method) {
- return this.constructor.__super__[method];
- };
- return child;
- };
-
- TreemaNode.didSelect = false;
-
- TreemaNode.changedTreemas = [];
-
- return TreemaNode;
-
-})();
-;var __init,
- __hasProp = {}.hasOwnProperty,
- __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
- __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
- __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
- __slice = [].slice;
-
-(__init = function() {
- var AnyNode, ArrayNode, BooleanNode, IntegerNode, NullNode, NumberNode, ObjectNode, StringNode, _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6;
- TreemaNode.setNodeSubclass('string', StringNode = (function(_super) {
- __extends(StringNode, _super);
-
- function StringNode() {
- _ref = StringNode.__super__.constructor.apply(this, arguments);
- return _ref;
- }
-
- StringNode.prototype.valueClass = 'treema-string';
-
- StringNode.prototype.getDefaultValue = function() {
- return '';
- };
-
- StringNode.inputTypes = ['color', 'date', 'datetime', 'datetime-local', 'email', 'month', 'range', 'search', 'tel', 'text', 'time', 'url', 'week'];
-
- StringNode.prototype.buildValueForDisplay = function(valEl) {
- return this.buildValueForDisplaySimply(valEl, "\"" + this.data + "\"");
- };
-
- StringNode.prototype.buildValueForEditing = function(valEl) {
- var input, _ref1;
- input = this.buildValueForEditingSimply(valEl, this.data);
- if (this.schema.maxLength) {
- input.attr('maxlength', this.schema.maxLength);
- }
- if (_ref1 = this.schema.format, __indexOf.call(StringNode.inputTypes, _ref1) >= 0) {
- return input.attr('type', this.schema.format);
- }
- };
-
- StringNode.prototype.saveChanges = function(valEl) {
- return this.data = $('input', valEl).val();
- };
-
- return StringNode;
-
- })(TreemaNode));
- TreemaNode.setNodeSubclass('number', NumberNode = (function(_super) {
- __extends(NumberNode, _super);
-
- function NumberNode() {
- _ref1 = NumberNode.__super__.constructor.apply(this, arguments);
- return _ref1;
- }
-
- NumberNode.prototype.valueClass = 'treema-number';
-
- NumberNode.prototype.getDefaultValue = function() {
- return 0;
- };
-
- NumberNode.prototype.buildValueForDisplay = function(valEl) {
- return this.buildValueForDisplaySimply(valEl, JSON.stringify(this.data));
- };
-
- NumberNode.prototype.buildValueForEditing = function(valEl) {
- var input;
- input = this.buildValueForEditingSimply(valEl, JSON.stringify(this.data), 'number');
- if (this.schema.maximum) {
- input.attr('max', this.schema.maximum);
- }
- if (this.schema.minimum) {
- return input.attr('min', this.schema.minimum);
- }
- };
-
- NumberNode.prototype.saveChanges = function(valEl) {
- return this.data = parseFloat($('input', valEl).val());
- };
-
- return NumberNode;
-
- })(TreemaNode));
- TreemaNode.setNodeSubclass('integer', IntegerNode = (function(_super) {
- __extends(IntegerNode, _super);
-
- function IntegerNode() {
- _ref2 = IntegerNode.__super__.constructor.apply(this, arguments);
- return _ref2;
- }
-
- IntegerNode.prototype.valueClass = 'treema-integer';
-
- IntegerNode.prototype.getDefaultValue = function() {
- return 0;
- };
-
- IntegerNode.prototype.buildValueForDisplay = function(valEl) {
- return this.buildValueForDisplaySimply(valEl, JSON.stringify(this.data));
- };
-
- IntegerNode.prototype.buildValueForEditing = function(valEl) {
- var input;
- input = this.buildValueForEditingSimply(valEl, JSON.stringify(this.data), 'number');
- if (this.schema.maximum) {
- input.attr('max', this.schema.maximum);
- }
- if (this.schema.minimum) {
- return input.attr('min', this.schema.minimum);
- }
- };
-
- IntegerNode.prototype.saveChanges = function(valEl) {
- return this.data = parseInt($('input', valEl).val());
- };
-
- return IntegerNode;
-
- })(TreemaNode));
- TreemaNode.setNodeSubclass('null', NullNode = NullNode = (function(_super) {
- __extends(NullNode, _super);
-
- function NullNode() {
- _ref3 = NullNode.__super__.constructor.apply(this, arguments);
- return _ref3;
- }
-
- NullNode.prototype.valueClass = 'treema-null';
-
- NullNode.prototype.editable = false;
-
- NullNode.prototype.buildValueForDisplay = function(valEl) {
- return this.buildValueForDisplaySimply(valEl, 'null');
- };
-
- return NullNode;
-
- })(TreemaNode));
- TreemaNode.setNodeSubclass('boolean', BooleanNode = (function(_super) {
- __extends(BooleanNode, _super);
-
- function BooleanNode() {
- _ref4 = BooleanNode.__super__.constructor.apply(this, arguments);
- return _ref4;
- }
-
- BooleanNode.prototype.valueClass = 'treema-boolean';
-
- BooleanNode.prototype.getDefaultValue = function() {
- return false;
- };
-
- BooleanNode.prototype.buildValueForDisplay = function(valEl) {
- return this.buildValueForDisplaySimply(valEl, JSON.stringify(this.data));
- };
-
- BooleanNode.prototype.buildValueForEditing = function(valEl) {
- var input;
- input = this.buildValueForEditingSimply(valEl, JSON.stringify(this.data));
- $('').text(JSON.stringify(this.data)).insertBefore(input);
- return input.focus();
- };
-
- BooleanNode.prototype.toggleValue = function(newValue) {
- var valEl;
- if (newValue == null) {
- newValue = null;
- }
- this.data = !this.data;
- if (newValue != null) {
- this.data = newValue;
- }
- valEl = this.getValEl().empty();
- if (this.isDisplaying()) {
- return this.buildValueForDisplay(valEl);
- } else {
- return this.buildValueForEditing(valEl);
- }
- };
-
- BooleanNode.prototype.onSpacePressed = function() {
- return this.toggleValue();
- };
-
- BooleanNode.prototype.onFPressed = function() {
- return this.toggleValue(false);
- };
-
- BooleanNode.prototype.onTPressed = function() {
- return this.toggleValue(true);
- };
-
- BooleanNode.prototype.saveChanges = function() {};
-
- BooleanNode.prototype.onClick = function(e) {
- var value;
- value = $(e.target).closest('.treema-value');
- if (!value.length) {
- return BooleanNode.__super__.onClick.call(this, e);
- }
- return this.toggleValue();
- };
-
- return BooleanNode;
-
- })(TreemaNode));
- TreemaNode.setNodeSubclass('array', ArrayNode = (function(_super) {
- __extends(ArrayNode, _super);
-
- function ArrayNode() {
- _ref5 = ArrayNode.__super__.constructor.apply(this, arguments);
- return _ref5;
- }
-
- ArrayNode.prototype.valueClass = 'treema-array';
-
- ArrayNode.prototype.getDefaultValue = function() {
- return [];
- };
-
- ArrayNode.prototype.collection = true;
-
- ArrayNode.prototype.ordered = true;
-
- ArrayNode.prototype.directlyEditable = false;
-
- ArrayNode.prototype.sort = false;
-
- ArrayNode.prototype.getChildren = function() {
- var key, value, _i, _len, _ref6, _results;
- _ref6 = this.data;
- _results = [];
- for (key = _i = 0, _len = _ref6.length; _i < _len; key = ++_i) {
- value = _ref6[key];
- _results.push([key, value, this.getChildSchema(key)]);
- }
- return _results;
- };
-
- ArrayNode.prototype.getChildSchema = function(index) {
- var schema;
- schema = this.workingSchema || this.schema;
- if (!((schema.items != null) || (schema.additionalItems != null))) {
- return {};
- }
- if ($.isPlainObject(schema.items)) {
- return this.resolveReference(schema.items, true);
- }
- if (index < schema.length) {
- return this.resolveReference(schema[index], true);
- }
- if ($.isPlainObject(schema.additionalItems)) {
- return this.resolveReference(schema.additionalItems, true);
- }
- return {};
- };
-
- ArrayNode.prototype.buildValueForDisplay = function(valEl) {
- var child, empty, helperTreema, index, text, val, _i, _len, _ref6;
- text = [];
- if (!this.data) {
- return;
- }
- _ref6 = this.data.slice(0, 3);
- for (index = _i = 0, _len = _ref6.length; _i < _len; index = ++_i) {
- child = _ref6[index];
- helperTreema = TreemaNode.make(null, {
- schema: this.getChildSchema(index),
- data: child
- }, this);
- val = $('');
- helperTreema.buildValueForDisplay(val);
- text.push(val.text());
- }
- if (this.data.length > 3) {
- text.push('...');
- }
- empty = this.schema.title != null ? "(empty " + this.schema.title + ")" : '(empty)';
- text = text.length ? text.join(' | ') : empty;
- return this.buildValueForDisplaySimply(valEl, text);
- };
-
- ArrayNode.prototype.buildValueForEditing = function(valEl) {
- return this.buildValueForEditingSimply(valEl, JSON.stringify(this.data));
- };
-
- ArrayNode.prototype.canAddChild = function() {
- if (this.settings.readOnly || this.schema.readOnly) {
- return false;
- }
- if (this.schema.additionalItems === false && this.data.length >= this.schema.items.length) {
- return false;
- }
- if ((this.schema.maxItems != null) && this.data.length >= this.schema.maxItems) {
- return false;
- }
- return true;
- };
-
- ArrayNode.prototype.addNewChild = function() {
- var childNode, newTreema, new_index, schema;
- if (!this.canAddChild()) {
- return;
- }
- if (this.isClosed()) {
- this.open();
- }
- new_index = Object.keys(this.childrenTreemas).length;
- schema = this.getChildSchema(new_index);
- newTreema = TreemaNode.make(void 0, {
- schema: schema
- }, this, new_index);
- newTreema.justCreated = true;
- newTreema.tv4 = this.tv4;
- childNode = this.createChildNode(newTreema);
- this.getAddButtonEl().before(childNode);
- if (newTreema.canEdit()) {
- newTreema.edit();
- } else {
- newTreema.select();
- this.integrateChildTreema(newTreema);
- newTreema.flushChanges();
- }
- return newTreema;
- };
-
- ArrayNode.prototype.open = function() {
- var shouldShorten, valEl;
- if (this.sort) {
- this.data.sort(this.sortFunction);
- }
- ArrayNode.__super__.open.apply(this, arguments);
- shouldShorten = this.buildValueForDisplay === ArrayNode.prototype.buildValueForDisplay;
- shouldShorten = false;
- if (shouldShorten) {
- valEl = this.getValEl().empty();
- if (shouldShorten) {
- return this.buildValueForDisplaySimply(valEl, '[...]');
- }
- }
- };
-
- ArrayNode.prototype.close = function() {
- var valEl;
- ArrayNode.__super__.close.apply(this, arguments);
- valEl = this.getValEl().empty();
- return this.buildValueForDisplay(valEl);
- };
-
- ArrayNode.prototype.sortFunction = function(a, b) {
- if (a > b) {
- return 1;
- }
- if (a < b) {
- return -1;
- }
- return 0;
- };
-
- return ArrayNode;
-
- })(TreemaNode));
- window.TreemaArrayNode = ArrayNode;
- TreemaNode.setNodeSubclass('object', ObjectNode = (function(_super) {
- __extends(ObjectNode, _super);
-
- function ObjectNode() {
- this.cleanupAddNewChild = __bind(this.cleanupAddNewChild, this);
- _ref6 = ObjectNode.__super__.constructor.apply(this, arguments);
- return _ref6;
- }
-
- ObjectNode.prototype.valueClass = 'treema-object';
-
- ObjectNode.prototype.getDefaultValue = function() {
- var childKey, childSchema, d, _ref7, _ref8;
- d = {};
- if (!((_ref7 = this.schema) != null ? _ref7.properties : void 0)) {
- return d;
- }
- _ref8 = this.schema.properties;
- for (childKey in _ref8) {
- childSchema = _ref8[childKey];
- if (childSchema["default"]) {
- d[childKey] = childSchema["default"];
- }
- }
- return d;
- };
-
- ObjectNode.prototype.collection = true;
-
- ObjectNode.prototype.keyed = true;
-
- ObjectNode.prototype.directlyEditable = false;
-
- ObjectNode.prototype.getChildren = function() {
- var children, key, keysAccountedFor, value, _ref7;
- children = [];
- keysAccountedFor = [];
- if (this.schema.properties) {
- for (key in this.schema.properties) {
- if (typeof this.data[key] === 'undefined') {
- continue;
- }
- keysAccountedFor.push(key);
- children.push([key, this.data[key], this.getChildSchema(key)]);
- }
- }
- _ref7 = this.data;
- for (key in _ref7) {
- value = _ref7[key];
- if (__indexOf.call(keysAccountedFor, key) >= 0) {
- continue;
- }
- children.push([key, value, this.getChildSchema(key)]);
- }
- return children;
- };
-
- ObjectNode.prototype.getChildSchema = function(key_or_title) {
- var child_schema, key, re, schema, _ref7, _ref8;
- schema = this.workingSchema || this.schema;
- _ref7 = schema.properties;
- for (key in _ref7) {
- child_schema = _ref7[key];
- if (key === key_or_title || child_schema.title === key_or_title) {
- return this.resolveReference(child_schema, true);
- }
- }
- _ref8 = schema.patternProperties;
- for (key in _ref8) {
- child_schema = _ref8[key];
- re = new RegExp(key);
- if (key.match(re)) {
- return this.resolveReference(child_schema, true);
- }
- }
- if ($.isPlainObject(schema.additionalProperties)) {
- return this.resolveReference(schema.additionalProperties, true);
- }
- return {};
- };
-
- ObjectNode.prototype.buildValueForDisplay = function(valEl) {
- var displayValue, empty, i, key, name, schema, text, value, valueString, _ref7;
- text = [];
- if (!this.data) {
- return;
- }
- displayValue = this.data[this.schema.displayProperty];
- if (displayValue) {
- text = displayValue;
- return this.buildValueForDisplaySimply(valEl, text);
- }
- i = 0;
- schema = this.workingSchema || this.schema;
- _ref7 = this.data;
- for (key in _ref7) {
- value = _ref7[key];
- if (i === 3) {
- text.push('...');
- break;
- }
- i += 1;
- name = this.getChildSchema(key).title || key;
- if ($.isPlainObject(value) || $.isArray(value)) {
- text.push("" + name);
- continue;
- }
- valueString = value;
- if ($.type(value) !== 'string') {
- valueString = JSON.stringify(value);
- }
- if (valueString.length > 20) {
- valueString = valueString.slice(0, 21) + ' ...';
- }
- text.push("" + name + "=" + valueString);
- }
- empty = this.schema.title != null ? "(empty " + this.schema.title + ")" : '(empty)';
- text = text.length ? text.join(', ') : empty;
- return this.buildValueForDisplaySimply(valEl, text);
- };
-
- ObjectNode.prototype.buildValueForEditing = function(valEl) {
- return this.buildValueForEditingSimply(valEl, JSON.stringify(this.data));
- };
-
- ObjectNode.prototype.populateData = function() {
- var helperTreema, key, _i, _len, _ref7, _results;
- ObjectNode.__super__.populateData.call(this);
- if (!this.schema.required) {
- return;
- }
- _ref7 = this.schema.required;
- _results = [];
- for (_i = 0, _len = _ref7.length; _i < _len; _i++) {
- key = _ref7[_i];
- if (this.data[key] != null) {
- continue;
- }
- helperTreema = TreemaNode.make(null, {
- schema: this.getChildSchema(key)
- }, this);
- helperTreema.populateData();
- _results.push(this.data[key] = helperTreema.data);
- }
- return _results;
- };
-
- ObjectNode.prototype.open = function() {
- var shouldShorten, valEl;
- ObjectNode.__super__.open.apply(this, arguments);
- shouldShorten = this.buildValueForDisplay === ObjectNode.prototype.buildValueForDisplay;
- shouldShorten = false;
- if (shouldShorten) {
- valEl = this.getValEl().empty();
- if (shouldShorten) {
- return this.buildValueForDisplaySimply(valEl, '{...}');
- }
- }
- };
-
- ObjectNode.prototype.close = function() {
- var valEl;
- ObjectNode.__super__.close.apply(this, arguments);
- valEl = this.getValEl().empty();
- return this.buildValueForDisplay(valEl);
- };
-
- ObjectNode.prototype.addNewChild = function() {
- var keyInput, properties,
- _this = this;
- if (!this.canAddChild()) {
- return;
- }
- if (!this.isRoot()) {
- this.open();
- }
- this.deselectAll();
- properties = this.childPropertiesAvailable();
- keyInput = $(this.newPropertyTemplate);
- keyInput.keydown(function(e) {
- return _this.originalTargetValue = $(e.target).val();
- });
- if (typeof keyInput.autocomplete === "function") {
- keyInput.autocomplete({
- source: properties,
- minLength: 0,
- delay: 0,
- autoFocus: true
- });
- }
- this.getAddButtonEl().before(keyInput);
- keyInput.focus();
- keyInput.autocomplete('search');
- return true;
- };
-
- ObjectNode.prototype.canAddChild = function() {
- if (this.settings.readOnly || this.schema.readOnly) {
- return false;
- }
- if ((this.schema.maxProperties != null) && Object.keys(this.data).length >= this.schema.maxProperties) {
- return false;
- }
- if (this.schema.additionalProperties !== false) {
- return true;
- }
- if (this.schema.patternProperties != null) {
- return true;
- }
- if (this.childPropertiesAvailable().length) {
- return true;
- }
- return false;
- };
-
- ObjectNode.prototype.childPropertiesAvailable = function() {
- var childSchema, properties, property, schema, _ref7;
- schema = this.workingSchema || this.schema;
- if (!schema.properties) {
- return [];
- }
- properties = [];
- _ref7 = schema.properties;
- for (property in _ref7) {
- childSchema = _ref7[property];
- if (this.data[property] != null) {
- continue;
- }
- if (childSchema.format === 'hidden') {
- continue;
- }
- if (childSchema.readOnly) {
- continue;
- }
- properties.push(childSchema.title || property);
- }
- return properties.sort();
- };
-
- ObjectNode.prototype.onDeletePressed = function(e) {
- if (!this.addingNewProperty()) {
- return ObjectNode.__super__.onDeletePressed.call(this, e);
- }
- if (!$(e.target).val()) {
- this.cleanupAddNewChild();
- e.preventDefault();
- return this.$el.find('.treema-add-child').focus();
- }
- };
-
- ObjectNode.prototype.onEscapePressed = function() {
- return this.cleanupAddNewChild();
- };
-
- ObjectNode.prototype.onTabPressed = function(e) {
- if (!this.addingNewProperty()) {
- return ObjectNode.__super__.onTabPressed.call(this, e);
- }
- e.preventDefault();
- return this.tryToAddNewChild(e, false);
- };
-
- ObjectNode.prototype.onEnterPressed = function(e) {
- if (!this.addingNewProperty()) {
- return ObjectNode.__super__.onEnterPressed.call(this, e);
- }
- return this.tryToAddNewChild(e, true);
- };
-
- ObjectNode.prototype.tryToAddNewChild = function(e, aggressive) {
- var key, keyInput, offset, treema;
- if ((!this.originalTargetValue) && (!aggressive)) {
- offset = e.shiftKey ? -1 : 1;
- this.cleanupAddNewChild();
- this.$el.find('.treema-add-child').focus();
- this.traverseWhileEditing(offset);
- return;
- }
- keyInput = $(e.target);
- key = this.getPropertyKey($(e.target));
- if (key.length && !this.canAddProperty(key)) {
- this.clearTemporaryErrors();
- this.showBadPropertyError(keyInput);
- return;
- }
- if (this.childrenTreemas[key] != null) {
- this.cleanupAddNewChild();
- treema = this.childrenTreemas[key];
- if (treema.canEdit()) {
- return treema.toggleEdit();
- } else {
- return treema.select();
- }
- }
- this.cleanupAddNewChild();
- return this.addNewChildForKey(key);
- };
-
- ObjectNode.prototype.getPropertyKey = function(keyInput) {
- var child_key, child_schema, key, _ref7;
- key = keyInput.val();
- if (this.schema.properties) {
- _ref7 = this.schema.properties;
- for (child_key in _ref7) {
- child_schema = _ref7[child_key];
- if (child_schema.title === key) {
- key = child_key;
- }
- }
- }
- return key;
- };
-
- ObjectNode.prototype.canAddProperty = function(key) {
- var pattern;
- if (this.schema.additionalProperties !== false) {
- return true;
- }
- if (this.schema.properties[key] != null) {
- return true;
- }
- if (this.schema.patternProperties != null) {
- if ((function() {
- var _results;
- _results = [];
- for (pattern in this.schema.patternProperties) {
- _results.push(RegExp(pattern).test(key));
- }
- return _results;
- }).call(this)) {
- return true;
- }
- }
- return false;
- };
-
- ObjectNode.prototype.showBadPropertyError = function(keyInput) {
- var tempError;
- keyInput.focus();
- tempError = this.createTemporaryError('Invalid property name.');
- tempError.insertAfter(keyInput);
- };
-
- ObjectNode.prototype.addNewChildForKey = function(key) {
- var child, childNode, children, newTreema, schema;
- schema = this.getChildSchema(key);
- newTreema = TreemaNode.make(null, {
- schema: schema
- }, this, key);
- childNode = this.createChildNode(newTreema);
- this.findObjectInsertionPoint(key).before(childNode);
- if (newTreema.canEdit()) {
- newTreema.edit();
- } else {
- this.integrateChildTreema(newTreema);
- children = newTreema.getChildren();
- if (children.length) {
- newTreema.open();
- child = newTreema.childrenTreemas[children[0][0]];
- child.select();
- } else {
- newTreema.addNewChild();
- }
- }
- return this.updateMyAddButton();
- };
-
- ObjectNode.prototype.findObjectInsertionPoint = function(key) {
- var afterKeys, allChildren, allProps, child, _i, _len, _ref7, _ref8;
- if (!((_ref7 = this.schema.properties) != null ? _ref7[key] : void 0)) {
- return this.getAddButtonEl();
- }
- allProps = Object.keys(this.schema.properties);
- afterKeys = allProps.slice(allProps.indexOf(key) + 1);
- allChildren = this.$el.find('> .treema-children > .treema-node');
- for (_i = 0, _len = allChildren.length; _i < _len; _i++) {
- child = allChildren[_i];
- if (_ref8 = $(child).data('instance').keyForParent, __indexOf.call(afterKeys, _ref8) >= 0) {
- return $(child);
- }
- }
- return this.getAddButtonEl();
- };
-
- ObjectNode.prototype.cleanupAddNewChild = function() {
- this.$el.find('.treema-new-prop').remove();
- return this.clearTemporaryErrors();
- };
-
- ObjectNode.prototype.addingNewProperty = function() {
- return document.activeElement === this.$el.find('.treema-new-prop')[0];
- };
-
- return ObjectNode;
-
- })(TreemaNode));
- window.TreemaObjectNode = ObjectNode;
- return TreemaNode.setNodeSubclass('any', AnyNode = (function(_super) {
- __extends(AnyNode, _super);
-
- AnyNode.prototype.helper = null;
-
- function AnyNode() {
- var splat;
- splat = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
- AnyNode.__super__.constructor.apply(this, splat);
- this.updateShadowMethods();
- }
-
- AnyNode.prototype.buildValueForEditing = function(valEl) {
- return this.buildValueForEditingSimply(valEl, JSON.stringify(this.data));
- };
-
- AnyNode.prototype.saveChanges = function(valEl) {
- var e;
- this.data = $('input', valEl).val();
- if (this.data[0] === "'" && this.data[this.data.length - 1] !== "'") {
- this.data = this.data.slice(1);
- } else if (this.data[0] === '"' && this.data[this.data.length - 1] !== '"') {
- this.data = this.data.slice(1);
- } else if (this.data.trim() === '[') {
- this.data = [];
- } else if (this.data.trim() === '{') {
- this.data = {};
- } else {
- try {
- this.data = JSON.parse(this.data);
- } catch (_error) {
- e = _error;
- console.log('could not parse data', this.data);
- }
- }
- this.updateShadowMethods();
- return this.rebuild();
- };
-
- AnyNode.prototype.updateShadowMethods = function() {
- var NodeClass, prop, _i, _len, _ref7, _results;
- NodeClass = TreemaNode.getNodeClassForSchema({
- type: $.type(this.data)
- });
- this.helper = new NodeClass(this.schema, {
- data: this.data,
- options: this.options
- }, this.parent);
- this.helper.tv4 = this.tv4;
- _ref7 = ['collection', 'ordered', 'keyed', 'getChildSchema', 'getChildren', 'getChildSchema', 'buildValueForDisplay', 'addNewChild', 'childPropertiesAvailable'];
- _results = [];
- for (_i = 0, _len = _ref7.length; _i < _len; _i++) {
- prop = _ref7[_i];
- _results.push(this[prop] = this.helper[prop]);
- }
- return _results;
- };
-
- AnyNode.prototype.rebuild = function() {
- var newNode, oldEl;
- oldEl = this.$el;
- if (this.parent) {
- newNode = this.parent.createChildNode(this);
- } else {
- newNode = this.build();
- }
- return this.$el = newNode;
- };
-
- AnyNode.prototype.onClick = function(e) {
- var clickedValue, usedModKey, _ref7;
- if ((_ref7 = e.target.nodeName) === 'INPUT' || _ref7 === 'TEXTAREA') {
- return;
- }
- clickedValue = $(e.target).closest('.treema-value').length;
- usedModKey = e.shiftKey || e.ctrlKey || e.metaKey;
- if (clickedValue && !usedModKey) {
- return this.toggleEdit();
- }
- return AnyNode.__super__.onClick.call(this, e);
- };
-
- return AnyNode;
-
- })(TreemaNode));
-})();
-;var __hasProp = {}.hasOwnProperty,
- __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
- __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
- __slice = [].slice;
-
-(function() {
- var AceNode, DatabaseSearchTreemaNode, LongStringNode, Point2DNode, Point3DNode, debounce, _ref, _ref1, _ref2, _ref3, _ref4;
- TreemaNode.setNodeSubclass('point2d', Point2DNode = (function(_super) {
- __extends(Point2DNode, _super);
-
- function Point2DNode() {
- _ref = Point2DNode.__super__.constructor.apply(this, arguments);
- return _ref;
- }
-
- Point2DNode.prototype.valueClass = 'treema-point2d';
-
- Point2DNode.prototype.getDefaultValue = function() {
- return {
- x: 0,
- y: 0
- };
- };
-
- Point2DNode.prototype.buildValueForDisplay = function(valEl) {
- return this.buildValueForDisplaySimply(valEl, "(" + this.data.x + ", " + this.data.y + ")");
- };
-
- Point2DNode.prototype.buildValueForEditing = function(valEl) {
- var xInput, yInput;
- xInput = $('').val(this.data.x).attr('placeholder', 'x');
- yInput = $('').val(this.data.y).attr('placeholder', 'y');
- valEl.append('(').append(xInput).append(', ').append(yInput).append(')');
- return valEl.find('input:first').focus().select();
- };
-
- Point2DNode.prototype.saveChanges = function(valEl) {
- this.data.x = parseFloat(valEl.find('input:first').val());
- return this.data.y = parseFloat(valEl.find('input:last').val());
- };
-
- return Point2DNode;
-
- })(TreemaNode));
- TreemaNode.setNodeSubclass('point3d', Point3DNode = (function(_super) {
- __extends(Point3DNode, _super);
-
- function Point3DNode() {
- _ref1 = Point3DNode.__super__.constructor.apply(this, arguments);
- return _ref1;
- }
-
- Point3DNode.prototype.valueClass = 'treema-point3d';
-
- Point3DNode.prototype.getDefaultValue = function() {
- return {
- x: 0,
- y: 0,
- z: 0
- };
- };
-
- Point3DNode.prototype.buildValueForDisplay = function(valEl) {
- return this.buildValueForDisplaySimply(valEl, "(" + this.data.x + ", " + this.data.y + ", " + this.data.z + ")");
- };
-
- Point3DNode.prototype.buildValueForEditing = function(valEl) {
- var xInput, yInput, zInput;
- xInput = $('').val(this.data.x).attr('placeholder', 'x');
- yInput = $('').val(this.data.y).attr('placeholder', 'y');
- zInput = $('').val(this.data.z).attr('placeholder', 'z');
- valEl.append('(').append(xInput).append(', ').append(yInput).append(', ').append(zInput).append(')');
- return valEl.find('input:first').focus().select();
- };
-
- Point3DNode.prototype.saveChanges = function() {
- var inputs;
- inputs = this.getInputs();
- this.data.x = parseFloat($(inputs[0]).val());
- this.data.y = parseFloat($(inputs[1]).val());
- return this.data.z = parseFloat($(inputs[2]).val());
- };
-
- return Point3DNode;
-
- })(TreemaNode));
- DatabaseSearchTreemaNode = (function(_super) {
- __extends(DatabaseSearchTreemaNode, _super);
-
- function DatabaseSearchTreemaNode() {
- this.searchCallback = __bind(this.searchCallback, this);
- this.search = __bind(this.search, this);
- _ref2 = DatabaseSearchTreemaNode.__super__.constructor.apply(this, arguments);
- return _ref2;
- }
-
- DatabaseSearchTreemaNode.prototype.valueClass = 'treema-search';
-
- DatabaseSearchTreemaNode.prototype.searchValueTemplate = '';
-
- DatabaseSearchTreemaNode.prototype.url = null;
-
- DatabaseSearchTreemaNode.prototype.lastTerm = null;
-
- DatabaseSearchTreemaNode.prototype.buildValueForDisplay = function(valEl) {
- var val;
- val = this.data ? this.formatDocument(this.data) : 'None';
- return this.buildValueForDisplaySimply(valEl, val);
- };
-
- DatabaseSearchTreemaNode.prototype.formatDocument = function(doc) {
- if ($.isString(doc)) {
- return doc;
- }
- return JSON.stringify(doc);
- };
-
- DatabaseSearchTreemaNode.prototype.buildValueForEditing = function(valEl) {
- var input;
- valEl.html(this.searchValueTemplate);
- input = valEl.find('input');
- input.focus().keyup(this.search);
- if (this.data) {
- return input.attr('placeholder', this.formatDocument(this.data));
- }
- };
-
- DatabaseSearchTreemaNode.prototype.search = function() {
- var term;
- term = this.getValEl().find('input').val();
- if (term === this.lastTerm) {
- return;
- }
- if (this.lastTerm && !term) {
- this.getSearchResultsEl().empty();
- }
- if (!term) {
- return;
- }
- this.lastTerm = term;
- this.getSearchResultsEl().empty().append('Searching');
- return $.ajax(this.url + '?term=' + term, {
- dataType: 'json',
- success: this.searchCallback
- });
- };
-
- DatabaseSearchTreemaNode.prototype.searchCallback = function(results) {
- var container, first, i, result, row, text, _i, _len;
- container = this.getSearchResultsEl().detach().empty();
- first = true;
- for (i = _i = 0, _len = results.length; _i < _len; i = ++_i) {
- result = results[i];
- row = $('').addClass('treema-search-result-row');
- text = this.formatDocument(result);
- if (text == null) {
- continue;
- }
- if (first) {
- row.addClass('treema-search-selected');
- }
- first = false;
- row.text(text);
- row.data('value', result);
- container.append(row);
- }
- if (!results.length) {
- container.append($('No results
'));
- }
- return this.getValEl().append(container);
- };
-
- DatabaseSearchTreemaNode.prototype.getSearchResultsEl = function() {
- return this.getValEl().find('.treema-search-results');
- };
-
- DatabaseSearchTreemaNode.prototype.getSelectedResultEl = function() {
- return this.getValEl().find('.treema-search-selected');
- };
-
- DatabaseSearchTreemaNode.prototype.saveChanges = function() {
- var selected;
- selected = this.getSelectedResultEl();
- if (!selected.length) {
- return;
- }
- return this.data = selected.data('value');
- };
-
- DatabaseSearchTreemaNode.prototype.onDownArrowPressed = function(e) {
- this.navigateSearch(1);
- return e.preventDefault();
- };
-
- DatabaseSearchTreemaNode.prototype.onUpArrowPressed = function(e) {
- e.preventDefault();
- return this.navigateSearch(-1);
- };
-
- DatabaseSearchTreemaNode.prototype.navigateSearch = function(offset) {
- var func, next, selected;
- selected = this.getSelectedResultEl();
- func = offset > 0 ? 'next' : 'prev';
- next = selected[func]('.treema-search-result-row');
- if (!next.length) {
- return;
- }
- selected.removeClass('treema-search-selected');
- return next.addClass('treema-search-selected');
- };
-
- DatabaseSearchTreemaNode.prototype.onClick = function(e) {
- var newSelection;
- newSelection = $(e.target).closest('.treema-search-result-row');
- if (!newSelection.length) {
- return DatabaseSearchTreemaNode.__super__.onClick.call(this, e);
- }
- this.getSelectedResultEl().removeClass('treema-search-selected');
- newSelection.addClass('treema-search-selected');
- this.saveChanges();
- return this.display();
- };
-
- DatabaseSearchTreemaNode.prototype.shouldTryToRemoveFromParent = function() {
- var selected;
- if (this.data != null) {
- return;
- }
- selected = this.getSelectedResultEl();
- return !selected.length;
- };
-
- return DatabaseSearchTreemaNode;
-
- })(TreemaNode);
- debounce = function(func, threshold, execAsap) {
- var timeout;
- timeout = null;
- return function() {
- var args, delayed, obj;
- args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
- obj = this;
- delayed = function() {
- if (!execAsap) {
- func.apply(obj, args);
- }
- return timeout = null;
- };
- if (timeout) {
- clearTimeout(timeout);
- } else if (execAsap) {
- func.apply(obj, args);
- }
- return timeout = setTimeout(delayed, threshold || 100);
- };
- };
- DatabaseSearchTreemaNode.prototype.search = debounce(DatabaseSearchTreemaNode.prototype.search, 200);
- window.DatabaseSearchTreemaNode = DatabaseSearchTreemaNode;
- TreemaNode.setNodeSubclass('ace', AceNode = (function(_super) {
- __extends(AceNode, _super);
-
- function AceNode() {
- _ref3 = AceNode.__super__.constructor.apply(this, arguments);
- return _ref3;
- }
-
- AceNode.prototype.valueClass = 'treema-ace treema-multiline';
-
- AceNode.prototype.getDefaultValue = function() {
- return '';
- };
-
- AceNode.prototype.buildValueForDisplay = function(valEl) {
- var pre, _ref4;
- if ((_ref4 = this.editor) != null) {
- _ref4.destroy();
- }
- pre = $('').text(this.data);
- return valEl.append(pre);
- };
-
- AceNode.prototype.buildValueForEditing = function(valEl) {
- var d;
- d = $('').text(this.data);
- valEl.append(d);
- this.editor = ace.edit(d[0]);
- this.editor.setReadOnly(false);
- if (this.schema.aceMode != null) {
- this.editor.getSession().setMode(this.schema.aceMode);
- }
- if (this.schema.aceTabSize != null) {
- this.editor.getSession().setTabSize(this.schema.aceTabSize);
- }
- if (this.schema.aceTheme != null) {
- this.editor.setTheme(this.schema.aceTheme);
- }
- return valEl.find('textarea').focus();
- };
-
- AceNode.prototype.saveChanges = function() {
- return this.data = this.editor.getValue();
- };
-
- AceNode.prototype.onTabPressed = function() {};
-
- AceNode.prototype.onEnterPressed = function() {};
-
- return AceNode;
-
- })(TreemaNode));
- return TreemaNode.setNodeSubclass('long-string', LongStringNode = (function(_super) {
- __extends(LongStringNode, _super);
-
- function LongStringNode() {
- _ref4 = LongStringNode.__super__.constructor.apply(this, arguments);
- return _ref4;
- }
-
- LongStringNode.prototype.valueClass = 'treema-long-string treema-multiline';
-
- LongStringNode.prototype.getDefaultValue = function() {
- return '';
- };
-
- LongStringNode.prototype.buildValueForDisplay = function(valEl) {
- var text;
- text = this.data;
- text = text.replace(/\n/g, '
');
- return valEl.append($("").html(text));
- };
-
- LongStringNode.prototype.buildValueForEditing = function(valEl) {
- var input;
- input = $('');
- if (this.data !== null) {
- input.val(this.data);
- }
- valEl.append(input);
- input.focus().select();
- input.blur(this.onEditInputBlur);
- return input;
- };
-
- LongStringNode.prototype.saveChanges = function(valEl) {
- var input;
- input = valEl.find('textarea');
- return this.data = input.val();
- };
-
- return LongStringNode;
-
- })(TreemaNode));
-})();
-;(function($) {
- return $.fn[TreemaNode.pluginName] = function(options) {
- var element;
- if (this.length === 0) {
- return null;
- }
- element = $(this[0]);
- return TreemaNode.make(element, options);
- };
-})(jQuery);
-;
-//@ sourceMappingURL=treema.js.map
\ No newline at end of file
diff --git a/vendor/styles/treema.css b/vendor/styles/treema.css
deleted file mode 100644
index d9cc2c630..000000000
--- a/vendor/styles/treema.css
+++ /dev/null
@@ -1,299 +0,0 @@
-@media -sass-debug-info{filename{}line{font-family:\000033}}
-.treema-node {
- user-select: none;
- -webkit-user-select: none;
- -moz-user-select: none;
- position: relative;
- font-family: Helvetica;
- clear: both;
- border-bottom: 1px solid #cccccc;
- font-size: 13px;
- cursor: pointer; }
-@media -sass-debug-info{filename{}line{font-family:\0000315}}
- .treema-node.treema-root > .treema-row .treema-value {
- display: none; }
-@media -sass-debug-info{filename{}line{font-family:\0000318}}
- .treema-node.treema-open > .treema-children {
- padding-top: 1px; }
-@media -sass-debug-info{filename{}line{font-family:\0000321}}
- .treema-node.treema-root {
- outline: none; }
-@media -sass-debug-info{filename{}line{font-family:\0000325}}
- .treema-node input, .treema-node select {
- font-size: 13px;
- font-family: Helvetica; }
-@media -sass-debug-info{filename{}line{font-family:\0000328}}
- .treema-node input {
- margin: -3px 0;
- width: 200px; }
-@media -sass-debug-info{filename{}line{font-family:\0000331}}
- .treema-node select {
- height: inherit;
- margin: 0;
- width: inherit; }
-
-@media -sass-debug-info{filename{}line{font-family:\0000336}}
-.treema-type-select-container, .treema-schema-select-container {
- display: block;
- position: relative;
- margin-right: 5px;
- top: 1px;
- float: left; }
-@media -sass-debug-info{filename{}line{font-family:\0000343}}
- .treema-type-select-container select, .treema-schema-select-container select {
- position: absolute;
- left: 0;
- right: 0;
- top: 0;
- bottom: 0;
- opacity: 0.01;
- width: inherit !important; }
-@media -sass-debug-info{filename{}line{font-family:\0000354}}
- .treema-type-select-container button, .treema-schema-select-container button {
- font-size: 10px;
- padding: 0 3px;
- width: 19px; }
-
-@media -sass-debug-info{filename{}line{font-family:\0000359}}
-.treema-children {
- margin-left: 15px;
- clear: both; }
-
-@media -sass-debug-info{filename{}line{font-family:\0000363}}
-.treema-add-child {
- background-color: #eeeeff;
- border: 1px solid #aaaaff;
- cursor: pointer;
- display: inline-block;
- margin: 3px 0 10px;
- padding: 0px 5px;
- font-weight: bold;
- position: relative;
- left: -10px; }
-@media -sass-debug-info{filename{}line{font-family:\0000374}}
- .treema-add-child:hover {
- background-color: #ccccff; }
-
-@media -sass-debug-info{filename{}line{font-family:\0000377}}
-.treema-full > .treema-children > .treema-add-child {
- display: none; }
-
-@media -sass-debug-info{filename{}line{font-family:\0000380}}
-.treema-full.treema-open > .treema-children {
- margin-bottom: 5px; }
-
-@media -sass-debug-info{filename{}line{font-family:\0000383}}
-.treema-row {
- padding: 2px 3px 2px 3px; }
-
-@media -sass-debug-info{filename{}line{font-family:\0000386}}
-.treema-value {
- cursor: text;
- display: block;
- float: left;
- max-width: 100%; }
-
-@media -sass-debug-info{filename{}line{font-family:\0000392}}
-.treema-key {
- color: #5353ac;
- float: left;
- display: block;
- cursor: pointer;
- margin-right: 5px; }
-
-@media -sass-debug-info{filename{}line{font-family:\0000399}}
-.treema-backdrop {
- position: absolute;
- top: 0;
- left: 0;
- height: 25px;
- background-color: rgba(64, 128, 255, 0);
- pointer-events: none;
- width: 100%;
- cursor: pointer; }
-
-@media -sass-debug-info{filename{}line{font-family:\00003109}}
-.treema-description {
- float: right;
- opacity: 0.8;
- font-size: 11px;
- line-height: 13px;
- min-width: 200px;
- text-align: right;
- display: none; }
-
-@media -sass-debug-info{filename{}line{font-family:\00003118}}
-.treema-selected > .treema-row > .treema-description {
- display: inline; }
-
-@media -sass-debug-info{filename{}line{font-family:\00003121}}
-.treema-edit + .treema-description {
- display: inline; }
-
-@media -sass-debug-info{filename{}line{font-family:\00003124}}
-.treema-selected > .treema-row {
- background-color: rgba(64, 128, 255, 0.25); }
-
-@media -sass-debug-info{filename{}line{font-family:\00003129}}
-.treema-error {
- float: right;
- color: darkred;
- margin: 2px 10px; }
-
-@media -sass-debug-info{filename{}line{font-family:\00003134}}
-.treema-has-error {
- background-color: lightpink;
- border: 1px solid darkred; }
-
-@media -sass-debug-info{filename{}line{font-family:\00003138}}
-.treema-temp-error {
- background-color: lightpink;
- padding: 2px 3px;
- color: darkred;
- margin: 0 5px;
- border: 1px solid darkred; }
-
-@media -sass-debug-info{filename{}line{font-family:\00003148}}
-.treema-toggle-hit-area {
- cursor: pointer;
- width: 15px;
- float: left;
- position: absolute;
- left: -15px;
- top: 0;
- bottom: 0; }
-@media -sass-debug-info{filename{}line{font-family:\00003157}}
- .treema-toggle-hit-area:hover {
- background-color: rgba(128, 128, 128, 0.1); }
-@media -sass-debug-info{filename{}line{font-family:\00003159}}
- .treema-toggle-hit-area:hover .treema-toggle {
- opacity: 1; }
-@media -sass-debug-info{filename{}line{font-family:\00003162}}
- .treema-toggle-hit-area .treema-toggle {
- width: 0;
- height: 0;
- opacity: 0.7;
- position: absolute; }
-@media -sass-debug-info{filename{}line{font-family:\00003168}}
- .treema-closed > .treema-toggle-hit-area .treema-toggle {
- border-top: 6px solid transparent;
- border-bottom: 6px solid transparent;
- border-left: 8px solid #666666;
- top: 5px;
- left: 5px; }
-@media -sass-debug-info{filename{}line{font-family:\00003175}}
- .treema-open > .treema-toggle-hit-area .treema-toggle {
- border-left: 6px solid transparent;
- border-right: 6px solid transparent;
- border-top: 8px solid #666666;
- top: 7px;
- left: 3px; }
-
-@media -sass-debug-info{filename{}line{font-family:\00003183}}
-.treema-clearfix:after {
- content: ".";
- display: block;
- height: 0;
- clear: both;
- visibility: hidden; }
-
-@media -sass-debug-info{filename{}line{font-family:\00003190}}
-.treema-shortened {
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap; }
-
-@media -sass-debug-info{filename{}line{font-family:\00003195}}
-.treema-multiline {
- width: inherit;
- margin-top: 20px;
- float: none !important; }
-
-@media -sass-debug-info{filename{}line{font-family:\00003201}}
-.treema-clipboard-container {
- position: fixed;
- left: 0px;
- top: 0px;
- width: 0px;
- height: 0px;
- z-index: 100;
- display: none;
- opacity: 0; }
-@media -sass-debug-info{filename{}line{font-family:\00003211}}
- .treema-clipboard-container .treema-clipboard {
- width: 1px;
- height: 1px;
- padding: 0px; }
-
-@media -sass-debug-info{filename{}line{font-family:\000033}}
-.treema-string {
- color: #998500; }
-
-@media -sass-debug-info{filename{}line{font-family:\000036}}
-.treema-number {
- color: #699900; }
-
-@media -sass-debug-info{filename{}line{font-family:\000039}}
-.treema-null {
- color: #524059;
- font-weight: bold; }
-
-@media -sass-debug-info{filename{}line{font-family:\0000313}}
-.treema-array {
- color: #009905;
- cursor: row-resize; }
-
-@media -sass-debug-info{filename{}line{font-family:\0000317}}
-.treema-object {
- color: #008799;
- cursor: row-resize; }
-
-@media -sass-debug-info{filename{}line{font-family:\0000321}}
-.treema-boolean {
- color: #140099;
- cursor: pointer; }
-@media -sass-debug-info{filename{}line{font-family:\0000324}}
- .treema-boolean.treema-edit {
- background-color: rgba(64, 128, 255, 0.25); }
-@media -sass-debug-info{filename{}line{font-family:\0000326}}
- .treema-boolean input {
- opacity: 0; }
-@media -sass-debug-info{filename{}line{font-family:\0000329}}
- .treema-boolean:hover + .treema-description {
- display: inline; }
-
-@media -sass-debug-info{filename{}line{font-family:\000034}}
-.treema-point2d input, .treema-point3d input {
- width: 40px; }
-
-@media -sass-debug-info{filename{}line{font-family:\000037}}
-.treema-search-results {
- width: 500px;
- margin-top: 10px;
- cursor: pointer; }
-
-@media -sass-debug-info{filename{}line{font-family:\0000312}}
-.treema-search-result-row:hover {
- background-color: rgba(166, 196, 255, 0.25); }
-
-@media -sass-debug-info{filename{}line{font-family:\0000315}}
-.treema-search-selected {
- background-color: rgba(64, 128, 255, 0.25) !important; }
-
-@media -sass-debug-info{filename{}line{font-family:\0000318}}
-.treema-ace.treema-display .treema-shortened {
- font-family: monospace; }
-
-@media -sass-debug-info{filename{}line{font-family:\0000321}}
-.treema-ace.treema-edit .ace_editor {
- width: 100%;
- height: 300px;
- border: 1px solid gray; }
-
-@media -sass-debug-info{filename{}line{font-family:\0000327}}
-.treema-long-string textarea {
- width: 100%;
- height: 300px; }
-
-
-/*@ sourceMappingURL=treema.css.map*/
\ No newline at end of file