alpaca
Version:
Alpaca provides the easiest and fastest way to generate interactive forms for the web and mobile devices. It runs simply as HTML5 or more elaborately using Bootstrap, jQuery Mobile or jQuery UI. Alpaca uses Handlebars to process JSON schema and provide
690 lines (619 loc) • 25.1 kB
JavaScript
define([
'jquery',
'summernote/base/core/func',
'summernote/base/core/list',
'summernote/base/core/agent'
], function ($, func, list, agent) {
var Buttons = function (context) {
var self = this;
var ui = $.summernote.ui;
var $toolbar = context.layoutInfo.toolbar;
var options = context.options;
var lang = options.langInfo;
var invertedKeyMap = func.invertObject(options.keyMap[agent.isMac ? 'mac' : 'pc']);
var representShortcut = this.representShortcut = function (editorMethod) {
var shortcut = invertedKeyMap[editorMethod];
if (agent.isMac) {
shortcut = shortcut.replace('CMD', '⌘').replace('SHIFT', '⇧');
}
shortcut = shortcut.replace('BACKSLASH', '\\')
.replace('SLASH', '/')
.replace('LEFTBRACKET', '[')
.replace('RIGHTBRACKET', ']');
return ' (' + shortcut + ')';
};
this.initialize = function () {
this.addToolbarButtons();
this.addImagePopoverButtons();
this.addLinkPopoverButtons();
this.fontInstalledMap = {};
};
this.destroy = function () {
delete this.fontInstalledMap;
};
this.isFontInstalled = function (name) {
if (!self.fontInstalledMap.hasOwnProperty(name)) {
self.fontInstalledMap[name] = agent.isFontInstalled(name) ||
list.contains(options.fontNamesIgnoreCheck, name);
}
return self.fontInstalledMap[name];
};
this.addToolbarButtons = function () {
context.memo('button.style', function () {
return ui.buttonGroup([
ui.button({
className: 'dropdown-toggle',
contents: ui.icon(options.icons.magic) + ' ' + ui.icon(options.icons.caret, 'span'),
tooltip: lang.style.style,
data: {
toggle: 'dropdown'
}
}),
ui.dropdown({
className: 'dropdown-style',
items: context.options.styleTags,
template: function (item) {
if (typeof item === 'string') {
item = { tag: item, title: item };
}
var tag = item.tag;
var title = item.title;
var style = item.style ? ' style="' + item.style + '" ' : '';
var className = item.className ? ' className="' + item.className + '"' : '';
return '<' + tag + style + className + '>' + title + '</' + tag + '>';
},
click: context.createInvokeHandler('editor.formatBlock')
})
]).render();
});
context.memo('button.bold', function () {
return ui.button({
className: 'note-btn-bold',
contents: ui.icon(options.icons.bold),
tooltip: lang.font.bold + representShortcut('bold'),
click: context.createInvokeHandler('editor.bold')
}).render();
});
context.memo('button.italic', function () {
return ui.button({
className: 'note-btn-italic',
contents: ui.icon(options.icons.italic),
tooltip: lang.font.italic + representShortcut('italic'),
click: context.createInvokeHandler('editor.italic')
}).render();
});
context.memo('button.underline', function () {
return ui.button({
className: 'note-btn-underline',
contents: ui.icon(options.icons.underline),
tooltip: lang.font.underline + representShortcut('underline'),
click: context.createInvokeHandler('editor.underline')
}).render();
});
context.memo('button.clear', function () {
return ui.button({
contents: ui.icon(options.icons.eraser),
tooltip: lang.font.clear + representShortcut('removeFormat'),
click: context.createInvokeHandler('editor.removeFormat')
}).render();
});
context.memo('button.strikethrough', function () {
return ui.button({
className: 'note-btn-strikethrough',
contents: ui.icon(options.icons.strikethrough),
tooltip: lang.font.strikethrough + representShortcut('strikethrough'),
click: context.createInvokeHandler('editor.strikethrough')
}).render();
});
context.memo('button.superscript', function () {
return ui.button({
className: 'note-btn-superscript',
contents: ui.icon(options.icons.superscript),
tooltip: lang.font.superscript,
click: context.createInvokeHandler('editor.superscript')
}).render();
});
context.memo('button.subscript', function () {
return ui.button({
className: 'note-btn-subscript',
contents: ui.icon(options.icons.subscript),
tooltip: lang.font.subscript,
click: context.createInvokeHandler('editor.subscript')
}).render();
});
context.memo('button.fontname', function () {
return ui.buttonGroup([
ui.button({
className: 'dropdown-toggle',
contents: '<span class="note-current-fontname"/> ' + ui.icon(options.icons.caret, 'span'),
tooltip: lang.font.name,
data: {
toggle: 'dropdown'
}
}),
ui.dropdownCheck({
className: 'dropdown-fontname',
checkClassName: options.icons.menuCheck,
items: options.fontNames.filter(self.isFontInstalled),
template: function (item) {
return '<span style="font-family:' + item + '">' + item + '</span>';
},
click: context.createInvokeHandler('editor.fontName')
})
]).render();
});
context.memo('button.fontsize', function () {
return ui.buttonGroup([
ui.button({
className: 'dropdown-toggle',
contents: '<span class="note-current-fontsize"/>' + ui.icon(options.icons.caret, 'span'),
tooltip: lang.font.size,
data: {
toggle: 'dropdown'
}
}),
ui.dropdownCheck({
className: 'dropdown-fontsize',
checkClassName: options.icons.menuCheck,
items: options.fontSizes,
click: context.createInvokeHandler('editor.fontSize')
})
]).render();
});
context.memo('button.color', function () {
return ui.buttonGroup({
className: 'note-color',
children: [
ui.button({
className: 'note-current-color-button',
contents: ui.icon(options.icons.font + ' note-recent-color'),
tooltip: lang.color.recent,
click: function (e) {
var $button = $(e.currentTarget);
context.invoke('editor.color', {
backColor: $button.attr('data-backColor'),
foreColor: $button.attr('data-foreColor')
});
},
callback: function ($button) {
var $recentColor = $button.find('.note-recent-color');
$recentColor.css('background-color', '#FFFF00');
$button.attr('data-backColor', '#FFFF00');
}
}),
ui.button({
className: 'dropdown-toggle',
contents: ui.icon(options.icons.caret, 'span'),
tooltip: lang.color.more,
data: {
toggle: 'dropdown'
}
}),
ui.dropdown({
items: [
'<li>',
'<div class="btn-group">',
' <div class="note-palette-title">' + lang.color.background + '</div>',
' <div>',
' <button type="button" class="note-color-reset btn btn-default" data-event="backColor" data-value="inherit">',
lang.color.transparent,
' </button>',
' </div>',
' <div class="note-holder" data-event="backColor"/>',
'</div>',
'<div class="btn-group">',
' <div class="note-palette-title">' + lang.color.foreground + '</div>',
' <div>',
' <button type="button" class="note-color-reset btn btn-default" data-event="removeFormat" data-value="foreColor">',
lang.color.resetToDefault,
' </button>',
' </div>',
' <div class="note-holder" data-event="foreColor"/>',
'</div>',
'</li>'
].join(''),
callback: function ($dropdown) {
$dropdown.find('.note-holder').each(function () {
var $holder = $(this);
$holder.append(ui.palette({
colors: options.colors,
eventName: $holder.data('event')
}).render());
});
},
click: function (event) {
var $button = $(event.target);
var eventName = $button.data('event');
var value = $button.data('value');
if (eventName && value) {
var key = eventName === 'backColor' ? 'background-color' : 'color';
var $color = $button.closest('.note-color').find('.note-recent-color');
var $currentButton = $button.closest('.note-color').find('.note-current-color-button');
$color.css(key, value);
$currentButton.attr('data-' + eventName, value);
context.invoke('editor.' + eventName, value);
}
}
})
]
}).render();
});
context.memo('button.ul', function () {
return ui.button({
contents: ui.icon(options.icons.unorderedlist),
tooltip: lang.lists.unordered + representShortcut('insertUnorderedList'),
click: context.createInvokeHandler('editor.insertUnorderedList')
}).render();
});
context.memo('button.ol', function () {
return ui.button({
contents: ui.icon(options.icons.orderedlist),
tooltip: lang.lists.ordered + representShortcut('insertOrderedList'),
click: context.createInvokeHandler('editor.insertOrderedList')
}).render();
});
var justifyLeft = ui.button({
contents: ui.icon(options.icons.alignLeft),
tooltip: lang.paragraph.left + representShortcut('justifyLeft'),
click: context.createInvokeHandler('editor.justifyLeft')
});
var justifyCenter = ui.button({
contents: ui.icon(options.icons.alignCenter),
tooltip: lang.paragraph.center + representShortcut('justifyCenter'),
click: context.createInvokeHandler('editor.justifyCenter')
});
var justifyRight = ui.button({
contents: ui.icon(options.icons.alignRight),
tooltip: lang.paragraph.right + representShortcut('justifyRight'),
click: context.createInvokeHandler('editor.justifyRight')
});
var justifyFull = ui.button({
contents: ui.icon(options.icons.alignJustify),
tooltip: lang.paragraph.justify + representShortcut('justifyFull'),
click: context.createInvokeHandler('editor.justifyFull')
});
var outdent = ui.button({
contents: ui.icon(options.icons.outdent),
tooltip: lang.paragraph.outdent + representShortcut('outdent'),
click: context.createInvokeHandler('editor.outdent')
});
var indent = ui.button({
contents: ui.icon(options.icons.indent),
tooltip: lang.paragraph.indent + representShortcut('indent'),
click: context.createInvokeHandler('editor.indent')
});
context.memo('button.justifyLeft', func.invoke(justifyLeft, 'render'));
context.memo('button.justifyCenter', func.invoke(justifyCenter, 'render'));
context.memo('button.justifyRight', func.invoke(justifyRight, 'render'));
context.memo('button.justifyFull', func.invoke(justifyFull, 'render'));
context.memo('button.outdent', func.invoke(outdent, 'render'));
context.memo('button.indent', func.invoke(indent, 'render'));
context.memo('button.paragraph', function () {
return ui.buttonGroup([
ui.button({
className: 'dropdown-toggle',
contents: ui.icon(options.icons.alignLeft) + ' ' + ui.icon(options.icons.caret, 'span'),
tooltip: lang.paragraph.paragraph,
data: {
toggle: 'dropdown'
}
}),
ui.dropdown([
ui.buttonGroup({
className: 'note-align',
children: [justifyLeft, justifyCenter, justifyRight, justifyFull]
}),
ui.buttonGroup({
className: 'note-list',
children: [outdent, indent]
})
])
]).render();
});
context.memo('button.height', function () {
return ui.buttonGroup([
ui.button({
className: 'dropdown-toggle',
contents: ui.icon(options.icons.textHeight) + ' ' + ui.icon(options.icons.caret, 'span'),
tooltip: lang.font.height,
data: {
toggle: 'dropdown'
}
}),
ui.dropdownCheck({
items: options.lineHeights,
checkClassName: options.icons.menuCheck,
className: 'dropdown-line-height',
click: context.createInvokeHandler('editor.lineHeight')
})
]).render();
});
context.memo('button.table', function () {
return ui.buttonGroup([
ui.button({
className: 'dropdown-toggle',
contents: ui.icon(options.icons.table) + ' ' + ui.icon(options.icons.caret, 'span'),
tooltip: lang.table.table,
data: {
toggle: 'dropdown'
}
}),
ui.dropdown({
className: 'note-table',
items: [
'<div class="note-dimension-picker">',
' <div class="note-dimension-picker-mousecatcher" data-event="insertTable" data-value="1x1"/>',
' <div class="note-dimension-picker-highlighted"/>',
' <div class="note-dimension-picker-unhighlighted"/>',
'</div>',
'<div class="note-dimension-display">1 x 1</div>'
].join('')
})
], {
callback: function ($node) {
var $catcher = $node.find('.note-dimension-picker-mousecatcher');
$catcher.css({
width: options.insertTableMaxSize.col + 'em',
height: options.insertTableMaxSize.row + 'em'
}).mousedown(context.createInvokeHandler('editor.insertTable'))
.on('mousemove', self.tableMoveHandler);
}
}).render();
});
context.memo('button.link', function () {
return ui.button({
contents: ui.icon(options.icons.link),
tooltip: lang.link.link,
click: context.createInvokeHandler('linkDialog.show')
}).render();
});
context.memo('button.picture', function () {
return ui.button({
contents: ui.icon(options.icons.picture),
tooltip: lang.image.image,
click: context.createInvokeHandler('imageDialog.show')
}).render();
});
context.memo('button.video', function () {
return ui.button({
contents: ui.icon(options.icons.video),
tooltip: lang.video.video,
click: context.createInvokeHandler('videoDialog.show')
}).render();
});
context.memo('button.hr', function () {
return ui.button({
contents: ui.icon(options.icons.minus),
tooltip: lang.hr.insert + representShortcut('insertHorizontalRule'),
click: context.createInvokeHandler('editor.insertHorizontalRule')
}).render();
});
context.memo('button.fullscreen', function () {
return ui.button({
className: 'btn-fullscreen',
contents: ui.icon(options.icons.arrowsAlt),
tooltip: lang.options.fullscreen,
click: context.createInvokeHandler('fullscreen.toggle')
}).render();
});
context.memo('button.codeview', function () {
return ui.button({
className: 'btn-codeview',
contents: ui.icon(options.icons.code),
tooltip: lang.options.codeview,
click: context.createInvokeHandler('codeview.toggle')
}).render();
});
context.memo('button.redo', function () {
return ui.button({
contents: ui.icon(options.icons.redo),
tooltip: lang.history.redo + representShortcut('redo'),
click: context.createInvokeHandler('editor.redo')
}).render();
});
context.memo('button.undo', function () {
return ui.button({
contents: ui.icon(options.icons.undo),
tooltip: lang.history.undo + representShortcut('undo'),
click: context.createInvokeHandler('editor.undo')
}).render();
});
context.memo('button.help', function () {
return ui.button({
contents: ui.icon(options.icons.question),
tooltip: lang.options.help,
click: context.createInvokeHandler('helpDialog.show')
}).render();
});
};
/**
* image : [
* ['imagesize', ['imageSize100', 'imageSize50', 'imageSize25']],
* ['float', ['floatLeft', 'floatRight', 'floatNone' ]],
* ['remove', ['removeMedia']]
* ],
*/
this.addImagePopoverButtons = function () {
// Image Size Buttons
context.memo('button.imageSize100', function () {
return ui.button({
contents: '<span class="note-fontsize-10">100%</span>',
tooltip: lang.image.resizeFull,
click: context.createInvokeHandler('editor.resize', '1')
}).render();
});
context.memo('button.imageSize50', function () {
return ui.button({
contents: '<span class="note-fontsize-10">50%</span>',
tooltip: lang.image.resizeHalf,
click: context.createInvokeHandler('editor.resize', '0.5')
}).render();
});
context.memo('button.imageSize25', function () {
return ui.button({
contents: '<span class="note-fontsize-10">25%</span>',
tooltip: lang.image.resizeQuarter,
click: context.createInvokeHandler('editor.resize', '0.25')
}).render();
});
// Float Buttons
context.memo('button.floatLeft', function () {
return ui.button({
contents: ui.icon(options.icons.alignLeft),
tooltip: lang.image.floatLeft,
click: context.createInvokeHandler('editor.floatMe', 'left')
}).render();
});
context.memo('button.floatRight', function () {
return ui.button({
contents: ui.icon(options.icons.alignRight),
tooltip: lang.image.floatRight,
click: context.createInvokeHandler('editor.floatMe', 'right')
}).render();
});
context.memo('button.floatNone', function () {
return ui.button({
contents: ui.icon(options.icons.alignJustify),
tooltip: lang.image.floatNone,
click: context.createInvokeHandler('editor.floatMe', 'none')
}).render();
});
// Remove Buttons
context.memo('button.removeMedia', function () {
return ui.button({
contents: ui.icon(options.icons.trash),
tooltip: lang.image.remove,
click: context.createInvokeHandler('editor.removeMedia')
}).render();
});
};
this.addLinkPopoverButtons = function () {
context.memo('button.linkDialogShow', function () {
return ui.button({
contents: ui.icon(options.icons.link),
tooltip: lang.link.edit,
click: context.createInvokeHandler('linkDialog.show')
}).render();
});
context.memo('button.unlink', function () {
return ui.button({
contents: ui.icon(options.icons.unlink),
tooltip: lang.link.unlink,
click: context.createInvokeHandler('editor.unlink')
}).render();
});
};
this.build = function ($container, groups) {
for (var groupIdx = 0, groupLen = groups.length; groupIdx < groupLen; groupIdx++) {
var group = groups[groupIdx];
var groupName = group[0];
var buttons = group[1];
var $group = ui.buttonGroup({
className: 'note-' + groupName
}).render();
for (var idx = 0, len = buttons.length; idx < len; idx++) {
var button = context.memo('button.' + buttons[idx]);
if (button) {
$group.append(typeof button === 'function' ? button(context) : button);
}
}
$group.appendTo($container);
}
};
this.updateCurrentStyle = function () {
var styleInfo = context.invoke('editor.currentStyle');
this.updateBtnStates({
'.note-btn-bold': function () {
return styleInfo['font-bold'] === 'bold';
},
'.note-btn-italic': function () {
return styleInfo['font-italic'] === 'italic';
},
'.note-btn-underline': function () {
return styleInfo['font-underline'] === 'underline';
},
'.note-btn-subscript': function () {
return styleInfo['font-subscript'] === 'subscript';
},
'.note-btn-superscript': function () {
return styleInfo['font-superscript'] === 'superscript';
},
'.note-btn-strikethrough': function () {
return styleInfo['font-strikethrough'] === 'strikethrough';
}
});
if (styleInfo['font-family']) {
var fontNames = styleInfo['font-family'].split(',').map(function (name) {
return name.replace(/[\'\"]/g, '')
.replace(/\s+$/, '')
.replace(/^\s+/, '');
});
var fontName = list.find(fontNames, self.isFontInstalled);
$toolbar.find('.dropdown-fontname li a').each(function () {
// always compare string to avoid creating another func.
var isChecked = ($(this).data('value') + '') === (fontName + '');
this.className = isChecked ? 'checked' : '';
});
$toolbar.find('.note-current-fontname').text(fontName);
}
if (styleInfo['font-size']) {
var fontSize = styleInfo['font-size'];
$toolbar.find('.dropdown-fontsize li a').each(function () {
// always compare with string to avoid creating another func.
var isChecked = ($(this).data('value') + '') === (fontSize + '');
this.className = isChecked ? 'checked' : '';
});
$toolbar.find('.note-current-fontsize').text(fontSize);
}
if (styleInfo['line-height']) {
var lineHeight = styleInfo['line-height'];
$toolbar.find('.dropdown-line-height li a').each(function () {
// always compare with string to avoid creating another func.
var isChecked = ($(this).data('value') + '') === (lineHeight + '');
this.className = isChecked ? 'checked' : '';
});
}
};
this.updateBtnStates = function (infos) {
$.each(infos, function (selector, pred) {
ui.toggleBtnActive($toolbar.find(selector), pred());
});
};
this.tableMoveHandler = function (event) {
var PX_PER_EM = 18;
var $picker = $(event.target.parentNode); // target is mousecatcher
var $dimensionDisplay = $picker.next();
var $catcher = $picker.find('.note-dimension-picker-mousecatcher');
var $highlighted = $picker.find('.note-dimension-picker-highlighted');
var $unhighlighted = $picker.find('.note-dimension-picker-unhighlighted');
var posOffset;
// HTML5 with jQuery - e.offsetX is undefined in Firefox
if (event.offsetX === undefined) {
var posCatcher = $(event.target).offset();
posOffset = {
x: event.pageX - posCatcher.left,
y: event.pageY - posCatcher.top
};
} else {
posOffset = {
x: event.offsetX,
y: event.offsetY
};
}
var dim = {
c: Math.ceil(posOffset.x / PX_PER_EM) || 1,
r: Math.ceil(posOffset.y / PX_PER_EM) || 1
};
$highlighted.css({ width: dim.c + 'em', height: dim.r + 'em' });
$catcher.data('value', dim.c + 'x' + dim.r);
if (3 < dim.c && dim.c < options.insertTableMaxSize.col) {
$unhighlighted.css({ width: dim.c + 1 + 'em'});
}
if (3 < dim.r && dim.r < options.insertTableMaxSize.row) {
$unhighlighted.css({ height: dim.r + 1 + 'em'});
}
$dimensionDisplay.html(dim.c + ' x ' + dim.r);
};
};
return Buttons;
});