etherpad-lite/src/static/js/pad_editbar.js

449 lines
13 KiB
JavaScript
Raw Normal View History

/**
2013-03-09 23:57:42 +01:00
* This code is mostly from the old Etherpad. Please help us to comment this code.
* This helps other people to understand this code better and helps them to improve it.
* TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
*/
2011-03-26 14:10:41 +01:00
/**
* Copyright 2009 Google Inc.
2011-07-07 19:59:34 +02:00
*
2011-03-26 14:10:41 +01:00
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
2011-07-07 19:59:34 +02:00
*
2011-03-26 14:10:41 +01:00
* http://www.apache.org/licenses/LICENSE-2.0
2011-07-07 19:59:34 +02:00
*
2011-03-26 14:10:41 +01:00
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var hooks = require('./pluginfw/hooks');
2012-03-07 02:27:03 +01:00
var padutils = require('./pad_utils').padutils;
var padeditor = require('./pad_editor').padeditor;
var padsavedrevs = require('./pad_savedrevs');
var ToolbarItem = function (element) {
this.$el = element;
};
ToolbarItem.prototype.getCommand = function () {
return this.$el.attr("data-key");
};
ToolbarItem.prototype.getValue = function () {
if (this.isSelect()) {
return this.$el.find("select").val();
}
};
ToolbarItem.prototype.setValue = function (val) {
if (this.isSelect()) {
return this.$el.find("select").val(val);
}
};
ToolbarItem.prototype.getType = function () {
return this.$el.attr("data-type");
};
ToolbarItem.prototype.isSelect = function () {
return this.getType() == "select";
};
ToolbarItem.prototype.isButton = function () {
return this.getType() == "button";
};
ToolbarItem.prototype.bind = function (callback) {
var self = this;
if (self.isButton()) {
self.$el.click(function (event) {
2015-03-25 16:49:41 +01:00
$(':focus').blur();
callback(self.getCommand(), self);
event.preventDefault();
});
}
else if (self.isSelect()) {
self.$el.find("select").change(function () {
callback(self.getCommand(), self);
});
}
};
2011-07-07 19:59:34 +02:00
var padeditbar = (function()
{
2011-03-26 14:10:41 +01:00
2011-07-07 19:59:34 +02:00
var syncAnimation = (function()
{
2011-03-26 14:10:41 +01:00
var SYNCING = -100;
var DONE = 100;
var state = DONE;
var fps = 25;
2011-07-07 19:59:34 +02:00
var step = 1 / fps;
2011-03-26 14:10:41 +01:00
var T_START = -0.5;
var T_FADE = 1.0;
var T_GONE = 1.5;
2011-07-07 19:59:34 +02:00
var animator = padutils.makeAnimationScheduler(function()
{
if (state == SYNCING || state == DONE)
{
2011-03-26 14:10:41 +01:00
return false;
}
2011-07-07 19:59:34 +02:00
else if (state >= T_GONE)
{
2011-03-26 14:10:41 +01:00
state = DONE;
$("#syncstatussyncing").css('display', 'none');
$("#syncstatusdone").css('display', 'none');
return false;
}
2011-07-07 19:59:34 +02:00
else if (state < 0)
{
2011-03-26 14:10:41 +01:00
state += step;
2011-07-07 19:59:34 +02:00
if (state >= 0)
{
2011-03-26 14:10:41 +01:00
$("#syncstatussyncing").css('display', 'none');
$("#syncstatusdone").css('display', 'block').css('opacity', 1);
}
return true;
}
2011-07-07 19:59:34 +02:00
else
{
2011-03-26 14:10:41 +01:00
state += step;
2011-07-07 19:59:34 +02:00
if (state >= T_FADE)
{
2011-03-26 14:10:41 +01:00
$("#syncstatusdone").css('opacity', (T_GONE - state) / (T_GONE - T_FADE));
}
return true;
}
2011-07-07 19:59:34 +02:00
}, step * 1000);
2011-03-26 14:10:41 +01:00
return {
2011-07-07 19:59:34 +02:00
syncing: function()
{
2011-03-26 14:10:41 +01:00
state = SYNCING;
$("#syncstatussyncing").css('display', 'block');
$("#syncstatusdone").css('display', 'none');
},
2011-07-07 19:59:34 +02:00
done: function()
{
2011-03-26 14:10:41 +01:00
state = T_START;
animator.scheduleAnimation();
}
};
}());
var self = {
init: function() {
var self = this;
self.dropdowns = [];
2014-11-06 16:43:21 +01:00
// Listen for resize events (sucks but needed as iFrame ace_inner has to be position absolute
// A CSS fix for this would be nice but I'm not sure how we'd do it.
$(window).resize(function(){
self.redrawHeight();
});
2011-03-26 14:10:41 +01:00
$("#editbar .editbarbutton").attr("unselectable", "on"); // for IE
$("#editbar").removeClass("disabledtoolbar").addClass("enabledtoolbar");
$("#editbar [data-key]").each(function () {
$(this).unbind("click");
(new ToolbarItem($(this))).bind(function (command, item) {
self.triggerCommand(command, item);
});
});
$('body:not(#editorcontainerbox)').on("keydown", function(evt){
bodyKeyEvent(evt);
});
2014-11-06 16:50:55 +01:00
$('#editbar').show();
2014-11-06 16:43:21 +01:00
this.redrawHeight();
registerDefaultCommands(self);
hooks.callAll("postToolbarInit", {
toolbar: self,
ace: padeditor.ace
});
2011-03-26 14:10:41 +01:00
},
2011-07-07 19:59:34 +02:00
isEnabled: function()
{
// return !$("#editbar").hasClass('disabledtoolbar');
return true;
2011-03-26 14:10:41 +01:00
},
2011-07-07 19:59:34 +02:00
disable: function()
{
2011-03-26 14:10:41 +01:00
$("#editbar").addClass('disabledtoolbar').removeClass("enabledtoolbar");
},
commands: {},
registerCommand: function (cmd, callback) {
this.commands[cmd] = callback;
return this;
},
2014-11-06 16:43:21 +01:00
redrawHeight: function(){
2014-11-29 01:54:06 +01:00
var editbarHeight = $('.menu_left').height() + 1 + "px";
var containerTop = $('.menu_left').height() + 6 + "px";
2014-11-06 16:43:21 +01:00
$('#editbar').css("height", editbarHeight);
2014-11-28 01:35:46 +01:00
2014-11-06 16:43:21 +01:00
$('#editorcontainer').css("top", containerTop);
2015-02-09 18:37:20 +01:00
2015-02-09 19:36:11 +01:00
// make sure pop ups are in the right place
if($('#editorcontainer').offset()){
$('.popup').css("top", $('#editorcontainer').offset().top + "px");
}
2015-02-09 19:36:11 +01:00
2015-02-09 18:37:20 +01:00
// If sticky chat is enabled..
if($('#options-stickychat').is(":checked")){
if($('#editorcontainer').offset()){
$('#chatbox').css("top", $('#editorcontainer').offset().top + "px");
}
};
2015-02-09 18:37:20 +01:00
// If chat and Users is enabled..
if($('#options-chatandusers').is(":checked")){
if($('#editorcontainer').offset()){
$('#users').css("top", $('#editorcontainer').offset().top + "px");
}
2015-02-09 18:37:20 +01:00
}
2014-11-06 16:43:21 +01:00
},
registerDropdownCommand: function (cmd, dropdown) {
dropdown = dropdown || cmd;
self.dropdowns.push(dropdown)
this.registerCommand(cmd, function () {
self.toggleDropDown(dropdown);
});
},
registerAceCommand: function (cmd, callback) {
this.registerCommand(cmd, function (cmd, ace) {
ace.callWithAce(function (ace) {
callback(cmd, ace);
}, cmd, true);
});
},
triggerCommand: function (cmd, item) {
if (self.isEnabled() && this.commands[cmd]) {
this.commands[cmd](cmd, padeditor.ace, item);
2011-03-26 14:10:41 +01:00
}
if(padeditor.ace) padeditor.ace.focus();
2011-03-26 14:10:41 +01:00
},
2012-07-13 08:24:02 +02:00
toggleDropDown: function(moduleName, cb)
2011-07-08 16:19:38 +02:00
{
// hide all modules and remove highlighting of all buttons
2011-07-08 16:19:38 +02:00
if(moduleName == "none")
{
var returned = false
for(var i=0;i<self.dropdowns.length;i++)
2011-07-08 16:19:38 +02:00
{
2011-08-21 20:07:35 +02:00
//skip the userlist
if(self.dropdowns[i] == "users")
2011-08-21 20:07:35 +02:00
continue;
2013-03-09 23:57:42 +01:00
var module = $("#" + self.dropdowns[i]);
2013-03-09 23:57:42 +01:00
2011-07-08 16:19:38 +02:00
if(module.css('display') != "none")
{
2014-10-03 17:35:48 +02:00
$("li[data-key=" + self.dropdowns[i] + "] > a").removeClass("selected");
module.slideUp("fast", cb);
returned = true;
2011-07-08 16:19:38 +02:00
}
}
if(!returned && cb) return cb();
2011-07-08 16:19:38 +02:00
}
2013-03-09 23:57:42 +01:00
else
2011-07-08 16:19:38 +02:00
{
// hide all modules that are not selected and remove highlighting
// respectively add highlighting to the corresponding button
for(var i=0;i<self.dropdowns.length;i++)
2011-07-08 16:19:38 +02:00
{
var module = $("#" + self.dropdowns[i]);
2013-03-09 23:57:42 +01:00
2011-07-08 16:19:38 +02:00
if(module.css('display') != "none")
{
2014-10-03 17:35:48 +02:00
$("li[data-key=" + self.dropdowns[i] + "] > a").removeClass("selected");
2011-07-08 16:19:38 +02:00
module.slideUp("fast");
}
else if(self.dropdowns[i]==moduleName)
2011-07-08 16:19:38 +02:00
{
2014-10-03 17:35:48 +02:00
$("li[data-key=" + self.dropdowns[i] + "] > a").addClass("selected");
module.slideDown("fast", cb);
2011-07-08 16:19:38 +02:00
}
}
}
},
2011-07-07 19:59:34 +02:00
setSyncStatus: function(status)
{
if (status == "syncing")
{
2011-03-26 14:10:41 +01:00
syncAnimation.syncing();
}
2011-07-07 19:59:34 +02:00
else if (status == "done")
{
2011-03-26 14:10:41 +01:00
syncAnimation.done();
}
2011-11-24 16:34:28 +01:00
},
setEmbedLinks: function()
{
if ($('#readonlyinput').is(':checked'))
{
var basePath = document.location.href.substring(0, document.location.href.indexOf("/p/"));
2012-04-23 16:20:55 +02:00
var readonlyLink = basePath + "/p/" + clientVars.readOnlyId;
$('#embedinput').val("<iframe name='embed_readonly' src='" + readonlyLink + "?showControls=true&showChat=true&showLineNumbers=true&useMonospaceFont=false' width=600 height=400></iframe>");
2011-11-24 16:34:28 +01:00
$('#linkinput').val(readonlyLink);
}
else
{
var padurl = window.location.href.split("?")[0];
$('#embedinput').val("<iframe name='embed_readwrite' src='" + padurl + "?showControls=true&showChat=true&showLineNumbers=true&useMonospaceFont=false' width=600 height=400></iframe>");
2011-11-24 16:34:28 +01:00
$('#linkinput').val(padurl);
}
2011-03-26 14:10:41 +01:00
}
};
2015-03-25 16:19:52 +01:00
var editbarPosition = 0;
function bodyKeyEvent(evt){
2015-03-26 15:26:21 +01:00
// If the event is Alt F9 or Escape & we're already in the editbar menu
// Send the users focus back to the pad
if((evt.keyCode === 120 && evt.altKey) || evt.keyCode === 27){
// If we're in the editbar already..
2015-03-26 15:26:21 +01:00
// Close any dropdowns we have open..
padeditbar.toggleDropDown("none");
// Shift focus away from any drop downs
2015-03-25 16:49:41 +01:00
$(':focus').blur(); // required to do not try to remove!
2015-03-26 15:26:21 +01:00
padeditor.ace.focus(); // Sends focus back to pad
// The above focus doesn't always work in FF, you have to hit enter afterwards
// This still needs fixing cake
2015-03-31 15:06:02 +02:00
evt.preventDefault();
}
// On arrow keys go to next/previous button item in editbar
if(evt.keyCode !== 39 && evt.keyCode !== 37) return;
2015-03-25 16:19:52 +01:00
// Get all the focusable items in the editbar
var focusItems = $('#editbar').find('button, select');
// On left arrow move to next button in editbar
if(evt.keyCode === 37){
2015-03-25 16:19:52 +01:00
editbarPosition--;
$(focusItems[editbarPosition]).focus()
}
// On right arrow move to next button in editbar
if(evt.keyCode === 39){
2015-03-25 16:19:52 +01:00
editbarPosition++;
$(focusItems[editbarPosition]).focus();
}
2015-03-25 16:19:52 +01:00
}
function aceAttributeCommand(cmd, ace) {
ace.ace_toggleAttributeOnSelection(cmd);
}
function registerDefaultCommands(toolbar) {
toolbar.registerDropdownCommand("showusers", "users");
toolbar.registerDropdownCommand("settings");
toolbar.registerDropdownCommand("connectivity");
2014-10-03 17:35:48 +02:00
toolbar.registerDropdownCommand("import_export");
toolbar.registerDropdownCommand("embed");
2015-03-26 00:30:17 +01:00
toolbar.registerCommand("settings", function () {
toolbar.toggleDropDown("settings", function(){
2015-03-26 00:30:17 +01:00
$('#options-stickychat').focus();
});
2015-03-26 00:30:17 +01:00
});
toolbar.registerCommand("import_export", function () {
toolbar.toggleDropDown("import_export", function(){
setTimeout(function(){
$('#importfileinput').focus();
}, 100);
});
});
toolbar.registerCommand("showusers", function () {
toolbar.toggleDropDown("users", function(){
$('#myusernameedit').focus();
});
});
toolbar.registerCommand("embed", function () {
toolbar.setEmbedLinks();
toolbar.toggleDropDown("embed", function(){
2015-03-26 00:30:17 +01:00
$('#linkinput').focus().select();
});
});
toolbar.registerCommand("savedRevision", function () {
padsavedrevs.saveNow();
});
toolbar.registerCommand("showTimeSlider", function () {
document.location = document.location.pathname+ '/timeslider';
});
toolbar.registerAceCommand("bold", aceAttributeCommand);
toolbar.registerAceCommand("italic", aceAttributeCommand);
toolbar.registerAceCommand("underline", aceAttributeCommand);
toolbar.registerAceCommand("strikethrough", aceAttributeCommand);
toolbar.registerAceCommand("undo", function (cmd, ace) {
ace.ace_doUndoRedo(cmd);
});
2014-03-30 13:05:51 +02:00
toolbar.registerAceCommand("redo", function (cmd, ace) {
ace.ace_doUndoRedo(cmd);
});
toolbar.registerAceCommand("insertunorderedlist", function (cmd, ace) {
ace.ace_doInsertUnorderedList();
});
toolbar.registerAceCommand("insertorderedlist", function (cmd, ace) {
ace.ace_doInsertOrderedList();
});
toolbar.registerAceCommand("indent", function (cmd, ace) {
if (!ace.ace_doIndentOutdent(false)) {
ace.ace_doInsertUnorderedList();
}
});
toolbar.registerAceCommand("outdent", function (cmd, ace) {
ace.ace_doIndentOutdent(true);
});
toolbar.registerAceCommand("clearauthorship", function (cmd, ace) {
if ((!(ace.ace_getRep().selStart && ace.ace_getRep().selEnd)) || ace.ace_isCaret()) {
if (window.confirm(html10n.get("pad.editbar.clearcolors"))) {
ace.ace_performDocumentApplyAttributesToCharRange(0, ace.ace_getRep().alltext.length, [
['author', '']
]);
}
}
else {
ace.ace_setAttributeOnSelection('author', '');
}
});
toolbar.registerCommand('timeslider_returnToPad', function(cmd) {
if( document.referrer.length > 0 && document.referrer.substring(document.referrer.lastIndexOf("/")-1, document.referrer.lastIndexOf("/")) === "p") {
document.location = document.referrer;
} else {
document.location = document.location.href.substring(0,document.location.href.lastIndexOf("/"));
}
});
}
2011-03-26 14:10:41 +01:00
return self;
2011-03-26 15:50:13 +01:00
}());
exports.padeditbar = padeditbar;