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/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/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/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 d8272d1b3..01f236154 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/bower.json b/bower.json
index 4580acd32..b5462543e 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,7 +37,10 @@
"firebase": "~1.0.2",
"catiline": "~2.9.3",
"d3": "~3.4.4",
- "nanoscroller": "~0.8.0"
+ "nanoscroller": "~0.8.0",
+ "jquery.tablesorter": "~2.15.13",
+ "treema": "~0.0.1",
+ "bootstrap": "~3.1.1"
},
"overrides": {
"backbone": {
@@ -51,6 +54,22 @@
},
"underscore.string": {
"main": "lib/underscore.string.js"
+ },
+ "jquery.tablesorter": {
+ "main": [
+ "js/jquery.tablesorter.js",
+ "js/jquery.tablesorter.widgets.js",
+ "css/theme.bootstrap.css"
+ ]
+ },
+ "bootstrap": {
+ "main": [
+ "./dist/js/bootstrap.js",
+ "./dist/fonts/glyphicons-halflings-regular.eot",
+ "./dist/fonts/glyphicons-halflings-regular.svg",
+ "./dist/fonts/glyphicons-halflings-regular.ttf",
+ "./dist/fonts/glyphicons-halflings-regular.woff"
+ ]
}
}
}
diff --git a/config.coffee b/config.coffee
index 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 665ac0131..fc229cd02 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/server/commons/schemas.coffee b/server/commons/schemas.coffee
index 060ff8348..49d97bbfe 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: '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/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/users/user_handler.coffee b/server/users/user_handler.coffee
index 168f10d91..cd78dad9e 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,6 +173,7 @@ UserHandler = class UserHandler extends Handler
return @getNamesByIds(req, res) if args[1] is 'names'
return @nameToID(req, res, args[0]) if args[1] is 'nameToID'
return @getLevelSessions(req, res, args[0]) if args[1] is 'level.sessions'
+ return @getCandidates(req, res) if args[1] is 'candidates'
return @sendNotFoundError(res)
agreeToCLA: (req, res) ->
@@ -191,9 +194,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 +210,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