.. rst-class:: book-body ======================================================================= Universal JavaScript ======================================================================= :author: Thava Alagu thavamuni@gmail.com :Version: 0.1 :Date: |date| :status: Draft .. |date| date:: %Y %B %d ----- .. |cright| replace:: Copyright (c) 2012 Thava Alagu. Some Rights Reserved. BSD License. .. |js| replace:: JavaScript .. _js: http://en.wikipedia.org/wiki/JavaScript .. _JavaScript: js_ .. _ECMAScript: http://www.ecma.ch/stand/ECMA-262.htm .. sidebar:: Contents .. contents:: :local: :depth: 3 .. sectnum:: :depth: 3 Prologue ======== This is an introductory book for JavaScript_ Language for C/C++ or Java Programmers. JavaScript_ is ubiquitous. It was born from browsers and is now used everywhere. At it's core, it is a decent programming language and it has it's strengths and weaknesses. Much of it's bad reputation came from inconsistent DOM implementations on browsers. However as a language, it is powerful and one of its kind. As per `Atwood's Law`__ proposed by `Jeff Atwood`__:: Any application that can be written in JavaScript, will eventually be written in JavaScript. __ : http://www.codinghorror.com/blog/2007/07/the-principle-of-least-power.html __ : http://en.wikipedia.org/wiki/Jeff_Atwood JavaScript is now used to write programs in different areas where no one ever had originally imagined while it was invented for browser. History ======== It is important to understand the history of JavaScript: .. rst-class:: minitable =========== ================ ================== ================================ Year Language/Std Author Comments =========== ================ ================== ================================ 1995-1996 LiveScript Brendan Eich at Renamed to JavaScript before Netscape release. ----------- ---------------- ------------------ -------------------------------- 1996 JScript Microsoft JScript -- new name to avoid trademark issues. IE 3.0 ----------- ---------------- ------------------ -------------------------------- 1997 ECMAScript Microsoft and Standardization effort with ECMA-262 Netscape European Computer Manufacturers Associatin. ----------- ---------------- ------------------ -------------------------------- 1999 ECMA-262 V3 Major Revision. IE 4.0+, Netscape 4.5+ ----------- ---------------- ------------------ -------------------------------- 2009 ECMA-262 V5 Major Revision. =========== ================ ================== ================================ Installation ============= You can learn javascript using any browser: * Chrome with built-in developer tools. (Tools=>Developer Tools=>Console). * Firefox with firebug extension It is also easier to learn javascript using standalone commandline shell: * The shell provided by `node.js`__ ; It is built on V8 Javascript engine used by chrome browser. * The Rhino_ javascript engine written in Java .. __: http://nodejs.org/ .. _Rhino: https://developer.mozilla.org/en-US/docs/Rhino/Download_Rhino .. _DOM : http://www.w3.org/DOM/ Document Object Model ===================== The primary target audience of JavaScript is Web programmers, both client side and server side. The related standard of interest is DOM, Document object Model. "Dynamic HTML" is a term to describe the combination of HTML, style sheets and scripts that allows documents to be animated. The W3C standard DOM_ (Document Object Model) standardizes the object model of XML and HTML documents to be exposed to the scripts. The W3C DOM specifications provide bindings only to Java and ECMAScript. It would be useful to skim through the ECMAScript bindings provided by DOM_ standard. Language Overview =================== JavaScript (or ECMAScript) is Object-based language, not class-based language. The basic language and host facilities are provided by objects. There is a notion of object, properties, attributes, built-in type, primitive value, function object and so on. It is important that we familiarize ourselves with the exact meaning of these terms in the context of Javascript. .. rst-class:: minitable +------------------+---------------------------------------------------------------------+ | **Term** | **Definition/Comments** | +==================+=====================================================================+ |object | Unordered collection of properties with zero or more attributes. | | | Example for an attribute is ReadOnly, DontEnum, etc. | | | An object is said to be of type Object. | +------------------+---------------------------------------------------------------------+ |property | Property is used to represent data or function. | | | It could be a primitive value or an object itself. | | | (Note the recursive definition). A property is a property in | | | the context of a 'owning' object. The ownership is not exclusive-- | | | i.e. a single object could be a property of 2 different objects. | +------------------+---------------------------------------------------------------------+ |builtin-types | Following are built-in types: Undefined, Null, Boolean, Number, | | | String and Object. Note that Function is not a separate type-- | | | Function type is a special type of Object type which is callable. | +------------------+---------------------------------------------------------------------+ |primitive-value | Value of the following built-in types: Undefined, Null, Boolean, | | | Number and String. | +------------------+---------------------------------------------------------------------+ |builtin-objects | All built-in types are also built-in objects. In addition, we have | | | Global object, Function, Array, Math, Date, Regexp and error | | | objects Error, EvalError, RangeError, etc. The reason to call | | | these as builtin-objects vs builtin-type is that these are | | | implemented in terms of built-in type Object. | +------------------+---------------------------------------------------------------------+ |Constructor | A constructor is a Function object that creates and initialises | | | objects. Constructor has an associated prototype object that is | | | used to implement inheritance. | +------------------+---------------------------------------------------------------------+ |Prototype | Object used to implement inheritance. constructor.prototype refers | | | to the object to be inherited. | +------------------+---------------------------------------------------------------------+ JavaScript supports prototype based inheritance. Understanding typeof Operator ----------------------------- .. rst-class:: minitable +---------------------------------------------------------+---------------------------+ |**typeof(t)** | **Result** | +=========================================================+===========================+ |Undefined | "Undefined" | +---------------------------------------------------------+---------------------------+ |Boolean | "boolean" | +---------------------------------------------------------+---------------------------+ |Number | "number" | +---------------------------------------------------------+---------------------------+ |String | "string" | +---------------------------------------------------------+---------------------------+ |Object (native and "object" doesn’t implement [[Call]]) | "object" | +---------------------------------------------------------+---------------------------+ |Object (native and "function" implements [[Call]]) | "function" | +---------------------------------------------------------+---------------------------+ |Object (host) | Implementation-dependent | +---------------------------------------------------------+---------------------------+ Language Tutorial ================== Following examples are based on using console window using Chrome browser. (Invoke by selecting following options: Tools => Developer Tools => Console). Arithmetic operations --------------------- :: > 5 + 10 15 > Math.pow(2, 5) 32 > 5/3 1.6666666666666667 > Math.floor(5/3) 1 > 5, 7, 21 # or (5, 7, 21) 21 > a = b = c = 100 100 > b = (c=5, d=10, 20) # Embedded assignment in expression is OK. 20 > 5 < 10 true > 5 && 10 10 // <== It is not a boolean! > !! (5 && 10) true // <== Use this to convert expr to boolean. > 5 || 10 5 > 5 & 1 1 > 2 << 5 64 > typeof 5 'number' > aaa ReferenceError: aaa is not defined > 2 * 'some' NaN > a++ 100 > a 101 > ++a 102 > a += 10 112 > 6 + '5' // Integer promoted to string. Not the other way. '65' > parseInt('123Hello') 123 > parseInt('08') <== Treats as Octal. 0 > parseInt('08', 10) <== Use Radix always 8 > a = 10; b = 30 ; eval (' a * b ') ; 300 String Operations ------------------ :: > 'hello ' + ' world' "hello world" > typeof 'hello' 'string' > a = 'hello world' > a. # Autocomplete works on node.js shell. a.__defineGetter__ a.__defineSetter__ a.__lookupGetter__ a.__lookupSetter__ a.constructor a.hasOwnProperty a.isPrototypeOf a.propertyIsEnumerable a.toLocaleString a.toString a.valueOf a.anchor a.big a.blink a.bold a.charAt a.charCodeAt a.concat a.constructor a.fixed a.fontcolor a.fontsize a.indexOf a.italics a.lastIndexOf a.length a.link a.localeCompare a.match a.replace a.search a.slice a.small a.split a.strike a.sub a.substr a.substring a.sup a.toLocaleLowerCase a.toLocaleUpperCase a.toLowerCase a.toString a.toUpperCase a.trim a.trimLeft a.trimRight a.valueOf > a.substring(0,5) 'hello' > a.substring(5,11) # a.slice() also does samething with minor differences. ' world' > a.substr(5,6) ' world' > a.length 11 > typeof a 'string' > b = String(123) # Convert number to primitive string '123' > typeof b 'string' > b = String('hello world') # without new operator, it is primitive type. > a == b true > c = new String('hello world') # It is now 'object' not 'string' ! > typeof c "object" > a == c # Primitive object promoted to String to compare true! true Array Operations ----------------- Array is a built-in object of built-in Object type. It can also be used like a type since it defines a constructor. :: > a = [1, 2, 3] > b = [1, 2, 3] > a == b false # They are different Objects. > a.push(4) [1, 2, 3, 4] > a.unshift(10) # Push at front [10, 1, 2, 3, 4] > a.shift() # Pop from font 10 > a.concat(b) # Creates new list a + b [1, 2, 3, 1, 2, 3] > a # a remains unchanged. [1, 2, 3] // Note: There is no built-in a.extend(b) method that will mutate a. > a + b # converts to String and joins. '1,2,31,2,3' # To join, use concat > a = [1, 2, 3, 4, 5, 6] > a.slice(2,4) # similar to python a[2:4] [3, 4] # a[2], a[3] >a. a.__defineGetter__ a.__defineSetter__ a.__lookupGetter__ a.__lookupSetter__ a.constructor a.hasOwnProperty a.isPrototypeOf a.propertyIsEnumerable a.toLocaleString a.toString a.valueOf a.concat a.constructor a.every a.filter a.forEach a.indexOf a.join a.lastIndexOf a.length a.map a.pop a.push a.reduce a.reduceRight a.reverse a.shift a.slice a.some a.sort a.splice a.toLocaleString a.toString a.unshift > a = [10, 20, 30] [ 10, 20, 30 ] > delete a[1] # delete on array element creates hole using 'undefined' true > a [ 10, undefined x 1 , 30 ] > a[1] undefined > a.length # length remains unchanged. 3 > a.splice(0, 2) # Really delete 2 elements starting from index 0. [ 10, undefined x 1 ] > a [ 30 ] > a = [10, 20, 30] > for (i in a) console.log(i) # This prints the keys in array. 0 1 2 > a.some = 200 # Don't mix non-numeric index into array! > a['abc'] = 300 # This is same as a.abc = 300 > for (i in a) console.log(i) 0 1 2 some abc > for (i in a) console.log(typeof(i)) string string string string string # All indexes stored as string internally. > c = new Array(10) # Array is an object as well as a type! [undefined × 10] # Array.constructor defines the type constructor. > a = [1, 2, 3]; typeof a "object" > a instanceof Array true > a.constructor [Function: Array] Object Operations ------------------ Object is the only non-primitive built-in type. :: > a = { 'one' : 1, 'two': 2 } # Creating object using literal > typeof a "object" > for (i in a) console.log(i) one two > a = new Object() Object # Chrome displays a clickable, explorable empty Object icon. {} # node.js shell displays the empty object. > a.v1 = 200 # Perfect for using as associative array. > a['v2'] = 300 # It is same as a.v2 = 300 > for (i in a) console.log(i) v1 v2 > F = function() { this.var1 = 100; this.var2 = 200 } > fobj = new F() # New object created which becomes 'this' object. { var1: 100, var2: 200 } > a. # This autocomplete operation works only on node.js shell. a.__defineGetter__ a.__defineSetter__ a.__lookupGetter__ a.__lookupSetter__ a.constructor a.hasOwnProperty a.isPrototypeOf a.propertyIsEnumerable a.toLocaleString a.toString a.valueOf a.v1 a.v2 Note: Object does not contain any enumerable properties, hence suitable for implementing hash/associative array with string keys. Prototype Based Inheritance ---------------------------- Javascript supports object based inheritance instead of classes. It makes use of ``prototype`` property to trace the parent object hierarchy:: > function object(o) { function F() {} F.prototype = o; return new F(); } > obj = new Object() > childobj = object(obj) # childobj is based on parent object obj. Misc operations ----------------- :: > typeof true "boolean" > a = [1, 2, 3] > JSON.stringify(a) # works for nested object as well. '[1,2,3]' > di { a: 10, b: 20 } > JSON.stringify(di) '{"a":10,"b":20}' > print(JSON.stringify(di, null, 4)) # Nicely formatted output { "a": 10, "b": 20 } > function f() { console.log('hello world') } undefined > f.toString() 'function f() { console.log(\'hello world\') }' > n = null > n && n.value # Use this pattern to avoid Null reference. null > null == undefined true > null === undefined false Input/Output ------------- Examples:: // alert(), confirm(), prompt() work in browsers. > alert('hello!!!') > r = confirm('Press Ok or Cancel') > r true // or false > console.log('You pressed ' + (r && 'OK' || 'Cancel')) > val = prompt('Enter Value ', '0') > val > typeof(val) 'string' // Following examples work with node.js > u=require('util'); u.puts('hello!') // Prints: 'hello!' > process.stdout.write('hello!') // Prints: 'hello!' // readline will be useful in node.js script, not at the command line: var readline = require('readline'); var rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: false }); rl.on('line', function (cmd) { console.log('You just typed: '+cmd); }); Common Libraries ================= Summary -------- * JQuery - Approx 9K lines. Browser independent way of accessing components and manipulating them. * JQuery-ui - Approx 15K lines. UI related stuff on top of JQuery. * prototype.js - Approx 7K lines. General useful functions. e.g. each(), clone(), etc. * Backbone.js - Approx 1500 lines. Small generic util collection to be used on any environment (browser or not). Includes Events, History, etc handling helper functions. Provides basic MVC framework. Requires underscore.js. Can optionally use jquery for view. The model closely resembles Ruby on Rails. * underscore.js - Approx 1200 lines. Provides functional programming support without extending any of the built-in JavaScript objects. Used along with Backbone or JQuery. jquery offers only partial support for functional programming. * modernizr.js - Approx 1500 lines. Small library to easily manipulate CSS. It is HTML5 and CSS3 aware. Good for progressive enhancement. Following are complete JavaScript framework libraries, more heavyweight: * YUI - Yahoo User Interface Library * dojo toolkit * mootools - A compact framework with 6000 lines of Javascript code. Until drupal 7 (PHP based web framework), only jQuery is included in core. Drupal 8 includes backbone.js and underscore.js in it's core. See Also: * http://stackoverflow.com/questions/394601/which-javascript-framework-jquery-vs-dojo-vs Node.js Platform -------------------- Node.js is a platform to develop standalone JavaScript based applications. Node.js uses an event-driven, non-blocking I/O model. You can import different modules like below:: var net = require('net'); var server = net.createServer(function(c) { // connection listener ... }) var os = require('os') console.log(os.hostname()) $ node -e 'var sys = require("sys");   sys.puts("hi world!"); $ node > util = require('util') # sys module renamed to util in some versions. > util.puts('hello') > print = util.puts > print('hello') > mymod = require('/abs/path/to/mymod.js') ; > u=require('util') { format: [Function], print: [Function], puts: [Function], debug: [Function], error: [Function], inspect: [Function: inspect], isArray: [Function: isArray], isRegExp: [Function: isRegExp], isDate: [Function: isDate], isError: [Function: isError], p: [Function], log: [Function], exec: [Function], pump: [Function], inherits: [Function], _deprecationWarning: [Function] } The other modules include: Cluster, Crypto, File System (fs), HTTP, Net, OS, Readline, etc. Built-in Types ============== There are only few built-in types. They are: * Undefined, Null, Boolean, Number, String and Object. Note that Function is not a separate type; Function type is a specialization of Object type-- which is callable. Built-in Objects ================= Operators =========== Statements =========== Overview -------- Statements can be of following kinds:: Block VariableStatement EmptyStatement ExpressionStatement IfStatement IterationStatement ContinueStatement BreakStatement ReturnStatement WithStatement LabelledStatement SwitchStatement ThrowStatement TryStatement Block Statement ---------------- Examples:: { x++; y++; } // Note: There is no block level scope for variables. var x = 1; { var x = 2; } ; alert(x); // outputs 2 Variable Statement ------------------- :: function f() { var i = 10; } // The i is local, not global variable. function g() { i = 10; } // The i is global. Expression Statement --------------------- :: a = 10; // (a = 10) evaluates to 10. Expression Statement. If Statement ------------- :: if (x == y)    f(x) else if (x > z)    g(x) else    h(x) Iteration Statement -------------------- Examples:: do x = f(x); while ( x < 1000 ); while ( notdone ) { f(x); g(x); } for (a = b+c; a < 10 ; a++ ) f(a); for ( var i=0; i < a.length ; i++ ) f(a[i]); for ( i in a ) f(a[i]); for ( var i in (c=a.concat(b)) ) f(c[i]); Continue and break Statements ------------------------------- :: outerloop: while (!finished) { f(x); innerloop: while (!completed) { g(x); if (condition_1) continue outerloop; if (condition_2) break outerloop; if (condition_3) continue; if (condition_4) break; h(x); } } With Statement (deprecated) ---------------------------- :: with (obj) { a = 10; // set obj.a = 10 if obj.a exists ; else set global a=10 b = 20; // set obj.b = 20 iff obj.b exists; } with (document.getElementById("blah").style) { background = "black"; color = "blue"; border = "1px solid green"; } // Because of the confusing semantics it is deprecated. Switch Statement ----------------- :: // c-style switch but case expression need not be constant! x = 'hello'; y = 'hell' ; z = 'o'; switch(x) // Allows strings { case 1 : console.log('it is 1'); break; case y+z : console.log('it is y+z'); break; default : console.log('it is default'); break; } // Prints: it is y+z Try Catch Finally ------------------ You can control the error handling by catching errors that are generated. These are the primary error types: * EvalError: Raised when the eval() functions is used in an incorrect manner. * RangeError: Raised when a numeric variable exceeds its allowed range. * ReferenceError: Raised when an invalid reference is used. * SyntaxError: Raised when a syntax error occurs while parsing JavaScript code. * TypeError: Raised when the type of a variable is not as expected. * URIError: Raised when the encodeURI() or decodeURI() functions are used in an incorrect manner. * Error: User defined errors can be thrown using this. e.g: throw new Error('Timeout!'); When user defined errors are thrown, it is typical to throw Error() object, but you can also simply throw anything like just plain string:: throw new Error('Timeout occurred!'); throw 'Timeout!'; Example:: function f() { try { val = prompt('Enter Option: 1:Throw; 2:return early; 3:Continue;'); if (val == '1') throw 'Option1-Error-thrown'; // finally will execute. else if (val == '2') return 'early'; // finally will execute before return. else if (val > 3) throw new RangeError('Invalid Option'); } catch (err){ // You can also do... switch (err.name) ... case 'RangeError': ...; // if (typeof err == 'string') then err.name == undefined; console.log('Caught error name: ' + err.name); console.log('Description:' + err); } finally{ console.log('Executing finally now...'); } return 'last'; } Functions ========== A function can be declared like below:: function f(x) { console.log('I am function f. Arg x is ' + x); } Above declaration is equivalent to following expression:: f = function(x) { console.log('I am function f. Arg x is ' + x); } Example:: function g(x) { if (x > 10) return; // return value is undefined if (x == 10) return x; y = 100; // y becomes a global variable. var z = 200; // z is local variable. { var z = 300; } // Blocks don't have new scope. console.log('z is : ' + z); // Prints: z is: 200 // You can call this function as: // g(); // x == undefined // g(10, 20, 30); // x == 10; Extra args ignored! console.log(arguments); // It is array of all arguments passed. } // You can use anonymous functions in order to use many temporary variables... (function(){ var tmp1 = 1; var tmp2 = 1; global_var1 = 100; .... )(); // Above function call creates only one global_var1; // The other local variables do not corrupt global namespace. Error Handling =============== See `Try Catch Finally`_ Object Oriented Programming ============================ Following example illustrates how to create and use an object in an object oriented style of programming:: function Car() { this.wheels = 4 ; this.color = 'red' ; } c = Car(); <== c becomes undefined c = new Car(); <== Get a nice object {wheels:4, color:'red'} function Car() { this.wheels = 4 ; this.color = 'red' ; return this; } c = Car(); <== c gets global object this !!! ; No warnings !!! c = new Car(); { wheels: 4, color: 'red' } Car.model = 'benz' c = new Car(); <== Does not inherit new properties. { wheels: 4, color: 'red' } c.model undefined Car.prototype.model = 'benz' <== Every Function has "prototype" predefined to {} c = new Car(); { wheels: 4, color: 'red' } <== By Default does not show parent obj props c.model 'benz' c.constructor { [Function: Car] } c.constructor.constructor [Function: Function] <== Leaf constructor/object c.constructor.constructor.constructor == c.constructor.constructor true c.constructor.constructor.prototype [Function: Empty] <== Equivalent to {} c.constructor.prototype // Same as Car.prototype { 'benz' } c.constructor.prototype.constructor == c.constructor true <== True for any object. Note: While dumping "prototype" object you may want to skip printing the properties twice, by checking .e.g. if (obj.constructor.prototype == obj) obj = obj.constructor. Following points worth noting: * Only Single inheritance style is supported. * The inheritance is prototype based, not class based. * Sharing of parent objects is done with Copy-on-write mechanism:: NewObject ---> ParentObject [ properties created in NewObject as CopyOnWrite ] * Deleting object elements only deletes at 'top-level' -- if it exists in prototype it may be visible for read even after deletion:: > c = new Car() > c.model 'benz' > c.model = 'fusion' > delete(c.model) > c.model 'benz' > delete(c.model) > c.model 'benz' # property in prototype is always available for reading. * Free objects are garbage collected * Object.prototype (is the last in the linkage hierarchy). It provides very few useful methods that every Object must inherit-- this includes toString(). By default, there is no clone() or equals() method implementation available. * Object itself provides some useful methods such as keys(), hasOwnPropertyNames(), create(), etc. These are to be used more like global functions. These methods are not inherited by the objects. * Ready made objects can be easily reorganized for inheritance. Object customization is lot easier than doing the same thing with classes. Closure ======== Design Patterns ================= Window Event Programming ========================= Example Events -------------- onmouseover, onmouseout AJAX Requests ============== An example usage:: function handler() { if (oReq.readyState == 4 /* complete */) { if (oReq.status == 200) { alert(oReq.responseText); } } } var oReq = new XMLHttpRequest(); if (oReq != null) { oReq.open("GET", "http://localhost/test.xml", true); oReq.onreadystatechange = handler; oReq.send(); } else { window.alert("AJAX (XMLHTTP) not supported."); } prototype.js defines following function:: var Ajax = { getTransport: function() { return Try.these( function() {return new XMLHttpRequest()}, function() {return new ActiveXObject('Msxml2.XMLHTTP')}, function() {return new ActiveXObject('Microsoft.XMLHTTP')} ) || false; }, activeRequestCount: 0 }; jquery.js sets the global XMLHttpRequest variable if needed using following code:: // If IE is used, create a wrapper for the XMLHttpRequest object if ( jQuery.browser.msie && typeof XMLHttpRequest == "undefined" ) XMLHttpRequest = function(){ return new ActiveXObject( navigator.userAgent.indexOf("MSIE 5") >= 0 ? "Microsoft.XMLHTTP" : "Msxml2.XMLHTTP" ); }; An example usage from w3schools::

Let AJAX change this text

Style Guidelines ================== Better to use CSS for presentation, HTML for structure and Javascript for interactivity. However, there are cases for which it makes sense to manipulate CSS and html from JavaScript-- this should be kept in minimum. For accessibility reasons, you may want to define CSS class for hiding like below:: .hide { position: absolute; top: -9999px; left: -9999px; } .remove { display: none; } JQuery Basics =============== * $(document).ready(fn); * Selectors: - CSS Style: $('#mycontainer'), $('a'), $('li:first-child'), etc. - X-Path: $('a[title]'), $('div[ul]'), etc. - Custom: $('li:eq(1)') * Some Useful Methods -------------------- * DOM Traversal – .parent(), .siblings(), .next() • * Manipulation – .html(), .empty(), .append(content) • * Events – .ready(fn), .hover(fn1, fn2), .click(fn) * Effects – .slideToggle(), .show(), .hide() Chaining ---------------- :: $(‘#someElement’) .parent().parent() .find(‘div.green’) .hide().end() .siblings().find(‘div.blue’) .show().end() .parent().next() .addClass(‘redBorder’); Show/Hide Example: $(document).ready(function() { $('a.showhide').click(function() { $(this).parent().parent() .find('div.view-data-body') .slideToggle(); return false; }); }); jQuery in Drupal -------------------- :: drupal_add_js($data, $type) – Add a JavaScript file, setting or inline code to the page, for example: drupal_add_js(drupal_get_path(‘module’, ‘mymodule’) .'/myjs.js'); • drupal_add_js(array(‘myjs’=>$mysettings), ‘setting’); Drupal_add_js(‘var myVar = “foo”;’, ‘inline’); Where do I put my code? ----------------------- :: – themers: put your .js file in your theme directory and call drupal_add_js(drupal_get_path(‘theme’, ‘mytheme’) . ‘myjs.js’) from a tpl file – module developers: put your .js file in your module directory and call drupal_add_js() before you output content Ajaxifying Drupal with jQuery ------------------------------- :: Basic essentials: – jQuery’s .ajax() or .get() method - drupal/path – callback function • drupal_to_js($var) – Converts a PHP variable into its JavaScript equivalent. Quick Tabs ---------- Create blocks of tabbed views! http://drupal.org/project/quicktabs .. Additional Various Operations ------------------------------- Tips ========= var_dump() equivalent in JavaScript ------------------------------------ * Use console.log(obj) if it is console. * obj.toString() ==> works well if obj is array; but for associative array yields just [Object ] * http://phpjs.org/functions/sprintf * Use following function. Be warned that it is too noisy:: function dump(obj) { var out = ''; for (var i in obj) { out += i + ": " + obj[i] + "\n"; } // alert(out); or ... console.log(out); // or, if you wanted to avoid alerts... var pre = document.createElement('pre'); pre.innerHTML = out; document.body.appendChild(pre) } * JSON.stringify(obj) (you may get recursive structure error) Objects Vs Arrays vs Associative Arrays --------------------------------------------- Enumerate all hidden properties/methods ------------------------------------------ for (var i in obj) console.log(i) // This excludes built-in properties. Possible solutions are: * console.dir(), console.log() // Chrome lets you explore Object by clicking. * Object.getOwnPropertyNames(obj); // This lists all properties enumerable or not. Object.getOwnPropertyNames(obj.__proto__); Object.getOwnPropertyNames(obj.__proto__.__proto__); // till __proto__ becomes null Note: getOwnPropertyNames() is part of ECMA v5 specification. Interesting API calls ------------------------ Object.getOwnPropertyNames(Math) // ECMA V5 Mixins ------------ Getting OuterHTML ------------------- See http://stackoverflow.com/questions/2419749/get-selected-elements-outer-html * Simplest method:: console.log(document.getElementsByTagName("head")[0].innerHTML) // innerHTML $('.classSelector').html(); // innerHTML using JQuery // Gives you the outside wrapper as well $('.classSelector')[0].outerHTML // Most browsers support this DOM property. * Using jQuery + clone:: (function($) { $.fn.outerHTML = function() { return $(this).clone().wrap('
').parent().html(); } })(jQuery); And use it like this: $("#myTableRow").outerHTML(); * Using jQuery without clone:: $.fn.outerHTML = function() { $t = $(this); if ('outerHTML' in $t[0]) { return $t[0].outerHTML; } else { var content = $t.wrap('
').parent().html(); $t.unwrap(); return content; } } And use it like this: $("#myID").outerHTML(); * Extend jQuery properly:: (function($) { if (!$.outerHTML) { $.extend({ outerHTML: function(ele) { var $return = undefined; if (ele.length === 1) { $return = ele[0].outerHTML; } else if (ele.length > 1) { $return = {}; ele.each(function(i) { $return[i] = $(this)[0].outerHTML; }) }; return $return; } }); $.fn.extend({ outerHTML: function() { return $.outerHTML($(this)); } }); } })(jQuery); $.outerHTML($("#eleID")); // will return outerHTML of that element and is // same as $("#eleID").outerHTML(); For multiple elements $("#firstEle, .someElesByClassname, tag").outerHTML(); Toggle Visibility Vs Display ----------------------------- Using javascript you can choose to hide an element by clicking on it. You can either completely remove it from display or just toggle visibility. :: function toggleVisibility() {   document.getElementById("toggleMe").style.display = "";   if(document.getElementById("toggleMe").style.visibility == "hidden" ) {     document.getElementById("toggleMe").style.visibility = "visible";   }   else {   document.getElementById("toggleMe").style.visibility = "hidden";   } } function toggleDisplay() {   document.getElementById("toggleMe").style.visibility = "visible";   if(document.getElementById("toggleMe").style.display == "none" ) {     document.getElementById("toggleMe").style.display = "";   }   else {     document.getElementById("toggleMe").style.display = "none";   } }

Click to toggle display. | Click to toggle visibility.

Treatment of this keyword ----------------------------- The `this` keyword is a magic keyword which refers to current object. In browser this is global window by default. The only way to implicitly set the current object is by calling as below:: > f = function() { console.log(this) } > f // Prints global object window > obj = { 'one' : 1, 'two' : 2 } > obj.p = f > obj.p() // Prints obj object Display definition of the function using toString() ------------------------------------------------------- :: > util.puts(util.puts.toString()) # To see definition of function!!! function () { for (var i = 0, len = arguments.length; i < len; ++i) { process.stdout.write(arguments[i] + '\n'); } } Glossary (In progress, Draft, needs cleanup ) =============================================== :V8 Javascript Engine: V8 JavaScript Engine is an open source JavaScript engine developed by Google. It ships with the Google Chrome web browser. :SpiderMonkey: First JavaScript Engine developed at Netscape. :PhoneGap: PhoneGap is a mobile development framework to build applications for mobile devices using JavaScript, HTML5 and CSS3, :Ringojs: A JavaScript runtime and shell based on Rhino providing a CommonJS conformant module library and web application framework. References ============= * http://stackoverflow.com/questions/2353818/how-do-i-get-started-with-node-js * http://eloquentjavascript.net/ : Good Text book * http://hyperpolyglot.org/embeddable : Scripting comparison * http://jster.net : Public javascript library catalog * http://jster.net/library/buckets : Library with generic Data structures * http://www.gotapi.com/html : General API Pointers For all web * https://github.com/dannycoates/node-inspector : node.js server debugger * http://www.slideshare.net/stoyan/javascript-is-everywhere * John Resig's Blog http://ejohn.org/ (JQuery author) * http://www.w3.org/DOM/ : Document Object Model * http://wtfjs.com/ Additional References ----------------------- * https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide * Core JavaScript Reference https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference * comp.lang.javascript FAQ http://jibbering.com/faq/ * Nicholas C. Zakas' Blog http://www.nczonline.net/ Standards ---------- * Specification: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf .. comments hidden > Boolean(null) # 10, -1, '0' => true; null, '', 0 => false; false > a = [1, 2, 3] > a instanceof Array # Warning: May not work if a is from different frame ??? true > a.constructor [Function: Array] > e = new Object() {} > e.constructor [Function: Object] > Object.toString() 'function Object() { [native code] }' > s1 = 'hello' > s1 instanceof String false > typeof s1 'string' > s2 = new String('hello') <=== instanceof has strange behaviour wrt built-in types. > s2 instanceof String true > typeof s2 object > s1 == s2 <== Good! true > n1 = 100 > typeof n1 'number' > n1 instanceof Number false > n1 = new Number(100) <== Does not return real number !!! {} > Object. Object.__defineGetter__ Object.__defineSetter__ Object.__lookupGetter__ Object.__lookupSetter__ Object.constructor Object.hasOwnProperty Object.isPrototypeOf Object.propertyIsEnumerable Object.toLocaleString Object.toString Object.valueOf Object.apply Object.arguments Object.bind Object.call Object.caller Object.constructor Object.length Object.name Object.toString Object.arguments Object.caller Object.create Object.defineProperties Object.defineProperty Object.freeze Object.getOwnPropertyDescriptor Object.getOwnPropertyNames Object.getPrototypeOf Object.isExtensible Object.isFrozen Object.isSealed Object.keys Object.length Object.name Object.preventExtensions Object.prototype Object.seal $ node debug script.js # command line debug $ node --debug script.js # Remote debug with eclipse Highlights ~ - Module is modeled as JSON object - A JSON object could act as a closure. JSON object method can refer to function local variable which is part of closure. - Prototype based vs class-inheritance based. - Very few built-in types: String, Object, # Array is a special type JSON object where keys are 0,1,2,... Number (64-bit float. i.e. double) - Default Arithmetic. Boolean Function Date Regexp Array - Special Values: null, undefined - No class. Only functions. No explicit access control mechanism. - Functions are first class. - Strings: Sequence of 16-bit chars UCS-2 not UTF-16 - Immutable - str1 == str2 True for string equality. - Loosely typed -- Not untyped. - Lots of reserved words (for future), but they are not really used!!! - // or C Style comments. - Number('42') same-as +"42" : + unary operator converts - Strange Logical Opeartors: 10 || 20 ==> 10 ; A || B == A? A : B 10 && 20 ==> 20 ; A && B == A? B : A !10 ==> false !!10 ==> true (to convert to boolean) A && A.member ==> To avoid null reference. - Statements: - Block statements: { x++; y++; } Note: There is no block scope. var x = 1; { var x = 2; } ; alert(x); // outputs 2 - if (x == y)   statement_1 [else if (x = y) <=== C-Like embedded expression OK.   statement_2] ... [else   statement_n] expression evaluating to false: false undefined null 0 NaN the empty string ("") - labeled break: e.g. loop: .... ; for(..) { ... break loop; ... } - C-style for loop - for (name in obj) { ... } - for (name in obj) // For each key in object ... { if (obj.hasOwnProperty(name)) { // To filter out inherited properties ... } } - var sum = 0; var obj = {prop1: 5, prop2: 13, prop3: 8}; for (var item in obj) { // <== for each statement!   sum += item; } print(sum); // Prints 26 - while (x < 5) { ... } - do   statement while (condition); - liberal c-style switch: switch(expr) // Allows strings { case expr1: ... // need not be constant. default: ... } - Throw new Error; Exception should have 3 keys: name type msg??? try { ... } catch(e) { ... } // Only one catch. Some built-in exceptions. - With Statement -- not recommended. - var name; <== define variables inside function. Set to 'undefined' by default. By default, name is global variable. - blocks don't have scope. Only functions do. - return expr; return; <== returns undefined. - Object literal: a = { one : 1, 'two': 2 } Can use to throw exception: throw new Error('Fire!'); throw { name: ... , msg: ..., .. } - Object augmentation obj.newelement = 1; obj['newelement'] = 1; - Creation of object: (secret link used for prototypal inheritance) - by object literal - obj = new somefunction(); // New object created and becomes 'this' - obj = object(anotherobj) // where object() is defined as follows: function object(o) { function F() {} F.prototype = o; return new F(); } - forEach function: array.forEach(callback[, thisArg]) function logArrayElements(element, index, array) {      console.log("a[" + index + "] = " + element); } [2, 5, 9].forEach(logArrayElements); // logs: // a[0] = 2 // a[1] = 5 // a[2] = 9 Supplying forEach function for javascripts older than 1.6 version: if ( !Array.prototype.forEach ) {   Array.prototype.forEach = function(fn, scope) {     for(var i = 0, len = this.length; i < len; ++i) {       fn.call(scope, this[i], i, this);     }   } } - delete arr[2] ; leaves hole in the array index. > arr = ['a', 'b', 'c', 'd' ] > delete arr[1] [ 'a', undefined, 'b', 'c', 'd' ] > arr.splice(1, 1); <== Use splice() to really delete!!! [ 'a', 'c', 'd' ] - Innerfunctions has access to variables/parameters of containing function. It is static scoping (or lexical scoping) : == Useful for most applications. == [Note: Compare this with dynamic scoping where caller supplies all context. In such a case, function just becomes a 'string' to be eval'ed ] - Closure: The scope that an inner-function enjoys even after the parent function terminates. This is called closure. - Javascript closure is a powerful mechanism which is leveraged to compensate for many other shortcomings. - f(1, 2, 3, extra_args) ==> extra_args ignored. - f(1,2) with missing_args ==> missing_args become 'undefined', 'undefined', ... No args checking. - Calling functions: f(1, 2, 3); ==> You have 'this' available which represents global object. To get access to 'this' from helper functions, do the following: var that = this; // Now inner function can access 'that' obj.f(1, 2, 3) ==> The 'this' refers to obj. new f(1, 2, 3) ==> New object is created which becomes 'this' within function f !!! If no explicit return value, 'this' is automatically returned. - Globals are the properties of global object. window in browsers. == null vs undefined == The DOM methods getElementById, parentNode etc return null (defined but having no value) The 'undefined' is returned if the property itself is not defined. null is of type object and undefined is of type undefined. null is valid in JSON but undefined is not: JSON.parse(undefined); //syntax error JSON.parse(null); //null > null == undefined true > null === undefined false == Warning === Object Creation == // Each of the following options will create a new empty object: var newObject = {}; var newObject = Object.create( null ); // Use non-null for inheritance var newObject = new Object(); Augment Property ~ 1. newObject.someKey = "Hello World"; 2. newObject["someKey"] = "Hello World"; 3. Object.defineProperty( newObject, "someKey", {     value: "for more control of the property's behavior",     writable: true,     enumerable: true,     configurable: true }); 4. Object.defineProperties( newObject, {   "someKey": { ...},   "anotherKey": { ...} }); Display enumerable properties == for (var j in obj) {  if (obj.propertyIsEnumerable(j)) console.log(j);     } Example Associative Array Implementation == See http://ajaxian.com/archives/javascript-associative-arrays-considered-harmful String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g, ”); } String.prototype.startsWith = function(s) { return this.substring(0,s.length – 1) == s; } String.prototype.endsWith = function(s) { return this.length >= s.length && this.substring(this.length – s.length) == s; } function AssocArray(){ } AssocArray.prototype.size = function() { var result = 0; for(i in this){ if(this[i].startsWith) result++; } return result; } AssocArray.prototype.keys = function(){ var result = new Array(); for(i in this){ if(this[i].startsWith) result[result.length] = i; } return result; } AssocArray.prototype.values = function(){ var result = new Array(); for(i in this){ if(this[i].startsWith) result[result.length] = this[i]; } return result; } And some test to the above: var taa = new AssocArray(); taa.testa = ‘value1a’; // or taa['testa'] = ‘value1a’; taa.testb = ‘value2a’; // … alert(‘taa.size = ‘ + taa.size()); var s = ”; var k = taa.keys(); for(i in k){ s += ‘;’ + k[i]; } alert(‘Keys are ‘ + s); s = ”; var v = taa.values(); for(i in taa.values()){ s += ‘;’ + v[i]; } alert(‘Values are ‘ + s); for(i in taa){ if(taa[i].startsWith) alert(i + ‘=’ + taa[i]); } Note the test for startsWith function in the for..in loops (otherwise the functions themselves are also counted, so I test to the current value to be a String, so it has my added startsWith function) The trim() and endsWith() functions are not used here, but might also be useful. Module Pattern == var myModule = {     myProperty: "someValue",     myConfig: {     useCaching: true,     language: "en"   },     myMethod: function () {     console.log( "Where in the world is Paul Irish today?" );   } }; myModule.myMethod(); Module with Private Variable == // Makes use of Closure feature. var testModule = (function () {   var counter = 0; myPrivateMethod = function( foo ) {   console.log( foo );   };   return {     incrementCounter: function () {       return counter++;     },   }; })();   testModule.incrementCounter(); == The 'this' context == All functions are supplied with 'this' context. By default, the global function 'this' refers to global context.(module) Otherwise 'this' refers to the current object. a = new MyFunc() { ...'this' refers to new object here ... } function AnotherFunc() { MyFunc.call(this); ... } <=== Note here. b = new AnotherFunc(); Note: This is how you can pass current this object to another function. == func.apply() vs func.call() == apply when args is supplied as *array-of-args* call when args listed explicitly. theFunction.apply(theThis, arrayOfArgs) theFunction.call(theThis, arg1, arg2, ...) Note: Both allow the 'this' object to be passed as first arg.   Module Import Mixin (import function and alias them) == var myModule = (function ( jQ, _ ) {     function privateMethod1(){         jQ(".container").html("test");     }     function privateMethod2(){       console.log( _.min([10, 5, 100, 2, 1000]) );     }     return{         publicMethod: function(){             privateMethod1();                        }                };      }( jQuery, _ ));   myModule.publicMethod();  File Operations == var fs = require('fs'); Read: f = fs.openSync("/tmp/foo", "w"); fs.writeSync(f, 'hello world'); fs.closeSync(f); Write: var buf = fs.readFileSync("/etc/hosts").toString(); buf.split("\n").forEach(function (s) {    console.log('Line: ' + s + '\n'); }); Directory Path Manipulation: var path =require('path'); See Also: fs.chmod(), fs.rename() Process == process <== predefined object process.argv.length process.argv[0] process.argv[1] > process { title: 'node', moduleLoadList: # These are not the only modules. [ 'Binding evals', 'Binding natives', 'NativeModule events', 'NativeModule buffer', 'Binding buffer', 'NativeModule assert', 'NativeModule util', 'NativeModule module', 'NativeModule path', 'NativeModule tty', 'NativeModule net', 'NativeModule stream', 'NativeModule timers', 'Binding timer_wrap', 'NativeModule _linklist', 'Binding tty_wrap', 'NativeModule vm', 'NativeModule fs', 'Binding fs', 'Binding constants', 'NativeModule readline', 'Binding signal_watcher', 'NativeModule console' ], _tickCallback: [Function], nextTick: [Function], versions: { node: '0.6.12', v8: '3.7.12.22', ares: '1.7.5', uv: '0.6', openssl: '1.0.1' }, arch: 'x64', platform: 'linux', argv: [ 'node' ], execPath: '/usr/bin/node', stdin: [Getter], stdout: [Getter], stderr: [Getter], openStdin: [Function], env: { env variables .... } exit: [Function], kill: [Function], pid: 9857, features: { debug: false, uv: true, ipv6: true, tls_npn: true, tls_sni: true, tls: true }, addListener: [Function], _needTickCallback: [Function], on: [Function], removeListener: [Function], reallyExit: [Function], chdir: [Function], debug: [Function], cwd: [Function], error: [Function], watchFile: [Function], umask: [Function], unwatchFile: [Function], getuid: [Function], mixin: [Function], setuid: [Function], createChildProcess: [Function], setgid: [Function], getgid: [Function], inherits: [Function], _kill: [Function], _byteLength: [Function], _debugProcess: [Function], _events: { SIGWINCH: [ [Function] ] }, dlopen: [Function], uptime: [Function], memoryUsage: [Function], uvCounters: [Function], binding: [Function] } Invoke Program == var exec =   require('child_process').exec; var child = exec('ls'); Node Package Manager npm == See: https://npmjs.org/ npm install node-ffi # Foreign Function Interface. # To call C Functions from Pure Javascript !!! var ffi = require('ffi'); var libm = ffi.Library('libm', { 'ceil': [ 'double', [ 'double' ] ] }); libm.ceil(1.5); // 2 // You can also access just functions in the current process by passing a null var current = ffi.Library(null, { 'atoi': [ 'int', [ 'string' ] ] }); current.atoi('1234'); // 1234 npm ls # List installed package. Uses ~/.npm/ directory See Also: common-node, Fibers, etc. /usr/lib/node_modules/common-node/examples/hello.js == Famous JavaScript Libraries == Lightweight: JQuery Prototype.js Mochikit.js mootools Underscore.js Framework larger libraries: YUI, Dojo == Example Implementation for range(...) == function defaultTo(object, values) { forEachIn(values, function(name, value) { if (!object.hasOwnProperty(name)) object[name] = value; }); } function range(args) { defaultTo(args, {start: 0, stepSize: 1}); if (args.end == undefined) args.end = args.start + args.stepSize * (args.length - 1); var result = []; for (; args.start <= args.end; args.start += args.stepSize) result.push(args.start); return result; } range({stepSize: 4, length: 5}); Javascript does not have implementation for such basic things. You need to use some library. == Named Parameters == JS style is to pass literal object: e.g. range({step:2, length:10}) == Regular expression == var pattern = new RegExp('one|two|three/i'); // Runtime compilation pattern.test(' test this ' ); false pattern.test(' i am gone '); true > ' i am gone '.match('gone') [ 'gone', index: 6, input: ' i am gone ' ] You construct a regular expression in one of two ways: Using a regular expression literal, as follows: var re = /ab+c/; // compile time evaluation var re = new RegExp("ab+c"); // Runtime compilation == cloning in Javascript == No built-in. // Shallow copy var newObject = jQuery.extend({}, oldObject); // Deep copy var newObject = jQuery.extend(true, {}, oldObject); == Alternative: Use JSON for cloning == obj2 = JSON.parse(JSON.stringify(obj)) == Alternative: Your own recursive clone == function clone(obj){ if(obj == null || typeof(obj) != 'object') return obj; var temp = obj.constructor(); // changed for(var key in obj) temp[key] = clone(obj[key]); return temp; } == Alternative: Change Object and add clone() method == var clone = function() { var newObj = (this instanceof Array) ? [] : {}; for (var i in this) { if (this[i] && typeof this[i] == "object") { newObj[i] = this[i].clone(); } else newObj[i] = this[i] } return newObj; }; Object.defineProperty( Object.prototype, "clone", {value: clone, enumerable: false}); == Alternative: Deep Clone or Merge with existing object == // extends 'from' object with members from 'to'. // If 'to' is null, a deep clone of 'from' is returned function extend(from, to) { if (from == null || typeof from != "object") return from; if (from.constructor != Object && from.constructor != Array) return from; if (from.constructor == Date || from.constructor == RegExp || from.constructor == Function || from.constructor == String || from.constructor == Number || from.constructor == Boolean) return new from.constructor(from); to = to || new from.constructor(); for (var name in from) { to[name] = typeof to[name] == "undefined" ? extend(from[name], null) : to[name]; } return to; } obj2 = extend(obj1); // Deep clone. obj2 = extend(obj1, obj2); // Merge with deep clone == Alternative: If inheritance is OK, use this to clone == function object(o) { function F() {} F.prototype = o; return new F(); } var newObject = object(oldObject); == prototype link == Depending on the browser the prototype link may be found one or another: var proto = obj.__proto__ || obj.constructor.prototype; == how to check if property is really in an object ? == A property may be defined to be undefined !!! a = { one : undefined, two : 2 } ; <== legal. a.one === undefined does not mean 'one' is not a key in a. function hasProperty(key, obj) { return key in obj; } // return obj.hasOwnProperty(key) ??? == window event programming == addHandler($("textfield"), "focus", function(event) { event.target.style.backgroundColor = "yellow"; }); addHandler($("textfield"), "blur", function(event) { // blur is "un-focus" event.target.style.backgroundColor = ""; }); == HTTP Requests == request = new XMLHttpRequest(); request.open("GET", "files/fruit.txt", false); // Default host = current host of webpage. request.send(null); console.log(request.responseText); request.getAllResponseHeaders() Javascript Arrays ----------------- http://andrewdupont.net/2006/05/18/javascript-associative-arrays-considered-harmful/ a = Array(); /* Old style */ a = []; // Better // Meant for numeric array only. a = new Object(); // Old style; a = {} ; // Better There is no built-in support for associative arrays. a['key'] = value; actuall is same as a.key = value Array is also object. So, a['key'] actually sets object property only. Better to use myobj = {}; for associative arrays. for i in a : ... enumerates the keys as well as the properties. There are big fans for prototype.js ======================================================================= Nice way to workaround autocomplete + search: http://stackoverflow.com/questions/4604216/jquery-ui-autocomplete-minlength0-issue ======================================================================= There is no default parameter value supported. Here is a workaround: Say you have a function with all 4 default parameters, mimicking for example PHP's declaration: function sum($a = 1, $b = 2, $c = 3, $d = 4) ... function sum(a, b, c, d) { // note no `break` needed switch (arguments.length) { case 0: a = 1; case 1: b = 2; case 2: c = 3; case 3: d = 4; } return a + b + c + d; } ======================================================================= instanceof supported: > a instanceof Object true > typeof a 'object' <== Returns string of typename only! > a { one: 1, two: 2 } Use parseInt(val, radix), String(obj), Boolean(obj), etc functions for casting. * Use JSON2.js * * You can get away without having to use DOM API by just using JQuery. Major Differences From PHP: * Globals automatically available. No global declaration * Use + for concatenation