diff --git a/config/demos/processors/demos.js b/config/demos/processors/demos.js index 01008105ae..751e50f0fc 100644 --- a/config/demos/processors/demos.js +++ b/config/demos/processors/demos.js @@ -24,7 +24,7 @@ module.exports = { var transform = { '.scenario.js': function(doc) { doc.url = 'http://localhost:' + - config.get('buildConfig.protractorPort') + path.join( + config.get('buildConfig.protractorPort') + '/' + path.join( config.demoFolderPrefix || '', config.versionData.current.folder, _.template(assetOutputPath, _.assign({},doc,{fileName:''})) diff --git a/config/gulp-tasks/docs.js b/config/gulp-tasks/docs.js new file mode 100644 index 0000000000..29e0f3707a --- /dev/null +++ b/config/gulp-tasks/docs.js @@ -0,0 +1,198 @@ +var buildConfig = require('../build.config.js'); +var cp = require('child_process'); +var dgeni = require('dgeni'); +var gutil = require('gulp-util'); +var htmlparser = require('htmlparser2'); +var lunr = require('lunr'); +var path = require('canonical-path'); +var projectRoot = path.resolve(__dirname, '../..'); +var yaml = require('js-yaml'); + +module.exports = function(gulp, argv) { + + if (argv.dist) { + buildConfig.dist = argv.dist; + } + + gulp.task('docs', function() { + var docVersion = argv['doc-version'] || 'nightly'; + if (docVersion != 'nightly' && !semver.valid(docVersion)) { + console.log('Usage: gulp docs --doc-version=(nightly|versionName)'); + return process.exit(1); + } + + var config = dgeni.loadConfig(path.resolve(projectRoot, 'config/docs/docs.config.js')); + config.set('currentVersion', docVersion); + config.set( + 'rendering.outputFolder', + argv.dist ? argv.dist : path.resolve(projectRoot, buildConfig.dist, 'ionic-site') + ); + + return dgeni.generator(config)().then(function() { + gutil.log('Docs for', gutil.colors.cyan(docVersion), 'generated!'); + }); + }); + + gulp.task('demos', function(done) { + var demoVersion = argv['demo-version'] || 'nightly'; + if (demoVersion != 'nightly' && !semver.valid(demoVersion)) { + console.log('Usage: gulp docs --doc-version=(nightly|versionName)'); + return process.exit(1); + } + + var config = dgeni.loadConfig(path.resolve(projectRoot, 'config/demos/demos.config.js')); + config.set('currentVersion', demoVersion); + config.set('dist', buildConfig.dist); + config.set( + 'rendering.outputFolder', + argv.dist ? argv.dist : path.resolve(projectRoot, buildConfig.dist, 'ionic-demo') + ); + config.set('demoFolderPrefix', argv.release ? '' : '/dist/ionic-demo'); + + dgeni.generator(config)().then(function() { + gutil.log('Demos for', gutil.colors.cyan(demoVersion), 'generated!'); + gutil.log('Building ionic into demo folder...'); + console.log(cp.spawn); + cp.spawn('node', [ + projectRoot + '/node_modules/.bin/gulp', + 'build', + IS_RELEASE_BUILD ? '--release' : '--no-release', + '--dist=' + config.rendering.outputFolder + '/' + + config.rendering.contentsFolder + '/ionic' + ]) + .on('exit', done); + }); + }); + + gulp.task('docs-index', function() { + var idx = lunr(function() { + this.field('path'); + this.field('title', {boost: 10}); + this.field('body'); + this.ref('id'); + }); + var ref = {}; + var refId = 0; + + function addToIndex(path, title, layout, body) { + // Add the data to the indexer and ref object + idx.add({'path': path, 'body': body, 'title': title, id: refId}); + ref[refId] = {'p': path, 't': title, 'l': layout}; + refId++; + } + + return gulp.src([ + 'temp/ionic-site/docs/{components,guide,api,overview}/**/*.{md,html,markdown}', + 'temp/ionic-site/docs/index.html', + 'temp/ionic-site/getting-started/index.html', + 'temp/ionic-site/tutorials/**/*.{md,html,markdown}', + 'temp/ionic-site/_posts/**/*.{md,html,markdown}' + ]) + .pipe(es.map(function(file, callback) { + //docs for gulp file objects: https://github.com/wearefractal/vinyl + var contents = file.contents.toString(); //was buffer + + // Grab relative path from ionic-site root + var relpath = file.path.replace(/^.*?temp\/ionic-site\//, ''); + + // Read out the yaml portion of the Jekyll file + var yamlStartIndex = contents.indexOf('---'); + + if (yamlStartIndex === -1) { + return callback(); + } + + // read Jekyll's page yaml variables at the top of the file + var yamlEndIndex = contents.indexOf('---', yamlStartIndex+3); //starting from start + var yamlRaw = contents.substring(yamlStartIndex+3, yamlEndIndex); + + var pageData = yaml.safeLoad(yamlRaw); + if(!pageData.title || !pageData.layout) { + return callback(); + } + + // manually set to not be searchable, or for a blog post, manually set to be searchable + if(pageData.searchable === false || (pageData.layout == 'post' && pageData.searchable !== true)) { + return callback(); + } + + // clean up some content so code variables are searchable too + contents = contents.substring(yamlEndIndex+3); + contents = contents.replace(//gi, ''); + contents = contents.replace(/<\/code>/gi, ''); + contents = contents.replace(/<\/code>/gi, ''); + contents = contents.replace(/``/gi, ''); + + // create a clean path to the URL + var path = '/' + relpath.replace('index.md', '') + .replace('index.html', '') + .replace('.md', '.html') + .replace('.markdown', '.html'); + if(pageData.layout == 'post') { + path = '/blog/' + path.substring(19).replace('.html', '/'); + } + + var parser; + if(pageData.search_sections === true) { + // each section within the content should be its own search result + var section = { body: '', title: '' }; + var isTitleOpen = false; + + parser = new htmlparser.Parser({ + ontext: function(text){ + if(isTitleOpen) { + section.title += text; // get the title of this section + } else { + section.body += text.replace(/{%.*%}/, '', 'g'); // Ignore any Jekyll expressions + } + }, + onopentag: function(name, attrs) { + if(name == 'section' && attrs.id) { + // start building new section data + section = { body: '', path: path + '#' + attrs.id, title: '' }; + } else if( (name == 'h1' || name == 'h2' || name == 'h3') && attrs.class == 'title') { + isTitleOpen = true; // the next text will be this sections title + } + }, + onclosetag: function(name) { + if(name == 'section') { + // section closed, index this section then clear it out + addToIndex(section.path, section.title, pageData.layout, section.body); + section = { body: '', title: '' }; + } else if( (name == 'h1' || name == 'h2' || name == 'h3') && isTitleOpen) { + isTitleOpen = false; + } + } + }); + parser.write(contents); + parser.end(); + + } else { + // index the entire page + var body = ''; + parser = new htmlparser.Parser({ + ontext: function(text){ + body += text.replace(/{%.*%}/, '', 'g'); // Ignore any Jekyll expressions + } + }); + parser.write(contents); + parser.end(); + + addToIndex(path, pageData.title, pageData.layout, body); + } + + callback(); + + })).on('end', function() { + // Write out as one json file + mkdirp.sync('temp/ionic-site/data'); + fs.writeFileSync( + 'temp/ionic-site/data/index.json', + JSON.stringify({'ref': ref, 'index': idx.toJSON()}) + ); + }); + }); + +}; diff --git a/config/protractor.conf.js b/config/protractor.conf.js index e45a8ec590..959c1574aa 100644 --- a/config/protractor.conf.js +++ b/config/protractor.conf.js @@ -15,7 +15,7 @@ exports.config = { jasmineNodeOpts: { showColors: true, // Use colors in the command line report. defaultTimeoutInterval: 120000, - // isVerbose: true + isVerbose: true }, baseUrl: 'http://localhost:' + buildConfig.protractorPort, diff --git a/test/css/bars-clear.html b/test/css/bars-clear.html index beb618ed7b..c20a2d6360 100644 --- a/test/css/bars-clear.html +++ b/test/css/bars-clear.html @@ -1,6 +1,7 @@ - + + Bars Clear diff --git a/test/css/button-bar.html b/test/css/button-bar.html index 25f927e219..aadcf27fa4 100644 --- a/test/css/button-bar.html +++ b/test/css/button-bar.html @@ -1,6 +1,7 @@ - + + Button Bar diff --git a/test/css/buttons-borderless.html b/test/css/buttons-borderless.html index 8f957400dd..f3a685b997 100644 --- a/test/css/buttons-borderless.html +++ b/test/css/buttons-borderless.html @@ -1,6 +1,7 @@ - + + Buttons diff --git a/test/css/buttons-button-block-clear.html b/test/css/buttons-button-block-clear.html index 4b7aca7dc1..185902b840 100644 --- a/test/css/buttons-button-block-clear.html +++ b/test/css/buttons-button-block-clear.html @@ -1,6 +1,7 @@ - + + Buttons diff --git a/test/css/buttons-button-block.html b/test/css/buttons-button-block.html index 146e88a6c9..b73ddb67f4 100644 --- a/test/css/buttons-button-block.html +++ b/test/css/buttons-button-block.html @@ -1,6 +1,7 @@ - + + Buttons diff --git a/test/css/buttons-button-outline.html b/test/css/buttons-button-outline.html index 996e90f37f..5edb0e7d67 100644 --- a/test/css/buttons-button-outline.html +++ b/test/css/buttons-button-outline.html @@ -1,6 +1,7 @@ - + + Buttons diff --git a/test/css/buttons-clear.html b/test/css/buttons-clear.html index e4caee9fe2..839196cae2 100644 --- a/test/css/buttons-clear.html +++ b/test/css/buttons-clear.html @@ -1,6 +1,7 @@ - + + Buttons diff --git a/test/css/buttons-default.html b/test/css/buttons-default.html index f98b2e8d24..faa3f9a8bd 100644 --- a/test/css/buttons-default.html +++ b/test/css/buttons-default.html @@ -1,6 +1,7 @@ - + + Buttons diff --git a/test/css/buttons-full.html b/test/css/buttons-full.html index 9c5142ce24..5f86eb0a91 100644 --- a/test/css/buttons-full.html +++ b/test/css/buttons-full.html @@ -1,6 +1,7 @@ - + + Buttons diff --git a/test/css/buttons-large.html b/test/css/buttons-large.html index f8a088d6ca..5217bb40a0 100644 --- a/test/css/buttons-large.html +++ b/test/css/buttons-large.html @@ -1,6 +1,7 @@ - + + Buttons diff --git a/test/css/buttons-link-block-clear.html b/test/css/buttons-link-block-clear.html index 6eb4603b58..ed8ee7d4f0 100644 --- a/test/css/buttons-link-block-clear.html +++ b/test/css/buttons-link-block-clear.html @@ -1,6 +1,7 @@ - + + Buttons diff --git a/test/css/buttons-link-block.html b/test/css/buttons-link-block.html index 046781721a..17ba0ba94d 100644 --- a/test/css/buttons-link-block.html +++ b/test/css/buttons-link-block.html @@ -1,6 +1,7 @@ - + + Buttons diff --git a/test/css/buttons-link-outline.html b/test/css/buttons-link-outline.html index 92a3635364..3cd5ec7d1f 100644 --- a/test/css/buttons-link-outline.html +++ b/test/css/buttons-link-outline.html @@ -1,6 +1,7 @@ - + + Buttons diff --git a/test/css/buttons-misc.html b/test/css/buttons-misc.html index a268b6d6b7..1f2e053ded 100644 --- a/test/css/buttons-misc.html +++ b/test/css/buttons-misc.html @@ -1,6 +1,7 @@ - + + Buttons diff --git a/test/css/buttons-outline.html b/test/css/buttons-outline.html index 4b25f25d86..de3c723b5d 100644 --- a/test/css/buttons-outline.html +++ b/test/css/buttons-outline.html @@ -1,6 +1,7 @@ - + + Buttons diff --git a/test/css/buttons-pseudo-icon-compare.html b/test/css/buttons-pseudo-icon-compare.html index 1bab6c4cd9..f563075ec2 100644 --- a/test/css/buttons-pseudo-icon-compare.html +++ b/test/css/buttons-pseudo-icon-compare.html @@ -1,6 +1,7 @@ - + + Buttons diff --git a/test/css/buttons-small.html b/test/css/buttons-small.html index fb5123efb7..1685034184 100644 --- a/test/css/buttons-small.html +++ b/test/css/buttons-small.html @@ -1,6 +1,7 @@ - + + Buttons diff --git a/test/css/cards-header-footer.html b/test/css/cards-header-footer.html index 0a7bbc327a..921baf33fd 100644 --- a/test/css/cards-header-footer.html +++ b/test/css/cards-header-footer.html @@ -1,6 +1,7 @@ - + + Cards diff --git a/test/css/cards-item-avatar.html b/test/css/cards-item-avatar.html index 6bde69bca2..19063f4378 100644 --- a/test/css/cards-item-avatar.html +++ b/test/css/cards-item-avatar.html @@ -1,6 +1,7 @@ - + + Cards diff --git a/test/css/cards-item-body.html b/test/css/cards-item-body.html index 3b3567b957..77c242ebbc 100644 --- a/test/css/cards-item-body.html +++ b/test/css/cards-item-body.html @@ -1,6 +1,7 @@ - + + Cards diff --git a/test/css/cards-item-icon.html b/test/css/cards-item-icon.html index 625461a60b..56d98d9b07 100644 --- a/test/css/cards-item-icon.html +++ b/test/css/cards-item-icon.html @@ -1,6 +1,7 @@ - + + Cards diff --git a/test/css/cards-item-thumbnail.html b/test/css/cards-item-thumbnail.html index 975c425472..54c4c122ce 100644 --- a/test/css/cards-item-thumbnail.html +++ b/test/css/cards-item-thumbnail.html @@ -1,6 +1,7 @@ - + + Cards diff --git a/test/css/cards-text.html b/test/css/cards-text.html index c171ff4723..9ccbb5d670 100644 --- a/test/css/cards-text.html +++ b/test/css/cards-text.html @@ -1,6 +1,7 @@ - + + Cards diff --git a/test/css/colors.html b/test/css/colors.html index c40b703424..0dcf6e3c76 100644 --- a/test/css/colors.html +++ b/test/css/colors.html @@ -1,6 +1,7 @@ - + + Colors diff --git a/test/css/footers.html b/test/css/footers.html index 5aba716b0a..ae11f33f47 100644 --- a/test/css/footers.html +++ b/test/css/footers.html @@ -1,6 +1,7 @@ - + + Footers diff --git a/test/css/grid.html b/test/css/grid.html index 949c48cbef..82066638ee 100644 --- a/test/css/grid.html +++ b/test/css/grid.html @@ -1,6 +1,7 @@ - + + Grid diff --git a/test/css/headers.html b/test/css/headers.html index 19d2552c5a..5395b8c6c4 100644 --- a/test/css/headers.html +++ b/test/css/headers.html @@ -1,6 +1,7 @@ - + + Header diff --git a/test/css/input-checkbox.html b/test/css/input-checkbox.html index 7608051a4b..83fe50113a 100644 --- a/test/css/input-checkbox.html +++ b/test/css/input-checkbox.html @@ -1,6 +1,7 @@ - + + Input: Checkbox diff --git a/test/css/input-radio.html b/test/css/input-radio.html index b55a400a8e..658391b7da 100644 --- a/test/css/input-radio.html +++ b/test/css/input-radio.html @@ -1,6 +1,7 @@ - + + Radio Inputs diff --git a/test/css/input-range.html b/test/css/input-range.html index 2e69626db0..1c9e54c114 100644 --- a/test/css/input-range.html +++ b/test/css/input-range.html @@ -1,6 +1,7 @@ - + + Input: Range diff --git a/test/css/input-select.html b/test/css/input-select.html index 2a6cea2cc0..6a146fa66e 100644 --- a/test/css/input-select.html +++ b/test/css/input-select.html @@ -1,6 +1,7 @@ - + + Input: Select diff --git a/test/css/input-text.html b/test/css/input-text.html index fbcbe56cb5..c9daa16043 100644 --- a/test/css/input-text.html +++ b/test/css/input-text.html @@ -1,16 +1,14 @@ - + + Text Inputs - - +

Text Inputs

diff --git a/test/css/input-textarea.html b/test/css/input-textarea.html index 863661c047..ee3a965941 100644 --- a/test/css/input-textarea.html +++ b/test/css/input-textarea.html @@ -1,6 +1,7 @@ - + + Textarea diff --git a/test/css/input-toggle.html b/test/css/input-toggle.html index 53d236eb57..5230b13304 100644 --- a/test/css/input-toggle.html +++ b/test/css/input-toggle.html @@ -1,6 +1,7 @@ - + + Input: Toggle diff --git a/test/css/lists-avatar.html b/test/css/lists-avatar.html index 66f3d06542..2fbf12274d 100644 --- a/test/css/lists-avatar.html +++ b/test/css/lists-avatar.html @@ -1,6 +1,7 @@ - + + Lists diff --git a/test/css/lists-buttons.html b/test/css/lists-buttons.html index b8ca64aabc..f00c865fe5 100644 --- a/test/css/lists-buttons.html +++ b/test/css/lists-buttons.html @@ -1,6 +1,7 @@ - + + Lists diff --git a/test/css/lists-colors.html b/test/css/lists-colors.html index c2bc65dfff..17f1e6d7d4 100644 --- a/test/css/lists-colors.html +++ b/test/css/lists-colors.html @@ -1,6 +1,7 @@ - + + Lists diff --git a/test/css/lists-icons.html b/test/css/lists-icons.html index 1da687abc4..62dfbae813 100644 --- a/test/css/lists-icons.html +++ b/test/css/lists-icons.html @@ -1,6 +1,7 @@ - + + Lists diff --git a/test/css/lists-text.html b/test/css/lists-text.html index 6bd2d30d98..652c492ad1 100644 --- a/test/css/lists-text.html +++ b/test/css/lists-text.html @@ -1,6 +1,7 @@ - + + Lists diff --git a/test/css/lists-thumbnails.html b/test/css/lists-thumbnails.html index 050caae733..80e82b5c77 100644 --- a/test/css/lists-thumbnails.html +++ b/test/css/lists-thumbnails.html @@ -1,6 +1,7 @@ - + + Lists diff --git a/test/css/progress.html b/test/css/progress.html index 59cbb843cb..58ed18d109 100644 --- a/test/css/progress.html +++ b/test/css/progress.html @@ -1,6 +1,7 @@ - + + Progress diff --git a/test/css/scroll.html b/test/css/scroll.html index b2af585d83..31962e9d9a 100644 --- a/test/css/scroll.html +++ b/test/css/scroll.html @@ -1,6 +1,7 @@ - + + Scroll diff --git a/test/css/search-ios.html b/test/css/search-ios.html index bd1b2a2c86..ed81d04232 100644 --- a/test/css/search-ios.html +++ b/test/css/search-ios.html @@ -1,6 +1,7 @@ - + + Search diff --git a/test/css/search.html b/test/css/search.html index 00d8839e9a..b29ba979a0 100644 --- a/test/css/search.html +++ b/test/css/search.html @@ -1,6 +1,7 @@ - + + Search diff --git a/test/css/split-pane.html b/test/css/split-pane.html index cb05e4b9e4..05b87d170a 100644 --- a/test/css/split-pane.html +++ b/test/css/split-pane.html @@ -1,6 +1,7 @@ - + + Split Pane diff --git a/test/css/status-bar.html b/test/css/status-bar.html index e45ad23bc6..a30fe17659 100644 --- a/test/css/status-bar.html +++ b/test/css/status-bar.html @@ -1,6 +1,7 @@ - + + Status Bar @@ -20,89 +21,6 @@ display: block; } - - @@ -113,34 +31,5 @@

Status Bar

Home
- -
- -

fullScreen: function(showFullScreen, showStatusBar)

- -

Body Class: {{ bodyClass }}

- -

isFullScreen: {{ isFullScreen }}

- -

_showStatusBar: {{ _showStatusBar }}

- -

- - - - - - - -

- -

- - - -

- -
- diff --git a/test/css/tabs-and-cards.html b/test/css/tabs-and-cards.html index 6b59884f70..7d7d770722 100644 --- a/test/css/tabs-and-cards.html +++ b/test/css/tabs-and-cards.html @@ -1,6 +1,7 @@ - + + Tabs: Top Icons diff --git a/test/css/tabs-default.html b/test/css/tabs-default.html index 4293f76c62..82fbe71e99 100644 --- a/test/css/tabs-default.html +++ b/test/css/tabs-default.html @@ -1,6 +1,7 @@ - + + Tabs: Top Icons diff --git a/test/css/tabs-icons-bottom.html b/test/css/tabs-icons-bottom.html index 224ec93dd1..f9b80b8e2b 100644 --- a/test/css/tabs-icons-bottom.html +++ b/test/css/tabs-icons-bottom.html @@ -1,6 +1,7 @@ - + + Tabs: Bottom Icons diff --git a/test/css/tabs-icons-left.html b/test/css/tabs-icons-left.html index f7f4587e39..26accf821e 100644 --- a/test/css/tabs-icons-left.html +++ b/test/css/tabs-icons-left.html @@ -1,6 +1,7 @@ - + + Tabs: Left Icons diff --git a/test/css/tabs-icons-right.html b/test/css/tabs-icons-right.html index be2956a6e6..f5499defd6 100644 --- a/test/css/tabs-icons-right.html +++ b/test/css/tabs-icons-right.html @@ -1,6 +1,7 @@ - + + Tabs: Right Icons diff --git a/test/css/tabs-no-icons.html b/test/css/tabs-no-icons.html index e0d774e6d0..140554ea1f 100644 --- a/test/css/tabs-no-icons.html +++ b/test/css/tabs-no-icons.html @@ -1,6 +1,7 @@ - + + Tabs: No Icons diff --git a/test/css/tabs-only-icons.html b/test/css/tabs-only-icons.html index 7def736f47..ce00a25f90 100644 --- a/test/css/tabs-only-icons.html +++ b/test/css/tabs-only-icons.html @@ -1,6 +1,7 @@ - + + Tabs: Only Icons diff --git a/test/css/tabs-with-footer.html b/test/css/tabs-with-footer.html index 52da245848..167fde43a6 100644 --- a/test/css/tabs-with-footer.html +++ b/test/css/tabs-with-footer.html @@ -1,6 +1,7 @@ - + + Tab with a Footer diff --git a/test/css/test.scenario.js b/test/css/test.scenario.js index 8ab96f7627..033635fce6 100644 --- a/test/css/test.scenario.js +++ b/test/css/test.scenario.js @@ -1,31 +1,65 @@ describe('css snapshot', function() { [ - 'bars-clear', - 'buttons', - 'cards-header-footer', - 'cards-item-avatar', - 'cards-item-body', - 'cards-item-icon', - 'cards-item-thumbnail', - 'cards-text', - 'colors', - 'footers', - 'grid', - 'headers', - 'input-checkbox', - 'input-radio', - 'input-range', - 'input-select', - 'input-text', - 'input-textarea', - 'input-toogle', - 'list', - 'modals', + "bars-clear.html", + "button-bar.html", + "buttons-borderless.html", + "buttons-button-block-clear.html", + "buttons-button-block.html", + "buttons-button-outline.html", + "buttons-clear.html", + "buttons-default.html", + "buttons-full.html", + "buttons-large.html", + "buttons-link-block-clear.html", + "buttons-link-block.html", + "buttons-link-outline.html", + "buttons-misc.html", + "buttons-outline.html", + "buttons-pseudo-icon-compare.html", + "buttons-small.html", + "cards-header-footer.html", + "cards-item-avatar.html", + "cards-item-body.html", + "cards-item-icon.html", + "cards-item-thumbnail.html", + "cards-text.html", + "colors.html", + "footers.html", + "grid.html", + "headers.html", + "input-checkbox.html", + "input-radio.html", + "input-range.html", + "input-select.html", + "input-text.html", + "input-textarea.html", + "input-toggle.html", + "lists-avatar.html", + "lists-buttons.html", + "lists-colors.html", + "lists-icons.html", + "lists-text.html", + "lists-thumbnails.html", + "progress.html", + "scroll.html", + "search-ios.html", + "search.html", + "split-pane.html", + "status-bar.html", + "tabs-and-cards.html", + "tabs-default.html", + "tabs-icons-bottom.html", + "tabs-icons-left.html", + "tabs-icons-right.html", + "tabs-no-icons.html", + "tabs-only-icons.html", + "tabs-with-footer.html", + "type.html", ].forEach(function(testId) { describe(testId, function() { it('init', function() { - browser.get('http://localhost:8876/test/css/' + testId + '.html'); + browser.get('http://localhost:8876/test/css/' + testId); }); }); }); diff --git a/test/css/type.html b/test/css/type.html index cd32a68a62..c5d97bd658 100644 --- a/test/css/type.html +++ b/test/css/type.html @@ -1,6 +1,7 @@ - + + Type