//----------------------------------------------------------------------------1// Copyright (C) 2012 The IPython Development Team2//3// Distributed under the terms of the BSD License. The full license is in4// the file COPYING, distributed as part of this software.5//----------------------------------------------------------------------------67//============================================================================8// CellToolbar9//============================================================================101112/**13* A Module to control the per-cell toolbar.14* @module IPython15* @namespace IPython16* @submodule CellToolbar17*/18var IPython = (function (IPython) {19"use strict";2021/**22* @constructor23* @class CellToolbar24* @param {The cell to attach the metadata UI to} cell25*/26var CellToolbar = function (cell) {27CellToolbar._instances.push(this);28this.cell = cell;29this.create_element();30this.rebuild();31return this;32};333435CellToolbar.prototype.create_element = function () {36this.inner_element = $('<div/>').addClass('celltoolbar')37this.element = $('<div/>').addClass('ctb_hideshow')38.append(this.inner_element);39this.show();40};414243// The default css style for the outer celltoolbar div44// (ctb_hideshow) is display: none.45// To show the cell toolbar, *both* of the following conditions must be met:46// - A parent container has class `ctb_global_show`47// - The celltoolbar has the class `ctb_show`48// This allows global show/hide, as well as per-cell show/hide.4950CellToolbar.global_hide = function () {51$('body').removeClass('ctb_global_show');52};535455CellToolbar.global_show = function () {56$('body').addClass('ctb_global_show');57};585960CellToolbar.prototype.hide = function () {61this.element.removeClass('ctb_show');62};636465CellToolbar.prototype.show = function () {66this.element.addClass('ctb_show');67};686970/**71* Class variable that should contain a dict of all available callback72* we need to think of wether or not we allow nested namespace73* @property _callback_dict74* @private75* @static76* @type Dict77*/78CellToolbar._callback_dict = {};798081/**82* Class variable that should contain the reverse order list of the button83* to add to the toolbar of each cell84* @property _ui_controls_list85* @private86* @static87* @type List88*/89CellToolbar._ui_controls_list = [];909192/**93* Class variable that should contain the CellToolbar instances for each94* cell of the notebook95*96* @private97* @property _instances98* @static99* @type List100*/101CellToolbar._instances = [];102103104/**105* keep a list of all the available presets for the toolbar106* @private107* @property _presets108* @static109* @type Dict110*/111CellToolbar._presets = {};112113114// this is by design not a prototype.115/**116* Register a callback to create an UI element in a cell toolbar.117* @method register_callback118* @param name {String} name to use to refer to the callback. It is advised to use a prefix with the name119* for easier sorting and avoid collision120* @param callback {function(div, cell)} callback that will be called to generate the ui element121*122*123* The callback will receive the following element :124*125* * a div in which to add element.126* * the cell it is responsible from127*128* @example129*130* Example that create callback for a button that toggle between `true` and `false` label,131* with the metadata under the key 'foo' to reflect the status of the button.132*133* // first param reference to a DOM div134* // second param reference to the cell.135* var toggle = function(div, cell) {136* var button_container = $(div)137*138* // let's create a button that show the current value of the metadata139* var button = $('<div/>').button({label:String(cell.metadata.foo)});140*141* // On click, change the metadata value and update the button label142* button.click(function(){143* var v = cell.metadata.foo;144* cell.metadata.foo = !v;145* button.button("option", "label", String(!v));146* })147*148* // add the button to the DOM div.149* button_container.append(button);150* }151*152* // now we register the callback under the name `foo` to give the153* // user the ability to use it later154* CellToolbar.register_callback('foo', toggle);155*/156CellToolbar.register_callback = function(name, callback){157// Overwrite if it already exists.158CellToolbar._callback_dict[name] = callback;159};160161162/**163* Register a preset of UI element in a cell toolbar.164* Not supported Yet.165* @method register_preset166* @param name {String} name to use to refer to the preset. It is advised to use a prefix with the name167* for easier sorting and avoid collision168* @param preset_list {List of String} reverse order of the button in the toolbar. Each String of the list169* should correspond to a name of a registerd callback.170*171* @private172* @example173*174* CellToolbar.register_callback('foo.c1', function(div, cell){...});175* CellToolbar.register_callback('foo.c2', function(div, cell){...});176* CellToolbar.register_callback('foo.c3', function(div, cell){...});177* CellToolbar.register_callback('foo.c4', function(div, cell){...});178* CellToolbar.register_callback('foo.c5', function(div, cell){...});179*180* CellToolbar.register_preset('foo.foo_preset1', ['foo.c1', 'foo.c2', 'foo.c5'])181* CellToolbar.register_preset('foo.foo_preset2', ['foo.c4', 'foo.c5'])182*/183CellToolbar.register_preset = function(name, preset_list) {184CellToolbar._presets[name] = preset_list;185$([IPython.events]).trigger('preset_added.CellToolbar', {name: name});186// When "register_callback" is called by a custom extension, it may be executed after notebook is loaded.187// In that case, activate the preset if needed.188if (IPython.notebook && IPython.notebook.metadata && IPython.notebook.metadata.celltoolbar === name)189this.activate_preset(name);190};191192193/**194* List the names of the presets that are currently registered.195*196* @method list_presets197* @static198*/199CellToolbar.list_presets = function() {200var keys = [];201for (var k in CellToolbar._presets) {202keys.push(k);203}204return keys;205};206207208/**209* Activate an UI preset from `register_preset`210*211* This does not update the selection UI.212*213* @method activate_preset214* @param preset_name {String} string corresponding to the preset name215*216* @static217* @private218* @example219*220* CellToolbar.activate_preset('foo.foo_preset1');221*/222CellToolbar.activate_preset = function(preset_name){223var preset = CellToolbar._presets[preset_name];224225if(preset !== undefined){226CellToolbar._ui_controls_list = preset;227CellToolbar.rebuild_all();228}229230$([IPython.events]).trigger('preset_activated.CellToolbar', {name: preset_name});231};232233234/**235* This should be called on the class and not on a instance as it will trigger236* rebuild of all the instances.237* @method rebuild_all238* @static239*240*/241CellToolbar.rebuild_all = function(){242for(var i=0; i < CellToolbar._instances.length; i++){243CellToolbar._instances[i].rebuild();244}245};246247/**248* Rebuild all the button on the toolbar to update its state.249* @method rebuild250*/251CellToolbar.prototype.rebuild = function(){252// strip evrything from the div253// which is probably inner_element254// or this.element.255this.inner_element.empty();256this.show();257258var callbacks = CellToolbar._callback_dict;259var preset = CellToolbar._ui_controls_list;260// Yes we iterate on the class variable, not the instance one.261for (var i=0; i < preset.length; i++) {262var key = preset[i];263var callback = callbacks[key];264if (!callback) continue;265266var local_div = $('<div/>').addClass('button_container');267try {268callback(local_div, this.cell, this);269} catch (e) {270console.log("Error in cell toolbar callback " + key, e);271continue;272}273// only append if callback succeeded.274this.inner_element.append(local_div);275}276};277278279/**280*/281CellToolbar.utils = {};282283284/**285* A utility function to generate bindings between a checkbox and cell/metadata286* @method utils.checkbox_ui_generator287* @static288*289* @param name {string} Label in front of the checkbox290* @param setter {function( cell, newValue )}291* A setter method to set the newValue292* @param getter {function( cell )}293* A getter methods which return the current value.294*295* @return callback {function( div, cell )} Callback to be passed to `register_callback`296*297* @example298*299* An exmple that bind the subkey `slideshow.isSectionStart` to a checkbox with a `New Slide` label300*301* var newSlide = CellToolbar.utils.checkbox_ui_generator('New Slide',302* // setter303* function(cell, value){304* // we check that the slideshow namespace exist and create it if needed305* if (cell.metadata.slideshow == undefined){cell.metadata.slideshow = {}}306* // set the value307* cell.metadata.slideshow.isSectionStart = value308* },309* //geter310* function(cell){ var ns = cell.metadata.slideshow;311* // if the slideshow namespace does not exist return `undefined`312* // (will be interpreted as `false` by checkbox) otherwise313* // return the value314* return (ns == undefined)? undefined: ns.isSectionStart315* }316* );317*318* CellToolbar.register_callback('newSlide', newSlide);319*320*/321CellToolbar.utils.checkbox_ui_generator = function(name, setter, getter){322return function(div, cell, celltoolbar) {323var button_container = $(div);324325var chkb = $('<input/>').attr('type', 'checkbox');326var lbl = $('<label/>').append($('<span/>').text(name));327lbl.append(chkb);328chkb.attr("checked", getter(cell));329330chkb.click(function(){331var v = getter(cell);332setter(cell, !v);333chkb.attr("checked", !v);334});335button_container.append($('<div/>').append(lbl));336};337};338339340/**341* A utility function to generate bindings between a dropdown list cell342* @method utils.select_ui_generator343* @static344*345* @param list_list {list of sublist} List of sublist of metadata value and name in the dropdown list.346* subslit shoud contain 2 element each, first a string that woul be displayed in the dropdown list,347* and second the corresponding value to be passed to setter/return by getter. the corresponding value348* should not be "undefined" or behavior can be unexpected.349* @param setter {function( cell, newValue )}350* A setter method to set the newValue351* @param getter {function( cell )}352* A getter methods which return the current value of the metadata.353* @param [label=""] {String} optionnal label for the dropdown menu354*355* @return callback {function( div, cell )} Callback to be passed to `register_callback`356*357* @example358*359* var select_type = CellToolbar.utils.select_ui_generator([360* ["<None>" , "None" ],361* ["Header Slide" , "header_slide" ],362* ["Slide" , "slide" ],363* ["Fragment" , "fragment" ],364* ["Skip" , "skip" ],365* ],366* // setter367* function(cell, value){368* // we check that the slideshow namespace exist and create it if needed369* if (cell.metadata.slideshow == undefined){cell.metadata.slideshow = {}}370* // set the value371* cell.metadata.slideshow.slide_type = value372* },373* //geter374* function(cell){ var ns = cell.metadata.slideshow;375* // if the slideshow namespace does not exist return `undefined`376* // (will be interpreted as `false` by checkbox) otherwise377* // return the value378* return (ns == undefined)? undefined: ns.slide_type379* }380* CellToolbar.register_callback('slideshow.select', select_type);381*382*/383CellToolbar.utils.select_ui_generator = function(list_list, setter, getter, label, cell_types){384label = label || "";385return function(div, cell, celltoolbar) {386var button_container = $(div);387var lbl = $("<label/>").append($('<span/>').text(label));388var select = $('<select/>').addClass('ui-widget ui-widget-content');389for(var i=0; i < list_list.length; i++){390var opt = $('<option/>')391.attr('value', list_list[i][1])392.text(list_list[i][0]);393select.append(opt);394}395select.val(getter(cell));396select.change(function(){397setter(cell, select.val());398});399button_container.append($('<div/>').append(lbl).append(select));400if (cell_types && cell_types.indexOf(cell.cell_type) == -1) {401celltoolbar.hide();402} else {403celltoolbar.show();404}405406};407};408409410IPython.CellToolbar = CellToolbar;411412return IPython;413}(IPython));414415416