(function($) { $(document).ready(function(){ var match_count = 0, match_page_size=5, match_offset = 0, cobalt_visible = false, cobalt_out_visible = false, matches = [], match_idx = 0, handler_idx = 0, current_text, current_action_text='', handler, item, actions, match_count=0, plugins = {}, plugin_names=[], catalogs = {}, global_handlers = [], handlers = {}, update_queue = [], required_updates = []; var log_error = function(message, err) { if (typeof(window.console) != 'undefined') { console.error(message); if(err) { console.log(err); } } }; var log_msg = function(message) { if (typeof(window.console) != 'undefined') { console.log(message); } }; var db = null; // Initialize database if (!db && typeof(openDatabase)=='function') { try { db = openDatabase('cobalt', '1.0', 'Cobalt Database', 204800); } catch (err) { if (err.name === 'SECURITY_ERR') { log_msg(Drupal.t('Failed to open database using the Web SQL Database API due to a security-related error.')); } else { log_msg(Drupal.t('Failed to open database using the Web SQL Database API.')); } } } if (!db) { log_error(Drupal.t('Could not open a client-side database. Cobalt requires a browser that implements the Web SQL Database API.')); return; } var nullDataHandler = function(transaction, results) { }; var current_state = function() { if (typeof(Drupal.settings.cobalt) != 'undefined' && typeof(Drupal.settings.cobalt.state) != 'undefined') { return Drupal.settings.cobalt.state; } else { return 0; } }; // Initialize Cobalt var cobalt = { 'dbErrorHandler': function(transaction, error) { $(document).trigger('cobalt-db-error'); if (window.console) { console.error(error.message+' (Code: '+error.code+')'); } return false; }, 'registerPlugin': function(name, plugin) { plugin_names.push(name); plugins[name] = plugin; if(typeof(plugin['catalogs'])!='undefined') { for(var c_name in plugin.catalogs) { catalogs[c_name] = plugin.catalogs[c_name]; } } if(typeof(plugin['handlers'])!='undefined') { var h_len = plugin.handlers.length; for(var i=0; i' + item.name + '' + Drupal.t('text') + ': ' + text + '
' + Drupal.t('id') + ': ' + item.id + '
' + Drupal.t('catalog') + ': ' + item.catalog + '
' + Drupal.t('class') + ': ' + item.data_class + '
' + Drupal.t('data') + ': ' + item.data + '
' + Drupal.t('weight') + ': ' + item.weight); } }, { 'id': 'cobalt_abbrev', 'name': Drupal.t('Assign shortcut'), 'handler': function(text, item) { cobalt.actionCandidates(item, function(cand) { var out = $('

' + item.name + '

' + Drupal.t('The keys !input should trigger the action:', {'!input': ''}) + '
' + '

'); var actions = $(out).find('.action-select'); var key_combo = $(out).find('.key-combo').css('width',50); var cand_count = cand.length; for(var i=0; i' + cand[i].name + ''); } $(out).find('button.ok').bind('click',function(){ toggle_output('hide'); cobalt.addKeyBinding(key_combo.val(), item.catalog, item.id, actions.val()); }); cobalt.showHtml(out); key_combo.focus(); }); } } ] }); db.transaction(function (transaction) { transaction.executeSql('CREATE TABLE IF NOT EXISTS versions(' + 'name TEXT NOT NULL, version INTEGER NOT NULL DEFAULT 0, ' + 'CONSTRAINT pk_versions PRIMARY KEY(name));', [], nullDataHandler,cobalt.dbErrorHandler); transaction.executeSql('CREATE TABLE IF NOT EXISTS entries(' + 'catalog TEXT NOT NULL, id TEXT NOT NULL, name TEXT NOT NULL, extra TEXT DEFAULT "", data TEXT NOT NULL DEFAULT "", data_class TEXT NOT NULL, ' + 'state INTEGER NOT NULL DEFAULT 0, active INTEGER NOT NULL DEFAULT 1, ' + 'CONSTRAINT pk_entries PRIMARY KEY(catalog, id));', [], nullDataHandler,cobalt.dbErrorHandler); transaction.executeSql('DELETE FROM entries WHERE catalog=?', ['*temporary*'], nullDataHandler, cobalt.dbErrorHandler); transaction.executeSql('CREATE TABLE IF NOT EXISTS key_bindings(' + 'binding TEXT NOT NULL, catalog TEXT NOT NULL, id TEXT NOT NULL, handler TEXT NOT NULL, ' + 'state INTEGER NOT NULL DEFAULT 0, active INTEGER NOT NULL DEFAULT 1, ' + 'CONSTRAINT pk_bindings PRIMARY KEY(binding, state));', [], nullDataHandler,cobalt.dbErrorHandler); transaction.executeSql('CREATE TABLE IF NOT EXISTS usage_data(catalog TEXT NOT NULL, id TEXT NOT NULL, ' + 'weight INTEGER NOT NULL DEFAULT 0, abbreviation TEXT NOT NULL DEFAULT "", last INTEGER, ' + 'CONSTRAINT pk_usage_data PRIMARY KEY(catalog, id));', [], nullDataHandler,cobalt.dbErrorHandler); transaction.executeSql('CREATE TABLE IF NOT EXISTS handler_usage_data(catalog TEXT NOT NULL, id TEXT NOT NULL, ' + 'handler TEXT NOT NULL, weight INTEGER NOT NULL DEFAULT 0,' + 'CONSTRAINT pk_handler_usage_data PRIMARY KEY(catalog, id, handler));', [], nullDataHandler,cobalt.dbErrorHandler); transaction.executeSql('CREATE INDEX IF NOT EXISTS idx_entries_catalog ON entries(catalog);', [], nullDataHandler,cobalt.dbErrorHandler); transaction.executeSql('CREATE INDEX IF NOT EXISTS idx_entries_abbreviation ON usage_data(abbreviation);', [], nullDataHandler,cobalt.dbErrorHandler); transaction.executeSql('CREATE INDEX IF NOT EXISTS idx_entries_active ON entries(active DESC);', [], nullDataHandler,cobalt.dbErrorHandler); transaction.executeSql('CREATE INDEX IF NOT EXISTS idx_entries_weight ON usage_data(weight DESC);', [], nullDataHandler,cobalt.dbErrorHandler); transaction.executeSql('CREATE TABLE IF NOT EXISTS catalogs(' + 'name TEXT NOT NULL PRIMARY KEY, updated INTEGER NOT NULL DEFAULT 0, state INTEGER NOT NULL DEFAULT 0, active INTEGER NOT NULL DEFAULT 1, uninstall INTEGER NOT NULL DEFAULT 0);', [], nullDataHandler,cobalt.dbErrorHandler); }); var cobalt_output = $('
').appendTo('body').hide(); var bind_key = function (binding, catalog, id, handler) { $(document).bind('keydown', binding, function(){ cobalt.loadEntry(catalog, id, function(item) { if (item) { cobalt.actionCandidates(item, function(cand){ var cand_count = cand.length; for(var i=0; i').html(title).appendTo(cobalt_ac); } matches = results.rows; //Only count for paging when we're not already offsetting the result and //have a match count equal to the page size if (match_offset) { update_pager(match_count); } else if (matches.length==match_page_size) { var like_expr = '%' + current_text + '%'; db.transaction(function (transaction) { transaction.executeSql("SELECT COUNT(*) as match_count FROM entries AS e " + "LEFT OUTER JOIN usage_data AS u ON (e.catalog=u.catalog AND e.id=u.id) " + "WHERE e.active=1 AND (u.abbreviation = ? OR e.name LIKE ? OR e.extra LIKE ?);", [ current_text, like_expr, like_expr], function(transaction, results) { if (results.rows.length) { match_count = results.rows.item(0).match_count; update_pager(match_count); } },cobalt.dbErrorHandler); }); } else { update_pager(matches.length); } cobalt_ac.show(); } ac_select(0); }; var update_pager = function(new_match_count) { match_count = new_match_count; cobalt_paging.hide().empty(); var page_count = Math.min(Math.ceil(match_count/match_page_size),25); for (var i=0; i ').appendTo(cobalt_paging); if (i==match_offset/match_page_size) { p.attr('class','current'); } } cobalt_paging.show(); }; var ac_page = function(new_offset) { if (new_offset>=match_count || new_offset<0) { return; } match_offset = new_offset; lookup(true); }; var ac_select = function(idx) { if (idx<0 || idx >= matches.length) { item = null; return; } item = matches.item(idx); item.information = JSON.parse(item.data); var old_item = matches.item(match_idx); $('#cobalt .ac-opt-' + match_idx).removeClass('active'); match_idx = idx; // Generate path specific classes that can be used for icons. // Matches the classes used in the Rubik admin theme. var classes = ''; if (item.data_class === 'url_data') { var path = JSON.parse(item.data); if (typeof(path) == 'object') { path = path.path; } classes = []; var args = path.split('/'); while(args.length) { classes[args.length - 1] = 'path-' + args.join('-'); args.pop(); } classes = classes.join(' '); }; $('#cobalt .left .inner').attr('class','inner cobalt-item-' + item.data_class + ' ' + classes); $('#cobalt .left label').text(item.name).show(); $('#cobalt .ac-opt-' + match_idx).addClass('active'); action_candidates(item, function(candidates){ actions = candidates; set_handler(0, true); }); }; var action_candidates = function(item, callback) { var candidates = [], gcount, i, j, cls_count, rc, cc, item, add_applicable = function(handler) { // Check if the handler matches the current_action_text (from closure), // or run the handlers own applicability check if it has one. if ((current_action_text=='' || handler['name'].toLowerCase().indexOf(current_action_text)!=-1) && (!handler['applicable'] || handler.applicable(current_text, item))) { candidates.push(handler); } }; // Add handlers for the items data class that match the current_action_text if (typeof(handlers[item.data_class])!='undefined') { cls_count = handlers[item.data_class].length; for (i=0; i').html(actions[i]['name']).appendTo(cobalt_actions); } } if (idx<0 || idx >= actions.length) { return; } $('#cobalt .action-opt-' + handler_idx).removeClass('active'); $('#cobalt .action-opt-' + idx).addClass('active'); handler_idx = idx; var new_handler = actions[idx]; handler = new_handler; if (handler) { var newClass = handler_class(); $('#cobalt .right .inner').attr('class','inner cobalt-action-' + newClass); $('#cobalt .right label').text(handler.name).show(); } }; var run_handler = function() { if (item && typeof(handler['handler']) == 'function') { var text = $.trim(cobalt_input.val()); register_use(text, item, handler); try { handler.handler(text, item); } catch(err) { log_error(Drupal.t("The handler threw a exception"), err); } } hide(); }; var get_last_entry = function() { db.transaction(function (transaction) { transaction.executeSql("SELECT e.*, u.weight FROM entries AS e INNER JOIN usage_data AS u ON u.catalog = e.catalog AND u.id = e.id WHERE last = 1;", [], function(transaction, results) { lookup_finished(transaction, results); }, cobalt.dbErrorHandler); }); }; var toggle = function(arg) { if (cobalt_visible) { cb.hide(); cobalt_visible = false; } else if (arg!='hide') { toggle_output('hide'); cobalt_input.val($.trim(cobalt_input.val())); cb.show(); cobalt_visible = true; setTimeout(function(){ cobalt_input.focus(); cobalt_input.select(); }, 100); } }; var hide = function() { toggle('hide'); }; var init = function() { $(document).trigger('cobalt-init', cobalt); // Load key bindings db.transaction(function (transaction) { transaction.executeSql('SELECT binding, catalog, id, handler FROM key_bindings', [], function(transaction, results) { for (var i=0; i now) { update_queue.push(inf); update_loop(); } else { if (catalog['update']) { catalog.update(inf.updated, function(enqueue){ var now = new Date().getTime(); cobalt.catalogUpdated(inf.name); if (enqueue) { update_queue.push({'name': inf.name, 'updated': now }); } update_counter++; update_loop(); }); } else { update_loop(); } } } },update_counter?1000:100); }; update_loop(); },cobalt.dbErrorHandler); }); $(document).trigger('cobalt-post-init', cobalt); cb.bind('click', function(e){ return false; }); $([cobalt_input[0], cobalt_h_input[0]]).bind('keydown', 'esc', function(){ toggle('hide'); toggle_output('hide'); return false; }). bind('keydown', 'return', function(){ run_handler(); return false; }); cobalt_input.bind('keydown', 'up', function(){ ac_select(match_idx-1); return false; }). bind('keydown', 'down', function(){ ac_select(match_idx+1); return false; }). bind('keydown', 'Alt+left', function(){ ac_page(match_offset-match_page_size); return false; }). bind('keydown', 'Alt+right', function(){ ac_page(match_offset+match_page_size); return false; }). bind('keyup', function(){ keypress_reaction(); return false; }). bind('focus', function() { if (matches && matches.length) { cobalt_ac.show(); cobalt_paging.show(); } cobalt_actions.hide(); }); cobalt_h_input.bind('keydown', 'up', function(){ set_handler(handler_idx-1); return false; }). bind('keydown', 'down', function(){ set_handler(handler_idx+1); return false; }). bind('keyup', function(){ action_keypress_reaction(); return false; }). bind('focus', function(){ cobalt_paging.hide(); cobalt_ac.hide(); cobalt_actions.show(); }); cobalt_output.bind('click', function(e){ return false; }); $('.cell.left', cb).bind('click', function(){ cobalt_input[0].focus(); }); $('.cell.right', cb).bind('click', function(){ cobalt_h_input[0].focus(); }); $(document).bind('click', function(){ toggle('hide'); toggle_output('hide'); }); if (Drupal && Drupal.settings && Drupal.settings.cobalt) { var bindings = Drupal.settings.cobalt.bindings, bind_count = bindings.length, i; for (i=0; i'+ '
'+ '
'+ '
        ').appendTo('body').hide(); var cobalt_input = $('#cobalt-input'); var cobalt_h_input = $('#cobalt-handler-input'); var cobalt_ac = $('#cobalt .cobalt-autocomplete'); var cobalt_actions = $('#cobalt .cobalt-actions'); var cobalt_paging = $('#cobalt .cobalt-paging'); $('#cobalt .right label').hide(); // Check if we need to update anything before initializing db.transaction(function (transaction) { transaction.executeSql('SELECT name, version FROM versions', [], function(transaction, results) { var v = {}; for (var i=0; i' + Drupal.t('Update required') + '' + '

        ' + Drupal.t('Cobalt must be updated before it can be used') + '

        ' + '

        ' + Drupal.t('Click here to update') + '

        '); }; $(document).bind('click', function(){ toggle_output('hide'); }); if (Drupal && Drupal.settings && Drupal.settings.cobalt) { var bindings = Drupal.settings.cobalt.bindings, bind_count = bindings.length, i; for (i=0; i