CocoModel = require 'models/CocoModel' CocoCollection = require 'collections/CocoCollection' {me} = require('core/auth') locale = require 'locale/locale' initializeFilePicker = -> require('core/services/filepicker')() unless window.application.isIPadApp class DateTimeTreema extends TreemaNode.nodeMap.string valueClass: 'treema-date-time' buildValueForDisplay: (el, data) -> el.text(moment(data).format('llll')) buildValueForEditing: (valEl) -> @buildValueForEditingSimply valEl, null, 'date' class VersionTreema extends TreemaNode valueClass: 'treema-version' buildValueForDisplay: (valEl, data) -> @buildValueForDisplaySimply(valEl, "#{data.major}.#{data.minor}") class LiveEditingMarkup extends TreemaNode.nodeMap.ace valueClass: 'treema-markdown treema-multiline treema-ace' constructor: -> super(arguments...) @workingSchema.aceMode = 'ace/mode/markdown' initializeFilePicker() initEditor: (valEl) -> buttonRow = $('
') valEl.append(buttonRow) @addPreviewToggle(buttonRow) @addImageUpload(buttonRow) super(valEl) valEl.append($('').hide()) addImageUpload: (valEl) -> return unless me.isAdmin() or me.isArtisan() valEl.append( $('').append( $('') .addClass('btn btn-sm btn-primary') .click(=> filepicker.pick @onFileChosen) ) ) addPreviewToggle: (valEl) -> valEl.append($('').append( $('') .addClass('btn btn-sm btn-primary') .click(@togglePreview) )) onFileChosen: (InkBlob) => body = url: InkBlob.url filename: InkBlob.filename mimetype: InkBlob.mimetype path: @settings.filePath force: true @uploadingPath = [@settings.filePath, InkBlob.filename].join('/') $.ajax('/file', { type: 'POST', data: body, success: @onFileUploaded }) onFileUploaded: (e) => @editor.insert "![#{e.metadata.name}](/file/#{@uploadingPath})" showingPreview: false togglePreview: => valEl = @getValEl() if @showingPreview valEl.find('.preview').hide() valEl.find('.pick-image-button').show() valEl.find('.ace_editor').show() else valEl.find('.preview').html(marked(@data)).show() valEl.find('.pick-image-button').hide() valEl.find('.ace_editor').hide() @showingPreview = not @showingPreview class SoundFileTreema extends TreemaNode.nodeMap.string valueClass: 'treema-sound-file' editable: false soundCollection: 'files' constructor: -> super arguments... initializeFilePicker() onClick: (e) -> return if $(e.target).closest('.btn').length super(arguments...) getFiles: -> @settings[@soundCollection]?.models or [] buildValueForDisplay: (valEl, data) -> mimetype = "audio/#{@keyForParent}" mimetypes = [mimetype] if mimetype is 'audio/mp3' # https://github.com/codecombat/codecombat/issues/445 # http://stackoverflow.com/questions/10688588/which-mime-type-should-i-use-for-mp3 mimetypes.push 'audio/mpeg' else if mimetype is 'audio/ogg' mimetypes.push 'application/ogg' mimetypes.push 'video/ogg' # huh, that's what it took to be able to upload ogg sounds in Firefox pickButton = $('') .click(=> filepicker.pick {mimetypes: mimetypes}, @onFileChosen) playButton = $('') .click(@playFile) stopButton = $('') .click(@stopFile) dropdown = $('') dropdownButton = $('') .addClass('btn btn-primary btn-xs dropdown-toggle') .attr('href', '#') .append($('')) .dropdown() dropdown.append dropdownButton menu = $('') files = @getFiles() for file in files continue unless file.get('contentType') in mimetypes path = file.get('metadata').path filename = file.get 'filename' fullPath = [path, filename].join('/') li = $('') .data('fullPath', fullPath) .text(filename) menu.append(li) menu.click (e) => @data = $(e.target).data('fullPath') or data @reset() dropdown.append(menu) valEl.append(pickButton) if data valEl.append(playButton) valEl.append(stopButton) valEl.append(dropdown) # if files.length and @canEdit() if data path = data.split('/') name = path[path.length-1] valEl.append($('').text(name)) reset: -> @instance = null @flushChanges() @refreshDisplay() playFile: => @src = "/file/#{@getData()}" if @instance @instance.play() else createjs.Sound.alternateExtensions = ['mp3','ogg'] registered = createjs.Sound.registerSound(@src) if registered is true @instance = createjs.Sound.play(@src) else f = (event) => @instance = createjs.Sound.play(event.src) if event.src is @src createjs.Sound.removeEventListener('fileload', f) createjs.Sound.addEventListener('fileload', f) stopFile: => @instance?.stop() onFileChosen: (InkBlob) => if not @settings.filePath console.error('Need to specify a filePath for this treema', @getRoot()) throw Error('cannot upload file') body = url: InkBlob.url filename: InkBlob.filename mimetype: InkBlob.mimetype path: @settings.filePath force: true @uploadingPath = [@settings.filePath, InkBlob.filename].join('/') $.ajax('/file', { type: 'POST', data: body, success: @onFileUploaded }) onFileUploaded: (e) => @data = @uploadingPath @reset() class ImageFileTreema extends TreemaNode.nodeMap.string valueClass: 'treema-image-file' editable: false constructor: -> super arguments... initializeFilePicker() onClick: (e) -> return if $(e.target).closest('.btn').length super(arguments...) buildValueForDisplay: (valEl, data) -> mimetype = 'image/*' pickButton = $(' Upload Picture') .click(=> filepicker.pick {mimetypes:[mimetype]}, @onFileChosen) valEl.append(pickButton) if data valEl.append $('').attr('src', "/file/#{data}") onFileChosen: (InkBlob) => if not @settings.filePath console.error('Need to specify a filePath for this treema', @getRoot()) throw Error('cannot upload file') body = url: InkBlob.url filename: InkBlob.filename mimetype: InkBlob.mimetype path: @settings.filePath force: true @uploadingPath = [@settings.filePath, InkBlob.filename].join('/') $.ajax('/file', { type: 'POST', data: body, success: @onFileUploaded }) onFileUploaded: (e) => @data = @uploadingPath @flushChanges() @refreshDisplay() codeLanguages = javascript: 'ace/mode/javascript' coffeescript: 'ace/mode/coffee' python: 'ace/mode/python' lua: 'ace/mode/lua' java: 'ace/mode/java' class CodeLanguagesObjectTreema extends TreemaNode.nodeMap.object childPropertiesAvailable: -> (key for key in _.keys(codeLanguages) when not @data[key]? and not (key is 'javascript' and @workingSchema.skipJavaScript)) class CodeLanguageTreema extends TreemaNode.nodeMap.string buildValueForEditing: (valEl, data) -> super(valEl, data) valEl.find('input').autocomplete(source: _.keys(codeLanguages), minLength: 0, delay: 0, autoFocus: true) valEl class CodeTreema extends TreemaNode.nodeMap.ace constructor: -> super(arguments...) @workingSchema.aceTabSize = 4 # TODO: Find a less hacky solution for this @workingSchema.aceMode = mode if mode = codeLanguages[@keyForParent] @workingSchema.aceMode = mode if mode = codeLanguages[@parent?.data?.language] class CoffeeTreema extends CodeTreema constructor: -> super(arguments...) @workingSchema.aceMode = 'ace/mode/coffee' @workingSchema.aceTabSize = 2 class JavaScriptTreema extends CodeTreema constructor: -> super(arguments...) @workingSchema.aceMode = 'ace/mode/javascript' @workingSchema.aceTabSize = 4 class InternationalizationNode extends TreemaNode.nodeMap.object findLanguageName: (languageCode) -> # to get around mongoose empty object bug, there's a prop in the object which needs to be ignored return '' if languageCode is '-' locale[languageCode]?.nativeDescription or "#{languageCode} Not Found" getChildren: -> res = super(arguments...) res = (r for r in res when r[0] isnt '-') res populateData: -> super() if Object.keys(@data).length is 0 @data['-'] = {'-':'-'} # also to get around mongoose bug getChildSchema: (key) -> #construct the child schema here i18nChildSchema = { title: @findLanguageName(key) type: 'object' properties: {} } return i18nChildSchema unless @parent unless @workingSchema.props? console.warn 'i18n props array is empty! Filling with all parent properties by default' @workingSchema.props = (prop for prop,_ of @parent.schema.properties when prop isnt 'i18n') for i18nProperty in @workingSchema.props parentSchemaProperties = @parent.schema.properties ? {} for extraSchemas in [@parent.schema.oneOf, @parent.schema.anyOf] for extraSchema in extraSchemas ? [] for prop, schema of extraSchema?.properties ? {} parentSchemaProperties[prop] ?= schema i18nChildSchema.properties[i18nProperty] = parentSchemaProperties[i18nProperty] return i18nChildSchema #this must be filled out in order for the i18n node to work childPropertiesAvailable: -> (key for key in _.keys(locale) when not @data[key]?) class LatestVersionCollection extends CocoCollection module.exports.LatestVersionReferenceNode = class LatestVersionReferenceNode extends TreemaNode searchValueTemplate: '' valueClass: 'treema-latest-version' url: '/db/article' lastTerm: null constructor: -> super(arguments...) # to dynamically build the search url, inspect the links url that should be included links = @workingSchema.links or [] link = (l for l in links when l.rel is 'db')[0] return unless link parts = (p for p in link.href.split('/') when p.length) @url = "/db/#{parts[1]}" @model = require('models/' + _.string.classify(parts[1])) buildValueForDisplay: (valEl, data) -> val = if data then @formatDocument(data) else 'None' @buildValueForDisplaySimply(valEl, val) buildValueForEditing: (valEl, data) -> valEl.html(@searchValueTemplate) input = valEl.find('input') input.focus().keyup @search input.attr('placeholder', @formatDocument(data)) if data buildSearchURL: (term) -> "#{@url}?term=#{term}&project=true" search: => term = @getValEl().find('input').val() return if term is @lastTerm @getSearchResultsEl().empty() if @lastTerm and not term return unless term @lastTerm = term @getSearchResultsEl().empty().append('Searching') @collection = new LatestVersionCollection([], model: @model) @collection.url = @buildSearchURL(term) @collection.fetch() @collection.once 'sync', @searchCallback, @ searchCallback: -> container = @getSearchResultsEl().detach().empty() first = true for model in @collection.models row = $('').addClass('treema-search-result-row') text = @formatDocument(model) continue unless text? row.addClass('treema-search-selected') if first first = false row.text(text) row.data('value', model) container.append(row) if not @collection.models.length container.append($('