(function() {
tinymce.create('tinymce.plugins.custom', {
init : function(ed, url) {
ed.addButton('custom', {
title : 'custom',
text: 'custom',
icon: false,
onclick: function() {
ed.focus();
ed.selection.setContent('<p class="custom">' + ed.selection.getContent() + '</p>');
}
});
},
createControl : function(n, cm) {
return null;
},
});
tinymce.PluginManager.add('custom', tinymce.plugins.custom);
})();
I've added a button to TinyMCE using the above JS code.
So, when user clicks the button, it wraps any highlighted words into tags.
My question is, how do I make it so that if the highlighted words are already in tag, then it should remove the tag?
Keep an array store of tags, remove the tag if found in array, push to array if not
var tags = [];
...
onclick: function() {
var content = ed.selection.getContent();
var index = tags.indexOf(content);
ed.focus();
if (index === -1) {
ed.selection.setContent('<p class="custom">' + content + '</p>');
tags.push(content);
} else {
ed.selection.setContent(content);
tags.splice(index, 1);
}
}
Related
I'm working on updating an older site to the newer version of jQuery and applying bootstrap.
We are using
backbone.js
jQueryui and
bootstrap.js.
I am getting the following error: Error: cannot call methods on tabs prior to initialization; attempted to call method 'destroy'
The lines of code it is related to is:
Thanks
define([
'jquery',
'jqueryui',
'underscore',
'backbone',
'vm',
'events',
'models/product',
'text!templates/editor/page.html'
], function ($, jui, _, Backbone, Vm, Events, Product, PageTemplate) {
var EditorPage = Backbone.View.extend({
model: Product,
el: '#editor',
_$editorErrorContainer: undefined,
events: {
"click #toolbar": "onToolbarClicked"
},
initialize: function () {
this.bindSaveEvents();
Events.unbind("addText");//HACK: Prevents zombie listeners for this specific situation
Events.bind("addText", this.onAddText, this);
},
onAddText: function (e) {
var layers = this.model.get('layers');
var textCollections = [];
//Search for valid text collections in our layer and push into our array
layers.models.forEach(function (layer) {
var tc = layer.get("textCollection");
if (typeof tc !== "undefined") {
if (layer.get("allowText")) {
var printColor = layer.get("printColor");
var src;
if(printColor) { src = printColor.get("src"); }
var _tc = { 'textCollection': tc, 'src': src, 'name':layer.get('name'), 'cid': layer.cid };
textCollections.push(_tc);
}
}
});
//if there's no choice to make, just add the text element.
if (textCollections.length == 1) {
var textCollection = textCollections[0].textCollection;
AddTextToCollection(textCollection);
return;
}
//Otherwise create dialog for choosing in which text collection to put a new text object
var dialogHTML = "<div> <p>What color would you like this text?</p>";
//Generate selections, use the src attribute of the print color if it uses printcolor
//TODO use other attributes of a textCollection such as rgb color if it uses it etc
textCollections.forEach(function (textCollection, i) {
var imgurl = "";
if(textCollection.src) {
imgurl = "/productEditor/assets/printcolors/icons/" + textCollection.src;
}
//Customers wont like zero indexed option names
var id = i + 1;
dialogHTML += "<p data-id= " + i + " class='selection'> Group " + id + ": " + textCollection.name + "<img src='" + imgurl + "'/> </p>";
});
dialogHTML += "</div>"
$(dialogHTML).dialog({
modal: true,
width: "25%",
height: "auto",
dialogClass: "textAddDialog",
resizable: false,
position: {
my: 'left top',
at: 'left top',
of: $("#editor"),
collision: 'flip'
},
show: 'fade',
create: function() {
var that = this;
//bind dialog events
$(this).children(".selection").click(function(){
$(that).children('.selected').removeClass("selected");
$(this).addClass("selected");
});
},
open: function (event, ui) {
$('.ui-dialog').css('z-index',2003);
$('.ui-widget-overlay').css({
'z-index': 2002,
'opacity': 0.5
});
},
close: function() {
$(this).children(".selection").unbind();
$(this).dialog('destroy').remove();
},
buttons: [{
text: "Accept",
"class": "acceptButton",
click: function() {
var index = $(this).children('.selected').data('id');
if(index >= 0) {
var textCollection = textCollections[index].textCollection;
AddTextToCollection(textCollection);
}
$(this).dialog("close");
}
},
{
text: "Cancel",
click: function() {
$(this).dialog("close");
}
}
]
});
function AddTextToCollection (textCollection) {
//Create text element in chosen group
var entry = textCollection.createEntry();
//Show user that this is a fresh text element that needs changing
entry.set("text", "Edit Me!");
entry.set("freshElement", true);//Fresh element denotes a completely new element, used to delete this element if user adds an element -> cancel button
//Trigger mouseup on element so our text editor dialog will pop for this element
var elem = $('.text_modifier[data-cid="' + entry.cid + '"]');
elem.trigger("click");
}
},
bindSaveEvents: function(){
Events.on('saveError', this.onSaveError.bind(this));
Events.on('saveSuccess', this.onSaveSuccess.bind(this));
},
unbindSaveEvents: function(){
Events.off('saveError', this.onSaveError.bind(this));
Events.off('saveSuccess', this.onSaveSuccess.bind(this));
},
onToolbarClicked: function (e) {
//console.log("toolbar clicked");
//this._productView.clearSelectedChildren();
},
onSaveError: function(errorMessage){
// if(this._$editorErrorContainer === undefined){
// this._$editorErrorContainer = $("<div/>").addClass("error");
// $(this.el).before(this._$editorErrorContainer);
// }
// this._$editorErrorContainer.text(errorMessage).show();
$('#appError').text(errorMessage).show();
},
onSaveSuccess: function(){
// if(this._$editorErrorContainer != null){
// this._$editorErrorContainer.hide();
// }
$('#appError').hide();
},
onRenderComplete: function (e) {
// ADD THE RETURN ELEMENT TO THE LIST OF
this._loadList = _.without(this._loadList, e);
if (this._loadList.length == 0) {
$(this.el).removeClass('loading');
}
},
remove: function(){
this.unbindSaveEvents();
},
renderApp: function () {
var that = this;
// PUT LIST OF ITEMS THAT RENDER INTO ARRAY
// renderComplete WILL REMOVE ITEMS, AND CHANGE STATE TO VISIBLE WHEN ALL ARE LOADED
this._loadList = ['layer', 'product', 'modifiers', 'tabs']
// CREATE PREVIEW MODIFIERS
require(['views/modifiers/product'], function (PreviewModifierView) {
that._modifierView = new PreviewModifierView({ model: that.model });
that._modifierView.on("renderComplete", that.onRenderComplete, that);
that._modifierView.render();
});
// CREATE PREVIEW
require(['views/preview/product'], function (PreviewProductView) {
that._productView = new PreviewProductView({ model: that.model });
that._productView.on("renderComplete", that.onRenderComplete, that);
that._productView.render();
});
// CREATE PRODUCT NAME DISPLAY
require(['views/toolbar/productname'], function (ProductNameView) {
that._productNameView = new ProductNameView({ model: that.model });
that._productNameView.render();
});
// CREATE PRODUCT DESCRIPTION DISPLAY
require(['views/toolbar/productdescription'], function (ProductDescView) {
that._productDescriptionView = new ProductDescView({ model: that.model });
that._productDescriptionView.render();
});
// CREATE TOOLBAR
require(['views/toolbar/layer'], function (ToolbarLayerView) {
that._toolbarView = new ToolbarLayerView({ model: that.model });
that._toolbarView.on("renderComplete", that.onRenderComplete, that);
that._toolbarView.render();
});
// CREATE FINISHED BUTTON
require(['views/toolbar/finished'], function (FinishedView) {
that._finishedView = new FinishedView({ model: that.model });
that._finishedView.render();
});
// CREATE TABS
require(['views/tabs/product'], function (TabsView) {
that._tabsView = new TabsView({ model: that.model });
that._tabsView.on("renderComplete", that.onRenderComplete, that);
that._tabsView.render();
});
},
render: function () {
//console.log("editor render!");
//console.log(this.model);
//$('#productName').html(this.model.get);
var pageTemplate = _.template(PageTemplate, this);
$(this.el).html(pageTemplate);
$('#button-editor-help').show(); // GET'S HIDDEN ON THE APPROVAL PAGE
this.renderApp();
}
});
return EditorPage;
});
In the tabs view, hook up a debugger where you call the destroy and create
Replicate the bug and check if you are calling destroy before initialization
May be you are calling close before the dialog is initialised.
I'm trying to add a search form inside a jPushMenu
but everytime I click on the input text, the Menu will close (probably because of the "closeOnClickOutside" parameter
I'm trying to write a function like this:
$('.cbp-spmenu input').on('click',function() {
// dont execute jPushMenu.close(o);
});
but I really can't figure it out how to do this
This is the jPushMenu.js
/*!
* jPushMenu.js
* 1.1.1
* #author: takien
* http://takien.com
* Original version (pure JS) is created by Mary Lou http://tympanus.net/
*/
(function($) {
$.fn.jPushMenu = function(customOptions) {
var o = $.extend({}, $.fn.jPushMenu.defaultOptions, customOptions);
$('body').addClass(o.pushBodyClass);
// Add class to toggler
$(this).addClass('jPushMenuBtn');
$(this).click(function(e) {
e.stopPropagation();
var target = '',
push_direction = '';
// Determine menu and push direction
if ($(this).is('.' + o.showLeftClass)) {
target = '.cbp-spmenu-left';
push_direction = 'toright';
}
else if ($(this).is('.' + o.showRightClass)) {
target = '.cbp-spmenu-right';
push_direction = 'toleft';
}
else if ($(this).is('.' + o.showTopClass)) {
target = '.cbp-spmenu-top';
}
else if ($(this).is('.' + o.showBottomClass)) {
target = '.cbp-spmenu-bottom';
}
if (target == '') {
return;
}
$(this).toggleClass(o.activeClass);
$(target).toggleClass(o.menuOpenClass);
if ($(this).is('.' + o.pushBodyClass) && push_direction != '') {
$('body').toggleClass(o.pushBodyClass + '-' + push_direction);
}
// Disable all other buttons
$('.jPushMenuBtn').not($(this)).toggleClass('disabled');
return;
});
var jPushMenu = {
close: function (o) {
$('.jPushMenuBtn,body,.cbp-spmenu')
.removeClass('disabled ' + o.activeClass + ' ' + o.menuOpenClass + ' ' + o.pushBodyClass + '-toleft ' + o.pushBodyClass + '-toright');
}
// Close menu on clicking outside menu
if (o.closeOnClickOutside) {
$(document).click(function() {
jPushMenu.close(o);
});
}
// Close menu on clicking menu link
if (o.closeOnClickLink) {
$('.cbp-spmenu a').on('click',function() {
if ( $(this).hasClass('preventclick') ) return;
jPushMenu.close(o);
});
}
// Close menu on clicking menu link
if (o.closeOnFocus) {
$('.logo_wrapper a').on('focus',function() {
jPushMenu.close(o);
});
}
};
/*
* In case you want to customize class name,
* do not directly edit here, use function parameter when call jPushMenu.
*/
$.fn.jPushMenu.defaultOptions = {
pushBodyClass : 'push-body',
showLeftClass : 'menu-left',
showRightClass : 'menu-right',
showTopClass : 'menu-top',
showBottomClass : 'menu-bottom',
activeClass : 'menu-active',
menuOpenClass : 'menu-open',
closeOnClickOutside: true,
closeOnClickLink : true,
closeOnFocus : true
};
})(jQuery);
This is caused by $(document).click() on line 66 in file jPushMenu.js (here). You can fix this by modifying your local file as follows:
// Close menu on clicking outside menu
if (o.closeOnClickOutside) {
$(document).click(function(e) {
if (!$(e.target).closest('.cbp-spmenu').length && $('.cbp-spmenu').is('.' + o.menuOpenClass)) {
jPushMenu.close(o)
}
});
}
And initialize your menu using closeOnClickLink as:
$('#menu').jPushMenu({
closeOnClickLink: false
})
I might send a pull request for this :)
I have a jqgrid that I am trying to use tinyMCE in the text area to send/store html in my database and reload to my grid. I have a custom column with tinyMCE for the text area but after I open editform once and close it next time it is opened tinymce does not initialize and a regular textarea appears. (Also when url modal it looses focus and focuses on editform. The text is pasted to the back "Project" field instead(picture below).)What I'm I doing wrong?
Reason why my TineMCE "Links" was losing focus was because of a simple syntax error. I had modal:true in the editgrid code.
UPDATE FOR WORKING INLINE EDIT IMPLEMENTATION
Here are examples for correct implementation for inline edit(Thank you #Oleg): DEMO1 | DEMO2
INLINE EDIT WORKING CODE SAMPLE:
{ name: "note", width: 260, sortable: false, editable: true,
//edittype: "textarea"
edittype:'custom',
editoptions: {
custom_element: function (value, options) {
var elm = $("<textarea></textarea>");
elm.val(value);
// give the editor time to initialize
setTimeout(function () {
try {
tinymce.remove("#" + options.id);
} catch(ex) {}
tinymce.init({selector: "#" + options.id, plugins: "link"});
}, 50);
return elm;
},
custom_value: function (element, oper, gridval) {
var id;
if (element.length > 0) {
id = element.attr("id");
} else if (typeof element.selector === "string") {
var sels = element.selector.split(" "),
idSel = sels[sels.length - 1];
if (idSel.charAt(0) === "#") {
id = idSel.substring(1);
} else {
return "";
}
}
if (oper === "get") {
return tinymce.get(id).getContent({format: "row"});
} else if (oper === "set") {
if (tinymce.get(id)) {
tinymce.get(id).setContent(gridval);
}
}
}}
}
jqGrid column:
<script src="js/jquery-1.9.0.min.js" type="text/javascript"></script>
<script src="js/i18n/grid.locale-en.js" type="text/javascript"></script>
<script src="js/jquery.jqGrid.min.js" type="text/javascript"></script>
<script src="js/jquery-ui-1.10.3.custom.js" type="text/javascript"></script>
<script src="http://code.jquery.com/jquery-migrate-1.2.1.js"></script>
<script src="tinymce/tinymce.min.js"type="text/javascript"></script>
<script src="tinymce/jquery.tinymce.min.js"type="text/javascript"></script>
<script src= "tinymce/plugins/link/plugin.min.js" type="text/javascript"></script>
<script type="text/javascript">
...
{ name:'notes',
index:'notes',
edittype:'custom', editable:true,
editoptions:{
"custom_element":function( value , options) {
var elm = $('<textarea></textarea>');
elm.val( value );
// give the editor time to initialize
setTimeout( function() {
tinymce.init({selector: "textarea#notes",plugins: "link"});
}, 100);
return elm;
},
"custom_value":function( element, oper, gridval) {
if(oper === 'get') {
return tinymce.get('notes').getContent({format: 'row'});
} else if( oper === 'set') {
if(tinymce.get('notes')) {
tinymce.get('notes').setContent( gridval );
}
}
}}}
Try to replace the code of custom_element to the following
custom_element: function (value, options) {
var elm = $("<textarea></textarea>");
elm.val(value);
// give the editor time to initialize
setTimeout(function () {
try {
tinymce.remove("#" + options.id);
} catch(ex) {}
tinymce.init({selector: "#" + options.id, plugins: "link"});
}, 50);
return elm;
}
One more simpel
I'm trying to trigger a callback to dynamically populate a CKEditor dialog with checkboxes when the dialog is opened. I've read other solutions that use iframes, but this won't work for me because the dialog needs to be populated based on other elements on the same page.
Here is what I have so far. There are no errors, but the dialog is just empty when it opens. I expect the addContents function to fill in the dialog. I've confirmed that dialog.definition.contents does include the contents and elements that I want, but it's just not filling in the actual dialog. What am I missing?
(function() {
CKEDITOR.plugins.add( 'embeds', {
icons: 'embed',
init: function(editor) {
var self = this,
elements = [];
CKEDITOR.dialog.add('EmbedsDialog', function (instance) {
return {
title : 'Embeds',
minWidth : 550,
minHeight : 200,
contents: [],
onShow: function() {
var dialog = this,
elements = [];
$('#embeds-fields tr').each(function() {
var title = $(this).find('input[type=text]').val(),
url = $(this).find('input[type=url]').val();
if(url != "") {
elements.push({
label : "embed",
title : url,
type : 'checkbox'
});
}
});
dialog.definition.removeContents('embeds');
dialog.definition.addContents({
id : 'embeds',
expand : true,
elements : elements
});
},
}; // return
});
editor.addCommand('Embeds',
new CKEDITOR.dialogCommand('EmbedsDialog', {
allowedContent: 'a[*](*)'
})
);
editor.ui.addButton('Embeds', {
label : 'Embeds',
command : 'Embeds',
toolbar : 'embeds'
});
} // init
}); // add
})(); // closure
Based off of this example, I ended up with this solution, where "main" is the ID of the original content.
CKEDITOR.on('dialogDefinition', function(ev) {
var dialogName = ev.data.name;
var dialogDefinition = ev.data.definition;
if (dialogName == 'EmbedsDialog') {
var main = dialogDefinition.getContents('main');
$('#embeds-fields tr').each(function() {
var title = $(this).find('input[type=text]').val(),
url = $(this).find('input[type=url]').val();
if(url != "") {
main.add({
type : 'checkbox',
label : title,
});
}
});
}
});
I am adding a button to tinyMCE and I want to know how to wrap text inside tags with javascript, for instance (this highlighted text gets wrapped inside [highlight][/highlight] tags).
and now the entire tinymce
(function() {
tinymce.create('tinymce.plugins.shoutButton', {
init : function(ed, url) {
ed.addButton('shout.button', {
title : 'shout.button',
image : 'viral.gif',
onclick : function() {
window.alert("booh");
});
},
createControl : function(n, cm) {
return null;
},
getInfo : function() {
return {
longname : "Shout button",
author : 'SAFAD',
authorurl : 'http://safadsoft.com/',
infourl : 'http://safadsoft.com/',
version : "1.0"
};
}
});
tinymce.PluginManager.add('shout.button', tinymce.plugins.ShoutButton);
})();
You can use the setSelectionRange (mozilla/webkit) or selection.createRange (IE) methods to find the currently highlighted text inside a textarea.
I put up an example on jsfiddle, but have commented out your regexp since it hangs the browser in many instances. You need to make it more restrictive, and it currently passes a lot of other things than youtube url's as well.
However, the example has a working solution how to get the currently selected text, which you can, after fixing your pattern, apply to the idPattern.exec().
idPattern = /(?:(?:[^v]+)+v.)?([^&=]{11})(?=&|$)/;
// var vidId = prompt("YouTube Video", "Enter the id or url for your video");
var vidId;
el = document.getElementById('texty');
if (el.setSelectionRange) {
var vidId = el.value.substring(el.selectionStart,el.selectionEnd);
}
else if(document.selection.createRange()) {
var vidId = document.selection.createRange().text;
}
alert(vidId);
EDIT: Wrapping the highlighted text and outputting it back to the element. example
el = document.getElementById('texty');
if (el.setSelectionRange) {
el.value = el.value.substring(0,el.selectionStart) + "[highlight]" + el.value.substring(el.selectionStart,el.selectionEnd) + "[/highlight]" + el.value.substring(el.selectionEnd,el.value.length);
}
else if(document.selection.createRange()) {
document.selection.createRange().text = "[highlight]" + document.selection.createRange().text + "[/highlight]";
}
The issue was syntax errors, not properly closed brackets and some missing semi-colons, using the help of the awesome Jsfiddle's JSHint and JSLint I fixed it :
(function () {
tinymce.create('tinymce.plugins.shoutButton', {
init: function (ed, url) {
ed.addButton('shout.button', {
title: 'shout.button',
image: 'viral.gif',
onclick: function () {
window.alert("booh");
}
});
createControl: function (n, cm) {
return null;
}
getInfo: function () {
return {
longname: "Shout button",
author: 'You !',
authorurl: 'http://example.com/',
infourl: 'http://example.com/',
version: "1.0"
};
}
}
});
tinymce.PluginManager.add('shout.button', tinymce.plugins.ShoutButton);
})();
Best Regards