Wed, 31 Dec 2014 20:38:35 +0100
Added the HTML5 to JavaScript converter.
--- a/PluginProjectWeb.py Wed Dec 31 19:16:39 2014 +0100 +++ b/PluginProjectWeb.py Wed Dec 31 20:38:35 2014 +0100 @@ -178,6 +178,8 @@ # TODO: add our actions here self.__html5ToCss3Act = self.__menu.addAction(self.tr( "HTML5 to CSS3"), self.__htm5ToCss3) + self.__html5ToJsAct = self.__menu.addAction(self.tr( + "HTML5 to JavaScript"), self.__htm5ToJs) self.__menu.aboutToShow.connect(self.__menuAboutToShow) @@ -190,6 +192,8 @@ self.__html5ToCss3Act.setEnabled( selectionAvailable and BeautifulSoupAvailable) + self.__html5ToJsAct.setEnabled( + selectionAvailable and BeautifulSoupAvailable) def __populateMenu(self, name, menu): """ @@ -254,7 +258,6 @@ """ Private slot handling the HTML5 to CSS3 conversion. """ - # TODO: implement this from ProjectWeb.Html5ToCss3Converter import Html5ToCss3Converter vm = e5App().getObject("ViewManager") editor = vm.activeWindow() @@ -269,3 +272,22 @@ newEditor = vm.activeWindow() newEditor.setText(css3) newEditor.setLanguage("dummy.css") + + def __htm5ToJs(self): + """ + Private slot handling the HTML5 to JavaScript conversion. + """ + from ProjectWeb.Html5ToJsConverter import Html5ToJsConverter + vm = e5App().getObject("ViewManager") + editor = vm.activeWindow() + html = editor.selectedText() + + converter = Html5ToJsConverter(html) + + js = converter.getJavaScript() + + if js: + vm.newEditor() + newEditor = vm.activeWindow() + newEditor.setText(js) + newEditor.setLanguage("dummy.js")
--- a/PluginWeb.e4p Wed Dec 31 19:16:39 2014 +0100 +++ b/PluginWeb.e4p Wed Dec 31 20:38:35 2014 +0100 @@ -19,14 +19,18 @@ <Source>PluginProjectWeb.py</Source> <Source>ProjectWeb/Html5ToCss3Converter.py</Source> <Source>ProjectWeb/Html5ToCss3ConverterParameterDialog.py</Source> + <Source>ProjectWeb/Html5ToJsConverter.py</Source> + <Source>ProjectWeb/Html5ToJsConverterParameterDialog.py</Source> </Sources> <Forms> <Form>ProjectWeb/Html5ToCss3ConverterParameterDialog.ui</Form> + <Form>ProjectWeb/Html5ToJsConverterParameterDialog.ui</Form> </Forms> <Translations/> <Resources/> <Interfaces> <Interface>ProjectWeb/Ui_Html5ToCss3ConverterParameterDialog.py</Interface> + <Interface>ProjectWeb/Ui_Html5ToJsConverterParameterDialog.py</Interface> </Interfaces> <Others> <Other>.hgignore</Other>
--- a/ProjectWeb/Html5ToCss3Converter.py Wed Dec 31 19:16:39 2014 +0100 +++ b/ProjectWeb/Html5ToCss3Converter.py Wed Dec 31 20:38:35 2014 +0100 @@ -31,10 +31,10 @@ 'font:inherit', 'vertical-align:baseline', 'line-height:1', 'outline:0', 'font-weight:inherit', 'font-style:inherit', 'font-family:inherit', 'vertical-align:baseline') - CssToIgnore = ('head', 'meta', 'noscript', 'script', 'style', 'link', - 'no-js', 'title', 'object', 'col', 'colgroup', 'option', - 'param', 'audio', 'basefont', 'isindex', 'svg', 'area', - 'embed', 'br') + TagsToIgnore = ('head', 'meta', 'noscript', 'script', 'style', 'link', + 'no-js', 'title', 'object', 'col', 'colgroup', 'option', + 'param', 'audio', 'basefont', 'isindex', 'svg', 'area', + 'embed', 'br') def __init__(self, html, parent=None): """ @@ -60,7 +60,7 @@ # TODO: implement this self.__createSoup() - alreadyDone = list(self.CssToIgnore) + alreadyDone = list(self.TagsToIgnore) css = '@charset "utf-8";{0}'.format(os.linesep) css += "/* {0} by {1}*/{2}".format(
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ProjectWeb/Html5ToJsConverter.py Wed Dec 31 20:38:35 2014 +0100 @@ -0,0 +1,152 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2014 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing the HTML5 to JavaScript converter. +""" + +from __future__ import unicode_literals + +import os +import re +import datetime +import getpass + +from PyQt5.QtCore import QObject +from PyQt5.QtWidgets import QDialog + +from .Html5ToJsConverterParameterDialog import \ + Html5ToJsConverterParameterDialog + + +class Html5ToJsConverter(QObject): + """ + Class implementing the HTML5 to JavaScript converter. + """ + JsTemplate8 = "{0}{1}{2}{3}{4}{5}{6}{7}" + TagsToIgnore = ('head', 'meta', 'noscript', 'script', 'style', 'link', + 'no-js', 'title', 'object', 'col', 'colgroup', 'option', + 'param', 'audio', 'basefont', 'isindex', 'svg', 'area', + 'embed', 'br') + + def __init__(self, html, parent=None): + """ + Constructor + + @param html HTML text to be converted (string) + @param parent reference to the parent object (QObject) + """ + super(Html5ToJsConverter, self).__init__(parent) + + self.__html = html + + def getJavaScript(self): + """ + Public method to get the converted JavaScript text. + + @return JavaScript text (string) + """ + dlg = Html5ToJsConverterParameterDialog() + if dlg.exec_() == QDialog.Accepted: + indentation, scriptTags = dlg.getData() + + self.__createSoup() + + alreadyDone = list(self.TagsToIgnore) + + js = "<script>{0}".format(os.linesep) if scriptTags else "" + js += "// {0} by {1}{2}".format( + datetime.datetime.now().isoformat().split(".")[0], + getpass.getuser(), + os.linesep + ) + js += "$(document).ready(function(){" + os.linesep + + # step 1: IDs + js += "/*{0}*/{1}".format( + "-" * 75, + os.linesep + ) + for id_ in self.__getIds(): + if id_ not in alreadyDone: + js += "{0}// {1}{2}".format( + indentation, + "#".join(id_).lower(), + os.linesep + ) + js += self.JsTemplate8.format( + indentation, + "var ", + re.sub("[^a-z0-9]", "", + id_[1].lower() if len(id_[1]) < 11 else + re.sub("[aeiou]", "", id_[1].lower())), + " = ", + '$("#{0}").length'.format(id_[1]), + ";", + os.linesep, + os.linesep + ) + alreadyDone.append(id_) + + # step 2: classes + js += "/*{0}*/{1}".format( + "-" * 75, + os.linesep + ) + for class_ in self.__getClasses(): + if class_ not in alreadyDone: + js += "{0}// {1}{2}".format( + indentation, + ".".join(class_).lower(), + os.linesep + ) + js += self.JsTemplate8.format( + indentation, + "var ", + re.sub("[^a-z0-9]", "", + class_[1].lower() if len(class_[1]) < 11 else + re.sub("[aeiou]", "", class_[1].lower())), + " = ", + '$(".{0}").length'.format(class_[1]), + ";", + os.linesep, + os.linesep + ) + alreadyDone.append(class_) + + js += "})" + js += "{0}</script>".format(os.linesep) if scriptTags else "" + else: + js = "" + return js.strip() + + def __createSoup(self): + """ + Private method to get a BeaitifulSoup object with our HTML text. + """ + from bs4 import BeautifulSoup + self.__soup = BeautifulSoup(BeautifulSoup(self.__html).prettify()) + + def __getClasses(self): + """ + Private method to extract all classes of the HTML text. + + @return list of tuples containing the tag name and its classes + as a blank separated string (list of tuples of two strings) + """ + classes = [(t.name, " ".join(t["class"])) for t in + self.__soup.find_all(True, {"class": True})] + return sorted(list(set(classes))) + + def __getIds(self): + """ + Private method to extract all IDs of the HTML text. + + @return list of tuples containing the tag name and its ID + (list of tuples of two strings) + """ + ids = [(t.name, t["id"]) for t in + self.__soup.find_all(True, {"id": True})] + return sorted(list(set(ids)))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ProjectWeb/Html5ToJsConverterParameterDialog.py Wed Dec 31 20:38:35 2014 +0100 @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2014 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing a dialog to enter the JavaScript conversion parameters. +""" + +from PyQt5.QtWidgets import QDialog + +from .Ui_Html5ToJsConverterParameterDialog import \ + Ui_Html5ToJsConverterParameterDialog + + +class Html5ToJsConverterParameterDialog( + QDialog, Ui_Html5ToJsConverterParameterDialog): + """ + Class implementing a dialog to enter the JavaScript conversion parameters. + """ + def __init__(self, parent=None): + """ + Constructor + + @param parent reference to the parent widget (QWidget) + """ + super(Html5ToJsConverterParameterDialog, self).__init__(parent) + self.setupUi(self) + + msh = self.minimumSizeHint() + self.resize(max(self.width(), msh.width()), msh.height()) + + def getData(self): + """ + Public method to get the entered data. + + @return tuple of indentation string (string) and a flag indicating to + enclose the code by 'script' tags (boolean) + """ + return (" " * self.indentationSpinBox.value(), + self.scriptCheckBox.isChecked())
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ProjectWeb/Html5ToJsConverterParameterDialog.ui Wed Dec 31 20:38:35 2014 +0100 @@ -0,0 +1,116 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Html5ToJsConverterParameterDialog</class> + <widget class="QDialog" name="Html5ToJsConverterParameterDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>317</width> + <height>102</height> + </rect> + </property> + <property name="windowTitle"> + <string>Conversion Parameters</string> + </property> + <property name="sizeGripEnabled"> + <bool>true</bool> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>JS Indentation Spaces:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QSpinBox" name="indentationSpinBox"> + <property name="toolTip"> + <string>Enter the amount of spaces to be used as indentation</string> + </property> + <property name="minimum"> + <number>2</number> + </property> + <property name="maximum"> + <number>8</number> + </property> + <property name="singleStep"> + <number>2</number> + </property> + <property name="value"> + <number>4</number> + </property> + </widget> + </item> + <item row="0" column="2"> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>220</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="2" column="0" colspan="3"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + <item row="1" column="0" colspan="3"> + <widget class="QCheckBox" name="scriptCheckBox"> + <property name="toolTip"> + <string>Select to enclose the generated JavaScript code by 'script' tags</string> + </property> + <property name="text"> + <string>Enclose by 'script' HTML tags</string> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>Html5ToJsConverterParameterDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>Html5ToJsConverterParameterDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui>