define([
'base/js/namespace',
'jquery',
'base/js/utils',
'codemirror/lib/codemirror',
'codemirror/addon/edit/matchbrackets',
'codemirror/addon/edit/closebrackets',
'codemirror/addon/comment/comment'
], function(IPython, $, utils, CodeMirror, cm_match, cm_closeb, cm_comment) {
"use strict";
var overlayHack = CodeMirror.scrollbarModel.native.prototype.overlayHack;
CodeMirror.scrollbarModel.native.prototype.overlayHack = function () {
overlayHack.apply(this, arguments);
if (/Mac/.test(navigator.platform)) {
this.horiz.style.minHeight = "";
}
};
var Cell = function (options) {
options = options || {};
this.keyboard_manager = options.keyboard_manager;
this.events = options.events;
var config = utils.mergeopt(Cell, options.config);
this.placeholder = config.placeholder || '';
this.selected = false;
this.rendered = false;
this.mode = 'command';
var that = this;
this._metadata = {};
Object.defineProperty(this, 'metadata', {
get: function() { return that._metadata; },
set: function(value) {
that._metadata = value;
if (that.celltoolbar) {
that.celltoolbar.rebuild();
}
}
});
this.user_highlight = 'auto';
var _local_cm_config = {};
if(this.class_config){
_local_cm_config = this.class_config.get_sync('cm_config');
}
this.cm_config = utils.mergeopt({}, config.cm_config, _local_cm_config);
this.cell_id = utils.uuid();
this._options = config;
this.element = null;
this.cell_type = this.cell_type || null;
this.code_mirror = null;
this.create_element();
if (this.element !== null) {
this.element.data("cell", this);
this.bind_events();
this.init_classes();
}
};
Cell.options_default = {
cm_config : {
indentUnit : 4,
readOnly: false,
theme: "default",
extraKeys: {
"Cmd-Right":"goLineRight",
"End":"goLineRight",
"Cmd-Left":"goLineLeft"
}
}
};
if (utils.browser[0] == "Safari") {
Cell.options_default.cm_config.dragDrop = false;
}
Cell.prototype.create_element = function () {
};
Cell.prototype.init_classes = function () {
if (this.selected) {
this.element.addClass('selected');
} else {
this.element.addClass('unselected');
}
if (this.rendered) {
this.element.addClass('rendered');
} else {
this.element.addClass('unrendered');
}
};
Cell.prototype.bind_events = function () {
var that = this;
that.element.click(function (event) {
if (!that.selected) {
that.events.trigger('select.Cell', {'cell':that});
}
});
that.element.focusin(function (event) {
if (!that.selected) {
that.events.trigger('select.Cell', {'cell':that});
}
});
if (this.code_mirror) {
this.code_mirror.on("change", function(cm, change) {
that.events.trigger("set_dirty.Notebook", {value: true});
});
}
if (this.code_mirror) {
this.code_mirror.on('focus', function(cm, change) {
that.events.trigger('edit_mode.Cell', {cell: that});
});
}
if (this.code_mirror) {
this.code_mirror.on('blur', function(cm, change) {
that.events.trigger('command_mode.Cell', {cell: that});
});
}
this.element.dblclick(function () {
if (that.selected === false) {
this.events.trigger('select.Cell', {'cell':that});
}
var cont = that.unrender();
if (cont) {
that.focus_editor();
}
});
};
Cell.prototype.handle_codemirror_keyevent = function (editor, event) {
var shortcuts = this.keyboard_manager.edit_shortcuts;
var cur = editor.getCursor();
if((cur.line !== 0 || cur.ch !==0) && event.keyCode === 38){
event._ipkmIgnore = true;
}
var nLastLine = editor.lastLine();
if ((event.keyCode === 40) &&
((cur.line !== nLastLine) ||
(cur.ch !== editor.getLineHandle(nLastLine).text.length))
) {
event._ipkmIgnore = true;
}
if (shortcuts.handles(event)) {
return true;
}
return false;
};
Cell.prototype.typeset = function () {
utils.typeset(this.element);
};
Cell.prototype.select = function () {
if (!this.selected) {
this.element.addClass('selected');
this.element.removeClass('unselected');
this.selected = true;
return true;
} else {
return false;
}
};
Cell.prototype.unselect = function () {
if (this.selected) {
this.element.addClass('unselected');
this.element.removeClass('selected');
this.selected = false;
return true;
} else {
return false;
}
};
Cell.prototype.execute = function () {
return;
};
Cell.prototype.render = function () {
if (!this.rendered) {
this.element.addClass('rendered');
this.element.removeClass('unrendered');
this.rendered = true;
return true;
} else {
return false;
}
};
Cell.prototype.unrender = function () {
if (this.rendered) {
this.element.addClass('unrendered');
this.element.removeClass('rendered');
this.rendered = false;
return true;
} else {
return false;
}
};
Cell.prototype.handle_keyevent = function (editor, event) {
if (this.mode === 'command') {
return true;
} else if (this.mode === 'edit') {
return this.handle_codemirror_keyevent(editor, event);
}
};
Cell.prototype.at_top = function () {
var cm = this.code_mirror;
var cursor = cm.getCursor();
if (cursor.line === 0 && cursor.ch === 0) {
return true;
}
return false;
};
Cell.prototype.at_bottom = function () {
var cm = this.code_mirror;
var cursor = cm.getCursor();
if (cursor.line === (cm.lineCount()-1) && cursor.ch === cm.getLine(cursor.line).length) {
return true;
}
return false;
};
Cell.prototype.command_mode = function () {
if (this.mode !== 'command') {
this.mode = 'command';
return true;
} else {
return false;
}
};
Cell.prototype.edit_mode = function () {
if (this.mode !== 'edit') {
this.mode = 'edit';
return true;
} else {
return false;
}
};
Cell.prototype.ensure_focused = function() {
if(this.element !== document.activeElement && !this.code_mirror.hasFocus()){
this.focus_cell();
}
}
Cell.prototype.focus_cell = function () {
this.element.focus();
};
Cell.prototype.focus_editor = function () {
this.refresh();
this.code_mirror.focus();
};
Cell.prototype.refresh = function () {
if (this.code_mirror) {
this.code_mirror.refresh();
}
};
Cell.prototype.get_text = function () {
};
Cell.prototype.set_text = function (text) {
};
Cell.prototype.toJSON = function () {
var data = {};
data.metadata = JSON.parse(JSON.stringify(this.metadata));
data.cell_type = this.cell_type;
return data;
};
Cell.prototype.fromJSON = function (data) {
if (data.metadata !== undefined) {
this.metadata = data.metadata;
}
};
Cell.prototype.is_splittable = function () {
return this.is_deletable();
};
Cell.prototype.is_mergeable = function () {
return this.is_deletable();
};
Cell.prototype.is_deletable = function () {
if (this.metadata.deletable === false) {
return false;
}
return true;
};
Cell.prototype.get_pre_cursor = function () {
var cursor = this.code_mirror.getCursor();
var text = this.code_mirror.getRange({line:0, ch:0}, cursor);
text = text.replace(/^\n+/, '').replace(/\n+$/, '');
return text;
};
Cell.prototype.get_post_cursor = function () {
var cursor = this.code_mirror.getCursor();
var last_line_num = this.code_mirror.lineCount()-1;
var last_line_len = this.code_mirror.getLine(last_line_num).length;
var end = {line:last_line_num, ch:last_line_len};
var text = this.code_mirror.getRange(cursor, end);
text = text.replace(/^\n+/, '').replace(/\n+$/, '');
return text;
};
Cell.prototype.show_line_numbers = function (value) {
this.code_mirror.setOption('lineNumbers', value);
this.code_mirror.refresh();
};
Cell.prototype.toggle_line_numbers = function () {
var val = this.code_mirror.getOption('lineNumbers');
this.show_line_numbers(!val);
};
Cell.prototype.force_highlight = function(mode) {
this.user_highlight = mode;
this.auto_highlight();
};
Cell.prototype.auto_highlight = function () {
this._auto_highlight(this.class_config.get_sync('highlight_modes'));
};
Cell.prototype._auto_highlight = function (modes) {
var that = this;
var mode;
if( this.user_highlight !== undefined && this.user_highlight != 'auto' )
{
mode = this.user_highlight;
CodeMirror.autoLoadMode(this.code_mirror, mode);
this.code_mirror.setOption('mode', mode);
return;
}
var current_mode = this.code_mirror.getOption('mode', mode);
var first_line = this.code_mirror.getLine(0);
for(mode in modes) {
var regs = modes[mode].reg;
for(var i=0; i<regs.length; i++) {
var re = regs[i];
if(typeof(re) === 'string'){
re = new RegExp(re)
}
if(first_line.match(re) !== null) {
if(current_mode == mode){
return;
}
if (mode.search('magic_') !== 0) {
utils.requireCodeMirrorMode(mode, function (spec) {
that.code_mirror.setOption('mode', spec);
});
return;
}
var open = modes[mode].open || "%%";
var close = modes[mode].close || "%%end";
var magic_mode = mode;
mode = magic_mode.substr(6);
if(current_mode == magic_mode){
return;
}
utils.requireCodeMirrorMode(mode, function (spec) {
CodeMirror.defineMode(magic_mode, function(config) {
return CodeMirror.multiplexingMode(
CodeMirror.getMode(config, 'text/plain'),
{open: open, close: close,
mode: CodeMirror.getMode(config, spec),
delimStyle: "delimit"
}
);
});
that.code_mirror.setOption('mode', magic_mode);
});
return;
}
}
}
var default_mode;
try {
default_mode = this._options.cm_config.mode;
} catch(e) {
default_mode = 'text/plain';
}
if( current_mode === default_mode){
return;
}
this.code_mirror.setOption('mode', default_mode);
};
var UnrecognizedCell = function (options) {
Cell.apply(this, arguments);
this.cell_type = 'unrecognized';
this.celltoolbar = null;
this.data = {};
Object.seal(this);
};
UnrecognizedCell.prototype = Object.create(Cell.prototype);
UnrecognizedCell.prototype.is_mergeable = function () {
return false;
};
UnrecognizedCell.prototype.is_splittable = function () {
return false;
};
UnrecognizedCell.prototype.toJSON = function () {
return JSON.parse(JSON.stringify(this.data));
};
UnrecognizedCell.prototype.fromJSON = function (data) {
this.data = data;
if (data.metadata !== undefined) {
this.metadata = data.metadata;
} else {
data.metadata = this.metadata;
}
this.element.find('.inner_cell').find("a").text("Unrecognized cell type: " + data.cell_type);
};
UnrecognizedCell.prototype.create_element = function () {
Cell.prototype.create_element.apply(this, arguments);
var cell = this.element = $("<div>").addClass('cell unrecognized_cell');
cell.attr('tabindex','2');
var prompt = $('<div/>').addClass('prompt input_prompt');
cell.append(prompt);
var inner_cell = $('<div/>').addClass('inner_cell');
inner_cell.append(
$("<a>")
.attr("href", "#")
.text("Unrecognized cell type")
);
cell.append(inner_cell);
this.element = cell;
};
UnrecognizedCell.prototype.bind_events = function () {
Cell.prototype.bind_events.apply(this, arguments);
var cell = this;
this.element.find('.inner_cell').find("a").click(function () {
cell.events.trigger('unrecognized_cell.Cell', {cell: cell});
});
};
IPython.Cell = Cell;
return {
Cell: Cell,
UnrecognizedCell: UnrecognizedCell
};
});