588 changes: 199 additions & 389 deletions README.md

Large diffs are not rendered by default.

244 changes: 80 additions & 164 deletions Rakefile
@@ -1,201 +1,117 @@
require 'rake/packagetask'

ZEPTO_VERSION = "0.8"

ZEPTO_ROOT = File.expand_path(File.dirname(__FILE__))
ZEPTO_SRC_DIR = File.join(ZEPTO_ROOT, 'src')
ZEPTO_DIST_DIR = File.join(ZEPTO_ROOT, 'dist')
ZEPTO_PKG_DIR = File.join(ZEPTO_ROOT, 'pkg')

ZEPTO_COMPONENTS = [
'polyfill',
'zepto',
'event',
'detect',
'fx',
# 'fx_methods',
'ajax',
'form',
# 'assets',
# 'data',
'touch',
# 'gesture'
]

task :default => [:clean, :concat, :dist]

ZEPTO_TESTS = %w[
test/zepto.html
test/ajax.html
test/data.html
test/detect.html
test/form.html
test/fx.html
test/polyfill.html
]

desc "Clean the distribution directory."
task :clean do
rm_rf ZEPTO_DIST_DIR
mkdir ZEPTO_DIST_DIR
end
DEFAULT_MODULES = %w[ polyfill zepto event detect fx ajax form touch ]

def normalize_whitespace(filename)
contents = File.readlines(filename)
contents.each { |line| line.sub!(/\s+$/, "") }
File.open(filename, "w") do |file|
file.write contents.join("\n").sub(/(\n+)?\Z/m, "\n")
end
end
KILO = 1024 # how many bytes in a "kilobyte"

desc "Strip trailing whitespace and ensure each file ends with a newline"
task :whitespace do
Dir["*", "src/**/*", "test/**/*", "examples/**/*"].each do |filename|
normalize_whitespace(filename) if File.file?(filename)
task :default => :dist

# module-aware file task
class BuildTask < Rake::FileTask
def modules
prerequisites.map {|f| File.basename(f, '.js') }
end
end

desc "Concatenate source files to build zepto.js"
task :concat, [:addons] => :whitespace do |task, args|
# colon-separated arguments such as `concat[foo:bar:-baz]` specify
# which components to add or exclude, depending on if it starts with "-"
add, exclude = args[:addons].to_s.split(':').partition {|c| c !~ /^-/ }
exclude.each {|c| c.sub!('-', '') }
components = (ZEPTO_COMPONENTS | add) - exclude

unless components == ZEPTO_COMPONENTS
puts "Building zepto.js by including: #{components.join(', ')}"
def remove_prerequisites to_remove
@prerequisites -= to_remove
return self
end

File.open(File.join(ZEPTO_DIST_DIR, 'zepto.js'), 'w') do |f|
f.puts components.map { |component|
File.read File.join(ZEPTO_SRC_DIR, "#{component}.js")
}
def modules_mismatch?
File.open(name, 'r') {|f| f.gets } !~ /modules: ([\w,\s]+)/ or
$1.split(/\W+/) != modules
end
end

def google_compiler(src, target)
puts "Minifying #{src} with Google Closure Compiler..."
`java -jar vendor/google-compiler/compiler.jar --js #{src} --summary_detail_level 3 --js_output_file #{target}`
def needed?() super or modules_mismatch? end
end

def yui_compressor(src, target)
puts "Minifying #{src} with YUI Compressor..."
`java -jar vendor/yuicompressor/yuicompressor-2.4.2.jar #{src} -o #{target}`
end
BuildTask.define_task 'dist/zepto.js' => DEFAULT_MODULES.map {|m| "src/#{m}.js" } do |task|
mkdir_p 'dist', :verbose => false
File.open(task.name, 'w') do |zepto|
zepto.puts "/* Zepto %s - %s - zeptojs.com/license */" %
[version_string, task.modules.join(' ')]

def uglifyjs(src, target)
begin
require 'uglifier'
rescue LoadError => e
if verbose
puts "\nYou'll need the 'uglifier' gem for minification. Just run:\n\n"
puts " $ gem install uglifier"
puts "\nand you should be all set.\n\n"
exit
task.prerequisites.each do |src|
# bring in source files one by one, but without copyright info
copyright = true
File.open(src).each_line do |line|
copyright = false if copyright and line !~ %r{^(/|\s*$)}
zepto.puts line unless copyright
end
end
return false
end
puts "Minifying #{src} with UglifyJS..."
File.open(target, "w"){|f| f.puts Uglifier.new.compile(File.read(src))}
end

def process_minified(src, target)
cp target, File.join(ZEPTO_DIST_DIR,'temp.js')
msize = File.size(File.join(ZEPTO_DIST_DIR,'temp.js'))
`gzip -9 #{File.join(ZEPTO_DIST_DIR,'temp.js')}`

osize = File.size(src)
dsize = File.size(File.join(ZEPTO_DIST_DIR,'temp.js.gz'))
rm_rf File.join(ZEPTO_DIST_DIR,'temp.js.gz')

puts "Original version: %.3fk" % (osize/1024.0)
puts "Minified: %.3fk" % (msize/1024.0)
puts "Minified and gzipped: %.3fk, compression factor %.3f" % [dsize/1024.0, osize/dsize.to_f]
file 'dist/zepto.min.js' => 'dist/zepto.js' do |task|
begin require 'uglifier'
rescue LoadError; fail "Uglifier not available: #{$!}"
else
File.open(task.name, 'w') do |min|
min << Uglifier.new.compile(File.read(task.prerequisites.first))
end
end
end

desc "Generates a minified version for distribution, using UglifyJS."
task :dist do
src, target = File.join(ZEPTO_DIST_DIR,'zepto.js'), File.join(ZEPTO_DIST_DIR,'zepto.min.js')
uglifyjs src, target
process_minified src, target
file 'dist/zepto.min.gz' => 'dist/zepto.min.js' do |task|
verbose false do
tmp_file = task.name.sub('.gz', '')
cp task.prerequisites.first, tmp_file
sh 'gzip', '--best', tmp_file
end
end

desc "Generates a minified version for distribution using the Google Closure compiler."
task :googledist do
src, target = File.join(ZEPTO_DIST_DIR,'zepto.js'), File.join(ZEPTO_DIST_DIR,'zepto.min.js')
google_compiler src, target
process_minified src, target
end
desc "Concatenate source files to build zepto.js"
task :concat, [:modules] do |task, args|
modules = args[:modules].to_s.split(':')
to_add, to_exclude = modules.partition {|m| m.sub!(/^(-)?(.+)/, 'src/\2.js'); !$1 }

desc "Generates a minified version for distribution using the YUI compressor."
task :yuidist do
src, target = File.join(ZEPTO_DIST_DIR,'zepto.js'), File.join(ZEPTO_DIST_DIR,'zepto.min.js')
yui_compressor src, target
process_minified src, target
Rake::Task['dist/zepto.js'].
remove_prerequisites(to_exclude).enhance(to_add).
invoke
end

desc "Generate docco documentation from sources."
task :docs do
puts "Generating docs..."
puts "Note: to work, install node.js first, then install docco with 'sudo npm install docco -g'."
puts `docco src/*`
end
desc "Generate zepto.js distribution files and report size statistics"
task :dist => ['dist/zepto.js', 'dist/zepto.min.js', 'dist/zepto.min.gz'] do |task|
orig_size, min_size, gz_size = task.prerequisites.map {|f| File.size(f) }

puts "Original version: %.3fk" % (orig_size.to_f / KILO)
puts "Minified: %.3fk" % (min_size.to_f / KILO)
puts "Minified and gzipped: %.3fk, compression factor %.3f" % [gz_size.to_f / KILO, orig_size.to_f / gz_size]

Rake::PackageTask.new('zepto', ZEPTO_VERSION) do |package|
package.need_tar_gz = true
package.need_zip = true
package.package_dir = ZEPTO_PKG_DIR
package.package_files.include(
'README.md',
'MIT-LICENSE',
'dist/**/*',
'src/**/*',
'test/**/*',
'vendor/evidence.js',
'examples/**/*'
).exclude(*`git ls-files -o test src examples -z`.split("\0"))
rm_f 'dist/zepto.min.gz', :verbose => false
end

desc "Run tests in headless WebKit"
task :test => "jasmine:headless" do
require 'rubygems'
require 'rubygems/specification'
task(:clean) { rm_rf 'dist' }

# HACK: jasmine-headless-webkit doesn't let us access its compiled specrunner directly
if jasmine_gem = Gem::Specification.find_by_name('jasmine-headless-webkit')
headless_root = jasmine_gem.full_gem_path
runner = File.join(headless_root, 'ext/jasmine-webkit-specrunner/jasmine-webkit-specrunner')
desc "Run tests with PhantomJS"
task :test do
sh 'script/test'
end

exec runner, '-c', *ZEPTO_TESTS
else
abort "Can't find 'jasmine-headless-webkit' gem"
desc "Strip trailing whitespace and ensure each file ends with a newline"
task :whitespace do
verbose false do
files = Dir['{src,test,examples}/**/*.{js,html}']
ruby(*%w'-p -i -e $_.sub!(/\s*\Z/,"\n")'.concat(files))
end
end

def silence_warnings
require 'stringio'
begin
old_stderr = $stderr
$stderr = StringIO.new
yield
ensure
$stderr = old_stderr
desc "Generate docco documentation from source files"
task :docco do
verbose false do
sh 'docco', *Dir['src/*.js']
end
end

begin
silence_warnings {
require 'jasmine'
load 'jasmine/tasks/jasmine.rake'
require 'jasmine/headless/task'
}

Jasmine::Headless::Task.new do |task|
task.colors = true
end
rescue LoadError
task :jasmine do
abort "Jasmine is not available. In order to run jasmine, you must: (sudo) gem install jasmine"
# Zepto version number + git sha if available
def version_string
desc = `git describe --tags HEAD 2>&1`.chomp
if $?.success?
desc
else
suffix, dir = '', File.basename(Dir.pwd)
# detect git sha from directory name of GitHub zip/tarball
suffix = "-g#{$1}" if dir =~ /^madrobby-zepto-([a-f0-9]{7,40})$/
ZEPTO_VERSION + suffix
end
end
2 changes: 1 addition & 1 deletion examples/anim.html
Expand Up @@ -5,5 +5,5 @@
<script src="../src/fx.js"></script>

<script>
$('div').anim({ rotate: '720deg', opacity: .5 }, 2, 'ease-out');
$('div').anim({ rotate: '720deg', opacity: .5 }, 2, 'ease-out')
</script>
22 changes: 11 additions & 11 deletions examples/iphone/index.html
Expand Up @@ -34,24 +34,24 @@ <h1>Menu 1</h1>
<script src="../../src/touch.js"></script>
<script>
$(document).ready(function(){
var activate = ('createTouch' in document) ? 'touchstart' : 'click';
var activate = ('createTouch' in document) ? 'touchstart' : 'click'

$("body > section").first().addClass("current")

$("a.back").live(activate, function(event) {
var current = $(this).attr("href");
$(".current").removeClass("current").addClass("reverse");
$(current).addClass("current");
});
var current = $(this).attr("href")
$(".current").removeClass("current").addClass("reverse")
$(current).addClass("current")
})

$(".menu a[href]").live(activate, function(event) {
var link = $(this), section = link.closest('section'),
prev_element = "#"+(section.removeClass("current").addClass("reverse").attr("id"));
$(link.attr("href")).addClass("current");
$(".current .back").remove();
$(".current .toolbar").prepend("<a href=\""+prev_element+"\" class=\"back\">Back</a>");
});
});
prev_element = "#"+(section.removeClass("current").addClass("reverse").attr("id"))
$(link.attr("href")).addClass("current")
$(".current .back").remove()
$(".current .toolbar").prepend("<a href=\""+prev_element+"\" class=\"back\">Back</a>")
})
})
</script>
</body>
</html>
18 changes: 18 additions & 0 deletions examples/load_jquery_on_ie.html
@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<title>Load jQuery if Zepto is not supported</title>
</head>
<body>
<h1>Load jQuery if Zepto is not supported</h1>
</body>
<script>document.write('<script src='+('__proto__' in {} ? '../dist/zepto' : 'https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery')+'.js><\/script>')</script>
<script>
$('body').append('<p>Hello from '+('Zepto' in window ? 'Zepto' : 'jQuery')+'!</p>')

$('script').each(function(index,script){
if (script.src)
$('<p>&lt;script src="'+script.src+'"&gt;</p>').appendTo('body')
})
</script>
</html>
51 changes: 51 additions & 0 deletions examples/snow/index.html
@@ -0,0 +1,51 @@
<!DOCTYPE html>
<html>
<head>
<title>Let it snow with Zepto.js</title>
<meta name="name" content="content">
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<style>
body { background: #000; color: #fff; overflow: hidden }
div { position: absolute; }
</style>
</head>
<body>

<script src="../../src/zepto.js"></script>
<script src="../../src/event.js"></script>
<script src="../../src/fx.js"></script>

<a href="https://twitter.com/share" class="twitter-share-button" data-url="http://zeptojs.com/let-it-snow" data-text="Let it snow with Zepto.js" data-via="zeptojs" data-lang="en">Tweet</a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs")</script>

<script>
$(document).ready(function(){
var i = 100, glyphs = '❄❅❆'.split('')

function rand(max){ return Math.floor(Math.random()*max) }

function addFlake(){
var el = $('<div><div>' + glyphs[rand(glyphs.length)] + '</div></div>')
$('body').append(el)
el.css({ left: rand(100)+'%', top: -100-rand(500), fontSize: 20+rand(30) })
el.anim(
{ translateY: 1500+rand(500)+'px', translateX: 50-rand(100)+'px', rotate: (2e3-rand(4e3))+'deg' },
6+rand(10),
'linear',
function(){
addFlake()
el.remove() // garbage collect
}
);
}

while(i--) addFlake()

// enable for extra fun
//window.ondeviceorientation = function(event){
// ('alpha' in event) && $('body').css({ '-webkit-transform': 'rotate('+event.alpha+'deg)'})
//}
})
</script>
</body>
</html>
30 changes: 30 additions & 0 deletions examples/touch_events.html
@@ -0,0 +1,30 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/>

<script src="../src/zepto.js"></script>
<script src="../src/event.js"></script>
<script src="../src/touch.js"></script>

<style>
.delete {
display: none;
color: red;
}
</style>

<ul id=items>
<li>List item 1 <span class=delete>DELETE</span></li>
<li>List item 2 <span class=delete>DELETE</span></li>
<li>List item 3 <span class=delete>DELETE</span></li>
<li>List item 4 <span class=delete>DELETE</span></li>
</ul>

<script>
$('#items li').swipe(function(){
$('.delete').hide()
$('.delete', this).show()
})

$('.delete').tap(function(){
$(this).parent('li').remove()
})
</script>
6 changes: 6 additions & 0 deletions script/guard
@@ -0,0 +1,6 @@
#!/bin/sh
if [ -z "$BUNDLE_GEMFILE" ]; then
export BUNDLE_GEMFILE=shutup
fi

exec guard --no-notify "$@"
2 changes: 2 additions & 0 deletions script/test
@@ -0,0 +1,2 @@
#!/bin/sh
phantomjs test/runner.coffee "$@"
212 changes: 0 additions & 212 deletions spec/javascripts/ajax_spec.js

This file was deleted.

223 changes: 0 additions & 223 deletions spec/javascripts/helpers/mock-ajax.js

This file was deleted.

15 changes: 0 additions & 15 deletions spec/javascripts/support/jasmine.yml

This file was deleted.

23 changes: 0 additions & 23 deletions spec/javascripts/support/jasmine_config.rb

This file was deleted.

32 changes: 0 additions & 32 deletions spec/javascripts/support/jasmine_runner.rb

This file was deleted.

459 changes: 160 additions & 299 deletions src/ajax.js

Large diffs are not rendered by default.

32 changes: 12 additions & 20 deletions src/assets.js
@@ -1,29 +1,21 @@
// Zepto.js
// (c) 2010, 2011 Thomas Fuchs
// (c) 2010-2012 Thomas Fuchs
// Zepto.js may be freely distributed under the MIT license.

(function($){
var cache = [], timeout;
;(function($){
var cache = [], timeout

// ### $.fn.remove
//
// Remove element from DOM
//
// *Example:*
//
// $('#projects, .comments').remove();
//
$.fn.remove = function(){
return this.each(function(){
if(this.parentNode !== null){
if(this.tagName == 'IMG'){
cache.push(this);
this.src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
if (timeout) clearTimeout(timeout);
timeout = setTimeout(function(){ cache = [] }, 60000);
if(this.parentNode){
if(this.tagName === 'IMG'){
cache.push(this)
this.src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='
if (timeout) clearTimeout(timeout)
timeout = setTimeout(function(){ cache = [] }, 60000)
}
this.parentNode.removeChild(this);
this.parentNode.removeChild(this)
}
});
})
}
})(Zepto);
})(Zepto)
73 changes: 53 additions & 20 deletions src/data.js
@@ -1,33 +1,66 @@
// Zepto.js
// (c) 2010, 2011 Thomas Fuchs
// (c) 2010-2012 Thomas Fuchs
// Zepto.js may be freely distributed under the MIT license.

// The following code is heavily inspired by jQuery's $.fn.data()

(function($) {
var data = {}, dataAttr = $.fn.data,
uuid = $.uuid = +new Date(),
exp = $.expando = 'Zepto' + uuid;
;(function($) {
var data = {}, dataAttr = $.fn.data, camelize = $.zepto.camelize,
exp = $.expando = 'Zepto' + (+new Date())

// Get value from node:
// 1. first try key as given,
// 2. then try camelized key,
// 3. fall back to reading "data-*" attribute.
function getData(node, name) {
var id = node[exp], store = id && data[id];
return name === undefined ? store || setData(node) :
(store && store[name]) || dataAttr.call($(node), name);
var id = node[exp], store = id && data[id]
if (name === undefined) return store || setData(node)
else {
if (store) {
if (name in store) return store[name]
var camelName = camelize(name)
if (camelName in store) return store[camelName]
}
return dataAttr.call($(node), name)
}
}

// Store value under camelized key on node
function setData(node, name, value) {
var id = node[exp] || (node[exp] = ++uuid),
store = data[id] || (data[id] = {});
if (name !== undefined) store[name] = value;
return store;
};
var id = node[exp] || (node[exp] = ++$.uuid),
store = data[id] || (data[id] = attributeData(node))
if (name !== undefined) store[camelize(name)] = value
return store
}

// Read all "data-*" attributes from a node
function attributeData(node) {
var store = {}
$.each(node.attributes, function(i, attr){
if (attr.name.indexOf('data-') == 0)
store[camelize(attr.name.replace('data-', ''))] = attr.value
})
return store
}

$.fn.data = function(name, value) {
return value === undefined ?
this.length == 0 ? undefined : getData(this[0], name) :
this.each(function(idx){
setData(this, name, $.isFunction(value) ?
value.call(this, idx, getData(this, name)) : value);
});
};
})(Zepto);
// set multiple values via object
$.isPlainObject(name) ?
this.each(function(i, node){
$.each(name, function(key, value){ setData(node, key, value) })
}) :
// get value from first element
this.length == 0 ? undefined : getData(this[0], name) :
// set value on all elements
this.each(function(){ setData(this, name, value) })
}

$.fn.removeData = function(names) {
if (typeof names == 'string') names = names.split(/\s+/)
return this.each(function(){
var id = this[exp], store = id && data[id]
if (store) $.each(names, function(){ delete store[camelize(this)] })
})
}
})(Zepto)
62 changes: 24 additions & 38 deletions src/detect.js
@@ -1,54 +1,40 @@
// Zepto.js
// (c) 2010, 2011 Thomas Fuchs
// (c) 2010-2012 Thomas Fuchs
// Zepto.js may be freely distributed under the MIT license.

(function($){
;(function($){
function detect(ua){
var os = (this.os = {}), browser = (this.browser = {}),
var os = this.os = {}, browser = this.browser = {},
webkit = ua.match(/WebKit\/([\d.]+)/),
android = ua.match(/(Android)\s+([\d.]+)/),
ipad = ua.match(/(iPad).*OS\s([\d_]+)/),
iphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/),
webos = ua.match(/(webOS|hpwOS)[\s\/]([\d.]+)/),
touchpad = webos && ua.match(/TouchPad/),
blackberry = ua.match(/(BlackBerry).*Version\/([\d.]+)/);
kindle = ua.match(/Kindle\/([\d.]+)/),
silk = ua.match(/Silk\/([\d._]+)/),
blackberry = ua.match(/(BlackBerry).*Version\/([\d.]+)/)

if (webkit) browser.version = webkit[1];
browser.webkit = !!webkit;
// todo clean this up with a better OS/browser
// separation. we need to discern between multiple
// browsers on android, and decide if kindle fire in
// silk mode is android or not

if (android) os.android = true, os.version = android[2];
if (iphone) os.ios = true, os.version = iphone[2].replace(/_/g, '.'), os.iphone = true;
if (ipad) os.ios = true, os.version = ipad[2].replace(/_/g, '.'), os.ipad = true;
if (webos) os.webos = true, os.version = webos[2];
if (touchpad) os.touchpad = true;
if (blackberry) os.blackberry = true, os.version = blackberry[2];
}
if (browser.webkit = !!webkit) browser.version = webkit[1]

// ### $.os
//
// Object containing information about browser platform
//
// *Example:*
//
// $.os.ios // => true if running on Apple iOS
// $.os.android // => true if running on Android
// $.os.webos // => true if running on HP/Palm WebOS
// $.os.touchpad // => true if running on a HP TouchPad
// $.os.version // => string with a version number, e.g.
// "4.0", "3.1.1", "2.1", etc.
// $.os.iphone // => true if running on iPhone
// $.os.ipad // => true if running on iPad
// $.os.blackberry // => true if running on BlackBerry
//
// ### $.browser
//
// *Example:*
//
// $.browser.webkit // => true if the browser is WebKit-based
// $.browser.version // => WebKit version string
detect.call($, navigator.userAgent);
if (android) os.android = true, os.version = android[2]
if (iphone) os.ios = os.iphone = true, os.version = iphone[2].replace(/_/g, '.')
if (ipad) os.ios = os.ipad = true, os.version = ipad[2].replace(/_/g, '.')
if (webos) os.webos = true, os.version = webos[2]
if (touchpad) os.touchpad = true
if (blackberry) os.blackberry = true, os.version = blackberry[2]
if (kindle) os.kindle = true, os.version = kindle[1]
if (silk) browser.silk = true, browser.version = silk[1]
if (!silk && os.android && ua.match(/Kindle Fire/)) browser.silk = true
}

detect.call($, navigator.userAgent)
// make available to unit tests
$.__detect = detect;
$.__detect = detect

})(Zepto);
})(Zepto)
232 changes: 129 additions & 103 deletions src/event.js
@@ -1,198 +1,224 @@
// Zepto.js
// (c) 2010, 2011 Thomas Fuchs
// (c) 2010-2012 Thomas Fuchs
// Zepto.js may be freely distributed under the MIT license.

(function($){
var $$ = $.qsa, handlers = {}, _zid = 1, specialEvents={};
;(function($){
var $$ = $.zepto.qsa, handlers = {}, _zid = 1, specialEvents={}

specialEvents.click = specialEvents.mousedown = specialEvents.mouseup = specialEvents.mousemove = 'MouseEvents';
specialEvents.click = specialEvents.mousedown = specialEvents.mouseup = specialEvents.mousemove = 'MouseEvents'

function zid(element) {
return element._zid || (element._zid = _zid++);
return element._zid || (element._zid = _zid++)
}
function findHandlers(element, event, fn, selector) {
event = parse(event);
if (event.ns) var matcher = matcherFor(event.ns);
event = parse(event)
if (event.ns) var matcher = matcherFor(event.ns)
return (handlers[zid(element)] || []).filter(function(handler) {
return handler
&& (!event.e || handler.e == event.e)
&& (!event.ns || matcher.test(handler.ns))
&& (!fn || handler.fn == fn)
&& (!selector || handler.sel == selector);
});
&& (!fn || zid(handler.fn) === zid(fn))
&& (!selector || handler.sel == selector)
})
}
function parse(event) {
var parts = ('' + event).split('.');
return {e: parts[0], ns: parts.slice(1).sort().join(' ')};
var parts = ('' + event).split('.')
return {e: parts[0], ns: parts.slice(1).sort().join(' ')}
}
function matcherFor(ns) {
return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)');
return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)')
}

function eachEvent(events, fn, iterator){
if ($.isObject(events)) $.each(events, iterator);
else events.split(/\s/).forEach(function(type){ iterator(type, fn) });
if ($.isObject(events)) $.each(events, iterator)
else events.split(/\s/).forEach(function(type){ iterator(type, fn) })
}

function add(element, events, fn, selector, getDelegate){
var id = zid(element), set = (handlers[id] || (handlers[id] = []));
function add(element, events, fn, selector, getDelegate, capture){
capture = !!capture
var id = zid(element), set = (handlers[id] || (handlers[id] = []))
eachEvent(events, fn, function(event, fn){
var delegate = getDelegate && getDelegate(fn, event),
callback = delegate || fn;
callback = delegate || fn
var proxyfn = function (event) {
var result = callback.apply(element, [event].concat(event.data));
if (result === false) event.preventDefault();
return result;
};
var handler = $.extend(parse(event), {fn: fn, proxy: proxyfn, sel: selector, del: delegate, i: set.length});
set.push(handler);
element.addEventListener(handler.e, proxyfn, false);
});
var result = callback.apply(element, [event].concat(event.data))
if (result === false) event.preventDefault()
return result
}
var handler = $.extend(parse(event), {fn: fn, proxy: proxyfn, sel: selector, del: delegate, i: set.length})
set.push(handler)
element.addEventListener(handler.e, proxyfn, capture)
})
}
function remove(element, events, fn, selector){
var id = zid(element);
var id = zid(element)
eachEvent(events || '', fn, function(event, fn){
findHandlers(element, event, fn, selector).forEach(function(handler){
delete handlers[id][handler.i];
element.removeEventListener(handler.e, handler.proxy, false);
});
});
delete handlers[id][handler.i]
element.removeEventListener(handler.e, handler.proxy, false)
})
})
}

$.event = { add: add, remove: remove }

$.proxy = function(fn, context) {
if ($.isFunction(fn)) {
var proxyFn = function(){ return fn.apply(context, arguments) }
proxyFn._zid = zid(fn)
return proxyFn
} else if (typeof context == 'string') {
return $.proxy(fn[context], fn)
} else {
throw new TypeError("expected function")
}
}

$.fn.bind = function(event, callback){
return this.each(function(){
add(this, event, callback);
});
};
add(this, event, callback)
})
}
$.fn.unbind = function(event, callback){
return this.each(function(){
remove(this, event, callback);
});
};
remove(this, event, callback)
})
}
$.fn.one = function(event, callback){
return this.each(function(i, element){
add(this, event, callback, null, function(fn, type){
return function(){
var result = fn.apply(element, arguments);
remove(element, type, fn);
return result;
var result = fn.apply(element, arguments)
remove(element, type, fn)
return result
}
});
});
};
})
})
}

var returnTrue = function(){return true},
returnFalse = function(){return false},
eventMethods = {
preventDefault: 'isDefaultPrevented',
stopImmediatePropagation: 'isImmediatePropagationStopped',
stopPropagation: 'isPropagationStopped'
};
}
function createProxy(event) {
var proxy = $.extend({originalEvent: event}, event);
var proxy = $.extend({originalEvent: event}, event)
$.each(eventMethods, function(name, predicate) {
proxy[name] = function(){
this[predicate] = returnTrue;
return event[name].apply(event, arguments);
};
proxy[predicate] = returnFalse;
this[predicate] = returnTrue
return event[name].apply(event, arguments)
}
proxy[predicate] = returnFalse
})
return proxy;
return proxy
}

// emulates the 'defaultPrevented' property for browsers that have none
function fix(event) {
if (!('defaultPrevented' in event)) {
event.defaultPrevented = false;
var prevent = event.preventDefault;
event.defaultPrevented = false
var prevent = event.preventDefault
event.preventDefault = function() {
this.defaultPrevented = true;
prevent.call(this);
this.defaultPrevented = true
prevent.call(this)
}
}
}

$.fn.delegate = function(selector, event, callback){
var capture = false
if(event == 'blur' || event == 'focus'){
if($.iswebkit)
event = event == 'blur' ? 'focusout' : event == 'focus' ? 'focusin' : event
else
capture = true
}

return this.each(function(i, element){
add(element, event, callback, selector, function(fn){
return function(e){
var evt, match = $(e.target).closest(selector, element).get(0);
var evt, match = $(e.target).closest(selector, element).get(0)
if (match) {
evt = $.extend(createProxy(e), {currentTarget: match, liveFired: element});
return fn.apply(match, [evt].concat([].slice.call(arguments, 1)));
evt = $.extend(createProxy(e), {currentTarget: match, liveFired: element})
return fn.apply(match, [evt].concat([].slice.call(arguments, 1)))
}
}
});
});
};
}, capture)
})
}
$.fn.undelegate = function(selector, event, callback){
return this.each(function(){
remove(this, event, callback, selector);
});
remove(this, event, callback, selector)
})
}

$.fn.live = function(event, callback){
$(document.body).delegate(this.selector, event, callback);
return this;
};
$(document.body).delegate(this.selector, event, callback)
return this
}
$.fn.die = function(event, callback){
$(document.body).undelegate(this.selector, event, callback);
return this;
};
$(document.body).undelegate(this.selector, event, callback)
return this
}

$.fn.on = function(event, selector, callback){
return selector === undefined || $.isFunction(selector) ?
this.bind(event, selector) : this.delegate(selector, event, callback);
};
return selector == undefined || $.isFunction(selector) ?
this.bind(event, selector) : this.delegate(selector, event, callback)
}
$.fn.off = function(event, selector, callback){
return selector === undefined || $.isFunction(selector) ?
this.unbind(event, selector) : this.undelegate(selector, event, callback);
};
return selector == undefined || $.isFunction(selector) ?
this.unbind(event, selector) : this.undelegate(selector, event, callback)
}

$.fn.trigger = function(event, data){
if (typeof event == 'string') event = $.Event(event);
fix(event);
event.data = data;
return this.each(function(){ this.dispatchEvent(event) });
};
if (typeof event == 'string') event = $.Event(event)
fix(event)
event.data = data
return this.each(function(){
// items in the collection might not be DOM elements
// (todo: possibly support events on plain old objects)
if('dispatchEvent' in this) this.dispatchEvent(event)
})
}

// triggers event handlers on current element just as if an event occurred,
// doesn't trigger an actual event, doesn't bubble
$.fn.triggerHandler = function(event, data){
var e, result;
var e, result
this.each(function(i, element){
e = createProxy(typeof event == 'string' ? $.Event(event) : event);
e.data = data; e.target = element;
e = createProxy(typeof event == 'string' ? $.Event(event) : event)
e.data = data
e.target = element
$.each(findHandlers(element, event.type || event), function(i, handler){
result = handler.proxy(e);
if (e.isImmediatePropagationStopped()) return false;
});
});
return result;
};
result = handler.proxy(e)
if (e.isImmediatePropagationStopped()) return false
})
})
return result
}

// shortcut methods for `.bind(event, fn)` for each event type
('focusin focusout load resize scroll unload click dblclick '+
;('focusin focusout load resize scroll unload click dblclick '+
'mousedown mouseup mousemove mouseover mouseout '+
'change select keydown keypress keyup error').split(' ').forEach(function(event) {
$.fn[event] = function(callback){ return this.bind(event, callback) };
});
$.fn[event] = function(callback){ return this.bind(event, callback) }
})

['focus', 'blur'].forEach(function(name) {
;['focus', 'blur'].forEach(function(name) {
$.fn[name] = function(callback) {
if (callback) this.bind(name, callback);
else if (this.length) try { this.get(0)[name]() } catch(e){};
return this;
};
});
if (callback) this.bind(name, callback)
else if (this.length) try { this.get(0)[name]() } catch(e){}
return this
}
})

$.Event = function(type, props) {
var event = document.createEvent(specialEvents[type] || 'Events'), bubbles = true;
if (props) for (var name in props) (name == 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name]);
event.initEvent(type, bubbles, true, null, null, null, null, null, null, null, null, null, null, null, null);
return event;
};
var event = document.createEvent(specialEvents[type] || 'Events'), bubbles = true
if (props) for (var name in props) (name == 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name])
event.initEvent(type, bubbles, true, null, null, null, null, null, null, null, null, null, null, null, null)
return event
}

})(Zepto);
})(Zepto)
94 changes: 20 additions & 74 deletions src/form.js
@@ -1,94 +1,40 @@
// Zepto.js
// (c) 2010, 2011 Thomas Fuchs
// (c) 2010-2012 Thomas Fuchs
// Zepto.js may be freely distributed under the MIT license.

(function ($) {

// ### $.fn.serializeArray
//
// Encode a set of form elements as an array of names and values
//
// *Example:*
//
// $('#login_form').serializeArray();
//
// returns
//
// [
// {
// name: 'email',
// value: 'koss@nocorp.me'
// },
// {
// name: 'password',
// value: '123456'
// }
// ]
//
;(function ($) {
$.fn.serializeArray = function () {
var result = [], el;
var result = [], el
$( Array.prototype.slice.call(this.get(0).elements) ).each(function () {
el = $(this);
var type = el.attr('type');
if (
el = $(this)
var type = el.attr('type')
if (this.nodeName.toLowerCase() != 'fieldset' &&
!this.disabled && type != 'submit' && type != 'reset' && type != 'button' &&
((type != 'radio' && type != 'checkbox') || this.checked)
) {
((type != 'radio' && type != 'checkbox') || this.checked))
result.push({
name: el.attr('name'),
value: el.val()
});
}
});
return result;
};
})
})
return result
}

// ### $.fn.serialize
//
//
// Encode a set of form elements as a string for submission
//
// *Example:*
//
// $('#login_form').serialize();
//
// returns
//
// "email=koss%40nocorp.me&password=123456"
//
$.fn.serialize = function () {
var result = [];
var result = []
this.serializeArray().forEach(function (elm) {
result.push( encodeURIComponent(elm.name) + '=' + encodeURIComponent(elm.value) );
});
return result.join('&');
};
result.push( encodeURIComponent(elm.name) + '=' + encodeURIComponent(elm.value) )
})
return result.join('&')
}

// ### $.fn.submit
//
// Bind or trigger the submit event for a form
//
// *Examples:*
//
// To bind a handler for the submit event:
//
// $('#login_form').submit(function (e) {
// alert('Form was submitted!');
// e.preventDefault();
// });
//
// To trigger form submit:
//
// $('#login_form').submit();
//
$.fn.submit = function (callback) {
if (callback) this.bind('submit', callback)
else if (this.length) {
var event = $.Event('submit');
this.eq(0).trigger(event);
var event = $.Event('submit')
this.eq(0).trigger(event)
if (!event.defaultPrevented) this.get(0).submit()
}
return this;
return this
}

})(Zepto);
})(Zepto)
91 changes: 52 additions & 39 deletions src/fx.js
@@ -1,78 +1,91 @@
// Zepto.js
// (c) 2010, 2011 Thomas Fuchs
// (c) 2010-2012 Thomas Fuchs
// Zepto.js may be freely distributed under the MIT license.

(function($, undefined){
;(function($, undefined){
var prefix = '', eventPrefix, endEventName, endAnimationName,
vendors = {Webkit: 'webkit', Moz: '', O: 'o', ms: 'MS'},
vendors = { Webkit: 'webkit', Moz: '', O: 'o', ms: 'MS' },
document = window.document, testEl = document.createElement('div'),
supportedTransforms = /^((translate|rotate|scale)(X|Y|Z|3d)?|matrix(3d)?|perspective|skew(X|Y)?)$/i;
supportedTransforms = /^((translate|rotate|scale)(X|Y|Z|3d)?|matrix(3d)?|perspective|skew(X|Y)?)$/i,
clearProperties = {}

function downcase(str) { return str.toLowerCase() }
function normalizeEvent(name) { return eventPrefix ? eventPrefix + name : downcase(name) };
function normalizeEvent(name) { return eventPrefix ? eventPrefix + name : downcase(name) }

$.each(vendors, function(vendor, event){
if (testEl.style[vendor + 'TransitionProperty'] !== undefined) {
prefix = '-' + downcase(vendor) + '-';
eventPrefix = event;
return false;
prefix = '-' + downcase(vendor) + '-'
eventPrefix = event
return false
}
});
})

clearProperties[prefix + 'transition-property'] =
clearProperties[prefix + 'transition-duration'] =
clearProperties[prefix + 'transition-timing-function'] =
clearProperties[prefix + 'animation-name'] =
clearProperties[prefix + 'animation-duration'] = ''

$.fx = {
off: (eventPrefix === undefined && testEl.style.transitionProperty === undefined),
cssPrefix: prefix,
transitionEnd: normalizeEvent('TransitionEnd'),
animationEnd: normalizeEvent('AnimationEnd')
};
}

$.fn.animate = function(properties, duration, ease, callback){
if ($.isObject(duration))
ease = duration.easing, callback = duration.complete, duration = duration.duration;
if (duration) duration = duration / 1000;
return this.anim(properties, duration, ease, callback);
};
ease = duration.easing, callback = duration.complete, duration = duration.duration
if (duration) duration = duration / 1000
return this.anim(properties, duration, ease, callback)
}

$.fn.anim = function(properties, duration, ease, callback){
var transforms, cssProperties = {}, key, that = this, wrappedCallback, endEvent = $.fx.transitionEnd;
if (duration === undefined) duration = 0.4;
if ($.fx.off) duration = 0;
var transforms, cssProperties = {}, key, that = this, wrappedCallback, endEvent = $.fx.transitionEnd
if (duration === undefined) duration = 0.4
if ($.fx.off) duration = 0

if (typeof properties == 'string') {
// keyframe animation
cssProperties[prefix + 'animation-name'] = properties;
cssProperties[prefix + 'animation-duration'] = duration + 's';
endEvent = $.fx.animationEnd;
cssProperties[prefix + 'animation-name'] = properties
cssProperties[prefix + 'animation-duration'] = duration + 's'
endEvent = $.fx.animationEnd
} else {
// CSS transitions
for (key in properties)
if (supportedTransforms.test(key)) {
transforms || (transforms = []);
transforms.push(key + '(' + properties[key] + ')');
transforms || (transforms = [])
transforms.push(key + '(' + properties[key] + ')')
}
else cssProperties[key] = properties[key];
else cssProperties[key] = properties[key]

if (transforms) cssProperties[prefix + 'transform'] = transforms.join(' ');
if (!$.fx.off) cssProperties[prefix + 'transition'] = 'all ' + duration + 's ' + (ease || '');
if (transforms) cssProperties[prefix + 'transform'] = transforms.join(' ')
if (!$.fx.off && typeof properties === 'object') {
cssProperties[prefix + 'transition-property'] = Object.keys(properties).join(', ')
cssProperties[prefix + 'transition-duration'] = duration + 's'
cssProperties[prefix + 'transition-timing-function'] = (ease || 'linear')
}
}

wrappedCallback = function(){
var props = {};
props[prefix + 'transition'] = props[prefix + 'animation-name'] = 'none';
$(this).css(props);
callback && callback.call(this);
wrappedCallback = function(event){
if (typeof event !== 'undefined') {
if (event.target !== event.currentTarget) return // makes sure the event didn't bubble from "below"
$(event.target).unbind(endEvent, arguments.callee)
}
$(this).css(clearProperties)
callback && callback.call(this)
}
if (duration > 0) this.one(endEvent, wrappedCallback);
if (duration > 0) this.bind(endEvent, wrappedCallback)

setTimeout(function() {
that.css(cssProperties);
that.css(cssProperties)
if (duration <= 0) setTimeout(function() {
that.each(function(){ wrappedCallback.call(this) });
}, 0);
}, 0);
that.each(function(){ wrappedCallback.call(this) })
}, 0)
}, 0)

return this;
};
return this
}

testEl = null;
})(Zepto);
testEl = null
})(Zepto)
94 changes: 34 additions & 60 deletions src/fx_methods.js
@@ -1,98 +1,72 @@
// Zepto.js
// (c) 2010, 2011 Thomas Fuchs
// (c) 2010-2012 Thomas Fuchs
// Zepto.js may be freely distributed under the MIT license.

(function($, undefined){
;(function($, undefined){
var document = window.document, docElem = document.documentElement,
origShow = $.fn.show, origHide = $.fn.hide, origToggle = $.fn.toggle,
speeds = { _default: 400, fast: 200, slow: 600 };
speeds = { _default: 400, fast: 200, slow: 600 }

function translateSpeed(speed) {
return typeof speed == 'number' ? speed : (speeds[speed] || speeds._default);
return typeof speed == 'number' ? speed : (speeds[speed] || speeds._default)
}

function anim(el, speed, opacity, scale, callback) {
if (typeof speed == 'function' && !callback) callback = speed, speed = undefined;
var props = { opacity: opacity };
if (typeof speed == 'function' && !callback) callback = speed, speed = undefined
var props = { opacity: opacity }
if (scale) {
if ($.fx.transforms3d) props.scale3d = scale + ',1';
else props.scale = scale;
el.css($.fx.cssPrefix + 'transform-origin', '0 0');
props.scale = scale
el.css($.fx.cssPrefix + 'transform-origin', '0 0')
}
return el.anim(props, translateSpeed(speed) / 1000, null, callback);
return el.anim(props, translateSpeed(speed) / 1000, null, callback)
}

function hide(el, speed, scale, callback) {
return anim(el, speed, 0, scale, function(){
origHide.call($(this));
callback && callback.call(this);
});
origHide.call($(this))
callback && callback.call(this)
})
}

$.fn.show = function(speed, callback) {
origShow.call(this);
if (speed === undefined) speed = 0;
else this.css('opacity', 0);
return anim(this, speed, 1, '1,1', callback);
};
origShow.call(this)
if (speed === undefined) speed = 0
else this.css('opacity', 0)
return anim(this, speed, 1, '1,1', callback)
}

$.fn.hide = function(speed, callback) {
if (speed === undefined) return origHide.call(this);
else return hide(this, speed, '0,0', callback);
if (speed === undefined) return origHide.call(this)
else return hide(this, speed, '0,0', callback)
}

$.fn.toggle = function(speed, callback) {
if (speed === undefined || typeof speed == 'boolean') return origToggle.call(this, speed);
else return this[this.css('display') == 'none' ? 'show' : 'hide'](speed, callback);
};
if (speed === undefined || typeof speed == 'boolean') return origToggle.call(this, speed)
else return this[this.css('display') == 'none' ? 'show' : 'hide'](speed, callback)
}

$.fn.fadeTo = function(speed, opacity, callback) {
return anim(this, speed, opacity, null, callback);
};
return anim(this, speed, opacity, null, callback)
}

$.fn.fadeIn = function(speed, callback) {
var target = this.css('opacity')
if (target > 0) this.css('opacity', 0)
else target = 1
return origShow.call(this).fadeTo(speed, target, callback);
};
return origShow.call(this).fadeTo(speed, target, callback)
}

$.fn.fadeOut = function(speed, callback) {
return hide(this, speed, null, callback);
};
return hide(this, speed, null, callback)
}

$.fn.fadeToggle = function(speed, callback) {
var hidden = this.css('opacity') == 0 || this.css('display') == 'none';
return this[hidden ? 'fadeIn' : 'fadeOut'](speed, callback);
};
var hidden = this.css('opacity') == 0 || this.css('display') == 'none'
return this[hidden ? 'fadeIn' : 'fadeOut'](speed, callback)
}

$.extend($.fx, {
speeds: speeds,
// feature detection for 3D transforms adapted from Modernizr
transforms3d: (function(props){
var ret = false;
$.each(props, function(i, prop){
if (docElem.style[prop] !== undefined) {
ret = i != 1 || webkitTransforms3d();
return false;
}
});
return ret;
})('perspectiveProperty WebkitPerspective MozPerspective OPerspective msPerspective'.split(' '))
});
speeds: speeds
})

function webkitTransforms3d() {
var ret, div = document.createElement('div'),
testEl = document.createElement('div'),
css = '@media (-webkit-transform-3d){#zeptotest{left:9px;position:absolute}}',
style = ['&shy;', '<style>', css, '</style>'].join('');

div.innerHTML += style;
testEl.id = 'zeptotest';
div.appendChild(testEl);
docElem.appendChild(div);
ret = testEl.offsetLeft === 9;
div.parentNode.removeChild(div);
return ret;
}
})(Zepto);
})(Zepto)
34 changes: 17 additions & 17 deletions src/gesture.js
@@ -1,35 +1,35 @@
// Zepto.js
// (c) 2010, 2011 Thomas Fuchs
// (c) 2010-2012 Thomas Fuchs
// Zepto.js may be freely distributed under the MIT license.

(function($){
;(function($){
if ($.os.ios) {
var gesture = {}, gestureTimeout;
var gesture = {}, gestureTimeout

function parentIfText(node){
return 'tagName' in node ? node : node.parentNode;
return 'tagName' in node ? node : node.parentNode
}

$(document).bind('gesturestart', function(e){
var now = Date.now(), delta = now - (gesture.last || now);
gesture.target = parentIfText(e.target);
gestureTimeout && clearTimeout(gestureTimeout);
gesture.e1 = e.scale;
gesture.last = now;
var now = Date.now(), delta = now - (gesture.last || now)
gesture.target = parentIfText(e.target)
gestureTimeout && clearTimeout(gestureTimeout)
gesture.e1 = e.scale
gesture.last = now
}).bind('gesturechange', function(e){
gesture.e2 = e.scale;
gesture.e2 = e.scale
}).bind('gestureend', function(e){
if (gesture.e2 > 0) {
Math.abs(gesture.e1 - gesture.e2) != 0 && $(gesture.target).trigger('pinch') &&
$(gesture.target).trigger('pinch' + (gesture.e1 - gesture.e2 > 0 ? 'In' : 'Out'));
gesture.e1 = gesture.e2 = gesture.last = 0;
$(gesture.target).trigger('pinch' + (gesture.e1 - gesture.e2 > 0 ? 'In' : 'Out'))
gesture.e1 = gesture.e2 = gesture.last = 0
} else if ('last' in gesture) {
gesture = {};
gesture = {}
}
});
})

['pinch', 'pinchIn', 'pinchOut'].forEach(function(m){
;['pinch', 'pinchIn', 'pinchOut'].forEach(function(m){
$.fn[m] = function(callback){ return this.bind(m, callback) }
});
})
}
})(Zepto);
})(Zepto)
34 changes: 17 additions & 17 deletions src/polyfill.js
@@ -1,36 +1,36 @@
// Zepto.js
// (c) 2010, 2011 Thomas Fuchs
// (c) 2010-2012 Thomas Fuchs
// Zepto.js may be freely distributed under the MIT license.

(function(undefined){
;(function(undefined){
if (String.prototype.trim === undefined) // fix for iOS 3.2
String.prototype.trim = function(){ return this.replace(/^\s+/, '').replace(/\s+$/, '') };
String.prototype.trim = function(){ return this.replace(/^\s+/, '').replace(/\s+$/, '') }

// For iOS 3.x
// from https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/reduce
if (Array.prototype.reduce === undefined)
Array.prototype.reduce = function(fun){
if(this === void 0 || this === null) throw new TypeError();
var t = Object(this), len = t.length >>> 0, k = 0, accumulator;
if(typeof fun != 'function') throw new TypeError();
if(len == 0 && arguments.length == 1) throw new TypeError();
if(this === void 0 || this === null) throw new TypeError()
var t = Object(this), len = t.length >>> 0, k = 0, accumulator
if(typeof fun != 'function') throw new TypeError()
if(len == 0 && arguments.length == 1) throw new TypeError()

if(arguments.length >= 2)
accumulator = arguments[1];
accumulator = arguments[1]
else
do{
if(k in t){
accumulator = t[k++];
break;
accumulator = t[k++]
break
}
if(++k >= len) throw new TypeError();
} while (true);
if(++k >= len) throw new TypeError()
} while (true)

while (k < len){
if(k in t) accumulator = fun.call(undefined, accumulator, t[k], k, t);
k++;
if(k in t) accumulator = fun.call(undefined, accumulator, t[k], k, t)
k++
}
return accumulator;
};
return accumulator
}

})();
})()
70 changes: 70 additions & 0 deletions src/selector.js
@@ -0,0 +1,70 @@
;(function($){
var zepto = $.zepto, oldQsa = zepto.qsa, oldMatches = zepto.matches

function visible(elem){
elem = $(elem)
return !!(elem.width() || elem.height()) && elem.css("display") !== "none"
}

// Implements a subset from:
// http://api.jquery.com/category/selectors/jquery-selector-extensions/
//
// Each filter function receives the current index, all nodes in the
// considered set, and a value if there were parentheses. The value
// of `this` is the node currently being considered. The function returns the
// resulting node(s), null, or undefined.
//
// Complex selectors are not supported:
// li:has(label:contains("foo")) + li:has(label:contains("bar"))
// "> h2"
// ul.inner:first > li
var filters = zepto.cssFilters = {
visible: function(){ if (visible(this)) return this },
hidden: function(){ if (!visible(this)) return this },
selected: function(){ if (this.selected) return this },
checked: function(){ if (this.checked) return this },
parent: function(){ return this.parentNode },
first: function(idx){ if (idx === 0) return this },
last: function(idx, nodes){ if (idx === nodes.length - 1) return this },
eq: function(idx, _, value){ if (idx === value) return this },
contains: function(idx, _, text){ if ($(this).text().indexOf(text) > -1) return this },
has: function(idx, _, sel){ if (zepto.qsa(this, sel).length) return this }
}

var re = new RegExp('(.*):(\\w+)(?:\\(([^)]+)\\))?$\\s*')

function process(sel, fn) {
var filter, arg, match = sel.match(re)
if (match && match[2] in filters) {
var filter = filters[match[2]], arg = match[3]
sel = match[1]
if (arg) {
var num = Number(arg)
if (isNaN(num)) arg = arg.replace(/^["']|["']$/g, '')
else arg = num
}
}
return fn(sel, filter, arg)
}

zepto.qsa = function(node, selector) {
return process(selector, function(sel, filter, arg){
try {
if (!sel && filter) sel = '*'
var nodes = oldQsa(node, sel)
} catch(e) {
console.error('error performing selector: %o', selector)
throw e
}
return !filter ? nodes :
zepto.uniq($.map(nodes, function(n, i){ return filter.call(n, i, nodes, arg) }))
})
}

zepto.matches = function(node, selector){
return process(selector, function(sel, filter, arg){
return (!sel || oldMatches(node, sel)) &&
(!filter || filter.call(node, null, arg) === node)
})
}
})(Zepto)
22 changes: 22 additions & 0 deletions src/stack.js
@@ -0,0 +1,22 @@
// Zepto.js
// (c) 2010-2012 Thomas Fuchs
// Zepto.js may be freely distributed under the MIT license.

;(function($){
$.fn.end = function(){
return this.prevObject || $()
}

$.fn.andSelf = function(){
return this.add(this.prevObject || $())
}

'filter,add,not,eq,first,last,find,closest,parents,parent,children,siblings'.split(',').forEach(function(property){
var fn = $.fn[property]
$.fn[property] = function(){
var ret = fn.apply(this, arguments)
ret.prevObject = this
return ret
}
})
})(Zepto)
101 changes: 61 additions & 40 deletions src/touch.js
@@ -1,64 +1,85 @@
// Zepto.js
// (c) 2010, 2011 Thomas Fuchs
// (c) 2010-2012 Thomas Fuchs
// Zepto.js may be freely distributed under the MIT license.

(function($){
var touch = {}, touchTimeout;
;(function($){
var touch = {}, touchTimeout

function parentIfText(node){
return 'tagName' in node ? node : node.parentNode;
return 'tagName' in node ? node : node.parentNode
}

function swipeDirection(x1, x2, y1, y2){
var xDelta = Math.abs(x1 - x2), yDelta = Math.abs(y1 - y2);
if (xDelta >= yDelta) {
return (x1 - x2 > 0 ? 'Left' : 'Right');
} else {
return (y1 - y2 > 0 ? 'Up' : 'Down');
}
var xDelta = Math.abs(x1 - x2), yDelta = Math.abs(y1 - y2)
return xDelta >= yDelta ? (x1 - x2 > 0 ? 'Left' : 'Right') : (y1 - y2 > 0 ? 'Up' : 'Down')
}

var longTapDelay = 750;
var longTapDelay = 750, longTapTimeout

function longTap(){
if (touch.last && (Date.now() - touch.last >= longTapDelay)) {
$(touch.target).trigger('longTap');
touch = {};
longTapTimeout = null
if (touch.last) {
touch.el.trigger('longTap')
touch = {}
}
}

function cancelLongTap(){
if (longTapTimeout) clearTimeout(longTapTimeout)
longTapTimeout = null
}

$(document).ready(function(){
var now, delta

$(document.body).bind('touchstart', function(e){
var now = Date.now(), delta = now - (touch.last || now);
touch.target = parentIfText(e.touches[0].target);
touchTimeout && clearTimeout(touchTimeout);
touch.x1 = e.touches[0].pageX;
touch.y1 = e.touches[0].pageY;
if (delta > 0 && delta <= 250) touch.isDoubleTap = true;
touch.last = now;
setTimeout(longTap, longTapDelay);
now = Date.now()
delta = now - (touch.last || now)
touch.el = $(parentIfText(e.touches[0].target))
touchTimeout && clearTimeout(touchTimeout)
touch.x1 = e.touches[0].pageX
touch.y1 = e.touches[0].pageY
if (delta > 0 && delta <= 250) touch.isDoubleTap = true
touch.last = now
longTapTimeout = setTimeout(longTap, longTapDelay)
}).bind('touchmove', function(e){
touch.x2 = e.touches[0].pageX;
touch.y2 = e.touches[0].pageY;
cancelLongTap()
touch.x2 = e.touches[0].pageX
touch.y2 = e.touches[0].pageY
}).bind('touchend', function(e){
cancelLongTap()

// double tap (tapped twice within 250ms)
if (touch.isDoubleTap) {
$(touch.target).trigger('doubleTap');
touch = {};
} else if (touch.x2 > 0 || touch.y2 > 0) {
(Math.abs(touch.x1 - touch.x2) > 30 || Math.abs(touch.y1 - touch.y2) > 30) &&
$(touch.target).trigger('swipe') &&
$(touch.target).trigger('swipe' + (swipeDirection(touch.x1, touch.x2, touch.y1, touch.y2)));
touch.x1 = touch.x2 = touch.y1 = touch.y2 = touch.last = 0;
touch.el.trigger('doubleTap')
touch = {}

// swipe
} else if ((touch.x2 && Math.abs(touch.x1 - touch.x2) > 30) ||
(touch.y2 && Math.abs(touch.y1 - touch.y2) > 30)) {
touch.el.trigger('swipe') &&
touch.el.trigger('swipe' + (swipeDirection(touch.x1, touch.x2, touch.y1, touch.y2)))
touch = {}

// normal tap
} else if ('last' in touch) {
touch.el.trigger('tap')

touchTimeout = setTimeout(function(){
touchTimeout = null;
$(touch.target).trigger('tap')
touch = {};
}, 250);
touchTimeout = null
touch.el.trigger('singleTap')
touch = {}
}, 250)
}
}).bind('touchcancel', function(){ touch = {} });
});
}).bind('touchcancel', function(){
if (touchTimeout) clearTimeout(touchTimeout)
if (longTapTimeout) clearTimeout(longTapTimeout)
longTapTimeout = touchTimeout = null
touch = {}
})
})

['swipe', 'swipeLeft', 'swipeRight', 'swipeUp', 'swipeDown', 'doubleTap', 'tap', 'longTap'].forEach(function(m){
;['swipe', 'swipeLeft', 'swipeRight', 'swipeUp', 'swipeDown', 'doubleTap', 'tap', 'singleTap', 'longTap'].forEach(function(m){
$.fn[m] = function(callback){ return this.bind(m, callback) }
});
})(Zepto);
})
})(Zepto)
637 changes: 379 additions & 258 deletions src/zepto.js

Large diffs are not rendered by default.

620 changes: 373 additions & 247 deletions test/ajax.html

Large diffs are not rendered by default.

22 changes: 13 additions & 9 deletions test/assets_functional.html
Expand Up @@ -23,6 +23,8 @@
<body>
<h1>Zepto assets functional test</h1>

<div id="browser"></div>

<ul>
<li><a href="?without">Run without the assets plugin</a></li>
<li><a href="?with">Run with the assets plugin</a></li>
Expand All @@ -38,19 +40,21 @@ <h1>Zepto assets functional test</h1>
var prefix = 'http://stuff.vandervossen.net/temporary/zepto_assets_test/sintel-',
mode = window.location.search.slice(1),
timeout,
i;
i
if (mode === 'with') {
document.write('<script src="../src/assets.js"></s'+'cript>');
document.write('RUNNING WITH ASSETS PLUGIN');
document.write('<script src="../src/assets.js"></s'+'cript>')
document.write('RUNNING WITH ASSETS PLUGIN')
}
if (mode !== '') {
i = 1;
(function frame() {
$('#image' + (i - 4)).remove();
$('#container').prepend('<div>' + i + ': <img id="image' + i + '" src="' + prefix + i + '.jpg"></div>');
if (i++ < 51) timeout = setTimeout(frame, 6000);
})();
i = 1
;(function frame() {
$('#image' + (i - 4)).remove()
$('#container').prepend('<div>' + i + ': <img id="image' + i + '" src="' + prefix + i + '.jpg"></div>')
if (i++ < 51) timeout = setTimeout(frame, 6000)
})()
}

$('#browser').text(navigator.userAgent)
</script>
</body>
</html>
193 changes: 155 additions & 38 deletions test/data.html
Expand Up @@ -16,81 +16,198 @@ <h1>Zepto data() tests</h1>
Running… see browser console for results
</p>
<div id="fixtures">
<div id="data_attr" data-one="uno" data-two="due"></div>
<div id="data_attr" data-one="uno" data-two="due" data-foo-bar="baz"></div>
<div id="data_full" data-mode="awesome"></div>
<div id="data_obj" data-mode="awesome"></div>
<ol id="data_list">
<li data-category="arts"></li>
<li data-category="science"></li>
</ol>
<ol id="data_list2">
<li></li>
<li></li>
</ol>
</div>

<script>
Evidence('ZeptoExtendedDataTest', {
testEmptyCollection: function(t){
var el = $('#does_not_exist');
t.assertUndefined(el.data('one'));
var el = $('#does_not_exist')
t.assertUndefined(el.data('one'))
},

testAttributeDoesNotExist: function(t){
var el = $('#data_attr')
t.assertUndefined(el.data('missing'))
},

testReadingAttribute: function(t){
var el = $('#data_attr');
t.assertEqual('uno', el.data('one'));
var el = $('#data_attr')
t.assertEqual('uno', el.data('one'))
},

testCamelized: function(t){
var el = $('#data_attr')
t.assertEqual('baz', el.data('foo-bar'))
t.assertEqual('baz', el.data('fooBar'))

el.data('fooBar', 'bam')
t.assertEqual('bam', el.data('foo-bar'))
t.assertEqual('bam', el.data('fooBar'))

el.data('a-b', 'c')
t.assertEqual('c', el.data().aB)
t.assertUndefined(el.data()['a-b'])
},

testNotChangingAttribute: function(t){
var el = $('#data_attr');
t.assertEqual('due', el.data('two'));
el.data('two', 'changed');
t.assertEqual('due', el.attr('data-two'));
var el = $('#data_attr')
t.assertEqual('due', el.data('two'))
el.data('two', 'changed')
t.assertEqual('due', el.attr('data-two'))
},

testExtendedData: function(t){
var els = $('#data_attr'),
els2 = $('#data_attr'),
obj = { a: 'A', b: 'B' };
obj = { a: 'A', b: 'B' }

els.data('obj', obj);
t.assertIdentical(obj, els.data('obj'));
t.assertIdentical(obj, els2.data('obj'));
els.data('obj', obj)
t.assertIdentical(obj, els.data('obj'))
t.assertIdentical(obj, els2.data('obj'))

els2.data('els', els);
t.assertIdentical(els, els.data('els'));
els2.data('els', els)
t.assertIdentical(els, els.data('els'))
},

testMultipleElements: function(t){
var items = $('#data_list li');
var items = $('#data_list li')

items.data('each', 'mark');
items.data('each', 'mark')

var values = items.map(function(){ return $(this).data('each') });
t.assertEqual('mark, mark', values.join(', '));
var values = items.map(function(){ return $(this).data('each') })
t.assertEqual('mark, mark', values.join(', '))
},

testFunctionArg: function(t){
var items = $('#data_list li');
var els = $('#data_attr')

var data = "hello"

els.data("addio", function () {
data = "goodbye"
})

t.assertEqual('hello', data)

items.data('category', function(i, val){
return i + '_' + val.toUpperCase();
});
els.data("addio")()

var values = items.map(function(){ return $(this).data('category') });
t.assertEqual('0_ARTS, 1_SCIENCE', values.join(', '));
t.assertEqual('goodbye', data)
},

testAllData: function(t){
var el = $('#data_full');

el.data().samurai = 7;
el.data('one', 'ichi').data('two', 'ni');
el.data('person', {name: 'Kurosawa'});

var all = el.data();
t.assertEqual(7, all.samurai);
t.assertEqual('ichi', all.one);
t.assertEqual('ni', all.two);
t.assertEqual('Kurosawa', all.person.name);
t.assertUndefined(all.mode);
var el = $('#data_full')

el.data().samurai = 7
el.data('one', 'ichi').data('two', 'ni')
el.data('person', {name: 'Kurosawa'})

var all = el.data()
t.assertEqual(7, all.samurai)
t.assertEqual('ichi', all.one)
t.assertEqual('ni', all.two)
t.assertEqual('Kurosawa', all.person.name)
},

testInitialDataFromAttributes: function(t){
var el = $('<div data-foo=bar data-foo-bar=baz />'),
store = el.data()

t.assertEqual('bar', store.foo)
t.assertEqual('baz', store.fooBar)
t.assertUndefined(store['foo-bar'])
},

testGettingBlanks: function(t){
var el = $('#data_attr'),
store = el.data()

store.nil = null
store.undef = undefined
store.blank = ''
store.bool = false

t.assertNull(el.data('nil'))
t.assertUndefined(el.data('undef'))
t.assertIdentical('', el.data('blank'))
t.assertFalse(el.data('bool'))
},

testRemoveData: function(t){
var el = $('<div data-foo=bar />')

el.data('foo', 'bam').data('bar', 'baz')
el.removeData('foo').removeData('bar')
t.assertEqual('bar', el.data('foo'))
t.assertUndefined(el.data('bar'))

el.data('uno', 'one').data('due', 'two')
el.removeData('uno due')
t.assertUndefined(el.data('uno'))
t.assertUndefined(el.data('due'))

el.data('one', 1).data('twoThree', 23)
el.removeData(['one', 'two-three'])
t.assertUndefined(el.data('one'))
t.assertUndefined(el.data('twoThree'))
},

testRemoveDataNoop: function(t){
var empty = $(),
vanilla = $('<div />')

t.assertIdentical(empty, empty.removeData('foo'))
t.assertIdentical(vanilla, vanilla.removeData('foo'))
},

testSettingDataWithObj: function(t){
var el = $('#data_obj')

el.data({
'foo': 'bar',
'answer': 42,
'color': 'blue'
})

var all = el.data()

t.assertEqual(all.answer, 42)
t.assertEqual(all.color, 'blue')
t.assertEqual(all.foo, 'bar')

el.data('foo', 'baz')

t.assertEqual(all.foo, 'baz')
t.assertEqual(all.answer, 42)
},

testSettingDataWithObjOnManyElements: function(t){
var items = $('#data_list2 li')

items.data({
'foo': 'bar',
'answer': 42,
'color': 'purple'
})

var values = items.map(function(){ return $(this).data('foo') })
t.assertEqual('bar, bar', values.join(', '))

var values2 = items.map(function(){ return $(this).data('answer') })
t.assertEqual('42, 42', values2.join(', '))
}
});

})
</script>
</body>
</html>
172 changes: 99 additions & 73 deletions test/detect.html
Expand Up @@ -44,118 +44,144 @@ <h1>Browser detection</h1>
Firefox_Mobile_Simulator: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:2.1.1) Gecko/ Firefox/4.0.2pre Fennec/4.0.1",

Opera_11_51: "Opera/9.80 (Macintosh; Intel Mac OS X 10.7.1; U; en) Presto/2.9.168 Version/11.51",
Opera_Mobile_Simulator: "Opera/9.80 (Macintosh; Intel Mac OS X; Opera Mobi/[BUILD_NR]; U; en) Presto/2.7.81 Version/11.00"
};
Opera_Mobile_Simulator: "Opera/9.80 (Macintosh; Intel Mac OS X; Opera Mobi/[BUILD_NR]; U; en) Presto/2.7.81 Version/11.00",

Kindle: "Mozilla/5.0 (Linux; U; en-US) AppleWebKit/528.5+ (KHTML, like Gecko, Safari/528.5+) Version/4.0 Kindle/3.0 (screen 600Ă—800; rotate)",
Silk_1_0_accel: "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-us; Silk/1.0.13.328_10008910) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 Silk-Accelerated=true",
Silk_1_0: "Mozilla/5.0 (Linux; U; Android 2.3.4; en-us; Kindle Fire Build/GINGERBREAD) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1"
}

function detect(ua, callback){
var obj = {};
$.__detect.call(obj, ua);
callback.call(null, obj.os, obj.browser);
var obj = {}
$.__detect.call(obj, ua)
callback.call(null, obj.os, obj.browser)
}

Evidence('ZeptoDetectTest', {

testWebOS: function(t){
detect(UA.WebOS_1_4_0_Pre, function(os, browser){
t.assertTrue(os.webos);
t.assert(!os.touchpad);
t.assertTrue(browser.webkit);
t.assertEqual("1.4.0", os.version);
});
t.assertTrue(os.webos)
t.assert(!os.touchpad)
t.assertTrue(browser.webkit)
t.assertEqual("1.4.0", os.version)
})
detect(UA.WebOS_1_4_0_Pixi, function(os){
t.assertTrue(os.webos);
t.assertEqual("1.4.0", os.version);
});
t.assertTrue(os.webos)
t.assertEqual("1.4.0", os.version)
})
detect(UA.WebOS_1_2_9_Pixi, function(os){
t.assertTrue(os.webos);
t.assertEqual("1.2.9", os.version);
});
t.assertTrue(os.webos)
t.assertEqual("1.2.9", os.version)
})
detect(UA.WebOS_3_0_0_TouchPad, function(os){
t.assertTrue(os.webos);
t.assertTrue(os.touchpad);
t.assertEqual("3.0.0", os.version);
});
t.assertTrue(os.webos)
t.assertTrue(os.touchpad)
t.assertEqual("3.0.0", os.version)
})
},

testAndroid: function(t){
detect(UA.Android_1_5, function(os, browser){
t.assertTrue(os.android);
t.assertTrue(browser.webkit);
t.assertEqual("1.5", os.version);
});
t.assertTrue(os.android)
t.assertTrue(browser.webkit)
t.assertEqual("1.5", os.version)
})
detect(UA.Android_2_1, function(os){
t.assertTrue(os.android);
t.assertEqual("2.1", os.version);
});
t.assertTrue(os.android)
t.assertEqual("2.1", os.version)
})
},

testIOS: function(t){
detect(UA.iOS_3_0_iPhone, function(os, browser){
t.assertTrue(os.ios);
t.assertTrue(os.iphone);
t.assertTrue(browser.webkit);
t.assertEqual("3.0", os.version);
t.assertEqual("420.1", browser.version);
});
t.assertTrue(os.ios)
t.assertTrue(os.iphone)
t.assertTrue(browser.webkit)
t.assertEqual("3.0", os.version)
t.assertEqual("420.1", browser.version)
})
detect(UA.iOS_3_1_1_iPod, function(os){
t.assertTrue(os.ios);
t.assertTrue(os.iphone);
t.assertUndefined(os.ipod);
t.assertEqual("3.1.1", os.version);
});
t.assertTrue(os.ios)
t.assertTrue(os.iphone)
t.assertUndefined(os.ipod)
t.assertEqual("3.1.1", os.version)
})
detect(UA.iOS_3_2_iPad, function(os){
t.assertTrue(os.ios);
t.assertTrue(os.ipad);
t.assert(!os.iphone);
t.assertEqual("3.2", os.version);
});
t.assertTrue(os.ios)
t.assertTrue(os.ipad)
t.assert(!os.iphone)
t.assertEqual("3.2", os.version)
})
detect(UA.iOS_3_2_iPad_2, function(os){
t.assertTrue(os.ios);
t.assertTrue(os.ipad);
t.assert(!os.iphone);
t.assertEqual("3.2", os.version);
});
t.assertTrue(os.ios)
t.assertTrue(os.ipad)
t.assert(!os.iphone)
t.assertEqual("3.2", os.version)
})
detect(UA.iOS_4_0_iPhone, function(os){
t.assertTrue(os.ios);
t.assertTrue(os.iphone);
t.assert(!os.ipad);
t.assertEqual("4.0", os.version);
});
t.assertTrue(os.ios)
t.assertTrue(os.iphone)
t.assert(!os.ipad)
t.assertEqual("4.0", os.version)
})
detect(UA.iOS_4_2_iPad, function(os){
t.assertTrue(os.ios);
t.assertTrue(os.ipad);
t.assertEqual("4.2", os.version);
});
t.assertTrue(os.ios)
t.assertTrue(os.ipad)
t.assertEqual("4.2", os.version)
})
detect(UA.iOS_4_3_iPhone_Simulator, function(os){
t.assertTrue(os.ios);
t.assertTrue(os.iphone);
t.assertEqual("4.3", os.version);
});
t.assertTrue(os.ios)
t.assertTrue(os.iphone)
t.assertEqual("4.3", os.version)
})
},

testBlackBerry: function(t) {
detect(UA.BlackBerry_6_0_0_141, function(os, browser){
t.assertTrue(os.blackberry);
t.assertTrue(browser.webkit);
t.assertEqual("6.0.0.141", os.version);
});
t.assertTrue(os.blackberry)
t.assertTrue(browser.webkit)
t.assertEqual("6.0.0.141", os.version)
})
},

testKindle: function(t) {
detect(UA.Kindle, function(os, browser){
t.assertTrue(os.kindle)
t.assertTrue(browser.webkit)
t.assertEqual("3.0", os.version)
})

detect(UA.Silk_1_0, function(os, browser){
t.assertTrue(os.android)
t.assertTrue(browser.webkit)
t.assertTrue(browser.silk)
t.assertEqual("2.3.4", os.version)
})

detect(UA.Silk_1_0_accel, function(os, browser){
t.assert(!os.android)
t.assertTrue(browser.webkit)
t.assertTrue(browser.silk)
t.assertEqual("1.0.13.328_10008910", browser.version)
})
},

testFirefox: function(t) {
detect(UA.Firefox_6_0_2, function(os, browser){
t.assertFalse(browser.webkit);
t.assertUndefined(browser.version);
});
t.assertFalse(browser.webkit)
t.assertUndefined(browser.version)
})
},

testOpera: function(t) {
detect(UA.Opera_11_51, function(os, browser){
t.assertFalse(browser.webkit);
t.assertUndefined(browser.version);
});
t.assertFalse(browser.webkit)
t.assertUndefined(browser.version)
})
}
});
})();
})
})()
</script>
</body>
</html>
137 changes: 137 additions & 0 deletions test/event.html
@@ -0,0 +1,137 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<link rel="stylesheet" href="test.css">
<title>Zepto event tests</title>
<script src="../vendor/evidence.js"></script>
<script src="evidence_runner.js"></script>
<script src="../src/polyfill.js"></script>
<script src="../src/zepto.js"></script>
<script src="../src/event.js"></script>
</head>
<body>
<h1>Zepto event tests</h1>
<p id="results">
Running… see browser console for results
</p>
<div id="fixtures">
</div><!-- fixtures -->

<script>
(function(){

function click(el){
var event = document.createEvent('MouseEvents')
event.initMouseEvent('click', true, true, document.defaultView, 1, 0, 0, 0, 0, false, false, false, false, 0, null)
$(el).get(0).dispatchEvent(event)
}

Evidence('ProxyTest', {
testProxyFnContext: function(t){
var a = {name: 'A', fn: function(n, o){ return this.name + n + o }},
b = {name: 'B'}

t.assertEqual('A13', a.fn(1, 3))
t.assertEqual('B52', $.proxy(a.fn, b)(5, 2))
},

testProxyInvalidFn: function(t){
try {
$.proxy(null)
t.fail("shouldn't be here")
} catch(e) {
t.assertEqual('TypeError', e.name)
t.assertEqual("expected function", e.message)
}
},

testProxyContextName: function(t){
var b = {name: 'B', fn: function(n, o){ return this.name + n + o }},
oldFn = b.fn

t.assertEqual('B52', $.proxy(b, 'fn')(5, 2))
t.assertIdentical(oldFn, b.fn)
},

testProxyUndefinedProperty: function(t){
try {
$.proxy({}, 'nonexistent')
t.fail("shouldn't be here")
} catch(e) {
t.assertEqual('TypeError', e.name)
t.assertEqual("expected function", e.message)
}
},

testProxyInvalidProperty: function(t){
try {
$.proxy({num:3}, 'num')
t.fail("shouldn't be here")
} catch(e) {
t.assertEqual('TypeError', e.name)
t.assertEqual("expected function", e.message)
}
}
})

Evidence('EventTest', {
setUp: function(){
this.el = $('<div />').appendTo('#fixtures')
},

tearDown: function(){
this.el.off().remove()
$([document, document.body]).off()
},

testProxiedHandlerCanBeUnbindedWithOriginal: function(t){
var obj = {times:0, fn: function(){ this.times++ }}

this.el.on('click', $.proxy(obj, 'fn'))
click(this.el)
t.assertEqual(1, obj.times)

this.el.off('click', obj.fn)
click(this.el)
t.assertEqual(1, obj.times)
},

testOnWithObject: function(t){
var log = []
this.el.on({ click: function(){ log.push('a') } }).
on({ click: function(){ log.push('b') } }, null)

click(this.el)
t.assertEqual('a b', log.sort().join(' '))
},

testOnWithNullSelector: function(t){
var log = []
this.el.on('click', null, function(){ log.push('a') }).
on('click', undefined, function(){ log.push('b') })

click(this.el)
t.assertEqual('a b', log.sort().join(' '))
},

testOffWithObject: function(t){
var log = [],
fn = function(){ log.push('a') },
fn2 = function(){ log.push('b') },
fn3 = function(){ log.push('c') }

this.el.on('click', fn).on('click', fn2).on('click', fn3)
click(this.el)
t.assertEqual('a b c', log.sort().join(' '))

this.el.off({ click: fn }).off({ click: fn2 }, null)
click(this.el)
t.assertEqual('a b c c', log.sort().join(' '))
}
})
})()
</script>
</body>
</html>
99 changes: 7 additions & 92 deletions test/evidence_runner.js
@@ -1,7 +1,6 @@
(function(){
var ConsoleTestRunner = Evidence.AutoRunner.RUNNERS.console,
ConsoleTestResult = Evidence.UI.Console.TestResult,
Logger = Evidence.UI.Console.Logger,
AutoRunner = Evidence.AutoRunner,
printf = Evidence.UI.printf

Expand All @@ -25,7 +24,7 @@
}

var TestRunner = inherit(ConsoleTestRunner, function(_super) {
Evidence.AutoRunner.RUNNERS.zepto = this
AutoRunner.RUNNERS.zepto = this
return {
_makeResult: function() { return new TestResult(this.logger) }
}
Expand Down Expand Up @@ -54,98 +53,12 @@
}
})

/* JHW = Jasmine Headless WebKit */

var JHWLogger = inherit(Logger, function(_super) {
Evidence.AutoRunner.LOGGERS.jhw = this
return {
log: function(level, msg, params) {
level = level || Logger.NOTSET;
if (level >= this.level) {
if (params) msg = printf(msg, params)
if (level >= Logger.WARN) console.log(msg)
else JHW.log(msg)
}
}
}
})

var JHWTestRunner = inherit(ConsoleTestRunner, function(_super) {
Evidence.AutoRunner.RUNNERS.jhw = this
return {
_makeResult: function() { return new JHWTestResult(this.logger) }
}
})

var JHWTestResult = inherit(Evidence.TestResult, function(_super) {
return {
initialize: function(logger) {
Evidence.TestResult.call(this)
this.logger = logger
this.failuresBuffer = []
},
addSkip: function(testcase, msg) {
_super.addSkip.call(this, testcase, msg)
this.failuresBuffer.push(function(){
JHW.printResult('Skipping testcase ' + testcase + ': ' + msg.message)
})
},
addFailure: function(testcase, msg) {
_super.addFailure.call(this, testcase, msg)
JHW.specFailed(msg)
this.failuresBuffer.push(function(){
JHW.printResult(printf(testcase + ': ' + msg.message + ' ' + msg.template, msg.args))
})
},
addError: function(testcase, error) {
_super.addError.call(this, testcase, error)
JHW.specFailed(error)
this.failuresBuffer.push(function(){
JHW.printResult(testcase + ': ' + error)
})
},
startTest: function(testcase) {
_super.startTest.call(this, testcase)
this.logger.debug('Started testcase ' + testcase + '.')
},
stopTest: function(testcase) {
this.logger.debug('Completed testcase ' + testcase + '.')
JHW.specPassed()
},
pauseTest: function(testcase) {
this.logger.debug('Paused testcase ' + testcase + '.')
},
restartTest: function(testcase) {
this.logger.debug('Restarted testcase ' + testcase + '.')
},
startSuite: function(suite) {
this.logger.info('Started suite ' + suite + '.')
},
stopSuite: function(suite) {
this.logger.debug('Completed suite ' + suite + '.')
if (this.failuresBuffer.length) JHW.printResult('') // HACK: force newline
while (this.failuresBuffer.length)
this.failuresBuffer.shift().call(this)
},
start: function(t0) {
_super.start.call(this, t0)
this.logger.debug('Started tests.')
},
stop: function(t1) {
_super.stop.call(this, t1)
JHW.finishSuite((t1-this.t0)/1000, this.testCount, this.failureCount + this.errorCount)
}
}
})

// HACK: force our test runner as default
;(function(){
var _super = AutoRunner.prototype.retrieveOptions
AutoRunner.prototype.retrieveOptions = function() {
var options = _super.call(this)
if (!options.runner) options.runner = window.JHW ? 'jhw' : 'zepto'
if (!options.logger && window.JHW) options.logger = 'jhw'
if (!options.timeout && window.JHW) options.timeout = 4
if (!options.runner) options.runner = 'zepto'
return options
}
})()
Expand All @@ -156,13 +69,15 @@
var container = $('results')
if (container) {
if (results.failureCount || results.errorCount) {
container.innerHTML = printf("Finished in %d s. &ndash; <em>%d failures, %d errors</em>",
[seconds, results.failureCount, results.errorCount])
container.className = 'failed'
container.innerHTML = printf("Finished in %d s. &ndash; <em>%d failures, %d errors</em> (%d assertions)",
[seconds, results.failureCount, results.errorCount, results.assertionCount])
} else {
container.innerHTML = printf("Finished in %d s. &ndash; <em>%d tests passed</em>", [seconds, results.testCount])
container.className = 'passed'
container.innerHTML = printf("Finished in %d s. &ndash; <em>%d tests passed</em> (%d assertions)",
[seconds, results.testCount, results.assertionCount])
}
container.className += ' finished'
}
}

Expand Down
16 changes: 16 additions & 0 deletions test/fixtures/ajax_load_selector_javascript.html
@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>testAjaxLoad</title>
</head>
<body>
<div id="ajax_load_test_div">ajax load with selector</div>
<div>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</div>
<script>
window.testValue = 1;
</script>
</body>
</html>
8 changes: 7 additions & 1 deletion test/fixtures/jsonp.js
@@ -1 +1,7 @@
jsonp1({"items":[]})
for (key in window) {
// we can't read callback from URL, hence this hack
if (/^jsonp(\d+)$/.test(key)) {
window[key]({ hello: 'world' })
}
}
delete window.key