Skip welcome & menu and move to editor
Welcome to JS Bin
Load cached copy from
 
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Ember Starter Kit</title>
  <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/normalize/2.1.0/normalize.css">
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
  <script src="http://builds.emberjs.com/canary/ember.debug.js"></script>
  <script src="http://builds.emberjs.com/canary/ember-template-compiler.js"></script>
</head>
<body>
  <div id='app'></div>
  <pre id="logs"></pre>
  
  <script type="text/x-handlebars" id="application">
{{#if isMorning}}  Good morning
{{else if isAfternoon}}  Good afternoon
{{else}}
  Good night
{{/if}}
  </script>
</body>
</html>
 
/* Put your CSS here */
html, body {
    margin: 20px;
}
 
const VOID_TAGS = { area: true,
                  base: true,
                  br: true,
                  col: true,
                  command: true,
                  embed: true,
                  hr: true,
                  img: true,
                  input: true,
                  keygen: true,
                  link: true,
                  meta: true,
                  param: true,
                  source: true,
                  track: true,
                  wbr: true };
function childrenFor(node) {
  if (node.type === 'Program') {
    return node.body;
  }
  if (node.type === 'BlockStatement') {
    return node.program.body;
  }
  if (node.type === 'ElementNode') {
    return node.children;
  }
}
function isTextNode(node) {
  return node.type === 'TextNode';
}
function isCommentStatement(node) {
  return node.type === 'CommentStatement';
}
function BlockIndentation(options) {
  this.options = options;
  this.syntax = null; // set by HTMLBars
    // split into a source array (allow windows and posix line endings)
  this.source = this.options.rawSource.split(/(?:\r\n?|\n)/g);
}
BlockIndentation.prototype.log = function(results) {
  log(results.message);
};
BlockIndentation.prototype.transform = function(ast) {
log('running');
  var pluginContext = this;
  let b = this.syntax.builders;
  let walker = new this.syntax.Walker();
  walker.visit(ast, function(node) {
    if (pluginContext.detect(node)) {
      return pluginContext.process(node);
    }
  });
  return ast;
};
BlockIndentation.prototype.detect = function(node) {
    return node.type === 'BlockStatement' || node.type === 'ElementNode';
  };
    /*eslint no-unused-expressions: 0*/
  BlockIndentation.prototype.process = function(node) {
    this.validateBlockElse(node);
    this.validateBlockEnd(node);
    this.validateBlockChildren(node);
  },
  BlockIndentation.prototype.validateBlockEnd = function(node) {
    if (!this.shouldValidateBlockEnd(node)) {
      return;
    }
    var isElementNode = node.type === 'ElementNode';
    var displayName = isElementNode ? node.tag : node.path.original;
    var display = isElementNode ? '</' + displayName + '>' : '{{/' + displayName + '}}';
    var startColumn = node.loc.start.column;
    var endColumn   = node.loc.end.column;
    var startOffset = this.startOffsetFor(node);
    var controlCharCount = this.endingControlCharCount(node);
    var correctedEndColumn = endColumn - displayName.length - controlCharCount + startOffset;
    console.log(correctedEndColumn, startColumn);
    if(correctedEndColumn !== startColumn) {
      debugger
      var startLocation = 'L' + node.loc.start.line + ':C' + node.loc.start.column;
      var endLocation = 'L' + node.loc.end.line + ':C' + node.loc.end.column;
      var warning = 'Incorrect indentation for `' + displayName + '` beginning at ' + startLocation +
            '. Expected `' + display + '` ending at ' + endLocation + ' to be at an indentation of ' + startColumn + ' but ' +
            'was found at ' + correctedEndColumn + '.';
      this.log({
        message: warning,
        line: node.loc.end.line,
        column: node.loc.end.column,
        source: this.sourceForNode(node)
      });
    }
  };
  BlockIndentation.prototype.validateBlockChildren = function(node) {
    var children = childrenFor(node);
    if (!children || !children.length) {
      return;
    }
    // HTML elements that start and end on the same line are fine
    if (node.loc.start.line === node.loc.end.line) {
      return;
    }
    var startColumn = node.loc.start.column;
    var expectedStartColumn = startColumn + 2;
    for (var i = 0; i < children.length; i++) {
      var child = children[i];
      if (!child.loc) { continue; }
      // We might not actually be the first thing on the line. We might be
      // preceded by another element or statement, or by some text. So walk
      // backwards looking for something else on this line.
      var hasLeadingContent = false;
      for (var j = i - 1; j >= 0; j--) {
        var sibling = children[j];
        if (sibling.loc && !isTextNode(sibling)) {
          // Found an element or statement. If it's on this line, then we
          // have leading content, so set the flag and break. If it's not
          // on this line, then we've scanned back to a previous line, so
          // we can also break.
          if (sibling.loc.end.line === child.loc.start.line) {
            hasLeadingContent = true;
          }
          break;
        } else {
          var lines = sibling.chars.split(/[\r\n]/);
          var lastLine = lines[lines.length - 1];
          if (lastLine.trim()) {
            // The last line in this text node has non-whitespace content, so
            // set the flag.
            hasLeadingContent = true;
          }
          if (lines.length > 1) {
            // There are multiple lines meaning we've now scanned back to a
            // previous line, so we can break.
            break;
          }
        }
      }
      if (hasLeadingContent) {
        // There's content before us on the same line, so we don't care about
        // our column.
        continue;
      }
      var childStartColumn;
      // sanitize text node starting column info
      if (isTextNode(child)) {
        // TextNode's include leading newlines, but those newlines do
        // not get used in calculating indentation
        var withoutLeadingNewLines = child.chars.replace(/^(\r\n|\n)*/, '');
        var firstNonWhitespace = withoutLeadingNewLines.search(/\S/);
        // the TextNode is whitespace only, do nothing
        if (firstNonWhitespace === -1) { continue; }
        // detect if the TextNode starts with `{{`, if it does
        // correct for the stripped leading backslash (`\{{foo}}`)
        if (child.chars.slice(0, 2) === '{{') {
          childStartColumn = child.loc.start.column - 1;
        } else {
          childStartColumn = firstNonWhitespace;
        }
      } else {
        childStartColumn = child.loc.start.column;
      }
      if (expectedStartColumn !== childStartColumn) {
        var isElementNode = child.type === 'ElementNode';
        var display;
        if (isElementNode) {
          display = '<' + child.tag + '>';
        } else if (child.type === 'BlockStatement'){
          display = '{{#' + child.path.original + '}}';
        } else if (child.type === 'MustacheStatement') {
          display = '{{' + child.path.original + '}}';
        } else if (isTextNode(child)) {
          display = child.chars;
        } else if (isCommentStatement(child)) {
          display = '<!--' + child.value + '-->';
        } else {
          display = child.path.original;
        }
        var startLocation = 'L' + child.loc.start.line + ':C' + child.loc.start.column;
        var warning = 'Incorrect indentation for `' + display + '` beginning at ' + startLocation +
            '. Expected `' + display + '` to be at an indentation of ' + expectedStartColumn + ' but ' +
            'was found at ' + childStartColumn + '.';
        this.log({
          message: warning,
          line: child.loc && child.loc.start.line,
          column: child.loc && child.loc.start.column,
          source: this.sourceForNode(node)
        });
      }
    }
  };
  BlockIndentation.prototype.validateBlockElse = function(node) {
    if (node.type !== 'BlockStatement' || !node.inverse) {
      return;
    }
    if (this.detectNestedElseIfBlock(node)) {
      this.processNestedElseIfBlock(node);
    }
    var inverse = node.inverse;
    var startColumn = node.loc.start.column;
    var elseStartColumn = inverse.loc.start.column;
    if(elseStartColumn !== startColumn) {
      var displayName = node.path.original;
      var startLocation = 'L' + node.loc.start.line + ':C' + node.loc.start.column;
      var elseLocation = 'L' + inverse.loc.start.line + ':C' + inverse.loc.start.column;
      var warning = 'Incorrect indentation for inverse block of `{{#' + displayName + '}}` beginning at ' + startLocation +
            '. Expected `{{else}}` starting at ' + elseLocation + ' to be at an indentation of ' + startColumn + ' but ' +
            'was found at ' + elseStartColumn + '.';
      this.log({
        message: warning,
        line: inverse.loc.start.line,
        column: inverse.loc.start.column,
        source: this.sourceForNode(node.inverse)
      });
    }
  };
  BlockIndentation.prototype.detectNestedElseIfBlock = function(node) {
    var inverse = node.inverse;
    var firstItem = inverse && inverse.body[0];
    // handle `{{else if foo}}`
    if (inverse && firstItem && firstItem.type === 'BlockStatement') {
      return inverse.loc.start.line === firstItem.loc.start.line &&
        inverse.loc.start.column === firstItem.loc.start.column;
    }
    return false;
  };
  BlockIndentation.prototype.processNestedElseIfBlock = function(node) {
    var elseBlockStatement = node.inverse.body[0];
    elseBlockStatement._startOffset = 5 + elseBlockStatement.path.original.length;
  };
  BlockIndentation.prototype.startOffsetFor = function(node) {
    return node._startOffset || 0;
  };
  BlockIndentation.prototype.shouldValidateBlockEnd = function(node) {
    // HTML elements that start and end on the same line are fine
    if (node.loc.start.line === node.loc.end.line) {
      return;
    }
    // do not validate indentation on VOID_TAG's
    if (VOID_TAGS[node.tag]) {
      return;
    }
    var source = this.sourceForNode(node);
    var endingToken = '/' + node.path.original;
    var indexOfEnding = source.lastIndexOf(endingToken);
    
    return indexOfEnding !== -1;
  };
  BlockIndentation.prototype.endingControlCharCount = function(node) {
    if (node.type === 'ElementNode') {
      // </>
      return 3;
    }
    
    var source = this.sourceForNode(node);
    var endingToken = '/' + node.path.original;
    var indexOfEnding = source.lastIndexOf(endingToken);
    
    if (indexOfEnding === -1) { 
      return 0;
    }
    var leadingControlCharCount = 0;
    var i = indexOfEnding - 1;
    while (isControlChar(source[i])) {
      leadingControlCharCount++;
      i--;
    }
    var trailingControlCharCount = 0;
    i = indexOfEnding + endingToken.length;
    while (isControlChar(source[i])) {
      trailingControlCharCount++;
      i++;
    }
    var closingSlash = 1;
    return leadingControlCharCount + closingSlash + trailingControlCharCount;
  };
  function isControlChar(char) {
    return char === '~' || char === '{' || char === '}';
  }
  BlockIndentation.prototype.sourceForNode = function(node) {
    if (!node.loc) { return; }
    
    var firstLine = node.loc.start.line - 1;
    var lastLine = node.loc.end.line - 1;
    var currentLine = firstLine - 1;
    var firstColumn = node.loc.start.column;
    var lastColumn = node.loc.end.column;
    var string = [];
    var line;
    while (currentLine < lastLine) {
      currentLine++;
      line = this.source[currentLine];
      if (currentLine === firstLine) {
        if (firstLine === lastLine) {
          string.push(line.slice(firstColumn, lastColumn));
        } else {
          string.push(line.slice(firstColumn));
        }
      } else if (currentLine === lastLine) {
        string.push(line.slice(0, lastColumn));
      } else {
        string.push(line);
      }
    }
    return string.join('\n');
  };
// vendor stuff
$('script[type="text/x-handlebars"]').each(function() {
  let template = $(this);
  let templateName = template.attr('data-template-name') || template.attr('id') || 'application';
  
  log('compiling');
  try {
    Ember.HTMLBars.compile(template.html(), {
      rawSource: template.html(),
      moduleName: templateName,
      plugins: {
        ast: [BlockIndentation]
      }
    });  
  } catch(e) {
    log(e.stack)
  }
  
  logger('compiled: ', template.html())
});
// copied from emberjs/ember.js packages/ember-template-compiler/lib/system/calculate-location-display.js
function calculateLocationDisplay(moduleName, _loc) {
  let loc = _loc || {};
  let { column, line } = loc.start || {};
  let moduleInfo = '';
  if (moduleName) {
    moduleInfo +=  `'${moduleName}'`;
  }
  if (line !== undefined && column !== undefined) {
    if (moduleName) {
      // only prepend @ if the moduleName was present
      moduleInfo += '@ ';
    }
    moduleInfo += `L${line}:C${column}`;
  }
  if (moduleInfo) {
    moduleInfo = `(${moduleInfo}) `;
  }
  return moduleInfo;
}
Ember.onerror = function(error) {
  log(error.stack);
};
function log() {
  var msg = [].slice.call(arguments).join(' ');
  logs.insertBefore(document.createTextNode("\n" + msg), logs.firstChild);
}
Output

You can jump to the latest bin by adding /latest to your URL

Dismiss x
public
Bin info
rwjbluepro
0viewers