JavaScriptでCSS Minifier

JavaScript で書かれた CSS Minifier がほしいと思って探してみたんだけど、PHP などは多々あれど JS のは皆無だった。(サーバーサイド JS が流行れば出てくると思うんだけど)

色々探した挙句、こういうのを発見して、

そこからのリンクで C# で超高速 CSS Minify アルゴリズムというのがあった。

やー、Stack Overflow すばらしいですね。


アルゴリズム正規表現を使うのではなくて、一種のステートマシンを作る方法でやっていて、高速かつ CSS の仕様にもよく適合してるらしい。


というわけで、それを JS で書いてみた。

ただし、元アルゴリズムをそのまま移植したらちょっとおかしくなるところがあったので、+ 1 したりオレオレ break を入れたりしてる部分があるのが不安な感じ…バグってたら教えてください

↑のブックマークレットをこのページで実行すると、↓この CSS を minify する。


body
{
margin: 0px; /**asd**/
angledouble: "Angle= 00deg00'00\" "; /* This is just for double-quotes strings. According to the spec, this is valid syntax */
anglesingle: 'Angle= 00deg00\'00" '; /* This is just for single-quotes strings. According to the spec, this is valid syntax */
background-color: #a8a488;
background-image: url(../grp/bg_body.png);
background-position: center top;
background-repeat: repeat-x;
color: #5f523e;
font-family: Verdana, Tahoma, Sans-Serif;
font-size: 11px;
height: 100%;

}

img
{
border: none;
}

a
{
color: #506d94;
}

a:visited
{
color: #7b4c7f;
}



h1,
h2
{
font-family: Verdana, Tahoma, Sans-Serif;
font-weight: bold;
margin-top: 1.2em;
margin-bottom: 0.2em;
color: #9f4c1f;
}

h1:first-child,
h2:first-child
{
margin-top: 0px;
}

h1
{
font-size: 15px;
border-bottom: solid 2px #9f4c1f;
padding-bottom: 3px;
}

h2
{
font-size: 13px;
}

h1 a,
h2 a
{
color: #9f4c1f; /* IE7 does not understand 'inherit' */
text-decoration: none;
}

h1 a:visited,
h2 a:visited
{
color: #9f4c1f; /* IE7 does not understand 'inherit' */
}

h1 a:hover
{
padding-right:18px;
background: url(../grp/bg_head_link.png) right center no-repeat;
}

h2 a:hover
{
text-decoration: underline;
}

p
{
margin-top: 0.4em;
margin-bottom: 1.0em;
line-height: 1.5em;
}

p:last-child
{
margin-bottom: 0em;
}

p.Last,
p.Signature
{
margin-bottom: 0em;
}

ul,
ol
{
line-height: 1.5em;
}

a.Footnote
{
text-decoration: none;
}

a.Outside
{
background: url(../grp/remote.gif) right center no-repeat;
padding-right: 10px;
}

.PageTop + *
{
margin-top: 0px;
}

/***************************************************************/

.Canvas
{
background-image: url(../grp/bg_main.png);
background-repeat:repeat-y;
margin-left: auto;
margin-right: auto;
width: 800px;
padding-left: 16px;
padding-right: 16px;
}

.Footer
{
background-image: url(../grp/bg_canvas_bottom.png);
background-repeat:no-repeat;
width: 780px;
height: 20px;
margin-left: auto;
margin-right: auto;
padding: 4px 26px 9px 26px;
text-align: right;
}

.FooterLeft
{
float: left;
}

.FooterRight
{
float: right;
}

.Navigation
{
position: relative;
margin-left: auto;
margin-right: auto;
padding: 18px 16px 13px 16px;
width: 800px;
height: 27px;
z-index: 1;
background-image: url(../grp/bg_navarea.png);
background-repeat:no-repeat;
}

.NavigationLeft
{
height: 100%;
float: left;
}

.NavigationRight
{
padding-right: 5px;
height: 100%;
float: right;
}

.NavigationSeparator
{
background-image: url(../grp/bg_navarea_sep.png);
background-repeat:no-repeat;
width: 2px;
height: 27px;
float: left;
display: inline-block;
}

.NavigationCell
{
padding: 7px 8px 0px 8px;
height: 20px;
float: left;
display: inline-block;
}

.NavigationLogoCell
{
padding-right: 8px;
height: 27px;
float: left;
display: inline-block;
}

.NavigationCell:hover
{
background-image: url(../grp/bg_navarea_selected.png);
background-repeat:repeat-x;
}


.SearchContainer
{
background-image: url(../grp/bg_search.png);
background-repeat:no-repeat;
background-position: top right;
margin: 4px 0px 3px auto;
padding: 2px 0px 2px 0px;
width: 190px;
height: 14px;
text-align: left;
vertical-align: middle;
}

.SearchContainer input
{
width: 160px;
height: 14px;
margin: 0px 0px 0px 6px;
padding: 0px;
border: none;
font-size: 10px;
vertical-align: top;
}


.Splash
{
position: relative;
background-image: url(../grp/bg_splasharea.png);
background-repeat:no-repeat;
margin: 0px auto -13px auto;
padding: 0px 16px;
top: -13px;
width: 800px;
}

.Splash img,
.Splash a,
.Splash object
{
display: block;
}

.SinglePane
{
width: 770px;
padding: 15px;
}

.TwoPanes
{
padding: 1px;
width: 798px;
border-spacing: 0px;
}

.MainPane,
.SidePane
{
padding: 14px;
vertical-align: top;
border-collapse: collapse;
}

.MainPane
{
width: 560px;
}

.SidePane
{
width: 180px;
background-image: url(../grp/bg_sidepane.png);
background-repeat:repeat-y;
font-size: 10px;
}


div.PopupMenuBlog
{
position: absolute;
z-index: 10000;
background-image: url(../grp/bg_popupmenu.png);
text-align: left;
border: solid 1px #222222;
font-size: smaller;
}


.PopupMenuBlog ul
{
padding: 6px;
margin: 0px;
list-style-type: none;
}

.PopupMenuBlog a,
.PopupMenuBlog a:visited,
.PopupMenuBlog a:hover,
.PopupMenuBlog a:active
{
padding: 0px 4px 0px 14px;
color: white;
text-decoration: none;
}

.PopupMenuBlog a:hover
{
text-decoration: underline;
background-image: url(../grp/bg_menubullet.png);
background-repeat:no-repeat;
background-position:3px center;
}

.Subnav
{
padding: 0px;
margin: 0px;
}

.Subnav li
{
list-style-type: none;
line-height: 16px;
padding-left: 10px;
padding-bottom: 1px;
background-image: url(../grp/bg_subnav_list.png);
background-position: top left;
background-repeat:no-repeat;
}

.Subnav a,
.Subnav a:visited
{
font-weight: bold;
color: #444444;
text-decoration: none;
}

.Subnav a:hover
{
font-weight: bold;
text-decoration: none;
color: #506d94;
}

table.TabularContent
{
width: 100%;
border-collapse: collapse;
border-spacing: 0px;
}

table.TabularContent > tbody > tr > td
{
vertical-align: top;
padding: 15px;
}

table.TabularContent td.Left
{ padding-left:0px; }

table.TabularContent td.Right
{ padding-right:0px; }

table.TabularContent td.Top
{ padding-top:0px; }

table.TabularContent td.Bottom
{ padding-bottom:0px; }


.SysReqTable
{
border-collapse: collapse;
border-spacing: 0px;
width: 100%;
}

.SysReqTable td,
.SysReqTable th
{
border-bottom: solid 1px black;
padding: 5px;
text-align: left;
}

.Term
{
font-weight: bold;
}

.ImageContainer
{
text-align: center;
}

.HomeRecentBlogPosts
{
list-style-type: none;
margin: 0px;
padding: 0px;
}

.Required:after
{
color: red;
content: " *";
}


.DownloadLinkContainer
{
text-align: right;
}

.DownloadButton
{
height: 40px;
display: inline-block;
color: white;
}

.DownloadButton .Left,
.DownloadButton .Right
{
display: inline-block;
vertical-align:top;
height: 40px;
width: 6px;
background-repeat: no-repeat;
}

.DownloadButton .Left
{ background-image: url(../grp/bg_button_l.png); }

.DownloadButton .Right
{ background-image: url(../grp/bg_button_r.png); }

.DownloadButton .Content
{
display: inline-block;
height: 40px;
line-height:38px;
min-width: 70px;
background-image: url(../grp/bg_button_c.png);
background-position:center center;
background-repeat: no-repeat;
text-align: center;
}

/* FEEDBACK */

.FeedbackTable
{
width:100%;
border-spacing:0px;
border-collapse:collapse;
table-layout:fixed;
}

.FieldLabelCell
{
text-align: right;
font-weight: bold;
width:220px;
}

.FieldValueCell
{
width:320px;
padding-left: 5px;
}

.FieldNoteCell
{
padding-left: 5px;
}

.FeedbackTable input[type="text"],
.FeedbackTable input[type="file"],
.FeedbackTable textarea
{
border: solid 1px silver;
padding: 2px;
width: 314px;
font-family:inherit;
font-size:inherit;
}

.FeedbackTable input[type="file"]
{
width:320px;
}


/*****************************************
Icon on the left, text on the right layout
******************************************/

.IndentedTable
{
border-collapse: collapse;
border-spacing: 0px;
width: 100%;
}

.IndentedTable td
{
padding: 0px;
vertical-align: top;
text-align: left;
}

.IndentedTable td:first-child
{
padding-right: 15px;
width: 100px;
}

.Signature
{
text-align: right;
font-style: italic;
}

/*****************************************
Widgets
*****************************************/

div#widgetzone
{
padding: 0px;
margin-top: 16px;
}

div#widgetzone:first-child
{
margin-top: 0px;
}

div#widgetzone .clear
{
clear: left;
}

.widget
{
margin-top: 16px;
z-index: 1;
min-width: 1px;
}

.widget:first-child
{
margin-top: 0px;
}

.widget ul
{
list-style-type: none;
margin: 0;
padding: 0;
text-transform: capitalize;
}

.widget li
{
margin-bottom: 3px;
}

.widget img.rssButton
{
margin-right: 3px;
}

.widget .content
{
line-height: 16px;
color: #666666;
}

.widget h2
{
margin: 0px 0px 6px 0px;
}

.widget a.edit
{
font-size: 10px;
font-weight: normal;
float: right;
z-index: 1;
}

.widget a.delete
{
font-weight: bold;
color: #999999;
float: right;
margin-left: 5px;
z-index: 1;
text-decoration: none;
}

.widget a.delete:hover
{
color: black;
}

ブックマークレットの実行結果が入るところ。


できること。

  • コメント削除。
  • カンマ、括弧、コロンの前後などはホワイトスペース削除。
  • シングルクオートやダブルクオートの中はホワイトスペースを削除しない。
  • 閉じ括弧の前のセミコロンを削除。({body:color;} → {body:color})

できないこと。(対応する気はない)

  • url("...") → url(...)
  • 無効なセレクターやプロパティの除去等、アクロバティックなこと。(CSS のコメントは /*...*/ しかないはずなのに // でコメントアウトしたつもりになってる行とか)


ライセンスは、元のやつが MIT ライセンス (をちょっと修正してバイナリ配布ができないようになってる) なのでそれを踏襲します。

こういう場合どこまでライセンスが及ぶんだろう? コード自体は元のものと同じ行は一行もないけど (コメント除く)、アルゴリズムはほとんど同じで、ちょっと変更点あり。できるならパブリックドメインにしたい…



同じく C# でこんなのも見つけた。High performance css minifier