Skip to content

Commit

Permalink
Minified!
Browse files Browse the repository at this point in the history
  • Loading branch information
nakajima committed May 22, 2010
1 parent 12e8287 commit 72c23b3
Show file tree
Hide file tree
Showing 2 changed files with 234 additions and 1 deletion.
2 changes: 1 addition & 1 deletion app/controllers/bookmarklets_controller.rb
Expand Up @@ -10,7 +10,7 @@ def show
format.html # show.html.erb
format.js do
@bookmarklet.increment! :count
render :js => @bookmarklet.code
render :js => JsMin.minify(@bookmarklet.code).strip
end
end
end
Expand Down
233 changes: 233 additions & 0 deletions lib/js_min.rb
@@ -0,0 +1,233 @@
#--
# jsmin.rb - Ruby implementation of Douglas Crockford's JSMin.
#
# This is a port of jsmin.c, and is distributed under the same terms, which are
# as follows:
#
# Copyright (c) 2002 Douglas Crockford (www.crockford.com)
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# The Software shall be used for Good, not Evil.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#++

require 'strscan'

# = JSMin
#
# Ruby implementation of Douglas Crockford's JavaScript minifier, JSMin.
#
# Author:: Ryan Grove (mailto:ryan@wonko.com)
# Version:: 1.0.0 (2008-03-22)
# Copyright:: Copyright (c) 2008 Ryan Grove. All rights reserved.
# Website:: http://github.com/rgrove/jsmin/
#
# == Example
#
# require 'rubygems'
# require 'jsmin'
#
# File.open('example.js', 'r') {|file| puts JSMin.minify(file) }
#
module JsMin
ORD_LF = "\n"[0].freeze
ORD_SPACE = ' '[0].freeze

class << self

# Reads JavaScript from +input+ (which can be a String or an IO object) and
# returns a String containing minified JS.
def minify(input)
@js = StringScanner.new(input.is_a?(IO) ? input.read : input.to_s)

@a = "\n"
@b = nil
@lookahead = nil
@output = ''

action_get

while !@a.nil? do
case @a
when ' '
if alphanum?(@b)
action_output
else
action_copy
end

when "\n"
if @b == ' '
action_get
elsif @b =~ /[{\[\(+-]/
action_output
else
if alphanum?(@b)
action_output
else
action_copy
end
end

else
if @b == ' '
if alphanum?(@a)
action_output
else
action_get
end
elsif @b == "\n"
if @a =~ /[}\]\)\\"+-]/
action_output
else
if alphanum?(@a)
action_output
else
action_get
end
end
else
action_output
end
end
end

@output
end

private

# Corresponds to action(1) in jsmin.c.
def action_output
@output << @a
action_copy
end

# Corresponds to action(2) in jsmin.c.
def action_copy
@a = @b

if @a == '\'' || @a == '"'
loop do
@output << @a
@a = get

break if @a == @b

if @a[0] <= ORD_LF
raise "JSMin parse error: unterminated string literal: #{@a}"
end

if @a == '\\'
@output << @a
@a = get

if @a[0] <= ORD_LF
raise "JSMin parse error: unterminated string literal: #{@a}"
end
end
end
end

action_get
end

# Corresponds to action(3) in jsmin.c.
def action_get
@b = nextchar

if @b == '/' && (@a == "\n" || @a =~ /[\(,=:\[!&|?{};]/)
@output << @a
@output << @b

loop do
@a = get

if @a == '/'
break
elsif @a == '\\'
@output << @a
@a = get
elsif @a[0] <= ORD_LF
raise "JSMin parse error: unterminated regular expression " +
"literal: #{@a}"
end

@output << @a
end

@b = nextchar
end
end

# Returns true if +c+ is a letter, digit, underscore, dollar sign,
# backslash, or non-ASCII character.
def alphanum?(c)
c.is_a?(String) && !c.empty? && (c[0] > 126 || c =~ /[0-9a-z_$\\]/i)
end

# Returns the next character from the input. If the character is a control
# character, it will be translated to a space or linefeed.
def get
c = @lookahead.nil? ? @js.getch : @lookahead
@lookahead = nil

return c if c.nil? || c == "\n" || c[0] >= ORD_SPACE
return "\n" if c == "\r"
return ' '
end

# Gets the next character, excluding comments.
def nextchar
c = get
return c unless c == '/'

case peek
when '/'
loop do
c = get
return c if c[0] <= ORD_LF
end

when '*'
get
loop do
case get
when '*'
if peek == '/'
get
return ' '
end

when nil
raise 'JSMin parse error: unterminated comment'
end
end

else
return c
end
end

# Gets the next character without getting it.
def peek
@lookahead = get
end
end
end

0 comments on commit 72c23b3

Please sign in to comment.