From 5d18b1bf99c6aeb0e58adf27265f448baabfe8db Mon Sep 17 00:00:00 2001 From: Gautier Colajanni Date: Tue, 23 Aug 2016 19:23:05 +0200 Subject: [PATCH] Settings with MDL * Save/load conf from localStorage * Propagate conf to r2 --- LICENSES.md | 1 + www/m/bower.json | 3 +- www/m/gulpfile.js | 2 + www/m/index.html | 2 + www/m/js/index.js | 274 +----------------------------- www/m/js/ui.js | 149 ++++++++++++++++ www/m/js/widgets/panelSettings.js | 244 ++++++++++++++++++++++++++ 7 files changed, 403 insertions(+), 272 deletions(-) create mode 100644 www/m/js/ui.js create mode 100644 www/m/js/widgets/panelSettings.js diff --git a/LICENSES.md b/LICENSES.md index b86b232..fe960c5 100644 --- a/LICENSES.md +++ b/LICENSES.md @@ -44,6 +44,7 @@ material (responsive) interface * [dialog-polyfill](https://github.com/GoogleChrome/dialog-polyfill) (BSD) * [DataTables](https://datatables.net/) (MIT) * [FileSaver](https://github.com/eligrey/FileSaver.js) (MIT) +* [mdl-selectfield](https://github.com/mebibou/mdl-selectfield) (MIT) panels (desktop) interface ----------- diff --git a/www/m/bower.json b/www/m/bower.json index 333a3d2..fda1b23 100644 --- a/www/m/bower.json +++ b/www/m/bower.json @@ -22,6 +22,7 @@ "dialog-polyfill": "^0.4.3", "datatables.net": "^1.10.12", "datatables.net-dt": "^1.10.12", - "file-saver": "^1.3.2" + "file-saver": "^1.3.2", + "mdl-selectfield": "^1.0.2" } } diff --git a/www/m/gulpfile.js b/www/m/gulpfile.js index bc0fe0c..307d7e1 100644 --- a/www/m/gulpfile.js +++ b/www/m/gulpfile.js @@ -26,6 +26,7 @@ gulp.task('dependencies', ['bower'], function() { './vendors/material-design-lite/material.min.js', './vendors/dialog-polyfill/dialog-polyfill.js', './vendors/datatables.net/js/jquery.dataTables.min.js', + './vendors/mdl-selectfield/dist/mdl-selectfield.min.js', './vendors/file-saver/FileSaver.min.js']) .pipe(gulp.dest(DEST + 'vendors/')); }); @@ -50,6 +51,7 @@ gulp.task('js', ['jscs'], function() { gulp.task('cssdeps', ['bower'], function() { gulp.src([ './vendors/dialog-polyfill/dialog-polyfill.css', + './vendors/mdl-selectfield/dist/mdl-selectfield.min.css', './vendors/material-design-lite/material.min.css']) .pipe(gulp.dest(DEST + 'vendors/')); diff --git a/www/m/index.html b/www/m/index.html index 0f5c84e..b83cb29 100644 --- a/www/m/index.html +++ b/www/m/index.html @@ -42,6 +42,7 @@ + @@ -239,6 +240,7 @@ + diff --git a/www/m/js/index.js b/www/m/js/index.js index a9fc689..b77116a 100755 --- a/www/m/js/index.js +++ b/www/m/js/index.js @@ -2,16 +2,6 @@ var update = function() {/* nop */}; var inColor = true; var lastView = panelDisasm; -function uiButton(href, label, type) { - var classes = 'mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect '; - classes += 'mdl-color--accent mdl-color-text--accent-contrast'; - if (type == 'active') { - var st = 'style="background-color:#f04040 !important"'; - return ' ' + label + ''; - } - return ' ' + label + ''; -} - function write() { var str = prompt('hexpairs, quoted string or :assembly'); if (str != '') { @@ -94,15 +84,6 @@ function analyze() { panelDisasm(); }); } -function uiCheckList(grp, id, label) { - var output = '
  • '; - ouput += '
  • '; - - return output; -} function notes() { var widget = widgetContainer.getWidget('Notes'); @@ -235,256 +216,12 @@ function analyzeNames() { }); } -function smallDisasm() { - r2.cmd('e asm.bytes=false'); - r2.cmd('e asm.lines=false'); - r2.cmd('e asm.cmtright=false'); -} - -function mediumDisasm() { - r2.cmd('e asm.bytes=false'); - r2.cmd('e asm.lines=true'); - r2.cmd('e asm.lineswidth=8'); - r2.cmd('e asm.cmtright=false'); -} - -function largeDisasm() { - r2.cmd('e asm.bytes=true'); - r2.cmd('e asm.lines=true'); - r2.cmd('e asm.lineswidth=12'); - r2.cmd('e asm.cmtright=true'); -} - -function configPseudo() { - r2.cmd('e asm.pseudo=1'); - r2.cmd('e asm.syntax=intel'); -} - -function configOpcodes() { - r2.cmd('e asm.pseudo=0'); - r2.cmd('e asm.syntax=intel'); -} - -function configATT() { - r2.cmd('e asm.pseudo=0'); - r2.cmd('e asm.syntax=att'); -} - function panelAbout() { r2.cmd('?V', function(version) { alert('radare2 material webui by --pancake @ 2015-2016\n\n' + version.trim()); }); } -function configColorDefault() { - r2.cmd('ecd', function() { - update(); - }); -} -function configColorRandom() { - r2.cmd('ecr', function() { - update(); - }); -} - -function configColorTheme(theme) { - r2.cmd('eco ' + theme, function() { - update(); - }); -} - -function configPA() { - r2.cmd('e io.va=false'); -} - -function configVA() { - r2.cmd('e io.va=true'); -} - -function configDebug() { - r2.cmd('e io.va=true'); - r2.cmd('e io.debug=true'); -} - -function configArch(name) { r2.cmd('e asm.arch=' + name); } -function configBits8() { r2.cmd('e asm.bits=8'); } -function configBits16() { r2.cmd('e asm.bits=16'); } -function configBits32() { r2.cmd('e asm.bits=32'); } -function configBits64() { r2.cmd('e asm.bits=64'); } -function configColorTrue() { inColor = true; } -function configColorFalse() { inColor = false; } - -var comboId = 0; - -function uiCombo(d) { - var funName = 'combo' + (++comboId); - var fun = funName + ' = function(e) {'; - fun += ' var sel = document.getElementById("opt_' + funName + '");'; - fun += ' var opt = sel.options[sel.selectedIndex].value;'; - fun += ' switch (opt) {'; - for (var a in d) { - fun += 'case "' + d[a].name + '": ' + d[a].js + '(' + d[a].name + ');break;'; - } - fun += '}}'; - // CSP violation here - eval(fun); - var out = ''; - return out; -} - -function uiSwitch(d) { - // TODO: not yet done - var out = d; - out += ''; - return out; -} - -function uiBlock(d) { - var classes = 'mdl-card__supporting-text mdl-shadow--2dp mdl-color-text--blue-grey-50 mdl-cell'; - var styles = 'display:inline-block;margin:5px;color:black !important;background-color:white !important'; - var out = '
    '; - out += '

    ' + d.name + '

    '; - for (var i in d.blocks) { - var D = d.blocks[i]; - out += '
    ' + D.name + ': '; - out += uiCombo(D.buttons); - } - out += '
    '; - return out; -} - -function panelSettings() { - var out = ''; - - var widget = widgetContainer.getWidget('Settings'); - var c = widgetContainer.getWidgetDOMWrapper(widget); - - updates.registerMethod(widget.getOffset(), panelSettings); - - c.style.backgroundColor = '#f0f0f0'; - out += '
    '; - out += uiBlock({ name: 'Platform', blocks: [ - { name: 'Arch', buttons: [ - { name: 'x86', js: 'configArch', default: true }, - { name: 'arm', js: 'configArch' }, - { name: 'mips', js: 'configArch' }, - { name: 'java', js: 'configArch' }, - { name: 'dalvik', js: 'configArch' }, - { name: '6502', js: 'configArch' }, - { name: '8051', js: 'configArch' }, - { name: 'h8300', js: 'configArch' }, - { name: 'hppa', js: 'configArch' }, - { name: 'i4004', js: 'configArch' }, - { name: 'i8008', js: 'configArch' }, - { name: 'lh5801', js: 'configArch' }, - { name: 'lm32', js: 'configArch' }, - { name: 'm68k', js: 'configArch' }, - { name: 'malbolge', js: 'configArch' }, - { name: 'mcs96', js: 'configArch' }, - { name: 'msp430', js: 'configArch' }, - { name: 'nios2', js: 'configArch' }, - { name: 'ppc', js: 'configArch' }, - { name: 'rar', js: 'configArch' }, - { name: 'sh', js: 'configArch' }, - { name: 'snes', js: 'configArch' }, - { name: 'sparc', js: 'configArch' }, - { name: 'spc700', js: 'configArch' }, - { name: 'sysz', js: 'configArch' }, - { name: 'tms320', js: 'configArch' }, - { name: 'v810', js: 'configArch' }, - { name: 'v850', js: 'configArch' }, - { name: 'ws', js: 'configArch' }, - { name: 'xcore', js: 'configArch' }, - { name: 'prospeller', js: 'configArch' }, - { name: 'gb', js: 'configArch' }, - { name: 'z80', js: 'configArch' }, - { name: 'arc', js: 'configArch' }, - { name: 'avr', js: 'configArch' }, - { name: 'bf', js: 'configArch' }, - { name: 'cr16', js: 'configArch' }, - { name: 'cris', js: 'configArch' }, - { name: 'csr', js: 'configArch' }, - { name: 'dcpu16', js: 'configArch' }, - { name: 'ebc', js: 'configArch' } - ]}, - { name: 'Bits', buttons: [ - { name: '64', js: 'configBits64' }, - { name: '32', js: 'configBits32', default: true }, - { name: '16', js: 'configBits16' }, - { name: '8', js: 'configBits8' } - ]}, - { name: 'OS', buttons: [ - { name: 'Linux', js: 'configOS_LIN', default: true }, - { name: 'Windows', js: 'configOS_W32' }, - { name: 'OSX', js: 'configOS_OSX' } - ]} - ] - }); - out += uiBlock({ name: 'Disassembly', blocks: [ - { name: 'Size', buttons: [ - { name: 'S', js: 'smallDisasm' }, - { name: 'M', js: 'mediumDisasm' }, - { name: 'L', js: 'largeDisasm' } - ]}, - { name: 'Decoding', buttons: [ - { name: 'Pseudo', js: 'configPseudo' }, - { name: 'Opcodes', js: 'configOpcodes' }, - { name: 'ATT', js: 'configATT' } - ]}, - { name: 'Colors', buttons: [ - { name: 'Yes', js: 'configColorTrue', default: true }, - { name: 'No', js: 'configColorFalse' } - ]}, - { name: 'Theme', buttons: [ - { name: 'Default', js: 'configColorDefault' }, - { name: 'Random', js: 'configColorRandom' }, - { name: 'Solarized', js: 'configColorTheme("solarized")' }, - { name: 'Ogray', js: 'configColorTheme("ogray")' }, - { name: 'Twilight', js: 'configColorTheme("twilight")' }, - { name: 'Rasta', js: 'configColorTheme("rasta")' }, - { name: 'Tango', js: 'configColorTheme("tango")' }, - { name: 'White', js: 'configColorTheme("white")' } - ]} - ]}); - out += uiBlock({ name: 'Core/IO', blocks: [ - { - name: 'Mode', buttons: [ - { name: 'PA', js: 'configPA' }, - { name: 'VA', js: 'configVA' }, - { name: 'Debug', js: 'configDebug' } - ] - } - ]}); - out += uiBlock({ name: 'Analysis', blocks: [ - { - name: 'HasNext', buttons: [ - { name: 'Yes', js: 'configAnalHasnextTrue', default: true }, - { name: 'No', js: 'configAnalHasnextFalse' } - ] - }, { - name: 'Skip Nops', buttons: [ - { name: 'Yes', js: 'configAnalNopskipTrue', default: true }, - { name: 'No', js: 'configAnalNopskipFalse' } - ] - }, { - name: 'NonCode', buttons: [ - { name: 'Yes', js: 'configAnalNoncodeTrue' }, - { name: 'No', js: 'configAnalNoncodeFalse', default: true } - ] - } - ]}); - out += '
    '; - c.innerHTML = out; -} - function panelFunctions() { var widget = widgetContainer.getWidget('Functions'); widget.setDark(); @@ -741,14 +478,6 @@ function down() { update(); } -function uiRoundButton(a, b, c) { - var out = ''; - out += ''; - return out; -} - var nativeDebugger = false; function srpc() { @@ -1018,6 +747,9 @@ function ready() { } twice = true; + // Loading configuration from localStorage (see panelSettings) + applyConf(); + updates = new UpdateManager(); lastViews = new UpdateManager(); diff --git a/www/m/js/ui.js b/www/m/js/ui.js new file mode 100644 index 0000000..1715a77 --- /dev/null +++ b/www/m/js/ui.js @@ -0,0 +1,149 @@ +function uiButton(href, label, type) { + var classes = 'mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect '; + classes += 'mdl-color--accent mdl-color-text--accent-contrast'; + if (type == 'active') { + var st = 'style="background-color:#f04040 !important"'; + return ' ' + label + ''; + } + return ' ' + label + ''; +} + +function uiCheckList(grp, id, label) { + var output = '
  • '; + ouput += '
  • '; + + return output; +} + +var comboId = 0; +function uiCombo(d) { + var funName = 'combo' + (++comboId); + var fun = funName + ' = function(e) {'; + fun += ' var sel = document.getElementById("opt_' + funName + '");'; + fun += ' var opt = sel.options[sel.selectedIndex].value;'; + fun += ' switch (opt) {'; + for (var a in d) { + fun += 'case "' + d[a].name + '": ' + d[a].js + '(' + d[a].name + ');break;'; + } + fun += '}}'; + // CSP violation here + eval(fun); + var out = ''; + return out; +} + +/** + * Add a switch, with a name "label", define default state by isChecked + * callbacks are bound when un-checked. + */ +var idSwitch = 0; +function uiSwitch(dom, name, isChecked, onChange) { + var id = 'switch-' + (++idSwitch); + + var label = document.createElement('label'); + label.className = 'mdl-switch mdl-js-switch mdl-js-ripple-effect'; + label.for = id; + dom.appendChild(label); + + var input = document.createElement('input'); + input.type = 'checkbox'; + input.className = 'mdl-switch__input'; + input.checked = isChecked; + input.id = id; + label.appendChild(input); + + input.addEventListener('change', function(evt) { + onChange(name, evt.target.checked); + }); + + var span = document.createElement('span'); + span.className = 'mdl-switch__label'; + span.innerHTML = name; + label.appendChild(span); +} + +function uiActionButton(dom, action, label) { + var button = document.createElement('a'); + button.href = '#'; + button.innerHTML = label; + button.addEventListener('click', action); + dom.appendChild(button); + + var classes = 'mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect '; + classes += 'mdl-color--accent mdl-color-text--accent-contrast'; + button.className = classes; + button.style.margin = '3px'; +} + +var selectId = 0; +function uiSelect(dom, name, list, defaultOffset, onChange) { + var id = 'select-' + (++selectId); + + var div = document.createElement('div'); + div.className = 'mdl-selectfield mdl-js-selectfield mdl-selectfield--floating-label'; + dom.appendChild(div); + + var select = document.createElement('select'); + select.className = 'mdl-selectfield__select'; + select.id = id; + select.name = id; + div.appendChild(select); + + for (var i = 0 ; i < list.length ; i++) { + var option = document.createElement('option'); + option.innerHTML = list[i]; + option.value = list[i]; + select.appendChild(option); + if (i === defaultOffset) { + option.selected = true; + } + } + + select.addEventListener('change', function(evt) { + onChange(evt.target.value); + }); + + var label = document.createElement('label'); + label.className = 'mdl-selectfield__label'; + label.for = id; + label.innerHTML = name; + div.appendChild(label); +} + +// function uiSwitch(d) { +// // TODO: not yet done +// var out = d; +// out += ''; +// return out; +// } + +function uiBlock(d) { + var classes = 'mdl-card__supporting-text mdl-shadow--2dp mdl-color-text--blue-grey-50 mdl-cell'; + var styles = 'display:inline-block;margin:5px;color:black !important;background-color:white !important'; + var out = ''; + for (var i in d.blocks) { + var D = d.blocks[i]; + out += '
    ' + D.name + ': '; + out += uiCombo(D.buttons); + } + return out; +} + +function uiRoundButton(a, b, c) { + var out = ''; + out += ''; + return out; +} diff --git a/www/m/js/widgets/panelSettings.js b/www/m/js/widgets/panelSettings.js new file mode 100644 index 0000000..46939e0 --- /dev/null +++ b/www/m/js/widgets/panelSettings.js @@ -0,0 +1,244 @@ +function getConf(confKey) { + var local = localStorage.getItem(confKey.name); + if (local !== null) { + if (local === 'false') { + local = false; + } else if (local === 'true') { + local = true; + } + return local; + } else { + return confKey.defVal; + } +} + +function saveConf(confKey, val) { + localStorage.setItem(confKey.name, val); + confKey.apply(val); +} + +function applyConf(force) { + force = (typeof force === 'undefined') ? false : force; + for (var item in R2Conf) { + var cnf = R2Conf[item]; + if ((!force && getConf(cnf) !== cnf.defVal) || force) { + cnf.apply(getConf(cnf)); + } + } +} + +function resetConf() { + for (var item in R2Conf) { + var cnf = R2Conf[item]; + localStorage.removeItem(cnf.name); + } + applyConf(true); +} + +var R2Conf = { + platform: { name: 'platform', defVal: 'x86', apply: function(p) { r2.cmd('e asm.arch=' + p); } }, + bits: { name: 'bits', defVal: '32', apply: function(p) { r2.cmd('e asm.bits=' + p); } }, + os: { name: 'os', defVal: 'Linux', apply: function(p) { console.log('OS is now: ' + p); } }, // missing + size: { name: 'size', defVal: 'S', apply: function(p) { + switch (p) { + case 'S': + r2.cmd('e asm.bytes=false'); + r2.cmd('e asm.lines=false'); + r2.cmd('e asm.cmtright=false'); + break; + case 'M': + r2.cmd('e asm.bytes=false'); + r2.cmd('e asm.lines=true'); + r2.cmd('e asm.lineswidth=8'); + r2.cmd('e asm.cmtright=false'); + break; + case 'L': + r2.cmd('e asm.bytes=true'); + r2.cmd('e asm.lines=true'); + r2.cmd('e asm.lineswidth=12'); + r2.cmd('e asm.cmtright=true'); + break; + }; + } + }, + decoding: { name: 'decoding', defVal: 'Pseudo', apply: function(p) { + switch (p) { + case 'Pseudo': + r2.cmd('e asm.pseudo=1'); + r2.cmd('e asm.syntax=intel'); + break; + case 'Opcodes': + r2.cmd('e asm.pseudo=0'); + r2.cmd('e asm.syntax=intel'); + break; + case 'ATT': + r2.cmd('e asm.pseudo=0'); + r2.cmd('e asm.syntax=att'); + break; + }; + } + }, + mode: { name: 'mode', defVal: 'PA', apply: function(p) { + switch (p) { + case 'PA': + r2.cmd('e io.va=false'); + break; + case 'VA': + r2.cmd('e io.va=true'); + break; + case 'Debug': + r2.cmd('e io.va=true'); + r2.cmd('e io.debug=true'); + break; + }; + } + }, + analHasNext: { name: 'analHasNext', defVal: true, apply: function(p) { console.log('analHasNext is ' + p); } }, + analSkipNops: { name: 'analSkipNops', defVal: true, apply: function(p) { console.log('analSkipNops is ' + p); } }, + analNonCode: { name: 'analNonCode', defVal: false, apply: function(p) { console.log('analNonCode is ' + p); } }, + colors: { name: 'colors', defVal: true, apply: function(p) { inColor = p; } }, + theme: { name: 'theme', defVal: 'none', apply: function(p) { r2.cmd('eco ' + p); } } // TODO +}; + +function panelSettings() { + var widget = widgetContainer.getWidget('Settings'); + var c = widgetContainer.getWidgetDOMWrapper(widget); + c.innerHTML = ''; + updates.registerMethod(widget.getOffset(), panelSettings); + + var grid = document.createElement('div'); + grid.className = 'mdl-grid'; + c.appendChild(grid); + + var platform = createGrid(grid, 'Platform'); + drawPlatform(platform); + + var disassembly = createGrid(grid, 'Disassembly'); + drawDisassembly(disassembly); + + var coreio = createGrid(grid, 'Core/IO'); + drawCoreIO(coreio); + + var analysis = createGrid(grid, 'Analysis'); + drawAnalysis(analysis); + + var colors = createGrid(grid, 'Colors'); + drawColors(colors); + + var reset = createGrid(grid, 'Reset configuration'); + uiActionButton(reset, function() { + resetConf(); + update(); + }, 'RESET'); + + componentHandler.upgradeDom(); +} + +function savedFromList(list, name, defaultOffset) { + var value = defaultOffset; + var saved = localStorage.getItem(name); + if (saved !== null) { + value = list.indexOf(saved); + } + return value; +} + +function drawPlatform(dom) { + var archs = ['x86', 'arm', 'mips', 'java', 'dalvik', '6502', '8051', 'h8300', 'hppa', 'i4004', 'i8008', 'lh5801', + 'lm32', 'm68k', 'malbolge', 'mcs96', 'msp430', 'nios2', 'ppc', 'rar', 'sh', 'snes', 'sparc', 'spc700', 'sysz', + 'tms320', 'v810', 'v850', 'ws', 'xcore', 'prospeller', 'gb', 'z80', 'arc', 'avr', 'bf', 'cr16', 'cris', 'csr', + 'dcpu16', 'ebc']; + uiSelect(dom, 'Platform', archs, archs.indexOf(getConf(R2Conf.platform)), function(item) { + saveConf(R2Conf.platform, item); + }); + + var bits = ['64', '32', '16', '8']; + uiSelect(dom, 'Bits', bits, bits.indexOf(getConf(R2Conf.bits)), function(item) { + saveConf(R2Conf.bits, item); + }); + + var os = ['Linux', 'Windows', 'OSX']; + uiSelect(dom, 'OS', os, os.indexOf(getConf(R2Conf.os)), function(item) { + saveConf(R2Conf.os, item); + }); +} + +function drawDisassembly(dom) { + var sizes = ['S', 'M', 'L']; + uiSelect(dom, 'Size', sizes, sizes.indexOf(getConf(R2Conf.size)), function(item) { + saveConf(R2Conf.size, item); + }); + var decoding = ['Pseudo', 'Opcodes', 'ATT']; + uiSelect(dom, 'Decoding', decoding, decoding.indexOf(getConf(R2Conf.decoding)), function(item) { + saveConf(R2Conf.decoding, item); + }); +} + +function drawCoreIO(dom) { + var mode = ['PA', 'VA', 'Debug']; + uiSelect(dom, 'Mode', mode, mode.indexOf(getConf(R2Conf.mode)), function(item) { + saveConf(R2Conf.mode, item); + }); +} + +function drawAnalysis(dom) { + var configAnal = function(param, state, conf) { + saveConf(conf, state); + }; + + uiSwitch(dom, 'HasNext', getConf(R2Conf.analHasNext), function(param, state) { + return configAnal(param, state, R2Conf.analHasNext); + }); + uiSwitch(dom, 'Skip Nops', getConf(R2Conf.analSkipNops), function(param, state) { + return configAnal(param, state, R2Conf.analSkipNops); + }); + uiSwitch(dom, 'NonCode', getConf(R2Conf.analNonCode), function(param, state) { + return configAnal(param, state, R2Conf.analNonCode); + }); +} + +function drawColors(dom) { + var colors; + r2.cmdj('ecoj', function(data) { + colors = data; + }); + + uiSwitch(dom, 'Colors', getConf(R2Conf.colors), function(param, state) { + saveConf(R2Conf.colors, state); + }); + + // Randomize + uiActionButton(dom, function() { + r2.cmd('ecr', function() { + update(); + }); + }, 'Randomize'); + + // Set default + uiActionButton(dom, function() { + r2.cmd('ecd', function() { + update(); + }); + }, 'Reset colors'); + + uiSelect(dom, 'Theme', colors, colors.indexOf(getConf(R2Conf.theme)), function(theme) { + saveConf(R2Conf.theme, theme); + }); +} + +function createGrid(dom, name) { + var div = document.createElement('div'); + div.className = 'mdl-cell mdl-color--white mdl-shadow--2dp mdl-cell--4-col'; + div.style.padding = '10px'; + dom.appendChild(div); + + var title = document.createElement('span'); + title.className = 'mdl-layout-title'; + title.innerHTML = name; + div.appendChild(title); + + var content = document.createElement('div'); + div.appendChild(content); + + return content; +}