Ace editor auto completion - autocomplete

I am working with ace editor in sql mode. I followed this link to enable auto completion: https://github.com/ajaxorg/ace/blob/master/demo/autocompletion.html. It generally works well. However, I want to tweak the auto completion a bit more in order to fulfill my further requirements. Here's wish list:
I hope the sql key words suggested are all in upper case. They are all in lower case by default;
I found as I type in some words, my previously input words are added to the suggestion dictionary, which is good. Can I programmatically add more words in the suggestion dictionary even before anything is typed in the editor. The reason I need this is that I want to preload some table names and field names into the dictionary.
I am new to this awesome editor. I hope to get some direction on how to tweak the auto completion feature. Thanks.

There is a pull request to add better completion for sql server mode https://github.com/ajaxorg/ace/pull/2460, sql mode could be handled same way.
to add more words you need to implement a custom completer, which is simple:
<!DOCTYPE html>
<html>
<head>
<script src="http://ajaxorg.github.io/ace-builds/src/ace.js">
</script>
<script src="http://ajaxorg.github.io/ace-builds/src/ext-language_tools.js">
</script>
<style>
#editor { position: absolute; top: 0; left: 0; right: 0; bottom: 0;}
</style>
</head>
<body>
<div id="editor">
press ctrl+space</div>
</body>
<script>
editor = ace.edit("editor")
editor.setOptions({
// mode: "ace/mode/javascript",
enableBasicAutocompletion: true
});
editor.completers.push({
getCompletions: function(editor, session, pos, prefix, callback) {
callback(null, [
{value: "foo", score: 1000, meta: "custom"},
{value: "bar", score: 1000, meta: "custom"}
]);
}
})
</script>
</html>
see also https://github.com/ajaxorg/ace/wiki/How-to-enable-Autocomplete-in-the-Ace-editor

Related

ArcGIS JavaScript API Popup Not Referencing REST Service Layer

The content in the popup created through the variable "popupCustom" is displaying string instead of referencing the specified field {IN_COUNTRY}. I followed the ArcGIS JS API Popup Tutorials, & can't see what my error is in failing to grab the attributes associated with that field. Here's the code -- any help is greatly appreciated!
*note: feature layer url within "Cyber_Areas" variable points to REST URL for referenced Feature Class.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
<title>Search widget with multiple sources - 4.6</title>
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
</style>
<link rel="stylesheet" href="https://js.arcgis.com/4.6/esri/css/main.css">
<script src="https://js.arcgis.com/4.6/"></script>
<script>
require([
"esri/Map",
"esri/views/MapView",
"esri/widgets/BasemapToggle",
"esri/widgets/Legend",
"esri/layers/TileLayer",
"esri/layers/FeatureLayer",
"esri/widgets/Search",
"esri/widgets/LayerList",
"esri/PopupTemplate",
"dojo/on",
"dojo/domReady!"
], function(
Map,
MapView,
BasemapToggle,
Legend,
TileLayer,
FeatureLayer,
Search,
LayerList,
PopupTemplate,
on
) {
var Cyber_Areas = new FeatureLayer({
url: "*inserturl*",
outFields: ["IN_COUNTRY"],
popupTemplate: popupCustom
});
var map = new Map({
basemap: "osm"
});
map.add(Cyber_Areas);
var view = new MapView({
container: "viewDiv",
map: map,
center: [-87.172865, 34.077613], // lon, lat
zoom: 16
});
var searchWidget = new Search({
view: view,
popupOpenOnSelect: false
});
view.ui.add(searchWidget, {
position: "top-left",
index: 0
});
var popupCustom = searchWidget.on('select-result', function(evt){
//console.info(evt);
view.popup.open({
location: evt.result.feature.geometry, // location of the click on the view
title: "Service Availability:", // title displayed in the popup
content: "<p><b>{IN_COUNTRY}"
});
});
});
</script>
</head>
<body>
<div id="viewDiv"></div>
</body>
</html>
From your code you are mixing the popup template value with when to display it. And those are two different things.
First, you are not setting correctly the popup template of the layer. It should be a PopupTemplate.
It seems to me that in you code the layer definition should be something like this,
var Cyber_Areas = new FeatureLayer({
url: "*inserturl*",
popupTemplate: {
outFields: ["IN_COUNTRY"],
title: "Service Availability:",
content: "<p><b>{IN_COUNTRY}</b></p>"
}
});
Now if you don't want the default behavior of the popup (left click on a feature), you cant disable it like this,
view.popup.autoOpenEnabled = false; // <- disable view popup auto open
And then you can open it wherever you want like this,
view.popup.open({ // <- open popup
location: evt.result.feature.geometry, // <- use map point of the event result
fetchFeatures: true // <- fetch the selected features (if any)
});
You have to understand that the fields you use in the content of the popup template are related to the layer. That is why i set in the popup of the view to fetch the results.

Polymer REST backend

I have a backend written in golang exposing /api/list interface. It returns lists when called from GET and create new list when it receive POST with parameters.
I can read it with standard core-ajax element, there is a huge amount of examples to do that.
What I didn't understood is what should I do, when I want to create new element through POST? I read the documentation and searched for sample code for half day, can you point me to right direction?
//
Ok, thanks for help, it was really only bad format of json I was sending. There is still dark cloud in my mind telling that I misunderstood something from conceptual view. Is this:
<link rel="import" href="bower_components/polymer/polymer.html">
<link rel="import" href="bower_components/core-ajax/core-ajax.html">
<polymer-element name="channels-service" attributes="channels">
<template>
<style>
:host {
display: none;
}
</style>
<core-ajax id="ch_load"
auto
url="/api/list"
on-core-response="{{channelsLoaded}}"
handleAs="json">
</core-ajax>
<core-ajax id="ch_update"
url="/api/list"
on-core-response="{{channelsUpdated}}"
method="POST"
handleAs="json">
</core-ajax>
</template>
<script>
Polymer('channels-service', {
created: function() {
this.channels = [];
},
channelsLoaded: function() {
// Make a copy of the loaded data
this.channels = this.$.ch_load.response.slice(0);
},
newChannel: function(ch_name) {
// this.$.ch_update.body = "ch_name";
this.$.ch_update.body = '{"Name":"pitchalist2"}'
this.$.ch_update.go();
},
channelsUpdated: function() {
//window.log(this.$.ch_update.response.slice(0));
}
});
</script>
</polymer-element>
correctly written data layer? It looks very counterintuitive to me and in examples using local data storage it works way easier.
You can send a POST request by setting the method attribute (method="POST") and the body attribute (body='{"my":"data"}'). Indeed you need a second iron-ajax element for this request.
See the attributes section in the iron-ajax documentation.

How to get popcorn.js working on dynamically loaded content?

I've followed this tutorial:
http://popcornjs.org/popcorn-101
Tutorial Code
<!doctype html>
<html>
<head>
<script src="http://popcornjs.org/code/dist/popcorn-complete.min.js"></script>
<script>
// ensure the web page (DOM) has loaded
document.addEventListener("DOMContentLoaded", function () {
// Create a popcorn instance by calling Popcorn("#id-of-my-video")
var pop = Popcorn("#ourvideo");
// add a footnote at 2 seconds, and remove it at 6 seconds
pop.footnote({
start: 2,
end: 6,
text: "Pop!",
target: "footnotediv"
});
// play the video right away
pop.play();
}, false);
</script>
</head>
<body>
<video height="180" width="300" id="ourvideo" controls>
<source src="http://videos.mozilla.org/serv/webmademovies/popcornplug.mp4">
<source src="http://videos.mozilla.org/serv/webmademovies/popcornplug.ogv">
<source src="http://videos.mozilla.org/serv/webmademovies/popcornplug.webm">
</video>
<div id="footnotediv"></div>
</body>
</html>
And can run this locally.
In Firebug, I see the footnote div update from:
<div style="display: none;">Pop!</div>
to:
<div style="display: inline;">Pop!</div>
On a live site however, I am loading my page html from a MongoDB database via Ajax and the footnote display functionality doesn't seem to be working.
Thinking this might have something to do with needing to 're-initialise' after the content has loaded, I've added the popcorn.js functionality to a function called on click:
Function
<script>
function myPopcornFunction() {
var pop = Popcorn("#ourvideo");
pop.footnote({
start: 2,
end: 6,
text: "Pop!",
target: "footnotediv"
});
pop.play();
}
</script>
Call
$(document).on("click","a.video", function (e) {
// passing values to python script and returning results from database via getJSON()
myPopcornFunction();
});
This doesn't seem to have an effect.
No footnotediv content is loaded when the video plays.
The video is also not playing automatically.
It's hard to reproduce in jsFiddle with dynamic content, so is there a generic approach to ensuring popcorn works with dynamically loaded content?
Firebug Error on click
TypeError: k.media.addEventListener is not a function
It seems to have been a timing issue in that originally I had made a call to the myPopcornFunction() outside of the function which loaded the content (a getJSON() function). When I placed the call within the same block as the getJSON() function, things seemed to maintain their 'order' and popcorn could work correctly.
Before
$(document).on("click","a.video", function (e) {
$.getJSON("/path", {cid: my_variable, format: 'json'}, function(results){
$("#content_area").html("");
$("#content_area").append(results.content);
});
e.preventDefault();
myPopcornFunction(); // the call WAS here
});
After
$(document).on("click","a.video", function (e) {
$.getJSON("/path", {cid: my_variable, format: 'json'}, function(results){
$("#content_area").html("");
$("#content_area").append(results.content);
myPopcornFunction(); // the call is now HERE
});
e.preventDefault();
});
The myPopcornFunction() was the same as in the original post.

How to disabled wysihtml5 HTML Clean Up in Editor?

How to disable HTML Clean Up while in the editor mode? I'm in a need of allowing css format & inline html in code. The idea is to disable parser and html clean up action when pasting the code and entering the Editor for editing. Thanks.
You can provide an identity function as the parser attribute upon initializing the wysihtml5 editor. The below script is a coffeescript snippet that disables the cleanup completely.
enableWysiwyg: ->
#$el.find('textarea').wysihtml5
"style": false
"font-styles": false #Font styling, e.g. h1, h2, etc. Default true
"emphasis": true #Italics, bold, etc. Default true
"lists": false #(Un)ordered lists, e.g. Bullets, Numbers. Default true
"html": true #Button which allows you to edit the generated HTML. Default false
"link": false #Button to insert a link. Default true
"image": false #Button to insert an image. Default true,
"color": false #Button to change color of font
parser: (html) -> html
JavaScript version of the above code:
$('textarea').wysihtml5({
"style": false,
"font-styles": false,
"emphasis": true,
"lists": false,
"html": true,
"link": false,
"image": false,
"color": false,
parser: function(html) {
return html;
}
});
Actually, this is what the parser rules are for.
You can attach your custom rules to the included var wysihtml5ParserRules before instantiate the editor object or just create your own rules object and give to the editor's constructor.
For example, to allow the h1 and h3 tag in addition to the tags allowed in the distributed simple example rules, you'd need to set up as follows:
<form>
<div id="toolbar" style="display: none;">
<a data-wysihtml5-command="bold" title="CTRL+B">bold</a> |
<a data-wysihtml5-command="italic" title="CTRL+I">italic</a>
<a data-wysihtml5-action="change_view">switch to html view</a>
</div>
<textarea id="textarea" placeholder="Enter text ..."></textarea>
<br><input type="reset" value="Reset form!">
</form>
<!-- The distributed parser rules -->
<script src="../parser_rules/simple.js"></script>
<script src="../dist/wysihtml5-0.4.0pre.min.js"></script>
<script>
// attach some custom rules
wysihtml5ParserRules.tags.h1 = {remove: 0};
wysihtml5ParserRules.tags.h3 = {remove: 0};
var editor = new wysihtml5.Editor("textarea", {
toolbar: "toolbar",
parserRules: wysihtml5ParserRules,
useLineBreaks: false
});
</script>
Now, when you enter/paste <title>test</title> into the editor, while you're in the editor mode, and then switch to html view, you'll get <title>test</title>. And when you switch back to editor view, you'll get <title>test</title> again.
That was the general part.
Now, in your case, I'm not sure if it's the best idea to work with 121 custom parser rules (the count of HTML tags to handle) or if it wouldn't be better to take the time and dig into the source code to find a more performant solution (doesn't make much sense to tell a parser to actualy just return the input string anyway, right?). Furthermore, you said you want to allow CSS as well. So your custom parser rules will even extend.
Anyway, as a starting point, feel free to use my custom parser rule set from here: https://github.com/eyecatchup/wysihtml5/blob/master/parser_rules/allow-all-html5.js.

Does codemirror provide Cut, Copy and Paste API?

From http://codemirror.net/doc/manual.html, I only find getRange(),
undo(), redo() etc, and I can't find cut(), copy() and paste API,
and more when I try to run editor.execCommand("cut"), I get the error.
Could you help me? Thanks!
Using clipboard.js, you can define the text() function to grab the value of the CodeMirror's inner document.
Store a reference to the (<textarea>) editor's selector for convenience.
var editorSelector = '#editor' // or '#editor + .CodeMirror';
Instantiate a new ClipBoard object with reference to your button.
new Clipboard('.clip-btn-native', {
text: function(trigger) {
return getCodeMirrorNative(editorSelector).getDoc().getValue();
}
});
Retrieve a CodeMirror Instance via native JavaScript.
function getCodeMirrorNative(target) {
var _target = target;
if (typeof _target === 'string') {
_target = document.querySelector(_target);
}
if (_target === null || !_target.tagName === undefined) {
throw new Error('Element does not reference a CodeMirror instance.');
}
if (_target.className.indexOf('CodeMirror') > -1) {
return _target.CodeMirror;
}
if (_target.tagName === 'TEXTAREA') {
return _target.nextSibling.CodeMirror;
}
return null;
};
Demo
Please see complete; in-depth demo over at JSFiddle.
There are no CodeMirror APIs for cut/copy/paste because browser security restrictions forbid JavaScript from accessing the clipboard programmatically. Paste could be used to steal private data and Cut/Copy can be used as a more elaborate attack vector.
The browser's own native code handles user gestures that access the clipboard (keyboard shortcuts and context menu items), based solely on the currently selected text or focused text field.
This SO thread has a good summary of attempts to work around these restrictions. CodeMirror's approach is the first bullet: it uses a hidden textarea to ensure that user clipboard gestures work, but that still doesn't support programmatic APIs.
But there is a partial workaround: use a small Flash widget (this is the 2nd bullet in the thread above). Flash relaxes the restrictions on Copy/Cut (but not Paste) a bit. It still has to be triggered by some user event, but it could be something like clicking a button in your HTML UI. Wrappers like ZeroClipboard and Clippy make it simple to access to these capabilities without needing to know Flash. You'd need to write a little glue code to pull the appropriate string from CodeMirror when copying, but it shouldn't be too bad.
Add a hidden contenteditable div to your textarea editor wrapper. Contenteditable divs respect new lines and tabs, which we need when copying code.
Here is my CodePen demo
var content = $('.content');
var toCopy = content.find('.copy-this');
// initialize the editor
var editorOptions = {
autoRefresh: true,
firstLineNumber: 1,
lineNumbers: true,
smartIndent: true,
lineWrapping: true,
indentWithTabs: true,
refresh: true,
mode: 'javascript'
};
var editor = CodeMirror.fromTextArea(content.find(".editor")[0], editorOptions);
content[0].editor = editor;
editor.save();
// set editors value from the textarea
var text = content.find('.editor').text();
editor.setValue(text);
// setting with editor.getValue() so that it respects \n and \t
toCopy.text(editor.getValue());
$(document).on('click', '.copy-code', function() {
var content = $(this).closest('.content');
var editor = content[0].editor;
var toCopy = content.find('.copy-this')[0];
var innerText = toCopy.innerText // using innerText here because it preserves newlines
// write the text to the clipboard
navigator.clipboard.writeText(innerText);
});
.content {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.CodeMirror {
height: fit-content !important;
}
.copy-code {
background: #339af0;
width: fit-content;
cursor: pointer;
}
<!-- resources -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.35.0/codemirror.css" />
<script src="https://codemirror.net/lib/codemirror.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.40.0/mode/javascript/javascript.min.js"></script>
<script src=""></script>
<script src=""></script>
<script src=""></script>
<script src=""></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="content">
<!-- button to copy the editor -->
<div class="copy-code" title="copy code">copy</div>
<!-- add contenteditable div as it respects new lines when copying unlike textarea -->
<div class="copy-this" contenteditable style="display: none"></div>
<textarea class="editor" style="display: none;">// here is a comment
// here is another comment
</textarea>
</div>