Introducing YUI 3 AutoComplete

Post on 19-Dec-2014

5.989 views 1 download

Tags:

description

An all-new AutoComplete widget is landing in YUI 3.3.0. In this talk from YUIConf 2010, AutoComplete author Ryan Grove will take you on a whirlwind tour of some of the many autocomplete patterns it makes possible, as well as a deep dive into its powerful new YQL integration, filtering, and highlighting capabilities.

Transcript of Introducing YUI 3 AutoComplete

A brief history of YUI AutoComplete

Photo: http://www.flickr.com/photos/margolove/1204358675/

AutoComplete appeared in YUI 0.10.0, the first public release of YUI.

It was one of the first open source JavaScript autocomplete widgets.

Since then, it has been one of the most popular YUI widgets.

There are lots of different autocomplete patterns in use on

the web today.

In the YUI 2 world, this meant AutoComplete had to provide many

options within a single module.

Patterns that weren’t similar to a traditional list-based autocomplete

weren’t well-served by this model.

This isn’t a problem in the more granular YUI 3 world.

Say hello to YUI 3 AutoComplete

Photo: http://www.flickr.com/photos/christianschuit/4201189548/

Download video: http://j.mp/yui3ac1

New modular API provides more flexibility and easier extension to

allow for countless autocomplete patterns.

Photo: http://www.flickr.com/photos/grdloizaga/817443503/

autocomplete-base

event-valuechange

autocomplete-filters

autocomplete-highlighters

autocomplete-list

AutoComplete modules

Synthetic event; fires when an input field’s value changes.

AutoComplete modules

autocomplete-base

event-valuechange

autocomplete-filters

autocomplete-highlighters

autocomplete-list

Core logic and API. No UI, minimal dependencies.

Optional prepackaged result filters and highlighters.

Traditional list widget, just like Mom used to make.

1KB

2KB

3KB

4KB

5KB

6KB

7KB

8KB

2.8.2 3.3.0

AutoComplete module size(min + gzip)

Filters

Highlighters

List

Base

90% feature parity with YUI 2 AutoComplete.

*

* This is a highly scientific number. I swear.

autocomplete-base autocomplete-list

allowBrowserAutocomplete activateFirstItem

inputNode align

maxResults alwaysShowList

minQueryLength circular

queryDelay visible

queryDelimiter tabSelect

requestTemplate zIndex

resultFilters

resultFormatter

resultHighlighter

resultListLocator

resultTextLocator

source

yqlProtocol

autocomplete-base autocomplete-list

allowBrowserAutocomplete activateFirstItem

inputNode align

maxResults alwaysShowList

minQueryLength circular

queryDelay visible

queryDelimiter tabSelect

requestTemplate zIndex

resultFilters

resultFormatter

resultHighlighter

resultListLocator

resultTextLocator

source

yqlProtocol

Results can come from a DataSource, Array, or Object.

Results can also come from aJSONP URL or a YQL query.

This is awesome.

Use the prepackaged result filters and highlighters, or provide your own.

Built-in filters and highlighters support word breaking and accent folding.

Provide a custom result formatter to free yourself from boring text-only

results.

Fully accessible out of the box.

Mobile-ready out of the box.

Photo: http://www.flickr.com/photos/tonymadrid/4696501004/

Photo: http://www.flickr.com/photos/tonymadrid/4696501004/

No keyboard? No point loading keyboard code.

Speaking of code...SHOW ME IT.

Photo: http://www.flickr.com/photos/tambako/3535904860/

// Plugin-style.YUI().use('autocomplete', function (Y) { Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: 'select * from search.suggest where query="{query}"' });});

Basic usage (YQL result source)

// Plugin-style.YUI().use('autocomplete', function (Y) { Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: 'select * from search.suggest where query="{query}"' });});

Basic usage (YQL result source)

// Plugin-style.YUI().use('autocomplete', function (Y) { Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: 'select * from search.suggest where query="{query}"' });});

Basic usage (YQL result source)

// Plugin-style.YUI().use('autocomplete', function (Y) { Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: 'select * from search.suggest where query="{query}"' });});

Basic usage (YQL result source)

// Plugin-style.YUI().use('autocomplete', function (Y) { Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: 'select * from search.suggest where query="{query}"' });});

Basic usage (YQL result source)

// Plugin-style.YUI().use('autocomplete', function (Y) { Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: 'select * from search.suggest where query="{query}"' });});

Basic usage (YQL result source)

// Plugin-style.YUI().use('autocomplete', function (Y) { Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: 'select * from search.suggest where query="{query}"' });});

Basic usage (YQL result source)

// Widget-style.YUI().use('autocomplete', function (Y) { var ac = new Y.AutoComplete({ inputNode: '#my-input', render : true, source : 'select * from search.suggest where query="{query}"' });});

// Plugin-style.YUI().use('autocomplete', function (Y) { Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: 'select * from search.suggest where query="{query}"' });});

Basic usage (YQL result source)

// Widget-style.YUI().use('autocomplete', function (Y) { var ac = new Y.AutoComplete({ inputNode: '#my-input', render : true, source : 'select * from search.suggest where query="{query}"' });});

// Plugin-style.YUI().use('autocomplete', function (Y) { Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: 'select * from search.suggest where query="{query}"' });});

Basic usage (YQL result source)

// Widget-style.YUI().use('autocomplete', function (Y) { var ac = new Y.AutoComplete({ inputNode: '#my-input', render : true, source : 'select * from search.suggest where query="{query}"' });});

// Plugin-style.YUI().use('autocomplete', function (Y) { Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: 'select * from search.suggest where query="{query}"' });});

Basic usage (YQL result source)

// Widget-style.YUI().use('autocomplete', function (Y) { var ac = new Y.AutoComplete({ inputNode: '#my-input', render : true, source : 'select * from search.suggest where query="{query}"' });});

Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: 'http://example.com/search?q={query}&callback={callback}'});

JSONP result source

Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: 'http://example.com/search?q={query}&callback={callback}'});

JSONP result source

Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: 'http://example.com/search?q={query}&callback={callback}'});

JSONP result source

Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: ['apple pie', 'peach pie', 'pecan pie', 'pumpkin pie']});

Array and object result sources

Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: ['apple pie', 'peach pie', 'pecan pie', 'pumpkin pie']});

Array and object result sources

Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: ['apple pie', 'peach pie', 'pecan pie', 'pumpkin pie']});

Array and object result sources

Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: { pies : ['apple', 'peach', 'pecan', 'pumpkin'], cookies: ['chocolate chip', 'molasses', 'peanut butter'] }});

Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: ['apple pie', 'peach pie', 'pecan pie', 'pumpkin pie']});

Array and object result sources

Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: { pies : ['apple', 'peach', 'pecan', 'pumpkin'], cookies: ['chocolate chip', 'molasses', 'peanut butter'] }});

Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: ['apple pie', 'peach pie', 'pecan pie', 'pumpkin pie']});

Array and object result sources

Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: { pies : ['apple', 'peach', 'pecan', 'pumpkin'], cookies: ['chocolate chip', 'molasses', 'peanut butter'] }});

var ds = new Y.DataSource.IO({ source: 'http://example.com/search'});

Y.one('#my-input').plug(Y.Plugin.AutoComplete, { requestTemplate: '?q={query}', source: ds});

DataSource result source

var ds = new Y.DataSource.IO({ source: 'http://example.com/search'});

Y.one('#my-input').plug(Y.Plugin.AutoComplete, { requestTemplate: '?q={query}', source: ds});

DataSource result source

Deep Dive

Photo: http://www.flickr.com/photos/steelcityhobbies/1084984228/

Locating Results

Photo: http://www.flickr.com/photos/st3f4n/3951143570/

[ "apple pie", "peach pie", "pecan pie", "pumpkin pie"]

Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: 'http://example.com/search?q={query}&callback={callback}'});

✓ No problem.

{ "status": "success",

"search": { "results": [ "apple pie", "peach pie", "pecan pie", "pumpkin pie" ] }}

Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: 'http://example.com/search?q={query}&callback={callback}'});

✗ Bit of a problem.

{ "status": "success",

"search": { "results": [ "apple pie", "peach pie", "pecan pie", "pumpkin pie" ] }}

Y.one('#my-input').plug(Y.Plugin.AutoComplete, { resultListLocator: 'search.results', source: 'http://example.com/search?q={query}&callback={callback}'});

✓ Awesome.

Y.one('#my-input').plug(Y.Plugin.AutoComplete, { resultListLocator: function (response) { return response && response.search && response.search.results; },

source: 'http://example.com/search?q={query}&callback={callback}'});

The list locator can also be a function.

Y.one('#my-input').plug(Y.Plugin.AutoComplete, { resultListLocator: 'results', source: 'http://search.twitter.com/search.json?q={query}&' + 'callback={callback}'});

Y.one('#my-input').plug(Y.Plugin.AutoComplete, { resultListLocator: 'results', source: 'http://search.twitter.com/search.json?q={query}&' + 'callback={callback}'});

{ "results": [ { "created_at": "Fri, 29 Oct 2010 07:00:58 +0000", "from_user": "EricF", "to_user_id": null, "text": "YUIConf is looking like it's gonna be crazy good!", "id": 29064356310, "from_user_id": 2469883, "iso_language_code": "en" },

... ]}

Y.one('#my-input').plug(Y.Plugin.AutoComplete, { resultListLocator: 'results', source: 'http://search.twitter.com/search.json?q={query}&' + 'callback={callback}'});

{ "results": [ { "created_at": "Fri, 29 Oct 2010 07:00:58 +0000", "from_user": "EricF", "to_user_id": null, "text": "YUIConf is looking like it's gonna be crazy good!", "id": 29064356310, "from_user_id": 2469883, "iso_language_code": "en" },

... ]}

Y.one('#my-input').plug(Y.Plugin.AutoComplete, { resultListLocator: 'results', resultTextLocator: 'text', source: 'http://search.twitter.com/search.json?q={query}&' + 'callback={callback}'});

Y.one('#my-input').plug(Y.Plugin.AutoComplete, { resultListLocator: 'results',

resultTextLocator: function (result) { return result.text; },

source: 'http://search.twitter.com/search.json?q={query}&' + 'callback={callback}'});

Like the list locator, the text locator can be a function.

Filtering & Highlighting Results

Photo: http://www.flickr.com/photos/27953349@N06/4465442322/

Prepackaged Filters & Highlighters

• All are case-insensitive by default, but case-sensitive versions are available (charMatchCase, phraseMatchCase, etc.).

• Accent-folding versions are available as well (charMatchFold, phraseMatchFold, etc.).

charMatchphraseMatchstartsWithwordMatch

YUIConf  is  super  awesome,  and  so  is  YUI.

charMatch: 'yui'

YUIConf  is  super  awesome,  and  so  is  YUI.

phraseMatch: 'yui'

YUIConf  is  super  awesome,  and  so  is  YUI.

startsWith: 'yui'

YUIConf  is  super  awesome,  and  so  is  YUI.

wordMatch: 'yui'

function customFilter(query, results) { return Y.Array.filter(results, function (result) { result = result.toLowerCase();

// Only include results that start with an "a" and contain // the query string. return result.charAt(0) === 'a' && result.indexOf(query) !== -1; });}

Custom Filter

function customFilter(query, results) { return Y.Array.filter(results, function (result) { result = result.toLowerCase();

// Only include results that start with an "a" and contain // the query string. return result.charAt(0) === 'a' && result.indexOf(query) !== -1; });}

Custom Filter

function customFilter(query, results) { return Y.Array.filter(results, function (result) { result = result.toLowerCase();

// Only include results that start with an "a" and contain // the query string. return result.charAt(0) === 'a' && result.indexOf(query) !== -1; });}

Custom Filter

function customFilter(query, results) { return Y.Array.filter(results, function (result) { result = result.toLowerCase();

// Only include results that start with an "a" and contain // the query string. return result.charAt(0) === 'a' && result.indexOf(query) !== -1; });}

Custom Filter

function customFilter(query, results) { return Y.Array.filter(results, function (result) { result = result.toLowerCase();

// Only include results that start with an "a" and contain // the query string. return result.charAt(0) === 'a' && result.indexOf(query) !== -1; });}

Custom Filter

Y.one('#my-input').plug(Y.Plugin.AutoComplete, { resultFilters: customFilter, source: 'select * from search.suggest where query="{query}"'});

Custom Filter

Y.one('#my-input').plug(Y.Plugin.AutoComplete, { resultFilters: customFilter, source: 'select * from search.suggest where query="{query}"'});

Custom Filter

Y.one('#my-input').plug(Y.Plugin.AutoComplete, { resultFilters: [customFilter, 'wordMatch', otherFilter, ...], source: 'select * from search.suggest where query="{query}"'});

Custom Filter

function customHighlighter(query, results) { return Y.Array.map(results, function (result) { return Y.Highlight.all(result, query); });}

Custom Highlighter

function customHighlighter(query, results) { return Y.Array.map(results, function (result) { return Y.Highlight.all(result, query); });}

Custom Highlighter

function customHighlighter(query, results) { return Y.Array.map(results, function (result) { return Y.Highlight.all(result, query); });}

Custom Highlighter

function customHighlighter(query, results) { return Y.Array.map(results, function (result) { return Y.Highlight.all(result, query); });}

Custom Highlighter

function customHighlighter(query, results) { return Y.Array.map(results, function (result) { return Y.Highlight.all(result, query); });}

Custom Highlighter

Y.one('#my-input').plug(Y.Plugin.AutoComplete, { resultHighlighter: customHighlighter, source: 'select * from search.suggest where query="{query}"'});

Custom Highlighter

Y.one('#my-input').plug(Y.Plugin.AutoComplete, { resultHighlighter: customHighlighter, source: 'select * from search.suggest where query="{query}"'});

Custom Highlighter

Formatting Results

Photo: http://www.flickr.com/photos/doug88888/4544745031/

function twitterFormatter(query, raw, highlighted) { return Y.Array.map(raw, function (result, i) { return Y.Lang.sub( '<div class="tweet">' + '<div class="hd">' + '<img src="{img}" class="photo" ' + 'alt="Profile photo for {user}">' + '</div>' + '<div class="bd">' + '<strong class="user">{user}</strong>' + '<span class="tweet-text">{text}</span>' + '</div>' + '<div class="ft">{time}</div>' + '</div>', { id : result.id, img : result.profile_image_url, text: highlighted[i], time: result.created_at, user: result.from_user } ); });}

function twitterFormatter(query, raw, highlighted) { return Y.Array.map(raw, function (result, i) { return Y.Lang.sub( '<div class="tweet">' + '<div class="hd">' + '<img src="{img}" class="photo" ' + 'alt="Profile photo for {user}">' + '</div>' + '<div class="bd">' + '<strong class="user">{user}</strong>' + '<span class="tweet-text">{text}</span>' + '</div>' + '<div class="ft">{time}</div>' + '</div>', { id : result.id, img : result.profile_image_url, text: highlighted[i], time: result.created_at, user: result.from_user } ); });}

function twitterFormatter(query, raw, highlighted) { return Y.Array.map(raw, function (result, i) { return Y.Lang.sub( '<div class="tweet">' + '<div class="hd">' + '<img src="{img}" class="photo" ' + 'alt="Profile photo for {user}">' + '</div>' + '<div class="bd">' + '<strong class="user">{user}</strong>' + '<span class="tweet-text">{text}</span>' + '</div>' + '<div class="ft">{time}</div>' + '</div>', { id : result.id, img : result.profile_image_url, text: highlighted[i], time: result.created_at, user: result.from_user } ); });}

{ "results": [ { "created_at": "Fri, 29 Oct 2010 07:00:58 +0000", "from_user": "EricF", "to_user_id": null, "text": "YUIConf is looking like it's gonna be crazy good!", "id": 29064356310, "from_user_id": 2469883, "iso_language_code": "en" },

... ]}

time: result.created_at, user: result.from_user } ); });}

function twitterFormatter(query, raw, highlighted) { return Y.Array.map(raw, function (result, i) { return Y.Lang.sub( '<div class="tweet">' + '<div class="hd">' + '<img src="{img}" class="photo" ' + 'alt="Profile photo for {user}">' + '</div>' + '<div class="bd">' + '<strong class="user">{user}</strong>' + '<span class="tweet-text">{text}</span>' + '</div>' + '<div class="ft">{time}</div>' + '</div>', { id : result.id, img : result.profile_image_url, text: highlighted[i], time: result.created_at, user: result.from_user } ); });}

function twitterFormatter(query, raw, highlighted) { return Y.Array.map(raw, function (result, i) { return Y.Lang.sub( '<div class="tweet">' + '<div class="hd">' + '<img src="{img}" class="photo" ' + 'alt="Profile photo for {user}">' + '</div>' + '<div class="bd">' + '<strong class="user">{user}</strong>' + '<span class="tweet-text">{text}</span>' + '</div>' + '<div class="ft">{time}</div>' + '</div>', { id : result.id, img : result.profile_image_url, text: highlighted[i], time: result.created_at, user: result.from_user } ); });}

[ "<b class=\"yui3-highlight\">YUIConf</b> is looking like it's gonna be crazy good!", ...]

time: result.created_at, user: result.from_user } ); });}

function twitterFormatter(query, raw, highlighted) { return Y.Array.map(raw, function (result, i) { return Y.Lang.sub( '<div class="tweet">' + '<div class="hd">' + '<img src="{img}" class="photo" ' + 'alt="Profile photo for {user}">' + '</div>' + '<div class="bd">' + '<strong class="user">{user}</strong>' + '<span class="tweet-text">{text}</span>' + '</div>' + '<div class="ft">{time}</div>' + '</div>', { id : result.id, img : result.profile_image_url, text: highlighted[i], time: result.created_at, user: result.from_user } ); });}

function twitterFormatter(query, raw, highlighted) { return Y.Array.map(raw, function (result, i) { return Y.Lang.sub( '<div class="tweet">' + '<div class="hd">' + '<img src="{img}" class="photo" ' + 'alt="Profile photo for {user}">' + '</div>' + '<div class="bd">' + '<strong class="user">{user}</strong>' + '<span class="tweet-text">{text}</span>' + '</div>' + '<div class="ft">{time}</div>' + '</div>', { id : result.id, img : result.profile_image_url, text: highlighted[i], time: result.created_at, user: result.from_user } ); });}

function twitterFormatter(query, raw, highlighted) { return Y.Array.map(raw, function (result, i) { return Y.Lang.sub( '<div class="tweet">' + '<div class="hd">' + '<img src="{img}" class="photo" ' + 'alt="Profile photo for {user}">' + '</div>' + '<div class="bd">' + '<strong class="user">{user}</strong>' + '<span class="tweet-text">{text}</span>' + '</div>' + '<div class="ft">{time}</div>' + '</div>', { id : result.id, img : result.profile_image_url, text: highlighted[i], time: result.created_at, user: result.from_user } ); });}

function twitterFormatter(query, raw, highlighted) { return Y.Array.map(raw, function (result, i) { return Y.Lang.sub( '<div class="tweet">' + '<div class="hd">' + '<img src="{img}" class="photo" ' + 'alt="Profile photo for {user}">' + '</div>' + '<div class="bd">' + '<strong class="user">{user}</strong>' + '<span class="tweet-text">{text}</span>' + '</div>' + '<div class="ft">{time}</div>' + '</div>', { id : result.id, img : result.profile_image_url, text: highlighted[i], time: result.created_at, user: result.from_user } ); });}

function twitterFormatter(query, raw, highlighted) { return Y.Array.map(raw, function (result, i) { return Y.Lang.sub( '<div class="tweet">' + '<div class="hd">' + '<img src="{img}" class="photo" ' + 'alt="Profile photo for {user}">' + '</div>' + '<div class="bd">' + '<strong class="user">{user}</strong>' + '<span class="tweet-text">{text}</span>' + '</div>' + '<div class="ft">{time}</div>' + '</div>', { id : result.id, img : result.profile_image_url, text: highlighted[i], time: result.created_at, user: result.from_user } ); });}

Y.one('#ac-input').plug(Y.Plugin.AutoComplete, { resultFormatter: twitterFormatter, resultHighlighter: 'phraseMatch', resultListLocator: 'results', resultTextLocator: 'text', source: 'http://search.twitter.com/search.json?q={query}&' + 'callback={callback}'});

Y.one('#ac-input').plug(Y.Plugin.AutoComplete, { resultFormatter: twitterFormatter, resultHighlighter: 'phraseMatch', resultListLocator: 'results', resultTextLocator: 'text', source: 'http://search.twitter.com/search.json?q={query}&' + 'callback={callback}'});

.tweet { clear: both; margin: 6px 0; padding: 2px 0;}

.tweet .hd { float: left; }

.tweet .bd,

.tweet .ft { margin-left: 52px; }

.tweet .ft { color: #cfcfcf; font-size: 11px;}

.tweet .photo { height: 48px; margin: 2px 0; width: 48px;}

.tweet .user { margin-right: 6px; }

Download video: http://j.mp/yui3ac2

Other new modules

Photo: http://www.flickr.com/photos/thomashawk/55519741/

• Synthetic event. Fires when an input node’s value changes due to user input.

• Handles keystrokes, pastes, and IME input.

• Creates a level playing field across browsers, particularly with languages that have multi-stroke characters.

event-valuechange

Y.one('#my-input').on('valueChange', function (e) { Y.log('Old value: ' + e.prevVal); Y.log('New value: ' + e.newVal);});

event-valuechange

Y.one('#my-input').on('valueChange', function (e) { Y.log('Old value: ' + e.prevVal); Y.log('New value: ' + e.newVal);});

event-valuechange

Y.one('#my-input').on('valueChange', function (e) { Y.log('Old value: ' + e.prevVal); Y.log('New value: ' + e.newVal);});

event-valuechange

escape

• Static utility methods for escaping strings.

• Currently has methods for escaping HTML and regex strings. More to come in future releases.

• Feel free to add your own escaping methods to the Y.Escape namespace (and share on the Gallery).

Y.Escape.html('<script>alert("pwned!")</script>');// => '&lt;script&gt;alert(&quot;pwned!&quot;)&lt;&#x2F;script&gt;'

Y.Escape.regex('$3.14 (plus tax)');// => '\$3\.14\ \(plus\ tax\)'

escape

Y.Escape.html('<script>alert("pwned!")</script>');// => '&lt;script&gt;alert(&quot;pwned!&quot;)&lt;&#x2F;script&gt;'

Y.Escape.regex('$3.14 (plus tax)');// => '\$3\.14\ \(plus\ tax\)'

escape

Y.Escape.html('<script>alert("pwned!")</script>');// => '&lt;script&gt;alert(&quot;pwned!&quot;)&lt;&#x2F;script&gt;'

Y.Escape.regex('$3.14 (plus tax)');// => '\$3\.14\ \(plus\ tax\)'

escape

Y.Escape.html('<script>alert("pwned!")</script>');// => '&lt;script&gt;alert(&quot;pwned!&quot;)&lt;&#x2F;script&gt;'

Y.Escape.regex('$3.14 (plus tax)');// => '\$3\.14\ \(plus\ tax\)'

escape

Y.Escape.html('<script>alert("pwned!")</script>');// => '&lt;script&gt;alert(&quot;pwned!&quot;)&lt;&#x2F;script&gt;'

Y.Escape.regex('$3.14 (plus tax)');// => '\$3\.14\ \(plus\ tax\)'

escape

Y.Escape.html('<script>alert("pwned!")</script>');// => '&lt;script&gt;alert(&quot;pwned!&quot;)&lt;&#x2F;script&gt;'

Y.Escape.regex('$3.14 (plus tax)');// => '\$3\.14\ \(plus\ tax\)'

escape

Y.Escape.html('<script>alert("pwned!")</script>');// => '&lt;script&gt;alert(&quot;pwned!&quot;)&lt;&#x2F;script&gt;'

Y.Escape.regex('$3.14 (plus tax)');// => '\$3\.14\ \(plus\ tax\)'

escape

And now, a brief interlude wherein I whine about having discovered, yesterday

afternoon, that “Unicode” is a registered trademark of the

Unicode® Consortium.

®?

®?

As a result, the word “Unicode” has been replaced with ☃ in the following slides.

I apologize for the inconvenience.

☃-accentfold

• Helpers for creating and working with accent-folded strings.

• Accent folding converts “résumé” to “resume” (etc.) for easier comparison.

• Should be a last resort. Only use it when better tools aren’t available on the server. Don’t fold strings you will display to the user.

• Will have an actual name before 3.3.0 final.

Y.☃.AccentFold.fold('résumé');// => 'resume'

Y.☃.AccentFold.compare('résumé', 'resume');// => true

Y.☃.AccentFold.filter(['föö', 'bår', 'móó'], function (str) { return str.indexOf('o') !== -1;});// => ['föö', 'móó']

☃-accentfold

Y.☃.AccentFold.fold('résumé');// => 'resume'

Y.☃.AccentFold.compare('résumé', 'resume');// => true

Y.☃.AccentFold.filter(['föö', 'bår', 'móó'], function (str) { return str.indexOf('o') !== -1;});// => ['föö', 'móó']

☃-accentfold

Y.☃.AccentFold.fold('résumé');// => 'resume'

Y.☃.AccentFold.compare('résumé', 'resume');// => true

Y.☃.AccentFold.filter(['föö', 'bår', 'móó'], function (str) { return str.indexOf('o') !== -1;});// => ['föö', 'móó']

☃-accentfold

Y.☃.AccentFold.fold('résumé');// => 'resume'

Y.☃.AccentFold.compare('résumé', 'resume');// => true

Y.☃.AccentFold.filter(['föö', 'bår', 'móó'], function (str) { return str.indexOf('o') !== -1;});// => ['föö', 'móó']

☃-accentfold

☃-wordbreak

• Implements ☃ text segmentation guidelines for word breaking.

• Handles edge cases like contractions, decimals, thousands separators, Katakana, non-Latin punctuation, etc.

• Not perfect, but much smarter than /\b/.

• Will have an actual name before 3.3.0 final.

Y.☃.WordBreak.getWords("A kilobyte's just 1,024 bytes.");// => ["A", "kilobyte's", "just", "1,024", "bytes"]

Y.☃.WordBreak.getWords('パイが好きです');// => ["パイ", "が", "好", "き", "で", "す"]

☃-wordbreak

Y.☃.WordBreak.getWords("A kilobyte's just 1,024 bytes.");// => ["A", "kilobyte's", "just", "1,024", "bytes"]

Y.☃.WordBreak.getWords('パイが好きです');// => ["パイ", "が", "好", "き", "で", "す"]

☃-wordbreak

Y.☃.WordBreak.getWords("A kilobyte's just 1,024 bytes.");// => ["A", "kilobyte's", "just", "1,024", "bytes"]

Y.☃.WordBreak.getWords('パイが好きです');// => ["パイ", "が", "好", "き", "で", "す"]

☃-wordbreak

highlight

• Static utility methods for highlighting strings using HTML.

• Supports phrase highlighting, start-of-string highlighting, and word highlighting.

• Can use accent folded strings for comparisons while highlighting the original, non-folded string.

• Smart enough not to highlight inside HTML entities like &quot;.

• Doesn’t infringe any trademarks.

Y.Highlight.all('YUIConf 2010: YUI rocks!', 'yui');// => '<b class="yui3-highlight">YUI</b>Conf 2010: <b class="yui3-highlight">YUI</b> rocks!'

Y.Highlight.start('YUIConf 2010: YUI rocks!', 'yui');// => '<b class="yui3-highlight">YUI</b>Conf 2010: YUI rocks!'

Y.Highlight.words('YUIConf 2010: YUI rocks!', 'yui');// => 'YUIConf 2010: <b class="yui3-highlight">YUI</b> rocks!'

Y.Highlight.allFold('YÜIConf 2010: YUI rocks!', 'yui');// => '<b class="yui3-highlight">YÜI</b>Conf 2010: <b class="yui3-highlight">YUI</b> rocks!'

highlight

Y.Highlight.all('YUIConf 2010: YUI rocks!', 'yui');// => '<b class="yui3-highlight">YUI</b>Conf 2010: <b class="yui3-highlight">YUI</b> rocks!'

Y.Highlight.start('YUIConf 2010: YUI rocks!', 'yui');// => '<b class="yui3-highlight">YUI</b>Conf 2010: YUI rocks!'

Y.Highlight.words('YUIConf 2010: YUI rocks!', 'yui');// => 'YUIConf 2010: <b class="yui3-highlight">YUI</b> rocks!'

Y.Highlight.allFold('YÜIConf 2010: YUI rocks!', 'yui');// => '<b class="yui3-highlight">YÜI</b>Conf 2010: <b class="yui3-highlight">YUI</b> rocks!'

highlight

Y.Highlight.all('YUIConf 2010: YUI rocks!', 'yui');// => '<b class="yui3-highlight">YUI</b>Conf 2010: <b class="yui3-highlight">YUI</b> rocks!'

Y.Highlight.start('YUIConf 2010: YUI rocks!', 'yui');// => '<b class="yui3-highlight">YUI</b>Conf 2010: YUI rocks!'

Y.Highlight.words('YUIConf 2010: YUI rocks!', 'yui');// => 'YUIConf 2010: <b class="yui3-highlight">YUI</b> rocks!'

Y.Highlight.allFold('YÜIConf 2010: YUI rocks!', 'yui');// => '<b class="yui3-highlight">YÜI</b>Conf 2010: <b class="yui3-highlight">YUI</b> rocks!'

highlight

Y.Highlight.all('YUIConf 2010: YUI rocks!', 'yui');// => '<b class="yui3-highlight">YUI</b>Conf 2010: <b class="yui3-highlight">YUI</b> rocks!'

Y.Highlight.start('YUIConf 2010: YUI rocks!', 'yui');// => '<b class="yui3-highlight">YUI</b>Conf 2010: YUI rocks!'

Y.Highlight.words('YUIConf 2010: YUI rocks!', 'yui');// => 'YUIConf 2010: <b class="yui3-highlight">YUI</b> rocks!'

Y.Highlight.allFold('YÜIConf 2010: YUI rocks!', 'yui');// => '<b class="yui3-highlight">YÜI</b>Conf 2010: <b class="yui3-highlight">YUI</b> rocks!'

highlight

Y.Highlight.all('YUIConf 2010: YUI rocks!', 'yui');// => '<b class="yui3-highlight">YUI</b>Conf 2010: <b class="yui3-highlight">YUI</b> rocks!'

Y.Highlight.start('YUIConf 2010: YUI rocks!', 'yui');// => '<b class="yui3-highlight">YUI</b>Conf 2010: YUI rocks!'

Y.Highlight.words('YUIConf 2010: YUI rocks!', 'yui');// => 'YUIConf 2010: <b class="yui3-highlight">YUI</b> rocks!'

Y.Highlight.allFold('YÜIConf 2010: YUI rocks!', 'yui');// => '<b class="yui3-highlight">YÜI</b>Conf 2010: <b class="yui3-highlight">YUI</b> rocks!'

highlight

Y.Highlight.all('YUIConf 2010: YUI rocks!', 'yui');// => '<b class="yui3-highlight">YUI</b>Conf 2010: <b class="yui3-highlight">YUI</b> rocks!'

Y.Highlight.start('YUIConf 2010: YUI rocks!', 'yui');// => '<b class="yui3-highlight">YUI</b>Conf 2010: YUI rocks!'

Y.Highlight.words('YUIConf 2010: YUI rocks!', 'yui');// => 'YUIConf 2010: <b class="yui3-highlight">YUI</b> rocks!'

Y.Highlight.allFold('YÜIConf 2010: YUI rocks!', 'yui');// => '<b class="yui3-highlight">YÜI</b>Conf 2010: <b class="yui3-highlight">YUI</b> rocks!'

highlight

Y.Highlight.all('YUIConf 2010: YUI rocks!', 'yui');// => '<b class="yui3-highlight">YUI</b>Conf 2010: <b class="yui3-highlight">YUI</b> rocks!'

Y.Highlight.start('YUIConf 2010: YUI rocks!', 'yui');// => '<b class="yui3-highlight">YUI</b>Conf 2010: YUI rocks!'

Y.Highlight.words('YUIConf 2010: YUI rocks!', 'yui');// => 'YUIConf 2010: <b class="yui3-highlight">YUI</b> rocks!'

Y.Highlight.allFold('YÜIConf 2010: YUI rocks!', 'yui');// => '<b class="yui3-highlight">YÜI</b>Conf 2010: <b class="yui3-highlight">YUI</b> rocks!'

highlight

Y.Highlight.all('YUIConf 2010: YUI rocks!', 'yui');// => '<b class="yui3-highlight">YUI</b>Conf 2010: <b class="yui3-highlight">YUI</b> rocks!'

Y.Highlight.start('YUIConf 2010: YUI rocks!', 'yui');// => '<b class="yui3-highlight">YUI</b>Conf 2010: YUI rocks!'

Y.Highlight.words('YUIConf 2010: YUI rocks!', 'yui');// => 'YUIConf 2010: <b class="yui3-highlight">YUI</b> rocks!'

Y.Highlight.allFold('YÜIConf 2010: YUI rocks!', 'yui');// => '<b class="yui3-highlight">YÜI</b>Conf 2010: <b class="yui3-highlight">YUI</b> rocks!'

highlight

Y.Highlight.all('YUIConf 2010: YUI rocks!', 'yui');// => '<b class="yui3-highlight">YUI</b>Conf 2010: <b class="yui3-highlight">YUI</b> rocks!'

Y.Highlight.start('YUIConf 2010: YUI rocks!', 'yui');// => '<b class="yui3-highlight">YUI</b>Conf 2010: YUI rocks!'

Y.Highlight.words('YUIConf 2010: YUI rocks!', 'yui');// => 'YUIConf 2010: <b class="yui3-highlight">YUI</b> rocks!'

Y.Highlight.allFold('YÜIConf 2010: YUI rocks!', 'yui');// => '<b class="yui3-highlight">YÜI</b>Conf 2010: <b class="yui3-highlight">YUI</b> rocks!'

highlight

Y.Highlight.all('YUIConf 2010: YUI rocks!', 'yui');// => '<b class="yui3-highlight">YUI</b>Conf 2010: <b class="yui3-highlight">YUI</b> rocks!'

Y.Highlight.start('YUIConf 2010: YUI rocks!', 'yui');// => '<b class="yui3-highlight">YUI</b>Conf 2010: YUI rocks!'

Y.Highlight.words('YUIConf 2010: YUI rocks!', 'yui');// => 'YUIConf 2010: <b class="yui3-highlight">YUI</b> rocks!'

Y.Highlight.allFold('YÜIConf 2010: YUI rocks!', 'yui');// => '<b class="yui3-highlight">YÜI</b>Conf 2010: <b class="yui3-highlight">YUI</b> rocks!'

highlight

Y.Highlight.all('YUIConf 2010: YUI rocks!', 'yui');// => '<b class="yui3-highlight">YUI</b>Conf 2010: <b class="yui3-highlight">YUI</b> rocks!'

Y.Highlight.start('YUIConf 2010: YUI rocks!', 'yui');// => '<b class="yui3-highlight">YUI</b>Conf 2010: YUI rocks!'

Y.Highlight.words('YUIConf 2010: YUI rocks!', 'yui');// => 'YUIConf 2010: <b class="yui3-highlight">YUI</b> rocks!'

Y.Highlight.allFold('YÜIConf 2010: YUI rocks!', 'yui');// => '<b class="yui3-highlight">YÜI</b>Conf 2010: <b class="yui3-highlight">YUI</b> rocks!'

highlight

Why <b>?

• HTML5 defines a new <mark> element for denoting the relevance (as opposed to the importance) of a span of text.

• It recommends using <mark> “to highlight a part of quoted text that was not originally emphasized”.

• But, since <mark> isn’t yet supported in all browsers, we use <b>, which HTML5 defines as a last resort for highlighting text without implying importance.

• You can customize the highlighting markup by changing Y.Highlight._TEMPLATE.

More details: http://www.whatwg.org/specs/web-apps/current-work/multipage/text-level-semantics.html#the-mark-element

Photo: http://www.flickr.com/photos/tim_norris/2789759648/

What I just saidbut with bullets

• Modular architecture makes it easy to implement a range of autocomplete patterns.

• Dead simple JSONP and YQL support.

• Use prepackaged filters and highlighters with word breaking and accent folding, or roll your own.

• Create custom result formatters with just a few lines of code.

• AutoComplete and friends are available now in YUI 3.3.0pr1.

Photo: http://www.flickr.com/photos/tim_norris/2789759648/

One more thing...

Photo: http://www.flickr.com/photos/jabb/3502160522/

Download video: http://j.mp/yui3ac3

YUI().use('gallery-node-tokeninput', function (Y) { Y.one('#my-input').plug(Y.Plugin.TokenInput);});

node-tokeninput

YUI().use('gallery-node-tokeninput', function (Y) { Y.one('#my-input').plug(Y.Plugin.TokenInput);});

node-tokeninput

YUI().use('gallery-node-tokeninput', function (Y) { Y.one('#my-input').plug(Y.Plugin.TokenInput);});

node-tokeninput

YUI().use('gallery-node-tokeninput', function (Y) { Y.one('#my-input').plug(Y.Plugin.TokenInput);});

node-tokeninput

...but beware, there are some rough edges at the moment.

Photo: http://www.flickr.com/photos/damaradeaella/2822846819/

Ryan Grove, YUI Team@yaypie on Twitter

https://github.com/rgrove/

Slides: http://lanyrd.com/smym

Photo: http://www.flickr.com/photos/damaradeaella/2822846819/