diff --git a/rubything.css b/rubything.css
new file mode 100644
index 0000000..f4eef4f
--- /dev/null
+++ b/rubything.css
@@ -0,0 +1,90 @@
+@import url(s6/projection.css); /* required to make the slide show run at all */
+
+body { font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; }
+
+a:link, a:visited { color: black; }
+
+.slide h1 { font-size: 30pt; }
+
+.slide h1 { text-align: center; }
+
+.slide h1.fullscreen { position: absolute;
+ top: 40%;
+ width: 100%; }
+
+/* lets you create slides with no heading (because heading is hidden but gets included in toc) */
+.slide h1.hidden { display: none; }
+
+
+.slide h2 { font-size: 28pt; }
+
+h3 { font-size: 25pt; }
+
+/* todo: add special formating for .cover slide
+ lets you use h1(cover). for title/cover slide (a la S5 slide0) but more generic (not bound to 1st slide)
+*/
+
+.cover h1 { /* tbd */ }
+.cover h2 { /* tbd */ }
+
+/* todo: add special formating for h1, h2 in footer */
+
+#footer h1 { /* tbd */ }
+#footer h2 { /* tbd */ }
+
+
+p, li, dt, dd, td, th { font-size: 18pt; }
+
+ul { list-style-type: square; }
+
+/**********************************/
+/* general text-alignment classes */
+
+.left { text-align: left; }
+.center { text-align: center; }
+.right { text-align: right; }
+
+/**********************************/
+/* general font-size classes */
+
+.small { font-size: 97%; }
+
+.x-small,
+.smaller { font-size: 88%; }
+
+.xx-small,
+.smallest,
+.tiny { font-size: 82%; }
+
+
+
+pre { font-size: 16pt; }
+
+.code {
+ background-color: azure;
+ padding: 5px;
+ }
+
+.footnote a:first-of-type { text-decoration: none; }
+
+
+p.small { font-size: 97%; }
+
+p.x-small,
+p.smaller,
+p.footnote { font-size: 88%; }
+
+p.xx-small,
+p.smallest,
+p.tiny { font-size: 82%; }
+
+
+.help p,
+.help td { font-size: 88%; }
+
+
+.step { color: silver; }
+/* or hide next steps e.g. .step { visibility: hidden; } */
+.stepcurrent { color: black; }
+
+
diff --git a/rubything.html b/rubything.html
new file mode 100644
index 0000000..c42efa0
--- /dev/null
+++ b/rubything.html
@@ -0,0 +1,965 @@
+
+
+
+
+
+
+
Learning Ruby and Rails
Good morning!
+
+
+
+
Outline of the Day
+ - 10:00-11:30 Introduction to Ruby
+ - 11:30-Noon Ruby Koans
+ - Noon-1:00 Lunch
+ - 1:00-2:45 Rails
+ - 2:45-3:00 Break
+ - 3:00-4:30 Rails part 2
+ - 4:30 What Next?
+
+
+
+
+
What We’ll Cover
+ - Basic concepts of programming
+ - Syntax and simple operations of Ruby
+
+ - Objects, classes, and methods
+
+ - An introduction to Rails
+
+ - How to use Rails machinery to manipulate application data
+ - Data validation in Rails
+ - Displaying data in Rails
+ - Using scaffolding to start a simple application
+
+
+
+
+
+
What Is Programming?
+ - Programming is the act of writing programs
+ - A program is a precise, text-based, description of how to perform a computation on data
+ - Data
+
+ - Numbers
+ - Strings: pieces of text
+ - Lists of things
+ - Booleans: true and false
+
+ - Computations
+
+ - Add 3 and 4
+ - Find the largest number in a list of numbers
+ - Contact Trimet’s network and find the most convenient bus route
+
+
+
+
+
+
What Is a Programming Language?
+ - A human readable format for describing computations
+ - Any language can describe the same computations
+ - Different languages are really just a different vocabulary
+
+
+
+
+
Why Ruby?
+ - It’s easy to learn
+ - Portland has a strong Ruby community
+ - Rails is a popular web framework built on Ruby
+ - Rails gives Ruby a good, simple, vocabulary for web development
+
+
+
+
+
Starting Ruby
Open a terminal window. Type “irb” and hit enter.
+
Try entering a few things:
+
1
+4 + 3
+"hat"
+[1, 2, 3, "happy"].each { |i| puts i }
+
>>
is your prompt to enter text and =>
will appear at the start of each response.
+
Use the up and down arrow keys to navigate back and forth through the previous commands you’ve entered.
+
+
+
+
Basic Ruby Data
+
+ Numbers |
+ 1 , 2 , 3 |
+
+
+ Strings |
+ "chicken" |
+
+
+ Booleans |
+ true , false |
+
+
+ Symbols |
+ :chicken |
+
+
+ Arrays |
+ [1, 7, "cake"] |
+
+
+ Hashes |
+ {:chicken => "chicken", :sillyexample => :chicken} |
+
+
+
Type these at the command line! What happens?
+
That’s the data, where are the computations?
+
+
+
+
Basic Ruby Computations
+
+ 2 + 2 |
+ |
+
+
+ 3 * 3 |
+ |
+
+
+ "chicken".reverse |
+ |
+
+
+ "chicken".length |
+ |
+
+
+ 2 + 2 == 4 |
+ |
+
+
+ 2 + 2 == 5 |
+ |
+
+
+ [1 , 2, 3].include?(2) |
+ |
+ |
+
+
+
+
+
+
Basic Ruby Computations
+
+ 2 + 2 |
+ 4 |
+
+
+ 3 * 3 |
+ |
+
+
+ "chicken".reverse |
+ |
+
+
+ "chicken".length |
+ |
+
+
+ 2 + 2 == 4 |
+ |
+
+
+ 2 + 2 == 5 |
+ |
+
+
+ [1 , 2, 3].include?(2) |
+ |
+ |
+
+
+
+
+
+
Basic Ruby Computations
+
+ 2 + 2 |
+ 4 |
+
+
+ 3 * 3 |
+ 9 |
+
+
+ "chicken".reverse |
+ |
+
+
+ "chicken".length |
+ |
+
+
+ 2 + 2 == 4 |
+ |
+
+
+ 2 + 2 == 5 |
+ |
+
+
+ [1 , 2, 3].include?(2) |
+ |
+ |
+
+
+
+
+
+
Basic Ruby Computations
+
+ 2 + 2 |
+ 4 |
+
+
+ 3 * 3 |
+ 9 |
+
+
+ "chicken".reverse |
+ "nekcihc" |
+
+
+ "chicken".length |
+ |
+
+
+ 2 + 2 == 4 |
+ |
+
+
+ 2 + 2 == 5 |
+ |
+
+
+ [1 , 2, 3].include?(2) |
+ |
+ |
+
+
+
+
+
+
Basic Ruby Computations
+
+ 2 + 2 |
+ 4 |
+
+
+ 3 * 3 |
+ 9 |
+
+
+ "chicken".reverse |
+ "nekcihc" |
+
+
+ "chicken".length |
+ 7 |
+
+
+ 2 + 2 == 4 |
+ |
+
+
+ 2 + 2 == 5 |
+ |
+
+
+ [1 , 2, 3].include?(2) |
+ |
+ |
+
+
+
+
+
+
Basic Ruby Computations
+
+ 2 + 2 |
+ 4 |
+
+
+ 3 * 3 |
+ 9 |
+
+
+ "chicken".reverse |
+ "nekcihc" |
+
+
+ "chicken".length |
+ 7 |
+
+
+ 2 + 2 == 4 |
+ true |
+
+
+ 2 + 2 == 5 |
+ |
+
+
+ [1, 2, 3].include?(2) |
+ |
+ |
+
+
+
+
+
+
Basic Ruby Computations
+
+ 2 + 2 |
+ 4 |
+
+
+ 3 * 3 |
+ 9 |
+
+
+ "chicken".reverse |
+ "nekcihc" |
+
+
+ "chicken".length |
+ 7 |
+
+
+ 2 + 2 == 4 |
+ true |
+
+
+ 2 + 2 == 5 |
+ false |
+
+
+ [1, 2, 3].include?(2) |
+ |
+ |
+
+
+
+
+
+
Basic Ruby Computations
+
+ 2 + 2 |
+ 4 |
+
+
+ 3 * 3 |
+ 9 |
+
+
+ "chicken".reverse |
+ "nekcihc" |
+
+
+ "chicken".length |
+ 7 |
+
+
+ 2 + 2 == 4 |
+ true |
+
+
+ 2 + 2 == 5 |
+ false |
+
+
+ [1, 2, 3].include?(2) |
+ |
+ true |
+
+
+
What do you think "chicken".reverse.length
does?
+What about "puppy".include?('p')
?
+
+
+
+
That’s Nice But…
+ - How do we combine steps or use the result of an action?
+ - Variables name particular values
+ - The same variable can be set to different values at different times
+
+
var1 = 4
+var1
+var2 = "chicken"
+var2
+var2 = var1
+var2
+
+
+
+
Interlude: String Interpolation
Something you might want to do is print out a string that has a value
+contained within it.
+
For example, try typing
+
"Our array is #{[1,2,3]}"
+
Most useful when dealing with variables!
+
var = [1,2,3,4,5,6]
+"Our array is #{var}"
+
Now back to variables!
+
+
+
+
Variables
Try typing the following, noting what happens at each step
+
thing = "chicken"
+thing
+thing.reverse
+thing
+thing = thing.reverse
+
+
+
+
Variables
+ thing.reverse
didn’t permanently reverse the string!
+ - We had to set the value with another assignment statement
+ - Some functions do change state
+
+
Try
+
awesomelist = [5,2,1,8]
+awesomelist.sort!
+awesomelist
+
How did that happen? Actions that end with a !
change the data!
+This is a Ruby convention, but a good one to follow.
+
+
+
+
Hashes
Let’s consider a book rating system:
+
+ - We use numeric values 0-5 to represent a book that you’ve read
+ - :not_rated will represent a book that you haven’t rated
+ - We’ll store all this data in a hash
+
+
Fill in a hash with some books, e.g.
+
books = { "Left Hand of Darkness" => 5,
+ "The Word for World Is Forest" => 5,
+ "Nevermind the Pollacks" => 0,
+ "Only Revolutions" => :not_rated }
+
+
+
+
Hashes
We can retrieve the rating for a particular book like so
+
books["Left Hand of Darkness"]
+
We can also set values like so
+
books["Only Revolutions"] = 3
+
How can we add a rating for a new book? Any guesses?
+
+
+
+
Hashes
We can retrieve the rating for a particular book like so
+
books["Left Hand of Darkness"]
+
We can also set values like so
+
books["Only Revolutions"] = 3
+
How can we add a rating for a new book? Any guesses?
+
We set the value of the book just like before!
+
books["White Teeth"] = 4
+
Now type books
to see the whole hash.
+
+
+
+
Let’s Try Something
40.reverse
+
What happens?
+
+
+
+
Let’s Try Something
40.reverse
+
What happens?
+
Ruby just reported NoMethodError: undefined method `reverse' for 40:Fixnum
+
That means that reverse
is not something you can do to the number 40.
+
+
+
+
Methods
Most computations in Ruby are performed by methods.
+Type 40.methods
to see which methods are available for basic numbers.
+
Notice +
, -
, et al. in the list?
+
What about "chicken".methods
?
+
+
+
+
Objects
An object is data along with the methods, computations, that
+can operate on it.
+
Everything in Ruby is an object: numbers, strings, hashes, etc.
+
How do you know what kind of object something is? Ask!
+
40.class
+
What is a class?
+
+
+
+
Classes
+ - Classes are templates for making objects
+ - Classes create different types of objects
+
+ - All numbers can be added
+ - All strings can be reversed
+
+ - They define kinds of data and methods on that data
+
+
+
+
+
Classes & Methods
Let’s learn the syntax by example:
+
class Counter
+end
+
What can we do with Counter
?
+
c = Counter.new
+
+
+
+
Classes & Methods
Let’s learn the syntax by example:
+
class Counter
+attr_accessor :value
+
+end
+
Now try
+
c = Counter.new
+c.value
+c.value = 10
+c.value
+
What happened? What does attr_accessor :value
do?
+
+
+
+
Classes & Methods
Let’s learn the syntax by example:
+
class Counter
+ attr_accessor :value
+
+ def initialize
+ @value = 0
+ end
+
+end
+
Now, again, try
+
c = Counter.new
+c.value
+
initialize
gives new
instructions on how to create an object
+
+
+
+
Classes & Methods
Let’s learn the syntax by example:
+
class Counter
+ attr_accessor :value
+
+ def initialize
+ @value = 0
+ end
+
+ def increment
+ @value = @value + 1
+ end
+
+end
+
+
+
+
Classes
Let’s use our Counter class:
+
count = Counter.new
+count.increment
+count.increment
+count.value
+
count.class
+count.methods
+
Try count.respond_to?("increment")
. What did you see?
+
+
+
+
Class Exercise
Let’s add a Counter.increment_by
method that takes an argument for how much to increment.
+
Don’t need to start all over – we open the class instead
+
class Counter
+
+def increment_by(n)
+ # fill in here
+end
+
+end
+
Test your code as follows:
+
c = Counter.new
+c.increment_by(5)
+c.value
+
+
+
+
Classes
You can add methods to existing classes as well:
+
class String
+ def chicken?
+ self == "chicken"
+ end
+end
+
"chicken".chicken?
+"puppy".chicken?
+
self
is a way of referring to the object that the method is being called on.
+
In "puppy".chicken?
, self
is "puppy"
.
+
+
+
+
Classes Exercise
Add a method to String that will test to see if a string is a palindrome.
+
A palindrome is any string that is the same read forwards or backwards.
+
To get you started type the following at the command line:
+
class String
+ def palindrome?
+
and finish the rest! Test it on "abba".palindrome?
and "puppy".palindrome?
+
+
+
+
+
Blocks
Some methods take blocks.
+
list.each {|p| code}
runs code
on every element of list
+
list = [1,2,3,4]
+list.each { |n| puts (n + 1) }
+
+ - A block is a computation, but not a method
+ - A block is of the form
{ |arg1, arg2, ..| code }
+ { |v| v.palindrome? }
+ { |x, y| x * y }
+
+
+
+
+
Blocks
A more complicated example of using each:
+
reviews = Hash.new(0)
+books.values.each { |rate| reviews[rate] = reviews[rate] + 1 }
+reviews
+
reviews
is a count of how many reviews you gave with a particular rating
+
reviews[5]
+
+
+
+
Blocks
There’s another way to write blocks. This is commonly used for multi-line expressions.
+
reviews = Hash.new(0)
+books.values.each do |rate|
+ reviews[rate] = reviews[rate] + 1
+ # more code can go here...
+end
+
+
+
+
Control Structures
Ruby provides control structures for writing more complicated code.
+
If statements are a switch on whether the argument is true or false.
+
if true
+ 1
+else
+ 2
+end
+
if false
+ 1
+else
+ 2
+end
+
+
+
+
Control Structures
Case statements evaluate a statement and execute the code in the corresponding branch:
+
case favorite_color
+when "green"
+ puts "Grass is green"
+when "blue"
+ puts "Skies are blue"
+when "red"
+ puts "Tomatoes are red"
+else
+ puts "Are you sure that's a color?"
+end
+
+
+
+
Control Structures
For-in statements allow iteration over a structure such as a list
+
list = [1,2,3,4,5]
+sum = 0
+for n in list
+ sum = sum + n
+end
+sum
+
+
+
+
+
Intro To Rails
+ - Rails is a framework for building and deploying web applications
+ - It’s structured using an architecture called MVC: Model-View-Controller
+ - MVC separates different kinds of application logic into sections based on what it’s used for. In Rails, these live in separate folders.
+ - Rails values “convention over configuration”—the platform will make certain assumptions about how you’re going to build your application. This makes tasks that suit Rails’ conventions very easy, but actions that don’t follow these conventions will take more work.
+
+
+
+
+
An Example Rails App
To get started:
+
+ - Go to the microrant directory
+ - Type
bundle install
+ - Then type
rails server
+ - Open http://localhost:3000 in your web browser
+
+
+
+
+
Microrant
+ - Twitter? Never heard of it.
+ - 10 characters to express your anger
+ - Basic activities: create, read, update, delete (CRUD)
+ - Go to http://localhost:3000/users/new to create a new user with your name
+ - Then go to /rants/new and create a new rant
+
+ - Rants can also be edited and deleted—give it a try!
+
+
+
+ - What’s behind the curtain?
+
+ - Rails provides classes that allow us to retrieve and manipulate data easily
+ - Logic for the application is written in normal Ruby, and a module called ActiveRecord does all of the database connection work for you
+
+
+
+
+
+
Behind the Scenes: the database
+
+
At The Console
+ - Close down the Rails server with Ctrl-C
+ - Run
rails console
+ - You can modify the data using
Rant
and User
classes
+ - The
User
class has id
and name
attributes
+ - The
Rant
class has id
, message
, and user_id
attributes
+
+
+
+
+
At The Console
How can we look up a rant after it’s been created?
+
r = Rant.find(1)
+r.message
+r.user_id
+
What about modifying a rant?
+
r.message = "RANTING!"
+r.save
+
We need to save
the changes when we’re done editing.
+
+
+
+
Where’s My Stuff
Rails models have built-in finder methods that help you retrieve records from the database.
+
Try:
+
+ Rant.all
+ Rant.first
+ Rant.find(:all, :user_id => 1)
+ Rant.all(:user_id => 1)
+ Rant.find_by_user_id(1)
+
+
+
+
+
At the Console
Let’s try creating a new Rant, for user_id
1
+
user = User.find(1)
+rant = user.rants.build
+rant.message = "WHAT IS TH"
+rant.save
+
Note that we didn’t need to set the id
field of the message!
+That was automatically set by new
.
+
We save
the record when we’re done, in order to create it, just like when we were editing a rant before.
+
+
+
+
At The Console
Creating a rant that way was verbose. Is there a better way?
+
User.find(1).rants.create(:message => "E POINT!?")
+
Notice that you don’t need to save when you use create
—it combines new
and save
into a single action.
+
+
+
+
Looking at models
Open up the user.rb and rant.rb model files.
+
class Rant < ActiveRecord::Base
+
+ belongs_to :user
+
+ validates_presence_of :user, :message
+ validates_length_of :message, :maximum => 10
+
+end
+
+class User < ActiveRecord::Base
+
+ has_many :rants
+
+ validates_presence_of :name
+
+end
+
+
+
+
Exercises
Some people are writing in lowercase! This won’t do!
+
Let’s write a method that can convert all lowercase rants to uppercase. First, go find the rant.rb file in your Rails models directory.
+
class Rant < ActiveRecord::Base
+before_save :convert_message_to_uppercase
+def convert_message_to_uppercase
+ ...
+
Rails provides many extensions to Ruby classes to make common tasks easier. Look in http://api.rubyonrails.org/classes/ActiveSupport/Multibyte/Chars.html to find the helper you need for this exercise.
+
Once you’ve added the code, try creating a lowercase rant and see what happens.
+
+
+
+
Exercises
Sometimes you want to work with your data in a different format. Go back to the Rails console and write a program that creates a hash that contains rant messages paired with user names.
+
How do you create a new Hash again?
+
+ - Earlier we saw
Hash.new
, but {}
is a simpler way.
+ - Try
new_hash = {}
+
+
The result should look like:
+
=> {"RANTING!"=>"caylee", "WTF"=>"audrey"}
+
Can you use an iterator to print this out as a series of formatted strings?
+
+
+
+
Callbacks
The before_save
code we wrote earlier uses a callback. Rails defines several callbacks that allow you to change the input to a model before or after actions like save, create, update, and delete.
+
before_save :geocode_address
+after_create :notify_admins
+before_validate :check_over_some_other_details
+
+
+
+
Quality Control
Validations provide an easy wrapper for checking that a particular attribute is present, in the right format, or other requirements before the application will save the data. The microblog application already contains some of these. Look at the Rant and User models to see the ones we’ve included.
+
Can you think of other validations you’d like to add? Try changing the length of a valid Rant message.
+
+
+
+
Displaying the data
Rails will automatically create views with content when you use the scaffold
command. Go to /app/views and have a look around.
+
These files are making use of a templating system called ERB that converts Ruby statements in the view into static HTML to display. A typical ERB statement might look like:
+
Documentation on ERB syntax: http://ruby-doc.org/stdlib/libdoc/erb/rdoc/classes/ERB.html
+
+
+
+
Exercises
The scaffolding system is quick to use, but not always pretty. Our list of rants lists the user_id
for the person who said it, and not their name. Let’s change that.
+
Go to /app/views/rants/ and open the index.html file. By default, the scaffolder makes the index view show a list of all records of the related model’s type. Find the line that displays the user id and change it to show the user’s name instead.
+
+
+
+
Exercises
On the users index page, let’s add a column to the table to show how many rants each person has written. ActiveRecord makes this easy by providing a method called count
that you can add to a collection of records to get the database count instead of the full records. For example, User.count
or Rants.count
.
+
These also work through has_many associations, so given a particular user record, you could try user.kittens.count
or user.books.count
(if those associations existed) or …
+
+
+
+
+
What about the C in MVC?
Now that we’ve seen and edited models and views, let’s take a quick look at the controller files in our project.
+
What does a controller do?
+
+ - It connects the view and the model, calling methods on the models to get the data needed for the view, and allowing you to access input parameters, such as those from a form.
+ - It responds to the HTTP request from the browser, and renders the requested type of output (in our scaffolds, this is html or xml, but other things like json can easily be added—instant API!)
+ - The controller is also where you would add access control, like only allowing the user who wrote the rant to delete it.
+
+
+
+
+
Making Your Own
+ - Exit
microrant
directory
+ - Run
rails new betterrant
+ - Enter the betterrant directory and run:
+
+
bundle install
+rails generate scaffold User name:string
+rails generate scaffold Rant user_id:integer message:string
+rake db:migrate
+
At this point you can start the server and take a look around.
+
+
+
+
Summary
+ - We’ve reviewed the basics of the Ruby language
+
+ - For a refresher, Try Ruby is a browser based overview of Ruby
+ - Ruby has a number of simple types of data such as strings, numbers, arrays, and hashes
+ - Most computations in Ruby are handled by methods.
+ - A class is a template for creating objects that bundles data and methods
+ - Classes can be extended as new needs arise
+
+
+
+
+
+
Summary
+ - We’ve covered a small Rails application: microrants
+
+ - A good next tutorial is Rails For Zombies
+ - More detail can be found in the Ruby Guides
+ - ActiveRecord allows us to manipulate our persistent data like normal objects
+ - .erb files allow us to intergrate Ruby code with html
+ - Scaffolding gives a way of automating the start of a Rails project
+
+
+
+
+
+
Next Steps
+ - Try to make betterrant match or improve upon microrant
+ - Get involved in the local Ruby user group
+ - Start coming to Code-n-Splode
+
+
+
+
+
Stepping outside
+
+
+
+
+
\ No newline at end of file
diff --git a/rubything.pdf.html b/rubything.pdf.html
new file mode 100644
index 0000000..07b3953
--- /dev/null
+++ b/rubything.pdf.html
@@ -0,0 +1,977 @@
+
+
+
+
+
+
+
Learning Ruby and Rails
Good morning!
+
+
+
+
Outline of the Day
+ - 10:00-11:30 Introduction to Ruby
+ - 11:30-Noon Ruby Koans
+ - Noon-1:00 Lunch
+ - 1:00-2:45 Rails
+ - 2:45-3:00 Break
+ - 3:00-4:30 Rails part 2
+ - 4:30 What Next?
+
+
+
+
+
What We’ll Cover
+ - Basic concepts of programming
+ - Syntax and simple operations of Ruby
+
+ - Objects, classes, and methods
+
+ - An introduction to Rails
+
+ - How to use Rails machinery to manipulate application data
+ - Data validation in Rails
+ - Displaying data in Rails
+ - Using scaffolding to start a simple application
+
+
+
+
+
+
What Is Programming?
+ - Programming is the act of writing programs
+ - A program is a precise, text-based, description of how to perform a computation on data
+ - Data
+
+ - Numbers
+ - Strings: pieces of text
+ - Lists of things
+ - Booleans: true and false
+
+ - Computations
+
+ - Add 3 and 4
+ - Find the largest number in a list of numbers
+ - Contact Trimet’s network and find the most convenient bus route
+
+
+
+
+
+
What Is a Programming Language?
+ - A human readable format for describing computations
+ - Any language can describe the same computations
+ - Different languages are really just a different vocabulary
+
+
+
+
+
Why Ruby?
+ - It’s easy to learn
+ - Portland has a strong Ruby community
+ - Rails is a popular web framework built on Ruby
+ - Rails gives Ruby a good, simple, vocabulary for web development
+
+
+
+
+
Starting Ruby
Open a terminal window. Type “irb” and hit enter.
+
Try entering a few things:
+
1
+4 + 3
+"hat"
+[1, 2, 3, "happy"].each { |i| puts i }
+
>>
is your prompt to enter text and =>
will appear at the start of each response.
+
Use the up and down arrow keys to navigate back and forth through the previous commands you’ve entered.
+
+
+
+
Basic Ruby Data
+
+ Numbers |
+ 1 , 2 , 3 |
+
+
+ Strings |
+ "chicken" |
+
+
+ Booleans |
+ true , false |
+
+
+ Symbols |
+ :chicken |
+
+
+ Arrays |
+ [1, 7, "cake"] |
+
+
+ Hashes |
+ {:chicken => "chicken", :sillyexample => :chicken} |
+
+
+
Type these at the command line! What happens?
+
That’s the data, where are the computations?
+
+
+
+
Basic Ruby Computations
+
+ 2 + 2 |
+ |
+
+
+ 3 * 3 |
+ |
+
+
+ "chicken".reverse |
+ |
+
+
+ "chicken".length |
+ |
+
+
+ 2 + 2 == 4 |
+ |
+
+
+ 2 + 2 == 5 |
+ |
+
+
+ [1 , 2, 3].include?(2) |
+ |
+ |
+
+
+
+
+
+
Basic Ruby Computations
+
+ 2 + 2 |
+ 4 |
+
+
+ 3 * 3 |
+ |
+
+
+ "chicken".reverse |
+ |
+
+
+ "chicken".length |
+ |
+
+
+ 2 + 2 == 4 |
+ |
+
+
+ 2 + 2 == 5 |
+ |
+
+
+ [1 , 2, 3].include?(2) |
+ |
+ |
+
+
+
+
+
+
Basic Ruby Computations
+
+ 2 + 2 |
+ 4 |
+
+
+ 3 * 3 |
+ 9 |
+
+
+ "chicken".reverse |
+ |
+
+
+ "chicken".length |
+ |
+
+
+ 2 + 2 == 4 |
+ |
+
+
+ 2 + 2 == 5 |
+ |
+
+
+ [1 , 2, 3].include?(2) |
+ |
+ |
+
+
+
+
+
+
Basic Ruby Computations
+
+ 2 + 2 |
+ 4 |
+
+
+ 3 * 3 |
+ 9 |
+
+
+ "chicken".reverse |
+ "nekcihc" |
+
+
+ "chicken".length |
+ |
+
+
+ 2 + 2 == 4 |
+ |
+
+
+ 2 + 2 == 5 |
+ |
+
+
+ [1 , 2, 3].include?(2) |
+ |
+ |
+
+
+
+
+
+
Basic Ruby Computations
+
+ 2 + 2 |
+ 4 |
+
+
+ 3 * 3 |
+ 9 |
+
+
+ "chicken".reverse |
+ "nekcihc" |
+
+
+ "chicken".length |
+ 7 |
+
+
+ 2 + 2 == 4 |
+ |
+
+
+ 2 + 2 == 5 |
+ |
+
+
+ [1 , 2, 3].include?(2) |
+ |
+ |
+
+
+
+
+
+
Basic Ruby Computations
+
+ 2 + 2 |
+ 4 |
+
+
+ 3 * 3 |
+ 9 |
+
+
+ "chicken".reverse |
+ "nekcihc" |
+
+
+ "chicken".length |
+ 7 |
+
+
+ 2 + 2 == 4 |
+ true |
+
+
+ 2 + 2 == 5 |
+ |
+
+
+ [1, 2, 3].include?(2) |
+ |
+ |
+
+
+
+
+
+
Basic Ruby Computations
+
+ 2 + 2 |
+ 4 |
+
+
+ 3 * 3 |
+ 9 |
+
+
+ "chicken".reverse |
+ "nekcihc" |
+
+
+ "chicken".length |
+ 7 |
+
+
+ 2 + 2 == 4 |
+ true |
+
+
+ 2 + 2 == 5 |
+ false |
+
+
+ [1, 2, 3].include?(2) |
+ |
+ |
+
+
+
+
+
+
Basic Ruby Computations
+
+ 2 + 2 |
+ 4 |
+
+
+ 3 * 3 |
+ 9 |
+
+
+ "chicken".reverse |
+ "nekcihc" |
+
+
+ "chicken".length |
+ 7 |
+
+
+ 2 + 2 == 4 |
+ true |
+
+
+ 2 + 2 == 5 |
+ false |
+
+
+ [1, 2, 3].include?(2) |
+ |
+ true |
+
+
+
What do you think "chicken".reverse.length
does?
+What about "puppy".include?('p')
?
+
+
+
+
That’s Nice But…
+ - How do we combine steps or use the result of an action?
+ - Variables name particular values
+ - The same variable can be set to different values at different times
+
+
var1 = 4
+var1
+var2 = "chicken"
+var2
+var2 = var1
+var2
+
+
+
+
Interlude: String Interpolation
Something you might want to do is print out a string that has a value
+contained within it.
+
For example, try typing
+
"Our array is #{[1,2,3]}"
+
Most useful when dealing with variables!
+
var = [1,2,3,4,5,6]
+"Our array is #{var}"
+
Now back to variables!
+
+
+
+
Variables
Try typing the following, noting what happens at each step
+
thing = "chicken"
+thing
+thing.reverse
+thing
+thing = thing.reverse
+
+
+
+
Variables
+ thing.reverse
didn’t permanently reverse the string!
+ - We had to set the value with another assignment statement
+ - Some functions do change state
+
+
Try
+
awesomelist = [5,2,1,8]
+awesomelist.sort!
+awesomelist
+
How did that happen? Actions that end with a !
change the data!
+This is a Ruby convention, but a good one to follow.
+
+
+
+
Hashes
Let’s consider a book rating system:
+
+ - We use numeric values 0-5 to represent a book that you’ve read
+ - :not_rated will represent a book that you haven’t rated
+ - We’ll store all this data in a hash
+
+
Fill in a hash with some books, e.g.
+
books = { "Left Hand of Darkness" => 5,
+ "The Word for World Is Forest" => 5,
+ "Nevermind the Pollacks" => 0,
+ "Only Revolutions" => :not_rated }
+
+
+
+
Hashes
We can retrieve the rating for a particular book like so
+
books["Left Hand of Darkness"]
+
We can also set values like so
+
books["Only Revolutions"] = 3
+
How can we add a rating for a new book? Any guesses?
+
+
+
+
Hashes
We can retrieve the rating for a particular book like so
+
books["Left Hand of Darkness"]
+
We can also set values like so
+
books["Only Revolutions"] = 3
+
How can we add a rating for a new book? Any guesses?
+
We set the value of the book just like before!
+
books["White Teeth"] = 4
+
Now type books
to see the whole hash.
+
+
+
+
Let’s Try Something
40.reverse
+
What happens?
+
+
+
+
Let’s Try Something
40.reverse
+
What happens?
+
Ruby just reported NoMethodError: undefined method `reverse' for 40:Fixnum
+
That means that reverse
is not something you can do to the number 40.
+
+
+
+
Methods
Most computations in Ruby are performed by methods.
+Type 40.methods
to see which methods are available for basic numbers.
+
Notice +
, -
, et al. in the list?
+
What about "chicken".methods
?
+
+
+
+
Objects
An object is data along with the methods, computations, that
+can operate on it.
+
Everything in Ruby is an object: numbers, strings, hashes, etc.
+
How do you know what kind of object something is? Ask!
+
40.class
+
What is a class?
+
+
+
+
Classes
+ - Classes are templates for making objects
+ - Classes create different types of objects
+
+ - All numbers can be added
+ - All strings can be reversed
+
+ - They define kinds of data and methods on that data
+
+
+
+
+
Classes & Methods
Let’s learn the syntax by example:
+
class Counter
+end
+
What can we do with Counter
?
+
c = Counter.new
+
+
+
+
Classes & Methods
Let’s learn the syntax by example:
+
class Counter
+attr_accessor :value
+
+end
+
Now try
+
c = Counter.new
+c.value
+c.value = 10
+c.value
+
What happened? What does attr_accessor :value
do?
+
+
+
+
Classes & Methods
Let’s learn the syntax by example:
+
class Counter
+ attr_accessor :value
+
+ def initialize
+ @value = 0
+ end
+
+end
+
Now, again, try
+
c = Counter.new
+c.value
+
initialize
gives new
instructions on how to create an object
+
+
+
+
Classes & Methods
Let’s learn the syntax by example:
+
class Counter
+ attr_accessor :value
+
+ def initialize
+ @value = 0
+ end
+
+ def increment
+ @value = @value + 1
+ end
+
+end
+
+
+
+
Classes
Let’s use our Counter class:
+
count = Counter.new
+count.increment
+count.increment
+count.value
+
count.class
+count.methods
+
Try count.respond_to?("increment")
. What did you see?
+
+
+
+
Class Exercise
Let’s add a Counter.increment_by
method that takes an argument for how much to increment.
+
Don’t need to start all over – we open the class instead
+
class Counter
+
+def increment_by(n)
+ # fill in here
+end
+
+end
+
Test your code as follows:
+
c = Counter.new
+c.increment_by(5)
+c.value
+
+
+
+
Classes
You can add methods to existing classes as well:
+
class String
+ def chicken?
+ self == "chicken"
+ end
+end
+
"chicken".chicken?
+"puppy".chicken?
+
self
is a way of referring to the object that the method is being called on.
+
In "puppy".chicken?
, self
is "puppy"
.
+
+
+
+
Classes Exercise
Add a method to String that will test to see if a string is a palindrome.
+
A palindrome is any string that is the same read forwards or backwards.
+
To get you started type the following at the command line:
+
class String
+ def palindrome?
+
and finish the rest! Test it on "abba".palindrome?
and "puppy".palindrome?
+
+
+
+
+
Blocks
Some methods take blocks.
+
list.each {|p| code}
runs code
on every element of list
+
list = [1,2,3,4]
+list.each { |n| puts (n + 1) }
+
+ - A block is a computation, but not a method
+ - A block is of the form
{ |arg1, arg2, ..| code }
+ { |v| v.palindrome? }
+ { |x, y| x * y }
+
+
+
+
+
Blocks
A more complicated example of using each:
+
reviews = Hash.new(0)
+books.values.each { |rate| reviews[rate] = reviews[rate] + 1 }
+reviews
+
reviews
is a count of how many reviews you gave with a particular rating
+
reviews[5]
+
+
+
+
Blocks
There’s another way to write blocks. This is commonly used for multi-line expressions.
+
reviews = Hash.new(0)
+books.values.each do |rate|
+ reviews[rate] = reviews[rate] + 1
+ # more code can go here...
+end
+
+
+
+
Control Structures
Ruby provides control structures for writing more complicated code.
+
If statements are a switch on whether the argument is true or false.
+
if true
+ 1
+else
+ 2
+end
+
if false
+ 1
+else
+ 2
+end
+
+
+
+
Control Structures
Case statements evaluate a statement and execute the code in the corresponding branch:
+
case favorite_color
+when "green"
+ puts "Grass is green"
+when "blue"
+ puts "Skies are blue"
+when "red"
+ puts "Tomatoes are red"
+else
+ puts "Are you sure that's a color?"
+end
+
+
+
+
Control Structures
For-in statements allow iteration over a structure such as a list
+
list = [1,2,3,4,5]
+sum = 0
+for n in list
+ sum = sum + n
+end
+sum
+
+
+
+
+
Intro To Rails
+ - Rails is a framework for building and deploying web applications
+ - It’s structured using an architecture called MVC: Model-View-Controller
+ - MVC separates different kinds of application logic into sections based on what it’s used for. In Rails, these live in separate folders.
+ - Rails values “convention over configuration”—the platform will make certain assumptions about how you’re going to build your application. This makes tasks that suit Rails’ conventions very easy, but actions that don’t follow these conventions will take more work.
+
+
+
+
+
An Example Rails App
To get started:
+
+ - Go to the microrant directory
+ - Type
bundle install
+ - Then type
rails server
+ - Open http://localhost:3000 in your web browser
+
+
+
+
+
Microrant
+ - Twitter? Never heard of it.
+ - 10 characters to express your anger
+ - Basic activities: create, read, update, delete (CRUD)
+ - Go to http://localhost:3000/users/new to create a new user with your name
+ - Then go to /rants/new and create a new rant
+
+ - Rants can also be edited and deleted—give it a try!
+
+
+
+ - What’s behind the curtain?
+
+ - Rails provides classes that allow us to retrieve and manipulate data easily
+ - Logic for the application is written in normal Ruby, and a module called ActiveRecord does all of the database connection work for you
+
+
+
+
+
+
Behind the Scenes: the database
+
+
At The Console
+ - Close down the Rails server with Ctrl-C
+ - Run
rails console
+ - You can modify the data using
Rant
and User
classes
+ - The
User
class has id
and name
attributes
+ - The
Rant
class has id
, message
, and user_id
attributes
+
+
+
+
+
At The Console
How can we look up a rant after it’s been created?
+
r = Rant.find(1)
+r.message
+r.user_id
+
What about modifying a rant?
+
r.message = "RANTING!"
+r.save
+
We need to save
the changes when we’re done editing.
+
+
+
+
Where’s My Stuff
Rails models have built-in finder methods that help you retrieve records from the database.
+
Try:
+
+ Rant.all
+ Rant.first
+ Rant.find(:all, :user_id => 1)
+ Rant.all(:user_id => 1)
+ Rant.find_by_user_id(1)
+
+
+
+
+
At the Console
Let’s try creating a new Rant, for user_id
1
+
user = User.find(1)
+rant = user.rants.build
+rant.message = "WHAT IS TH"
+rant.save
+
Note that we didn’t need to set the id
field of the message!
+That was automatically set by new
.
+
We save
the record when we’re done, in order to create it, just like when we were editing a rant before.
+
+
+
+
At The Console
Creating a rant that way was verbose. Is there a better way?
+
User.find(1).rants.create(:message => "E POINT!?")
+
Notice that you don’t need to save when you use create
—it combines new
and save
into a single action.
+
+
+
+
Looking at models
Open up the user.rb and rant.rb model files.
+
class Rant < ActiveRecord::Base
+
+ belongs_to :user
+
+ validates_presence_of :user, :message
+ validates_length_of :message, :maximum => 10
+
+end
+
+class User < ActiveRecord::Base
+
+ has_many :rants
+
+ validates_presence_of :name
+
+end
+
+
+
+
Exercises
Some people are writing in lowercase! This won’t do!
+
Let’s write a method that can convert all lowercase rants to uppercase. First, go find the rant.rb file in your Rails models directory.
+
class Rant < ActiveRecord::Base
+before_save :convert_message_to_uppercase
+def convert_message_to_uppercase
+ ...
+
Rails provides many extensions to Ruby classes to make common tasks easier. Look in http://api.rubyonrails.org/classes/ActiveSupport/Multibyte/Chars.html to find the helper you need for this exercise.
+
Once you’ve added the code, try creating a lowercase rant and see what happens.
+
+
+
+
Exercises
Sometimes you want to work with your data in a different format. Go back to the Rails console and write a program that creates a hash that contains rant messages paired with user names.
+
How do you create a new Hash again?
+
+ - Earlier we saw
Hash.new
, but {}
is a simpler way.
+ - Try
new_hash = {}
+
+
The result should look like:
+
=> {"RANTING!"=>"caylee", "WTF"=>"audrey"}
+
Can you use an iterator to print this out as a series of formatted strings?
+
+
+
+
Callbacks
The before_save
code we wrote earlier uses a callback. Rails defines several callbacks that allow you to change the input to a model before or after actions like save, create, update, and delete.
+
before_save :geocode_address
+after_create :notify_admins
+before_validate :check_over_some_other_details
+
+
+
+
Quality Control
Validations provide an easy wrapper for checking that a particular attribute is present, in the right format, or other requirements before the application will save the data. The microblog application already contains some of these. Look at the Rant and User models to see the ones we’ve included.
+
Can you think of other validations you’d like to add? Try changing the length of a valid Rant message.
+
+
+
+
Displaying the data
Rails will automatically create views with content when you use the scaffold
command. Go to /app/views and have a look around.
+
These files are making use of a templating system called ERB that converts Ruby statements in the view into static HTML to display. A typical ERB statement might look like:
+
Documentation on ERB syntax: http://ruby-doc.org/stdlib/libdoc/erb/rdoc/classes/ERB.html
+
+
+
+
Exercises
The scaffolding system is quick to use, but not always pretty. Our list of rants lists the user_id
for the person who said it, and not their name. Let’s change that.
+
Go to /app/views/rants/ and open the index.html file. By default, the scaffolder makes the index view show a list of all records of the related model’s type. Find the line that displays the user id and change it to show the user’s name instead.
+
+
+
+
Exercises
On the users index page, let’s add a column to the table to show how many rants each person has written. ActiveRecord makes this easy by providing a method called count
that you can add to a collection of records to get the database count instead of the full records. For example, User.count
or Rants.count
.
+
These also work through has_many associations, so given a particular user record, you could try user.kittens.count
or user.books.count
(if those associations existed) or …
+
+
+
+
+
What about the C in MVC?
Now that we’ve seen and edited models and views, let’s take a quick look at the controller files in our project.
+
What does a controller do?
+
+ - It connects the view and the model, calling methods on the models to get the data needed for the view, and allowing you to access input parameters, such as those from a form.
+ - It responds to the HTTP request from the browser, and renders the requested type of output (in our scaffolds, this is html or xml, but other things like json can easily be added—instant API!)
+ - The controller is also where you would add access control, like only allowing the user who wrote the rant to delete it.
+
+
+
+
+
Making Your Own
+ - Exit
microrant
directory
+ - Run
rails new betterrant
+ - Enter the betterrant directory and run:
+
+
bundle install
+rails generate scaffold User name:string
+rails generate scaffold Rant user_id:integer message:string
+rake db:migrate
+
At this point you can start the server and take a look around.
+
+
+
+
Summary
+ - We’ve reviewed the basics of the Ruby language
+
+ - For a refresher, Try Ruby is a browser based overview of Ruby
+ - Ruby has a number of simple types of data such as strings, numbers, arrays, and hashes
+ - Most computations in Ruby are handled by methods.
+ - A class is a template for creating objects that bundles data and methods
+ - Classes can be extended as new needs arise
+
+
+
+
+
+
Summary
+ - We’ve covered a small Rails application: microrants
+
+ - A good next tutorial is Rails For Zombies
+ - More detail can be found in the Ruby Guides
+ - ActiveRecord allows us to manipulate our persistent data like normal objects
+ - .erb files allow us to intergrate Ruby code with html
+ - Scaffolding gives a way of automating the start of a Rails project
+
+
+
+
+
+
Next Steps
+ - Try to make betterrant match or improve upon microrant
+ - Get involved in the local Ruby user group
+ - Start coming to Code-n-Splode
+
+
+
+
+
Stepping outside
+
+
+
+
+
+
diff --git a/s6/jquery.js b/s6/jquery.js
new file mode 100644
index 0000000..5c99a8d
--- /dev/null
+++ b/s6/jquery.js
@@ -0,0 +1,8176 @@
+/*!
+ * jQuery JavaScript Library v1.5
+ * http://jquery.com/
+ *
+ * Copyright 2011, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2011, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Mon Jan 31 08:31:29 2011 -0500
+ */
+(function( window, undefined ) {
+
+// Use the correct document accordingly with window argument (sandbox)
+var document = window.document;
+var jQuery = (function() {
+
+// Define a local copy of jQuery
+var jQuery = function( selector, context ) {
+ // The jQuery object is actually just the init constructor 'enhanced'
+ return new jQuery.fn.init( selector, context, rootjQuery );
+ },
+
+ // Map over jQuery in case of overwrite
+ _jQuery = window.jQuery,
+
+ // Map over the $ in case of overwrite
+ _$ = window.$,
+
+ // A central reference to the root jQuery(document)
+ rootjQuery,
+
+ // A simple way to check for HTML strings or ID strings
+ // (both of which we optimize for)
+ quickExpr = /^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/,
+
+ // Check if a string has a non-whitespace character in it
+ rnotwhite = /\S/,
+
+ // Used for trimming whitespace
+ trimLeft = /^\s+/,
+ trimRight = /\s+$/,
+
+ // Check for digits
+ rdigit = /\d/,
+
+ // Match a standalone tag
+ rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/,
+
+ // JSON RegExp
+ rvalidchars = /^[\],:{}\s]*$/,
+ rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
+ rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
+ rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,
+
+ // Useragent RegExp
+ rwebkit = /(webkit)[ \/]([\w.]+)/,
+ ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/,
+ rmsie = /(msie) ([\w.]+)/,
+ rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/,
+
+ // Keep a UserAgent string for use with jQuery.browser
+ userAgent = navigator.userAgent,
+
+ // For matching the engine and version of the browser
+ browserMatch,
+
+ // Has the ready events already been bound?
+ readyBound = false,
+
+ // The deferred used on DOM ready
+ readyList,
+
+ // Promise methods
+ promiseMethods = "then done fail isResolved isRejected promise".split( " " ),
+
+ // The ready event handler
+ DOMContentLoaded,
+
+ // Save a reference to some core methods
+ toString = Object.prototype.toString,
+ hasOwn = Object.prototype.hasOwnProperty,
+ push = Array.prototype.push,
+ slice = Array.prototype.slice,
+ trim = String.prototype.trim,
+ indexOf = Array.prototype.indexOf,
+
+ // [[Class]] -> type pairs
+ class2type = {};
+
+jQuery.fn = jQuery.prototype = {
+ constructor: jQuery,
+ init: function( selector, context, rootjQuery ) {
+ var match, elem, ret, doc;
+
+ // Handle $(""), $(null), or $(undefined)
+ if ( !selector ) {
+ return this;
+ }
+
+ // Handle $(DOMElement)
+ if ( selector.nodeType ) {
+ this.context = this[0] = selector;
+ this.length = 1;
+ return this;
+ }
+
+ // The body element only exists once, optimize finding it
+ if ( selector === "body" && !context && document.body ) {
+ this.context = document;
+ this[0] = document.body;
+ this.selector = "body";
+ this.length = 1;
+ return this;
+ }
+
+ // Handle HTML strings
+ if ( typeof selector === "string" ) {
+ // Are we dealing with HTML string or an ID?
+ match = quickExpr.exec( selector );
+
+ // Verify a match, and that no context was specified for #id
+ if ( match && (match[1] || !context) ) {
+
+ // HANDLE: $(html) -> $(array)
+ if ( match[1] ) {
+ context = context instanceof jQuery ? context[0] : context;
+ doc = (context ? context.ownerDocument || context : document);
+
+ // If a single string is passed in and it's a single tag
+ // just do a createElement and skip the rest
+ ret = rsingleTag.exec( selector );
+
+ if ( ret ) {
+ if ( jQuery.isPlainObject( context ) ) {
+ selector = [ document.createElement( ret[1] ) ];
+ jQuery.fn.attr.call( selector, context, true );
+
+ } else {
+ selector = [ doc.createElement( ret[1] ) ];
+ }
+
+ } else {
+ ret = jQuery.buildFragment( [ match[1] ], [ doc ] );
+ selector = (ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment).childNodes;
+ }
+
+ return jQuery.merge( this, selector );
+
+ // HANDLE: $("#id")
+ } else {
+ elem = document.getElementById( match[2] );
+
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ if ( elem && elem.parentNode ) {
+ // Handle the case where IE and Opera return items
+ // by name instead of ID
+ if ( elem.id !== match[2] ) {
+ return rootjQuery.find( selector );
+ }
+
+ // Otherwise, we inject the element directly into the jQuery object
+ this.length = 1;
+ this[0] = elem;
+ }
+
+ this.context = document;
+ this.selector = selector;
+ return this;
+ }
+
+ // HANDLE: $(expr, $(...))
+ } else if ( !context || context.jquery ) {
+ return (context || rootjQuery).find( selector );
+
+ // HANDLE: $(expr, context)
+ // (which is just equivalent to: $(context).find(expr)
+ } else {
+ return this.constructor( context ).find( selector );
+ }
+
+ // HANDLE: $(function)
+ // Shortcut for document ready
+ } else if ( jQuery.isFunction( selector ) ) {
+ return rootjQuery.ready( selector );
+ }
+
+ if (selector.selector !== undefined) {
+ this.selector = selector.selector;
+ this.context = selector.context;
+ }
+
+ return jQuery.makeArray( selector, this );
+ },
+
+ // Start with an empty selector
+ selector: "",
+
+ // The current version of jQuery being used
+ jquery: "1.5",
+
+ // The default length of a jQuery object is 0
+ length: 0,
+
+ // The number of elements contained in the matched element set
+ size: function() {
+ return this.length;
+ },
+
+ toArray: function() {
+ return slice.call( this, 0 );
+ },
+
+ // Get the Nth element in the matched element set OR
+ // Get the whole matched element set as a clean array
+ get: function( num ) {
+ return num == null ?
+
+ // Return a 'clean' array
+ this.toArray() :
+
+ // Return just the object
+ ( num < 0 ? this[ this.length + num ] : this[ num ] );
+ },
+
+ // Take an array of elements and push it onto the stack
+ // (returning the new matched element set)
+ pushStack: function( elems, name, selector ) {
+ // Build a new jQuery matched element set
+ var ret = this.constructor();
+
+ if ( jQuery.isArray( elems ) ) {
+ push.apply( ret, elems );
+
+ } else {
+ jQuery.merge( ret, elems );
+ }
+
+ // Add the old object onto the stack (as a reference)
+ ret.prevObject = this;
+
+ ret.context = this.context;
+
+ if ( name === "find" ) {
+ ret.selector = this.selector + (this.selector ? " " : "") + selector;
+ } else if ( name ) {
+ ret.selector = this.selector + "." + name + "(" + selector + ")";
+ }
+
+ // Return the newly-formed element set
+ return ret;
+ },
+
+ // Execute a callback for every element in the matched set.
+ // (You can seed the arguments with an array of args, but this is
+ // only used internally.)
+ each: function( callback, args ) {
+ return jQuery.each( this, callback, args );
+ },
+
+ ready: function( fn ) {
+ // Attach the listeners
+ jQuery.bindReady();
+
+ // Add the callback
+ readyList.done( fn );
+
+ return this;
+ },
+
+ eq: function( i ) {
+ return i === -1 ?
+ this.slice( i ) :
+ this.slice( i, +i + 1 );
+ },
+
+ first: function() {
+ return this.eq( 0 );
+ },
+
+ last: function() {
+ return this.eq( -1 );
+ },
+
+ slice: function() {
+ return this.pushStack( slice.apply( this, arguments ),
+ "slice", slice.call(arguments).join(",") );
+ },
+
+ map: function( callback ) {
+ return this.pushStack( jQuery.map(this, function( elem, i ) {
+ return callback.call( elem, i, elem );
+ }));
+ },
+
+ end: function() {
+ return this.prevObject || this.constructor(null);
+ },
+
+ // For internal use only.
+ // Behaves like an Array's method, not like a jQuery method.
+ push: push,
+ sort: [].sort,
+ splice: [].splice
+};
+
+// Give the init function the jQuery prototype for later instantiation
+jQuery.fn.init.prototype = jQuery.fn;
+
+jQuery.extend = jQuery.fn.extend = function() {
+ var options, name, src, copy, copyIsArray, clone,
+ target = arguments[0] || {},
+ i = 1,
+ length = arguments.length,
+ deep = false;
+
+ // Handle a deep copy situation
+ if ( typeof target === "boolean" ) {
+ deep = target;
+ target = arguments[1] || {};
+ // skip the boolean and the target
+ i = 2;
+ }
+
+ // Handle case when target is a string or something (possible in deep copy)
+ if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
+ target = {};
+ }
+
+ // extend jQuery itself if only one argument is passed
+ if ( length === i ) {
+ target = this;
+ --i;
+ }
+
+ for ( ; i < length; i++ ) {
+ // Only deal with non-null/undefined values
+ if ( (options = arguments[ i ]) != null ) {
+ // Extend the base object
+ for ( name in options ) {
+ src = target[ name ];
+ copy = options[ name ];
+
+ // Prevent never-ending loop
+ if ( target === copy ) {
+ continue;
+ }
+
+ // Recurse if we're merging plain objects or arrays
+ if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
+ if ( copyIsArray ) {
+ copyIsArray = false;
+ clone = src && jQuery.isArray(src) ? src : [];
+
+ } else {
+ clone = src && jQuery.isPlainObject(src) ? src : {};
+ }
+
+ // Never move original objects, clone them
+ target[ name ] = jQuery.extend( deep, clone, copy );
+
+ // Don't bring in undefined values
+ } else if ( copy !== undefined ) {
+ target[ name ] = copy;
+ }
+ }
+ }
+ }
+
+ // Return the modified object
+ return target;
+};
+
+jQuery.extend({
+ noConflict: function( deep ) {
+ window.$ = _$;
+
+ if ( deep ) {
+ window.jQuery = _jQuery;
+ }
+
+ return jQuery;
+ },
+
+ // Is the DOM ready to be used? Set to true once it occurs.
+ isReady: false,
+
+ // A counter to track how many items to wait for before
+ // the ready event fires. See #6781
+ readyWait: 1,
+
+ // Handle when the DOM is ready
+ ready: function( wait ) {
+ // A third-party is pushing the ready event forwards
+ if ( wait === true ) {
+ jQuery.readyWait--;
+ }
+
+ // Make sure that the DOM is not already loaded
+ if ( !jQuery.readyWait || (wait !== true && !jQuery.isReady) ) {
+ // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+ if ( !document.body ) {
+ return setTimeout( jQuery.ready, 1 );
+ }
+
+ // Remember that the DOM is ready
+ jQuery.isReady = true;
+
+ // If a normal DOM Ready event fired, decrement, and wait if need be
+ if ( wait !== true && --jQuery.readyWait > 0 ) {
+ return;
+ }
+
+ // If there are functions bound, to execute
+ readyList.resolveWith( document, [ jQuery ] );
+
+ // Trigger any bound ready events
+ if ( jQuery.fn.trigger ) {
+ jQuery( document ).trigger( "ready" ).unbind( "ready" );
+ }
+ }
+ },
+
+ bindReady: function() {
+ if ( readyBound ) {
+ return;
+ }
+
+ readyBound = true;
+
+ // Catch cases where $(document).ready() is called after the
+ // browser event has already occurred.
+ if ( document.readyState === "complete" ) {
+ // Handle it asynchronously to allow scripts the opportunity to delay ready
+ return setTimeout( jQuery.ready, 1 );
+ }
+
+ // Mozilla, Opera and webkit nightlies currently support this event
+ if ( document.addEventListener ) {
+ // Use the handy event callback
+ document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+
+ // A fallback to window.onload, that will always work
+ window.addEventListener( "load", jQuery.ready, false );
+
+ // If IE event model is used
+ } else if ( document.attachEvent ) {
+ // ensure firing before onload,
+ // maybe late but safe also for iframes
+ document.attachEvent("onreadystatechange", DOMContentLoaded);
+
+ // A fallback to window.onload, that will always work
+ window.attachEvent( "onload", jQuery.ready );
+
+ // If IE and not a frame
+ // continually check to see if the document is ready
+ var toplevel = false;
+
+ try {
+ toplevel = window.frameElement == null;
+ } catch(e) {}
+
+ if ( document.documentElement.doScroll && toplevel ) {
+ doScrollCheck();
+ }
+ }
+ },
+
+ // See test/unit/core.js for details concerning isFunction.
+ // Since version 1.3, DOM methods and functions like alert
+ // aren't supported. They return false on IE (#2968).
+ isFunction: function( obj ) {
+ return jQuery.type(obj) === "function";
+ },
+
+ isArray: Array.isArray || function( obj ) {
+ return jQuery.type(obj) === "array";
+ },
+
+ // A crude way of determining if an object is a window
+ isWindow: function( obj ) {
+ return obj && typeof obj === "object" && "setInterval" in obj;
+ },
+
+ isNaN: function( obj ) {
+ return obj == null || !rdigit.test( obj ) || isNaN( obj );
+ },
+
+ type: function( obj ) {
+ return obj == null ?
+ String( obj ) :
+ class2type[ toString.call(obj) ] || "object";
+ },
+
+ isPlainObject: function( obj ) {
+ // Must be an Object.
+ // Because of IE, we also have to check the presence of the constructor property.
+ // Make sure that DOM nodes and window objects don't pass through, as well
+ if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
+ return false;
+ }
+
+ // Not own constructor property must be Object
+ if ( obj.constructor &&
+ !hasOwn.call(obj, "constructor") &&
+ !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
+ return false;
+ }
+
+ // Own properties are enumerated firstly, so to speed up,
+ // if last one is own, then all properties are own.
+
+ var key;
+ for ( key in obj ) {}
+
+ return key === undefined || hasOwn.call( obj, key );
+ },
+
+ isEmptyObject: function( obj ) {
+ for ( var name in obj ) {
+ return false;
+ }
+ return true;
+ },
+
+ error: function( msg ) {
+ throw msg;
+ },
+
+ parseJSON: function( data ) {
+ if ( typeof data !== "string" || !data ) {
+ return null;
+ }
+
+ // Make sure leading/trailing whitespace is removed (IE can't handle it)
+ data = jQuery.trim( data );
+
+ // Make sure the incoming data is actual JSON
+ // Logic borrowed from http://json.org/json2.js
+ if ( rvalidchars.test(data.replace(rvalidescape, "@")
+ .replace(rvalidtokens, "]")
+ .replace(rvalidbraces, "")) ) {
+
+ // Try to use the native JSON parser first
+ return window.JSON && window.JSON.parse ?
+ window.JSON.parse( data ) :
+ (new Function("return " + data))();
+
+ } else {
+ jQuery.error( "Invalid JSON: " + data );
+ }
+ },
+
+ // Cross-browser xml parsing
+ // (xml & tmp used internally)
+ parseXML: function( data , xml , tmp ) {
+
+ if ( window.DOMParser ) { // Standard
+ tmp = new DOMParser();
+ xml = tmp.parseFromString( data , "text/xml" );
+ } else { // IE
+ xml = new ActiveXObject( "Microsoft.XMLDOM" );
+ xml.async = "false";
+ xml.loadXML( data );
+ }
+
+ tmp = xml.documentElement;
+
+ if ( ! tmp || ! tmp.nodeName || tmp.nodeName === "parsererror" ) {
+ jQuery.error( "Invalid XML: " + data );
+ }
+
+ return xml;
+ },
+
+ noop: function() {},
+
+ // Evalulates a script in a global context
+ globalEval: function( data ) {
+ if ( data && rnotwhite.test(data) ) {
+ // Inspired by code by Andrea Giammarchi
+ // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html
+ var head = document.getElementsByTagName("head")[0] || document.documentElement,
+ script = document.createElement("script");
+
+ script.type = "text/javascript";
+
+ if ( jQuery.support.scriptEval() ) {
+ script.appendChild( document.createTextNode( data ) );
+ } else {
+ script.text = data;
+ }
+
+ // Use insertBefore instead of appendChild to circumvent an IE6 bug.
+ // This arises when a base node is used (#2709).
+ head.insertBefore( script, head.firstChild );
+ head.removeChild( script );
+ }
+ },
+
+ nodeName: function( elem, name ) {
+ return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
+ },
+
+ // args is for internal usage only
+ each: function( object, callback, args ) {
+ var name, i = 0,
+ length = object.length,
+ isObj = length === undefined || jQuery.isFunction(object);
+
+ if ( args ) {
+ if ( isObj ) {
+ for ( name in object ) {
+ if ( callback.apply( object[ name ], args ) === false ) {
+ break;
+ }
+ }
+ } else {
+ for ( ; i < length; ) {
+ if ( callback.apply( object[ i++ ], args ) === false ) {
+ break;
+ }
+ }
+ }
+
+ // A special, fast, case for the most common use of each
+ } else {
+ if ( isObj ) {
+ for ( name in object ) {
+ if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
+ break;
+ }
+ }
+ } else {
+ for ( var value = object[0];
+ i < length && callback.call( value, i, value ) !== false; value = object[++i] ) {}
+ }
+ }
+
+ return object;
+ },
+
+ // Use native String.trim function wherever possible
+ trim: trim ?
+ function( text ) {
+ return text == null ?
+ "" :
+ trim.call( text );
+ } :
+
+ // Otherwise use our own trimming functionality
+ function( text ) {
+ return text == null ?
+ "" :
+ text.toString().replace( trimLeft, "" ).replace( trimRight, "" );
+ },
+
+ // results is for internal usage only
+ makeArray: function( array, results ) {
+ var ret = results || [];
+
+ if ( array != null ) {
+ // The window, strings (and functions) also have 'length'
+ // The extra typeof function check is to prevent crashes
+ // in Safari 2 (See: #3039)
+ // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930
+ var type = jQuery.type(array);
+
+ if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) {
+ push.call( ret, array );
+ } else {
+ jQuery.merge( ret, array );
+ }
+ }
+
+ return ret;
+ },
+
+ inArray: function( elem, array ) {
+ if ( array.indexOf ) {
+ return array.indexOf( elem );
+ }
+
+ for ( var i = 0, length = array.length; i < length; i++ ) {
+ if ( array[ i ] === elem ) {
+ return i;
+ }
+ }
+
+ return -1;
+ },
+
+ merge: function( first, second ) {
+ var i = first.length,
+ j = 0;
+
+ if ( typeof second.length === "number" ) {
+ for ( var l = second.length; j < l; j++ ) {
+ first[ i++ ] = second[ j ];
+ }
+
+ } else {
+ while ( second[j] !== undefined ) {
+ first[ i++ ] = second[ j++ ];
+ }
+ }
+
+ first.length = i;
+
+ return first;
+ },
+
+ grep: function( elems, callback, inv ) {
+ var ret = [], retVal;
+ inv = !!inv;
+
+ // Go through the array, only saving the items
+ // that pass the validator function
+ for ( var i = 0, length = elems.length; i < length; i++ ) {
+ retVal = !!callback( elems[ i ], i );
+ if ( inv !== retVal ) {
+ ret.push( elems[ i ] );
+ }
+ }
+
+ return ret;
+ },
+
+ // arg is for internal usage only
+ map: function( elems, callback, arg ) {
+ var ret = [], value;
+
+ // Go through the array, translating each of the items to their
+ // new value (or values).
+ for ( var i = 0, length = elems.length; i < length; i++ ) {
+ value = callback( elems[ i ], i, arg );
+
+ if ( value != null ) {
+ ret[ ret.length ] = value;
+ }
+ }
+
+ // Flatten any nested arrays
+ return ret.concat.apply( [], ret );
+ },
+
+ // A global GUID counter for objects
+ guid: 1,
+
+ proxy: function( fn, proxy, thisObject ) {
+ if ( arguments.length === 2 ) {
+ if ( typeof proxy === "string" ) {
+ thisObject = fn;
+ fn = thisObject[ proxy ];
+ proxy = undefined;
+
+ } else if ( proxy && !jQuery.isFunction( proxy ) ) {
+ thisObject = proxy;
+ proxy = undefined;
+ }
+ }
+
+ if ( !proxy && fn ) {
+ proxy = function() {
+ return fn.apply( thisObject || this, arguments );
+ };
+ }
+
+ // Set the guid of unique handler to the same of original handler, so it can be removed
+ if ( fn ) {
+ proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
+ }
+
+ // So proxy can be declared as an argument
+ return proxy;
+ },
+
+ // Mutifunctional method to get and set values to a collection
+ // The value/s can be optionally by executed if its a function
+ access: function( elems, key, value, exec, fn, pass ) {
+ var length = elems.length;
+
+ // Setting many attributes
+ if ( typeof key === "object" ) {
+ for ( var k in key ) {
+ jQuery.access( elems, k, key[k], exec, fn, value );
+ }
+ return elems;
+ }
+
+ // Setting one attribute
+ if ( value !== undefined ) {
+ // Optionally, function values get executed if exec is true
+ exec = !pass && exec && jQuery.isFunction(value);
+
+ for ( var i = 0; i < length; i++ ) {
+ fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
+ }
+
+ return elems;
+ }
+
+ // Getting an attribute
+ return length ? fn( elems[0], key ) : undefined;
+ },
+
+ now: function() {
+ return (new Date()).getTime();
+ },
+
+ // Create a simple deferred (one callbacks list)
+ _Deferred: function() {
+ var // callbacks list
+ callbacks = [],
+ // stored [ context , args ]
+ fired,
+ // to avoid firing when already doing so
+ firing,
+ // flag to know if the deferred has been cancelled
+ cancelled,
+ // the deferred itself
+ deferred = {
+
+ // done( f1, f2, ...)
+ done: function() {
+ if ( !cancelled ) {
+ var args = arguments,
+ i,
+ length,
+ elem,
+ type,
+ _fired;
+ if ( fired ) {
+ _fired = fired;
+ fired = 0;
+ }
+ for ( i = 0, length = args.length; i < length; i++ ) {
+ elem = args[ i ];
+ type = jQuery.type( elem );
+ if ( type === "array" ) {
+ deferred.done.apply( deferred, elem );
+ } else if ( type === "function" ) {
+ callbacks.push( elem );
+ }
+ }
+ if ( _fired ) {
+ deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] );
+ }
+ }
+ return this;
+ },
+
+ // resolve with given context and args
+ resolveWith: function( context, args ) {
+ if ( !cancelled && !fired && !firing ) {
+ firing = 1;
+ try {
+ while( callbacks[ 0 ] ) {
+ callbacks.shift().apply( context, args );
+ }
+ }
+ finally {
+ fired = [ context, args ];
+ firing = 0;
+ }
+ }
+ return this;
+ },
+
+ // resolve with this as context and given arguments
+ resolve: function() {
+ deferred.resolveWith( jQuery.isFunction( this.promise ) ? this.promise() : this, arguments );
+ return this;
+ },
+
+ // Has this deferred been resolved?
+ isResolved: function() {
+ return !!( firing || fired );
+ },
+
+ // Cancel
+ cancel: function() {
+ cancelled = 1;
+ callbacks = [];
+ return this;
+ }
+ };
+
+ return deferred;
+ },
+
+ // Full fledged deferred (two callbacks list)
+ Deferred: function( func ) {
+ var deferred = jQuery._Deferred(),
+ failDeferred = jQuery._Deferred(),
+ promise;
+ // Add errorDeferred methods, then and promise
+ jQuery.extend( deferred, {
+ then: function( doneCallbacks, failCallbacks ) {
+ deferred.done( doneCallbacks ).fail( failCallbacks );
+ return this;
+ },
+ fail: failDeferred.done,
+ rejectWith: failDeferred.resolveWith,
+ reject: failDeferred.resolve,
+ isRejected: failDeferred.isResolved,
+ // Get a promise for this deferred
+ // If obj is provided, the promise aspect is added to the object
+ promise: function( obj , i /* internal */ ) {
+ if ( obj == null ) {
+ if ( promise ) {
+ return promise;
+ }
+ promise = obj = {};
+ }
+ i = promiseMethods.length;
+ while( i-- ) {
+ obj[ promiseMethods[ i ] ] = deferred[ promiseMethods[ i ] ];
+ }
+ return obj;
+ }
+ } );
+ // Make sure only one callback list will be used
+ deferred.then( failDeferred.cancel, deferred.cancel );
+ // Unexpose cancel
+ delete deferred.cancel;
+ // Call given func if any
+ if ( func ) {
+ func.call( deferred, deferred );
+ }
+ return deferred;
+ },
+
+ // Deferred helper
+ when: function( object ) {
+ var args = arguments,
+ length = args.length,
+ deferred = length <= 1 && object && jQuery.isFunction( object.promise ) ?
+ object :
+ jQuery.Deferred(),
+ promise = deferred.promise(),
+ resolveArray;
+
+ if ( length > 1 ) {
+ resolveArray = new Array( length );
+ jQuery.each( args, function( index, element ) {
+ jQuery.when( element ).then( function( value ) {
+ resolveArray[ index ] = arguments.length > 1 ? slice.call( arguments, 0 ) : value;
+ if( ! --length ) {
+ deferred.resolveWith( promise, resolveArray );
+ }
+ }, deferred.reject );
+ } );
+ } else if ( deferred !== object ) {
+ deferred.resolve( object );
+ }
+ return promise;
+ },
+
+ // Use of jQuery.browser is frowned upon.
+ // More details: http://docs.jquery.com/Utilities/jQuery.browser
+ uaMatch: function( ua ) {
+ ua = ua.toLowerCase();
+
+ var match = rwebkit.exec( ua ) ||
+ ropera.exec( ua ) ||
+ rmsie.exec( ua ) ||
+ ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) ||
+ [];
+
+ return { browser: match[1] || "", version: match[2] || "0" };
+ },
+
+ sub: function() {
+ function jQuerySubclass( selector, context ) {
+ return new jQuerySubclass.fn.init( selector, context );
+ }
+ jQuery.extend( true, jQuerySubclass, this );
+ jQuerySubclass.superclass = this;
+ jQuerySubclass.fn = jQuerySubclass.prototype = this();
+ jQuerySubclass.fn.constructor = jQuerySubclass;
+ jQuerySubclass.subclass = this.subclass;
+ jQuerySubclass.fn.init = function init( selector, context ) {
+ if ( context && context instanceof jQuery && !(context instanceof jQuerySubclass) ) {
+ context = jQuerySubclass(context);
+ }
+
+ return jQuery.fn.init.call( this, selector, context, rootjQuerySubclass );
+ };
+ jQuerySubclass.fn.init.prototype = jQuerySubclass.fn;
+ var rootjQuerySubclass = jQuerySubclass(document);
+ return jQuerySubclass;
+ },
+
+ browser: {}
+});
+
+// Create readyList deferred
+readyList = jQuery._Deferred();
+
+// Populate the class2type map
+jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
+ class2type[ "[object " + name + "]" ] = name.toLowerCase();
+});
+
+browserMatch = jQuery.uaMatch( userAgent );
+if ( browserMatch.browser ) {
+ jQuery.browser[ browserMatch.browser ] = true;
+ jQuery.browser.version = browserMatch.version;
+}
+
+// Deprecated, use jQuery.browser.webkit instead
+if ( jQuery.browser.webkit ) {
+ jQuery.browser.safari = true;
+}
+
+if ( indexOf ) {
+ jQuery.inArray = function( elem, array ) {
+ return indexOf.call( array, elem );
+ };
+}
+
+// IE doesn't match non-breaking spaces with \s
+if ( rnotwhite.test( "\xA0" ) ) {
+ trimLeft = /^[\s\xA0]+/;
+ trimRight = /[\s\xA0]+$/;
+}
+
+// All jQuery objects should point back to these
+rootjQuery = jQuery(document);
+
+// Cleanup functions for the document ready method
+if ( document.addEventListener ) {
+ DOMContentLoaded = function() {
+ document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+ jQuery.ready();
+ };
+
+} else if ( document.attachEvent ) {
+ DOMContentLoaded = function() {
+ // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+ if ( document.readyState === "complete" ) {
+ document.detachEvent( "onreadystatechange", DOMContentLoaded );
+ jQuery.ready();
+ }
+ };
+}
+
+// The DOM ready check for Internet Explorer
+function doScrollCheck() {
+ if ( jQuery.isReady ) {
+ return;
+ }
+
+ try {
+ // If IE is used, use the trick by Diego Perini
+ // http://javascript.nwbox.com/IEContentLoaded/
+ document.documentElement.doScroll("left");
+ } catch(e) {
+ setTimeout( doScrollCheck, 1 );
+ return;
+ }
+
+ // and execute any waiting functions
+ jQuery.ready();
+}
+
+// Expose jQuery to the global object
+return (window.jQuery = window.$ = jQuery);
+
+})();
+
+
+(function() {
+
+ jQuery.support = {};
+
+ var div = document.createElement("div");
+
+ div.style.display = "none";
+ div.innerHTML = "