拡張Bundleは動的Bundleとjavascriptの混同をサポートする
拡張Bundleは動的Bundleとjavascriptの混同をサポートする
2つの目的:GlobalにBundleを毎回追加することなく、動的ページ上のBundleをサポートします. Javascript混同をサポート ASP.NET MVC持参Bundle
通常ASP.NET MVC 4.0以降持参したBundleは以下の通り.
ページでの使用:
新しいダイナミックBundle
重要でないページもあれば、わざわざBundleを作成する必要はないと思いますが、ページにBundleを動的に作成すると便利です.たとえば、ページで次のように使用します.
もちろん上はStyleは自分で作成した拡張子で、次の拡張子のソースコードが貼られています.
上のコードは分かりやすいと信じています.説明する必要はありません.上はただの考えで、人々は勝手にあなたの創作を発揮することができます.ただ、Javascript混同器を追加し、次のようなコードを生成するために、
混同器コードはネットで探したもので、以下の通りです.
上のコードには作者の情報がいくつかあります.
これを使用するには、BundleTransformを作成します.
もちろんこの混同器はHandlerとして使用することができ、あまり説明しません.
まとめ
上のコードはすべて接続されていて、私が使いたい2つの機能です.これは自分の少しの私欲のために作成された小さなツールにすぎず、一部の人に役に立つことを望んでいます.ちなみに、ネット上にはBundle Transformer:YUI 1.8.0などの強力なBundleプラグインがあります.興味があれば行ってみてください.
2つの目的:
通常ASP.NET MVC 4.0以降持参したBundleは以下の通り.
BundleConfig.RegisterBundles(BundleTable.Bundles);
public class BundleConfig
{
// For more information on Bundling, visit http://go.microsoft.com/fwlink/?LinkId=254725
public static void RegisterBundles(BundleCollection bundles)
{
// Use the development version of Modernizr to develop with and learn from. Then, when you're
// ready for production, use the build tool at http://modernizr.com to pick only the tests you need.
bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(
"~/Scripts/modernizr-*"));
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/jquery-{version}.js"));
#if !DEBUG
BundleTable.EnableOptimizations = true;
#endif
}
}
ページでの使用:
@Scripts.Render("~/bundles/jquery")
新しいダイナミックBundle
重要でないページもあれば、わざわざBundleを作成する必要はないと思いますが、ページにBundleを動的に作成すると便利です.たとえば、ページで次のように使用します.
@Html.Style("~/Scripts/JQueryUI2/themes/smoothness/jquery.ui.theme.css", "~/Scripts/JQueryUI2/themes/smoothness/jquery.ui.menu.css")
@Html.Script("~/Scripts/JQueryUI2/ui/jquery.ui.core.js", "~/Scripts/JQueryUI2/ui/jquery.ui.position.js", "~/Scripts/JQueryUI2/ui/jquery.ui.widget.js", "~/Scripts/JQueryUI2/ui/jquery.ui.menu.js")
もちろん上はStyleは自分で作成した拡張子で、次の拡張子のソースコードが貼られています.
public static class Extension
{
public static IHtmlString Script(this HtmlHelper helper, params string[] urls)
{
var bundleDirectory = "~/bundles/" + MakeBundleName("js", urls);
var bundle = BundleTable.Bundles.GetBundleFor(bundleDirectory);
if (bundle == null)
{
var transform = new JavascriptObfuscator();
bundle = new ScriptBundle(bundleDirectory).Include(urls);
bundle.Transforms.Add(transform);
BundleTable.Bundles.Add(bundle);
}
return Scripts.Render(bundleDirectory);
}
public static IHtmlString Style(this HtmlHelper helper, params string[] urls)
{
var bundleDirectory = "~/bundles/" + MakeBundleName("css", urls);
var bundle=BundleTable.Bundles.GetBundleFor(bundleDirectory);
if (bundle == null)
{
bundle = new StyleBundle(bundleDirectory).Include(urls);
BundleTable.Bundles.Add(bundle);
}
return Styles.Render(bundleDirectory);
}
private static string MakeBundleName(string type, params string[] urls)
{
var array =
urls.SelectMany(url => url.Split('/'))
.SelectMany(url => url.Split('.'))
.Distinct()
.Except(new[] {"~", type});
return string.Join("-", array);
}
}
上のコードは分かりやすいと信じています.説明する必要はありません.上はただの考えで、人々は勝手にあなたの創作を発揮することができます.ただ、Javascript混同器を追加し、次のようなコードを生成するために、
eval(function(p,a,c,k,e,d){e=function(c){return(c35?String.fromChar
混同器コードはネットで探したもので、以下の通りです.
///
/// Packs a javascript file into a smaller area, removing unnecessary characters from the output.
///
public class ECMAScriptPacker : IHttpHandler
{
///
/// The encoding level to use. See http://dean.edwards.name/packer/usage/ for more info.
///
public enum PackerEncoding { None = 0, Numeric = 10, Mid = 36, Normal = 62, HighAscii = 95 };
private PackerEncoding encoding = PackerEncoding.Normal;
private bool fastDecode = true;
private bool specialChars = false;
private bool enabled = true;
string IGNORE = "$1";
///
/// The encoding level for this instance
///
public PackerEncoding Encoding
{
get { return encoding; }
set { encoding = value; }
}
///
/// Adds a subroutine to the output to speed up decoding
///
public bool FastDecode
{
get { return fastDecode; }
set { fastDecode = value; }
}
///
/// Replaces special characters
///
public bool SpecialChars
{
get { return specialChars; }
set { specialChars = value; }
}
///
/// Packer enabled
///
public bool Enabled
{
get { return enabled; }
set { enabled = value; }
}
public ECMAScriptPacker()
{
Encoding = PackerEncoding.Normal;
FastDecode = true;
SpecialChars = false;
}
///
/// Constructor
///
/// The encoding level for this instance
/// Adds a subroutine to the output to speed up decoding
/// Replaces special characters
public ECMAScriptPacker(PackerEncoding encoding, bool fastDecode, bool specialChars)
{
Encoding = encoding;
FastDecode = fastDecode;
SpecialChars = specialChars;
}
///
/// Packs the script
///
/// the script to pack
/// the packed script
public string Pack(string script)
{
if (enabled)
{
script += "
";
script = basicCompression(script);
if (SpecialChars)
script = encodeSpecialChars(script);
if (Encoding != PackerEncoding.None)
script = encodeKeywords(script);
}
return script;
}
//zero encoding - just removal of whitespace and comments
private string basicCompression(string script)
{
ParseMaster parser = new ParseMaster();
// make safe
parser.EscapeChar = '\\';
// protect strings
parser.Add("'[^'\
\\r]*'", IGNORE);
parser.Add("\"[^\"\
\\r]*\"", IGNORE);
// remove comments
parser.Add("\\/\\/[^\
\\r]*[\
\\r]");
parser.Add("\\/\\*[^*]*\\*+([^\\/][^*]*\\*+)*\\/");
// protect regular expressions
parser.Add("\\s+(\\/[^\\/\
\\r\\*][^\\/\
\\r]*\\/g?i?)", "$2");
parser.Add("[^\\w\\$\\/'\"*)\\?:]\\/[^\\/\
\\r\\*][^\\/\
\\r]*\\/g?i?", IGNORE);
// remove: ;;; doSomething();
if (specialChars)
parser.Add(";;[^\
\\r]+[\
\\r]");
// remove redundant semi-colons
parser.Add(";+\\s*([};])", "$2");
// remove white-space
parser.Add("(\\b|\\$)\\s+(\\b|\\$)", "$2 $3");
parser.Add("([+\\-])\\s+([+\\-])", "$2 $3");
parser.Add("\\s+");
// done
return parser.Exec(script);
}
WordList encodingLookup;
private string encodeSpecialChars(string script)
{
ParseMaster parser = new ParseMaster();
// replace: $name -> n, $$name -> na
parser.Add("((\\$+)([a-zA-Z\\$_]+))(\\d*)",
new ParseMaster.MatchGroupEvaluator(encodeLocalVars));
// replace: _name -> _0, double-underscore (__name) is ignored
Regex regex = new Regex("\\b_[A-Za-z\\d]\\w*");
// build the word list
encodingLookup = analyze(script, regex, new EncodeMethod(encodePrivate));
parser.Add("\\b_[A-Za-z\\d]\\w*", new ParseMaster.MatchGroupEvaluator(encodeWithLookup));
script = parser.Exec(script);
return script;
}
private string encodeKeywords(string script)
{
// escape high-ascii values already in the script (i.e. in strings)
if (Encoding == PackerEncoding.HighAscii) script = escape95(script);
// create the parser
ParseMaster parser = new ParseMaster();
EncodeMethod encode = getEncoder(Encoding);
// for high-ascii, don't encode single character low-ascii
Regex regex = new Regex(
(Encoding == PackerEncoding.HighAscii) ? "\\w\\w+" : "\\w+"
);
// build the word list
encodingLookup = analyze(script, regex, encode);
// encode
parser.Add((Encoding == PackerEncoding.HighAscii) ? "\\w\\w+" : "\\w+",
new ParseMaster.MatchGroupEvaluator(encodeWithLookup));
// if encoded, wrap the script in a decoding function
return (script == string.Empty) ? "" : bootStrap(parser.Exec(script), encodingLookup);
}
private string bootStrap(string packed, WordList keywords)
{
// packed: the packed script
packed = "'" + escape(packed) + "'";
// ascii: base for encoding
int ascii = Math.Min(keywords.Sorted.Count, (int)Encoding);
if (ascii == 0)
ascii = 1;
// count: number of words contained in the script
int count = keywords.Sorted.Count;
// keywords: list of words contained in the script
foreach (object key in keywords.Protected.Keys)
{
keywords.Sorted[(int)key] = "";
}
// convert from a string to an array
StringBuilder sbKeywords = new StringBuilder("'");
foreach (string word in keywords.Sorted)
sbKeywords.Append(word + "|");
sbKeywords.Remove(sbKeywords.Length - 1, 1);
string keywordsout = sbKeywords.ToString() + "'.split('|')";
string encode;
string inline = "c";
switch (Encoding)
{
case PackerEncoding.Mid:
encode = "function(c){return c.toString(36)}";
inline += ".toString(a)";
break;
case PackerEncoding.Normal:
encode = "function(c){return(c35?String.fromCharCode(c+29):c.toString(36))}";
inline += ".toString(a)";
break;
case PackerEncoding.HighAscii:
encode = "function(c){return(c (int)PackerEncoding.Normal || fastDecode)
{
// insert the encode function
r = new Regex("\\{");
unpack = r.Replace(unpack, "{e=" + encode + ";", 1);
}
else
{
r = new Regex("e\\(c\\)");
unpack = r.Replace(unpack, inline);
}
// no need to pack the boot function since i've already done it
string _params = "" + packed + "," + ascii + "," + count + "," + keywordsout;
if (fastDecode)
{
//insert placeholders for the decoder
_params += ",0,{}";
}
// the whole thing
return "eval(" + unpack + "(" + _params + "))
";
}
private string escape(string input)
{
Regex r = new Regex("([\\\\'])");
return r.Replace(input, "\\$1");
}
private EncodeMethod getEncoder(PackerEncoding encoding)
{
switch (encoding)
{
case PackerEncoding.Mid:
return new EncodeMethod(encode36);
case PackerEncoding.Normal:
return new EncodeMethod(encode62);
case PackerEncoding.HighAscii:
return new EncodeMethod(encode95);
default:
return new EncodeMethod(encode10);
}
}
private string encode10(int code)
{
return code.ToString();
}
//lookups seemed like the easiest way to do this since
// I don't know of an equivalent to .toString(36)
private static string lookup36 = "0123456789abcdefghijklmnopqrstuvwxyz";
private string encode36(int code)
{
string encoded = "";
int i = 0;
do
{
int digit = (code / (int)Math.Pow(36, i)) % 36;
encoded = lookup36[digit] + encoded;
code -= digit * (int)Math.Pow(36, i++);
} while (code > 0);
return encoded;
}
private static string lookup62 = lookup36 + "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private string encode62(int code)
{
string encoded = "";
int i = 0;
do
{
int digit = (code / (int)Math.Pow(62, i)) % 62;
encoded = lookup62[digit] + encoded;
code -= digit * (int)Math.Pow(62, i++);
} while (code > 0);
return encoded;
}
private static string lookup95 = "¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ";
private string encode95(int code)
{
string encoded = "";
int i = 0;
do
{
int digit = (code / (int)Math.Pow(95, i)) % 95;
encoded = lookup95[digit] + encoded;
code -= digit * (int)Math.Pow(95, i++);
} while (code > 0);
return encoded;
}
private string escape95(string input)
{
Regex r = new Regex("[\xa1-\xff]");
return r.Replace(input, new MatchEvaluator(escape95Eval));
}
private string escape95Eval(Match match)
{
return "\\x" + ((int)match.Value[0]).ToString("x"); //return hexadecimal value
}
private string encodeLocalVars(Match match, int offset)
{
int length = match.Groups[offset + 2].Length;
int start = length - Math.Max(length - match.Groups[offset + 3].Length, 0);
return match.Groups[offset + 1].Value.Substring(start, length) +
match.Groups[offset + 4].Value;
}
private string encodeWithLookup(Match match, int offset)
{
return (string)encodingLookup.Encoded[match.Groups[offset].Value];
}
private delegate string EncodeMethod(int code);
private string encodePrivate(int code)
{
return "_" + code;
}
private WordList analyze(string input, Regex regex, EncodeMethod encodeMethod)
{
// analyse
// retreive all words in the script
MatchCollection all = regex.Matches(input);
WordList rtrn;
rtrn.Sorted = new StringCollection(); // list of words sorted by frequency
rtrn.Protected = new HybridDictionary(); // dictionary of word->encoding
rtrn.Encoded = new HybridDictionary(); // instances of "protected" words
if (all.Count > 0)
{
StringCollection unsorted = new StringCollection(); // same list, not sorted
HybridDictionary Protected = new HybridDictionary(); // "protected" words (dictionary of word->"word")
HybridDictionary values = new HybridDictionary(); // dictionary of charCode->encoding (eg. 256->ff)
HybridDictionary count = new HybridDictionary(); // word->count
int i = all.Count, j = 0;
string word;
// count the occurrences - used for sorting later
do
{
word = "$" + all[--i].Value;
if (count[word] == null)
{
count[word] = 0;
unsorted.Add(word);
// make a dictionary of all of the protected words in this script
// these are words that might be mistaken for encoding
Protected["$" + (values[j] = encodeMethod(j))] = j++;
}
// increment the word counter
count[word] = (int)count[word] + 1;
} while (i > 0);
/* prepare to sort the word list, first we must protect
words that are also used as codes. we assign them a code
equivalent to the word itself.
e.g. if "do" falls within our encoding range
then we store keywords["do"] = "do";
this avoids problems when decoding */
i = unsorted.Count;
string[] sortedarr = new string[unsorted.Count];
do
{
word = unsorted[--i];
if (Protected[word] != null)
{
sortedarr[(int)Protected[word]] = word.Substring(1);
rtrn.Protected[(int)Protected[word]] = true;
count[word] = 0;
}
} while (i > 0);
string[] unsortedarr = new string[unsorted.Count];
unsorted.CopyTo(unsortedarr, 0);
// sort the words by frequency
Array.Sort(unsortedarr, (IComparer)new CountComparer(count));
j = 0;
/*because there are "protected" words in the list
we must add the sorted words around them */
do
{
if (sortedarr[i] == null)
sortedarr[i] = unsortedarr[j++].Substring(1);
rtrn.Encoded[sortedarr[i]] = values[i];
} while (++i < unsortedarr.Length);
rtrn.Sorted.AddRange(sortedarr);
}
return rtrn;
}
private struct WordList
{
public StringCollection Sorted;
public HybridDictionary Encoded;
public HybridDictionary Protected;
}
private class CountComparer : IComparer
{
HybridDictionary count;
public CountComparer(HybridDictionary count)
{
this.count = count;
}
#region IComparer Members
public int Compare(object x, object y)
{
return (int)count[y] - (int)count[x];
}
#endregion
}
#region IHttpHandler Members
public void ProcessRequest(HttpContext context)
{
// try and read settings from config file
if (System.Configuration.ConfigurationSettings.GetConfig("ecmascriptpacker") != null)
{
NameValueCollection cfg =
(NameValueCollection)
System.Configuration.ConfigurationSettings.GetConfig("ecmascriptpacker");
if (cfg["Encoding"] != null)
{
switch (cfg["Encoding"].ToLower())
{
case "none":
Encoding = PackerEncoding.None;
break;
case "numeric":
Encoding = PackerEncoding.Numeric;
break;
case "mid":
Encoding = PackerEncoding.Mid;
break;
case "normal":
Encoding = PackerEncoding.Normal;
break;
case "highascii":
case "high":
Encoding = PackerEncoding.HighAscii;
break;
}
}
if (cfg["FastDecode"] != null)
{
if (cfg["FastDecode"].ToLower() == "true")
FastDecode = true;
else
FastDecode = false;
}
if (cfg["SpecialChars"] != null)
{
if (cfg["SpecialChars"].ToLower() == "true")
SpecialChars = true;
else
SpecialChars = false;
}
if (cfg["Enabled"] != null)
{
if (cfg["Enabled"].ToLower() == "true")
Enabled = true;
else
Enabled = false;
}
}
// try and read settings from URL
if (context.Request.QueryString["Encoding"] != null)
{
switch (context.Request.QueryString["Encoding"].ToLower())
{
case "none":
Encoding = PackerEncoding.None;
break;
case "numeric":
Encoding = PackerEncoding.Numeric;
break;
case "mid":
Encoding = PackerEncoding.Mid;
break;
case "normal":
Encoding = PackerEncoding.Normal;
break;
case "highascii":
case "high":
Encoding = PackerEncoding.HighAscii;
break;
}
}
if (context.Request.QueryString["FastDecode"] != null)
{
if (context.Request.QueryString["FastDecode"].ToLower() == "true")
FastDecode = true;
else
FastDecode = false;
}
if (context.Request.QueryString["SpecialChars"] != null)
{
if (context.Request.QueryString["SpecialChars"].ToLower() == "true")
SpecialChars = true;
else
SpecialChars = false;
}
if (context.Request.QueryString["Enabled"] != null)
{
if (context.Request.QueryString["Enabled"].ToLower() == "true")
Enabled = true;
else
Enabled = false;
}
//handle the request
TextReader r = new StreamReader(context.Request.PhysicalPath);
string jscontent = r.ReadToEnd();
r.Close();
context.Response.ContentType = "text/javascript";
context.Response.Output.Write(Pack(jscontent));
}
public bool IsReusable
{
get
{
if (System.Configuration.ConfigurationSettings.GetConfig("ecmascriptpacker") != null)
{
NameValueCollection cfg =
(NameValueCollection)
System.Configuration.ConfigurationSettings.GetConfig("ecmascriptpacker");
if (cfg["IsReusable"] != null)
if (cfg["IsReusable"].ToLower() == "true")
return true;
}
return false;
}
}
#endregion
}
上のコードには作者の情報がいくつかあります.
これを使用するには、BundleTransformを作成します.
public class JavascriptObfuscator : IBundleTransform
{
public void Process(BundleContext context, BundleResponse response)
{
var p = new ECMAScriptPacker(ECMAScriptPacker.PackerEncoding.Normal, true, false);
response.Content = p.Pack(response.Content);
}
}
もちろんこの混同器はHandlerとして使用することができ、あまり説明しません.
まとめ
上のコードはすべて接続されていて、私が使いたい2つの機能です.これは自分の少しの私欲のために作成された小さなツールにすぎず、一部の人に役に立つことを望んでいます.ちなみに、ネット上にはBundle Transformer:YUI 1.8.0などの強力なBundleプラグインがあります.興味があれば行ってみてください.