.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
$(ROOTDIR)/configure $(CONFIGURE_ARGS)
# Binary
-${PROG}: check_config $(OBJS) $(ALLDEPS)
+${PROG}: check_config make_webui $(OBJS) $(ALLDEPS)
$(CC) -o $@ $(OBJS) $(CFLAGS) $(LDFLAGS)
# Object
clean:
rm -rf ${BUILDDIR}/src ${BUILDDIR}/bundle*
find . -name "*~" | xargs rm -f
+ $(MAKE) -f Makefile.webui clean
distclean: clean
rm -rf ${ROOTDIR}/libav_static
@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)
--- /dev/null
+#
+# 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
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,
"<div id=\"systemlog\"></div>\n"
"</body></html>\n",
tvheadend_version);
+
http_output_html(hc);
return 0;
}
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");
text: "Discover SAT>IP servers",
tooltip: 'Look for new SAT>IP servers',
iconCls: 'find',
- handler: satipDiscover,
+ handler: satipDiscover
});
} else {
var satipButton = null;
bodyStyle: 'padding:15px',
layout: 'form',
items: _items,
- tbar: _tbar,
+ tbar: _tbar
});
tvheadend.paneladd(panel, mpanel, index);
function satipDiscover() {
Ext.Ajax.request({
url: 'api/hardware/satip/discover',
- params: { op: 'all' },
+ params: { op: 'all' }
});
}
};
},
help: function() {
new tvheadend.help('Bouquets', 'config_bouquet.html');
- },
+ }
});
return panel;
tvheadend.AjaxConfirm({
url: 'api/dvr/entry/cancel',
params: {
- uuid: event.dvrUuid,
+ uuid: event.dvrUuid
},
success: function(d) {
win.close();
tvheadend.AjaxConfirm({
url: 'api/idnode/delete',
params: {
- uuid: event.dvrUuid,
+ uuid: event.dvrUuid
},
success: function(d) {
win.close();
'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'
}
]
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);
}
url: 'epggrab',
params: {
op: 'otaepgTrigger',
- after: 1,
+ after: 1
},
waitMsg: 'Triggering...',
failure: function(response, options) {
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;
// {{{
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'
);
--- /dev/null
+Ext.namespace('tvheadend');
}
);
}
- },
+ }
],
destroy: function() {
}
}
);
}
- },
+ }
],
destroy: function() {
}
* 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)) {
--- /dev/null
+#!/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)
}
.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 {
--- /dev/null
+#!/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()