From: Jaroslav Kysela Date: Wed, 13 May 2015 17:01:13 +0000 (+0200) Subject: WEBUI: compress and optimize the default js/css files X-Git-Tag: v4.1~19 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=231274e5779ac4fb6ca3ca7706f6b672095b8c28;p=thirdparty%2Ftvheadend.git WEBUI: compress and optimize the default js/css files - 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 --- diff --git a/.gitignore b/.gitignore index 9ad87a595..d16f2cec4 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/Makefile b/Makefile index 98b84ee35..7833674f9 100644 --- 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 index 000000000..3e2fafaf8 --- /dev/null +++ b/Makefile.webui @@ -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 . +# + +# +# 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 diff --git a/src/webui/extjs.c b/src/webui/extjs.c index a4cf38c93..769c8e6c9 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -113,81 +113,21 @@ extjs_root(http_connection_t *hc, const char *remain, void *opaque) htsbuf_qprintf(hq, "\n"); htsbuf_qprintf(hq, "\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) "
\n" "\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, "\n"); htsbuf_qprintf(hq, "\n"); htsbuf_qprintf(hq, "HTS Tvheadend %s\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, "\n"); diff --git a/src/webui/static/app/config.js b/src/webui/static/app/config.js index af0fa769e..354d306c5 100644 --- a/src/webui/static/app/config.js +++ b/src/webui/static/app/config.js @@ -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' } }); } }; diff --git a/src/webui/static/app/cteditor.js b/src/webui/static/app/cteditor.js index c86900250..b44267c96 100644 --- a/src/webui/static/app/cteditor.js +++ b/src/webui/static/app/cteditor.js @@ -66,7 +66,7 @@ tvheadend.bouquet = function(panel, index) }, help: function() { new tvheadend.help('Bouquets', 'config_bouquet.html'); - }, + } }); return panel; diff --git a/src/webui/static/app/epg.js b/src/webui/static/app/epg.js index 5032521bd..cd7675196 100644 --- a/src/webui/static/app/epg.js +++ b/src/webui/static/app/epg.js @@ -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); } diff --git a/src/webui/static/app/epggrab.js b/src/webui/static/app/epggrab.js index a9255b785..68c135cb7 100644 --- a/src/webui/static/app/epggrab.js +++ b/src/webui/static/app/epggrab.js @@ -406,7 +406,7 @@ tvheadend.epggrab = function(panel, index) { url: 'epggrab', params: { op: 'otaepgTrigger', - after: 1, + after: 1 }, waitMsg: 'Triggering...', failure: function(response, options) { diff --git a/src/webui/static/app/ext.css b/src/webui/static/app/ext.css index 760425d39..e27609434 100644 --- a/src/webui/static/app/ext.css +++ b/src/webui/static/app/ext.css @@ -12,12 +12,12 @@ 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; diff --git a/src/webui/static/app/extensions.js b/src/webui/static/app/extensions.js index a8df6cb8f..aefa3f681 100644 --- a/src/webui/static/app/extensions.js +++ b/src/webui/static/app/extensions.js @@ -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 index 000000000..200125f91 --- /dev/null +++ b/src/webui/static/app/namespace.js @@ -0,0 +1 @@ +Ext.namespace('tvheadend'); diff --git a/src/webui/static/app/status.js b/src/webui/static/app/status.js index cfeb6e363..26fdb593c 100644 --- a/src/webui/static/app/status.js +++ b/src/webui/static/app/status.js @@ -262,7 +262,7 @@ tvheadend.status_streams = function(panel, index) } ); } - }, + } ], destroy: function() { } @@ -509,7 +509,7 @@ tvheadend.status_conns = function(panel, index) { } ); } - }, + } ], destroy: function() { } diff --git a/src/webui/webui.c b/src/webui/webui.c index e397925cf..104d35f8c 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -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 index 000000000..8db79a6f5 --- /dev/null +++ b/support/css.py @@ -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) diff --git a/vendor/ext-3.4.1/examples/ux/gridfilters/css/GridFilters.css b/vendor/ext-3.4.1/examples/ux/gridfilters/css/GridFilters.css index 2e62e875c..a5ea5e3f1 100644 --- a/vendor/ext-3.4.1/examples/ux/gridfilters/css/GridFilters.css +++ b/vendor/ext-3.4.1/examples/ux/gridfilters/css/GridFilters.css @@ -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 index 000000000..e7854798a --- /dev/null +++ b/vendor/rjsmin-1.0.10/rjsmin.py @@ -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()