Get CodeMirror instance - javascript

I want to get an instance of CodeMirror (it is binded to a textarea '#code'). From an onclick-event I want to add a value to the current value of the CodeMirror instance. How can this be achieved? From the docs I can't seem to find anything to get an instance and bind it to a loca var in javascript.

Another method I have found elsewhere is as follows:
//Get a reference to the CodeMirror editor
var editor = document.querySelector('.CodeMirror').CodeMirror;
This works well when you are creating the CodeMirror instance dynamically or replacing an existing DOM element with a CodeMirror instance.

Someone just posted an answer but removed it. Nevertheless, it was a working solution.
Thanks!
-- Basically this was his solution:
// create an instance
var editor = CodeMirror.fromTextArea('code');
// store it
$('#code').data('CodeMirrorInstance', editor);
// get it
var myInstance = $('code').data('CodeMirrorInstance');
// from here on the API functions are available to 'myInstance' again.

There is a getWrapperElement on code mirror editor objects which gives you the root DOM element of the code mirror instance:
var codemirrorDomElem = editor.getWrapperElement();

You can find the instance starting with the <textarea> and moving to the next sibling.
Native
Functional
document.querySelector('#code').nextSibling,
Selector
document.querySelector('#code + .CodeMirror'),
jQuery
Functional
$('#code').next('.CodeMirror').get(0),
Selector
$('#code + .CodeMirror').get(0)
Extra: A more advanced solution involving clipboard.js -> JSFiddle Demo
Example
// Selector for textarea
var selector = '#code';
$(function() {
var editor = CodeMirror.fromTextArea($(selector).get(0), {
mode: 'javascript',
theme: 'paraiso-dark',
lineNumbers : true
});
editor.setSize(320, 240);
editor.getDoc().setValue(JSON.stringify(getSampleData(), null, 4));
$('#response').text(allEqual([
document.querySelector(selector).nextSibling, // Native - Functional
document.querySelector(selector + ' + .CodeMirror'), // Native - Selector
$(selector).next('.CodeMirror').get(0), // jQuery - Functional
$(selector + ' + .CodeMirror').get(0) // jQuery - Selector
]));
});
function allEqual(arr) {
return arr.every(function(current, index, all) {
return current === all[(index + 1) % all.length];
});
};
// Return sample JSON data.
function getSampleData() {
return [
{ color: "red", value: "#f00" },
{ color: "green", value: "#0f0" },
{ color: "blue", value: "#00f" }
];
}
#response { font-weight: bold; }
<link href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.7.0/codemirror.min.css" rel="stylesheet"/>
<link href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.7.0/theme/paraiso-dark.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.7.0/codemirror.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div>All equal?: <span id="response"></span></div>
<textarea rows="10" cols="60" id="code"></textarea>

I am using with Vue CLI 3, vue-codemirror component like this:
<codemirror id="textarea_editor" v-model="textarea_input" :options="cm_editor_config"></codemirror>
import codemirror to use within page:
import { codemirror } from 'vue-codemirror'
import 'codemirror/lib/codemirror.css'
and simply add codemirror inside components object, thereafter configuration in data section is:
codemirror_editor: undefined,
cm_editor_config: {
tabSize: 4,
mode: 'application/json',
theme: 'base16-dark',
lineNumbers: true,
// lineWrapping: true,
styleActiveSelected: true,
line: true,
}
and initialized the object in mounted lifecycle section of Vue:
mounted () {
if ( !this.codemirror_editor ) {
this.codemirror_editor = $('#textarea_editor').find('.CodeMirror').get(0).CodeMirror;
}
// do something with this.codemirror_editor
}
Hope this would help many one.

You can simply drop the var: instead of having
var editor = CodeMirror.fromTextArea...
Just have
editor = CodeMirror.fromTextArea...
Then editor is directly available to use in other functions

Related

Materialize dropdown options

I want to use the options from here:
http://materializecss.com/dropdown.html#options (The docs don't say so much).
My app is a rails app that use the materialize gem with the asset
pipeline.
My code now looks like this:
ul#dropdown1.dropdown-content.z-depth-0
li
a Profile settings
li
a payments
a.dropdown-button.btn-large.btn-flat.waves-effect.menu_trigger href="#!" data-activates="dropdown1"
i.material-icons menu
javascript:
var elem = document.querySelector('.menu_trigger');
var instance = M.Dropdown.init(elem, {
coverTrigger: false,
constrainWidth: false,
});
In practice, using the data attribute isn't always the best way. Options can (or rather should be, correct me if I'm wrong) passed the following way:
// native javascript way
document.addEventListener('DOMContentLoaded', function() {
var dropdown1 = document.querySelector('.simple-dropdown');
var dropdownOptions = {
'closeOnClick': true,
'hover':true
}
var instanceDropdown1 = M.Dropdown.init(dropdown1, dropdownOptions);
});
// Initializing the jQuery way
$(".simple-dropdown").dropdown(
{
'closeOnClick': true,
'hover': true,
});
Solved!
Finally like it says here http://archives.materializecss.com/0.100.2/dropdown.html#options
Solved where it says:
To use these inline you have to add them as data attributes. If you
want more dynamic control, you can define them using the jQuery plugin
below.
So then, with something like this:
a.dropdown-button.btn-large.btn-flat.waves-effect href="#!" data-activates="dropdown1" data-beloworigin="true"
i.material-icons menu
Done what i wanted
Couldn't find directly in Materialize docs, but by trial and error I found this javscript code is working fine. It looks like an expected options variable should be an Object with property "dropdownOptions" with assigned another Object with properties listed in docs.
document.addEventListener('DOMContentLoaded', function () {
var options = {
dropdownOptions: {
alignment: 'right',
hover: true
}
}
var elems = document.querySelectorAll('.dropdown-trigger');
var instances = M.Dropdown.init(elems, options);
});

Render angular directives in Selectize.js items

I am using angular-selectize to use Selectize.js in my angular project.
To use custom items in Selectize.js selector, I am using Selectize.js' render option:
render: {
item: function(item, escape) {
var avatar = '<div>' +
'<span avatars="\'' + escape(item._id) +'\'" class="avatars">' +
'</span>' +
escape(item.nick) +
'</div>';
var compiledAvatar = $compile(avatar)($rootScope);
$timeout();
return compiledAvatar.html();
},
where avatars is a custom directive with asychronous behaviour
The problem is that the render.item function expects an HTML string as an output but:
There is no way of returning a rendered or "$compileed" HTML string in a synchronous way as expected by render.item method.
I do not know how to render that item's elements afterwards when they have already been added to the DOM.
Note that although $compile is called, returned string would not be the expected compiled result but the string before compilation due to the asynchronous nature of $compile.
One idea is to use DOM manipulation which is not the most recommended Angular way, but I got it working on this plunker. and a second one with custom directive and randomized data to simulate your compiled avatar.
To simulate your asynchronous call, I use ngResource. My render function returns a string "<div class='compiledavatar'>Temporary Avatar</div>" with a special class markup compiledavatar. For a second or two, you will see Temporary Avatar as you select an element. When the ngResource calls finishes I look for the element with class compiledavatar and then replace the html with what I downloaded. Here is the full code:
var app = angular.module('plunker', ['selectize', 'ngResource']);
app.controller('MainCtrl', function($scope, $resource, $document) {
var vm = this;
vm.name = 'World';
vm.$resource = $resource;
vm.myModel = 1;
vm.$document = $document;
vm.myOptions = [{
id: 1,
title: 'Spectrometer'
}, {
id: 2,
title: 'Star Chart'
}, {
id: 3,
title: 'Laser Pointer'
}];
vm.myConfig = {
create: true,
valueField: 'id',
labelField: 'title',
delimiter: '|',
placeholder: 'Pick something',
onInitialize: function(selectize) {
// receives the selectize object as an argument
},
render: {
item: function(item, escape) {
var label = item.title;
var caption = item.id;
var Stub = vm.$resource('mydata', {});
// This simulates your asynchronous call
Stub.get().$promise.then(function(s) {
var result = document.getElementsByClassName("compiledavatar")
angular.element(result).html(s.compiledAvatar);
// Once the work is done, remove the class so next time this element wont be changed
// Remove class
var elems = document.querySelectorAll(".compiledavatar");
[].forEach.call(elems, function(el) {
el.className = el.className.replace(/compiledavatar/, "");
});
});
return "<div class='compiledavatar'>Temporary Avatar</div>"
}
},
// maxItems: 1
};
});
To simulate the JSON API I just created a file in plunker mydata:
{
"compiledAvatar": "<div><span style='display: block; color: black; font-size: 14px;'>an avatar</span></div>"
}
Of course your compiled function should return you something different every calls. Me it gives me the same to demonstrate the principle.
In addition, if your dynamic code is an Agular directive, here is a second plunker with a custom directive and randomized data so you can better see the solution:
The data include a custom directive my-customer:
[{
"compiledAvatar": "<div><span style='display: block; color: black; font-size: 14px;'>an avatar #1 <my-customer></my-customer></span></div>"
},
{
"compiledAvatar": "<div><span style='display: block; color: black; font-size: 14px;'>an avatar #2 <my-customer></my-customer></span></div>"
},
(...)
The directive is defined as:
app.directive('myCustomer', function() {
return {
template: '<div>and a custom directive</div>'
};
});
And the main difference in the app is that you have to add $compile when replacing the HTML and the text should show An avatar #(number) and a custom directive. I get an array of json value and use a simple random to pick a value. Once the HTML is replaced I remove the class, so next time only the last added element will be changed.
Stub.query().$promise.then(function(s) {
var index = Math.floor(Math.random() * 10);
var result = document.getElementsByClassName("compiledavatar")
angular.element(result).html($compile(s[index].compiledAvatar)($scope));
// Remove class
var elems = document.querySelectorAll(".compiledavatar");
[].forEach.call(elems, function(el) {
el.className = el.className.replace(/compiledavatar/, "");
});
});
Also, I looked at selectize library and you cant return a promise... as it does a html.replace on the value returned by render. This is why I went to the route of a temporary string with a class to retrieve later and update.
Let me know if that helps.
This answer is based in the helpful answer by #gregori with the following differences:
Take into account Selectize.js' Render Cache. The standard behaviour of Selectize.js is that the items are cached as returned by the render function, and not with the modifications we have done to them. After adding and deleting some elements, the cached and not the modified version would be displayed if we do not update the render cache acordingly.
Using random id's to identify the elements to select to be manipulated from DOM.
Using watchers to know when the compilation has been done
First, we define a method to modify the selectize.js render cache:
scope.selectorCacheUpdate = function(key, value, type){
var cached = selectize.renderCache[type][key];
// update cached element
var newValue = angular.element(cached).html(value);
selectize.renderCache[type][key] = newValue[0].outerHTML;
return newValue.html();
};
Then, the render function is defined as follows:
function renderAvatar(item, escape, type){
// Random id used to identify the element
var randomId = Math.floor(Math.random() * 0x10000000).toString(16);
var avatar =
'<div id="' + randomId + '">' +
'<span customAvatarTemplate ...></span>' +
...
'</div>';
var compiled = $compile(avatar)($rootScope);
// watcher to see when the element has been compiled
var destroyWatch = $rootScope.$watch(
function (){
return compiled[0].outerHTML;
},
function (newValue, oldValue){
if(newValue !== oldValue){
var elem = angular.element(document.getElementById(randomId));
var rendered = elem.scope().selectorCacheUpdate(item._id, compiled.html(), type);
// Update DOM element
elem.html(rendered);
destroyWatch();
}
}
);
});
return avatar;
}
Note: The key for the render cache is the valueField of the selectize items, in this case, _id
Finally, we add this function as a selectize render function in the selectize configuration object:
config = {
...
render: {
item: function(i,e){
return renderAvatar(i, e, 'item');
},
option: function(i,e){
return renderAvatar(i, e, 'option');
}
},
...
}
For more details, see how this solution has been added to the application that motivated this question: https://github.com/P2Pvalue/teem/commit/968a437e58c5f1e70e80cc6aa77f5aefd76ba8e3.

ACE Editor - Beautify for CSS

I'm currently implementing ace as an editor for several programming languages. I really want to implement a beautify functionality and currently I use this approach:
var beautify = ace.require("ace/ext/beautify"); // get reference to extension
var editor = ace.edit("editor"); // get reference to editor
beautify.beautify(editor.session);
More info
Sadly this does not format CSS correctly. Example:
.class1 .subClass { color: red; }
beautified via the described code this changes to
.class1.subClass{
color:red;
}
as you can see all spaces in the selector were removed and this changes the target of the rule.
Is my code wrong? Is there a alternative beautifier for CSS in ace?
As a fallback I would remove the functionality which isn't the ideal solution.
TL;DR
Is there a plugin/extension for ace that can beautify CSS correctly? Am I doing something wrong?
Well I found a nice css beautifier which i added to my solution.
Here's the magic:
container.querySelector('.editor_beautify').addEventListener('click', function () {
var currentMode = editor.session.$modeId.substr(editor.session.$modeId.lastIndexOf('/') + 1);
switch (currentMode) {
case modes.css:
util.formatCss(editor, configuration.scriptBase);
break;
default:
util.ensureScript(configuration.scriptBase + 'ext-beautify.js', function () {
var beautify = ace.require("ace/ext/beautify").beautify(editor.session);
});
}
});
formatCss: function (editorAce, scriptBase) {
var unformatted = editorAce.getValue();
if (unformatted.trim().length > 0) {
util.ensureScript(scriptBase + 'cssbeautify.js', function () {
editorAce.getSession().setUseWrapMode(false);
var formatted = cssbeautify(unformatted, {
indent: ' ',
openbrace: 'end-of-line',
autosemicolon: true
});
editorAce.setValue(formatted);
editorAce.getSession().setUseWrapMode(true);
});
}
}

Styling Polymer paper-slider

So I'm essentially wrapping the standard paper-slider element with a custom element, and wanting to include some styling. Below is what I currently have:
<dom-module id="my-slider">
<template>
<style>
/* Works */
paper-slider {
--paper-slider-active-color: red;
--paper-slider-knob-color: red;
--paper-slider-height: 3px;
}
/* Doesn't work */
paper-slider #sliderContainer.paper-slider {
margin-left: 0;
}
/* Also doesn't work */
.slider-input {
width: 100px;
}
</style>
<div>
<paper-slider editable$="[[editable]]" disabled$="[[disabled]]" value="{{value}}" min="{{min}}" max="{{max}}"></paper-slider>
</div>
</template>
<script>
Polymer({
is: "my-slider",
properties: {
value: {
type: Number,
value: 0,
reflectToAttribute: true
},
min: {
type: Number,
value: 0
},
max: {
type: Number,
value: 100
},
editable: Boolean,
disabled: Boolean
}
});
</script>
</dom-module>
JSFiddle: https://jsfiddle.net/nhy7f8tt/
Defining the variables that the paper-slider uses works as expected, but when I try to address anything inside of it directly via its selector, it doesn't work.
I'm fairly new to Polymer, so this may be a very simple/stupid question, but I'm really quite confused and would greatly appreciate any help!
A secondary (but related) issue is the clipping of the input to the right of the editable paper-slider element when the value is 100.
You could use selectors like ::shadow and /deep/ but they are deprecated. If an element doesn't provide the hooks (CSS variables and mixins) then you're basically out of luck.
What you can do, is to create a feature request in the elements GitHub repo to support additional selectors.
Another workaround I already used successfully is to add a style module.
var myDomModule = document.createElement('style', 'custom-style');
myDomModule.setAttribute('include', 'mySharedStyleModuleName');
Polymer.dom(sliderElem.root).appendChild(myDomModule);
I hope the syntax is correct. I use Polymer only with Dart.
The dom-module needs to be a custom-style even though this is normally only necessary when used outside a Polymer element.
See also https://github.com/Polymer/polymer/issues/2681 about issues I run into with this approach.
The following code is working for me,
attached: function(){
var sliderContainer = document.querySelector("#sliderContainer.editable.paper-slider");
sliderContainer.style.marginTop = "5px";
sliderContainer.style.marginBottom = "5px";
},

CKEditor - remove script tag with data processor

I am quite new with CKEditor (starting to use it 2 days ago) and I am still fighting with some configuration like removing the tag from editor.
So for example, if a user type in source mode the following:
<script type="text/javascript">alert('hello');</script>
I would like to remove it.
Looking the documentation, I found that this can be done using an HTML filter. I so defined it but it does not work.
var editor = ev.editor;
var dataProcessor = editor.dataProcessor;
var htmlFilter = dataProcessor && dataProcessor.htmlFilter;
htmlFilter.addRules(
{
elements :
{
script : function(element)
{
alert('Found script :' + element.name);
element.remove();
},
img : function( element )
{
alert('Found script :' + element.name);
if ( !element.attributes.alt )
element.attributes.alt = 'Cookingfactory';
}
}
});
The img part is working well but not the script one. I guess I missed something. It even does not display the alert message for script.
Any help would be more than welcome :o)
You can use this :
CKEDITOR.replace('editor1', {
on: {
pluginsLoaded: function(event) {
event.editor.dataProcessor.dataFilter.addRules({
elements: {
script: function(element) {
return false;
}
}
});
}
}
});
If you are using CKEditor 4.1 or above, you may use Advanced Content Filter to allow the content you want.
If you are using CKEditor 4.4 or above, there is an easier way. You can use Disallowed Content to filter content you don't like .
config.disallowedContent = 'script';
As I'm having CKEditor 4, I did the next
CKEDITOR.instances.editor1.config.protectedSource.push( /{.*\".*}/g );
It will ignore quotes in smarty curly brackets

Categories

Resources