.. include:: .. raw:: html :file: includes/logo.html =================== Intro to MochiKit =================== .. raw:: LaTeX :file: includes/logo.tex :Author: Bob Ippolito :Date: May 2006 :Venue: The Ajax Experience 2006 .. raw:: LaTeX \newpage What's MochiKit? ================ * Makes JavaScript suck less * Provides browser workarounds * Simplifies AJAX and DOM * Plays nicely with other code Another Library?! ================= (July 2005) Prototype: no docs or tests, mangles built-ins Dojo: no docs or demos MochiKit Design Goals ===================== * Documentation * DOCUMENTATION * Lots of tests * Stay out of the way (not a framework) * Consistent and portable Why MochiKit? ============= * Good docs, well tested * Async abstractions * Painless DOM syntax * Consistent browser events * Python-like (think "standard library") * Safari 2.0, Firefox 1.0, IE 6, Opera 8.5 (and others) Why Not MochiKit? ================= * No support for IE 5.5 * No widgets * No animation, transitions, etc. * ... MochiKit 1.4 MochiKit.Base ============= * Beats some sense into JavaScript * Provides analogs to a lot Python built-ins and object protocols * This is the "suck less" part toString Ambiguity ================== * [1].toString() === '1' * (1).toString() === '1' * '1'.toString() === '1' repr > toString =============== * MochiKit provides repr() instead, just like Python. * Unambiguous, extensible MochiKit Interpreter ==================== .. raw:: html :file: includes/interpreter.html .. class:: handout In-line demo of the MochiKit interpreter example. The advantages of repr vs. toString and some of the interpreter's features will be demonstrated during this slide. Unreliable Operators ==================== * Most JavaScript comparisons based on toString * [1] == [1] is false! * compare(a, b) provides consistent results * Extensible JSON Serialization ================== * serializeJSON(object) -> JSON string * Extensible Adapters? ========= * Not a good idea to hack on built-in objects * MochiKit doesn't always know what you want to do * Adapters let you extend existing functions Adapting ======== name: unique identifier for your adapter check: should wrap be called? wrap: performs the operation DOM Comparator Example ====================== Register HTML-based comparator for DOM nodes:: function isDOMNode(node) { return typeof(node.nodeType) == 'number'; } function compareDOMNodes(a, b) { return compare(a.innerHTML, b.innerHTML); } registerComparator('compareDOM', isDOMNode, compareDOMNode); queryString =========== * Easily build URL query strings * queryString(['foo', 'bar'], [1, 2]) == 'foo=1&bar=2' * queryString({foo: 1, bar: 2}) == 'foo=1&bar=2' queryString and DOM =================== * queryString('formNode') == 'foo=1&bar=2', given::
Mangling Objects ================ merge(obj[, ...]): New object, every prop:value of given objects update(obj[, ...]): In-place merge updatetree(obj[, ...]): Recursive update setdefault(obj[, ...]): update, but no overwrite Object Introspection ==================== keys(obj): Array of obj's properties items(obj): Array of obj's [property, value] Function Functions ================== bind(fn, self[, arg...]): fn.apply(self, concat(arg..., arguments)) method(self, fn[, arg...]): convenience form for bind itemgetter(name): obj[name] Array Functions =============== Array missing lots of useful functionality concat(lst[, ...]): concatenates Arrays extend(self, seq, skip=0): extends Array in-place flattenArguments(args[, ...]): recursively flatten arguments to single Array Array Searching =============== findValue(lst, value): finds index of value findIdentical(lst, value): finds index of identical value listMin(lst): finds least item in lst listMax(lst): finds greatest item in lst Higher-order Array ================== filter(func, lst): Array where funcn(lst[n]) map(func, lst): [func(lst[0]), ...] keyComparator(key): compare(a[key], b[key]) MochiKit.Iter ============= MochiKit.Iter provides generalized iteration, like Python's iteration protocol and itertools module. * Works great on Arrays, but also on anything iterable. * Anything with a ``next`` method is iterable, and the iteration stops when StopIteration is thrown. * Extensible Collapsing Iterators ==================== exhaust(iterable): Iterate and ignore results list(iterable): new Array sorted(iterable): sorted Array sum(iterable, start=0): Return start plus sum of items Iterating Iterables =================== The hard way:: var itr = iter(iterable); try { while (true) { var item = itr.next(); // ... } } catch (e) { if (e != StopIteration) throw e; } Sane Iterable Iteration ======================= The easy way:: forEach(iterable, function (item) { // ... }) Infinite Iterators ================== count(n=0): n, n + 1, n + 2, ... cycle(iterable): while (1) { iterable[0], ... } repeat(item): item, item, item, item, ... MochiKit.DateTime ================= * JavaScript Date objects aren't very convenient * W3C profile ISO 8601 style timestamps are Good ISO Dates ========= isoDate(str): Date object from ISO 8601 date string toISODate(date): Date object to ISO 8601 date string American Dates ============== americanDate(str): MM/DD/YYYY to a Date object toAmericanDate(date): Date object to M/D/YYYY toPaddedAmericanDate(date): Date object to MM/DD/YYYY Time and Timestamps =================== isoTimestamp(str): YYYY-MM-DDThh:mm:ssZ to Date object toISOTime(date): Date object to hh:mm:ss toISOTimestamp(date, realISO=false): Date object to a YYYY-MM-DD hh:mm:ss (or YYYY-MM-DDThh:mm:ssZ) MochiKit.Format =============== * JavaScript has no sprintf * Thousands separators help readability * Java's NumberFormat pattern syntax Whitespace Assassins ==================== lstrip(str): strip leading whitespace rstrip(str): strip trailing whitespace strip(str): strip leading and trailing whitespace Number Formatting ================= numberFormatter(pattern): new function that formats numbers to pattern Currency Formatter Example ========================== Currency:: >>> money = numberFormatter('$###,###.##') >>> money(1234567.89) "$1,234,567.89" Percent Formatter Example ========================= Percent:: >>> percent = numberFormatter('###,###%') >>> percent(123.45) "12,345%" MochiKit.Logging ================ * alert() sucks * Debugging is hard enough * FireBug not portable * Venkman hard to use * Microsoft Script Debugger.... Simple Logging ============== log(msg): Logs a message at the INFO level logDebug, logWarning, logError, logFatal... Logs Are Where? =============== Native console: Safari, FireBug, Opera Logging listener(s): functions called with log message objects Bookmarklet Debugging ===================== Pop-up MochiKit.LoggingPane:: javascript:logger.debuggingBookmarklet() MochiKit.LoggingPane ==================== * MochiKit.Logging listener * Can be used in-line or as a pop-up window Manually creating a LoggingPane =============================== Pop-up:: createLoggingPane() Inline:: createLoggingPane(true) Inline LoggingPane Example ========================== .. raw:: html :file: includes/logging_pane.html .. class:: handout In-line demo of the MochiKit LoggingPane. MochiKit.DOM ============ * W3C bindings are painful * Easily find, create, mangle DOM nodes * Don't need $(s), automatic * Works on responseXML too! createDOM ========= * Any object into a DOM node * Strings to text, flattens iterators * Extensible createDOM Example ================= createDOM(tagName, attributes, contents...) A simple list:: var node = createDOM('ul', null, createDOM('li', null, 'first'), createDOM('li', null, 'second')); Renders as:: Less Ugly ========= Use aliases instead, supports common tags:: var node = UL(null, LI('first'), LI('second')); Flattening for the DOM ====================== Functional style handy for DOM creation:: var items = ['first', 'second']; var node = UL(null, map(LI, items)); Attributes ========== First parameter is either an object (attributes), or a string (text node):: var classes = repeat({'class': 'itemclass'}); var items = ['first', 'second']; var node = UL({'class': 'listclass'}, map(LI, classes, items)); Alternating =========== MochiKit.Iter good for table rows:: var classes = cycle( {'class': 'even'}, {'class': 'odd'}); var items = ['first', 'second']; var node = UL(null, map(LI, classes, items)); Interpreter DOM =============== .. raw:: html :file: includes/dominterpreter.html .. class:: handout Another in-line interpreter demo, this time showing off MochiKit's DOM support. Scraping Text ============= Scraping text is useful for progressive enhancement... HTML:: text is here JavaScript:: >>> scrapeText('scrape_me'); "text is here" Forms ===== HTML::
JavaScript:: >>> formContents('formNode') [["foo", "bar"], ["1", "2"]] Manipulating DOM ================ appendChildNodes(parent, children...): Add nodes via createDOM replaceChildNodes(parent, children...): Remove all, then append swapDOM(dest, src): Replace dest with src (or remove) DOM Attributes ============== setNodeAttribute(node, attr, value): node attribute attr=value updateNodeAttributes(node, attrs): node attributes from object attrs DOM Gotchas =========== * DOM manipulation isn't as fast as innerHTML, but it's a LOT easier * IE expects tables to have a TBODY MochiKit.Color ============== * Full CSS3 color model with alpha * NSColor-like API (from Cocoa) * Works in RGB, HSV, HSL * Normalized to [0, 1.0] Components to Color =================== * Color.fromRGB(r, g, b, alpha=1.0) * fromHSL, fromHSV * Also {r: 1, g: 0, b: 0, a: 1} String to Color =============== Color.fromString(str): Color from any valid CSS color description - 'rgb(...)' - 'hsl(...)' - '#RRGGBB' - 'blue' DOM to Color ============ - Color.fromBackground(node) - fromComputedStyle, fromText, ... NSColor Colors ============== Cocoa-based constructors for basic colors - Color.whiteColor() - blueColor, transparentColor, ... Mixing Colors ============= - color.blendedColor(otherColor, fraction) - color.colorWithHue, colorWithLevel, ... Color Components ================ Objects: - color.asRGB(), asHSL, asHSV Strings: - color.toHexString(), toRGBString, toHSLString MochiKit.Async ============== * AJAX! * Model based on Twisted * XMLHttpRequest and timed events (setTimeout) WTF is a Deferred? ================== * A "promise" for a result * Can be chained * Model on any asynchronous platform * Not "ideal" API, but no coroutines or threads Trivial Deferreds ================= succeed(value): successful Deferred from value fail(error): failed Deferred from error maybeDeferred(func, arguments..): Deferred from func(args..) call Timed Events ============ wait(seconds, value): Deferred that waits callLater(seconds, func, arguments...): Deferred that waits, then calls Network Events ============== doSimpleXMLHttpRequest(url): Deferred from XMLHttpRequest GET loadJSONDoc(url): Deferred from XMLHttpRequest GET then eval sendXMLHttpRequest(req, data): Deferred from async XMLHttpRequest Deferred Usage ============== Fetch a JSON document:: function gotDocument(json) { // ... } var d = loadJSONDoc("example.json"); d.addCallback(gotDocument); d.addErrback(logError); Result Chaining =============== loadJSONDoc implementation:: var d = doSimpleXMLHttpRequest(url); d.addCallback(evalJSONDoc); return d; Uses Deferred's chained results Deferred Chaining ================= Returning a Deferred from a callback will "pause" the callback chain:: function gotDocument(json) { /* ... */ } function delay(res) { return wait(2.0, res); } var d = loadJSONDoc('example.json'); d.addCallback(delay); d.addCallback(gotDocument); MochiKit.Signal =============== * Crown jewel of MochiKit 1.3 * Sorry it took so long! * Browsers totally suck at events Browser Events Suck =================== * IE is totally different * IE's garbage "collector" * Safari needs help * They all disagree on pixel positions * Key events seriously borked connect to the DOM ================== Works everywhere:: function myClick(e) { var mouse = e.mouse(); log('page coordinates: ' + mouse.page); log('client coordinates: ' + mouse.client); } connect('element_id', 'onclick', myClick); Custom Event Object =================== * Consistent event object!! * e.type() is the event type * e.target() is the event target * e.mouse() has mouse coordinates * e.key() for keyboard state * e.modifiers() for keyboard modifiers * e.stop() to stop Mouse Events ============ .. raw:: html :file: includes/draggable.html .. class:: handout The slide's logo will be dragged during this slide, while displaying live pixel coordinates. Keyboard Events =============== .. raw:: html :file: includes/key_events.html .. class:: handout An in-line version of the key_events demo will be shown during this slide, showing capture of onkeypress, onkeydown, onkeyup events. Also shows the consistent naming of keys. Signal Anything =============== * Broadcast your own events! * connect() and disconnect() are the same * signal(src, signal, args...) to fire MochiKit Support ================ - Check the documentation - Ask on the mailing list - Check the wiki/bug tracker - #mochikit on irc.freenode.net MochiKit on the Web =================== Home page: http://mochikit.com/ Bug tracker/Wiki: http://trac.mochikit.com/ Subversion repository: http://svn.mochikit.com/