Skip to content

Instantly share code, notes, and snippets.

@dawsontoth
Created October 10, 2011 10:11

Revisions

  1. dawsontoth revised this gist Oct 13, 2011. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions suggest.js
    Original file line number Diff line number Diff line change
    @@ -133,8 +133,8 @@ exports.bind = function(args) {
    args.control.addEventListener('focus', args.control.onFocus);

    args.control.onBlur = function() {
    args.control.onChangeIntervalID = setInterval(sync, 1000);
    args.control.addEventListener('change', args.control.onChange);
    clearInterval(args.control.onChangeIntervalID);
    args.control.removeEventListener('change', args.control.onChange);
    if (visible) {
    hide();
    }
  2. dawsontoth created this gist Oct 10, 2011.
    72 changes: 72 additions & 0 deletions app.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,72 @@
    /**
    * This demonstrates how to show suggestions on a text field using just JavaScript with Appcelerator Titanium.
    *
    * You will need to download four images to get this to work:
    *
    * 1) http://dl.dropbox.com/u/16441391/Suggest/bg.png
    * 2) http://dl.dropbox.com/u/16441391/Suggest/[email protected]
    * 3) http://dl.dropbox.com/u/16441391/Suggest/separator.png
    * 4) http://dl.dropbox.com/u/16441391/Suggest/[email protected]
    *
    * I suggest placing them in "images/suggest/", but you can configure where they go in the "config" method.
    *
    * By Dawson Toth, Appcelerator Inc.
    */

    // STEP 1: Include our JavaScript module.
    var suggest = require('suggest');
    suggest.config({
    imageDirectory: 'images/suggest/'
    });

    var win = Ti.UI.createWindow({
    layout: 'vertical', backgroundColor: '#fff'
    });

    // STEP 2: We'll define four text field in a loop, down below.
    var fields = [
    'To', 'From', 'CC', 'BCC'
    ];

    var customerEngineers = [ 'Dawson Toth', 'Pedro Enrique', 'Jon Alter', 'Alan Leard', 'Rick Blalock', 'Matthew Congrove' ];
    function findCustomerEngineers(evt) {
    var results = [];
    // No text in the text field? That's fine, we won't show any suggestions.
    if (evt.value.length == 0)
    return results;
    var searchFor = evt.value.toLowerCase();
    for (var p in customerEngineers) {
    if (customerEngineers[p].toLowerCase().split(searchFor).length > 1) {
    results.push(customerEngineers[p]);
    if (results.length > 3) {
    break;
    }
    }
    }
    return results;
    }

    for (var f in fields) {
    // STEP 3: We need to wrap the text field in a view; the suggestions will be added to the bottom of this.
    var wrapper = Ti.UI.createView({
    top: 0, left: 20, right: 20, height: 50
    });
    // STEP 4: Create the text field itself, and add it to the wrapper.
    var text = Ti.UI.createTextField({
    top: 20, left: 0, right: 0, height: 30,
    hintText: fields[f], autocapitalization: 0,
    borderStyle: Ti.UI.INPUT_BORDERSTYLE_ROUNDED
    });
    wrapper.add(text);
    // STEP 5: Call the "bind" method of our module.
    suggest.bind({
    wrapper: wrapper,
    control: text,
    dataSource: findCustomerEngineers
    });

    // STEP 6: Add the wrapper to the window, open the window, and we're done!
    win.add(wrapper);
    }

    win.open();
    163 changes: 163 additions & 0 deletions suggest.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,163 @@
    /**
    * This module lets you set up suggestions on text fields.
    *
    * You will need to download four images to get this to work:
    *
    * 1) http://dl.dropbox.com/u/16441391/Suggest/bg.png
    * 2) http://dl.dropbox.com/u/16441391/Suggest/[email protected]
    * 3) http://dl.dropbox.com/u/16441391/Suggest/separator.png
    * 4) http://dl.dropbox.com/u/16441391/Suggest/[email protected]
    *
    * I suggest placing them in "images/suggest/", but you can configure where they go in the "config" method.
    */

    // Instance Variables
    var baseImageDirectory;

    /**
    * Allows you to customize the behavior of the module.
    * @param args A dictionary with the following keys: imageDirectory, controls where the images are loaded from.
    */
    exports.config = function(args) {
    baseImageDirectory = args.imageDirectory || '';
    };

    /**
    * Sets up a text field for showing suggestions.
    * @param args A dictionary with the following keys: control, wrapper, and dataSource.
    */
    exports.bind = function(args) {
    if (!args.control || !args.wrapper || !args.dataSource) {
    throw 'Not all required parameters provided: "control", "wrapper", and "dataSource" must be specified!';
    }

    var visible = false, changed = false, latestValue = '';

    var suggestions = Ti.UI.createView({
    backgroundImage: baseImageDirectory + 'bg.png',
    backgroundLeftCap: 9,
    height: 43,
    top: args.wrapper.size.height - 5,
    left: 20, right: 20
    });

    var container = Ti.UI.createScrollView({
    left: 5, right: 5, height: 43, width: 'auto',
    layout: 'horizontal', contentWidth: 'auto'
    });
    suggestions.add(container);

    function clickListener(evt) {
    if (evt.source.isSuggestion) {
    args.control.value = latestValue = evt.source.text;
    visible = changed = false;
    hide();
    }
    }

    var children = [];

    var originalHeight = args.wrapper.size.height || 0;

    function show() {
    args.wrapper.height = originalHeight + suggestions.height - 5;
    args.wrapper.add(suggestions);
    }

    function hide() {
    args.wrapper.remove(suggestions);
    args.wrapper.height = originalHeight;
    }

    function sync() {
    if (changed) {
    changed = false;
    var data = args.dataSource({ source: args.control, value: latestValue });
    if (!data || data.length == 0) {
    if (visible) {
    visible = false;
    hide();
    }
    }
    else {
    if (!visible) {
    visible = true;
    show();
    }
    for (var c = 0; c < children.length; c++) {
    if (children[c].isSuggestion) {
    children[c].removeEventListener('click', clickListener);
    }
    container.remove(children[c]);
    }
    children = [];
    for (var i = 0; i < data.length; i++) {
    var child = Ti.UI.createLabel({
    isSuggestion: true,
    text: data[i], font: { fontSize: 14 },
    color: '#fff', shadowColor: '#000', shadowOffset: { x: 0, y: -1 },
    width: 'auto', height: 14, top: 13, bottom: 16, left: 15, right: 15
    });
    child.addEventListener('click', clickListener);
    children.push(child);
    container.add(child);
    if (i != data.length - 1) {
    var separator = Ti.UI.createView({
    backgroundImage: baseImageDirectory + 'separator.png',
    width: 2, height: 36, top: 2
    });
    children.push(separator);
    container.add(separator);
    }
    }
    }
    }
    }

    args.control.onChange = function(evt) {
    if (evt.source.value != latestValue) {
    latestValue = evt.source.value;
    changed = true;
    }
    };

    args.control.onFocus = function() {
    args.control.onChangeIntervalID = setInterval(sync, 1000);
    args.control.addEventListener('change', args.control.onChange);
    // force a refresh
    changed = true;
    if (visible) {
    show();
    }
    };
    args.control.addEventListener('focus', args.control.onFocus);

    args.control.onBlur = function() {
    args.control.onChangeIntervalID = setInterval(sync, 1000);
    args.control.addEventListener('change', args.control.onChange);
    if (visible) {
    hide();
    }
    };
    args.control.addEventListener('blur', args.control.onBlur);
    };

    /**
    * Stops a text field from showing suggestions.
    * @param args A dictionary with the following keys: control.
    */
    exports.unbind = function(args) {
    if (!args.control) {
    throw 'Not all required parameters provided: "control" must be specified!';
    }
    clearInterval(args.control.onChangeIntervalID);
    args.control.fireEvent('blur');
    args.control.removeEventListener('focus', args.control.onFocus);
    args.control.removeEventListener('blur', args.control.onBlur);
    args.control.removeEventListener('change', args.control.onChange);
    args.control.onChange
    = args.control.onFocus
    = args.control.onBlur
    = args.control.onChangeIntervalID
    = null;
    };