jquery select2 plugin how to match only beginning of word - autocomplete

I am using select2 in my website and i want the autocomplete to match only the beginning of the word. For example, if I type "CA" I want CAmeroun to appear and not "vatiCAn".

I figured out how to resolve this by searching in the documentation (here https://github.com/select2/select2/issues/428).
In select2 library, replace in select2.js :
matcher: function(term, text) {
return stripDiacritics(''+text).toUpperCase().indexOf(stripDiacritics(''+term).toUpperCase()) >= 0;
},
by :
matcher: function(term, text) {
if (text.toUpperCase().indexOf(term.toUpperCase()) == 0) {
return true;
}
},
And tadaaa. It works. I hope someone who is better in JS (99% of JS developers) could give a better answer or create a good patch.
Don't forget to minify your JS ;) !

Inspired by #IsmailH answer. I've merged this code as matchCustom in the provided example, here.
And here's my modification,
function matchCustom(params, data) {
// If there are no search terms, return all of the data
if ($.trim(params.term) === '') {
return data;
}
// `params.term` should be the term that is used for searching
// `data.text` is the text that is displayed for the data object
if (data.text.toUpperCase().indexOf(params.term.toUpperCase()) == 0) {
var modifiedData = $.extend({}, data, true);
// You can return modified objects from here
// This includes matching the `children` how you want in nested data sets
return modifiedData;
}
// Return `null` if the term should not be displayed
return null;
};

Related

Wagtail - how to get tags to work with `telepath` (tags in streamfield)?

I can use tags in regular page fields without any issue. When using tags within blocks (within a streamfield), the UI works and the tags are saved BUT the current page tags do not show up when loading the page in the admin. That's because the current value is not in the template anymore, it's in a JSON loaded via telepath.
I can confirm that the tags are saved and present in the data passed to initBlockWidget in the page source but these are ignored. Also, if I used a regular text field instead of the tag-widget, I can see the saved-values in the admin.
This is the code I have (which used to be enough before the refactor with telepath).
from wagtail.admin.widgets import AdminTagWidget
class TagBlock(TextBlock):
#cached_property
def field(self):
field_kwargs = {"widget": AdminTagWidget()}
field_kwargs.update(self.field_options)
return forms.CharField(**field_kwargs)
I think the following link is what I need to complete somehow to get it to work: https://docs.wagtail.io/en/stable/reference/streamfield/widget_api.html#form-widget-client-side-api
I've tried with this:
class AdminTagWidgetAdapter(WidgetAdapter):
class Media:
js = [
"wagtailadmin/js/vendor/tag-it.js",
"js/admin/admin-tag-widget-adapter.js",
]
register(AdminTagWidgetAdapter(), AdminTagWidget)
And under js/admin/admin-tag-widget-adapter.js:
console.log("adapter"); // this shows up in the console
class BoundWidget { // copied from wagtail source code
constructor(element, name, idForLabel, initialState) {
var selector = ':input[name="' + name + '"]';
this.input = element.find(selector).addBack(selector); // find, including element itself
this.idForLabel = idForLabel;
this.setState(initialState);
}
getValue() {
return this.input.val();
}
getState() {
return this.input.val();
}
setState(state) {
this.input.val(state);
}
getTextLabel(opts) {
const val = this.getValue();
if (typeof val !== 'string') return null;
const maxLength = opts && opts.maxLength;
if (maxLength && val.length > maxLength) {
return val.substring(0, maxLength - 1) + '…';
}
return val;
}
focus() {
this.input.focus();
}
}
// my code here:
class AdminTagWidget {
constructor(html, idPattern) {
this.html = html;
this.idPattern = idPattern;
}
boundWidgetClass = BoundWidget;
render(placeholder, name, id, initialState) {
console.log("RENDER", placeholder, name, id, initialState); // this does not show
var html = this.html.replace(/__NAME__/g, name).replace(/__ID__/g, id);
var idForLabel = this.idPattern.replace(/__ID__/g, id);
var dom = $(html);
$(placeholder).replaceWith(dom);
// eslint-disable-next-line new-cap
return new this.boundWidgetClass(dom, name, idForLabel, initialState);
}
}
console.log("here") // does show in the console
// variants I've tried:
//window.telepath.register('wagtail.admin.widgets.tags.AdminTagWidget', AdminTagWidget);
//window.telepath.register('wagtail.widgets.AdminTagWidget', AdminTagWidget);
window.telepath.register('path.where.its.used.AdminTagWidget', AdminTagWidget)
The log from my custom render method does not show. It seems that I'm not calling the right path within window.telepath.register but I don't know how what the string is supposed to be...
I'm not even sure if this is the right way forward.
Notes:
it works in regular field, the question is about tags in blocks
I'm using Wagtail version 2.13.2 but I've also tried with 2.15 without any difference.
In the console, I can log window.telepath and see my custom widget. It's just not "applied" to anything
Your WidgetAdapter class needs a js_constructor attribute:
class AdminTagWidgetAdapter(WidgetAdapter):
js_constructor = 'myapp.widgets.AdminTagWidget'
class Media:
js = [
"wagtailadmin/js/vendor/tag-it.js",
"js/admin/admin-tag-widget-adapter.js",
]
Any string value will work here - it just needs to uniquely identify the class, so it's recommended to use a dotted module-like path to avoid colliding with others. This then matches the string you pass to window.telepath.register on the Javascript side:
window.telepath.register('myapp.widgets.AdminTagWidget', AdminTagWidget)

Codemirror adding keywords on the fly for syntax highlight

I am currently adding a new language mode in CodeMirror for my current project. This is a proprietary language in which user can create a new keyword. Basically I am trying to update existing keyword list at the runtime and my syntax highlighter can pick this new keyword.
var mode = editor.doc.modeOption;
if(mode === "dmsrl") mode = "text/dmsrl";
var keyWords = CodeMirror.resolveMode(mode).keywords;
keyWords[x]=true;
I am currently trying to add new keyword like above, but somehow the list is not getting updated and new keyword is unavailable in my tokebase() method.
Any help would be appreciated.
You can try to redefine hintOptions object, that pass to Codemirror's init function and than building hints in your specific hint addon with this data. Just try this
cm.setOption("hintOptions", { "keywords" : ["k1", "k2"] });
Look at first in sql-hint for example (link):
cm.setOption("hintOptions", { "tables" : ["k1", "k2"] });
For sql-mode this is not heavy operation
I wanted to reach the same goal as yours but with some more degree of freedom, consisting in inputting a container, which I can re-define along the run.
1) Put the following code into a file custom.mode.js, to be loaded from your web page
var _glob_keywords = [ [ "key1", "keyword1" ],
[ "key2", "keyword2" ]
] ;
var cm_custom_check_stream_fn = function( stream )
{
for( var _i = 0 ; _i < _glob_keywords.length ; _i++ )
{
if ( stream.match( _glob_keywords[_i][0] ) ) return _glob_keywords[_i][1] ;
}
return "" ;
}
CodeMirror.defineMode("custom.mode", function()
{
return {
token: function(stream,state)
{
var _ret = cm_custom_check_stream_fn( stream ) ;
if ( _ret.length > 0 ) return _ret ;
else { stream.next(); return null; }
}
};
});
This code will be automatically embedded into the Codemirror object to dynamically handle the input in the textbox.
Example: if "key1" is found, then "keyword1" is returned.
We assume that "keyword1", "keyword2" refer to entries inside a custom css definitions file, as explained in the codemirror documentation, that is,
.cm-keyword1 { color:#8BA8C4; }
.cm-keyword2 { color:lime; }
Hope it helps!

How can I get an alert if a specific url substring is present and nothing if null

function getCode() {
if (window.location.href.indexOf("?discount=")) {
var url = (document.URL);
var id = url.substring(url.lastIndexOf('=') + 1);
window.alert(id);
return true;
} else {
return false;
}
}
Purpose: When people go to our "Service Request" page using a QR code that has a substring of ?discount=1234. I have been testing by creating an alert box with the discount code showing. Eventually I want to be able to populate that "1234" automatically into a "Discount Code:" text field on page load.
The above is a mixture of a few suggestions when I researched it.
Result: Going to example.com/serviceRequest.html?discount=1234 gives me the appropriate alert "1234", as I want... Going to example.com/serviceRequest.html gives me the alert http://example.com/serviceRequest.html, but I don't want anything to happen if "?discount=" is null.
Any suggestions?
indexOf returns -1 if the search pattern doesn't exist. In JavaScript, anything not a 0 or false or undefined is considered true.
So your line of:
if(window.location.href.indexOf("?discount=")) {
Would better search as:
if(window.location.href.indexOf("?discount=") > -1) {
Try changing your if-statement to:
if(window.location.href.indexOf("?discount=") != -1)
Look up the documentation for ".indexOf". It returns -1 for not found and >= 0 if it is found.
...indexOf("?discount=") >= 0
substring and indexOf return -1 if the text is not found, so you can test for this. E.g.
function getCode() {
if(window.location.href.indexOf("?discount=") != -1) {
var url = (document.URL);
var id = url.substring(url.lastIndexOf('=') + 1);
window.alert(id);
return true;
}
else {
return false;
}
}
You just need to test the indexOf value:
function getCode() {
if (window.location.href.indexOf("?discount=") !== -1) {
var url = (document.URL);
var id = url.substring(url.lastIndexOf('=') + 1);
window.alert(id);
return true;
}
else {
return false;
}
}
So the quick and dirty answer would be
var discount = window.location.search.split("?discount=")[1];
alert(discount);
But this doesn't take into account the occurence of other query string parameters.
You'll really want to parse all the query parameters into a hash map.
This article does a good job of showing you a native and jQuery version.
http://jquery-howto.blogspot.com/2009/09/get-url-parameters-values-with-jquery.html

tinymce.dom.replace throws an exception concerning parentNode

I'm writing a tinyMce plugin which contains a section of code, replacing one element for another. I'm using the editor's dom instance to create the node I want to insert, and I'm using the same instance to do the replacement.
My code is as follows:
var nodeData =
{
"data-widgetId": data.widget.widgetKey(),
"data-instanceKey": "instance1",
src: "/content/images/icon48/cog.png",
class: "widgetPlaceholder",
title: data.widget.getInfo().name
};
var nodeToInsert = ed.dom.create("img", nodeData);
// Insert this content into the editor window
if (data.mode == 'add') {
tinymce.DOM.add(ed.getBody(), nodeToInsert);
}
else if (data.mode == 'edit' && data.selected != null) {
var instanceKey = $(data.selected).attr("data-instancekey");
var elementToReplace = tinymce.DOM.select("[data-instancekey=" + instanceKey + "]");
if (elementToReplace.length === 1) {
ed.dom.replace(elementToReplace[0], nodeToInsert);
}
else {
throw new "No element to replace with that instance key";
}
}
TinyMCE breaks during the replace, here:
replace : function(n, o, k) {
var t = this;
if (is(o, 'array'))
n = n.cloneNode(true);
return t.run(o, function(o) {
if (k) {
each(tinymce.grep(o.childNodes), function(c) {
n.appendChild(c);
});
}
return o.parentNode.replaceChild(n, o);
});
},
..with the error Cannot call method 'replaceChild' of null.
I've verified that the two argument's being passed into replace() are not null and that their parentNode fields are instantiated. I've also taken care to make sure that the elements are being created and replace using the same document instance (I understand I.E has an issue with this).
I've done all this development in Google Chrome, but I receive the same errors in Firefox 4 and IE8 also. Has anyone else come across this?
Thanks in advance
As it turns out, I was simply passing in the arguments in the wrong order. I should have been passing the node I wanted to insert first, and the node I wanted to replace second.

How can I tell if a <script> tag with a given src attribute is present on the page in ASP.net?

Like the title says, I'm trying to find out if I need to include a script library that my ASP.net UserControl needs to work. I don't want to include it multiple times per page, but I want my control to be able to be used multiple times on the same page.
How can I, in the codebehind of my control, check to see if a given <script/> tag is present?
This is .Net 2.0, no LINQ.
If !Page.ClientScript.IsClientScriptIncludeRegistered("jQuery")
Page.ClientScript.RegisterClientScriptInclude("jQuery", "/scripts/jquery.js");
or if you need a script block, And want to include the control's type:
if (!Page.ClientScript.IsClientScriptBlockRegistered(this.GetType(), "myScript"))
Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "myScript"
, "<script>alert('xx');</script>", false);
This might be a bit overkill for your usage, but I'm using it to search for existing CSS and JS before adding to the page:
private static bool HeaderLinkExists(Page page, string path)
{
path = path.ToLowerInvariant();
foreach (Control c in page.Header.Controls)
{
if (c is HtmlLink)
{
// stylesheet (or other links), check href
HtmlLink link = (HtmlLink)c;
if (link.Href.ToLowerInvariant().Contains(path))
{
return true;
}
}
else if (c is HtmlGenericControl)
{
// any generic html tag, check for src or href
HtmlGenericControl hgc = (HtmlGenericControl)c;
if ((!string.IsNullOrEmpty(hgc.Attributes["src"]) && hgc.Attributes["src"].ToLowerInvariant().Contains(path)) || (!string.IsNullOrEmpty(hgc.Attributes["href"]) && hgc.Attributes["href"].ToLowerInvariant().Contains(path)))
{
return true;
}
}
else if (c is LiteralControl)
{
// scripts or other html literal controls, use regex to look for src or hrefs and check each one
LiteralControl lit = (LiteralControl)c;
if (MatchLiteralText(lit.Text, path))
{
return true;
}
}
else if (c is Literal)
{
// similar to above, use regex to look for src or hrefs and check each one
Literal lit = (Literal)c;
if (MatchLiteralText(lit.Text, path))
{
return true;
}
}
}
return false;
}
private static readonly Regex linkMatcher = new Regex(#"(?:src|href)\s*=\s*([""']?)(?<LinkValue>[^\1]+?)[\1>]", RegexOptions.Compiled);
private static bool MatchLiteralText(string text, string path)
{
if (!string.IsNullOrEmpty(text))
{
text = text.ToLowerInvariant()
foreach (Match m in linkMatcher.Matches(text))
{
if (m.Groups["LinkValue"].Value.Contains(path))
{
return true;
}
}
}
return false;
}
// usage:
if (!HeaderLinkExists(page, "/css/controlstyles.css"))
{
HtmlHeadUtility.RegisterStylesheetInHeader(page, "~/css/controlstyles.css");
}
if (!HeaderLinkExists(page, "/js/controlscript.js"))
{
HtmlHeadUtility.RegisterClientScriptIncludeInHeader(page, "~/js/controlscript.js");
}
It works great if you're sure that the LINK or SCRIPT is guaranteed to be in the HEAD.
No, there isn't.
Instead, you should maintain a set of <script> tags that have already been included, and update it whenever you add a script. (For example, a [ThreadStatic] static List<String>)
I would suggest re-thinking your problem/solution. It seems like what you are trying to do would couple your user control and the pages that use it. This would require that a page reference that script in order to use a control.
Is there any reason you can't just put the script tag in your user control so that the pages consuming your user control don't need to know that they need to include a specific script tag to work?
I tend to think that if you want to just use a control you should be able to just use it.