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

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.

Related

Fabric Modding: Modifying the Enchantment Table using Mixins

I'm trying to add a new book item, that can be enchanted just like normal books, but that have a higher enchantability. Everything works so far, just one thing is missing: When you enchant normal books, they get converted to Enchanted Books. The code that does that, is located in the onButtonClick method of the EnchantmentScreenHandler class:
public boolean onButtonClick(PlayerEntity player, int id) {
if (id >= 0 && id < this.enchantmentPower.length) {
//...
if (//...) {
return false;
} else if (//...) {
return false;
} else {
this.context.run((world, pos) -> {
//...
if (//...) {
player.applyEnchantmentCosts(itemStack, i);
boolean bl = itemStack.isOf(Items.BOOK); //<--- "bl" is the important variable
if (bl) {
itemStack3 = new ItemStack(Items.ENCHANTED_BOOK);
NbtCompound nbtCompound = itemStack.getNbt();
if (nbtCompound != null) {
itemStack3.setNbt(nbtCompound.copy());
}
this.inventory.setStack(0, itemStack3);
}
}
So if the item that gets enchanted, is a Book, it gets later replaced with an Enchanted Book. So my idea was to just modify the assigment of bl using the #ModifyVariable annotation and then check, if the item is made of my modded book. But whatever I try, I can't get it to work. I always get the error No refMap loaded.. I figured it's not working, because the variable bl is located inside a lambda function, but I'm not sure if that's really the issue. Of course I can just replace the entire body of the method, but that wouldn't be good for compatibility with other mods.

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)

jquery select2 plugin how to match only beginning of word

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;
};

jsTree customize <li> using the alternative JSON format along with AJAX

I am using the alternative JSON format along with AJAX to load data in tree. Now there is a new ask, I am required to add a new element at the end of <li> tag.
I have created sample URL to display what I am currently doing.
Tree crated using alternative JSON format along with AJAX
And how the new LI should appear
Tree created using hard coded HTML but shows how the LI should look like
I think I should be able to do this if I use HTML Data but since the project is already live with JSON format this would require me to change a lot so before I start making this major change I just wanted to check if this is possible using JSON and AJAX format or not.
So I got answer from Ivan - Answer
In short there is misc.js in the src folder which has question mark plugin, this is the best example of what I wanted to do.
I tweaked its code for my needs and here is the new code.
(function ($, undefined) {
"use strict";
var span = document.createElement('span');
span.className = 'glyphicons glyphicons-comments flip jstree-comment'
$.jstree.defaults.commenticon = $.noop;
$.jstree.plugins.commenticon = function (options, parent) {
this.bind = function () {
parent.bind.call(this);
};
this.teardown = function () {
if (this.settings.commenticon) {
this.element.find(".jstree-comment").remove();
}
parent.teardown.call(this);
};
this.redraw_node = function (obj, deep, callback, force_draw) {
var addCommentIcon = true;
var data = this.get_node(obj).data;
//....Code for deciding whether comment icon is needed or not based on "data"
var li = parent.redraw_node.call(this, obj, deep, callback, force_draw);
if (li && addCommentIcon) {
var tmp = span.cloneNode(true);
tmp.id = li.id + "_commenticon";
var $a = $("a", li);
$a.append(tmp);
}
return li;
};
};
})(jQuery);

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.