]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
WEBUI: compress and optimize the default js/css files
authorJaroslav Kysela <perex@perex.cz>
Wed, 13 May 2015 17:01:13 +0000 (19:01 +0200)
committerJaroslav Kysela <perex@perex.cz>
Thu, 14 May 2015 06:46:36 +0000 (08:46 +0200)
- create Makefile.webui to kept all in one place
- fix various .js errors
- don't compress .jpg and .gif files
- use rjsmin-1.0.10 as a JS/CSS compressor

16 files changed:
.gitignore
Makefile
Makefile.webui [new file with mode: 0644]
src/webui/extjs.c
src/webui/static/app/config.js
src/webui/static/app/cteditor.js
src/webui/static/app/epg.js
src/webui/static/app/epggrab.js
src/webui/static/app/ext.css
src/webui/static/app/extensions.js
src/webui/static/app/namespace.js [new file with mode: 0644]
src/webui/static/app/status.js
src/webui/webui.c
support/css.py [new file with mode: 0755]
vendor/ext-3.4.1/examples/ux/gridfilters/css/GridFilters.css
vendor/rjsmin-1.0.10/rjsmin.py [new file with mode: 0755]

index 9ad87a595580dbc127260a587b043f23cd1e8ccf..d16f2cec4a9c3b1dee552c887fce333e85cf0a91 100644 (file)
@@ -2,6 +2,14 @@ build.*
 .config.mk
 
 src/version.c
+src/webui/extjs-std.c
+src/webui/extjs-debug.c
+src/webui/extjs-tv-std.c
+src/webui/extjs-tv-debug.c
+src/webui/static/tvh.js.gz
+src/webui/static/tvh-tv.css.gz
+src/webui/static/tvh-tv.js.gz
+src/webui/static/tvh.css.gz
 
 data/dvb-scan
 
index 98b84ee354fabba30bbbb4d456b699d06dbf7225..7833674f9f2e700a2d7480f33f2249413ccf6e83 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -420,7 +420,7 @@ reconfigure:
        $(ROOTDIR)/configure $(CONFIGURE_ARGS)
 
 # Binary
-${PROG}: check_config $(OBJS) $(ALLDEPS)
+${PROG}: check_config make_webui $(OBJS) $(ALLDEPS)
        $(CC) -o $@ $(OBJS) $(CFLAGS) $(LDFLAGS)
 
 # Object
@@ -437,6 +437,7 @@ ${BUILDDIR}/%.so: ${SRCS_EXTRA}
 clean:
        rm -rf ${BUILDDIR}/src ${BUILDDIR}/bundle*
        find . -name "*~" | xargs rm -f
+       $(MAKE) -f Makefile.webui clean
 
 distclean: clean
        rm -rf ${ROOTDIR}/libav_static
@@ -461,10 +462,14 @@ $(BUILDDIR)/bundle.o: $(BUILDDIR)/bundle.c
        @mkdir -p $(dir $@)
        $(CC) -I${ROOTDIR}/src -c -o $@ $<
 
-$(BUILDDIR)/bundle.c: check_dvb_scan
+$(BUILDDIR)/bundle.c: check_dvb_scan make_webui
        @mkdir -p $(dir $@)
        $(MKBUNDLE) -o $@ -d ${BUILDDIR}/bundle.d $(BUNDLE_FLAGS) $(BUNDLES:%=$(ROOTDIR)/%)
 
+.PHONY: make_webui
+make_webui:
+       $(MAKE) -f Makefile.webui all
+
 # Static FFMPEG
 
 ifeq ($(CONFIG_LIBFFMPEG_STATIC),yes)
diff --git a/Makefile.webui b/Makefile.webui
new file mode 100644 (file)
index 0000000..3e2fafa
--- /dev/null
@@ -0,0 +1,221 @@
+#
+#  Tvheadend WEBUI interface
+#  Copyright (C) 2015 Jaroslav Kysela
+#
+#  This program is free software: you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation, either version 3 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+#
+# Configuration
+#
+
+IAM=$(lastword $(MAKEFILE_LIST))
+WEBDIR ?= src/webui
+ROOTPATH = static
+
+ifneq ($(WEBUI),)
+
+TOPDIR=$(dir $(IAM))
+include $(TOPDIR)/.config.mk
+EXTJSPATH = $(ROOTPATH)/extjs
+CSS_PY = support/css.py
+RUN_JS = vendor/rjsmin-1.0.10/rjsmin.py
+RUN_CSS = vendor/rjsmin-1.0.10/rjsmin.py
+GZIP = gzip -n
+
+ifeq ($(WEBUI),std)
+DEBUG =
+else
+DEBUG = -debug
+endif
+
+JAVASCRIPT =
+JAVASCRIPT2 =
+JAVASCRIPT_TV =
+CSS =
+CSS_TV =
+
+#
+# base extjs files
+#
+
+JAVASCRIPT += $(EXTJSPATH)/adapter/ext/ext-base$(DEBUG).js
+JAVASCRIPT += $(EXTJSPATH)/ext-all$(DEBUG).js
+
+#
+# CSS
+#
+
+CSS += $(EXTJSPATH)/resources/css/ext-all-notheme.css
+CSS += $(EXTJSPATH)/resources/css/xtheme-blue.css
+CSS += $(ROOTPATH)/livegrid/resources/css/ext-ux-livegrid.css
+CSS += $(EXTJSPATH)/examples/ux/gridfilters/css/GridFilters.css
+CSS += $(EXTJSPATH)/examples/ux/gridfilters/css/RangeMenu.css
+CSS += static/xcheckbox/xcheckbox.css
+CSS += static/app/ext.css
+
+#
+# extjs extensions
+#
+
+JAVASCRIPT += $(ROOTPATH)/app/extensions.js
+JAVASCRIPT += $(ROOTPATH)/livegrid/livegrid-all.js
+JAVASCRIPT += $(ROOTPATH)/lovcombo/lovcombo-all.js
+JAVASCRIPT += $(ROOTPATH)/multiselect/multiselect.js
+JAVASCRIPT += $(ROOTPATH)/multiselect/ddview.js
+JAVASCRIPT += $(ROOTPATH)/xcheckbox/xcheckbox.js
+JAVASCRIPT += $(ROOTPATH)/checkcolumn/CheckColumn.js
+JAVASCRIPT += $(EXTJSPATH)/examples/ux/gridfilters/GridFilters.js
+JAVASCRIPT += $(EXTJSPATH)/examples/ux/gridfilters/filter/Filter.js
+JAVASCRIPT += $(EXTJSPATH)/examples/ux/gridfilters/filter/BooleanFilter.js
+JAVASCRIPT += $(EXTJSPATH)/examples/ux/gridfilters/filter/DateFilter.js
+JAVASCRIPT += $(EXTJSPATH)/examples/ux/gridfilters/filter/ListFilter.js
+JAVASCRIPT += $(EXTJSPATH)/examples/ux/gridfilters/filter/NumericFilter.js
+JAVASCRIPT += $(EXTJSPATH)/examples/ux/gridfilters/filter/StringFilter.js
+JAVASCRIPT += $(EXTJSPATH)/examples/ux/gridfilters/menu/ListMenu.js
+JAVASCRIPT += $(EXTJSPATH)/examples/ux/gridfilters/menu/RangeMenu.js
+
+#
+# app itself
+#
+
+JAVASCRIPT += $(ROOTPATH)/app/namespace.js
+JAVASCRIPT += $(ROOTPATH)/smoothie.js
+JAVASCRIPT += $(ROOTPATH)/app/comet.js
+JAVASCRIPT += $(ROOTPATH)/app/tableeditor.js
+JAVASCRIPT += $(ROOTPATH)/app/cteditor.js
+JAVASCRIPT += $(ROOTPATH)/app/acleditor.js
+
+CACLIENT-$(CONFIG_CWC) = yes
+CACLIENT-$(CONFIG_CAPMT) = yes
+
+ifeq ($(CACLIENT-yes), yes)
+JAVASCRIPT += $(ROOTPATH)/app/caclient.js
+endif
+
+JAVASCRIPT += $(ROOTPATH)/app/tvadapters.js
+JAVASCRIPT += $(ROOTPATH)/app/idnode.js
+JAVASCRIPT += $(ROOTPATH)/app/esfilter.js
+ifeq ($(CONFIG_MPEGTS), yes)
+JAVASCRIPT += $(ROOTPATH)/app/mpegts.js
+endif
+ifeq ($(CONFIG_TIMESHIFT), yes)
+JAVASCRIPT += $(ROOTPATH)/app/timeshift.js
+endif
+JAVASCRIPT += $(ROOTPATH)/app/chconf.js
+JAVASCRIPT += $(ROOTPATH)/app/epg.js
+JAVASCRIPT += $(ROOTPATH)/app/dvr.js
+JAVASCRIPT += $(ROOTPATH)/app/epggrab.js
+JAVASCRIPT += $(ROOTPATH)/app/config.js
+JAVASCRIPT += $(ROOTPATH)/app/tvhlog.js
+JAVASCRIPT += $(ROOTPATH)/app/status.js
+JAVASCRIPT += $(ROOTPATH)/tv.js
+JAVASCRIPT += $(ROOTPATH)/app/servicemapper.js
+
+JAVASCRIPT += $(ROOTPATH)/app/tvheadend.js
+
+#
+# tv
+#
+
+CSS_TV += $(ROOTPATH)/tv.css
+JAVASCRIPT_TV += $(EXTJSPATH)/adapter/ext/ext-base$(DEBUG).js
+JAVASCRIPT_TV += $(EXTJSPATH)/ext-all$(DEBUG).js
+JAVASCRIPT_TV += $(ROOTPATH)/tv.js
+
+#
+# real source paths
+#
+
+JAVASCRIPT_SRC = $(foreach f,$(JAVASCRIPT),$(WEBDIR)/$(f))
+CSS_SRC = $(foreach f,$(CSS),$(WEBDIR)/$(f))
+JAVASCRIPT_TV_SRC = $(foreach f,$(JAVASCRIPT_TV),$(WEBDIR)/$(f))
+CSS_TV_SRC = $(foreach f,$(CSS_TV),$(WEBDIR)/$(f))
+
+endif # WEBUI defined
+
+ifndef V
+VV = @
+endif
+
+define GO_JS
+       $(VV)cat $^ > $@.tmp
+       $(VV)$(RUN_JS) < $@.tmp > $@.tmp2
+       @stat --printf="%-35n %7s\n" $@.tmp $@.tmp2
+       @$(GZIP) -c $@.tmp2 > $@.tmp
+       @rm $@.tmp2
+       @mv $@.tmp $@
+       @stat --printf="%-35n %7s\n" $@
+endef
+
+define GO_CSS
+       $(VV)$(CSS_PY) --in="$^" > $@.tmp
+       $(VV)$(RUN_CSS) < $@.tmp > $@.tmp2
+       @stat --printf="%-35n %7s\n" $@.tmp $@.tmp2
+       @$(GZIP) -c $@.tmp2 > $@.tmp
+       @rm $@.tmp2
+       @mv $@.tmp $@
+       @stat --printf="%-35n %7s\n" $@
+endef
+
+all:
+       $(MAKE) -f $(IAM) WEBUI=std   compile-std
+       $(MAKE) -f $(IAM) WEBUI=debug compile-debug
+
+$(WEBDIR)/extjs-std.c: $(JAVASCRIPT_SRC) $(CSS_SRC)
+       $(VV)echo -e 'extjs_lcss(hq, "static/tvh.css.gz");' > $@
+       $(VV)echo -e 'extjs_load(hq, "static/tvh.js.gz");' >> $@
+
+$(WEBDIR)/extjs-debug.c: $(JAVASCRIPT_SRC) $(CSS_SRC)
+       $(VV)echo -e '$(foreach f,$(CSS),extjs_lcss(hq, "$(f)");\r\n)' > $@
+       $(VV)echo -e '$(foreach f,$(JAVASCRIPT),extjs_load(hq, "$(f)");\r\n)' >> $@
+
+$(WEBDIR)/extjs-tv-std.c: $(JAVASCRIPT_TV_SRC) $(CSS_TV_SRC)
+       $(VV)echo -e 'extjs_lcss(hq, "static/tvh-tv.css.gz");' > $@
+       $(VV)echo -e 'extjs_load(hq, "static/tvh-tv.js.gz");' >> $@
+
+$(WEBDIR)/extjs-tv-debug.c: $(JAVASCRIPT_TV_SRC) $(CSS_TV_SRC)
+       $(VV)echo -e '$(foreach f,$(CSS_TV),extjs_lcss(hq, "$(f)");\r\n)' > $@
+       $(VV)echo -e '$(foreach f,$(JAVASCRIPT_TV),extjs_load(hq, "$(f)");\r\n)' >> $@
+
+$(WEBDIR)/$(ROOTPATH)/tvh.js.gz: $(JAVASCRIPT_SRC)
+       $(call GO_JS)
+
+$(WEBDIR)/$(ROOTPATH)/tvh.css.gz: $(CSS_SRC)
+       $(call GO_CSS)
+
+$(WEBDIR)/$(ROOTPATH)/tvh-tv.js.gz: $(JAVASCRIPT_TV_SRC)
+       $(call GO_JS)
+
+$(WEBDIR)/$(ROOTPATH)/tvh-tv.css.gz: $(CSS_TV_SRC)
+       $(call GO_CSS)
+
+.PHONY: compile-std
+compile-std: $(WEBDIR)/$(ROOTPATH)/tvh.js.gz $(WEBDIR)/$(ROOTPATH)/tvh.css.gz \
+             $(WEBDIR)/$(ROOTPATH)/tvh-tv.js.gz $(WEBDIR)/$(ROOTPATH)/tvh-tv.css.gz \
+             $(WEBDIR)/extjs-std.c $(WEBDIR)/extjs-tv-std.c
+       @echo "WEBUI std finished"
+       
+.PHONY: compile-debug
+compile-debug: $(WEBDIR)/extjs-debug.c $(WEBDIR)/extjs-tv-debug.c
+       @echo "WEBUI debug finished"
+
+.PHONY:
+clean:
+       rm -f $(foreach f,tvh.js tvh.css tvh-tv.js tvh-tv.css,\
+               $(WEBDIR)/$(ROOTPATH)/$(f).gz \
+               $(WEBDIR)/$(ROOTPATH)/$(f).tmp \
+               $(WEBDIR)/$(ROOTPATH)/$(f).tmp2) \
+              $(WEBDIR)/extjs-std.c $(WEBDIR)/extjs-tv-std.c \
+              $(WEBDIR)/extjs-debug.c $(WEBDIR)/extjs-tv-debug.c
index a4cf38c934d096eb9d3131864d357f488c14f2bd..769c8e6c944247251fe9425349a98654f97885df 100644 (file)
@@ -113,81 +113,21 @@ extjs_root(http_connection_t *hc, const char *remain, void *opaque)
   htsbuf_qprintf(hq, "<head>\n");
 
   htsbuf_qprintf(hq, "<meta name=\"apple-itunes-app\" content=\"app-id=638900112\">\n");
+
+  if (tvheadend_webui_debug) {
   
-  extjs_load(hq, EXTJSPATH "/adapter/ext/ext-base%s.js", tvheadend_webui_debug ? "-debug" : "");
-  extjs_load(hq, EXTJSPATH "/ext-all%s.js", tvheadend_webui_debug ? "-debug" : "");
-  extjs_lcss(hq, EXTJSPATH "/resources/css/ext-all-notheme.css");
-  extjs_lcss(hq, EXTJSPATH "/resources/css/xtheme-blue.css");
-  extjs_lcss(hq, "static/livegrid/resources/css/ext-ux-livegrid.css");
-  extjs_lcss(hq, EXTJSPATH "/examples/ux/gridfilters/css/GridFilters.css");
-  extjs_lcss(hq, EXTJSPATH "/examples/ux/gridfilters/css/RangeMenu.css");
-  extjs_lcss(hq, "static/xcheckbox/xcheckbox.css");
-  extjs_lcss(hq, "static/app/ext.css");
-  
-  extjs_exec(hq, "Ext.BLANK_IMAGE_URL = \'" EXTJSPATH "/resources/images/default/s.gif';");
-
-  /**
-   * Load extjs extensions
-   */
-  extjs_load(hq, "static/app/extensions.js");
-  extjs_load(hq, "static/livegrid/livegrid-all.js");
-  extjs_load(hq, "static/lovcombo/lovcombo-all.js");
-  extjs_load(hq, "static/multiselect/multiselect.js");
-  extjs_load(hq, "static/multiselect/ddview.js");
-  extjs_load(hq, "static/xcheckbox/xcheckbox.js");
-  extjs_load(hq, "static/checkcolumn/CheckColumn.js");
-  extjs_load(hq, EXTJSPATH "/examples/ux/gridfilters/GridFilters.js");
-  extjs_load(hq, EXTJSPATH "/examples/ux/gridfilters/filter/Filter.js");
-  extjs_load(hq, EXTJSPATH "/examples/ux/gridfilters/filter/BooleanFilter.js");
-  extjs_load(hq, EXTJSPATH "/examples/ux/gridfilters/filter/DateFilter.js");
-  extjs_load(hq, EXTJSPATH "/examples/ux/gridfilters/filter/ListFilter.js");
-  extjs_load(hq, EXTJSPATH "/examples/ux/gridfilters/filter/NumericFilter.js");
-  extjs_load(hq, EXTJSPATH "/examples/ux/gridfilters/filter/StringFilter.js");
-  extjs_load(hq, EXTJSPATH "/examples/ux/gridfilters/menu/ListMenu.js");
-  extjs_load(hq, EXTJSPATH "/examples/ux/gridfilters/menu/RangeMenu.js");
-
-  /**
-   * Create a namespace for our app
-   */
-  extjs_exec(hq, "Ext.namespace('tvheadend');");
-
-  /**
-   * Load all components
-   */
-  extjs_load(hq, "static/smoothie.js");
-  extjs_load(hq, "static/app/comet.js");
-  extjs_load(hq, "static/app/tableeditor.js");
-  extjs_load(hq, "static/app/cteditor.js");
-  extjs_load(hq, "static/app/acleditor.js");
-#if ENABLE_CWC || ENABLE_CAPMT
-  extjs_load(hq, "static/app/caclient.js");
-#endif
-  extjs_load(hq, "static/app/tvadapters.js");
-  extjs_load(hq, "static/app/idnode.js");
-  extjs_load(hq, "static/app/esfilter.js");
-#if ENABLE_MPEGTS
-  extjs_load(hq, "static/app/mpegts.js");
-#endif
-#if ENABLE_TIMESHIFT
-  extjs_load(hq, "static/app/timeshift.js");
-#endif
-  extjs_load(hq, "static/app/chconf.js");
-  extjs_load(hq, "static/app/epg.js");
-  extjs_load(hq, "static/app/dvr.js");
-  extjs_load(hq, "static/app/epggrab.js");
-  extjs_load(hq, "static/app/config.js");
-  extjs_load(hq, "static/app/tvhlog.js");
-  extjs_load(hq, "static/app/status.js");
-  extjs_load(hq, "static/tv.js");
-  extjs_load(hq, "static/app/servicemapper.js");
-
-  /**
-   * Finally, the app itself
-   */
-  extjs_load(hq, "static/app/tvheadend.js");
-  extjs_exec(hq, "Ext.onReady(tvheadend.app.init, tvheadend.app);");
-  
+#include "extjs-debug.c"
 
+  } else {
+
+#include "extjs-std.c"
+
+  }
+
+  extjs_exec(hq, "\
+Ext.BLANK_IMAGE_URL = \'" EXTJSPATH "/resources/images/default/s.gif';\r\n\
+Ext.onReady(tvheadend.app.init, tvheadend.app);\
+");
 
 
   htsbuf_qprintf(hq,
@@ -213,6 +153,7 @@ extjs_root(http_connection_t *hc, const char *remain, void *opaque)
                 "<div id=\"systemlog\"></div>\n"
                 "</body></html>\n",
                 tvheadend_version);
+
   http_output_html(hc);
   return 0;
 }
@@ -230,17 +171,17 @@ extjs_livetv(http_connection_t *hc, const char *remain, void *opaque)
   htsbuf_qprintf(hq, "<html>\n");
   htsbuf_qprintf(hq, "<head>\n");
   htsbuf_qprintf(hq, "<title>HTS Tvheadend %s</title>\n", tvheadend_version);
-  extjs_lcss(hq, "static/tv.css");
 
-  if(tvheadend_webui_debug) {
-    extjs_load(hq, EXTJSPATH "/adapter/ext/ext-base-debug.js");
-    extjs_load(hq, EXTJSPATH "/ext-all-debug.js");
+  if (tvheadend_webui_debug) {
+
+#include "extjs-tv-debug.c"
+
   } else {
-    extjs_load(hq, EXTJSPATH "/adapter/ext/ext-base.js");
-    extjs_load(hq, EXTJSPATH "/ext-all.js");
+
+#include "extjs-tv-std.c"
+
   }
 
-  extjs_load(hq, "static/tv.js");
   extjs_exec(hq, "Ext.onReady(tv.app.init, tv.app);");
 
   htsbuf_qprintf(hq, "</head>\n");
index af0fa769efdfff5fdba10f8703d36d045e18e2ac..354d306c551aa3f55264142a76cde5eebb729d4f 100644 (file)
@@ -307,7 +307,7 @@ tvheadend.miscconf = function(panel, index) {
             text: "Discover SAT>IP servers",
             tooltip: 'Look for new SAT>IP servers',
             iconCls: 'find',
-            handler: satipDiscover,
+            handler: satipDiscover
         });
     } else {
         var satipButton = null;
@@ -360,7 +360,7 @@ tvheadend.miscconf = function(panel, index) {
         bodyStyle: 'padding:15px',
         layout: 'form',
         items: _items,
-        tbar: _tbar,
+        tbar: _tbar
     });
 
     tvheadend.paneladd(panel, mpanel, index);
@@ -424,7 +424,7 @@ tvheadend.miscconf = function(panel, index) {
     function satipDiscover() {
         Ext.Ajax.request({
             url: 'api/hardware/satip/discover',
-            params: { op: 'all' },
+            params: { op: 'all' }
         });
     }
 };
index c869002506d00370bc900aa6875de8351013edbd..b44267c964c1beabb4f37c1532d10647d9fcb813 100644 (file)
@@ -66,7 +66,7 @@ tvheadend.bouquet = function(panel, index)
         },
         help: function() {
             new tvheadend.help('Bouquets', 'config_bouquet.html');
-        },
+        }
     });
 
     return panel;
index 5032521bdf0765673942a58da22b852bd11436ab..cd76751960fd4b701666c1f803dc28ad2af80498 100644 (file)
@@ -271,7 +271,7 @@ tvheadend.epgDetails = function(event) {
         tvheadend.AjaxConfirm({
             url: 'api/dvr/entry/cancel',
             params: {
-                uuid: event.dvrUuid,
+                uuid: event.dvrUuid
             },
             success: function(d) {
                 win.close();
@@ -284,7 +284,7 @@ tvheadend.epgDetails = function(event) {
         tvheadend.AjaxConfirm({
             url: 'api/idnode/delete',
             params: {
-                uuid: event.dvrUuid,
+                uuid: event.dvrUuid
             },
             success: function(d) {
                 win.close();
@@ -327,16 +327,16 @@ tvheadend.epg = function() {
             'recordingError': detailsfcn,
             'scheduled':      detailsfcn,
             'completed':      detailsfcn,
-            'completedError': detailsfcn,
+            'completedError': detailsfcn
         },
         actions: [
             {
                 iconCls: 'broadcast_details',
                 qtip: 'Broadcast details',
-                cb: detailsfcn,
+                cb: detailsfcn
             },
             {
-                iconIndex: 'dvrState',
+                iconIndex: 'dvrState'
             }
                                                                                                           
         ]
@@ -901,9 +901,9 @@ tvheadend.epg = function() {
     tvheadend.comet.on('epg', function(m) {
         if (!panel.isVisible())
             return;
-        if (m.delete) {
-            for (var i = 0; i < m.delete.length; i++) {
-                var r = epgStore.getById(m.delete[i]);
+        if ('delete' in m) {
+            for (var i = 0; i < m['delete'].length; i++) {
+                var r = epgStore.getById(m['delete'][i]);
                 if (r)
                   epgStore.remove(r);
             }
index a9255b785b56a7a7226f48f7264412c17284cd86..68c135cb7348e341dc3c4f8fdc52338df1eec10f 100644 (file)
@@ -406,7 +406,7 @@ tvheadend.epggrab = function(panel, index) {
             url: 'epggrab',
             params: {
                 op: 'otaepgTrigger',
-                after: 1,
+                after: 1
             },
             waitMsg: 'Triggering...',
             failure: function(response, options) {
index 760425d390781366e8dfbad10ad3881ba0866b47..e27609434726ef6334676089ef3ca4fef41b33e6 100644 (file)
     height: 5.3em;
     margin: 0;
     padding: 6px 8px 0 6px;
-    background: url("../img/bg-header.png") repeat-x scroll 0 0 transparent;
+    background: url(../img/bg-header.png) repeat-x scroll 0 0 transparent;
     height: 45px;
 }
 
 #header > h1 {
-    background: url("../img/logo.png") no-repeat scroll 10px 20% transparent;
+    background: url(../img/logo.png) no-repeat scroll 10px 20% transparent;
     color: #E0E0E0;
     font-size: 22px;
     padding: 2px 55px;
index a8df6cb8fa5f4a51728ecc116ef2c8577074490d..aefa3f68147db817a5b1560ba28c0d5bb277aa94 100644 (file)
@@ -508,41 +508,41 @@ Ext.ux.grid.RowActions = function(config) {
     // {{{
     this.addEvents(
             /**
-             * @event beforeaction
+             * event beforeaction
              * Fires before action event. Return false to cancel the subsequent action event.
-             * @param {Ext.grid.GridPanel} grid
-             * @param {Ext.data.Record} record Record corresponding to row clicked
-             * @param {String} action Identifies the action icon clicked. Equals to icon css class name.
-             * @param {Integer} rowIndex Index of clicked grid row
-             * @param {Integer} colIndex Index of clicked grid column that contains all action icons
+             * param {Ext.grid.GridPanel} grid
+             * param {Ext.data.Record} record Record corresponding to row clicked
+             * param {String} action Identifies the action icon clicked. Equals to icon css class name.
+             * param {Integer} rowIndex Index of clicked grid row
+             * param {Integer} colIndex Index of clicked grid column that contains all action icons
              */
             'beforeaction'
             /**
-             * @event action
+             * event action
              * Fires when icon is clicked
-             * @param {Ext.grid.GridPanel} grid
-             * @param {Ext.data.Record} record Record corresponding to row clicked
-             * @param {String} action Identifies the action icon clicked. Equals to icon css class name.
-             * @param {Integer} rowIndex Index of clicked grid row
-             * @param {Integer} colIndex Index of clicked grid column that contains all action icons
+             * param {Ext.grid.GridPanel} grid
+             * param {Ext.data.Record} record Record corresponding to row clicked
+             * param {String} action Identifies the action icon clicked. Equals to icon css class name.
+             * param {Integer} rowIndex Index of clicked grid row
+             * param {Integer} colIndex Index of clicked grid column that contains all action icons
              */
             , 'action'
             /**
-             * @event beforegroupaction
+             * event beforegroupaction
              * Fires before group action event. Return false to cancel the subsequent groupaction event.
-             * @param {Ext.grid.GridPanel} grid
-             * @param {Array} records Array of records in this group
-             * @param {String} action Identifies the action icon clicked. Equals to icon css class name.
-             * @param {String} groupId Identifies the group clicked
+             * param {Ext.grid.GridPanel} grid
+             * param {Array} records Array of records in this group
+             * param {String} action Identifies the action icon clicked. Equals to icon css class name.
+             * param {String} groupId Identifies the group clicked
              */
             , 'beforegroupaction'
             /**
-             * @event groupaction
+             * event groupaction
              * Fires when icon in a group header is clicked
-             * @param {Ext.grid.GridPanel} grid
-             * @param {Array} records Array of records in this group
-             * @param {String} action Identifies the action icon clicked. Equals to icon css class name.
-             * @param {String} groupId Identifies the group clicked
+             * param {Ext.grid.GridPanel} grid
+             * param {Array} records Array of records in this group
+             * param {String} action Identifies the action icon clicked. Equals to icon css class name.
+             * param {String} groupId Identifies the group clicked
              */
             , 'groupaction'
             );
diff --git a/src/webui/static/app/namespace.js b/src/webui/static/app/namespace.js
new file mode 100644 (file)
index 0000000..200125f
--- /dev/null
@@ -0,0 +1 @@
+Ext.namespace('tvheadend');
index cfeb6e3630267fd01396743b1d6c44d57f6bcebe..26fdb593c24dd74376d7fddbcd0cf0cedd70e930 100644 (file)
@@ -262,7 +262,7 @@ tvheadend.status_streams = function(panel, index)
                             }
                        );
                    }
-               },
+               }
             ],
             destroy: function() {
             }
@@ -509,7 +509,7 @@ tvheadend.status_conns = function(panel, index) {
                             }
                        );
                    }
-               },
+               }
             ],
             destroy: function() {
             }
index e397925cf803a07ee408b049ded2c619f7c062cd..104d35f8c7290f947abdf2fda0791b1c80cf1fee 100644 (file)
@@ -153,41 +153,53 @@ redirect:
  * Static download of a file from the filesystem
  */
 int
-page_static_file(http_connection_t *hc, const char *remain, void *opaque)
+page_static_file(http_connection_t *hc, const char *_remain, void *opaque)
 {
   int ret = 0;
   const char *base = opaque;
+  char *remain, *postfix;
   char path[500];
   ssize_t size;
-  const char *content = NULL, *postfix;
+  const char *content = NULL;
   char buf[4096];
-  const char *gzip;
+  const char *gzip = NULL;
+  int nogzip = 0;
 
-  if(remain == NULL)
+  if(_remain == NULL)
     return HTTP_STATUS_NOT_FOUND;
 
-  if(strstr(remain, ".."))
+  if(strstr(_remain, ".."))
     return HTTP_STATUS_BAD_REQUEST;
 
-  snprintf(path, sizeof(path), "%s/%s", base, remain);
+  snprintf(path, sizeof(path), "%s/%s", base, _remain);
 
+  remain = tvh_strdupa(_remain);
   postfix = strrchr(remain, '.');
+  if(postfix != NULL && !strcmp(postfix + 1, "gz")) {
+    gzip = "gzip";
+    *postfix = '\0';
+    postfix = strrchr(remain, '.');
+  }
   if(postfix != NULL) {
     postfix++;
     if(!strcmp(postfix, "js"))
       content = "text/javascript; charset=UTF-8";
     else if(!strcmp(postfix, "css"))
       content = "text/css; charset=UTF-8";
+    else if(!strcmp(postfix, "git"))
+      nogzip = 1;
+    else if(!strcmp(postfix, "jpg"))
+      nogzip = 1;
   }
 
-  // TODO: handle compression
-  fb_file *fp = fb_open(path, 0, 1);
+  fb_file *fp = fb_open(path, 0, (nogzip || gzip) ? 0 : 1);
   if (!fp) {
     tvhlog(LOG_ERR, "webui", "failed to open %s", path);
     return HTTP_STATUS_INTERNAL;
   }
   size = fb_size(fp);
-  gzip = fb_gzipped(fp) ? "gzip" : NULL;
+  if (!gzip && fb_gzipped(fp))
+    gzip = "gzip";
 
   http_send_header(hc, 200, content, size, gzip, NULL, 10, 0, NULL, NULL);
   while (!fb_eof(fp)) {
diff --git a/support/css.py b/support/css.py
new file mode 100755 (executable)
index 0000000..8db79a6
--- /dev/null
@@ -0,0 +1,75 @@
+#!/usr/bin/env python
+#
+# Change css
+#
+
+import sys, os
+
+TVHDIR = os.path.realpath('.')
+
+def error(fmt, *msg):
+  sys.stderr.write(" [ERROR] " + (fmt % msg) + '\n')
+  sys.exit(1)
+
+def umangle(u, f, r):
+  if not u.startswith(f):
+    error('Internal error')
+  return r + u[len(f):]
+
+def ustrip(u, f):
+  return u[len(f):]
+
+def url(fn):
+
+  PATHS={
+    '../docresources':'../docresources',
+  }
+
+  f = open(fn)
+  if fn[0] != '/':
+    fn = os.path.join(os.environ['PWD'], fn)
+  fn = os.path.normpath(fn)
+  if not fn.startswith(TVHDIR):
+    error('Wrong filename "%s"', fn)
+  bd = os.path.dirname(fn)
+  while 1:
+    l = f.readline()
+    if not l:
+      break
+    n = l
+    p = l.find('url(')
+    if p >= 0:
+      e = l[p:].find(')')
+      if e < 0:
+        error('Wrong url() "%s" (from %s)', l, fn)
+      e += p
+      p += 4
+      url = l[p:e].strip().lstrip()
+      if url.startswith('../docresources'):
+        url = umangle(url, '../docresources', TVHDIR + '/docs/docresources')
+      elif url.startswith('../../docresources'):
+        url = umangle(url, '../../docresources', TVHDIR + '/docs/docresources')
+      else:
+        url = os.path.normpath(bd + '/' + url)
+      if not os.path.exists(url):
+        error('Wrong path "%s" (from %s)', url, fn)
+      url = url[len(TVHDIR)+1:]
+      if url.startswith('src/webui/static/'):
+        url = ustrip(url, 'src/webui/static/')
+      elif url.startswith('docs/docresources/'):
+        url = 'docresources/' + ustrip(url, 'docs/docresources/')
+      n = l[:p] + url + l[e:]
+    sys.stdout.write(n)
+  f.close()
+
+fn=''
+for opt in sys.argv:
+  if opt.startswith('--tvhdir='):
+    TVHDIR=os.path.realpath(opt[9:])
+  if opt.startswith('--in='):
+    fn=os.path.normpath(opt[5:]).split(' ')
+
+if not fn:
+  error('Specify input file')
+for f in fn:
+  url(f)
index 2e62e875c9a63cc71f7db9aa16fb6abe897f3b07..a5ea5e3f1b041185176f1fbfd3fbd8eb46c9e8d6 100644 (file)
@@ -28,11 +28,11 @@ Build date: 2013-04-03 15:07:25
 }
 
 .x-grid3-hd-row .ux-filtered-column .x-grid3-hd-inner {
-       background-image: url(../images/header_bg.gif);
+       /* background-image: urL(../images/header_bg.gif); */
 }
 
 .ux-filtered-column .x-grid3-hd-btn {
-       background-image: url(../images/hd-btn.gif);
+       /* background-image: urL(../images/hd-btn.gif); */
 }
 */
 .x-grid3-hd-row td.ux-filtered-column {   
diff --git a/vendor/rjsmin-1.0.10/rjsmin.py b/vendor/rjsmin-1.0.10/rjsmin.py
new file mode 100755 (executable)
index 0000000..e785479
--- /dev/null
@@ -0,0 +1,446 @@
+#!/usr/bin/env python
+# -*- coding: ascii -*-
+r"""
+=====================
+ Javascript Minifier
+=====================
+
+rJSmin is a javascript minifier written in python.
+
+The minifier is based on the semantics of `jsmin.c by Douglas Crockford`_\\.
+
+:Copyright:
+
+ Copyright 2011 - 2014
+ Andr\xe9 Malo or his licensors, as applicable
+
+:License:
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+The module is a re-implementation aiming for speed, so it can be used at
+runtime (rather than during a preprocessing step). Usually it produces the
+same results as the original ``jsmin.c``. It differs in the following ways:
+
+- there is no error detection: unterminated string, regex and comment
+  literals are treated as regular javascript code and minified as such.
+- Control characters inside string and regex literals are left untouched; they
+  are not converted to spaces (nor to \\n)
+- Newline characters are not allowed inside string and regex literals, except
+  for line continuations in string literals (ECMA-5).
+- "return /regex/" is recognized correctly.
+- "+ +" and "- -" sequences are not collapsed to '++' or '--'
+- Newlines before ! operators are removed more sensibly
+- Comments starting with an exclamation mark (``!``) can be kept optionally
+- rJSmin does not handle streams, but only complete strings. (However, the
+  module provides a "streamy" interface).
+
+Since most parts of the logic are handled by the regex engine it's way faster
+than the original python port of ``jsmin.c`` by Baruch Even. The speed factor
+varies between about 6 and 55 depending on input and python version (it gets
+faster the more compressed the input already is). Compared to the
+speed-refactored python port by Dave St.Germain the performance gain is less
+dramatic but still between 3 and 50 (for huge inputs). See the docs/BENCHMARKS
+file for details.
+
+rjsmin.c is a reimplementation of rjsmin.py in C and speeds it up even more.
+
+Both python 2 and python 3 are supported.
+
+.. _jsmin.c by Douglas Crockford:
+   http://www.crockford.com/javascript/jsmin.c
+"""
+if __doc__:
+    # pylint: disable = W0622
+    __doc__ = __doc__.encode('ascii').decode('unicode_escape')
+__author__ = r"Andr\xe9 Malo".encode('ascii').decode('unicode_escape')
+__docformat__ = "restructuredtext en"
+__license__ = "Apache License, Version 2.0"
+__version__ = '1.0.10'
+__all__ = ['jsmin']
+
+import re as _re
+
+
+def _make_jsmin(python_only=False):
+    """
+    Generate JS minifier based on `jsmin.c by Douglas Crockford`_
+
+    .. _jsmin.c by Douglas Crockford:
+       http://www.crockford.com/javascript/jsmin.c
+
+    :Parameters:
+      `python_only` : ``bool``
+        Use only the python variant. If true, the c extension is not even
+        tried to be loaded.
+
+    :Return: Minifier
+    :Rtype: ``callable``
+    """
+    # pylint: disable = R0912, R0914, W0612
+
+    if not python_only:
+        try:
+            import _rjsmin  # pylint: disable = F0401
+        except ImportError:
+            pass
+        else:
+            return _rjsmin.jsmin
+    try:
+        xrange
+    except NameError:
+        xrange = range  # pylint: disable = W0622
+
+    space_chars = r'[\000-\011\013\014\016-\040]'
+
+    line_comment = r'(?://[^\r\n]*)'
+    space_comment = r'(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)'
+    space_comment_nobang = r'(?:/\*(?!!)[^*]*\*+(?:[^/*][^*]*\*+)*/)'
+    bang_comment = r'(?:/\*![^*]*\*+(?:[^/*][^*]*\*+)*/)'
+
+    string1 = \
+        r'(?:\047[^\047\\\r\n]*(?:\\(?:[^\r\n]|\r?\n|\r)[^\047\\\r\n]*)*\047)'
+    string2 = r'(?:"[^"\\\r\n]*(?:\\(?:[^\r\n]|\r?\n|\r)[^"\\\r\n]*)*")'
+    strings = r'(?:%s|%s)' % (string1, string2)
+
+    charclass = r'(?:\[[^\\\]\r\n]*(?:\\[^\r\n][^\\\]\r\n]*)*\])'
+    nospecial = r'[^/\\\[\r\n]'
+    regex = r'(?:/(?![\r\n/*])%s*(?:(?:\\[^\r\n]|%s)%s*)*/)' % (
+        nospecial, charclass, nospecial
+    )
+    space = r'(?:%s|%s)' % (space_chars, space_comment)
+    space_nobang = r'(?:%s|%s)' % (space_chars, space_comment_nobang)
+    newline = r'(?:%s?[\r\n])' % line_comment
+
+    def fix_charclass(result):
+        """ Fixup string of chars to fit into a regex char class """
+        pos = result.find('-')
+        if pos >= 0:
+            result = r'%s%s-' % (result[:pos], result[pos + 1:])
+
+        def sequentize(string):
+            """
+            Notate consecutive characters as sequence
+
+            (1-4 instead of 1234)
+            """
+            first, last, result = None, None, []
+            for char in map(ord, string):
+                if last is None:
+                    first = last = char
+                elif last + 1 == char:
+                    last = char
+                else:
+                    result.append((first, last))
+                    first = last = char
+            if last is not None:
+                result.append((first, last))
+            return ''.join(['%s%s%s' % (
+                chr(first),
+                last > first + 1 and '-' or '',
+                last != first and chr(last) or ''
+            ) for first, last in result])
+
+        return _re.sub(
+            r'([\000-\040\047])',  # \047 for better portability
+            lambda m: '\\%03o' % ord(m.group(1)), (
+                sequentize(result)
+                .replace('\\', '\\\\')
+                .replace('[', '\\[')
+                .replace(']', '\\]')
+            )
+        )
+
+    def id_literal_(what):
+        """ Make id_literal like char class """
+        match = _re.compile(what).match
+        result = ''.join([
+            chr(c) for c in xrange(127) if not match(chr(c))
+        ])
+        return '[^%s]' % fix_charclass(result)
+
+    def not_id_literal_(keep):
+        """ Make negated id_literal like char class """
+        match = _re.compile(id_literal_(keep)).match
+        result = ''.join([
+            chr(c) for c in xrange(127) if not match(chr(c))
+        ])
+        return r'[%s]' % fix_charclass(result)
+
+    not_id_literal = not_id_literal_(r'[a-zA-Z0-9_$]')
+    preregex1 = r'[(,=:\[!&|?{};\r\n]'
+    preregex2 = r'%(not_id_literal)sreturn' % locals()
+
+    id_literal = id_literal_(r'[a-zA-Z0-9_$]')
+    id_literal_open = id_literal_(r'[a-zA-Z0-9_${\[(!+-]')
+    id_literal_close = id_literal_(r'[a-zA-Z0-9_$}\])"\047+-]')
+
+    dull = r'[^\047"/\000-\040]'
+
+    space_sub_simple = _re.compile((
+        # noqa pylint: disable = C0330
+
+        r'(%(dull)s+)'
+        r'|(%(strings)s%(dull)s*)'
+        r'|(?<=%(preregex1)s)'
+            r'%(space)s*(?:%(newline)s%(space)s*)*'
+            r'(%(regex)s%(dull)s*)'
+        r'|(?<=%(preregex2)s)'
+            r'%(space)s*(?:%(newline)s%(space)s)*'
+            r'(%(regex)s%(dull)s*)'
+        r'|(?<=%(id_literal_close)s)'
+            r'%(space)s*(?:(%(newline)s)%(space)s*)+'
+            r'(?=%(id_literal_open)s)'
+        r'|(?<=%(id_literal)s)(%(space)s)+(?=%(id_literal)s)'
+        r'|(?<=\+)(%(space)s)+(?=\+)'
+        r'|(?<=-)(%(space)s)+(?=-)'
+        r'|%(space)s+'
+        r'|(?:%(newline)s%(space)s*)+'
+    ) % locals()).sub
+    #print space_sub_simple.__self__.pattern
+
+    def space_subber_simple(match):
+        """ Substitution callback """
+        # pylint: disable = R0911
+
+        groups = match.groups()
+        if groups[0]:
+            return groups[0]
+        elif groups[1]:
+            return groups[1]
+        elif groups[2]:
+            return groups[2]
+        elif groups[3]:
+            return groups[3]
+        elif groups[4]:
+            return '\n'
+        elif groups[5] or groups[6] or groups[7]:
+            return ' '
+        else:
+            return ''
+
+    space_sub_banged = _re.compile((
+        # noqa pylint: disable = C0330
+
+        r'(%(dull)s+)'
+        r'|(%(strings)s%(dull)s*)'
+        r'|(%(bang_comment)s%(dull)s*)'
+        r'|(?<=%(preregex1)s)'
+            r'%(space)s*(?:%(newline)s%(space)s*)*'
+            r'(%(regex)s%(dull)s*)'
+        r'|(?<=%(preregex2)s)'
+            r'%(space)s*(?:%(newline)s%(space)s)*'
+            r'(%(regex)s%(dull)s*)'
+        r'|(?<=%(id_literal_close)s)'
+            r'%(space)s*(?:(%(newline)s)%(space)s*)+'
+            r'(?=%(id_literal_open)s)'
+        r'|(?<=%(id_literal)s)(%(space)s)+(?=%(id_literal)s)'
+        r'|(?<=\+)(%(space)s)+(?=\+)'
+        r'|(?<=-)(%(space)s)+(?=-)'
+        r'|%(space)s+'
+        r'|(?:%(newline)s%(space)s*)+'
+    ) % dict(locals(), space=space_nobang)).sub
+    #print space_sub_banged.__self__.pattern
+
+    def space_subber_banged(match):
+        """ Substitution callback """
+        # pylint: disable = R0911
+
+        groups = match.groups()
+        if groups[0]:
+            return groups[0]
+        elif groups[1]:
+            return groups[1]
+        elif groups[2]:
+            return groups[2]
+        elif groups[3]:
+            return groups[3]
+        elif groups[4]:
+            return groups[4]
+        elif groups[5]:
+            return '\n'
+        elif groups[6] or groups[7] or groups[8]:
+            return ' '
+        else:
+            return ''
+
+    def jsmin(script, keep_bang_comments=False):  # pylint: disable = W0621
+        r"""
+        Minify javascript based on `jsmin.c by Douglas Crockford`_\.
+
+        Instead of parsing the stream char by char, it uses a regular
+        expression approach which minifies the whole script with one big
+        substitution regex.
+
+        .. _jsmin.c by Douglas Crockford:
+           http://www.crockford.com/javascript/jsmin.c
+
+        :Parameters:
+          `script` : ``str``
+            Script to minify
+
+          `keep_bang_comments` : ``bool``
+            Keep comments starting with an exclamation mark? (``/*!...*/``)
+
+        :Return: Minified script
+        :Rtype: ``str``
+        """
+        if keep_bang_comments:
+            return space_sub_banged(
+                space_subber_banged, '\n%s\n' % script
+            ).strip()
+        else:
+            return space_sub_simple(
+                space_subber_simple, '\n%s\n' % script
+            ).strip()
+
+    return jsmin
+
+jsmin = _make_jsmin()
+
+
+def jsmin_for_posers(script, keep_bang_comments=False):
+    r"""
+    Minify javascript based on `jsmin.c by Douglas Crockford`_\.
+
+    Instead of parsing the stream char by char, it uses a regular
+    expression approach which minifies the whole script with one big
+    substitution regex.
+
+    .. _jsmin.c by Douglas Crockford:
+       http://www.crockford.com/javascript/jsmin.c
+
+    :Warning: This function is the digest of a _make_jsmin() call. It just
+              utilizes the resulting regexes. It's here for fun and may
+              vanish any time. Use the `jsmin` function instead.
+
+    :Parameters:
+      `script` : ``str``
+        Script to minify
+
+      `keep_bang_comments` : ``bool``
+        Keep comments starting with an exclamation mark? (``/*!...*/``)
+
+    :Return: Minified script
+    :Rtype: ``str``
+    """
+    if not keep_bang_comments:
+        rex = (
+            r'([^\047"/\000-\040]+)|((?:(?:\047[^\047\\\r\n]*(?:\\(?:[^\r\n]'
+            r'|\r?\n|\r)[^\047\\\r\n]*)*\047)|(?:"[^"\\\r\n]*(?:\\(?:[^\r\n]'
+            r'|\r?\n|\r)[^"\\\r\n]*)*"))[^\047"/\000-\040]*)|(?<=[(,=:\[!&|?'
+            r'{};\r\n])(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*'
+            r'][^*]*\*+)*/))*(?:(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\0'
+            r'14\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)*((?:/(?![\r'
+            r'\n/*])[^/\\\[\r\n]*(?:(?:\\[^\r\n]|(?:\[[^\\\]\r\n]*(?:\\[^\r'
+            r'\n][^\\\]\r\n]*)*\]))[^/\\\[\r\n]*)*/)[^\047"/\000-\040]*)|(?<'
+            r'=[\000-#%-,./:-@\[-^`{-~-]return)(?:[\000-\011\013\014\016-\04'
+            r'0]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*(?:(?:(?://[^\r\n]*)?['
+            r'\r\n])(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^'
+            r'*]*\*+)*/)))*((?:/(?![\r\n/*])[^/\\\[\r\n]*(?:(?:\\[^\r\n]|(?:'
+            r'\[[^\\\]\r\n]*(?:\\[^\r\n][^\\\]\r\n]*)*\]))[^/\\\[\r\n]*)*/)['
+            r'^\047"/\000-\040]*)|(?<=[^\000-!#%&(*,./:-@\[\\^`{|~])(?:[\000'
+            r'-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*(?'
+            r':((?:(?://[^\r\n]*)?[\r\n]))(?:[\000-\011\013\014\016-\040]|(?'
+            r':/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)+(?=[^\000-\040"#%-\047)*,.'
+            r'/:-@\\-^`|-~])|(?<=[^\000-#%-,./:-@\[-^`{-~-])((?:[\000-\011\0'
+            r'13\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)))+(?=[^\00'
+            r'0-#%-,./:-@\[-^`{-~-])|(?<=\+)((?:[\000-\011\013\014\016-\040]'
+            r'|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)))+(?=\+)|(?<=-)((?:[\000-'
+            r'\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)))+(?'
+            r'=-)|(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]'
+            r'*\*+)*/))+|(?:(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\014\0'
+            r'16-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)+'
+        )
+
+        def subber(match):
+            """ Substitution callback """
+            groups = match.groups()
+            return (
+                groups[0] or
+                groups[1] or
+                groups[2] or
+                groups[3] or
+                (groups[4] and '\n') or
+                (groups[5] and ' ') or
+                (groups[6] and ' ') or
+                (groups[7] and ' ') or
+                ''
+            )
+    else:
+        rex = (
+            r'([^\047"/\000-\040]+)|((?:(?:\047[^\047\\\r\n]*(?:\\(?:[^\r\n]'
+            r'|\r?\n|\r)[^\047\\\r\n]*)*\047)|(?:"[^"\\\r\n]*(?:\\(?:[^\r\n]'
+            r'|\r?\n|\r)[^"\\\r\n]*)*"))[^\047"/\000-\040]*)|((?:/\*![^*]*\*'
+            r'+(?:[^/*][^*]*\*+)*/)[^\047"/\000-\040]*)|(?<=[(,=:\[!&|?{};\r'
+            r'\n])(?:[\000-\011\013\014\016-\040]|(?:/\*(?!!)[^*]*\*+(?:[^/*'
+            r'][^*]*\*+)*/))*(?:(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\0'
+            r'14\016-\040]|(?:/\*(?!!)[^*]*\*+(?:[^/*][^*]*\*+)*/))*)*((?:/('
+            r'?![\r\n/*])[^/\\\[\r\n]*(?:(?:\\[^\r\n]|(?:\[[^\\\]\r\n]*(?:'
+            r'\\[^\r\n][^\\\]\r\n]*)*\]))[^/\\\[\r\n]*)*/)[^\047"/\000-\040]'
+            r'*)|(?<=[\000-#%-,./:-@\[-^`{-~-]return)(?:[\000-\011\013\014\0'
+            r'16-\040]|(?:/\*(?!!)[^*]*\*+(?:[^/*][^*]*\*+)*/))*(?:(?:(?://['
+            r'^\r\n]*)?[\r\n])(?:[\000-\011\013\014\016-\040]|(?:/\*(?!!)[^*'
+            r']*\*+(?:[^/*][^*]*\*+)*/)))*((?:/(?![\r\n/*])[^/\\\[\r\n]*(?:('
+            r'?:\\[^\r\n]|(?:\[[^\\\]\r\n]*(?:\\[^\r\n][^\\\]\r\n]*)*\]))[^/'
+            r'\\\[\r\n]*)*/)[^\047"/\000-\040]*)|(?<=[^\000-!#%&(*,./:-@\[\\'
+            r'^`{|~])(?:[\000-\011\013\014\016-\040]|(?:/\*(?!!)[^*]*\*+(?:['
+            r'^/*][^*]*\*+)*/))*(?:((?:(?://[^\r\n]*)?[\r\n]))(?:[\000-\011'
+            r'\013\014\016-\040]|(?:/\*(?!!)[^*]*\*+(?:[^/*][^*]*\*+)*/))*)+'
+            r'(?=[^\000-\040"#%-\047)*,./:-@\\-^`|-~])|(?<=[^\000-#%-,./:-@'
+            r'\[-^`{-~-])((?:[\000-\011\013\014\016-\040]|(?:/\*(?!!)[^*]*\*'
+            r'+(?:[^/*][^*]*\*+)*/)))+(?=[^\000-#%-,./:-@\[-^`{-~-])|(?<=\+)'
+            r'((?:[\000-\011\013\014\016-\040]|(?:/\*(?!!)[^*]*\*+(?:[^/*][^'
+            r'*]*\*+)*/)))+(?=\+)|(?<=-)((?:[\000-\011\013\014\016-\040]|(?:'
+            r'/\*(?!!)[^*]*\*+(?:[^/*][^*]*\*+)*/)))+(?=-)|(?:[\000-\011\013'
+            r'\014\016-\040]|(?:/\*(?!!)[^*]*\*+(?:[^/*][^*]*\*+)*/))+|(?:(?'
+            r':(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\014\016-\040]|(?:/\*('
+            r'?!!)[^*]*\*+(?:[^/*][^*]*\*+)*/))*)+'
+        )
+
+        def subber(match):
+            """ Substitution callback """
+            groups = match.groups()
+            return (
+                groups[0] or
+                groups[1] or
+                groups[2] or
+                groups[3] or
+                groups[4] or
+                (groups[5] and '\n') or
+                (groups[6] and ' ') or
+                (groups[7] and ' ') or
+                (groups[8] and ' ') or
+                ''
+            )
+
+    return _re.sub(rex, subber, '\n%s\n' % script).strip()
+
+
+if __name__ == '__main__':
+    def main():
+        """ Main """
+        import sys as _sys
+        keep_bang_comments = (
+            '-b' in _sys.argv[1:]
+            or '-bp' in _sys.argv[1:]
+            or '-pb' in _sys.argv[1:]
+        )
+        if '-p' in _sys.argv[1:] or '-bp' in _sys.argv[1:] \
+                or '-pb' in _sys.argv[1:]:
+            global jsmin  # pylint: disable = W0603
+            jsmin = _make_jsmin(python_only=True)
+        _sys.stdout.write(jsmin(
+            _sys.stdin.read(), keep_bang_comments=keep_bang_comments
+        ))
+    main()