Creating a custom Rails tinyMCE plugin: editor.ui is undefined - ruby-on-rails

I want to add a custom plugin to a rails backed tinyMCE editor, however in the console I get editor.ui is undefined and it fails
If I comment out any code relating to editor.ui in the config.js file then the editor loads and using the 'Help' plugin I can see that my plugin is loading fine from the getMetadata function
/config/tinymce.yml
toolbar:
- bold italic underline superscript subscript underline | link | alignleft aligncenter alignright | bullist numlist | hr | forecolor | backcolor | table | myplugin | image | help
plugins:
- link table lists textcolor myplugin image imagetools help
app/assets/javascripts/tinymce/plugins/myplugin/plugin.js
(this is just copied directly from https://www.tiny.cloud/docs/advanced/creating-a-plugin/ changing "example" for "myplugin")
$(document).on("ready", function() {
tinymce.PluginManager.add("myplugin", function(editor, url) {
var openDialog = function () {
return editor.windowManager.open({
title: "Example plugin",
body: {
type: "panel",
items: [
{
type: "input",
name: "title",
label: "Title"
}
]
},
buttons: [
{
type: "cancel",
text: "Close"
},
{
type: "submit",
text: "Save",
primary: true
}
],
onSubmit: function (api) {
var data = api.getData();
// Insert content when the window form is submitted
editor.insertContent("Title: " + data.title);
api.close();
}
});
};
// Add a button that opens a window
editor.ui.registry.addButton("myplugin", {
text: "My button",
onAction: function () {
// Open window
openDialog();
}
});
// Adds a menu item, which can then be included in any menu via the menu/menubar configuration
editor.ui.registry.addMenuItem("myplugin", {
text: "Example plugin",
onAction: function() {
// Open window
openDialog();
}
});
return {
getMetadata: function () {
return {
name: "Example plugin",
url: "http://exampleplugindocsurl.com"
};
}
};
});
});

Which version of TinyMCE are you using? This code works fine on our fiddle website:
http://fiddle.tinymce.com/PGgaab

Without seeing the full code you are using it will be hard to say for sure but document ready in jQuery is likely firing well before TinyMCE itself is initialized. If that is the case you can't use a TinyMCE API to add a plugin before TinyMCE is initialized.
I would, as that documentation page suggests, place the plugin code in a file and load it like all other TinyMCE plugins. TinyMCE will get the plugins at the correct time in its initialization process and this would no longer be an issue.

Related

Using select2 how can I collapse/expand optgroups?

I'm using select2 (v 4.0.3) with Bootstrap 3 and I have to display several hundreds of alternatives per optgroup. Now I wish to collapse/expand the optgroups on click to make it a bit more manageable. I couldn't find any info on this so I thought I'd post a question.
I found an approach to this problem but I couldn't get it to work (it seems a bit outdated issue #730). The basic problem with that approach now is that in the current version of select2 elements aren't created until they are needed. Also, the class names seem to have changed a bit, as have apparently the names of events in the move to the latest version.
So far I've managed to get the collapse/expand functionality to work for the optgroups, but issues arise when the user provides text input (check the fiddle).
$(function () {
var data = [
{
text: "Group 1",
children: [
{ id: 'A1', text: 'a1'},
{ id: 'B2', text: 'b2'},
{ id: 'C3', text: 'c3'}]
},
{
text: "Group 2",
children: [
{ id: 'A2', text: 'a2'},
{ id: 'B3', text: 'b3'},
{ id: 'C1', text: 'c1'}
]
}];
$('#mySelect')
.select2({data: data, placeholder : 'Select one' });
// Toggle group on click
$('.select2')
.on('click', function(){
$('.select2-results__option').on('click', function(){
$(this).find('.select2-results__options--nested').toggle();
});
});
});
When the text input is used select2 runs the search and the events I've registered are dropped. My plan was to capture text input and check if the input field is empty or not, based on which I can decide to recreate the optgroup listeners or show all optgroups. Any help in this direction would be appreciated.

Sencha Touch 2: Trying to create a login form

I am a 100% newb to Sencha and am trying to take a stab at re-factoring my company's mobile app.
Here is my app.js:
Ext.application({
name: 'RecruitTalkTouch',
views: ['Login'],
launch: function () {
Ext.Viewport.add([
{ xtype: 'loginview' }
]);
}
});
Login.js View:
Ext.define('RecruitTalkTouch.view.Login', {
extend: 'Ext.Container',
alias: "widget.loginview",
xtype: 'loginForm',
id: 'loginForm',
requires: ['Ext.form.FieldSet', 'Ext.form.Password', 'Ext.Label', 'Ext.Button' ],
config: {
title: 'Login',
items: [
{
xtype: 'label',
html: 'Login failed. Please enter the correct credentials.',
itemId: 'signInFailedLabel',
hidden: true,
hideAnimation: 'fadeOut',
showAnimation: 'fadeIn',
style: 'color:#990000;margin:5px 0px;'
},
{
xtype: 'fieldset',
title: 'Login Example',
items: [
{
xtype: 'textfield',
placeHolder: 'Email',
itemId: 'userNameTextField',
name: 'userNameTextField',
required: true
},
{
xtype: 'passwordfield',
placeHolder: 'Password',
itemId: 'passwordTextField',
name: 'passwordTextField',
required: true
}
]
},
{
xtype: 'button',
itemId: 'logInButton',
ui: 'action',
padding: '10px',
text: 'Log In'
}
]
}
});
Login.js Controller:
Ext.define('RecruitTalkTouch.controller.Login', {
extend: 'Ext.app.Controller',
config: {
refs: {
loginForm: 'loginForm'
},
control: {
'#logInButton': {
tap: 'onSignInCommand'
}
}
},
onSignInCommand: function(){
console.log("HELLO WORLD");
}
});
When I click the submit button, nothing happens. How can I hook up my submit button to listen for events (click, tap, etc) along with submitting the information to a backend API?
In app.js file of your application, add:
controllers: [
'Login'
]
in your application class. And for submitting information, call a Ajax request like this:
Ext.Ajax.request({
url: // api url..,
method: 'POST',
params: {
username: // get user name from form and put here,
password: // get password and ...
},
success: function(response) {
do something...
},
failure: function(err) {do ..}
});
from inside onSignInCommand() function.
You must activate your controller by adding it to the controllers option of your application class.
Then, to submit your data to the backend, you've got several options. You can use a form panel instead of your raw container, and use its submit method. Alternatively, you can use the Ext.Ajax singleton. In this case, you'll have to build the payload yourself. Finally, you can create a model with a configured proxy, and use its save method. This last way is probably the best for long term maintainability... Even if in the case of a simple login form, that may be a little bit overkill.
Can u please refer this sample app to create login form. Its very simple app please go through it.
http://miamicoder.com/2012/adding-a-login-screen-to-a-sencha-touch-application/

How to check files already uploaded in Uploadify JQ plugin?

I have to prevent users adding files which is already added in the server in 'uploadify' jquery plugin.
There is a grid which shows files which are already added, so I'm planning to get the files name collection from the grid and check it when on select function for preventing to add the already uploaded files.
Is there any better or easy way to do this?
Please help in finishing the code.
Will the above logic work ?
var uploadQueue = new Array();
jQuery().ready(function ($) {
debugger;
$.ajax({
type: "POST",
async: false,
url: ROOT + "Documents/DocumentAJAXController",
data: { "ID": $('[id="ID"]').val() },
success: function (result) {
}
});
$("#SubmitButton1").hide();
$("#fileuploader").fileUpload({
'uploader': "#Url.Content("~/Scripts/FileUpload/uploader.swf")",
'cancelImg': "#Url.Content("~/Content/Images/clearBtn.png")",
'buttonText': 'Browse Files',
//'buttonImg' : "#Url.Content("~/Content/Images/attachDoc.png")",
'script': "#Url.Content("~/Documents/Upload/")",
'folder': "#Url.Content("~/Documents/")",
//'fileDesc': 'Documents Files',
'fileExt': '*.pdf;*.doc;*.ppt;*.xls',
'multi': true,
'auto': false,
onError: function (a, b, c, d) {
},
'onSelect': function (event, queueID, fileObj) {
// uploadQueue.push(queueID);
},
'onAllComplete': function(event,data){
alert("Uploaded successfully");
$('.popupClose').click();
}
});
This functionality is built into the latest version of uploadify.
Uploadify provides for a checkScript handler in the settings that you pass to uploadify.
If you point this at an appropriate server script it will not allow files that already exist.
A sample script called check-exists.php is provided in the uploadify zip.

leaflet geojson contextmenu

I want to add a context menu on right click for various elements on my geojson layer (I'm doing a road map so on a right click on the road at any part I want to show the context menu).
I've managed to get the left click working fine by using the onEachFeature and doing the following
function onEachFeature(feature, layer) {
layer.on({
click: showAssetInfo,
contextmenu: contextreg
});
}
function showAssetInfo(e) {
AssetMouseClick(e.target.feature.properties.objectid, e.latlng);
}
For the context menu I have followed the example here . The context menu library is found here
I have the following that gets called on the document ready (jquery)
$.contextMenu({
selector: 'path.leaflet-clickable',
zIndex: 99999,
callback: function (key, options) {
var m = "clicked: " + key;
window.console && console.log(m) || alert(m);
},
items: {
"edit": { name: "Edit", icon: "edit" },
"cut": { name: "Cut", icon: "cut" },
"copy": { name: "Copy", icon: "copy" },
"paste": { name: "Paste", icon: "paste" },
"delete": { name: "Delete", icon: "delete" },
"sep1": "---------",
"quit": { name: "Quit", icon: "quit" }
}
});
I've tested it and the selector does return the GeoJson features, also if it attach the same menu to something else it works correctly.
Is there something I am missing here?
Also is there a good way to pass in the objectid to the menu when it starts up so I can use it when calling the different options of the menu
EDIT:
I have created this fiddle to demonstrate http://jsfiddle.net/Q3L4c/22/
There is a good leaflet plugin for context menu that was created in Aug 2013:
Leaflet.contextmenu
This context menu library has great documentation including step-by-step instructions for implementation in GeoJSON layers.
In the following code snippet, notice how we can easily pass through the full feature and layer objects to the function that is called when the edit menu item is selected. In this example the layer group is a GeoJSON layer group, the GeoJSON properties can be accessed via feature.properties
NOTE: In this solution content menu item definitions are generated during the onEachFeature processing, not dynamically when the context menu is invoked, just something to be aware of if your were planning on dynamic menu item generation which might be dependent on variables that could change at run time, you need to evaluate the visibility or enabled option for each item as a static value when you create the menu item.
function onEachFeature(feature, layer) {
layer.bindContextMenu({
contextmenu: true,
contextmenuInheritItems: false,
contextmenuItems: [
{ text: 'edit', icon: 'edit', callback: function () { editFeature(feature, layer); } },
{ text: 'cut', icon: cut', callback: function () { console.log('cut'); } },
{ text: 'copy', icon: 'copy', callback: function () { console.log('copy'); } },
{ text: 'paste', icon: 'paste', callback: function () { console.log('paste'); } },
{ text: 'delete', icon: 'delete', callback: function () { console.log('delete'); } },
{ separator: true },
{ text: 'quit', icon: 'quit', callback: function () { console.log('quit'); } },
]
});
layer.on({
click: showAssetInfo
});
}
/**
* Edit a feature on the map
* #param {GeoJSON} feature GeoJSON feature, access metadata through feature.properties
* #param {any} layer Leaflet layer represents this feature on the map (the shape)
*/
function editFeature(feature, layer) {
console.log(JSON.stringify(feature.properties));
}
function showAssetInfo(e) {
AssetMouseClick(e.target.feature.properties.objectid, e.latlng);
}

Select2 dropdown but allow new values by user?

I want to have a dropdown with a set of values but also allow the user to "select" a new value not listed there.
I see that select2 supports this if you are using it in tags mode, but is there a way to do it without using tags?
The excellent answer provided by #fmpwizard works for Select2 3.5.2 and below, but it will not work in 4.0.0.
Since very early on (but perhaps not as early as this question), Select2 has supported "tagging": where users can add in their own value if you allow them to. This can be enabled through the tags option, and you can play around with an example in the documentation.
$("select").select2({
tags: true
});
By default, this will create an option that has the same text as the search term that they have entered. You can modify the object that is used if you are looking to mark it in a special way, or create the object remotely once it is selected.
$("select").select2({
tags: true,
createTag: function (params) {
return {
id: params.term,
text: params.term,
newOption: true
}
}
});
In addition to serving as an easy to spot flag on the object passed in through the select2:select event, the extra property also allows you to render the option slightly differently in the result. So if you wanted to visually signal the fact that it is a new option by putting "(new)" next to it, you could do something like this.
$("select").select2({
tags: true,
createTag: function (params) {
return {
id: params.term,
text: params.term,
newOption: true
}
},
templateResult: function (data) {
var $result = $("<span></span>");
$result.text(data.text);
if (data.newOption) {
$result.append(" <em>(new)</em>");
}
return $result;
}
});
For version 4+ check this answer below by Kevin Brown
In Select2 3.5.2 and below, you can use something like:
$(selector).select2({
minimumInputLength:1,
"ajax": {
data:function (term, page) {
return { term:term, page:page };
},
dataType:"json",
quietMillis:100,
results: function (data, page) {
return {results: data.results};
},
"url": url
},
id: function(object) {
return object.text;
},
//Allow manually entered text in drop down.
createSearchChoice:function(term, data) {
if ( $(data).filter( function() {
return this.text.localeCompare(term)===0;
}).length===0) {
return {id:term, text:term};
}
},
});
(taken from an answer on the select2 mailing list, but cannot find the link now)
Just for the sake of keep the code alive, I'm posting #rrauenza Fiddle's code from his comment.
HTML
<input type='hidden' id='tags' style='width:300px'/>
jQuery
$("#tags").select2({
createSearchChoice:function(term, data) {
if ($(data).filter(function() {
return this.text.localeCompare(term)===0;
}).length===0)
{return {id:term, text:term};}
},
multiple: false,
data: [{id: 0, text: 'story'},{id: 1, text: 'bug'},{id: 2, text: 'task'}]
});
Since many of these answers don't work in 4.0+, if you are using ajax, you could have the server add the new value as an option. So it would work like this:
User searches for value (which makes ajax request to server)
If value found great, return the option. If not just have the server append that option like this: [{"text":" my NEW option)","id":"0"}]
When the form is submitted just check to see if that option is in the db and if not create it before saving.
There is a better solution I think now
simply set tagging to true on the select options ?
tags: true
from https://select2.org/tagging
Improvent on #fmpwizard answer:
//Allow manually entered text in drop down.
createSearchChoice:function(term, data) {
if ( $(data).filter( function() {
return term.localeCompare(this.text)===0; //even if the this.text is undefined it works
}).length===0) {
return {id:term, text:term};
}
},
//solution to this error: Uncaught TypeError: Cannot read property 'localeCompare' of undefined
Thanks for the help guys, I used the code below within Codeigniter I I am using version: 3.5.2 of select2.
var results = [];
var location_url = <?php echo json_encode(site_url('job/location')); ?>;
$('.location_select').select2({
ajax: {
url: location_url,
dataType: 'json',
quietMillis: 100,
data: function (term) {
return {
term: term
};
},
results: function (data) {
results = [];
$.each(data, function(index, item){
results.push({
id: item.location_id,
text: item.location_name
});
});
return {
results: results
};
}
},
//Allow manually entered text in drop down.
createSearchChoice:function(term, results) {
if ($(results).filter( function() {
return term.localeCompare(this.text)===0;
}).length===0) {
return {id:term, text:term + ' [New]'};
}
},
});
I just stumbled upon this from Kevin Brown.
https://stackoverflow.com/a/30019966/112680
All you have to do for v4.0.6 is use tags: true parameter.
var text = 'New York Mills';
var term = 'new york mills';
return text.localeCompare(term)===0;
In most cases we need to compare values with insensitive register. And this code will return false, which will lead to the creation of duplicate records in the database. Moreover String.prototype.localeCompare () is not supported by browser Safary and this code will not work in this browser;
return this.text.localeCompare(term)===0;
will better replace to
return this.text.toLowerCase() === term.toLowerCase();

Resources