PageRenderTime 34ms CodeModel.GetById 8ms app.highlight 17ms RepoModel.GetById 2ms app.codeStats 0ms

/scalate-core/src/main/scala/org/fusesource/scalate/filter/CoffeeScriptFilter.scala

http://github.com/scalate/scalate
Scala | 139 lines | 78 code | 14 blank | 47 comment | 3 complexity | 28744b9d097d10b6c8d7185faf26c67a MD5 | raw file
  1/**
  2 * Copyright (C) 2009-2011 the original author or authors.
  3 * See the notice.md file distributed with this work for additional
  4 * information regarding copyright ownership.
  5 *
  6 * Licensed under the Apache License, Version 2.0 (the "License");
  7 * you may not use this file except in compliance with the License.
  8 * You may obtain a copy of the License at
  9 *
 10 *     http://www.apache.org/licenses/LICENSE-2.0
 11 *
 12 * Unless required by applicable law or agreed to in writing, software
 13 * distributed under the License is distributed on an "AS IS" BASIS,
 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 15 * See the License for the specific language governing permissions and
 16 * limitations under the License.
 17 */
 18package org.fusesource.scalate
 19package filter
 20
 21import java.util.concurrent.atomic.AtomicBoolean
 22
 23import javax.script.ScriptException
 24import org.fusesource.scalate.support.RenderHelper
 25import org.fusesource.scalate.util.Log
 26import tv.cntt.rhinocoffeescript.Compiler
 27
 28/**
 29 * Surrounds the filtered text with <script> and CDATA tags.
 30 *
 31 * <p>Useful for including inline Javascript.</p>
 32 *
 33 * @author <a href="http://hiramchirino.com">Hiram Chirino</a>
 34 */
 35object CoffeeScriptFilter extends Filter with Log {
 36
 37  /**
 38   * Server side compilation of coffeescript is enabled by default. Disable this flag
 39   * if you want to disable it (for example to avoid the optional dependency on rhino)
 40   */
 41  val serverSideCompile = true
 42  protected val warnedMissingRhino = new AtomicBoolean()
 43
 44  def filter(context: RenderContext, content: String) = {
 45
 46    def clientSideCompile: String = {
 47      context.attributes("REQUIRES_COFFEE_SCRIPT_JS") = "true"
 48      """<script type='text/coffeescript'>
 49         |  //<![CDATA[
 50         |    """.stripMargin + RenderHelper.indent("    ", content) + """
 51         |  //]]>
 52         |</script>""".stripMargin
 53    }
 54
 55    def missingRhino(e: Throwable): String = {
 56      // we don't have rhino on the classpath
 57      // so lets do client side compilation
 58      if (warnedMissingRhino.compareAndSet(false, true)) {
 59        warn("No Rhino on the classpath: " + e + ". Using client side CoffeeScript compile", e)
 60      }
 61      clientSideCompile
 62    }
 63
 64    if (serverSideCompile) {
 65      try {
 66        CoffeeScriptCompiler.compile(content, Some(context.currentTemplate)).fold({
 67          error =>
 68            warn("Could not compile coffeescript: " + error, error)
 69            throw new CompilerException(error.message, Nil)
 70        }, {
 71          coffee =>
 72            """<script type='text/javascript'>
 73            |  //<![CDATA[
 74            |    """.stripMargin + RenderHelper.indent("    ", coffee) + """
 75            |  //]]>
 76            |</script>""".stripMargin
 77        })
 78      } catch {
 79        case e: NoClassDefFoundError => missingRhino(e)
 80        case e: ClassNotFoundException => missingRhino(e)
 81      }
 82    } else {
 83      clientSideCompile
 84    }
 85  }
 86}
 87
 88/**
 89 * Compiles a .coffee file into JS on the server side
 90 */
 91object CoffeeScriptPipeline extends Filter with Log {
 92
 93  /**
 94   * Installs the coffeescript pipeline
 95   */
 96  def apply(engine: TemplateEngine): Unit = {
 97    engine.pipelines += "coffee" -> List(NoLayoutFilter(this, "text/javascript"))
 98    engine.templateExtensionsFor("js") += "coffee"
 99  }
100
101  def filter(context: RenderContext, content: String) = {
102    CoffeeScriptCompiler.compile(content, Some(context.currentTemplate)).fold({
103      error =>
104        warn("Could not compile coffeescript: " + error, error)
105        throw new CompilerException(error.message, Nil)
106    }, {
107      coffee => coffee
108    })
109  }
110}
111
112/**
113 * A Scala / Rhino Coffeescript compiler.
114 */
115object CoffeeScriptCompiler {
116
117  /**
118   * Compiles a string of Coffeescript code to Javascript.
119   *
120   * @param code the Coffeescript code
121   * @param sourceName a descriptive name for the code unit under compilation (e.g a filename)
122   * @param bare if true, no function wrapper will be generated
123   * @return the compiled Javascript code
124   */
125  def compile(code: String, sourceName: Option[String] = None): Either[CompilationError, String] =
126    {
127      try {
128        Right(Compiler.compile(code))
129      } catch {
130        case e: ScriptException =>
131          val line = e.getLineNumber
132          val column = e.getColumnNumber
133          val message = "CoffeeScript syntax error at %d:%d".format(line, column)
134          Left(CompilationError(sourceName, message))
135      }
136    }
137}
138
139case class CompilationError(sourceName: Option[String], message: String)