Merge branch 'develop' into dev_cxt

This commit is contained in:
cxt 2017-04-18 10:30:36 +08:00
commit c49191b70f
612 changed files with 110507 additions and 1252 deletions

View File

@ -75,7 +75,7 @@ group :development, :test do
gem 'pry-byebug'
gem "test-unit", "~>3.0"
end
gem 'rspec-rails', '~> 3.0'
# gem 'rspec-rails', '~> 3.0'
gem 'factory_girl_rails'
end

View File

@ -268,6 +268,8 @@ class AttachmentsController < ApplicationController
def upload
# Make sure that API users get used to set this content type
# as it won't trigger Rails' automatic parsing of the request body for parameters
unless request.content_type == 'application/octet-stream'
@ -295,6 +297,26 @@ class AttachmentsController < ApplicationController
end
end
def upload_with_markdown
logger.debug "upload_with_markdown"
@attachment = Attachment.new(:file => params["editormd-image-file"])
@attachment.author = User.current
if !params[:project].nil?
@attachment.container_type = 'Project'
@attachment.container_id = params[:project].split("?")[0]
end
@attachment.filename = params[:filename].presence || Redmine::Utils.random_hex(16)
saved = @attachment.save
render json: {
success: saved ? 1 : 0, # | 1, // 0 表示上传失败1 表示上传成功
message: "上传失败:#{ @attachment.errors.full_messages.join(',') }",
url: download_attachment_path(@attachment.id) # 上传成功时才返回
}
end
def upload_attachment_version
@flag = false
Attachment.transaction do

View File

@ -24,34 +24,49 @@ class ChallengesController < ApplicationController
@challenge.position = challenge_count + 1
@challenge.shixun = @shixun
@challenge.user = User.current
if @challenge.save
if params[:sample][:input].length > 0
params[:sample][:input].each_with_index do |value, index|
unless (value == params[:sample][:output][index] && value.blank?)
ChallengeSample.create(:challenge_id => @challenge.id, :input => value, :output => params[:sample][:output][index])
ActiveRecord::Base.transaction do
begin
@challenge.save!
if params[:test_set][:hidden].length > 0
params[:test_set][:input].each_with_index do |input, index|
params[:test_set][:hidden][index].to_i == 0 ? open = 1 : open = 0
unless (input[index] == params[:test_set][:output][index] && input[index].nil?)
TestSet.create(:challenge_id => @challenge.id, :input => input, :output => params[:test_set][:output][index], :is_public => open, :position => (index + 1))
end
end
end
end
if params[:program][:output].length > 0
params[:program][:output].each do |output|
TestSet.create(:challenge_id => @challenge.id, :input => nil, :output => output)
unless params[:knowledge].blank?
params[:knowledge][:input].each do |input|
ChallengeTag.create(:name => input, :challenge_id => @challenge.id)
end
end
end
unless params[:knowledge].blank?
params[:knowledge][:input].each do |input|
ChallengeTag.create(:name => input, :challenge_id => @challenge.id)
# 向jenkins端发送请求构建pipeline片段实时返回结果
jenkins_shixuns = Redmine::Configuration['jenkins_shixuns']
params = {:challengeStage => "#{@challenge.position}", :challengeType => "#{@challenge.evaluation_way.to_i}", :challengeProgramName => "#{@challenge.path}", :trainingID => "#{@shixun.id}"}
uri = URI("#{jenkins_shixuns}/jenkins-exec/game/generatePipelineScriptForChallenge")
res = uri_exec uri, {challengeInfo: params.to_json}
if res['code'] == 0
@challenge.update_attribute(:pipeline_script, res['msg'])
respond_to do |format|
format.html {redirect_to shixun_challenge_path(@challenge, :shixun_id => @shixun)}
end
else
logger.error("res['code'] is #{res['code']}, res['msg'] is #{res['code']}")
raise("脚本构建失败")
end
end
respond_to do |format|
format.html {redirect_to shixun_challenge_path(@challenge, :shixun_id => @shixun), notice: '创建成功!'}
end
else
respond_to do |format|
format.html { render :action => 'new' }
rescue Exception => e
flash[:error] = "#{e.message}"
redirect_to new_shixun_challenge_path(:shixun_id => @shixun.id)
raise ActiveRecord::Rollback
end
end
end
# 处理新建challenge返回的pipeline片段
def fragment_from_jenkins
end
def index
# 顶部导航
@challenges = @shixun.challenges.order("position desc")
@ -85,21 +100,16 @@ class ChallengesController < ApplicationController
respond_to do |format|
if @challenge.update_attributes(params[:challenge])
ActiveRecord::Base.transaction do
@challenge_samples.delete_all unless @challenge_samples.blank?
@test_sets.delete_all unless @test_sets.blank?
@challenge_tags.delete_all unless @challenge_tags.blank?
if params[:sample][:input].length > 0
params[:sample][:input].each_with_index do |value, index|
unless (value == params[:sample][:output][index] && value.blank?)
ChallengeSample.create(:challenge_id => @challenge.id, :input => value, :output => params[:sample][:output][index])
@challenge_tags.delete_all unless @challenge_tags.blank?
if params[:test_set][:hidden].length > 0
params[:test_set][:input].each_with_index do |input, index|
unless (input[index] == params[:test_set][:output][index] && input[index].nil?)
params[:test_set][:hidden][index].to_i == 0 ? open = 1 : open = 0
TestSet.create(:challenge_id => @challenge.id, :input => input, :output => params[:test_set][:output][index], :is_public => open, :position => (index + 1))
end
end
end
if params[:program][:output].length > 0
params[:program][:output].each do |output|
TestSet.create(:challenge_id => @challenge.id, :input => nil, :output => output)
end
end
unless params[:knowledge].blank?
params[:knowledge][:input].each do |input|
ChallengeTag.create(:name => input, :challenge_id => @challenge.id)
@ -181,7 +191,6 @@ class ChallengesController < ApplicationController
end
def query_challeges
@challenge_samples = @challenge.challenge_samples
@test_sets = @challenge.test_sets
@challenge_tags = @challenge.challenge_tags
end

View File

@ -22,8 +22,12 @@ class GamesController < ApplicationController
# mushixun的版本库必须创建时就创建
# 首次进入版本库自动打开文件
# path"" && path: @game.path
# @praise 判断是否点赞
# @praise 判断是否踩
def show
@game_challenge = @game.challenge
@praise = PraiseTread.where("praise_tread_object_id=? and praise_tread_object_type=? and user_id=?", @game_challenge.id, @game_challenge.class.to_s, User.current.id).first
@tread = PraiseTread.where("praise_tread_object_id=? and praise_tread_object_type=? and user_id=?", @game_challenge.id, "ChallengeTread", User.current.id).first
logger.info("test ................#{@game_challenge.id}")
game_path = (@game_challenge.path.blank? ? @path : @game_challenge.path.strip)
logger.info("test game_path ................#{game_path}")
@ -46,19 +50,14 @@ class GamesController < ApplicationController
logger.info("7777************#{@entries} ")
end
logger.info("88888************#{@entries} ")
@latest_output = @game.latest_output.try(:out_put).to_s
outputs = @game.outputs
(@myshixun.games.count == @game_challenge.position && @game.status ==2) ? @had_done = 1 : @had_done = 0
if outputs.count == 0
@results = []
else
@results = outputs.map{|result| [result.code, result.id]}
@test_sets = @game_challenge.test_sets
@test_sets.each_with_index do |test_set, index|
@test_set_error_position = (index + 1) unless test_set.result
end
logger.info("##################languae is #{@language}")
logger.info("##################latest_output is #{@latest_output}")
logger.info("##################had_done is #{@had_done}")
logger.info("##################status is #{@game.status}")
logger.info("##################result is #{@results}")
@test_sets_count = @test_sets.count
(@myshixun.games.count == @game_challenge.position && @game.status ==2) ? @had_done = 1 : @had_done = 0
@test_sets_hidden_count = @test_sets.where(:is_public => false).count
respond_to do |format|
format.html
format.js
@ -110,18 +109,10 @@ class GamesController < ApplicationController
jobName = "myshixun_#{@myshixun.id}"
ActiveRecord::Base.transaction do
@game.update_attributes!(:status => 1)
testCode = {}
test_sets = game_challenge.test_sets
unless test_sets.blank?
test_sets.each_with_index do |test_set, index|
testCode.store("testCode_#{index}",test_set.try(:output))
end
end
testCode = testCode.to_json
jenkins_shixuns = Redmine::Configuration['jenkins_shixuns']
step = game_challenge.try(:position)
params = {:jobName => "#{jobName}", :taskId => "#{taskId}", :step => "#{step}", :gitUrl => "#{gitUrl}", :testCode => "#{testCode}"}
uri = URI("#{jenkins_shixuns}/jenkins-exec/api/buildJob")
params = {:jobNameForInstance => "#{jobName}", :buildID => "#{taskId}", :step => "#{step}", :instanceGitURL => "#{gitUrl}", :instanceChallenge => "#{step}"}
uri = URI("#{jenkins_shixuns}/jenkins-exec/api/buildJobForInstance")
res = uri_exec uri, params
if (res && res['code'] != 0)
raise("Build job failed")
@ -167,7 +158,7 @@ class GamesController < ApplicationController
def next_step
render_403 if @game.status != 2
next_game = @game.next_game
next_game.update_attribute(:status, 0) if next_game.status == 3
next_game.update_attributes(:status => 0, :open_time => Time.now) if next_game.status == 3
respond_to do |format|
format.js{redirect_to myshixun_game_path(next_game, :myshixun_id => @myshixun.id)}

View File

@ -5,28 +5,33 @@ class MyshixunsController < ApplicationController
before_filter :find_myshixun, :only => [:show]
# taskId 即返回的game id
# params [:stauts] 0 表示成功,其它则失败
# 返回结果:params [:stauts] 0 表示成功,其它则失败
# msg 错误信息
# output 为测试用户编译输出结果
# myshixun:status 1为完成实训
# @jenkins: caseId对应test_set的positionpassed: 1表示成功0表示失败
def training_task_status
status = params[:status].to_i
task_id = params[:taskId]
outPut = Base64.decode64(params[:outPut]) unless params[:outPut].blank?
message = Base64.decode64(params[:msg]) unless params[:msg].blank?
game = Game.find(task_id)
jsonTestDetails = JSON.parse(params[:jsonTestDetails])
status = jsonTestDetails['status']
game_id = jsonTestDetails['buildID']
outPut = Base64.decode64(jsonTestDetails['outPut']) unless params[:outPut].blank?
jenkins_testsets = jsonTestDetails['msg']
# message = Base64.decode64(params[:msg]) unless params[:msg].blank?
game = Game.find(game_id)
challenge = game.challenge
if status == 0
myshixun = game.myshixun
games_count = myshixun.games.count
if challenge.position == games_count
myshixun.update_attribute(:status, 1)
test_sets = challenge.test_sets
game_outputs = Output.create(:code => status, :game_id => game_id, :out_put => outPut)
unless jenkins_testsets.blank?
jenkins_testsets.each do |j_test_set|
test_set = TestSet.where(:position => j_test_set['caseId'], :challenge_id => challenge).first
test_set.update_attributes(:actual_output => j_test_set['expectedOutput'], :result => j_test_set['passed'].to_i)
end
game_outputs = Output.create(:code => status, :msg => message, :game_id => task_id, :out_put => outPut)
end
if status == 0
game.update_attribute(:status, 2)
game.update_attribute(:final_score, challenge.score)
else
game_outputs = Output.create(:code => status, :msg => message, :game_id => task_id, :out_put => outPut)
game.update_attribute(:status, 0)
end
render :json => {:data => "success"}

View File

@ -17,13 +17,19 @@ class PraiseTreadController < ApplicationController
end
# @is_in_list = nil
@obj = find_object_by_type_and_id(@obj_type,@obj_id)
pts = PraiseTread.where("praise_tread_object_id=? and praise_tread_object_type=? and user_id=?",@obj_id,@obj_type.to_s,User.current.id)
unless pts.empty?
pts = PraiseTread.where("praise_tread_object_id=? and praise_tread_object_type=? and user_id=?",@obj_id,@obj_type.to_s,User.current.id).first
unless pts.blank?
if(params[:game_praise] == "true")
pts.delete
render :json => {praise: false}
return
end
respond_to do |format|
format.js
end
return
end
@horizontal = params[:horizontal].downcase == "false" ? false:true if params[:horizontal]
# if @obj.respond_to?("author_id")
# author_id = @obj.author_id
@ -33,6 +39,10 @@ class PraiseTreadController < ApplicationController
# unless author_id == User.current.id
praise_tread_plus(@obj_type,@obj_id,1)
# end
if params[:game_praise] == "true"
render :json => {praise: true}
return
end
respond_to do |format|
format.js
end
@ -137,6 +147,8 @@ class PraiseTreadController < ApplicationController
@obj = Syllabus.find_by_id(id)
when 'Work'
@obj = Work.find_by_id(id)
when 'Challenge' || 'ChallengeTread'
@obj = Challenge.find_by_id(id)
else
@obj = nil
end

View File

@ -5,28 +5,75 @@ class ShixunsController < ApplicationController
before_filter :require_login
before_filter :find_shixun, :except => [ :index, :new, :create]
before_filter :shixun_view_allow, :only => [:show]
before_filter :require_manager, :only => [ :settings, :add_script]
before_filter :require_manager, :only => [ :settings, :add_script, :publish]
include ApplicationHelper
def shixun_monitor
monitor_filter
if @had_exec
@tpm = Myshixun.where(:user_id => User.current, :shixun_id => @shixun).first
end
# monitor_filter
# if @had_exec
# @tpm = Myshixun.where(:user_id => User.current, :shixun_id => @shixun).first
# end
#
# respond_to do |format|
# format.js
# end
end
respond_to do |format|
format.js
# jenkins回调脚本
# gameID 及实训ID
def publish
jenkins_shixuns = Redmine::Configuration['jenkins_shixuns']
fragments = []
testSet = []
testCases = []
gameInfo = []
begin
challenges = @shixun.challenges.includes(:test_sets)
challenges.each do |challenge|
pipeline_script = challenge.try(:pipeline_script)
challenge_script = {:challengeStage => "#{challenge.position}", :challengePipeline => "#{pipeline_script}"}
fragments << challenge_script
challenge.test_sets.each do |test_set|
test_cases = {:caseId => test_set.id.to_s, :input => test_set.input, :output => test_set.output}
testSet << test_cases
end
test_sets_list = {:challengeStage => challenge.position.to_s, :challengeTestCases => testSet}
testCases << test_sets_list
end
gameInfo = {:trainingID => @shixun.id.to_s, :operationEnvironment => @shixun.language, :challengePipelines => fragments, :testCases => testCases}
fragments = fragments.to_json
uri = URI("#{jenkins_shixuns}/jenkins-exec/game/publishGame")
params = {gameInfo:gameInfo.to_json}
res = uri_exec uri, params
if res['code'] == 0
@shixun.update_attribute(:status, 1)
@shixun.update_attribute(:script, res["msg"])
else
logger.error("res['code'] is #{res['code']}, res['msg'] is #{res['code']}")
@jenkin_error = true
raise("服务器异常,请联系系统管理员")
end
rescue
respond_to do |format|
format.js
end
end
end
# copy_shixun复制一个新的实训模块包括版本库
# copy_myshixun自动创建系列game,game中只包含状态等信息公共信息从Challeges中读取
#
# 开启过实训的则直接跳入my实训页面
def shixun_exec
if has_exec_cur_shixun(@shixun) || User.current.id == @shixun.user_id
unless allow_shixun_exec(@shixun)
render_403
end
myshixun = Myshixun.where(:user_id => User.current.id, :shixun_id => @shixun.id).first
unless myshixun.blank?
logger.info("current task id is #{myshixun.current_task}")
redirect_to myshixun_game_path(myshixun.current_task, :myshixun_id => myshixun)
return
end
repository = @shixun.repository
ActiveRecord::Base.transaction do
begin
@ -42,17 +89,17 @@ class ShixunsController < ApplicationController
# 之所以增加user_id是为了方便统计查询性能
challenges.each_with_index do |challenge, index|
status = (index == 0 ? 0 : 3)
Game.create!(:challenge_id => challenge.id, :myshixun_id => myshixun.id, :status => status, :user_id => myshixun.user_id)
Game.create!(:challenge_id => challenge.id, :myshixun_id => myshixun.id, :status => status, :user_id => myshixun.user_id, :open_time => Time.now)
end
else
raise("网络异常,请稍后重试")
end
respond_to do |format|
format.html{myshixun_game_url(myshixun.current_task, :myshixun_id => myshixun)}
end
redirect_to myshixun_game_path(myshixun.current_task, :myshixun_id => myshixun)
rescue Exception => e
flash[:error] = l(:notice_shixun_failed_exec)+ " : " + e.message
flash[:error] = e.message
logger.info("failed to exec shixun: current task id is #{e}")
@g.delete_project(gshixun.id) if !gshixun.id.nil?
redirect_to shixun_challenges_path(@shixun)
raise ActiveRecord::Rollback
end
end
@ -92,7 +139,6 @@ class ShixunsController < ApplicationController
end
def edit
end
def update
@ -186,13 +232,14 @@ class ShixunsController < ApplicationController
jenkins_shixuns = Redmine::Configuration['jenkins_shixuns']
if myshixun.save!
MyshixunMember.create!(:myshixun_id => myshixun.id, :user_id => User.current.id, :role => 1)
uri = URI("#{jenkins_shixuns}/jenkins-exec/api/createJob")
pipeLine = "#{Base64.encode64(shixun.script)}"
params = {jobName: "myshixun_#{myshixun.id}", pipeLine: pipeLine}
res = uri_exec uri, params
rep = Repository.new(:myshixun_id => myshixun.id, :identifier => gshixun.name,:project_id => -1, :shixun_id => -2)
rep.type = "Repository::Gitlab"
rep.save!
rep_url = git_repository_url(myshixun, "Myshixun")
uri = URI("#{jenkins_shixuns}/jenkins-exec/game/openGameInstance")
pipeLine = "#{shixun.script}"
params = {jobNameForInstance: "myshixun_#{myshixun.id}", gamePipelineScript: pipeLine, instanceGitURL:rep_url}
res = uri_exec uri, params
if (res && res['code'] != 0)
raise("Job 创建失败")
end
@ -219,7 +266,7 @@ class ShixunsController < ApplicationController
# 实训管理员或者超级管理员
def require_manager
render_403 unless User.current.manager_of_shixun?(@shixun)
render_403 unless (User.current.manager_of_shixun?(@shixun) || User.current.admin?)
end
end

View File

@ -514,8 +514,19 @@ class WordsController < ApplicationController
if params[:text].size>0 && @user
# @user.add_jour(me, params[:text])
#私信
if params[:shixun_name]
game_id = params[:game_id]
message = params[:text]
admin_id = User.where(:admin => 1).first.try(:id)
user_ids = [admin_id, params[:shixun_author_id].to_i]
user_ids.each do |user_id|
JournalsForMessage.create(:jour_id => game_id, :jour_type => "Game", :user_id => user_id, :notes => message, :reply_id => 0, :status => true, :is_readed => false, :private => 1)
end
else
message = "<span style='color:red;'>【未收到激活邮件的用户反馈,用户邮箱:"+me.mail+"】</span><br>"+params[:text]
@user.journals_for_messages << JournalsForMessage.new(:user_id => me.id, :notes => message, :reply_id => 0, :status => true, :is_readed => false, :private => 1)
end
else
status = 0
end
@ -524,6 +535,7 @@ class WordsController < ApplicationController
render_403
end
end
private

View File

@ -50,6 +50,10 @@ module ApplicationHelper
end
# 定义实训相关方法
def sum_final_score
Game.find_by_sql("SELECT sum(final_score) as total_score FROM `games` where user_id=#{User.current.id}").first.try(:total_score)
end
# myshixun 最高分
def top_score shixun, position
Game.find_by_sql("SELECT max(final_score) as top_score FROM `games` g, `challenges` c where g.challenge_id = c.id and c.position=#{position} and g.myshixun_id in (SELECT id FROM `myshixuns` ms where ms.shixun_id=#{shixun.id})").first.try(:top_score)
@ -118,7 +122,7 @@ module ApplicationHelper
time = "#{hour}小时 : #{min}"
end
else
time = "#{day}: #{hour}小时 : #{min}"
time = "#{day}#{hour}小时 #{min}"
end
return time
end
@ -181,7 +185,23 @@ module ApplicationHelper
end
def uri_exec uri, params
res = Net::HTTP.post_form(uri, params).body
begin
logger.error("@parmas is #{params}, url is #{uri}")
res = Net::HTTP.post_form(uri, params).body
res = JSON.parse(res)
rescue => e
logger.error("failed to create challenge by 'generatePipelineScriptForChallenge'! #{e}")
raise("Jenkins服务器异常")
end
end
def uri_json_exec uri, params
Net::HTTP.start(uri.hostname, uri.port) {|http|
http.post(uri.path, params.to_json)
}
http = Net::HTTP.new(uri.host, uri.port)
res = Net::HTTP.post(uri, params.to_json).body
res = JSON.parse(res)
end
@ -276,7 +296,7 @@ module ApplicationHelper
end
def allow_shixun_exec shixun
(User.current.logged? && User.current.id != shixun.user_id && shixun.status == 1)
(User.current.logged? && shixun.challenges.count > 0 && !shixun.repository.blank?)
end
# 通过系统外部邮箱查找用户,如果用户不存在则用邮箱替换

View File

@ -1,2 +1,14 @@
# encoding: utf-8
module ChallengesHelper
def difficulty_type difficulty
case difficulty
when 1
"简单"
when 2
"中等"
when 3
"复杂"
end
end
end

View File

@ -1,7 +1,7 @@
class Game < ActiveRecord::Base
# stauts 0: can exe 1doing 2successed 3:locked
default_scope :order => 'created_at desc'
attr_accessible :myshixun_id, :user_id, :status, :final_score, :challenge_id
attr_accessible :myshixun_id, :user_id, :status, :final_score, :challenge_id, :open_time
belongs_to :myshixun,:touch=> true
belongs_to :user
belongs_to :challenge
@ -9,6 +9,14 @@ class Game < ActiveRecord::Base
has_many :test_sets, :dependent => :destroy
has_many :challenge_samples, :dependent => :destroy
def last_game
challenge = self.challenge
last_challenge_id = challenge.last_challenge
game = Game.where(:myshixun_id => self.myshixun_id, :challenge_id => last_challenge_id).first
render_404 if game.nil?
return game
end
def next_game
challenge = self.challenge
next_challenge_id = challenge.next_challenge

View File

@ -322,6 +322,9 @@ class JournalsForMessage < ActiveRecord::Base
receivers.each do |r|
self.user_feedback_messages << UserFeedbackMessage.new(:user_id => r.id, :journals_for_message_id => self.id, :journals_for_message_type => "Principal", :viewed => false)
end
elsif self.jour_type == 'Game'
# game不存在回复
self.user_feedback_messages << UserFeedbackMessage.new(:user_id => self.user_id, :journals_for_message_id => self.id, :journals_for_message_type => "Game", :viewed => false)
end
end

View File

@ -1,4 +1,5 @@
class TestSet < ActiveRecord::Base
attr_accessible :challenge_id, :input, :output, :game_id
default_scope :order => 'id asc'
attr_accessible :challenge_id, :input, :output, :game_id, :actual_output, :is_public, :result, :position
belongs_to :challenge
end

View File

@ -4,20 +4,60 @@
<%= stylesheet_link_tag '/editormd/css/editormd','/editormd/css/editormd.min' %>
<%= javascript_include_tag '/editormd/editormd.min.js','/editormd/examples/js/jquery.min.js' %>
<style>
.CodeMirror-scroll{
overflow: auto !important;
margin-bottom: -30px;
margin-right: -30px;
padding-bottom: 30px;
height: 100%;
outline: none;
position: relative;
}
</style>
<script type="text/javascript">
$(function() {
editormd("challenge_ready_konewlege", {
width : "80%",
height : 300,
syncScrolling : "single",
//你的lib目录的路径我这边用JSP做测试的
path : "/editormd/lib/",
//这个配置在simple.html中并没有但是为了能够提交表单使用这个配置可以让构造出来的HTML代码直接在第二个隐藏的textarea域中方便post提交表单。
saveHTMLToTextarea : true
// 用于增加自定义工具栏的功能可以直接插入HTML标签不使用默认的元素创建图标
challenge_editormd = editormd("challenge_ready_konewlege", {
width : "89.6%",
height : 300,
syncScrolling : "single",
//你的lib目录的路径我这边用JSP做测试的
path : "/editormd/lib/",
toolbarIcons : function() {
// Or return editormd.toolbarModes[name]; // full, simple, mini
// Using "||" set icons align right.
return ["bold", "italic", "|", "h1", "h2", "h3", "|", "list-ul", "list-ol", "|", "image", "code", "code-block", "|", "watch", "clear"]
},
//这个配置在simple.html中并没有但是为了能够提交表单使用这个配置可以让构造出来的HTML代码直接在第二个隐藏的textarea域中方便post提交表单。
saveHTMLToTextarea : true,
// 用于增加自定义工具栏的功能可以直接插入HTML标签不使用默认的元素创建图标
dialogMaskOpacity : 0.6,
placeholder: "请输入完成当前任务依赖的知识点或者其它相关信息",
imageUpload : true,
imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp"],
imageUploadURL : "<%= upload_with_markdown_path %>" //url
});
taskpass_editormd = editormd("challenge_task_pass", {
width : "89.6%",
height : 300,
syncScrolling : "single",
//你的lib目录的路径我这边用JSP做测试的
path : "/editormd/lib/",
toolbarIcons : function() {
// Or return editormd.toolbarModes[name]; // full, simple, mini
// Using "||" set icons align right.
return ["bold", "italic", "|", "h1", "h2", "h3", "|", "list-ul", "list-ol", "|", "image", "code", "code-block", "|", "watch", "clear"]
},
//这个配置在simple.html中并没有但是为了能够提交表单使用这个配置可以让构造出来的HTML代码直接在第二个隐藏的textarea域中方便post提交表单。
saveHTMLToTextarea : true,
// 用于增加自定义工具栏的功能可以直接插入HTML标签不使用默认的元素创建图标
dialogMaskOpacity : 0.6,
placeholder: "请输入完成当前任务依赖的知识点或者其它相关信息",
imageUpload : true,
imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp"],
imageUploadURL : "<%= upload_with_markdown_path %>"//url
});
});
</script>
@ -25,24 +65,16 @@
<li class="clearfix">
<p class="clearfix">
<span class="color-green fb fl" name="sample_inputs_label"></span>
<input type="checkbox" name="test_set[lock][]" class="fl ml5 mr5 mt8" value=""><span class="fl mr5">锁定</span>
<input type="hidden" name="test_set[hidden][]"/>
<a href="javascript:void(0)" title="增加" class="sample_icon_add"><i class="fa fa-plus-circle color-grey font-16 ml10 fl mt7"></i></a>
<a href="javascript:void(0)" title="删除" class="sample_icon_remove"><i class="fa fa-times-circle color-grey font-16 ml10 fl mt7"></i></a>
</p>
<input type="text" class="panel-form-width-100 panel-box-sizing mb10" name="sample[input][]" id="textarea_input_test" placeholder="样例输入" />
<input type="text" class="panel-form-width-100 panel-box-sizing mb10" name="sample[output][]" id="textarea_output_test" placeholder="样例输出" />
</li>
</script>
<script id="t:test-answer-list" type="text/html">
<li class="clearfix">
<p class="clearfix">
<span class="color-green fb fl" name="inputs_label"></span>
<a href="javascript:void(0);" title="增加" class="test_icon_add"><i class="fa fa-plus-circle color-grey font-16 ml10 fl mt7"></i></a>
<a href="javascript:void(0);" title="删除" class="test_icon_remove"><i class="fa fa-times-circle color-grey font-16 ml10 fl mt7"></i></a>
</p>
<!-- <input type="text" class="panel-form-width-670 panel-form-height-30 mb10" name="program[input][]" placeholder="测试输入" />-->
<textarea class="panel-form-width-100 panel-box-sizing" id="textarea_output_test" name="program[output][]" placeholder="输入测试集内容" ></textarea>
<textarea class="panel-form-width-40 panel-box-sizing" id="textarea_sample_input_test" name="test_set[input][]" placeholder="输入" ></textarea>
<textarea class="panel-form-width-40 panel-box-sizing" id="textarea_sample_output_test" name="test_set[output][]" placeholder="希望输出" ></textarea>
</li>
</script>
<div id="shixun_form">
<li class="clearfix">
<label class="panel-form-label fl"><span class="c_red mr5">*</span>名称:</label>
@ -57,88 +89,70 @@
<label class="panel-form-label fl">预备知识:</label>
<div id="challenge_ready_konewlege">
<%#= f.text_area :ready_knowledge, :placeholder => "请输入完成当前任务依赖的知识点或者其它相关信息,指导学员完成任务" %>
<textarea name="challenge[ready_knowledge]" placeholder = "请输入完成当前任务依赖的知识点或者其它相关信息,指导学员完成任务"></textarea>
<textarea name="challenge[ready_knowledge]" placeholder = "请输入完成当前任务依赖的知识点或者其它相关信息,指导学员完成任务"><%= @challenge.ready_knowledge %></textarea>
</div>
<%#= f.kindeditor :ready_knowledge, :placeholder => "请输入完成当前任务依赖的知识点或者其它相关信息,指导学员完成任务", :width=>'87.5%', :height => 159, :resizeType => 0, :no_label => true %>
</li>
<li class="clearfix">
<label class="panel-form-label fl"><span class="c_red mr5">*</span>过关任务:</label>
<%#= f.kindeditor :task_pass, :editor_id => "challenge_task_pass", :width=>'87.5%', :height => 159, :resizeType => 0, :no_label => true %>
<%= f.text_area :task_pass, :class => "panel-form-width-690 panel-box-sizing", :no_label => true %>
<%#= f.text_area :task_pass, :class => "panel-form-width-690 panel-box-sizing", :no_label => true %>
<div id="challenge_task_pass">
<%#= f.text_area :ready_knowledge, :placeholder => "请输入完成当前任务依赖的知识点或者其它相关信息,指导学员完成任务" %>
<textarea name="challenge[task_pass]" placeholder = "请输入完成当前任务依赖的知识点或者其它相关信息,指导学员完成任务"><%= @challenge.task_pass %></textarea>
</div>
<span style="display: none" class="c_red ml90" id="new_shixun_pass">过关任务不能为空</span>
</li>
<li class="clearfix">
<% if params[:action] == "edit" && @challenge.challenge_samples.count > 0 %>
<label class="panel-form-label fl">样例设置:</label>
<label class="panel-form-label fl"><span class="c_red mr5">*</span>评测方式:</label>
<%= select_tag :evaluation_way, options_for_select([["输出输出", 1]]), :name => 'challenge[evaluation_way]', :class => " fl", :style => "height: 40px; width:200px;" %>
<span class="fl ml15 mt8">请选择自动检测学员是否完成本任务的评测标准</span>
</li>
<li class="clearfix">
<% if params[:action] == "edit" && @challenge.test_sets.count > 0 %>
<label class="panel-form-label fl">测试集设置:</label>
<ul class="fl task-bg-grey panel-box-sizing" >
<% @challenge.challenge_samples.each_with_index do |sample, index| %>
<% @challenge.test_sets.each_with_index do |test_set, index| %>
<li class="clearfix">
<p class="clearfix">
<span class="color-green fb fl" name="sample_inputs_label">样例<%= index + 1 %></span>
<span class="color-green fb fl" name="sample_inputs_label">组<%= index + 1 %></span>
<% if test_set.is_public? %>
<input type="checkbox" name="test_set[lock][]" class="fl ml5 mr5 mt8"><span class="fl mr5">锁定</span>
<% else %>
<input type="checkbox" name="test_set[lock][]" class="fl ml5 mr5 mt8" checked="checked"><span class="fl mr5">锁定</span>
<% end %>
<input type="hidden" name="test_set[hidden][]"/>
<% if index == 0 %>
<span class="color-grey fr" style="font-size:12px;">温馨提示:输入样例供学员参考。</span>
<span class="color-grey fr" style="font-size:12px;">温馨提示:选中“锁定”,则对学员隐藏该条测试集,但在“提交评测”时也将被自动检测。</span>
<% end %>
<a href="javascript:void(0)" title="增加" class="sample_icon_add"><i class="fa fa-plus-circle color-grey font-16 ml10 fl mt7"></i></a>
<% if index > 0 %>
<a href="javascript:void(0)" title="删除" class="sample_icon_remove"><i class="fa fa-times-circle color-grey font-16 ml10 fl mt7"></i></a>
<% end %>
</p>
<input type="text" class="panel-form-width-100 panel-box-sizing mb10" name="sample[input][]" id="textarea_sample_input_test" placeholder="样例输入" value="<%= sample.input %>" />
<input type="text" class="panel-form-width-100 panel-box-sizing" name="sample[output][]" id="textarea_sample_output_test" placeholder="样例输出" value="<%= sample.output %>" />
<textarea class="panel-form-width-40 panel-box-sizing" id="textarea_sample_input_test" name="test_set[input][]" placeholder="输入"><%= test_set.input %></textarea>
<textarea class="panel-form-width-40 panel-box-sizing" id="textarea_sample_output_test" name="test_set[output][]" placeholder="希望输出"><%= test_set.output %></textarea>
</li>
<% end %>
</ul>
<% else %>
<label class="panel-form-label fl">样例设置:</label>
<label class="panel-form-label fl">测试集设置:</label>
<ul class="fl task-bg-grey panel-box-sizing">
<li class="clearfix">
<p class="clearfix">
<span class="color-green fb fl" name="sample_inputs_label">样例1</span>
<span class="color-grey fr" style="font-size:12px;">温馨提示:输入样例供学员参考。</span>
<span class="color-green fb fl" name="sample_inputs_label">组1</span>
<span class="color-grey fr" style="font-size:12px;">温馨提示:选中“锁定”,则对学员隐藏该条测试集,但在“提交评测”时也将被自动检测。</span>
<input type="checkbox" name="test_set[lock][]" class="fl ml5 mr5 mt8" value=""><span class="fl mr5">锁定</span>
<input type="hidden" name="test_set[hidden][]"/>
<a href="javascript:void(0)" title="增加" class="sample_icon_add"><i class="fa fa-plus-circle color-grey font-16 ml10 fl mt7"></i></a>
</p>
<input type="text" class="panel-form-width-100 panel-box-sizing mb10" name="sample[input][]" id="textarea_sample_input_test" placeholder="样例输入" />
<input type="text" class="panel-form-width-100 panel-box-sizing mb10" name="sample[output][]" id="textarea_sample_output_test" placeholder="样例输出" />
<textarea class="panel-form-width-40 panel-box-sizing" id="textarea_sample_input_test" name="test_set[input][]" placeholder="输入" ></textarea>
<textarea class="panel-form-width-40 panel-box-sizing" id="textarea_sample_output_test" name="test_set[output][]" placeholder="希望输出" ></textarea>
</li>
</ul>
<% end %>
</li>
<li class="clearfix">
<% if params[:action] == "edit" && @challenge.test_sets.count > 0 %>
<label class="panel-form-label fl">测试集设置:</label>
<ul class="fl task-bg-grey panel-box-sizing" >
<% @challenge.test_sets.each_with_index do |test, index| %>
<li class="clearfix">
<p class="clearfix">
<span class="color-green fb fl" name="inputs_label">测试集<%= index + 1 %></span>
<% if index == 0%>
<span class="color-grey fr" style="font-size:12px;">温馨提示:在学员"提交评测"时进行自动检测。</span>
<% end %>
<a href="javascript:void(0);" title="增加" class="test_icon_add"><i class="fa fa-plus-circle color-grey font-16 ml10 fl mt7"></i></a>
<% if index > 0 %>
<a href="javascript:void(0);" title="删除" class="test_icon_remove"><i class="fa fa-times-circle color-grey font-16 ml10 fl mt7"></i></a>
<% end %>
</p>
<!--<input type="text" class="panel-form-width-670 panel-form-height-30 mb10" name="program[input][]" id="textarea_input_test" value="<%#= test.input %>"/>-->
<textarea class="panel-form-width-100 panel-box-sizing" id="textarea_output_test" name="program[output][]" placeholder="输入测试集内容" ><%= test.output %></textarea>
</li>
<% end %>
</ul>
<% else %>
<label class="panel-form-label fl">测试集设置:</label>
<ul class="fl task-bg-grey panel-box-sizing">
<li class="clearfix">
<p class="clearfix">
<span class="color-green fb fl" name="inputs_label">测试集1</span>
<span class="color-grey fr" style="font-size:12px;">温馨提示:在学员"提交评测"时进行自动检测。</span>
<a href="javascript:void(0);" title="增加" class="test_icon_add"><i class="fa fa-plus-circle color-grey font-16 ml10 fl mt7"></i></a>
</p>
<!-- <input type="text" class="panel-form-width-670 panel-form-height-30 mb10" name="program[input][]" id="textarea_input_test" placeholder="测试输入" />-->
<textarea class="panel-form-width-100 panel-box-sizing " id="textarea_output_test" name="program[output][]" placeholder="输入用例内容" ></textarea>
</li>
</ul>
<% end %>
</li>
<li class="clearfix">
<label class=" panel-form-label fl"> 知识/技能点:</label>
<div class="fl task-bd-grey">
@ -161,19 +175,28 @@
<label class=" panel-form-label fl"> 参考答案:</label>
<%= f.text_area :answer, :class => "panel-form-width-690 panel-form-height-150 fl task-textarea-pd", :no_label => true, :style => "line-height:1.9;" %>
</li>
<li class="clearfix">
<label class=" panel-form-label fl"><span class="c_red mr5">*</span>分值设定:</label>
<%= f.text_field :score, :class => "panel-form-height-30 fl", :no_label => true, :style => "padding:5px;", :placeholder => "请输入分值(只能是数字)" %>
<span class="fl ml5" style="line-height: 40px;">分</span>
<label class="panel-form-label fl"><span class="c_red mr5">*</span>难易度:</label>
<span class="mt10 fl"><%= radio_button_tag("challenge[difficulty]", 1, checked = (@challenge.difficulty ==1 ? true : false)) %></span><span class="ml5 fl mt8 mr15">简单</span>
<span class="mt10 fl"><%= radio_button_tag("challenge[difficulty]", 2, checked = (@challenge.difficulty ==2 ? true : false)) %></span><span class="ml5 fl mt8 mr15">中等</span>
<span class="mt10 fl"><%= radio_button_tag("challenge[difficulty]", 3, checked = (@challenge.difficulty ==3 ? true : false)) %></span><span class="ml5 fl mt8 mr15">困难 </span>
<!-- <span class="fl color-orange ml30 mt8"><i class="fa fa-exclamation-circle mr5"></i>请选择难易度并设定通过本关后,学员得到的经验值</span>-->
</li>
<li class="clearfix">
<label class="panel-form-label fl"><span class="c_red mr5">*</span>奖励经验值:</label>
<%#= f.text_field :score, :class => "panel-form-height-30 fl", :no_label => true, :style => "padding:5px;", :placeholder => "请输入分值(只能是数字)" %>
<%= select_tag :challenge_score, options_for_select([100, 200]), :name => 'challenge[score]', :class => " fl", :style => "height: 40px; width:170px;" %>
<div class="clear"></div>
<span style="display: none" class="c_red ml90" id="new_shixun_score">分值设定不能为空</span>
</li>
<li class="clearfix">
<% if params[:action] == "edit" %>
<a href="javascript:void(0)" class="task-btn task-btn-green fr " onclick="challenge_update()">保存</a>
<a href="javascript:void(0)" class="task-btn task-btn-green fr" onclick="challenge_update()">保存</a>
<a href="<%= shixun_challenge_path(@challenge, :shixun_id => @shixun) %>" class="task-btn fr mr10">取消</a>
<% else %>
<a href="javascript:void(0)" class="task-btn task-btn-green fr " onclick="challenge_create()">保存</a>
<a href="javascript:void(0)" class="task-btn task-btn-green fr" onclick="challenge_create()">保存</a>
<a href="<%= shixun_challenges_path(@shixun) %>" class="task-btn fr mr10">取消</a>
<% end %>
@ -218,7 +241,7 @@
var outputs = document.getElementsByName("sample[output][]");
var inputs_labels = document.getElementsByName("sample_inputs_label");
for (var j = 0; j < inputs_labels.length; j++) {
$(inputs_labels[j]).html("样例" + (j + 1));
$(inputs_labels[j]).html("" + (j + 1));
}
if (inputs.length == outputs.length) {
for (var i = 0; i < inputs.length; i++) {
@ -232,14 +255,55 @@
$(this).parent().parent('.clearfix').remove();
var inputs_labels = document.getElementsByName("sample_inputs_label");
for (var j = 0; j < inputs_labels.length; j++) {
$(inputs_labels[j]).html("样例" + (j + 1));
$(inputs_labels[j]).html("" + (j + 1));
}
});
// 根据难易程度设置不同的奖励经验值(适用新建与编辑模式)
var list= $('input:radio[name="challenge[difficulty]"]:checked').val(); // 获取select的索引值
if(list == 1){
$("#challenge_score").empty();
for(var i= 100; i <= 200; i += 100){
$("#challenge_score").append("<option value="+ i + ">"+ i +"</option>");
}
}else if(list == 2){
$("#challenge_score").empty();
for(var i= 300; i <= 600; i += 100){
$("#challenge_score").append("<option value="+ i + ">"+ i +"</option>");
}
}else if(list == 3){
$("#challenge_score").empty();
for(var i= 700; i <= 1000; i += 100){
$("#challenge_score").append("<option value="+ i + ">"+ i +"</option>");
}
}
$("#challenge_score").find("option[value='<%= @challenge.score %>']").attr("selected",true); // 设置option默认值
$("#challenge_difficulty_1").click(function(){
$("#challenge_score").empty();
for(var i= 100; i <= 200; i += 100){
$("#challenge_score").append("<option value="+ i + ">"+ i +"</option>");
}
});
$("#challenge_difficulty_2").click(function(){
$("#challenge_score").empty();
for(var i= 300; i <= 600; i += 100){
$("#challenge_score").append("<option value="+ i + ">"+ i +"</option>");
}
});
$("#challenge_difficulty_3").click(function(){
$("#challenge_score").empty();
for(var i= 700; i <= 1000; i += 100){
$("#challenge_score").append("<option value="+ i + ">"+ i +"</option>");
}
})
});
// 设置textarea的宽度到140以后出现滚动条
var text1 = document.getElementById("textarea_sample_input_test");
var text2 = document.getElementById("textarea_sample_output_test");
// autoTextarea(text1, 0, 140);
// autoTextarea(text2, 0, 140);
autoTextarea(text1, 0, 140);
autoTextarea(text2, 0, 140);
var text3 = document.getElementById("textarea_input_test");
var text4 = document.getElementById("textarea_output_test");
// autoTextarea2(text3, text4, 0, 140);
@ -256,10 +320,25 @@
$("#challenge_subject").keydown(function(){
$("#new_shixun_pass").hide();
});
// 判断测试集是否锁定, 0未锁定1锁定
function test_set_whether_lock(){
var lock = document.getElementsByName("test_set[lock][]");
var hid = document.getElementsByName("test_set[hidden][]");
for(var o = 0; o < lock.length; o++){
if($(lock[o]).prop("checked")){
$(hid[o]).val(1);
}else{
$(hid[o]).val(0);
}
}
}
function challenge_create(){
test_set_whether_lock();
if($('#challenge_subject').val().trim() == ""){
$("#new_shixun_name").show();
}else if($("#challenge_task_pass").val().trim() == ""){
}else if($("#challenge_task_pass textarea").val().trim() == ""){
$("#new_shixun_pass").show();
}else if($("#challenge_score").val().trim()==""){
$("#new_shixun_score").show();
@ -269,9 +348,10 @@
}
function challenge_update(){
test_set_whether_lock();
if($('#challenge_subject').val().trim() == ""){
$("#new_shixun_name").show();
}else if($("#challenge_task_pass").val().trim() == ""){
}else if($("#challenge_task_pass textarea").val().trim() == ""){
$("#new_shixun_pass").show();
}else if($("#challenge_score").val().trim()==""){
$("#new_shixun_score").show();
@ -294,7 +374,7 @@
$('#challenge_answer').val(cMirror.getValue());
});
challenge_task_pass_editor = KindEditor.create('#challenge_task_pass',
/* challenge_task_pass_editor = KindEditor.create('#challenge_task_pass',
{"width":"89.7%",
"resizeType":0,
"no_label":true,
@ -317,7 +397,7 @@
"fileManagerJson":"/kindeditor/filemanager"
});
*/
function add_tag(){
var num = $(".task-bd-grey").children('div').length;
var val = $(".task-tag-input").val().trim();
@ -336,5 +416,4 @@
var obj = thisObj.parentNode.id;
$("#"+obj).remove();
}
</script>

View File

@ -18,6 +18,14 @@
flowChart : true, // 默认不解析
sequenceDiagram : true // 默认不解析
});
var taskPassMD = editormd.markdownToHTML("challenge_task_pass", {
htmlDecode : "style,script,iframe", // you can filter tags decode
emoji : true,
taskList : true,
tex : true, // 默认不解析
flowChart : true, // 默认不解析
sequenceDiagram : true // 默认不解析
});
})
</script>
@ -48,7 +56,7 @@
</li>
<li class="clearfix challenge_bottom">
<label class="panel-form-label fl">预备知识:</label>
<div class="fl task-bg-grey panel-box-sizing panel-form-width-690" id="wordsView">
<div class="fl task-bg-grey panel-box-sizing panel-form-width-690" id="wordsView" style="width: 90%">
<!--id 命名规则challenge_ready_view-->
<textarea style="display:none;"><%= @challenge.ready_knowledge %></textarea>
<%#= @challenge.ready_knowledge.blank? ? "无" : (h @challenge.ready_knowledge.html_safe) %>
@ -56,27 +64,15 @@
</li>
<li class="clearfix">
<label class="panel-form-label fl"><span class="c_red mr5">*</span>过关任务:</label>
<div class="fl task-bg-grey panel-box-sizing panel-form-width-690" id="challenge_task_pass">
<%= h @challenge.task_pass.html_safe %>
<div class="fl task-bg-grey panel-box-sizing panel-form-width-690" id="challenge_task_pass" style="width: 90%">
<textarea style="display:none;"><%= @challenge.task_pass %></textarea>
</div>
</li>
<li class="clearfix">
<label class="panel-form-label fl">样例设置:</label>
<ul class="fl task-bg-grey panel-box-sizing panel-form-width-690">
<% if @challenge_samples.count > 0 %>
<% @challenge_samples.each_with_index do |sample, index| %>
<li class="clearfix">
<p class="clearfix">
<span class="color-green fb">样例<%= index + 1 %></span>
</p>
<div class="clearfix"><span class="fl fb">样例输入:</span><p class="fl"><%= sample.input %></p></div>
<div class="clearfix"><span class="fl fb">样例输出:</span><p class="fl"><%= sample.output %></p></div>
</li>
<% end %>
<% else %>
<% end %>
</ul>
<label class="panel-form-label fl"><span class="c_red mr5">*</span>评测方式:</label>
<div class="fl task-bg-grey panel-box-sizing panel-form-width-690">
输入输出
</div>
</li>
<li class="clearfix">
<label class="panel-form-label fl">测试集设置:</label>
@ -86,8 +82,14 @@
<li class="clearfix">
<p class="clearfix">
<span class="color-green fb">测试集<%= index + 1 %></span>
<% if test.is_public? %>
<i class="fa fa-unlock-alt font-grey ml5" ></i>
<% else %>
<i class="fa fa-lock font-grey ml5" ></i>
<% end %>
</p>
<div class="clearfix"><p class="fl"><%= test.output %></p></div>
<div class="clearfix"><span class="fl fb">输入:</span><p class="fl"><%= test.input %></p></div>
<div class="clearfix"><span class="fl fb">输出:</span><p class="fl"><%= test.output %></p></div>
</li>
<% end %>
<% else %>
@ -113,7 +115,13 @@
<div id="hidden_tpm_answer_show" class="undis"><%= @challenge.answer.blank? ? "无" : (@challenge.answer) %></div>
</li>
<li class="clearfix">
<label class="panel-form-label fl"><span class="c_red mr5">*</span>分值设定:</label>
<label class="panel-form-label fl">难易度:</label>
<div class="fl task-bg-grey panel-box-sizing panel-form-width-690">
简单
</div>
</li>
<li class="clearfix">
<label class="panel-form-label fl"><span class="c_red mr5">*</span>奖励经验值:</label>
<div class="fl task-bg-grey panel-box-sizing panel-form-width-690">
<%= @challenge.score %>分
</div>
@ -123,7 +131,6 @@
</div>
</div>
<script>
$('#challenge_task_pass').find('img').css("max-width", "680px");
var tpmMirror = $("#tpm_answer_show")[0];
var TpmCodeMirror = CodeMirror(tpmMirror, {
value: $("#hidden_tpm_answer_show").text(),

View File

@ -1,27 +1,124 @@
<div class="col-width fl content-submit ml15 mt15">
<div class="panel-header ">
<h3 >操作</h3>
</div>
<div class="content-submitbox">
<a href="#" class="task-btn mb10">保存修改</a>
<% if @game.status == 0 %>
<%= link_to "提交评测", { :controller => 'games',
:action => "game_build",
:id => @game,
:myshixun_id => @myshixun},
:class => "task-btn task-btn-green",
:onclick => "training_task_submmit();",
:remote => true %>
<% elsif @game.status == 2 %>
<a class="task-btn mb10">评测中..</a>
<% elsif @game.status == 1 %>
<%= link_to "下 一 步 ", {:controller => 'games',
:action => "next_step",
:id => @game,
:myshixun_id => @myshixun},
:class => "task-btn task-btn-green",
:onclick => "training_task_submmit();",
:remote => true %>
<% end %>
</div>
</div>
<span class="mt10 -flex c_grey ml15">耗时:<%= game_spend_time(@game.open_time, @game.updated_at) %></span>
<div id="code_estimate">
<% if @game.status == 0 %>
<a href="javascript:void(0)" class="task-btn task-btn-blue mr15 mt8" id="code_test" onclick="training_task_submmit();">评测</a>
<% elsif @game.status == 1 %>
<a class="task-btn mb10 mt6" id="code_testing">评测中..</a>
<% elsif @game.status == 2 %>
<%= link_to "下 一 步 ", {:controller => 'games',:action => "next_step", :id => @game,
:myshixun_id => @myshixun},
:class => "task-btn task-btn-blue mr15 mt8",
:onclick => "training_task_submmit();",
:remote => true %>
<% end %>
</div>
<script>
function training_task_submmit(){
var i = 0;
$("#ajax-indicator").show();
$("#code_estimate").replaceWith("<a class='task-btn mb10 mt6 mr15' id='code_testing'>评测中..</a>");
$.ajax({
url: '<%= game_build_myshixun_game_path(@game, :myshixun_id => @myshixun) %>',
success: function (){
//移除载入
var temp = $("#ajax-indicator").children('span')[0];
$("#ajax-indicator").children('span')[0].remove();
$("#ajax-indicator").css('border', 0);
$("#ajax-indicator").css('padding', 0);
var intId = setInterval(function(){
var ajaxTimeoutTest = $.ajax({
url: '<%= game_status_myshixun_game_path(@game, :myshixun_id => @myshixun) %>',
timeout : 50000,
data:'test',
success:function(data){
//如果查到了,就退出
// i变量用来统计setInterval执行了多少次便于统计访问时间
i = i + 1;
if(data.status == 2 || data.status == 0){
clearInterval(intId);
// 显示载入
$("#ajax-indicator").html(temp);
$("#ajax-indicator").css('border', "1px solid #bbb");
$("#ajax-indicator").css('padding', "0.6em");
// test_sets 公开测hide_test_sets 隐藏测试集
var html = bt('t:exec_results_1',{
test_sets: [
{
is_public: 1,
result: 1,
input: "input7",
actual_output: "actual_output7",
output: "output7"
},
{
is_public: 1,
result: 1,
input: "input8",
actual_output: "actual_output8",
output: "output8"
},
{
is_public: 1,
result: 1,
input: "input9",
actual_output: "actual_output9",
output: "output9"
}
],
hide_test_sets: [
{
is_public: 0,
result: 0,
input: "input10",
actual_output: "actual_output10",
output: "output10"
},
{
is_public: 0,
result: 0,
input: "input11",
actual_output: "actual_output11",
output: "output11"
},
{
is_public: 0,
result: 0,
input: "input12",
actual_output: "actual_output12",
output: "output12"
}
]
});
$("#game_test_set_results").html(html);
if(data.status == 2){
if( data.had_done == 0 ) {
var htmlvalue = "<%= j (render :partial => 'games/pass_game_show', :locals => { :game=> @game, :myshixun => @myshixun, :had_done => 0}) %>";
}else{
var htmlvalue = "<%= j (render :partial => 'games/pass_game_show', :locals => { :game=> @game, :myshixun => @myshixun, :had_done => 1}) %>";
}
// 传递具体屏幕的宽高,实现不同浏览器与不同分辨率都能撑满屏幕
pop_box_new2(htmlvalue, window.innerWidth, window.innerHeight);
}
}
// 访问30次即60s后后台还未改变状态即执行
if(i == 30){
$.ajax({
url: '<%= change_status_myshixun_game_path(@game, :myshixun_id => @myshixun) %>',
success:function(data){
clearInterval(intId);
var html = "<a href='javascript:void(0)' class='task-btn task-btn-blue mr15 mt8' id='code_test' onclick='training_task_submmit();'>评测</a>";
$("#code_testing").replaceWith(html);
}
});
}
},
complete: function(XMLHttpRequest,status){if(status=='timeout'){
ajaxTimeoutTest.abort();
clearInterval(intId);
}}
});
}, 2000);
}
});
}
</script>

View File

@ -4,23 +4,26 @@
<form action="/myshixuns/<%= @myshixun.id %>/games/<%= @game.id %>/file_update?path=<%= @path %>" method="post" id="file_update_id" data-remote="true">
<li class="clearfix" xmlns="http://www.w3.org/1999/html" id="edit_code_contents">
<textarea class = "" id="challenge-answer-edit-small" name="content" style="height: 98%;width: 98%;"><%= Redmine::CodesetUtil.replace_invalid_utf8(@content) %></textarea>
<script>
var text = document.getElementById("challenge-answer-edit-small");
</script>
<textarea class = "" id="challenge-answer-edit-small" name="content"><%= Redmine::CodesetUtil.replace_invalid_utf8(@content) %></textarea>
</li>
</form>
<script type="text/javascript">
// 自动完成
/* CodeMirror.commands.autocomplete = function(cm) {
cm.showHint({hint: CodeMirror.hint.anyword});
};*/
var editor_CodeMirror = CodeMirror.fromTextArea(document.getElementById("challenge-answer-edit-small"), {
mode: {name: 'text/x-ruby',
mode: {name: "text/x-java",
// version: 2,
singleLineStringErrors: false},
lineNumbers: true,
theme: "ambiance",
// extraKeys: {"Ctrl-Q": "autocomplete"}, // 快捷键
indentUnit: 4, //代码缩进为一个tab的距离
matchBrackets: true,
autoRefresh: true,
smartIndent: true //智能换行
smartIndent: true//智能换行
});
editor_CodeMirror.on('change',function(cMirror){
// get value right from instance
@ -35,7 +38,7 @@
// $("#challenge-answer-edit-small" ).nextAll(".CodeMirror").css("line-height", 1.2);
$(document).ready(function(){
// 设置codemirror的高度/宽度自适应
editor_CodeMirror.setSize("auto",residue_h+50+"px");
editor_CodeMirror.setSize("auto","auto");
editor_CodeMirror.refresh();
});
</script>

View File

@ -0,0 +1,5 @@
<% if @test_set_error_position.nil? %>
<p class="color-light-green mb10"><i class="fa fa-check-circle font-16 " ></i><span class="ml5 mr5"><%= @test_sets_count %>/<%= @test_sets_count %></span> 全部通过</p>
<% else %>
<p class="color-orange mb10"><i class="fa fa-exclamation-circle font-16" ></i><span class="ml5 mr5"><%= @test_set_error_position %>/<%= @test_sets_count %></span> 编译错误</p>
<% end %>

View File

@ -0,0 +1,301 @@
<%= content_for(:header_tags) do %>
<%= javascript_include_tag 'baiduTemplate', 'jquery.datetimepicker.js' %>
<% end %>
<ul id="blacktab_nav">
<li id="blacktab_nav_1" class="blacktab_hover " onclick="HoverLi(1);">
<a href="javascript:void(0);" class="tab_type">测试结果</a>
</li>
<li id="blacktab_nav_2" onclick="HoverLi(2);">
<a href="javascript:void(0);" class="tab_type" >评测信息</a>
</li>
<a href="javascript:void(0);" onclick="valuation_extend_and_zoom();" id="valuation_extend_and_zoom"><i class="fa fa-expand font-16 color-grey fr mt10 mr15" ></i></a>
<div class="cl"></div>
</ul>
<div class="cl"></div>
<div id="game_test_set_results" class="-flex -relative blacktab-inner"></div>
<script id="t:exec_results_1" type="text/html">
<div id="blacktab_con_1" class="" >
<div class="fit -scroll">
<div class="-layout-v -fit">
<div class="-flex -scroll task-padding16">
<! for(var i = 0; i < test_sets.length; i++ ) { !>
<div class="-task-ces-box mb10 clearfix">
<div class="-task-ces-top" onclick="toggle_test_case('<!= test_sets[i].is_public !>', '<!= i !>')" style="cursor:pointer">
<i class="fa fa-caret-down" ></i>
<span>测试集 <!= i + 1 !></span>
<! if( test_sets[i].result == 0 ) { !>
<i class="fa fa-exclamation-circle color-orange fr mt8 ml5" ></i>
<! } !>
<! if(test_sets[i].is_public == 0) { !>
<i class="fa fa-lock fr mt8" ></i>
<! } !>
</div>
<! if(test_sets[i].is_public == 1) { !>
<div class="-task-ces-info <!= i == 0? '' : 'undis' !>" id="test_case_<!= i !>">
<ul>
<li><span class="-task-ces-info-left color-blue">测试输入:</span><span><!= test_sets[i].input !></span></li>
<li><span class="-task-ces-info-left color-blue">预期输出:</span><span><!= test_sets[i].output !></span></li>
<li><span class="-task-ces-info-left color-blue">实际输出:</span><span><!= test_sets[i].actual_output !></span></li>
</ul>
</div>
<! } !>
</div>
<! } !>
<! for(var i = 0; i < hide_test_sets.length; i++ ) { !>
<div class="-task-ces-box mb10 clearfix">
<div class="-task-ces-top">
<i class="fa fa-caret-down" ></i>
<span>测试集 <!= i + test_sets.length !></span>
<! if( hide_test_sets[i].result == 0 ) { !>
<i class="fa fa-exclamation-circle color-orange fr mt8 ml5" ></i>
<! } !>
<! if(hide_test_sets[i].is_public == 0) { !>
<i class="fa fa-lock fr mt8" ></i>
<! } !>
</div>
</div>
<! } !>
</div>
</div>
</div>
</div>
<div id="blacktab_con_2" class="undis " >
<div class="fit -scroll">
<div class="-layout-v -fit">
<div class="-flex -scroll task-padding16 ">
<p class="color-light-green mb10"><i class="fa fa-check-circle font-16 " ></i><span class="ml5 mr5">11/11</span> 全部通过</p>
<div class="-task-ces-box mb10 clearfix">
<div class="-task-ces-info">
<ul>
<li><span class="-task-ces-info-left ">样例测试:</span><span class="color-light-green">9/9</span></li>
<li><span class="-task-ces-info-left">隐藏测试:</span><span class="color-light-green">9/9</span></li>
<li><span class="-task-ces-info-left">经验值:</span><span class="color-light-green">+ 300</span></li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</script>
<script>
var bt = baidu.template;
bt.LEFT_DELIMITER = '<!';
bt.RIGHT_DELIMITER = '!>';
$(function(){
var html = bt('t:exec_results_1',
{
test_sets: [
{
is_public: 1,
result: 1,
input: "input1",
actual_output: "actual_output1",
output: "output1"
},
{
is_public: 1,
result: 1,
input: "input2",
actual_output: "actual_output2",
output: "output2"
},
{
is_public: 1,
result: 1,
input: "input3",
actual_output: "actual_output3",
output: "output3"
}
],
hide_test_sets: [
{
is_public: 0,
result: 0,
input: "input4",
actual_output: "actual_output4",
output: "output4"
},
{
is_public: 0,
result: 0,
input: "input5",
actual_output: "actual_output5",
output: "output5"
},
{
is_public: 0,
result: 0,
input: "input6",
actual_output: "actual_output6",
output: "output6"
}
]
}
);
$("#game_test_set_results").html(html);
});
// 公开的测试集允许展开与隐藏
function toggle_test_case(t_case, id){
if(t_case){
$("#test_case_"+id).toggle();
}
}
</script>
<% if false %>
<div id="code_results" class="panel-footer CELL COLS mt15 ml15">
</div>
<script id="t:exec_results" type="text/html">
<div class="col-width content-history CELL mb15" >
<div class="panel-header clearfix">
<h3 class="fl">测评历史</h3>
<div class="fr mt5">
<a href="javascript:void(0)" onclick="extend_window()"><i class="fa fa-expand font-14 fl color-grey"></i></a>
</div>
</div>
<div class="content-history-inner">
<!--<! if (results.length != 0) { !>-->
<! for(var i=0;i< results.length; i++) { !>
<! if(results[i][0] == 0){ !>
<div class="clearfix history-success mb10">
<span class="icon-success fl mr5"><!= results.length - i !></span>
<p class="fl">恭喜,您已经完成了本任务!</p>
<a href="/myshixuns/<%= @myshixun.id %>/games/<%= @game.id %>/outputs_show?game_output_id=<!= results[i][1] !>" class="fr mr10" data-remote="true">详情</a>
</div>
<! }else{ !>
<div class="clearfix history-fail mb10">
<span class="icon-fail fl mr5"><!= results.length - i !></span>
<p class="fl">错误结果!</p>
<a href="/myshixuns/<%= @myshixun.id %>/games/<%= @game.id %>/outputs_show?game_output_id=<!= results[i][1] !>" class="fr mr10" data-remote="true">详情</a>
<div class="cl"></div>
</div>
<div class="undis" style="background: #f5f9fc;padding: 10px;margin-top:-10px;margin-bottom: 10px" id="game_exception_results_<!= results[i][1] !>"></div>
<! } !>
<! } !>
<!--<! } !>-->
</div>
</div>
<div class="col-width content-output ml15 CELL mb15">
<div class="panel-header clearfix">
<h3>测试输出</h3>
</div>
<div class="content-history-inner" id="game_test_output"></div>
<div class="undis" id="game_test_output1">
<%= h @latest_output %>
</div>
</div>
<div class="col-width content-submit ml15 CELL mb15">
<div class="panel-header">
<h3 >操作</h3>
</div>
<div class="content-submitbox">
<div id="game_rep_modify">
</div>
<! if(status == 0){ !>
<div id="game_commit">
<a href="javascript:void(0)" class="task-btn task-btn-green" onclick="training_task_submmit();">提交评测</a>
</div>
<! }else if(status == 1){ !>
<a class="task-btn mb10">评测中..</a>
<! }else if(status == 2 && had_done != 1){ !>
<%= link_to "下 一 关", {:controller => 'games', :action => "next_step", :id => @game, :myshixun_id => @myshixun}, :class => "task-btn task-btn-green", :remote => true %>
<! } !>
<! if(had_done == 1) { !>
<a href="javascript:void(0)" class="task-btn mb10" >已通关</a>
<! } !>
</div>
</div>
</script>
<script >
var bt = baidu.template;
bt.LEFT_DELIMITER = '<!';
bt.RIGHT_DELIMITER = '!>';
$(function(){
var html = bt('t:exec_results',{status: <%= @game.status %>, results: <%= @results %>, had_done: "<%= @had_done %>"});
$("#code_results").html(html);
$("#game_test_output").html($("#game_test_output1").text())
});
function training_task_submmit(){
var i = 0;
$("#ajax-indicator").show();
$("#game_commit").html("<a class='task-btn mb10'>评测中..</a>");
$.ajax({
url: '<%= game_build_myshixun_game_path(@game, :myshixun_id => @myshixun) %>',
success: function (){
//移除载入
var temp = $("#ajax-indicator").children('span')[0];
$("#ajax-indicator").children('span')[0].remove();
$("#ajax-indicator").css('border', 0);
$("#ajax-indicator").css('padding', 0);
var intId = setInterval(function(){
var ajaxTimeoutTest = $.ajax({
url: '<%= game_status_myshixun_game_path(@game, :myshixun_id => @myshixun) %>',
timeout : 50000,
data:'test',
success:function(data){
//如果查到了,就退出
// i变量用来统计setInterval执行了多少次便于统计访问时间
i = i + 1;
if(data.status == 2 || data.status == 0){
clearInterval(intId);
// 显示载入
$("#ajax-indicator").html(temp);
$("#ajax-indicator").css('border', "1px solid #bbb");
$("#ajax-indicator").css('padding', "0.6em");
var html = bt('t:exec_results',{status: data.status, results: data.results, had_done: data.had_done});
$("#code_results").html(html);
console.log(data.output)
$("#game_test_output").html(data.output)
var html = bt('t:extend_exec_results',{status: data.status, output: data.output, results: data.results, had_done: data.had_done});
$("#extend_code_results").html(html);
if(data.status == 2){
if( data.had_done == 0 ) {
var htmlvalue = "<%= j (render :partial => 'games/pass_game_show', :locals => { :game=> @game, :myshixun => @myshixun, :had_done => 0}) %>";
}else{
var htmlvalue = "<%= j (render :partial => 'games/pass_game_show', :locals => { :game=> @game, :myshixun => @myshixun, :had_done => 1}) %>";
}
// 传递具体屏幕的宽高,实现不同浏览器与不同分辨率都能撑满屏幕
pop_box_new2(htmlvalue, window.innerWidth, window.innerHeight);
}
}
// 访问30次即60s后后台还未改变状态即执行
if(i == 30){
$.ajax({
url: '<%= change_status_myshixun_game_path(@game, :myshixun_id => @myshixun) %>',
success:function(data){
clearInterval(intId);
var html = bt('t:exec_results',{status: data.status, output: data.output, results: data.results, had_done: data.had_done});
$("#code_results").html(html);
}
});
}
},
complete: function(XMLHttpRequest,status){if(status=='timeout'){
ajaxTimeoutTest.abort();
clearInterval(intId);
}}
});
}, 2000);
}
});
}
function extend_window(){
$("#extend_code_results").show();
}
function file_edit_submit1(){
$('#file_update_id').submit();
$("#bottom_save_edit").hide();
}
</script>
<% end %>

View File

@ -1,47 +1,89 @@
<div class="content clearfix CELL _FLEX COLS ml15 mr15 mt15">
<div class="content-info col-width CELL" id="tpi_contents_info">
<div class="panel-header clearfix" id="contents_top">
<h3 class="fl">第<%= @game_challenge.position %>关:<%= @game_challenge.subject %></h3>
<div class="fr mt5">
<a href="javascript:void(0);" onclick="extend_game_description();"><i class="fa fa-expand font-14 fl color-grey"></i></a>
</div>
</div>
<div class="tab_content clearfix">
<ul id="tab_nav">
<li id="tab_nav_1" class="tab_hover" onclick="HoverLi(1);">
<a href="javascript:void(0);" class="tab_type">过关任务</a>
</li>
<li id="tab_nav_2" onclick="HoverLi(2);">
<a href="javascript:void(0);" class="tab_type" >预备知识</a>
</li>
<li id="tab_nav_3" onclick="HoverLi(3);">
<a href="javascript:void(0);" class="tab_type" >参考答案</a>
</li>
</ul>
<div class="cl"></div>
<style>
.editormd-preview-container, .editormd-html-preview{
text-align: left;
font-size: 14px;
line-height: 1.8;
}
</style>
<div class="split-panel--first -layout -vertical -flex -relative -flex-basic50" id="game_left_contents" >
<div class="-fit -layout-v">
<div class="-layout-v -flex -bg-white -task-ml80">
<ul id="tab_nav" class="-tab-nav">
<li id="tab_nav_1" class="tab_hover" onclick="HoverLi1(1);">
<a href="javascript:void(0);" class="tab_type">过关任务</a>
</li>
<li id="tab_nav_2" onclick="HoverLi1(2);">
<a href="javascript:void(0);" class="tab_type" >预备知识</a>
</li>
<li id="tab_nav_3" onclick="HoverLi1(3);">
<a href="javascript:void(0);" class="tab_type" >参考答案</a>
</li>
<span class="btn-cir-big fr mt8 mr15" >经验值:<%= @game_challenge.score %></span>
</ul>
<div class="cl"></div>
<div class="-flex -relative greytab-inner" >
<div id="tab_con_1" class="tab-info" >
<div class="tab-info-inner" id="task_pass_contents">
<%= h @game_challenge.task_pass.try(:html_safe) %>
<div class="fit -scroll">
<div class="-layout-v -fit">
<div class="-flex -scroll task-padding16 panel-box-sizing" id="game_task_pass">
<textarea style="display:none;"><%= @game_challenge.task_pass %></textarea>
</div>
</div>
</div>
</div>
<div id="tab_con_2" class="undis tab-info">
<div class="tab-info-inner" id="task_pass_contents">
<%= h @game_challenge.ready_knowledge.try(:html_safe) %>
<div id="tab_con_2" class="undis tab-info" >
<div class="fit -scroll">
<div class="-layout-v -fit">
<div class="-flex -scroll task-padding16 panel-box-sizing" id="game_ready_knowledge">
<textarea style="display:none;"><%= @game_challenge.ready_knowledge %></textarea>
</div>
</div>
</div>
</div>
<div id="tab_con_3" class="undis tab-info">
<div class="tab-info-inner" style="line-height: 1.9" id="game_answer_show"></div>
<div class="undis" id="hidden_game_answer"><%= @game_challenge.answer %></div>
<div id="tab_con_3" class="undis tab-info" >
<div class="fit -scroll">
<div class="-layout-v -fit">
<div class="-flex -scroll task-padding16" id="game_answer_show"></div>
<div class="undis" id="hidden_game_answer"><%= @game_challenge.answer %></div>
</div>
</div>
</div>
</div>
</div>
<%= render :partial => 'games/repository' %>
<div class="-layout-h -center -padding-h-16 -horizontal -footer-left" >
<ul class="-layout -task-ml80">
<li title="<%= @praise.nil? ? "点赞" : "取消点赞" %>" onclick="game_praise()" id="game_praise_tread"><i class="fa fa-thumbs-up mr15 font-20 <%= @praise.nil? ? "" : "color-light-green" %>" alt="赞" ></i></li>
<li title="<%= @tread.nil? ? "踩" : "取消踩" %>" onclick="game_tread()" id="game_tread"><i class="fa fa-thumbs-down mr15 font-20 <%= @tread.nil? ? "" : "color-orange" %>" ></i></li>
<li title="纠错" onclick="error_correction_pop();"><i class="fa fa-flag mr30 font-20" ></i></li>
</ul>
<div class="cl"></div>
</div>
</div>
</div>
<div class="split-panel--second -layout -vertical -flex -relative -flex-basic50" id="game_right_contents">
<div class="-layout-v -flex">
<div class="-flex -relative">
<div class="split-panel -fit -vertical">
<div class="-layout -stretch -fit -vertical">
<div class="split-panel--first -layout -vertical -flex -relative -bg-black -flex-basic60" id="games_repository_contents">
<%= render :partial => 'games/repository' %>
</div>
<div class="split-panel--second -layout -vertical -flex -relative -bg-black -flex-basic40" id="games_valuation_contents">
<%= render :partial => 'games/game_results' %>
</div>
</div>
</div>
</div>
<!--按钮的操作-->
<div class="-layout-h -center -bg-grey-90 -grey-20 -bg-darkblack " style="height:48px; ">
<div class="-flex -layout-h" id="game_operate_action">
<%= render :partial => "games/code_actions" %>
</div>
</div>
</div>
</div>
<%= render :partial => 'games/exec_results' %>
<%#= render :partial => 'games/desc_full_show' %>
<script type="text/javascript" language="javascript">
<script>
var myMirror = $("#game_answer_show")[0];
var myCodeMirror = CodeMirror(myMirror, {
value: $("#hidden_game_answer").text(),
@ -51,18 +93,70 @@
autofocus: true,
border: 0
});
$(document).ready(function(){
$(document).ready(function() {
var wordsView;
task_md = editormd.markdownToHTML("game_task_pass", {
htmlDecode : "style,script,iframe", // you can filter tags decode
emoji : true,
taskList : true,
tex : true, // 默认不解析
flowChart : true, // 默认不解析
sequenceDiagram : true // 默认不解析
});
var wordsView;
knowledge_md = editormd.markdownToHTML("game_ready_knowledge", {
htmlDecode : "style,script,iframe", // you can filter tags decode
emoji : true,
taskList : true,
tex : true, // 默认不解析
flowChart : true, // 默认不解析
sequenceDiagram : true // 默认不解析
});
myCodeMirror.setSize('auto','auto');//设置版本库codemirror的高度
$("#game_answer_show" ).children(".CodeMirror").css("line-height", 1.2);
$("#game_answer_show").children().css("border", 0);
// 设置图片最大宽度不能超出边框
$('#task_pass_contents').find('img').css("max-width", "100%");
$("#task_pass_contents").css("height", residue_h + "px");
$(".tab-info-inner").css("height", residue_h+"px");
});
//课程大纲tab
function extend_game_description(){
$("#game_left_contents").show();
function error_correction_pop(){
var htmlvalue = "<%= escape_javascript(render :partial => 'games/send_error_pop') %>";
pop_box_new(htmlvalue,460,316);
};
// 点赞/取消点赞功能
var h = true;
function game_praise(){
$.ajax({
url: '<%= praise_tread_praise_plus_path({:obj_id => @game_challenge.id, :obj_type => @game_challenge.class}) %>',
data: {horizontal: h, game_praise: true},
success:function(data){
h = !h;
if(data.praise){
$("#game_praise_tread").children("i").addClass("color-light-green");
$("#game_praise_tread").attr("title", "取消点赞")
}else{
$("#game_praise_tread").children("i").removeClass("color-light-green");
$("#game_praise_tread").attr("title", "点赞")
}
}
});
}
// 踩/取消踩功能
var d = true;
function game_tread(){
$.ajax({
url: '<%= praise_tread_praise_plus_path({:obj_id => @game_challenge.id, :obj_type => "ChallengeTread"}) %>',
data: {horizontal: d, game_praise: true},
success:function(data){
d = !d;
if(data.praise){
$("#game_tread").children("i").addClass("color-orange");
$("#game_tread").attr("title", "踩")
}else{
$("#game_tread").children("i").removeClass("color-orange");
$("#game_tread").attr("title", "取消踩")
}
}
});
}
</script>

View File

@ -1,66 +1,29 @@
<div class="content clearfix" >
<div class="col-width-10 ml15 mt15 mr15">
<div class="panel-header clearfix">
<h3 class="fl">全部任务</h3><span class="btn-cir btn-cir-grey ml5 mt3 fl"><%= @games_count %></span>
<div class="page--over">
<div class="col-width-3 -scroll" style="height:100%;" id="all_task_index">
<div class="-task-list-header clearfix">
<h3 class="fl">全部任务</h3>
</div>
<% @games.each do |game| %>
<div class="panel-list">
<div class=" clearfix panel-inner">
<div class=" clearfix -task-list-inner">
<span class="icon-success fl mr5"><%= game.challenge.try(:position) %></span>
<% if game.status == 0 %>
<div class="fl">
<h4 class=" panel-inner-title "><i class="fa fa-dot-circle-o font-18 color-green mr5"></i><span class="color-red">第<%= game.challenge.try(:position) %>关</span>
<%= link_to game.challenge.try(:subject), myshixun_game_path(game, :myshixun_id => @myshixun), :remote => true %></h4>
<p class="ml15 mt15 color-grey">
<span class=" mr10">已闯关:<%= had_pass(@myshixun.shixun_id, game.challenge.try(:position)) %>人</span>
<span class=" mr10">平均耗时:<%= game_spend_time(avg_spend_time(@myshixun.shixun_id, game.challenge.try(:position)), 0) %></span>
</p>
</div>
<a href="<%= game_build_myshixun_game_path(game, :myshixun_id => @myshixun) %>" class="task-btn task-btn-green fr" id="open_game" data-remote="true">马上开启</a>
<div class="cl"></div>
<h4 class=" -task-list-title fl"> <%= link_to game.challenge.try(:subject), myshixun_game_path(game, :myshixun_id => @myshixun), :remote => true %></h4>
<% elsif game.status == 1 %>
<div class="fl">
<h4 class=" panel-inner-title "><i class="fa fa-dot-circle-o font-18 color-green mr5"></i><span class="color-red">第<%= game.challenge.try(:position) %>关</span>
<%= game.challenge.try(:subject) %></h4>
<p class="ml15 mt15 color-grey">
<span class=" mr10">已闯关:<%= had_pass(@myshixun.shixun_id, game.challenge.try(:position)) %>人</span>
<span class=" mr10">平均耗时:<%= game_spend_time(avg_spend_time(@myshixun.shixun_id, game.challenge.try(:position)), 0) %></span>
</p>
</div>
<a href="#" class=" fr link-color-green">正在解决中</a>
<div class="cl"></div>
<h4 class="-task-list-title fl"> <%= game.challenge.try(:subject) %></h4>
<span class="fr color-light-green">正在解决</span>
<% elsif game.status == 2 %>
<h4 class="fl panel-inner-title "><i class="fa fa-check-circle font-18 color-green mr5"></i><span class="color-red">第<%= game.challenge.try(:position) %>关</span>
<%= link_to game.challenge.try(:subject), myshixun_game_path(game, :myshixun_id => @myshixun), :remote => true %></h4>
<!--<p class="fr "><i class="fa fa-trophy color-yellow font-16 mr5 "></i><span class=" color-grey">NO.120</span></p>-->
<div class="cl"></div>
<p class="ml15 mt15 color-grey">
<span class=" mr10">开始时间:<%= format_time(game.created_at) %></span>
<span class=" mr10">完成时间:<%= format_time(game.updated_at) %></span>
<span class=" mr10">耗时:<%= game_spend_time(game.created_at, game.updated_at) %></span>
<span class=" mr10">已闯关:<%= had_pass(@myshixun.shixun_id, game.challenge.try(:position)) %>人</span>
<span class=" mr10">得分:<%= game.final_score %>分</span>
</p>
<h4 class="-task-list-title fl"> <%= link_to game.challenge.try(:subject), myshixun_game_path(game, :myshixun_id => @myshixun), :remote => true %></h4>
<a class="fr"> <i class="fa fa-check fr font-18 mt5" ></i></a>
<% elsif game.status == 3 %>
<div class="fl">
<h4 class=" panel-inner-title "><i class="fa fa-lock font-18 color-grey mr5"></i><span class="color-red">第<%= game.challenge.try(:position) %>关</span>
<%= game.challenge.try(:subject) %></h4>
<p class="ml15 mt15 color-grey">
<span class=" mr10">已闯关:<%= had_pass(@myshixun.shixun_id, game.challenge.try(:position)) %>人</span>
<span class=" mr10">平均耗时:<%= game_spend_time(avg_spend_time(@myshixun.shixun_id, game.challenge.try(:position)), 0) %></span>
</p>
</div>
<a href="#" class=" task-btn fr"> &nbsp;&nbsp;已锁定&nbsp;&nbsp;</a>
<div class="cl"></div>
<h4 class="-task-list-title fl"> <%= game.challenge.try(:subject) %></h4>
<a class="fr"> <i class="fa fa-lock fr font-18 mt5" ></i></a>
<% end %>
<div class="cl"></div>
</div>
</div>
<% end %>
</div>
</div>
<script>
$("#open_game").click(function(){
$("#open_game").removeClass("task-btn-green");
$("#open_game").html("评测中..")
});
</script>

View File

@ -1,16 +1,27 @@
<div class="content-editor col-width2 CELL" id="myshixun_repository">
<%= render :partial => 'myshixun_repository' %>
<div class="panel-header clearfix" id="top_repository" style="border-bottom:0">
<h3 class="fl">
<%= render :partial => 'myshixun_breadcrumbs', :locals => {:path => @path, :kind => 'dir'} %>
</h3>
<div class="fr mt5 -horizontal">
<a href="javascript:void(0);" onclick="repository_extend_and_zoom();" id="extend_and_zoom"><i class="fa fa-arrows-alt font-16"></i></a>
</div>
</div>
<div class="cl"></div>
<script>
var web_h = window.innerHeight;
$(document).ready(function(){
});
function extend_repository(){
$("#extend_code_results").hide();
$("#myshixun_extend_repository").show();
extend_editor.refresh();
}
</script>
<div class="-flex -relative">
<div class="fit -scroll">
<div class="-layout-v -fit">
<div class="-flex -scroll">
<div class="content-editor-inner">
<% if params[:action] == "entry" || @file_open %>
<div id="file_entry_content">
<%= render :partial => 'games/file_edit_form', :locals => {:filename => @path, :content => @content} %>
</div>
<% else %>
<% unless @entries.blank? %>
<%= render :partial => 'tree', :locals => { :extend => false}%>
<% end %>
<% end %>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,25 @@
<div class=" task-popup-title clearfix task-popup-bggrey" style="width: 500px">
<h3 class="fl ">问题报告</h3>
<a href="javascript:void(0)" onclick="hideModal();"><i class="fa fa-times-circle font-18 link-color-grey fr mt5"></i></a>
</div>
<div class="task-popup-content">
<p class=" font-16">这个任务有什么问题或错误?</p>
<p class=" font-12 color-grey">请尽可能清晰的描述</p>
<textarea class="task-form-100 panel-box-sizing mt10" placeholder="限100字以内" id="error_contents"></textarea>
</div>
<div class=" clearfix mb20 mr15">
<a href="javascript:void(0);" onclick="hideModal();" class="task-btn fr">取消</a>
<a href="javascript:void(0);" onclick="problem_report('<%= User.current.id %>');hideModal();" class="task-btn task-btn-blue fr mr10">确定</a>
</div>
<script>
// 给导师和管理员发送error
function problem_report(user){
$.ajax({
url: '<%= leave_email_activation_message_path(1) %>',
data: {user: user, text: $("#error_contents").val(), game_id: '<%= @game.id %>', shixun_name:'<%= @myshixun.name %>', shixun_author_id:'<%= @game_challenge.user_id %>', position:'<%= @game_challenge.position %>'},
type: "POST",
success: function (data) {
}
});
}
</script>

View File

@ -8,7 +8,7 @@
<% ent_path = Redmine::CodesetUtil.replace_invalid_utf8(sub_path) %>
<% ent_name = Redmine::CodesetUtil.replace_invalid_utf8(entry.name) %>
<tr id="<%= tr_id %>" class="<%= h params[:parent_id] %> entry <%= entry.kind %>">
<td class="filename_no_report hidden">
<td class="filename_no_report hidden" style="background: #333;border: solid;">
<%= link_to h(ent_name),
{:action => (entry.is_dir? ? 'show' : 'entry'), :id => @game, :myshixun_id => @myshixun, :path => to_path_param(ent_path), :rev => @rev, :extend => extend },
:remote => true,

View File

@ -1,6 +1,5 @@
$("#myshixun_repository").html('<%= escape_javascript(render :partial => 'games/myshixun_repository') %>');
$("#myshixun_extend_repository").html('<%= escape_javascript(render :partial => 'games/myshixun_extend_repository') %>');
//$("#game_rep_modify").html('<a href="javascript:void(0)" class="task-btn task-btn-green mb10" id="bottom_save_edit" onclick="file_edit_submit1()">保存修改</a>');
$("#games_repository_contents").html('<%= escape_javascript(render :partial => 'games/repository') %>');

View File

@ -1,10 +1,19 @@
$("#content_list").html('<%= escape_javascript(render :partial => 'games/games_list') %>');
$("#current_task_tab").attr('href','<%= myshixun_game_path(@myshixun.current_task, :myshixun_id => @myshixun) %>');
$("#myshixun_top").html('<%= escape_javascript(render :partial => 'myshixuns/myshixun_top') %>');
// 左侧导航样式改动
$("#current_task_tab").removeClass("leftnav-active");
$("#all_task_tab").addClass("leftnav-active");
// 宽版隐藏
$("#myshixun_extend_repository").hide();
$("#game_left_contents").hide();
$("#extend_code_results").hide();
$("#all_task_show").html('<%= escape_javascript(render :partial => 'games/games_list') %>');
$("#all_task_show").css("height", "100%");
if(fadein == 0){
$("#all_task_show").css("background", "rgba(0,0,0,0.3)");
$("#all_task_show").show();
$("#all_task_index").css("left","-420px").stop().animate({
left: 0
}, 400, function(){
fadein = 1;
});
}else{
$("#all_task_show").css("background","rgba(0,0,0,0)");
$("#all_task_index").css("left", 0).stop().animate({
left: "-505px"
}, 400, function(){
$("#all_task_show").hide();
fadein = 0;
});
}

View File

@ -1,51 +1,56 @@
<%= javascript_include_tag 'baiduTemplate', 'jquery.datetimepicker.js' %>
<%= javascript_include_tag "/assets/codemirror/codemirror_python_ruby_c" %>
<%= stylesheet_link_tag "/assets/codemirror/codemirror" %>
<div id="content_list" class="CELL _FLEX ROWS">
<%= render :partial => "game_show" %>
</div>
<div id="content_attr_1">
<%= render :partial => 'desc_full_show'%>
</div>
<div class="col-width content-all-fix undis" style="border: none" id="myshixun_extend_repository">
<%= render :partial => 'myshixun_extend_repository' %>
</div>
<div id="content_attr_2">
<%= render :partial => 'extend_test_output' %>
<%= javascript_include_tag "/codemirror/lib/codemirror", "/codemirror/mode/javascript/javascript", "/codemirror/addon/edit/matchbrackets.js", "/codemirror/mode/clike/clike" %>
<%= stylesheet_link_tag "/codemirror/lib/codemirror", "/codemirror/theme/ambiance" %>
<%= stylesheet_link_tag '/editormd/css/editormd','/editormd/css/editormd.min' %>
<%= javascript_include_tag '/editormd/lib/marked.min.js','/editormd/lib/prettify.min.js','/editormd/lib/raphael.min.js','/editormd/lib/underscore.min.js','/editormd/lib/sequence-diagram.min.js',
'/editormd/lib/flowchart.min.js','/editormd/lib/jquery.flowchart.min.js','/editormd/editormd.js'%>
<div class="page--body -margin-t-64 -flex">
<div class="page--over undis" id="all_task_show">
</div>
<div class="-layout -stretch -fit" id="game_show_content">
<%= render :partial => 'games/game_show' %>
</div>
</div>
<script>
var fadein = 0; //控制全部任务的渐入浅出效果
// 版本库扩大与缩小
var control = 0; // 版本库控制 0表示点击放大 1表示点击缩小
var control_1 = 0; // 测评控制 0表示点击放大 1表示点击缩小
<script type="text/javascript" language="javascript">
// contents_h 是中间左侧内容页的高度,不同浏览器的得到高度不一致
// contents_top_h 是内容页头部的高度
// contents_tab_nav是三个tab的高度
// residue_h contents剩余展示内容的高度
// 左侧内容
var contents_h = $("#tpi_contents_info").height();
var contents_top_h = $("#contents_top").height() + 20;
var contents_tab_nav = $("#tab_nav").height();
var residue_h = contents_h - contents_top_h - contents_tab_nav - 10;
$(document).ready(function(){
$(".tab-info-inner").css("height", residue_h+"px");
$(".content-half-fix").css("height", residue_h + 310 +"px")
});
//课程大纲tab
function g(o){return document.getElementById(o);}
function HoverLi(n){
for(var i=1;i<=3;i++){
g('tab_nav_'+i).className='tab_nomal';
g('tab_con_'+i).className='undis';
}
g('tab_con_'+n).className='dis';
g('tab_nav_'+n).className='tab_hover';
if(n == 3){
// 点击参考答案时让codemirror展示,如果不refresh的话第一次展示会隐藏
myCodeMirror.refresh();
}
}
$("#close_myshixun_tip").click(function(){
$("#myshixun_tip").hide();
});
function close_big_repository(){
$("#myshixun_extend_repository").hide()
}
</script>
function repository_extend_and_zoom(){
if(control == 0){
$("#game_left_contents").addClass("-flex-basic0");
$("#game_right_contents").addClass("-flex-basic100 -task-ml80");
$("#games_repository_contents").addClass("-flex-basic100");
$("#games_valuation_contents").addClass("-flex-basic0");
$("#extend_and_zoom").children("i").addClass("fa-compress");
$("#extend_and_zoom").children("i").removeClass("fa-arrows-alt");
editor_CodeMirror.setSize("auto", "auto");
control = 1;
}else if(control == 1){
$("#game_left_contents").removeClass("-flex-basic0");
$("#game_right_contents").removeClass("-flex-basic100 -task-ml80");
$("#games_repository_contents").removeClass("-flex-basic100");
$("#games_valuation_contents").removeClass("-flex-basic0");
$("#extend_and_zoom").children("i").removeClass("fa-compress");
$("#extend_and_zoom").children("i").addClass("fa-arrows-alt");
editor_CodeMirror.setSize("auto", "auto");
control = 0;
}
}
// 测评的扩大与缩小
function valuation_extend_and_zoom(){
if(control_1 == 0){
$("#games_repository_contents").addClass("-flex-basic0");
$("#games_valuation_contents").addClass("-flex-basic100");
$("#valuation_extend_and_zoom").children("i").removeClass("fa-expand");
$("#valuation_extend_and_zoom").children("i").addClass("fa-compress");
control_1 = 1;
}else if(control_1 == 1){
$("#games_repository_contents").removeClass("-flex-basic0");
$("#games_valuation_contents").removeClass("-flex-basic100");
$("#valuation_extend_and_zoom").children("i").addClass("fa-expand");
$("#valuation_extend_and_zoom").children("i").removeClass("fa-compress");
control_1 = 0;
}
}
</script>

View File

@ -1,6 +1,4 @@
$("#content_list").html('<%= escape_javascript(render :partial => 'games/game_show') %>');
$("#content_attr_1").html('<%= escape_javascript(render :partial => 'games/desc_full_show') %>');
$("#myshixun_extend_repository").html('<%= escape_javascript(render :partial => 'games/myshixun_extend_repository') %>');
$("#content_attr_2").html('<%= escape_javascript(render :partial => 'games/extend_test_output') %>');
$("#myshixun_top").html('<%= escape_javascript(render :partial => 'myshixuns/myshixun_top') %>');
$("#game_show_content").html('<%= escape_javascript(render :partial => 'games/game_show') %>');
$("#current_task_tab").attr('href','<%= myshixun_game_path(@myshixun.current_task, :myshixun_id => @myshixun) %>');
$("#all_task_show").hide();
fadein = 0;

View File

@ -9,7 +9,7 @@
<%= favicon %>
<%= javascript_heads %>
<%= heads_for_theme %>
<%= stylesheet_link_tag 'jquery/jquery-ui-1.9.2','css/common', 'css/font-awesome','css/taskstyle','css/structure','scm','css/public', 'css/project','css/popup','repository','css/gantt', 'css/calendar', 'css/moduel', 'css/cssPlus-v.0.2-build' %>
<%= stylesheet_link_tag 'jquery/jquery-ui-1.9.2','css/common', 'css/font-awesome','css/taskstyle','css/structure','scm','css/public', 'css/project','css/popup','repository','css/gantt', 'css/calendar', 'css/moduel', 'css/cssPlus-v.0.2-build', 'css/bigdata-common','css/bigdata-public', 'css/bigdata-popup' %>
<%= call_hook :view_layouts_base_html_head %>
<!-- page specific tags -->
<%= yield :header_tags -%>
@ -18,39 +18,30 @@
<body onload="prettyPrint();">
<div class="cl"></div>
<div class="task-container EXTENDER">
<div class="COLS">
<div class="leftbar CELL" style="width: 120px;">
<div class="CELL">
<div class="user-info" style="width: 120px;height:120px;padding-top: 10px;">
<%= link_to image_tag(url_to_avatar(@myshixun.owner), :width => "100", :height => "100"), user_path(@myshixun.owner), :alt => "用户头像", :class => "user-info-img", :style => "border-radius: 100px;margin-left: 5px;" %>
<!--<a href="#" class="user-info-img"><img src="images/user/male.jpg" width="100" height="" alt="100"></a>-->
<%#= link_to @myshixun.owner.try(:show_name), user_path(@myshixun.owner), :class => "user-info-name" %>
</div>
<div class="leftnav">
<ul>
<li class="leftnav-box" style="width: 120px">
<a href="<%= myshixun_game_path(@myshixun.current_task, :myshixun_id => @myshixun) %>" class="leftnav-box-inner leftnav-active" style="width: 120px" id="current_task_tab" data-remote="true">
<i class="fa fa-gamepad font-28"></i><br/><span class="font-16">当前任务</span>
</a>
</li>
<li class="leftnav-box" style="width: 120px">
<a href="<%= myshixun_games_path(@myshixun) %>" class="leftnav-box-inner" style="width: 120px" id="all_task_tab" data-remote="true">
<i class="fa fa-qrcode font-28"></i><br/><span class="font-16">全部任务</span><span class="btn-cir ml5"><%= @myshixun.games.count %></span>
</a>
</li>
</ul>
</div>
</div>
</div>
<div class="rightbar CELL _FLEX ROWS">
<div id="myshixun_top">
<%= render :partial => "myshixuns/myshixun_top" %>
</div>
<%= yield %>
</div>
<div class="page -layout-v -fit ">
<div class="page--header clearfix" id="myshixun_top">
<%= render :partial => 'myshixuns/myshixun_top' %>
</div>
<div class="page--leftnav">
<div class="user-info">
<%= link_to image_tag(url_to_avatar(@myshixun.owner), :width => "50", :height => "50"), user_path(@myshixun.owner), :alt => "用户头像", :class => "user-info-img" %>
<%= link_to @myshixun.owner.try(:show_name), user_path(@myshixun.owner), :class => "user-info-name" %>
</div>
<div class="leftnav">
<ul>
<li class="leftnav-box">
<a href="<%= myshixun_game_path(@myshixun.current_task, :myshixun_id => @myshixun) %>" class="leftnav-box-inner leftnav-active" id="current_task_tab" data-remote="true">
<i class="fa fa-gamepad font-20"></i><br/><span class="font-12">当前任务</span>
</a></li>
<li class="leftnav-box">
<a href="<%= myshixun_games_path(@myshixun, :fadein => true) %>" class="leftnav-box-inner" id="all_task_tab" data-remote="true">
<i class="fa fa-qrcode font-20"></i><br/><span class="font-12">全部任务</span><br/><span class="btn-cir ml5"><%= @myshixun.games.count %></span>
</a>
</li>
</ul>
</div>
</div>
<%= yield %>
</div>
<div id="ajax-indicator" style="display:none;">
@ -61,13 +52,43 @@
</body>
<!-- MathJax的配置 -->
<script type="text/javascript" src="/javascripts/MathJax/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
function g(o){return document.getElementById(o);}
function HoverLi1(n){
for(var i=1;i<=3;i++){
g('tab_nav_'+i).className='tab_nomal';
g('tab_con_'+i).className='undis';
}
g('tab_con_'+n).className='dis';
g('tab_nav_'+n).className='tab_hover';
if(n == 3){
// 点击参考答案时让codemirror展示,如果不refresh的话第一次展示会隐藏
myCodeMirror.refresh();
}
}
function HoverLi(n){
for(var i=1;i<=2;i++){
g('blacktab_nav_'+i).className='blacktab_nomal';
g('blacktab_con_'+i).className='undis';
}
g('blacktab_con_'+n).className='dis';
g('blacktab_nav_'+n).className='blacktab_hover';
}
// 点击全部任务左侧tab样式切换
var c = 0; // 设置全部任务展开样式,与不展开时的样式
$("#all_task_tab").click(function(){
$("#all_task_tab").addClass("leftnav-active");
$("#current_task_tab").removeClass("leftnav-active");
if( c == 0){
$("#all_task_tab").addClass('leftnav-active');
c = 1;
}else if(c == 1){
$("#all_task_tab").removeClass('leftnav-active');
c = 0;
}
});
$("#current_task_tab").click(function(){
$("#current_task_tab").addClass("leftnav-active");
$("#all_task_tab").removeClass("leftnav-active");
$("#current_task_tab").addClass('leftnav-active');
$("#all_task_tab").removeClass('leftnav-active');
c = 0;
});
</script>
<%= javascript_include_tag 'cookie','project',"avatars", 'header','prettify','select_list_move','attachments' %>

View File

@ -9,7 +9,7 @@
<%= favicon %>
<%= javascript_heads %>
<%= heads_for_theme %>
<%= stylesheet_link_tag 'jquery/jquery-ui-1.9.2','css/common', 'css/taskstyle', 'css/structure','scm','css/public', 'css/project','css/popup','repository','css/gantt', 'css/calendar', 'css/moduel', 'css/font-awesome' %>
<%= stylesheet_link_tag 'jquery/jquery-ui-1.9.2','css/common', 'css/bigdata-common','css/bigdata-public', 'css/taskstyle', 'css/structure','scm','css/public', 'css/project','css/popup','repository','css/gantt', 'css/calendar', 'css/moduel', 'css/font-awesome' %>
<%= call_hook :view_layouts_base_html_head %>
<!-- page specific tags -->
<%= yield :header_tags -%>
@ -17,9 +17,28 @@
<!--add by huang-->
<body onload="prettyPrint();">
<div class="navContainer"> <%= render :partial => User.current.logged? ? 'layouts/logined_header' : 'layouts/unlogin_header' %></div>
<div id="shixun_top">
<%= render :partial => 'shixuns/shixun_top' %>
</div>
<div class="task-header-nav">
<ul class="task-header-navs clearfix">
<li><a href="<%= shixun_challenges_url(@shixun) %>" class="<%= params[:action] == "index" ? "active" : "" %>" >实训</a></li>
<% unless @shixun.repository.nil? %>
<li>
<%= link_to l(:project_module_repository),
({:controller => 'repositories',
:action => 'shixun_show',
:id => @shixun,
:repository_id => shixun_repository(@shixun).try(:identifier)}),
:class => (params[:controller] == 'repositories' ? 'active' : '') %>
</li>
<% end %>
<% if User.current.manager_of_shixun?(@shixun) %>
<li><a href="<%= settings_shixun_path(@shixun) %>" class="<%= params[:action] == "settings" ? "active" : "" %>">配置</a></li>
<% end %>
</ul>
</div>
<%#= render_flash_messages %>
<div class="task-pm-content">
<%= render_flash_messages %>
@ -33,9 +52,7 @@
<div class="cl"></div>
<%= render :partial => 'layouts/footer' %>
<div class="cl"></div>
<% if hidden_unproject_infos %>
<%= render :partial => 'layouts/new_feedback' %>
<% end %>
<div id="ajax-indicator" style="display:none;">
<span><%= l(:label_loading) %></span>
</div>

View File

@ -1,17 +1,16 @@
<div class="rightbar-header clearfix CELL">
<h2 class="rightbar-h2 fl"><%= @myshixun.name %></h2>
<a href="javascript:void(0)" class="fr rightbar-pause" id="leave_shixun_page"><i class="fa fa-power-off font-20 mt5"></i><span class="ml10">暂时离开</span></a>
<ul class="rightbar-score fr" >
<!--<li><i class="fa fa-signal font-14 mr5"></i>排名:<a href="#" class="">123</a></li>-->
<% if params[:action] == "show" && (Time.now - @game.created_at) > 60 %>
<li><i class="fa fa-trophy font-14 mr5"></i>积分:<%= shixun_final_score(@myshixun) %></li>
<li><i class="fa fa-clock-o font-14 mr5"></i>耗时:<%= @game.status != 2 ? game_spend_time(@game.created_at, Time.now) : game_spend_time(@game.created_at, @game.updated_at) %></li>
<% end %>
</ul>
<div class="-layout-h ml10 fl">
<h2 class="-header-title task-hide font-20 color-white"><%= @myshixun.name %></h2>
<i class="fa fa-angle-right color-white font-20 ml5 mr5 mt10"></i>
<span class="font-20 color-white">第<%= @game_challenge.position %>关</span>
</div>
<script>
$("#leave_shixun_page").click(function(){
var htmlvalue = "<%= j (render :partial => 'games/leave_tip') %>";
pop_box_new(htmlvalue,460,316);
});
</script>
<div class="-layout-h mr10 fr mt3 -header-right-box">
<div class="-header-right clearfix">
<img src="/images/task/coin.png" class="fl" width="30" height="30">
<span class="ml5 color-white fl"><%= sum_final_score %></span>
</div>
<!--<div class="-header-right-info ">-->
<!--<font></font>-->
<!--<div class="-task-bar-bg"><span class="-task-bar-inner -task-widht-30"></span>3k/8K</div>-->
<!--<span class="font-12 color-grey">距离下次升级需要 +5000 经验值</span>-->
<!--</div>-->
</div>

View File

@ -49,7 +49,7 @@
<script>
$(document).ready(function(){
$.ajax({
url:"<%= rep_tree_changes_shixun_path(@project, :rev => @rev, :ent_path => ent_path, :gpid => @project.gpid) %>",
url:"<%= repository_tree_changes_project_path(@project, :rev => @rev, :ent_path => ent_path, :gpid => @project.gpid) %>",
type: "GET",
data: "text",
success:function(data){

View File

@ -16,7 +16,6 @@
:repository_id => @repository.identifier_param,
:path => to_path_param(ent_path),
:rev => @rev,
:depth => (depth + 1),
:parent_id => tr_id)) %>');">&nbsp;</span>
<% end %>
@ -24,42 +23,7 @@
{:action => (entry.is_dir? ? 'shixun_show' : 'shixun_entry'), :id => @shixun, :repository_id => @repository.identifier_param, :path => to_path_param(ent_path), :rev => @rev},
:class => (entry.is_dir? ? 'old-icon old-icon-folder' : "old-icon old-icon-file #{Redmine::MimeType.css_class_of(ent_name)}")%>
</td>
<div id="children_tree">
<td class="tree-author c_grey">
<div class="hidden" id="changes_author_<%= tr_id %>">
<%#= (latest_changes.author_name) if latest_changes %>
</div>
</td>
<td class="tree-comments c_grey hidden">
<div class="hidden" id="changes_message_<%= tr_id %>">
<%#= (latest_changes.message) if latest_changes %>
</div>
</td>
<td class="tree-age c_grey">
<div class="hidden" id="changes_time_<%= tr_id %>">
<%# 为了转换UTC时间时差8小时 %>
<%#= distance_of_time_in_words(latest_changes.time, Time.now) if latest_changes %>
<%#= link_to "", repository_tree_changes_project_path(@project, :rev => @rev, :ent_name => ent_name) %>
</div>
</td>
</div>
</tr>
<script>
$(document).ready(function(){
$.ajax({
url:"<%= repository_tree_changes_project_path(@shixun, :rev => @rev, :ent_path => ent_path, :gpid => @shixun.gpid) %>",
type: "GET",
data: "text",
success:function(data){
$('#changes_message_<%=tr_id %>').html(data.message)
$('#changes_author_<%=tr_id %>').html(data.author_name)
$('#changes_time_<%=tr_id %>').html(data.time)
}
});
});
</script>
<% end %>

View File

@ -17,7 +17,7 @@
<p class="c_orange ml70" id="project_name_notice" style="display: none;">实训名称不能为空</p>
</li>
<li class="ml45 mb10">
<label class="fl mr5">&nbsp;&nbsp;预备知识&nbsp;&nbsp;:</label>
<label class="fl ml30 mr5">简介&nbsp;&nbsp;:</label>
<%= f.kindeditor :description, :editor_id => 'project_create_editor',
:owner_id => @project.nil? ? 0: @project.id,
:owner_type => OwnerTypeHelper::PROJECT,

View File

@ -0,0 +1,2 @@
var htmlvalue = "<%= escape_javascript(render :partial => 'shixuns/monitor_tip') %>";
pop_box_new(htmlvalue,460,316);

View File

@ -1,4 +1,4 @@
<div style="width:460px;">
<div style="width:360px;">
<div class="task-popup-title clearfix">
<h3 class="fl color-grey">提示</h3>
<a href="javascript:void(0);" onclick="hideModal()"><i class="fa fa-times-circle font-18 link-color-grey fr"></i></a>
@ -7,21 +7,8 @@
<div class="sy_popup_con" style="width:380px;">
<ul class="sy_popup_add" >
<li class="center mb30" style="line-height:20px">
<%= @notice %>
服务器异常,请联系系统管理员
</li>
<div class="task-popup-submit clearfix">
<a href="javascript:void(0);" class="task-btn fl" onclick="hideModal();show_begin_challeng();">取消</a>
<% if @had_exec == true %>
<%= link_to "确 定", myshixun_path(@tpm), :class => "task-btn task-btn-green fr", :onclick => "hideModal();show_begin_challeng();", :target => "_blank" %>
<% else %>
<a href="<%= shixun_exec_shixun_path(@shixun) %>" class="task-btn task-btn-green fr" onclick="hideModal();show_begin_challeng()" target="_blank">确定</a>
<% end %>
</div>
</ul>
</div>
</div>
<script>
function show_begin_challeng(){
$("#challenge_begin").show();
}
</script>

View File

@ -10,7 +10,7 @@
<span class="c_red ml5 w690" id="project_name_notice" style="padding-left:100px;display: none;">实训名称不能为空!</span>
</li>
<li class="clearfix">
<label class="panel-form-label fl">预备知识</label>
<label class="panel-form-label fl">简介</label>
<%= f.kindeditor :description, :editor_id => 'shixun_setting_editor',
:owner_id => @shixun.nil? ? 0: @shixun.id,
:owner_type => OwnerTypeHelper::PROJECT,

View File

@ -1,6 +1,6 @@
<div class="task-pd15-box task-setting-tab">
<div class="alert alert-blue mb15" id="warning_frame">
<button data-dismiss="alert" class="task-close fr" type="button" onclick="close_waring_frame();">×</button>
<!--<button data-dismiss="alert" class="task-close fr" type="button" onclick="close_waring_frame();">×</button>-->
温馨提示:每个项目只能创建一个版本库。
</div>
<%= error_messages_for 'project' %>

View File

@ -6,31 +6,18 @@
<span class="ml5 mr5 fl">/ </span><p class="task-header-title fl" title="<%= @shixun.name %>"><%= @shixun.name %></p>
</h2>
<% if allow_shixun_exec(@shixun) %>
<%= link_to "开始挑战", shixun_monitor_shixun_path(@shixun),
:class => "fr task-btn task-btn-orange",
:id => "challenge_begin",
:remote => true %>
<% if User.current.id == @shixun.user_id %>
<% if @shixun.status == 1 %>
<a class="fr task-btn task-btn-orange">已发布</a>
<% else %>
<%= link_to "发布实训", publish_shixun_path(@shixun), :class => "fr task-btn task-btn-orange", :id => "challenge_begin", :remote => true %>
<% end %>
<% else %>
<% if @shixun.status == 1 %>
<%= link_to "开始挑战", shixun_exec_shixun_path(@shixun), :class => "fr task-btn task-btn-orange", :id => "challenge_begin" %>
<% end %>
<% end %>
<% end %>
<div class="cl"></div>
</div>
</div>
<div class="task-header-nav">
<ul class="task-header-navs clearfix">
<li><a href="<%= shixun_challenges_url(@shixun) %>" class="<%= params[:action] == "index" ? "active" : "" %>" >实训</a></li>
<% unless @shixun.repository.nil? %>
<li>
<%= link_to l(:project_module_repository),
({:controller => 'repositories',
:action => 'shixun_show',
:id => @shixun,
:repository_id => shixun_repository(@shixun).try(:identifier)}),
:class => (params[:controller] == 'repositories' ? 'active' : '')
%>
</li>
<% end %>
<% if User.current.manager_of_shixun?(@shixun) %>
<li><a href="<%= settings_shixun_path(@shixun) %>" class="<%= params[:action] == "settings" ? "active" : "" %>">配置</a></li>
<% end %>
</ul>
</div>

View File

@ -0,0 +1,6 @@
<% if @jenkin_error %>
var htmlvalue = "<%= escape_javascript(render :partial => 'shixuns/monitor_tip') %>";
pop_box_new(htmlvalue,460,406);
<% else %>
$("#shixun_top").html('<%= escape_javascript(render :partial => 'shixuns/shixun_top') %>');
<% end %>

View File

@ -8,9 +8,9 @@
<li id="tab_nav_2" onclick="HoverLi(2);">
<a href="javascript:void(0);" class="tab_type" >版本库</a>
</li>
<li id="tab_nav_3" onclick="HoverLi(3);">
<a href="javascript:void(0);" class="tab_type" >测试脚本</a>
</li>
<!--<li id="tab_nav_3" onclick="HoverLi(3);">-->
<!--<a href="javascript:void(0);" class="tab_type" >测试脚本</a>-->
<!--</li>-->
</ul>
<div class="cl"></div>
<div id="tab_con_1" class="" >
@ -19,9 +19,9 @@
<div id="tab_con_2" class="undis" >
<%= render :partial=>"shixuns/settings_repository" %>
</div>
<div id="tab_con_3" class="undis" >
<%= render :partial=>"shixuns/settings_challenges" %>
</div>
<!--<div id="tab_con_3" class="undis" >-->
<!--<%#= render :partial=>"shixuns/settings_challenges" %>-->
<!--</div>-->
</div>
</div>
</div>
@ -33,7 +33,7 @@
<li><a href="javascript:void(0)" id="pro_st_tb_1" class="<%= show_memu == 'edit_project' ? 'active' : ''%>" onclick="project_setting(1);">信息</a></li>
<!--<li id="pro_st_tb_5" class="pro_st_normaltab" onclick="project_setting(5);">问题类别</li>-->
<li><a href="javascript:void(0)" id="pro_st_tb_6" class="<%= show_memu == 'manage_repository' ? 'active' : ''%>" onclick="project_setting(6);">版本库</a></li>
<li><a href="javascript:void(0)" id="pro_st_tb_7" class="<%= show_memu == 'trainig_task' ? 'active' : ''%>" onclick="project_setting(7);">实训任务</a></li>
<!--<li><a href="javascript:void(0)" id="pro_st_tb_7" class="<%#= show_memu == 'trainig_task' ? 'active' : ''%>" onclick="project_setting(7);">实训任务</a></li>-->
</ul>
</div>
@ -45,16 +45,16 @@
<%= render :partial=>"shixuns/settings_repository" %>
</div><!--tbc_06 end-->
<div class="<%= show_memu == 'training_task' ? 'pro_st_dis' : 'pro_st_undis'%>" id="pro_st_tbc_07">
<%= render :partial=>"shixuns/settings_challenges" %>
</div><!--tbc_06 end-->
<!--<div class="<%#= show_memu == 'training_task' ? 'pro_st_dis' : 'pro_st_undis'%>" id="pro_st_tbc_07">-->
<!--<%#= render :partial=>"shixuns/settings_challenges" %>-->
<!--</div>&lt;!&ndash;tbc_06 end&ndash;&gt;-->
</div>
</div>
<% end %>
<script type="text/javascript">
function g(o){return document.getElementById(o);}
function HoverLi(n){
for(var i=1;i<=3;i++){
for(var i=1;i<=2;i++){
g('tab_nav_'+i).className='tab_nomal';
g('tab_con_'+i).className='undis';
}

View File

@ -41,6 +41,7 @@ RedmineApp::Application.routes.draw do
get 'shixun_job_create'
get 'shixun_job_update'
get 'rep_tree_changes', :action => 'rep_tree_changes', :as => 'rep_tree_changes'
match 'publish', :via => [:get, :post]
end
collection do
end
@ -1314,7 +1315,7 @@ RedmineApp::Application.routes.draw do
post 'attachments/upload_attachment_version',:to=>'attachments#upload_attachment_version',:as=>'upload_attachment_version'
get 'attachments/download/:id/:filename', :to => 'attachments#download', :id => /\d+/, :filename => /.*/, :as => 'download_named_attachment'
get 'attachments/download_history/:id/:filename', :to => 'attachments#download_history', :id => /\d+/, :filename => /.*/, :as => 'download_history_attachment'
get 'attachments/download/:id', :to => 'attachments#download', :id => /\d+/
get 'attachments/download/:id', :to => 'attachments#download', :id => /\d+/, :as => 'download_attachment'
get 'attachments/thumbnail/:id(/:size)', :to => 'attachments#thumbnail', :id => /\d+/, :size => /\d+/, :as => 'thumbnail'
get 'attachments/autocomplete'
get 'attachments/update_attachment_publish_time/:id',:to=>'attachments#update_attachment_publish_time',:as=>'update_attachment_publish_time'
@ -1581,6 +1582,7 @@ RedmineApp::Application.routes.draw do
match 'sys/fetch_changesets', :via => :get
match 'uploads', :to => 'attachments#upload', :via => :post
match 'upload_with_markdown', :to => 'attachments#upload_with_markdown', :via => :post
# Added by Tao
match 'upload_avatar', :to => 'avatar#upload', :via => :post
match 'delete_avatar', :to => 'avatar#delete_image',:via => :post

View File

@ -0,0 +1,6 @@
class AddEvaluationWayToChallenge < ActiveRecord::Migration
def change
add_column :challenges, :evaluation_way, :integer, :default => 0
add_column :challenges, :difficulty, :integer, :default => 1
end
end

View File

@ -0,0 +1,5 @@
class AddScriptToChallenge < ActiveRecord::Migration
def change
add_column :challenges, :pipeline_script, :text
end
end

View File

@ -0,0 +1,5 @@
class AddActualOutputToTestSets < ActiveRecord::Migration
def change
add_column :test_sets, :actual_output, :string
end
end

View File

@ -0,0 +1,6 @@
class AddHiddenToTestSets < ActiveRecord::Migration
def change
add_column :test_sets, :is_public, :boolean, :default => true
add_column :test_sets, :result, :boolean, :default => true
end
end

View File

@ -0,0 +1,5 @@
class AddOpenTimeToGame < ActiveRecord::Migration
def change
add_column :games, :open_time, :datetime
end
end

View File

@ -0,0 +1,10 @@
class UpdateOpenTimeToGame < ActiveRecord::Migration
def up
Game.all.each do |game|
game.update_attribute(:open_time, game.created_at)
end
end
def down
end
end

View File

@ -0,0 +1,5 @@
class AddPositionToTestSets < ActiveRecord::Migration
def change
add_column :test_sets, :position, :integer
end
end

8
public/codemirror/.gitattributes vendored Normal file
View File

@ -0,0 +1,8 @@
*.txt text
*.js text
*.html text
*.md text
*.json text
*.yml text
*.css text
*.svg text

View File

@ -0,0 +1,11 @@
/node_modules
/demo
/doc
/test
/test*.html
/index.html
/mode/*/*test.js
/mode/*/*.html
/mode/index.html
.*
bin

View File

@ -0,0 +1,4 @@
language: node_js
node_js:
- stable
sudo: false

639
public/codemirror/AUTHORS Normal file
View File

@ -0,0 +1,639 @@
List of CodeMirror contributors. Updated before every release.
4r2r
Aaron Brooks
Abdelouahab
Abe Fettig
Adam Ahmed
Adam King
adanlobato
Adán Lobato
Adrian Aichner
Adrian Heine
Adrien Bertrand
aeroson
Ahmad Amireh
Ahmad M. Zawawi
ahoward
Akeksandr Motsjonov
Alasdair Smith
Alberto González Palomo
Alberto Pose
Albert Xing
Alexander Pavlov
Alexander Schepanovski
Alexander Shvets
Alexander Solovyov
Alexandre Bique
alexey-k
Alex Piggott
Aliaksei Chapyzhenka
Allen Sarkisyan
Ami Fischman
Amin Shali
Amin Ullah Khan
amshali@google.com
Amsul
amuntean
Amy
Ananya Sen
anaran
AndersMad
Anders Nawroth
Anderson Mesquita
Anders Wåglund
Andrea G
Andreas Reischuck
Andres Taylor
Andre von Houck
Andrew Cheng
Andrey Fedorov
Andrey Klyuchnikov
Andrey Lushnikov
Andrey Shchekin
Andy Joslin
Andy Kimball
Andy Li
Angelo
angelozerr
angelo.zerr@gmail.com
Ankit
Ankit Ahuja
Ansel Santosa
Anthony Dugois
anthonygego
Anthony Gégo
Anthony Grimes
Anton Kovalyov
Apollo Zhu
AQNOUCH Mohammed
areos
Arnab Bose
Arthur Müller
Arun Narasani
as3boyan
atelierbram
AtomicPages LLC
Atul Bhouraskar
Aurelian Oancea
Axel Lewenhaupt
Barret Rennie
Basarat Ali Syed
Bastian Müller
belhaj
Bem Jones-Bey
benbro
Beni Cherniavsky-Paskin
Benjamin DeCoste
Ben Keen
Ben Miller
Ben Mosher
Bernhard Sirlinger
Bert Chang
Bharad
BigBlueHat
Billy Moon
binny
B Krishna Chaitanya
Blaine G
blukat29
boomyjee
borawjm
Brad Metcalf
Brandon Frohs
Brandon Wamboldt
Brett Zamir
Brian Grinstead
Brian Sletten
brrd
Bruce Mitchener
Bryan Massoth
Caitlin Potter
Calin Barbat
callodacity
Camilo Roca
Chad Jolly
Chandra Sekhar Pydi
Charles Skelton
Cheah Chu Yeow
Chris Coyier
Chris Ford
Chris Granger
Chris Houseknecht
Chris Lohfink
Chris Morgan
Chris Smith
Christian Oyarzun
Christian Petrov
Christopher Brown
Christopher Kramer
Christopher Mitchell
Christopher Pfohl
Chunliang Lyu
ciaranj
CodeAnimal
coderaiser
Cole R Lawrence
ComFreek
Curtis Gagliardi
dagsta
daines
Dale Jung
Dan Bentley
Dan Heberden
Daniel, Dao Quang Minh
Daniele Di Sarli
Daniel Faust
Daniel Huigens
Daniel Kesler
Daniel KJ
Daniel Neel
Daniel Parnell
Danny Yoo
darealshinji
Darius Roberts
Dave Brondsema
Dave Myers
David Barnett
David H. Bronke
David Mignot
David Pathakjee
David Vázquez
David Whittington
deebugger
Deep Thought
Devin Abbott
Devon Carew
Dick Choi
dignifiedquire
Dimage Sapelkin
Dmitry Kiselyov
domagoj412
Dominator008
Domizio Demichelis
Doug Wikle
Drew Bratcher
Drew Hintz
Drew Khoury
Drini Cami
Dror BG
duralog
eborden
edsharp
ekhaled
Elisée
Emmanuel Schanzer
Enam Mijbah Noor
Eric Allam
Erik Welander
eustas
Fabien Dubosson
Fabien O'Carroll
Fabio Zendhi Nagao
Faiza Alsaied
Fauntleroy
fbuchinger
feizhang365
Felipe Lalanne
Felix Raab
ficristo
Filip Noetzel
Filip Stollár
flack
ForbesLindesay
Forbes Lindesay
Ford_Lawnmower
Forrest Oliphant
Frank Wiegand
Gabriel Gheorghian
Gabriel Horner
Gabriel Nahmias
galambalazs
Gary Sheng
Gautam Mehta
Gavin Douglas
gekkoe
Geordie Hall
geowarin
Gerard Braad
Gergely Hegykozi
Giovanni Calò
Glebov Boris
Glenn Jorde
Glenn Ruehle
Golevka
Google Inc.
Gordon Smith
Grant Skinner
greengiant
Gregory Koberger
Grzegorz Mazur
Guillaume Massé
Guillaume Massé
guraga
Gustavo Rodrigues
Hakan Tunc
Hans Engel
Hardest
Harshvardhan Gupta
Hasan Karahan
Hector Oswaldo Caballero
Hendrik Wallbaum
Herculano Campos
Hiroyuki Makino
hitsthings
Hocdoc
Hugues Malphettes
Ian Beck
Ian Dickinson
Ian Wehrman
Ian Wetherbee
Ice White
ICHIKAWA, Yuji
idleberg
ilvalle
Ingo Richter
Irakli Gozalishvili
Ivan Kurnosov
Ivoah
Jacob Lee
Jake Peyser
Jakob Miland
Jakub Vrana
Jakub Vrána
James Campos
James Howard
James Thorne
Jamie Hill
Jan Jongboom
jankeromnes
Jan Keromnes
Jan Odvarko
Jan Schär
Jan T. Sott
Jared Dean
Jared Forsyth
Jared Jacobs
Jason
Jason Barnabe
Jason Grout
Jason Johnston
Jason San Jose
Jason Siefken
Jaydeep Solanki
Jean Boussier
Jeff Blaisdell
Jeff Jenkins
jeffkenton
Jeff Pickhardt
jem (graphite)
Jeremy Parmenter
Jim
Jim Avery
JobJob
jochenberger
Jochen Berger
Joel Einbinder
joelpinheiro
Johan Ask
John Connor
John-David Dalton
John Engler
John Lees-Miller
John Snelson
John Van Der Loo
Jon Ander Peñalba
Jonas Döbertin
Jonathan Malmaud
Jon Gacnik
jongalloway
Jon Malmaud
Jon Sangster
Joost-Wim Boekesteijn
Joseph Pecoraro
Josh Barnes
Josh Cohen
Josh Soref
Joshua Newman
Josh Watzman
jots
jsoojeon
ju1ius
Juan Benavides Romero
Jucovschi Constantin
Juho Vuori
Julien Rebetez
Justin Andresen
Justin Hileman
jwallers@gmail.com
kaniga
karevn
Kayur Patel
Kazuhito Hokamura
Ken Newman
ken restivo
Ken Rockot
Kevin Earls
Kevin Muret
Kevin Sawicki
Kevin Ushey
Klaus Silveira
Koh Zi Han, Cliff
komakino
Konstantin Lopuhin
koops
Kris Ciccarello
ks-ifware
kubelsmieci
KwanEsq
Kyle Kelley
Lanfei
Lanny
Laszlo Vidacs
leaf corcoran
Leonid Khachaturov
Leon Sorokin
Leonya Khachaturov
Liam Newman
Libo Cannici
LloydMilligan
LM
lochel
Lorenzo Stoakes
Luca Fabbri
Luciano Longo
Lu Fangjian
Luke Browning
Luke Granger-Brown
Luke Stagner
lynschinzer
M1cha
Madhura Jayaratne
Maksim Lin
Maksym Taran
Malay Majithia
Manideep
Manuel Rego Casasnovas
Marat Dreizin
Marcel Gerber
Marcelo Camargo
Marco Aurélio
Marco Munizaga
Marcus Bointon
Marek Rudnicki
Marijn Haverbeke
Mário Gonçalves
Mario Pietsch
Mark Anderson
Mark Lentczner
Marko Bonaci
Mark Peace
Markus Bordihn
Martin Balek
Martín Gaitán
Martin Hasoň
Martin Hunt
Martin Laine
Martin Zagora
Mason Malone
Mateusz Paprocki
Mathias Bynens
mats cronqvist
Matt Gaide
Matthew Bauer
Matthew Beale
matthewhayes
Matthew Rathbone
Matthias Bussonnier
Matthias BUSSONNIER
Matt McDonald
Matt Pass
Matt Sacks
mauricio
Maximilian Hils
Maxim Kraev
Max Kirsch
Max Schaefer
Max Xiantu
mbarkhau
McBrainy
melpon
Metatheos
Micah Dubinko
Michael
Michael Goderbauer
Michael Grey
Michael Kaminsky
Michael Lehenbauer
Michael Zhou
Michal Dorner
Mighty Guava
Miguel Castillo
mihailik
Mike
Mike Brevoort
Mike Diaz
Mike Ivanov
Mike Kadin
Mike Kobit
MinRK
Miraculix87
misfo
mkaminsky11
mloginov
Moritz Schwörer
mps
ms
mtaran-google
Mu-An Chiou
Narciso Jaramillo
Nathan Williams
ndr
nerbert
nextrevision
ngn
nguillaumin
Ng Zhi An
Nicholas Bollweg
Nicholas Bollweg (Nick)
Nick Kreeger
Nick Small
Nicolò Ribaudo
Niels van Groningen
nightwing
Nikita Beloglazov
Nikita Vasilyev
Nikolay Kostov
nilp0inter
Nisarg Jhaveri
nlwillia
noragrossman
Norman Rzepka
Oreoluwa Onatemowo
Oskar Segersvärd
pablo
pabloferz
Page
Panupong Pasupat
paris
Paris
Paris Kasidiaris
Patil Arpith
Patrick Stoica
Patrick Strawderman
Paul Garvin
Paul Ivanov
Paul Masson
Pavel
Pavel Feldman
Pavel Petržela
Pavel Strashkin
Paweł Bartkiewicz
peteguhl
peter
Peter Flynn
peterkroon
Peter Kroon
Philipp A
Philip Stadermann
Pierre Gerold
Piët Delport
Pontus Melke
prasanthj
Prasanth J
Prayag Verma
Radek Piórkowski
Rahul
Rahul Anand
ramwin1
Randall Mason
Randy Burden
Randy Edmunds
Rasmus Erik Voel Jensen
ray ratchup
Ray Ratchup
Remi Nyborg
Richard Denton
Richard van der Meer
Richard Z.H. Wang
Rishi Goomar
Robert Crossfield
Roberto Abdelkader Martínez Pérez
robertop23
Robert Plummer
Rrandom
Rrrandom
Ruslan Osmanov
Ryan Petrello
Ryan Prior
sabaca
Sam Lee
Samuel Ainsworth
Sam Wilson
sandeepshetty
Sander AKA Redsandro
Sander Verweij
santec
Sascha Peilicke
satamas
satchmorun
sathyamoorthi
S. Chris Colbert
SCLINIC\jdecker
Scott Aikin
Scott Goodhew
Sebastian Zaha
Sergey Goder
Sergey Tselovalnikov
Se-Won Kim
shaund
shaun gilchrist
Shawn A
Shea Bunge
sheopory
Shiv Deepak
Shmuel Englard
Shubham Jain
Siamak Mokhtari
silverwind
sinkuu
snasa
soliton4
sonson
spastorelli
srajanpaliwal
Stanislav Oaserele
Stas Kobzar
Stefan Borsje
Steffen Beyer
Steffen Bruchmann
Stephen Lavelle
Steve Champagne
Steve Hoover
Steve O'Hara
stoskov
Stu Kennedy
Sungho Kim
sverweij
Taha Jahangir
takamori
Tako Schotanus
Takuji Shimokawa
Tarmil
TDaglis
tel
tfjgeorge
Thaddee Tyl
thanasis
TheHowl
themrmax
think
Thomas Dvornik
Thomas Kluyver
Thomas Schmid
Tim Alby
Tim Baumann
Timothy Farrell
Timothy Gu
Timothy Hatcher
TobiasBg
Todd Berman
Tomas-A
Tomas Varaneckas
Tom Erik Støwer
Tom Klancer
Tom MacWright
Tony Jian
Travis Heppe
Triangle717
Tristan Tarrant
TSUYUSATO Kitsune
twifkak
VapidWorx
Vestimir Markov
vf
Victor Bocharsky
Vincent Woo
Volker Mische
Weiyan Shao
wenli
Wes Cossick
Wesley Wiser
Will Binns-Smith
Will Dean
William Jamieson
William Stein
Willy
Wojtek Ptak
Wu Cheng-Han
Xavier Mendez
Yassin N. Hassan
YNH Webdev
Yunchi Luo
Yuvi Panda
Zac Anger
Zachary Dremann
Zeno Rocha
Zhang Hao
zziuni
魏鹏刚

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,88 @@
# How to contribute
- [Getting help](#getting-help)
- [Submitting bug reports](#submitting-bug-reports)
- [Contributing code](#contributing-code)
## Getting help
Community discussion, questions, and informal bug reporting is done on the
[discuss.CodeMirror forum](http://discuss.codemirror.net).
## Submitting bug reports
The preferred way to report bugs is to use the
[GitHub issue tracker](http://github.com/codemirror/CodeMirror/issues). Before
reporting a bug, read these pointers.
**Note:** The issue tracker is for *bugs*, not requests for help. Questions
should be asked on the
[discuss.CodeMirror forum](http://discuss.codemirror.net) instead.
### Reporting bugs effectively
- CodeMirror is maintained by volunteers. They don't owe you anything, so be
polite. Reports with an indignant or belligerent tone tend to be moved to the
bottom of the pile.
- Include information about **the browser in which the problem occurred**. Even
if you tested several browsers, and the problem occurred in all of them,
mention this fact in the bug report. Also include browser version numbers and
the operating system that you're on.
- Mention which release of CodeMirror you're using. Preferably, try also with
the current development snapshot, to ensure the problem has not already been
fixed.
- Mention very precisely what went wrong. "X is broken" is not a good bug
report. What did you expect to happen? What happened instead? Describe the
exact steps a maintainer has to take to make the problem occur. We can not
fix something that we can not observe.
- If the problem can not be reproduced in any of the demos included in the
CodeMirror distribution, please provide an HTML document that demonstrates
the problem. The best way to do this is to go to
[jsbin.com](http://jsbin.com/ihunin/edit), enter it there, press save, and
include the resulting link in your bug report.
## Contributing code
- Make sure you have a [GitHub Account](https://github.com/signup/free)
- Fork [CodeMirror](https://github.com/codemirror/CodeMirror/)
([how to fork a repo](https://help.github.com/articles/fork-a-repo))
- Make your changes
- If your changes are easy to test or likely to regress, add tests.
Tests for the core go into `test/test.js`, some modes have their own
test suite under `mode/XXX/test.js`. Feel free to add new test
suites to modes that don't have one yet (be sure to link the new
tests into `test/index.html`).
- Follow the general code style of the rest of the project (see
below). Run `bin/lint` to verify that the linter is happy.
- Make sure all tests pass. Visit `test/index.html` in your browser to
run them.
- Submit a pull request
([how to create a pull request](https://help.github.com/articles/fork-a-repo)).
Don't put more than one feature/fix in a single pull request.
By contributing code to CodeMirror you
- agree to license the contributed code under CodeMirror's [MIT
license](http://codemirror.net/LICENSE).
- confirm that you have the right to contribute and license the code
in question. (Either you hold all rights on the code, or the rights
holder has explicitly granted the right to use it like this,
through a compatible open source license or through a direct
agreement with you.)
### Coding standards
- 2 spaces per indentation level, no tabs.
- Note that the linter (`bin/lint`) which is run after each commit
complains about unused variables and functions. Prefix their names
with an underscore to muffle it.
- CodeMirror does *not* follow JSHint or JSLint prescribed style.
Patches that try to 'fix' code to pass one of these linters will be
unceremoniously discarded.

21
public/codemirror/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (C) 2017 by Marijn Haverbeke <marijnh@gmail.com> and others
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,34 @@
# CodeMirror
[![Build Status](https://travis-ci.org/codemirror/CodeMirror.svg)](https://travis-ci.org/codemirror/CodeMirror)
[![NPM version](https://img.shields.io/npm/v/codemirror.svg)](https://www.npmjs.org/package/codemirror)
[![Join the chat at https://gitter.im/codemirror/CodeMirror](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/codemirror/CodeMirror)
[Funding status: ![maintainer happiness](https://marijnhaverbeke.nl/fund/status_s.png?again)](https://marijnhaverbeke.nl/fund/)
CodeMirror is a versatile text editor implemented in JavaScript for
the browser. It is specialized for editing code, and comes with over
100 language modes and various addons that implement more advanced
editing functionality.
A rich programming API and a CSS theming system are available for
customizing CodeMirror to fit your application, and extending it with
new functionality.
You can find more information (and the
[manual](http://codemirror.net/doc/manual.html)) on the [project
page](http://codemirror.net). For questions and discussion, use the
[discussion forum](https://discuss.codemirror.net/).
See
[CONTRIBUTING.md](https://github.com/codemirror/CodeMirror/blob/master/CONTRIBUTING.md)
for contributing guidelines.
The CodeMirror community aims to be welcoming to everybody. We use the
[Contributor Covenant
(1.1)](http://contributor-covenant.org/version/1/1/0/) as our code of
conduct.
### Quickstart
To build the project, make sure you have Node.js installed (at least version 6)
and then `npm install`. To run, just open `index.html` in your
browser (you don't need to run a webserver). Run the tests with `npm test`.

View File

@ -0,0 +1,213 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
var noOptions = {};
var nonWS = /[^\s\u00a0]/;
var Pos = CodeMirror.Pos;
function firstNonWS(str) {
var found = str.search(nonWS);
return found == -1 ? 0 : found;
}
CodeMirror.commands.toggleComment = function(cm) {
cm.toggleComment();
};
CodeMirror.defineExtension("toggleComment", function(options) {
if (!options) options = noOptions;
var cm = this;
var minLine = Infinity, ranges = this.listSelections(), mode = null;
for (var i = ranges.length - 1; i >= 0; i--) {
var from = ranges[i].from(), to = ranges[i].to();
if (from.line >= minLine) continue;
if (to.line >= minLine) to = Pos(minLine, 0);
minLine = from.line;
if (mode == null) {
if (cm.uncomment(from, to, options)) mode = "un";
else { cm.lineComment(from, to, options); mode = "line"; }
} else if (mode == "un") {
cm.uncomment(from, to, options);
} else {
cm.lineComment(from, to, options);
}
}
});
// Rough heuristic to try and detect lines that are part of multi-line string
function probablyInsideString(cm, pos, line) {
return /\bstring\b/.test(cm.getTokenTypeAt(Pos(pos.line, 0))) && !/^[\'\"\`]/.test(line)
}
function getMode(cm, pos) {
var mode = cm.getMode()
return mode.useInnerComments === false || !mode.innerMode ? mode : cm.getModeAt(pos)
}
CodeMirror.defineExtension("lineComment", function(from, to, options) {
if (!options) options = noOptions;
var self = this, mode = getMode(self, from);
var firstLine = self.getLine(from.line);
if (firstLine == null || probablyInsideString(self, from, firstLine)) return;
var commentString = options.lineComment || mode.lineComment;
if (!commentString) {
if (options.blockCommentStart || mode.blockCommentStart) {
options.fullLines = true;
self.blockComment(from, to, options);
}
return;
}
var end = Math.min(to.ch != 0 || to.line == from.line ? to.line + 1 : to.line, self.lastLine() + 1);
var pad = options.padding == null ? " " : options.padding;
var blankLines = options.commentBlankLines || from.line == to.line;
self.operation(function() {
if (options.indent) {
var baseString = null;
for (var i = from.line; i < end; ++i) {
var line = self.getLine(i);
var whitespace = line.slice(0, firstNonWS(line));
if (baseString == null || baseString.length > whitespace.length) {
baseString = whitespace;
}
}
for (var i = from.line; i < end; ++i) {
var line = self.getLine(i), cut = baseString.length;
if (!blankLines && !nonWS.test(line)) continue;
if (line.slice(0, cut) != baseString) cut = firstNonWS(line);
self.replaceRange(baseString + commentString + pad, Pos(i, 0), Pos(i, cut));
}
} else {
for (var i = from.line; i < end; ++i) {
if (blankLines || nonWS.test(self.getLine(i)))
self.replaceRange(commentString + pad, Pos(i, 0));
}
}
});
});
CodeMirror.defineExtension("blockComment", function(from, to, options) {
if (!options) options = noOptions;
var self = this, mode = getMode(self, from);
var startString = options.blockCommentStart || mode.blockCommentStart;
var endString = options.blockCommentEnd || mode.blockCommentEnd;
if (!startString || !endString) {
if ((options.lineComment || mode.lineComment) && options.fullLines != false)
self.lineComment(from, to, options);
return;
}
if (/\bcomment\b/.test(self.getTokenTypeAt(Pos(from.line, 0)))) return
var end = Math.min(to.line, self.lastLine());
if (end != from.line && to.ch == 0 && nonWS.test(self.getLine(end))) --end;
var pad = options.padding == null ? " " : options.padding;
if (from.line > end) return;
self.operation(function() {
if (options.fullLines != false) {
var lastLineHasText = nonWS.test(self.getLine(end));
self.replaceRange(pad + endString, Pos(end));
self.replaceRange(startString + pad, Pos(from.line, 0));
var lead = options.blockCommentLead || mode.blockCommentLead;
if (lead != null) for (var i = from.line + 1; i <= end; ++i)
if (i != end || lastLineHasText)
self.replaceRange(lead + pad, Pos(i, 0));
} else {
self.replaceRange(endString, to);
self.replaceRange(startString, from);
}
});
});
CodeMirror.defineExtension("uncomment", function(from, to, options) {
if (!options) options = noOptions;
var self = this, mode = getMode(self, from);
var end = Math.min(to.ch != 0 || to.line == from.line ? to.line : to.line - 1, self.lastLine()), start = Math.min(from.line, end);
// Try finding line comments
var lineString = options.lineComment || mode.lineComment, lines = [];
var pad = options.padding == null ? " " : options.padding, didSomething;
lineComment: {
if (!lineString) break lineComment;
for (var i = start; i <= end; ++i) {
var line = self.getLine(i);
var found = line.indexOf(lineString);
if (found > -1 && !/comment/.test(self.getTokenTypeAt(Pos(i, found + 1)))) found = -1;
if (found == -1 && nonWS.test(line)) break lineComment;
if (found > -1 && nonWS.test(line.slice(0, found))) break lineComment;
lines.push(line);
}
self.operation(function() {
for (var i = start; i <= end; ++i) {
var line = lines[i - start];
var pos = line.indexOf(lineString), endPos = pos + lineString.length;
if (pos < 0) continue;
if (line.slice(endPos, endPos + pad.length) == pad) endPos += pad.length;
didSomething = true;
self.replaceRange("", Pos(i, pos), Pos(i, endPos));
}
});
if (didSomething) return true;
}
// Try block comments
var startString = options.blockCommentStart || mode.blockCommentStart;
var endString = options.blockCommentEnd || mode.blockCommentEnd;
if (!startString || !endString) return false;
var lead = options.blockCommentLead || mode.blockCommentLead;
var startLine = self.getLine(start), open = startLine.indexOf(startString)
if (open == -1) return false
var endLine = end == start ? startLine : self.getLine(end)
var close = endLine.indexOf(endString, end == start ? open + startString.length : 0);
if (close == -1 && start != end) {
endLine = self.getLine(--end);
close = endLine.indexOf(endString);
}
var insideStart = Pos(start, open + 1), insideEnd = Pos(end, close + 1)
if (close == -1 ||
!/comment/.test(self.getTokenTypeAt(insideStart)) ||
!/comment/.test(self.getTokenTypeAt(insideEnd)) ||
self.getRange(insideStart, insideEnd, "\n").indexOf(endString) > -1)
return false;
// Avoid killing block comments completely outside the selection.
// Positions of the last startString before the start of the selection, and the first endString after it.
var lastStart = startLine.lastIndexOf(startString, from.ch);
var firstEnd = lastStart == -1 ? -1 : startLine.slice(0, from.ch).indexOf(endString, lastStart + startString.length);
if (lastStart != -1 && firstEnd != -1 && firstEnd + endString.length != from.ch) return false;
// Positions of the first endString after the end of the selection, and the last startString before it.
firstEnd = endLine.indexOf(endString, to.ch);
var almostLastStart = endLine.slice(to.ch).lastIndexOf(startString, firstEnd - to.ch);
lastStart = (firstEnd == -1 || almostLastStart == -1) ? -1 : to.ch + almostLastStart;
if (firstEnd != -1 && lastStart != -1 && lastStart != to.ch) return false;
self.operation(function() {
self.replaceRange("", Pos(end, close - (pad && endLine.slice(close - pad.length, close) == pad ? pad.length : 0)),
Pos(end, close + endString.length));
var openEnd = open + startString.length;
if (pad && startLine.slice(openEnd, openEnd + pad.length) == pad) openEnd += pad.length;
self.replaceRange("", Pos(start, open), Pos(start, openEnd));
if (lead) for (var i = start + 1; i <= end; ++i) {
var line = self.getLine(i), found = line.indexOf(lead);
if (found == -1 || nonWS.test(line.slice(0, found))) continue;
var foundEnd = found + lead.length;
if (pad && line.slice(foundEnd, foundEnd + pad.length) == pad) foundEnd += pad.length;
self.replaceRange("", Pos(i, found), Pos(i, foundEnd));
}
});
return true;
});
});

View File

@ -0,0 +1,85 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
var modes = ["clike", "css", "javascript"];
for (var i = 0; i < modes.length; ++i)
CodeMirror.extendMode(modes[i], {blockCommentContinue: " * "});
function continueComment(cm) {
if (cm.getOption("disableInput")) return CodeMirror.Pass;
var ranges = cm.listSelections(), mode, inserts = [];
for (var i = 0; i < ranges.length; i++) {
var pos = ranges[i].head, token = cm.getTokenAt(pos);
if (token.type != "comment") return CodeMirror.Pass;
var modeHere = CodeMirror.innerMode(cm.getMode(), token.state).mode;
if (!mode) mode = modeHere;
else if (mode != modeHere) return CodeMirror.Pass;
var insert = null;
if (mode.blockCommentStart && mode.blockCommentContinue) {
var end = token.string.indexOf(mode.blockCommentEnd);
var full = cm.getRange(CodeMirror.Pos(pos.line, 0), CodeMirror.Pos(pos.line, token.end)), found;
if (end != -1 && end == token.string.length - mode.blockCommentEnd.length && pos.ch >= end) {
// Comment ended, don't continue it
} else if (token.string.indexOf(mode.blockCommentStart) == 0) {
insert = full.slice(0, token.start);
if (!/^\s*$/.test(insert)) {
insert = "";
for (var j = 0; j < token.start; ++j) insert += " ";
}
} else if ((found = full.indexOf(mode.blockCommentContinue)) != -1 &&
found + mode.blockCommentContinue.length > token.start &&
/^\s*$/.test(full.slice(0, found))) {
insert = full.slice(0, found);
}
if (insert != null) insert += mode.blockCommentContinue;
}
if (insert == null && mode.lineComment && continueLineCommentEnabled(cm)) {
var line = cm.getLine(pos.line), found = line.indexOf(mode.lineComment);
if (found > -1) {
insert = line.slice(0, found);
if (/\S/.test(insert)) insert = null;
else insert += mode.lineComment + line.slice(found + mode.lineComment.length).match(/^\s*/)[0];
}
}
if (insert == null) return CodeMirror.Pass;
inserts[i] = "\n" + insert;
}
cm.operation(function() {
for (var i = ranges.length - 1; i >= 0; i--)
cm.replaceRange(inserts[i], ranges[i].from(), ranges[i].to(), "+insert");
});
}
function continueLineCommentEnabled(cm) {
var opt = cm.getOption("continueComments");
if (opt && typeof opt == "object")
return opt.continueLineComment !== false;
return true;
}
CodeMirror.defineOption("continueComments", null, function(cm, val, prev) {
if (prev && prev != CodeMirror.Init)
cm.removeKeyMap("continueComment");
if (val) {
var key = "Enter";
if (typeof val == "string")
key = val;
else if (typeof val == "object" && val.key)
key = val.key;
var map = {name: "continueComment"};
map[key] = continueComment;
cm.addKeyMap(map);
}
});
});

View File

@ -0,0 +1,32 @@
.CodeMirror-dialog {
position: absolute;
left: 0; right: 0;
background: inherit;
z-index: 15;
padding: .1em .8em;
overflow: hidden;
color: inherit;
}
.CodeMirror-dialog-top {
border-bottom: 1px solid #eee;
top: 0;
}
.CodeMirror-dialog-bottom {
border-top: 1px solid #eee;
bottom: 0;
}
.CodeMirror-dialog input {
border: none;
outline: none;
background: transparent;
width: 20em;
color: inherit;
font-family: monospace;
}
.CodeMirror-dialog button {
font-size: 70%;
}

157
public/codemirror/addon/dialog/dialog.js vendored Normal file
View File

@ -0,0 +1,157 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
// Open simple dialogs on top of an editor. Relies on dialog.css.
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
function dialogDiv(cm, template, bottom) {
var wrap = cm.getWrapperElement();
var dialog;
dialog = wrap.appendChild(document.createElement("div"));
if (bottom)
dialog.className = "CodeMirror-dialog CodeMirror-dialog-bottom";
else
dialog.className = "CodeMirror-dialog CodeMirror-dialog-top";
if (typeof template == "string") {
dialog.innerHTML = template;
} else { // Assuming it's a detached DOM element.
dialog.appendChild(template);
}
return dialog;
}
function closeNotification(cm, newVal) {
if (cm.state.currentNotificationClose)
cm.state.currentNotificationClose();
cm.state.currentNotificationClose = newVal;
}
CodeMirror.defineExtension("openDialog", function(template, callback, options) {
if (!options) options = {};
closeNotification(this, null);
var dialog = dialogDiv(this, template, options.bottom);
var closed = false, me = this;
function close(newVal) {
if (typeof newVal == 'string') {
inp.value = newVal;
} else {
if (closed) return;
closed = true;
dialog.parentNode.removeChild(dialog);
me.focus();
if (options.onClose) options.onClose(dialog);
}
}
var inp = dialog.getElementsByTagName("input")[0], button;
if (inp) {
inp.focus();
if (options.value) {
inp.value = options.value;
if (options.selectValueOnOpen !== false) {
inp.select();
}
}
if (options.onInput)
CodeMirror.on(inp, "input", function(e) { options.onInput(e, inp.value, close);});
if (options.onKeyUp)
CodeMirror.on(inp, "keyup", function(e) {options.onKeyUp(e, inp.value, close);});
CodeMirror.on(inp, "keydown", function(e) {
if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) { return; }
if (e.keyCode == 27 || (options.closeOnEnter !== false && e.keyCode == 13)) {
inp.blur();
CodeMirror.e_stop(e);
close();
}
if (e.keyCode == 13) callback(inp.value, e);
});
if (options.closeOnBlur !== false) CodeMirror.on(inp, "blur", close);
} else if (button = dialog.getElementsByTagName("button")[0]) {
CodeMirror.on(button, "click", function() {
close();
me.focus();
});
if (options.closeOnBlur !== false) CodeMirror.on(button, "blur", close);
button.focus();
}
return close;
});
CodeMirror.defineExtension("openConfirm", function(template, callbacks, options) {
closeNotification(this, null);
var dialog = dialogDiv(this, template, options && options.bottom);
var buttons = dialog.getElementsByTagName("button");
var closed = false, me = this, blurring = 1;
function close() {
if (closed) return;
closed = true;
dialog.parentNode.removeChild(dialog);
me.focus();
}
buttons[0].focus();
for (var i = 0; i < buttons.length; ++i) {
var b = buttons[i];
(function(callback) {
CodeMirror.on(b, "click", function(e) {
CodeMirror.e_preventDefault(e);
close();
if (callback) callback(me);
});
})(callbacks[i]);
CodeMirror.on(b, "blur", function() {
--blurring;
setTimeout(function() { if (blurring <= 0) close(); }, 200);
});
CodeMirror.on(b, "focus", function() { ++blurring; });
}
});
/*
* openNotification
* Opens a notification, that can be closed with an optional timer
* (default 5000ms timer) and always closes on click.
*
* If a notification is opened while another is opened, it will close the
* currently opened one and open the new one immediately.
*/
CodeMirror.defineExtension("openNotification", function(template, options) {
closeNotification(this, close);
var dialog = dialogDiv(this, template, options && options.bottom);
var closed = false, doneTimer;
var duration = options && typeof options.duration !== "undefined" ? options.duration : 5000;
function close() {
if (closed) return;
closed = true;
clearTimeout(doneTimer);
dialog.parentNode.removeChild(dialog);
}
CodeMirror.on(dialog, 'click', function(e) {
CodeMirror.e_preventDefault(e);
close();
});
if (duration)
doneTimer = setTimeout(close, duration);
return close;
});
});

View File

@ -0,0 +1,47 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"))
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod)
else // Plain browser env
mod(CodeMirror)
})(function(CodeMirror) {
"use strict"
CodeMirror.defineOption("autoRefresh", false, function(cm, val) {
if (cm.state.autoRefresh) {
stopListening(cm, cm.state.autoRefresh)
cm.state.autoRefresh = null
}
if (val && cm.display.wrapper.offsetHeight == 0)
startListening(cm, cm.state.autoRefresh = {delay: val.delay || 250})
})
function startListening(cm, state) {
function check() {
if (cm.display.wrapper.offsetHeight) {
stopListening(cm, state)
if (cm.display.lastWrapHeight != cm.display.wrapper.clientHeight)
cm.refresh()
} else {
state.timeout = setTimeout(check, state.delay)
}
}
state.timeout = setTimeout(check, state.delay)
state.hurry = function() {
clearTimeout(state.timeout)
state.timeout = setTimeout(check, 50)
}
CodeMirror.on(window, "mouseup", state.hurry)
CodeMirror.on(window, "keyup", state.hurry)
}
function stopListening(_cm, state) {
clearTimeout(state.timeout)
CodeMirror.off(window, "mouseup", state.hurry)
CodeMirror.off(window, "keyup", state.hurry)
}
});

View File

@ -0,0 +1,6 @@
.CodeMirror-fullscreen {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
height: auto;
z-index: 9;
}

View File

@ -0,0 +1,41 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
CodeMirror.defineOption("fullScreen", false, function(cm, val, old) {
if (old == CodeMirror.Init) old = false;
if (!old == !val) return;
if (val) setFullscreen(cm);
else setNormal(cm);
});
function setFullscreen(cm) {
var wrap = cm.getWrapperElement();
cm.state.fullScreenRestore = {scrollTop: window.pageYOffset, scrollLeft: window.pageXOffset,
width: wrap.style.width, height: wrap.style.height};
wrap.style.width = "";
wrap.style.height = "auto";
wrap.className += " CodeMirror-fullscreen";
document.documentElement.style.overflow = "hidden";
cm.refresh();
}
function setNormal(cm) {
var wrap = cm.getWrapperElement();
wrap.className = wrap.className.replace(/\s*CodeMirror-fullscreen\b/, "");
document.documentElement.style.overflow = "";
var info = cm.state.fullScreenRestore;
wrap.style.width = info.width; wrap.style.height = info.height;
window.scrollTo(info.scrollLeft, info.scrollTop);
cm.refresh();
}
});

123
public/codemirror/addon/display/panel.js vendored Normal file
View File

@ -0,0 +1,123 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
CodeMirror.defineExtension("addPanel", function(node, options) {
options = options || {};
if (!this.state.panels) initPanels(this);
var info = this.state.panels;
var wrapper = info.wrapper;
var cmWrapper = this.getWrapperElement();
if (options.after instanceof Panel && !options.after.cleared) {
wrapper.insertBefore(node, options.before.node.nextSibling);
} else if (options.before instanceof Panel && !options.before.cleared) {
wrapper.insertBefore(node, options.before.node);
} else if (options.replace instanceof Panel && !options.replace.cleared) {
wrapper.insertBefore(node, options.replace.node);
options.replace.clear();
} else if (options.position == "bottom") {
wrapper.appendChild(node);
} else if (options.position == "before-bottom") {
wrapper.insertBefore(node, cmWrapper.nextSibling);
} else if (options.position == "after-top") {
wrapper.insertBefore(node, cmWrapper);
} else {
wrapper.insertBefore(node, wrapper.firstChild);
}
var height = (options && options.height) || node.offsetHeight;
this._setSize(null, info.heightLeft -= height);
info.panels++;
if (options.stable && isAtTop(this, node))
this.scrollTo(null, this.getScrollInfo().top + height)
return new Panel(this, node, options, height);
});
function Panel(cm, node, options, height) {
this.cm = cm;
this.node = node;
this.options = options;
this.height = height;
this.cleared = false;
}
Panel.prototype.clear = function() {
if (this.cleared) return;
this.cleared = true;
var info = this.cm.state.panels;
this.cm._setSize(null, info.heightLeft += this.height);
if (this.options.stable && isAtTop(this.cm, this.node))
this.cm.scrollTo(null, this.cm.getScrollInfo().top - this.height)
info.wrapper.removeChild(this.node);
if (--info.panels == 0) removePanels(this.cm);
};
Panel.prototype.changed = function(height) {
var newHeight = height == null ? this.node.offsetHeight : height;
var info = this.cm.state.panels;
this.cm._setSize(null, info.height += (newHeight - this.height));
this.height = newHeight;
};
function initPanels(cm) {
var wrap = cm.getWrapperElement();
var style = window.getComputedStyle ? window.getComputedStyle(wrap) : wrap.currentStyle;
var height = parseInt(style.height);
var info = cm.state.panels = {
setHeight: wrap.style.height,
heightLeft: height,
panels: 0,
wrapper: document.createElement("div")
};
wrap.parentNode.insertBefore(info.wrapper, wrap);
var hasFocus = cm.hasFocus();
info.wrapper.appendChild(wrap);
if (hasFocus) cm.focus();
cm._setSize = cm.setSize;
if (height != null) cm.setSize = function(width, newHeight) {
if (newHeight == null) return this._setSize(width, newHeight);
info.setHeight = newHeight;
if (typeof newHeight != "number") {
var px = /^(\d+\.?\d*)px$/.exec(newHeight);
if (px) {
newHeight = Number(px[1]);
} else {
info.wrapper.style.height = newHeight;
newHeight = info.wrapper.offsetHeight;
info.wrapper.style.height = "";
}
}
cm._setSize(width, info.heightLeft += (newHeight - height));
height = newHeight;
};
}
function removePanels(cm) {
var info = cm.state.panels;
cm.state.panels = null;
var wrap = cm.getWrapperElement();
info.wrapper.parentNode.replaceChild(wrap, info.wrapper);
wrap.style.height = info.setHeight;
cm.setSize = cm._setSize;
cm.setSize();
}
function isAtTop(cm, dom) {
for (var sibling = dom.nextSibling; sibling; sibling = sibling.nextSibling)
if (sibling == cm.getWrapperElement()) return true
return false
}
});

View File

@ -0,0 +1,62 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
CodeMirror.defineOption("placeholder", "", function(cm, val, old) {
var prev = old && old != CodeMirror.Init;
if (val && !prev) {
cm.on("blur", onBlur);
cm.on("change", onChange);
cm.on("swapDoc", onChange);
onChange(cm);
} else if (!val && prev) {
cm.off("blur", onBlur);
cm.off("change", onChange);
cm.off("swapDoc", onChange);
clearPlaceholder(cm);
var wrapper = cm.getWrapperElement();
wrapper.className = wrapper.className.replace(" CodeMirror-empty", "");
}
if (val && !cm.hasFocus()) onBlur(cm);
});
function clearPlaceholder(cm) {
if (cm.state.placeholder) {
cm.state.placeholder.parentNode.removeChild(cm.state.placeholder);
cm.state.placeholder = null;
}
}
function setPlaceholder(cm) {
clearPlaceholder(cm);
var elt = cm.state.placeholder = document.createElement("pre");
elt.style.cssText = "height: 0; overflow: visible";
elt.className = "CodeMirror-placeholder";
var placeHolder = cm.getOption("placeholder")
if (typeof placeHolder == "string") placeHolder = document.createTextNode(placeHolder)
elt.appendChild(placeHolder)
cm.display.lineSpace.insertBefore(elt, cm.display.lineSpace.firstChild);
}
function onBlur(cm) {
if (isEmpty(cm)) setPlaceholder(cm);
}
function onChange(cm) {
var wrapper = cm.getWrapperElement(), empty = isEmpty(cm);
wrapper.className = wrapper.className.replace(" CodeMirror-empty", "") + (empty ? " CodeMirror-empty" : "");
if (empty) setPlaceholder(cm);
else clearPlaceholder(cm);
}
function isEmpty(cm) {
return (cm.lineCount() === 1) && (cm.getLine(0) === "");
}
});

View File

@ -0,0 +1,51 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
CodeMirror.defineOption("rulers", false, function(cm, val) {
if (cm.state.rulerDiv) {
cm.state.rulerDiv.parentElement.removeChild(cm.state.rulerDiv)
cm.state.rulerDiv = null
cm.off("refresh", drawRulers)
}
if (val && val.length) {
cm.state.rulerDiv = cm.display.lineSpace.parentElement.insertBefore(document.createElement("div"), cm.display.lineSpace)
cm.state.rulerDiv.className = "CodeMirror-rulers"
drawRulers(cm)
cm.on("refresh", drawRulers)
}
});
function drawRulers(cm) {
cm.state.rulerDiv.textContent = ""
var val = cm.getOption("rulers");
var cw = cm.defaultCharWidth();
var left = cm.charCoords(CodeMirror.Pos(cm.firstLine(), 0), "div").left;
cm.state.rulerDiv.style.minHeight = (cm.display.scroller.offsetHeight + 30) + "px";
for (var i = 0; i < val.length; i++) {
var elt = document.createElement("div");
elt.className = "CodeMirror-ruler";
var col, conf = val[i];
if (typeof conf == "number") {
col = conf;
} else {
col = conf.column;
if (conf.className) elt.className += " " + conf.className;
if (conf.color) elt.style.borderColor = conf.color;
if (conf.lineStyle) elt.style.borderLeftStyle = conf.lineStyle;
if (conf.width) elt.style.borderLeftWidth = conf.width;
}
elt.style.left = (left + col * cw) + "px";
cm.state.rulerDiv.appendChild(elt)
}
}
});

View File

@ -0,0 +1,202 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
var defaults = {
pairs: "()[]{}''\"\"",
triples: "",
explode: "[]{}"
};
var Pos = CodeMirror.Pos;
CodeMirror.defineOption("autoCloseBrackets", false, function(cm, val, old) {
if (old && old != CodeMirror.Init) {
cm.removeKeyMap(keyMap);
cm.state.closeBrackets = null;
}
if (val) {
cm.state.closeBrackets = val;
cm.addKeyMap(keyMap);
}
});
function getOption(conf, name) {
if (name == "pairs" && typeof conf == "string") return conf;
if (typeof conf == "object" && conf[name] != null) return conf[name];
return defaults[name];
}
var bind = defaults.pairs + "`";
var keyMap = {Backspace: handleBackspace, Enter: handleEnter};
for (var i = 0; i < bind.length; i++)
keyMap["'" + bind.charAt(i) + "'"] = handler(bind.charAt(i));
function handler(ch) {
return function(cm) { return handleChar(cm, ch); };
}
function getConfig(cm) {
var deflt = cm.state.closeBrackets;
if (!deflt || deflt.override) return deflt;
var mode = cm.getModeAt(cm.getCursor());
return mode.closeBrackets || deflt;
}
function handleBackspace(cm) {
var conf = getConfig(cm);
if (!conf || cm.getOption("disableInput")) return CodeMirror.Pass;
var pairs = getOption(conf, "pairs");
var ranges = cm.listSelections();
for (var i = 0; i < ranges.length; i++) {
if (!ranges[i].empty()) return CodeMirror.Pass;
var around = charsAround(cm, ranges[i].head);
if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass;
}
for (var i = ranges.length - 1; i >= 0; i--) {
var cur = ranges[i].head;
cm.replaceRange("", Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1), "+delete");
}
}
function handleEnter(cm) {
var conf = getConfig(cm);
var explode = conf && getOption(conf, "explode");
if (!explode || cm.getOption("disableInput")) return CodeMirror.Pass;
var ranges = cm.listSelections();
for (var i = 0; i < ranges.length; i++) {
if (!ranges[i].empty()) return CodeMirror.Pass;
var around = charsAround(cm, ranges[i].head);
if (!around || explode.indexOf(around) % 2 != 0) return CodeMirror.Pass;
}
cm.operation(function() {
cm.replaceSelection("\n\n", null);
cm.execCommand("goCharLeft");
ranges = cm.listSelections();
for (var i = 0; i < ranges.length; i++) {
var line = ranges[i].head.line;
cm.indentLine(line, null, true);
cm.indentLine(line + 1, null, true);
}
});
}
function contractSelection(sel) {
var inverted = CodeMirror.cmpPos(sel.anchor, sel.head) > 0;
return {anchor: new Pos(sel.anchor.line, sel.anchor.ch + (inverted ? -1 : 1)),
head: new Pos(sel.head.line, sel.head.ch + (inverted ? 1 : -1))};
}
function handleChar(cm, ch) {
var conf = getConfig(cm);
if (!conf || cm.getOption("disableInput")) return CodeMirror.Pass;
var pairs = getOption(conf, "pairs");
var pos = pairs.indexOf(ch);
if (pos == -1) return CodeMirror.Pass;
var triples = getOption(conf, "triples");
var identical = pairs.charAt(pos + 1) == ch;
var ranges = cm.listSelections();
var opening = pos % 2 == 0;
var type;
for (var i = 0; i < ranges.length; i++) {
var range = ranges[i], cur = range.head, curType;
var next = cm.getRange(cur, Pos(cur.line, cur.ch + 1));
if (opening && !range.empty()) {
curType = "surround";
} else if ((identical || !opening) && next == ch) {
if (identical && stringStartsAfter(cm, cur))
curType = "both";
else if (triples.indexOf(ch) >= 0 && cm.getRange(cur, Pos(cur.line, cur.ch + 3)) == ch + ch + ch)
curType = "skipThree";
else
curType = "skip";
} else if (identical && cur.ch > 1 && triples.indexOf(ch) >= 0 &&
cm.getRange(Pos(cur.line, cur.ch - 2), cur) == ch + ch &&
(cur.ch <= 2 || cm.getRange(Pos(cur.line, cur.ch - 3), Pos(cur.line, cur.ch - 2)) != ch)) {
curType = "addFour";
} else if (identical) {
if (!CodeMirror.isWordChar(next) && enteringString(cm, cur, ch)) curType = "both";
else return CodeMirror.Pass;
} else if (opening && (cm.getLine(cur.line).length == cur.ch ||
isClosingBracket(next, pairs) ||
/\s/.test(next))) {
curType = "both";
} else {
return CodeMirror.Pass;
}
if (!type) type = curType;
else if (type != curType) return CodeMirror.Pass;
}
var left = pos % 2 ? pairs.charAt(pos - 1) : ch;
var right = pos % 2 ? ch : pairs.charAt(pos + 1);
cm.operation(function() {
if (type == "skip") {
cm.execCommand("goCharRight");
} else if (type == "skipThree") {
for (var i = 0; i < 3; i++)
cm.execCommand("goCharRight");
} else if (type == "surround") {
var sels = cm.getSelections();
for (var i = 0; i < sels.length; i++)
sels[i] = left + sels[i] + right;
cm.replaceSelections(sels, "around");
sels = cm.listSelections().slice();
for (var i = 0; i < sels.length; i++)
sels[i] = contractSelection(sels[i]);
cm.setSelections(sels);
} else if (type == "both") {
cm.replaceSelection(left + right, null);
cm.triggerElectric(left + right);
cm.execCommand("goCharLeft");
} else if (type == "addFour") {
cm.replaceSelection(left + left + left + left, "before");
cm.execCommand("goCharRight");
}
});
}
function isClosingBracket(ch, pairs) {
var pos = pairs.lastIndexOf(ch);
return pos > -1 && pos % 2 == 1;
}
function charsAround(cm, pos) {
var str = cm.getRange(Pos(pos.line, pos.ch - 1),
Pos(pos.line, pos.ch + 1));
return str.length == 2 ? str : null;
}
// Project the token type that will exists after the given char is
// typed, and use it to determine whether it would cause the start
// of a string token.
function enteringString(cm, pos, ch) {
var line = cm.getLine(pos.line);
var token = cm.getTokenAt(pos);
if (/\bstring2?\b/.test(token.type) || stringStartsAfter(cm, pos)) return false;
var stream = new CodeMirror.StringStream(line.slice(0, pos.ch) + ch + line.slice(pos.ch), 4);
stream.pos = stream.start = token.start;
for (;;) {
var type1 = cm.getMode().token(stream, token.state);
if (stream.pos >= pos.ch + 1) return /\bstring2?\b/.test(type1);
stream.start = stream.pos;
}
}
function stringStartsAfter(cm, pos) {
var token = cm.getTokenAt(Pos(pos.line, pos.ch + 1))
return /\bstring/.test(token.type) && token.start == pos.ch
}
});

169
public/codemirror/addon/edit/closetag.js vendored Normal file
View File

@ -0,0 +1,169 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
/**
* Tag-closer extension for CodeMirror.
*
* This extension adds an "autoCloseTags" option that can be set to
* either true to get the default behavior, or an object to further
* configure its behavior.
*
* These are supported options:
*
* `whenClosing` (default true)
* Whether to autoclose when the '/' of a closing tag is typed.
* `whenOpening` (default true)
* Whether to autoclose the tag when the final '>' of an opening
* tag is typed.
* `dontCloseTags` (default is empty tags for HTML, none for XML)
* An array of tag names that should not be autoclosed.
* `indentTags` (default is block tags for HTML, none for XML)
* An array of tag names that should, when opened, cause a
* blank line to be added inside the tag, and the blank line and
* closing line to be indented.
*
* See demos/closetag.html for a usage example.
*/
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"), require("../fold/xml-fold"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror", "../fold/xml-fold"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
CodeMirror.defineOption("autoCloseTags", false, function(cm, val, old) {
if (old != CodeMirror.Init && old)
cm.removeKeyMap("autoCloseTags");
if (!val) return;
var map = {name: "autoCloseTags"};
if (typeof val != "object" || val.whenClosing)
map["'/'"] = function(cm) { return autoCloseSlash(cm); };
if (typeof val != "object" || val.whenOpening)
map["'>'"] = function(cm) { return autoCloseGT(cm); };
cm.addKeyMap(map);
});
var htmlDontClose = ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param",
"source", "track", "wbr"];
var htmlIndent = ["applet", "blockquote", "body", "button", "div", "dl", "fieldset", "form", "frameset", "h1", "h2", "h3", "h4",
"h5", "h6", "head", "html", "iframe", "layer", "legend", "object", "ol", "p", "select", "table", "ul"];
function autoCloseGT(cm) {
if (cm.getOption("disableInput")) return CodeMirror.Pass;
var ranges = cm.listSelections(), replacements = [];
for (var i = 0; i < ranges.length; i++) {
if (!ranges[i].empty()) return CodeMirror.Pass;
var pos = ranges[i].head, tok = cm.getTokenAt(pos);
var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
if (inner.mode.name != "xml" || !state.tagName) return CodeMirror.Pass;
var opt = cm.getOption("autoCloseTags"), html = inner.mode.configuration == "html";
var dontCloseTags = (typeof opt == "object" && opt.dontCloseTags) || (html && htmlDontClose);
var indentTags = (typeof opt == "object" && opt.indentTags) || (html && htmlIndent);
var tagName = state.tagName;
if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch);
var lowerTagName = tagName.toLowerCase();
// Don't process the '>' at the end of an end-tag or self-closing tag
if (!tagName ||
tok.type == "string" && (tok.end != pos.ch || !/[\"\']/.test(tok.string.charAt(tok.string.length - 1)) || tok.string.length == 1) ||
tok.type == "tag" && state.type == "closeTag" ||
tok.string.indexOf("/") == (tok.string.length - 1) || // match something like <someTagName />
dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1 ||
closingTagExists(cm, tagName, pos, state, true))
return CodeMirror.Pass;
var indent = indentTags && indexOf(indentTags, lowerTagName) > -1;
replacements[i] = {indent: indent,
text: ">" + (indent ? "\n\n" : "") + "</" + tagName + ">",
newPos: indent ? CodeMirror.Pos(pos.line + 1, 0) : CodeMirror.Pos(pos.line, pos.ch + 1)};
}
for (var i = ranges.length - 1; i >= 0; i--) {
var info = replacements[i];
cm.replaceRange(info.text, ranges[i].head, ranges[i].anchor, "+insert");
var sel = cm.listSelections().slice(0);
sel[i] = {head: info.newPos, anchor: info.newPos};
cm.setSelections(sel);
if (info.indent) {
cm.indentLine(info.newPos.line, null, true);
cm.indentLine(info.newPos.line + 1, null, true);
}
}
}
function autoCloseCurrent(cm, typingSlash) {
var ranges = cm.listSelections(), replacements = [];
var head = typingSlash ? "/" : "</";
for (var i = 0; i < ranges.length; i++) {
if (!ranges[i].empty()) return CodeMirror.Pass;
var pos = ranges[i].head, tok = cm.getTokenAt(pos);
var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
if (typingSlash && (tok.type == "string" || tok.string.charAt(0) != "<" ||
tok.start != pos.ch - 1))
return CodeMirror.Pass;
// Kludge to get around the fact that we are not in XML mode
// when completing in JS/CSS snippet in htmlmixed mode. Does not
// work for other XML embedded languages (there is no general
// way to go from a mixed mode to its current XML state).
var replacement;
if (inner.mode.name != "xml") {
if (cm.getMode().name == "htmlmixed" && inner.mode.name == "javascript")
replacement = head + "script";
else if (cm.getMode().name == "htmlmixed" && inner.mode.name == "css")
replacement = head + "style";
else
return CodeMirror.Pass;
} else {
if (!state.context || !state.context.tagName ||
closingTagExists(cm, state.context.tagName, pos, state))
return CodeMirror.Pass;
replacement = head + state.context.tagName;
}
if (cm.getLine(pos.line).charAt(tok.end) != ">") replacement += ">";
replacements[i] = replacement;
}
cm.replaceSelections(replacements);
ranges = cm.listSelections();
for (var i = 0; i < ranges.length; i++)
if (i == ranges.length - 1 || ranges[i].head.line < ranges[i + 1].head.line)
cm.indentLine(ranges[i].head.line);
}
function autoCloseSlash(cm) {
if (cm.getOption("disableInput")) return CodeMirror.Pass;
return autoCloseCurrent(cm, true);
}
CodeMirror.commands.closeTag = function(cm) { return autoCloseCurrent(cm); };
function indexOf(collection, elt) {
if (collection.indexOf) return collection.indexOf(elt);
for (var i = 0, e = collection.length; i < e; ++i)
if (collection[i] == elt) return i;
return -1;
}
// If xml-fold is loaded, we use its functionality to try and verify
// whether a given tag is actually unclosed.
function closingTagExists(cm, tagName, pos, state, newTag) {
if (!CodeMirror.scanForClosingTag) return false;
var end = Math.min(cm.lastLine() + 1, pos.line + 500);
var nextClose = CodeMirror.scanForClosingTag(cm, pos, null, end);
if (!nextClose || nextClose.tag != tagName) return false;
var cx = state.context;
// If the immediate wrapping context contains onCx instances of
// the same tag, a closing tag only exists if there are at least
// that many closing tags of that type following.
for (var onCx = newTag ? 1 : 0; cx && cx.tagName == tagName; cx = cx.prev) ++onCx;
pos = nextClose.to;
for (var i = 1; i < onCx; i++) {
var next = CodeMirror.scanForClosingTag(cm, pos, null, end);
if (!next || next.tag != tagName) return false;
pos = next.to;
}
return true;
}
});

View File

@ -0,0 +1,51 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
var listRE = /^(\s*)(>[> ]*|[*+-] \[[x ]\]\s|[*+-]\s|(\d+)([.)]))(\s*)/,
emptyListRE = /^(\s*)(>[> ]*|[*+-] \[[x ]\]|[*+-]|(\d+)[.)])(\s*)$/,
unorderedListRE = /[*+-]\s/;
CodeMirror.commands.newlineAndIndentContinueMarkdownList = function(cm) {
if (cm.getOption("disableInput")) return CodeMirror.Pass;
var ranges = cm.listSelections(), replacements = [];
for (var i = 0; i < ranges.length; i++) {
var pos = ranges[i].head;
var eolState = cm.getStateAfter(pos.line);
var inList = eolState.list !== false;
var inQuote = eolState.quote !== 0;
var line = cm.getLine(pos.line), match = listRE.exec(line);
if (!ranges[i].empty() || (!inList && !inQuote) || !match) {
cm.execCommand("newlineAndIndent");
return;
}
if (emptyListRE.test(line)) {
if (!/>\s*$/.test(line)) cm.replaceRange("", {
line: pos.line, ch: 0
}, {
line: pos.line, ch: pos.ch + 1
});
replacements[i] = "\n";
} else {
var indent = match[1], after = match[5];
var bullet = unorderedListRE.test(match[2]) || match[2].indexOf(">") >= 0
? match[2].replace("x", " ")
: (parseInt(match[3], 10) + 1) + match[4];
replacements[i] = "\n" + indent + bullet + after;
}
}
cm.replaceSelections(replacements);
};
});

View File

@ -0,0 +1,122 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
var ie_lt8 = /MSIE \d/.test(navigator.userAgent) &&
(document.documentMode == null || document.documentMode < 8);
var Pos = CodeMirror.Pos;
var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
function findMatchingBracket(cm, where, strict, config) {
var line = cm.getLineHandle(where.line), pos = where.ch - 1;
var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
if (!match) return null;
var dir = match.charAt(1) == ">" ? 1 : -1;
if (strict && (dir > 0) != (pos == where.ch)) return null;
var style = cm.getTokenTypeAt(Pos(where.line, pos + 1));
var found = scanForBracket(cm, Pos(where.line, pos + (dir > 0 ? 1 : 0)), dir, style || null, config);
if (found == null) return null;
return {from: Pos(where.line, pos), to: found && found.pos,
match: found && found.ch == match.charAt(0), forward: dir > 0};
}
// bracketRegex is used to specify which type of bracket to scan
// should be a regexp, e.g. /[[\]]/
//
// Note: If "where" is on an open bracket, then this bracket is ignored.
//
// Returns false when no bracket was found, null when it reached
// maxScanLines and gave up
function scanForBracket(cm, where, dir, style, config) {
var maxScanLen = (config && config.maxScanLineLength) || 10000;
var maxScanLines = (config && config.maxScanLines) || 1000;
var stack = [];
var re = config && config.bracketRegex ? config.bracketRegex : /[(){}[\]]/;
var lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1)
: Math.max(cm.firstLine() - 1, where.line - maxScanLines);
for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) {
var line = cm.getLine(lineNo);
if (!line) continue;
var pos = dir > 0 ? 0 : line.length - 1, end = dir > 0 ? line.length : -1;
if (line.length > maxScanLen) continue;
if (lineNo == where.line) pos = where.ch - (dir < 0 ? 1 : 0);
for (; pos != end; pos += dir) {
var ch = line.charAt(pos);
if (re.test(ch) && (style === undefined || cm.getTokenTypeAt(Pos(lineNo, pos + 1)) == style)) {
var match = matching[ch];
if ((match.charAt(1) == ">") == (dir > 0)) stack.push(ch);
else if (!stack.length) return {pos: Pos(lineNo, pos), ch: ch};
else stack.pop();
}
}
}
return lineNo - dir == (dir > 0 ? cm.lastLine() : cm.firstLine()) ? false : null;
}
function matchBrackets(cm, autoclear, config) {
// Disable brace matching in long lines, since it'll cause hugely slow updates
var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000;
var marks = [], ranges = cm.listSelections();
for (var i = 0; i < ranges.length; i++) {
var match = ranges[i].empty() && findMatchingBracket(cm, ranges[i].head, false, config);
if (match && cm.getLine(match.from.line).length <= maxHighlightLen) {
var style = match.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
marks.push(cm.markText(match.from, Pos(match.from.line, match.from.ch + 1), {className: style}));
if (match.to && cm.getLine(match.to.line).length <= maxHighlightLen)
marks.push(cm.markText(match.to, Pos(match.to.line, match.to.ch + 1), {className: style}));
}
}
if (marks.length) {
// Kludge to work around the IE bug from issue #1193, where text
// input stops going to the textare whever this fires.
if (ie_lt8 && cm.state.focused) cm.focus();
var clear = function() {
cm.operation(function() {
for (var i = 0; i < marks.length; i++) marks[i].clear();
});
};
if (autoclear) setTimeout(clear, 800);
else return clear;
}
}
var currentlyHighlighted = null;
function doMatchBrackets(cm) {
cm.operation(function() {
if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;}
currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets);
});
}
CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) {
if (old && old != CodeMirror.Init) {
cm.off("cursorActivity", doMatchBrackets);
if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;}
}
if (val) {
cm.state.matchBrackets = typeof val == "object" ? val : {};
cm.on("cursorActivity", doMatchBrackets);
}
});
CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);});
CodeMirror.defineExtension("findMatchingBracket", function(pos, strict, config){
return findMatchingBracket(this, pos, strict, config);
});
CodeMirror.defineExtension("scanForBracket", function(pos, dir, style, config){
return scanForBracket(this, pos, dir, style, config);
});
});

View File

@ -0,0 +1,66 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"), require("../fold/xml-fold"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror", "../fold/xml-fold"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
CodeMirror.defineOption("matchTags", false, function(cm, val, old) {
if (old && old != CodeMirror.Init) {
cm.off("cursorActivity", doMatchTags);
cm.off("viewportChange", maybeUpdateMatch);
clear(cm);
}
if (val) {
cm.state.matchBothTags = typeof val == "object" && val.bothTags;
cm.on("cursorActivity", doMatchTags);
cm.on("viewportChange", maybeUpdateMatch);
doMatchTags(cm);
}
});
function clear(cm) {
if (cm.state.tagHit) cm.state.tagHit.clear();
if (cm.state.tagOther) cm.state.tagOther.clear();
cm.state.tagHit = cm.state.tagOther = null;
}
function doMatchTags(cm) {
cm.state.failedTagMatch = false;
cm.operation(function() {
clear(cm);
if (cm.somethingSelected()) return;
var cur = cm.getCursor(), range = cm.getViewport();
range.from = Math.min(range.from, cur.line); range.to = Math.max(cur.line + 1, range.to);
var match = CodeMirror.findMatchingTag(cm, cur, range);
if (!match) return;
if (cm.state.matchBothTags) {
var hit = match.at == "open" ? match.open : match.close;
if (hit) cm.state.tagHit = cm.markText(hit.from, hit.to, {className: "CodeMirror-matchingtag"});
}
var other = match.at == "close" ? match.open : match.close;
if (other)
cm.state.tagOther = cm.markText(other.from, other.to, {className: "CodeMirror-matchingtag"});
else
cm.state.failedTagMatch = true;
});
}
function maybeUpdateMatch(cm) {
if (cm.state.failedTagMatch) doMatchTags(cm);
}
CodeMirror.commands.toMatchingTag = function(cm) {
var found = CodeMirror.findMatchingTag(cm, cm.getCursor());
if (found) {
var other = found.at == "close" ? found.open : found.close;
if (other) cm.extendSelection(other.to, other.from);
}
};
});

View File

@ -0,0 +1,27 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
CodeMirror.defineOption("showTrailingSpace", false, function(cm, val, prev) {
if (prev == CodeMirror.Init) prev = false;
if (prev && !val)
cm.removeOverlay("trailingspace");
else if (!prev && val)
cm.addOverlay({
token: function(stream) {
for (var l = stream.string.length, i = l; i && /\s/.test(stream.string.charAt(i - 1)); --i) {}
if (i > stream.pos) { stream.pos = i; return null; }
stream.pos = l;
return "trailingspace";
},
name: "trailingspace"
});
});
});

View File

@ -0,0 +1,105 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
CodeMirror.registerHelper("fold", "brace", function(cm, start) {
var line = start.line, lineText = cm.getLine(line);
var tokenType;
function findOpening(openCh) {
for (var at = start.ch, pass = 0;;) {
var found = at <= 0 ? -1 : lineText.lastIndexOf(openCh, at - 1);
if (found == -1) {
if (pass == 1) break;
pass = 1;
at = lineText.length;
continue;
}
if (pass == 1 && found < start.ch) break;
tokenType = cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1));
if (!/^(comment|string)/.test(tokenType)) return found + 1;
at = found - 1;
}
}
var startToken = "{", endToken = "}", startCh = findOpening("{");
if (startCh == null) {
startToken = "[", endToken = "]";
startCh = findOpening("[");
}
if (startCh == null) return;
var count = 1, lastLine = cm.lastLine(), end, endCh;
outer: for (var i = line; i <= lastLine; ++i) {
var text = cm.getLine(i), pos = i == line ? startCh : 0;
for (;;) {
var nextOpen = text.indexOf(startToken, pos), nextClose = text.indexOf(endToken, pos);
if (nextOpen < 0) nextOpen = text.length;
if (nextClose < 0) nextClose = text.length;
pos = Math.min(nextOpen, nextClose);
if (pos == text.length) break;
if (cm.getTokenTypeAt(CodeMirror.Pos(i, pos + 1)) == tokenType) {
if (pos == nextOpen) ++count;
else if (!--count) { end = i; endCh = pos; break outer; }
}
++pos;
}
}
if (end == null || line == end && endCh == startCh) return;
return {from: CodeMirror.Pos(line, startCh),
to: CodeMirror.Pos(end, endCh)};
});
CodeMirror.registerHelper("fold", "import", function(cm, start) {
function hasImport(line) {
if (line < cm.firstLine() || line > cm.lastLine()) return null;
var start = cm.getTokenAt(CodeMirror.Pos(line, 1));
if (!/\S/.test(start.string)) start = cm.getTokenAt(CodeMirror.Pos(line, start.end + 1));
if (start.type != "keyword" || start.string != "import") return null;
// Now find closing semicolon, return its position
for (var i = line, e = Math.min(cm.lastLine(), line + 10); i <= e; ++i) {
var text = cm.getLine(i), semi = text.indexOf(";");
if (semi != -1) return {startCh: start.end, end: CodeMirror.Pos(i, semi)};
}
}
var startLine = start.line, has = hasImport(startLine), prev;
if (!has || hasImport(startLine - 1) || ((prev = hasImport(startLine - 2)) && prev.end.line == startLine - 1))
return null;
for (var end = has.end;;) {
var next = hasImport(end.line + 1);
if (next == null) break;
end = next.end;
}
return {from: cm.clipPos(CodeMirror.Pos(startLine, has.startCh + 1)), to: end};
});
CodeMirror.registerHelper("fold", "include", function(cm, start) {
function hasInclude(line) {
if (line < cm.firstLine() || line > cm.lastLine()) return null;
var start = cm.getTokenAt(CodeMirror.Pos(line, 1));
if (!/\S/.test(start.string)) start = cm.getTokenAt(CodeMirror.Pos(line, start.end + 1));
if (start.type == "meta" && start.string.slice(0, 8) == "#include") return start.start + 8;
}
var startLine = start.line, has = hasInclude(startLine);
if (has == null || hasInclude(startLine - 1) != null) return null;
for (var end = startLine;;) {
var next = hasInclude(end + 1);
if (next == null) break;
++end;
}
return {from: CodeMirror.Pos(startLine, has + 1),
to: cm.clipPos(CodeMirror.Pos(end))};
});
});

View File

@ -0,0 +1,59 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
CodeMirror.registerGlobalHelper("fold", "comment", function(mode) {
return mode.blockCommentStart && mode.blockCommentEnd;
}, function(cm, start) {
var mode = cm.getModeAt(start), startToken = mode.blockCommentStart, endToken = mode.blockCommentEnd;
if (!startToken || !endToken) return;
var line = start.line, lineText = cm.getLine(line);
var startCh;
for (var at = start.ch, pass = 0;;) {
var found = at <= 0 ? -1 : lineText.lastIndexOf(startToken, at - 1);
if (found == -1) {
if (pass == 1) return;
pass = 1;
at = lineText.length;
continue;
}
if (pass == 1 && found < start.ch) return;
if (/comment/.test(cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1))) &&
(found == 0 || lineText.slice(found - endToken.length, found) == endToken ||
!/comment/.test(cm.getTokenTypeAt(CodeMirror.Pos(line, found))))) {
startCh = found + startToken.length;
break;
}
at = found - 1;
}
var depth = 1, lastLine = cm.lastLine(), end, endCh;
outer: for (var i = line; i <= lastLine; ++i) {
var text = cm.getLine(i), pos = i == line ? startCh : 0;
for (;;) {
var nextOpen = text.indexOf(startToken, pos), nextClose = text.indexOf(endToken, pos);
if (nextOpen < 0) nextOpen = text.length;
if (nextClose < 0) nextClose = text.length;
pos = Math.min(nextOpen, nextClose);
if (pos == text.length) break;
if (pos == nextOpen) ++depth;
else if (!--depth) { end = i; endCh = pos; break outer; }
++pos;
}
}
if (end == null || line == end && endCh == startCh) return;
return {from: CodeMirror.Pos(line, startCh),
to: CodeMirror.Pos(end, endCh)};
});
});

150
public/codemirror/addon/fold/foldcode.js vendored Normal file
View File

@ -0,0 +1,150 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
function doFold(cm, pos, options, force) {
if (options && options.call) {
var finder = options;
options = null;
} else {
var finder = getOption(cm, options, "rangeFinder");
}
if (typeof pos == "number") pos = CodeMirror.Pos(pos, 0);
var minSize = getOption(cm, options, "minFoldSize");
function getRange(allowFolded) {
var range = finder(cm, pos);
if (!range || range.to.line - range.from.line < minSize) return null;
var marks = cm.findMarksAt(range.from);
for (var i = 0; i < marks.length; ++i) {
if (marks[i].__isFold && force !== "fold") {
if (!allowFolded) return null;
range.cleared = true;
marks[i].clear();
}
}
return range;
}
var range = getRange(true);
if (getOption(cm, options, "scanUp")) while (!range && pos.line > cm.firstLine()) {
pos = CodeMirror.Pos(pos.line - 1, 0);
range = getRange(false);
}
if (!range || range.cleared || force === "unfold") return;
var myWidget = makeWidget(cm, options);
CodeMirror.on(myWidget, "mousedown", function(e) {
myRange.clear();
CodeMirror.e_preventDefault(e);
});
var myRange = cm.markText(range.from, range.to, {
replacedWith: myWidget,
clearOnEnter: getOption(cm, options, "clearOnEnter"),
__isFold: true
});
myRange.on("clear", function(from, to) {
CodeMirror.signal(cm, "unfold", cm, from, to);
});
CodeMirror.signal(cm, "fold", cm, range.from, range.to);
}
function makeWidget(cm, options) {
var widget = getOption(cm, options, "widget");
if (typeof widget == "string") {
var text = document.createTextNode(widget);
widget = document.createElement("span");
widget.appendChild(text);
widget.className = "CodeMirror-foldmarker";
}
return widget;
}
// Clumsy backwards-compatible interface
CodeMirror.newFoldFunction = function(rangeFinder, widget) {
return function(cm, pos) { doFold(cm, pos, {rangeFinder: rangeFinder, widget: widget}); };
};
// New-style interface
CodeMirror.defineExtension("foldCode", function(pos, options, force) {
doFold(this, pos, options, force);
});
CodeMirror.defineExtension("isFolded", function(pos) {
var marks = this.findMarksAt(pos);
for (var i = 0; i < marks.length; ++i)
if (marks[i].__isFold) return true;
});
CodeMirror.commands.toggleFold = function(cm) {
cm.foldCode(cm.getCursor());
};
CodeMirror.commands.fold = function(cm) {
cm.foldCode(cm.getCursor(), null, "fold");
};
CodeMirror.commands.unfold = function(cm) {
cm.foldCode(cm.getCursor(), null, "unfold");
};
CodeMirror.commands.foldAll = function(cm) {
cm.operation(function() {
for (var i = cm.firstLine(), e = cm.lastLine(); i <= e; i++)
cm.foldCode(CodeMirror.Pos(i, 0), null, "fold");
});
};
CodeMirror.commands.unfoldAll = function(cm) {
cm.operation(function() {
for (var i = cm.firstLine(), e = cm.lastLine(); i <= e; i++)
cm.foldCode(CodeMirror.Pos(i, 0), null, "unfold");
});
};
CodeMirror.registerHelper("fold", "combine", function() {
var funcs = Array.prototype.slice.call(arguments, 0);
return function(cm, start) {
for (var i = 0; i < funcs.length; ++i) {
var found = funcs[i](cm, start);
if (found) return found;
}
};
});
CodeMirror.registerHelper("fold", "auto", function(cm, start) {
var helpers = cm.getHelpers(start, "fold");
for (var i = 0; i < helpers.length; i++) {
var cur = helpers[i](cm, start);
if (cur) return cur;
}
});
var defaultOptions = {
rangeFinder: CodeMirror.fold.auto,
widget: "\u2194",
minFoldSize: 0,
scanUp: false,
clearOnEnter: true
};
CodeMirror.defineOption("foldOptions", null);
function getOption(cm, options, name) {
if (options && options[name] !== undefined)
return options[name];
var editorOptions = cm.options.foldOptions;
if (editorOptions && editorOptions[name] !== undefined)
return editorOptions[name];
return defaultOptions[name];
}
CodeMirror.defineExtension("foldOption", function(options, name) {
return getOption(this, options, name);
});
});

View File

@ -0,0 +1,20 @@
.CodeMirror-foldmarker {
color: blue;
text-shadow: #b9f 1px 1px 2px, #b9f -1px -1px 2px, #b9f 1px -1px 2px, #b9f -1px 1px 2px;
font-family: arial;
line-height: .3;
cursor: pointer;
}
.CodeMirror-foldgutter {
width: .7em;
}
.CodeMirror-foldgutter-open,
.CodeMirror-foldgutter-folded {
cursor: pointer;
}
.CodeMirror-foldgutter-open:after {
content: "\25BE";
}
.CodeMirror-foldgutter-folded:after {
content: "\25B8";
}

View File

@ -0,0 +1,146 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"), require("./foldcode"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror", "./foldcode"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
CodeMirror.defineOption("foldGutter", false, function(cm, val, old) {
if (old && old != CodeMirror.Init) {
cm.clearGutter(cm.state.foldGutter.options.gutter);
cm.state.foldGutter = null;
cm.off("gutterClick", onGutterClick);
cm.off("change", onChange);
cm.off("viewportChange", onViewportChange);
cm.off("fold", onFold);
cm.off("unfold", onFold);
cm.off("swapDoc", onChange);
}
if (val) {
cm.state.foldGutter = new State(parseOptions(val));
updateInViewport(cm);
cm.on("gutterClick", onGutterClick);
cm.on("change", onChange);
cm.on("viewportChange", onViewportChange);
cm.on("fold", onFold);
cm.on("unfold", onFold);
cm.on("swapDoc", onChange);
}
});
var Pos = CodeMirror.Pos;
function State(options) {
this.options = options;
this.from = this.to = 0;
}
function parseOptions(opts) {
if (opts === true) opts = {};
if (opts.gutter == null) opts.gutter = "CodeMirror-foldgutter";
if (opts.indicatorOpen == null) opts.indicatorOpen = "CodeMirror-foldgutter-open";
if (opts.indicatorFolded == null) opts.indicatorFolded = "CodeMirror-foldgutter-folded";
return opts;
}
function isFolded(cm, line) {
var marks = cm.findMarks(Pos(line, 0), Pos(line + 1, 0));
for (var i = 0; i < marks.length; ++i)
if (marks[i].__isFold && marks[i].find().from.line == line) return marks[i];
}
function marker(spec) {
if (typeof spec == "string") {
var elt = document.createElement("div");
elt.className = spec + " CodeMirror-guttermarker-subtle";
return elt;
} else {
return spec.cloneNode(true);
}
}
function updateFoldInfo(cm, from, to) {
var opts = cm.state.foldGutter.options, cur = from;
var minSize = cm.foldOption(opts, "minFoldSize");
var func = cm.foldOption(opts, "rangeFinder");
cm.eachLine(from, to, function(line) {
var mark = null;
if (isFolded(cm, cur)) {
mark = marker(opts.indicatorFolded);
} else {
var pos = Pos(cur, 0);
var range = func && func(cm, pos);
if (range && range.to.line - range.from.line >= minSize)
mark = marker(opts.indicatorOpen);
}
cm.setGutterMarker(line, opts.gutter, mark);
++cur;
});
}
function updateInViewport(cm) {
var vp = cm.getViewport(), state = cm.state.foldGutter;
if (!state) return;
cm.operation(function() {
updateFoldInfo(cm, vp.from, vp.to);
});
state.from = vp.from; state.to = vp.to;
}
function onGutterClick(cm, line, gutter) {
var state = cm.state.foldGutter;
if (!state) return;
var opts = state.options;
if (gutter != opts.gutter) return;
var folded = isFolded(cm, line);
if (folded) folded.clear();
else cm.foldCode(Pos(line, 0), opts.rangeFinder);
}
function onChange(cm) {
var state = cm.state.foldGutter;
if (!state) return;
var opts = state.options;
state.from = state.to = 0;
clearTimeout(state.changeUpdate);
state.changeUpdate = setTimeout(function() { updateInViewport(cm); }, opts.foldOnChangeTimeSpan || 600);
}
function onViewportChange(cm) {
var state = cm.state.foldGutter;
if (!state) return;
var opts = state.options;
clearTimeout(state.changeUpdate);
state.changeUpdate = setTimeout(function() {
var vp = cm.getViewport();
if (state.from == state.to || vp.from - state.to > 20 || state.from - vp.to > 20) {
updateInViewport(cm);
} else {
cm.operation(function() {
if (vp.from < state.from) {
updateFoldInfo(cm, vp.from, state.from);
state.from = vp.from;
}
if (vp.to > state.to) {
updateFoldInfo(cm, state.to, vp.to);
state.to = vp.to;
}
});
}
}, opts.updateViewportTimeSpan || 400);
}
function onFold(cm, from) {
var state = cm.state.foldGutter;
if (!state) return;
var line = from.line;
if (line >= state.from && line < state.to)
updateFoldInfo(cm, line, line + 1);
}
});

View File

@ -0,0 +1,48 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
function lineIndent(cm, lineNo) {
var text = cm.getLine(lineNo)
var spaceTo = text.search(/\S/)
if (spaceTo == -1 || /\bcomment\b/.test(cm.getTokenTypeAt(CodeMirror.Pos(lineNo, spaceTo + 1))))
return -1
return CodeMirror.countColumn(text, null, cm.getOption("tabSize"))
}
!
CodeMirror.registerHelper("fold", "indent", function(cm, start) {
var myIndent = lineIndent(cm, start.line)
if (myIndent < 0) return
var lastLineInFold = null
// Go through lines until we find a line that definitely doesn't belong in
// the block we're folding, or to the end.
for (var i = start.line + 1, end = cm.lastLine(); i <= end; ++i) {
var indent = lineIndent(cm, i)
if (indent == -1) {
} else if (indent > myIndent) {
// Lines with a greater indent are considered part of the block.
lastLineInFold = i;
} else {
// If this line has non-space, non-comment content, and is
// indented less or equal to the start line, it is the start of
// another block.
break;
}
}
if (lastLineInFold) return {
from: CodeMirror.Pos(start.line, cm.getLine(start.line).length),
to: CodeMirror.Pos(lastLineInFold, cm.getLine(lastLineInFold).length)
};
});
});

View File

@ -0,0 +1,49 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
CodeMirror.registerHelper("fold", "markdown", function(cm, start) {
var maxDepth = 100;
function isHeader(lineNo) {
var tokentype = cm.getTokenTypeAt(CodeMirror.Pos(lineNo, 0));
return tokentype && /\bheader\b/.test(tokentype);
}
function headerLevel(lineNo, line, nextLine) {
var match = line && line.match(/^#+/);
if (match && isHeader(lineNo)) return match[0].length;
match = nextLine && nextLine.match(/^[=\-]+\s*$/);
if (match && isHeader(lineNo + 1)) return nextLine[0] == "=" ? 1 : 2;
return maxDepth;
}
var firstLine = cm.getLine(start.line), nextLine = cm.getLine(start.line + 1);
var level = headerLevel(start.line, firstLine, nextLine);
if (level === maxDepth) return undefined;
var lastLineNo = cm.lastLine();
var end = start.line, nextNextLine = cm.getLine(end + 2);
while (end < lastLineNo) {
if (headerLevel(end + 1, nextLine, nextNextLine) <= level) break;
++end;
nextLine = nextNextLine;
nextNextLine = cm.getLine(end + 2);
}
return {
from: CodeMirror.Pos(start.line, firstLine.length),
to: CodeMirror.Pos(end, cm.getLine(end).length)
};
});
});

182
public/codemirror/addon/fold/xml-fold.js vendored Normal file
View File

@ -0,0 +1,182 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
var Pos = CodeMirror.Pos;
function cmp(a, b) { return a.line - b.line || a.ch - b.ch; }
var nameStartChar = "A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD";
var nameChar = nameStartChar + "\-\:\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040";
var xmlTagStart = new RegExp("<(/?)([" + nameStartChar + "][" + nameChar + "]*)", "g");
function Iter(cm, line, ch, range) {
this.line = line; this.ch = ch;
this.cm = cm; this.text = cm.getLine(line);
this.min = range ? Math.max(range.from, cm.firstLine()) : cm.firstLine();
this.max = range ? Math.min(range.to - 1, cm.lastLine()) : cm.lastLine();
}
function tagAt(iter, ch) {
var type = iter.cm.getTokenTypeAt(Pos(iter.line, ch));
return type && /\btag\b/.test(type);
}
function nextLine(iter) {
if (iter.line >= iter.max) return;
iter.ch = 0;
iter.text = iter.cm.getLine(++iter.line);
return true;
}
function prevLine(iter) {
if (iter.line <= iter.min) return;
iter.text = iter.cm.getLine(--iter.line);
iter.ch = iter.text.length;
return true;
}
function toTagEnd(iter) {
for (;;) {
var gt = iter.text.indexOf(">", iter.ch);
if (gt == -1) { if (nextLine(iter)) continue; else return; }
if (!tagAt(iter, gt + 1)) { iter.ch = gt + 1; continue; }
var lastSlash = iter.text.lastIndexOf("/", gt);
var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
iter.ch = gt + 1;
return selfClose ? "selfClose" : "regular";
}
}
function toTagStart(iter) {
for (;;) {
var lt = iter.ch ? iter.text.lastIndexOf("<", iter.ch - 1) : -1;
if (lt == -1) { if (prevLine(iter)) continue; else return; }
if (!tagAt(iter, lt + 1)) { iter.ch = lt; continue; }
xmlTagStart.lastIndex = lt;
iter.ch = lt;
var match = xmlTagStart.exec(iter.text);
if (match && match.index == lt) return match;
}
}
function toNextTag(iter) {
for (;;) {
xmlTagStart.lastIndex = iter.ch;
var found = xmlTagStart.exec(iter.text);
if (!found) { if (nextLine(iter)) continue; else return; }
if (!tagAt(iter, found.index + 1)) { iter.ch = found.index + 1; continue; }
iter.ch = found.index + found[0].length;
return found;
}
}
function toPrevTag(iter) {
for (;;) {
var gt = iter.ch ? iter.text.lastIndexOf(">", iter.ch - 1) : -1;
if (gt == -1) { if (prevLine(iter)) continue; else return; }
if (!tagAt(iter, gt + 1)) { iter.ch = gt; continue; }
var lastSlash = iter.text.lastIndexOf("/", gt);
var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
iter.ch = gt + 1;
return selfClose ? "selfClose" : "regular";
}
}
function findMatchingClose(iter, tag) {
var stack = [];
for (;;) {
var next = toNextTag(iter), end, startLine = iter.line, startCh = iter.ch - (next ? next[0].length : 0);
if (!next || !(end = toTagEnd(iter))) return;
if (end == "selfClose") continue;
if (next[1]) { // closing tag
for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == next[2]) {
stack.length = i;
break;
}
if (i < 0 && (!tag || tag == next[2])) return {
tag: next[2],
from: Pos(startLine, startCh),
to: Pos(iter.line, iter.ch)
};
} else { // opening tag
stack.push(next[2]);
}
}
}
function findMatchingOpen(iter, tag) {
var stack = [];
for (;;) {
var prev = toPrevTag(iter);
if (!prev) return;
if (prev == "selfClose") { toTagStart(iter); continue; }
var endLine = iter.line, endCh = iter.ch;
var start = toTagStart(iter);
if (!start) return;
if (start[1]) { // closing tag
stack.push(start[2]);
} else { // opening tag
for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == start[2]) {
stack.length = i;
break;
}
if (i < 0 && (!tag || tag == start[2])) return {
tag: start[2],
from: Pos(iter.line, iter.ch),
to: Pos(endLine, endCh)
};
}
}
}
CodeMirror.registerHelper("fold", "xml", function(cm, start) {
var iter = new Iter(cm, start.line, 0);
for (;;) {
var openTag = toNextTag(iter), end;
if (!openTag || iter.line != start.line || !(end = toTagEnd(iter))) return;
if (!openTag[1] && end != "selfClose") {
var startPos = Pos(iter.line, iter.ch);
var endPos = findMatchingClose(iter, openTag[2]);
return endPos && {from: startPos, to: endPos.from};
}
}
});
CodeMirror.findMatchingTag = function(cm, pos, range) {
var iter = new Iter(cm, pos.line, pos.ch, range);
if (iter.text.indexOf(">") == -1 && iter.text.indexOf("<") == -1) return;
var end = toTagEnd(iter), to = end && Pos(iter.line, iter.ch);
var start = end && toTagStart(iter);
if (!end || !start || cmp(iter, pos) > 0) return;
var here = {from: Pos(iter.line, iter.ch), to: to, tag: start[2]};
if (end == "selfClose") return {open: here, close: null, at: "open"};
if (start[1]) { // closing tag
return {open: findMatchingOpen(iter, start[2]), close: here, at: "close"};
} else { // opening tag
iter = new Iter(cm, to.line, to.ch, range);
return {open: here, close: findMatchingClose(iter, start[2]), at: "open"};
}
};
CodeMirror.findEnclosingTag = function(cm, pos, range) {
var iter = new Iter(cm, pos.line, pos.ch, range);
for (;;) {
var open = findMatchingOpen(iter);
if (!open) break;
var forward = new Iter(cm, pos.line, pos.ch, range);
var close = findMatchingClose(forward, open.tag);
if (close) return {open: open, close: close};
}
};
// Used by addon/edit/closetag.js
CodeMirror.scanForClosingTag = function(cm, pos, name, end) {
var iter = new Iter(cm, pos.line, pos.ch, end ? {from: 0, to: end} : null);
return findMatchingClose(iter, name);
};
});

View File

@ -0,0 +1,41 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
var WORD = /[\w$]+/, RANGE = 500;
CodeMirror.registerHelper("hint", "anyword", function(editor, options) {
var word = options && options.word || WORD;
var range = options && options.range || RANGE;
var cur = editor.getCursor(), curLine = editor.getLine(cur.line);
var end = cur.ch, start = end;
while (start && word.test(curLine.charAt(start - 1))) --start;
var curWord = start != end && curLine.slice(start, end);
var list = options && options.list || [], seen = {};
var re = new RegExp(word.source, "g");
for (var dir = -1; dir <= 1; dir += 2) {
var line = cur.line, endLine = Math.min(Math.max(line + dir * range, editor.firstLine()), editor.lastLine()) + dir;
for (; line != endLine; line += dir) {
var text = editor.getLine(line), m;
while (m = re.exec(text)) {
if (line == cur.line && m[0] === curWord) continue;
if ((!curWord || m[0].lastIndexOf(curWord, 0) == 0) && !Object.prototype.hasOwnProperty.call(seen, m[0])) {
seen[m[0]] = true;
list.push(m[0]);
}
}
}
}
return {list: list, from: CodeMirror.Pos(cur.line, start), to: CodeMirror.Pos(cur.line, end)};
});
});

View File

@ -0,0 +1,60 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"), require("../../mode/css/css"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror", "../../mode/css/css"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
var pseudoClasses = {link: 1, visited: 1, active: 1, hover: 1, focus: 1,
"first-letter": 1, "first-line": 1, "first-child": 1,
before: 1, after: 1, lang: 1};
CodeMirror.registerHelper("hint", "css", function(cm) {
var cur = cm.getCursor(), token = cm.getTokenAt(cur);
var inner = CodeMirror.innerMode(cm.getMode(), token.state);
if (inner.mode.name != "css") return;
if (token.type == "keyword" && "!important".indexOf(token.string) == 0)
return {list: ["!important"], from: CodeMirror.Pos(cur.line, token.start),
to: CodeMirror.Pos(cur.line, token.end)};
var start = token.start, end = cur.ch, word = token.string.slice(0, end - start);
if (/[^\w$_-]/.test(word)) {
word = ""; start = end = cur.ch;
}
var spec = CodeMirror.resolveMode("text/css");
var result = [];
function add(keywords) {
for (var name in keywords)
if (!word || name.lastIndexOf(word, 0) == 0)
result.push(name);
}
var st = inner.state.state;
if (st == "pseudo" || token.type == "variable-3") {
add(pseudoClasses);
} else if (st == "block" || st == "maybeprop") {
add(spec.propertyKeywords);
} else if (st == "prop" || st == "parens" || st == "at" || st == "params") {
add(spec.valueKeywords);
add(spec.colorKeywords);
} else if (st == "media" || st == "media_parens") {
add(spec.mediaTypes);
add(spec.mediaFeatures);
}
if (result.length) return {
list: result,
from: CodeMirror.Pos(cur.line, start),
to: CodeMirror.Pos(cur.line, end)
};
});
});

View File

@ -0,0 +1,348 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"), require("./xml-hint"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror", "./xml-hint"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
var langs = "ab aa af ak sq am ar an hy as av ae ay az bm ba eu be bn bh bi bs br bg my ca ch ce ny zh cv kw co cr hr cs da dv nl dz en eo et ee fo fj fi fr ff gl ka de el gn gu ht ha he hz hi ho hu ia id ie ga ig ik io is it iu ja jv kl kn kr ks kk km ki rw ky kv kg ko ku kj la lb lg li ln lo lt lu lv gv mk mg ms ml mt mi mr mh mn na nv nb nd ne ng nn no ii nr oc oj cu om or os pa pi fa pl ps pt qu rm rn ro ru sa sc sd se sm sg sr gd sn si sk sl so st es su sw ss sv ta te tg th ti bo tk tl tn to tr ts tt tw ty ug uk ur uz ve vi vo wa cy wo fy xh yi yo za zu".split(" ");
var targets = ["_blank", "_self", "_top", "_parent"];
var charsets = ["ascii", "utf-8", "utf-16", "latin1", "latin1"];
var methods = ["get", "post", "put", "delete"];
var encs = ["application/x-www-form-urlencoded", "multipart/form-data", "text/plain"];
var media = ["all", "screen", "print", "embossed", "braille", "handheld", "print", "projection", "screen", "tty", "tv", "speech",
"3d-glasses", "resolution [>][<][=] [X]", "device-aspect-ratio: X/Y", "orientation:portrait",
"orientation:landscape", "device-height: [X]", "device-width: [X]"];
var s = { attrs: {} }; // Simple tag, reused for a whole lot of tags
var data = {
a: {
attrs: {
href: null, ping: null, type: null,
media: media,
target: targets,
hreflang: langs
}
},
abbr: s,
acronym: s,
address: s,
applet: s,
area: {
attrs: {
alt: null, coords: null, href: null, target: null, ping: null,
media: media, hreflang: langs, type: null,
shape: ["default", "rect", "circle", "poly"]
}
},
article: s,
aside: s,
audio: {
attrs: {
src: null, mediagroup: null,
crossorigin: ["anonymous", "use-credentials"],
preload: ["none", "metadata", "auto"],
autoplay: ["", "autoplay"],
loop: ["", "loop"],
controls: ["", "controls"]
}
},
b: s,
base: { attrs: { href: null, target: targets } },
basefont: s,
bdi: s,
bdo: s,
big: s,
blockquote: { attrs: { cite: null } },
body: s,
br: s,
button: {
attrs: {
form: null, formaction: null, name: null, value: null,
autofocus: ["", "autofocus"],
disabled: ["", "autofocus"],
formenctype: encs,
formmethod: methods,
formnovalidate: ["", "novalidate"],
formtarget: targets,
type: ["submit", "reset", "button"]
}
},
canvas: { attrs: { width: null, height: null } },
caption: s,
center: s,
cite: s,
code: s,
col: { attrs: { span: null } },
colgroup: { attrs: { span: null } },
command: {
attrs: {
type: ["command", "checkbox", "radio"],
label: null, icon: null, radiogroup: null, command: null, title: null,
disabled: ["", "disabled"],
checked: ["", "checked"]
}
},
data: { attrs: { value: null } },
datagrid: { attrs: { disabled: ["", "disabled"], multiple: ["", "multiple"] } },
datalist: { attrs: { data: null } },
dd: s,
del: { attrs: { cite: null, datetime: null } },
details: { attrs: { open: ["", "open"] } },
dfn: s,
dir: s,
div: s,
dl: s,
dt: s,
em: s,
embed: { attrs: { src: null, type: null, width: null, height: null } },
eventsource: { attrs: { src: null } },
fieldset: { attrs: { disabled: ["", "disabled"], form: null, name: null } },
figcaption: s,
figure: s,
font: s,
footer: s,
form: {
attrs: {
action: null, name: null,
"accept-charset": charsets,
autocomplete: ["on", "off"],
enctype: encs,
method: methods,
novalidate: ["", "novalidate"],
target: targets
}
},
frame: s,
frameset: s,
h1: s, h2: s, h3: s, h4: s, h5: s, h6: s,
head: {
attrs: {},
children: ["title", "base", "link", "style", "meta", "script", "noscript", "command"]
},
header: s,
hgroup: s,
hr: s,
html: {
attrs: { manifest: null },
children: ["head", "body"]
},
i: s,
iframe: {
attrs: {
src: null, srcdoc: null, name: null, width: null, height: null,
sandbox: ["allow-top-navigation", "allow-same-origin", "allow-forms", "allow-scripts"],
seamless: ["", "seamless"]
}
},
img: {
attrs: {
alt: null, src: null, ismap: null, usemap: null, width: null, height: null,
crossorigin: ["anonymous", "use-credentials"]
}
},
input: {
attrs: {
alt: null, dirname: null, form: null, formaction: null,
height: null, list: null, max: null, maxlength: null, min: null,
name: null, pattern: null, placeholder: null, size: null, src: null,
step: null, value: null, width: null,
accept: ["audio/*", "video/*", "image/*"],
autocomplete: ["on", "off"],
autofocus: ["", "autofocus"],
checked: ["", "checked"],
disabled: ["", "disabled"],
formenctype: encs,
formmethod: methods,
formnovalidate: ["", "novalidate"],
formtarget: targets,
multiple: ["", "multiple"],
readonly: ["", "readonly"],
required: ["", "required"],
type: ["hidden", "text", "search", "tel", "url", "email", "password", "datetime", "date", "month",
"week", "time", "datetime-local", "number", "range", "color", "checkbox", "radio",
"file", "submit", "image", "reset", "button"]
}
},
ins: { attrs: { cite: null, datetime: null } },
kbd: s,
keygen: {
attrs: {
challenge: null, form: null, name: null,
autofocus: ["", "autofocus"],
disabled: ["", "disabled"],
keytype: ["RSA"]
}
},
label: { attrs: { "for": null, form: null } },
legend: s,
li: { attrs: { value: null } },
link: {
attrs: {
href: null, type: null,
hreflang: langs,
media: media,
sizes: ["all", "16x16", "16x16 32x32", "16x16 32x32 64x64"]
}
},
map: { attrs: { name: null } },
mark: s,
menu: { attrs: { label: null, type: ["list", "context", "toolbar"] } },
meta: {
attrs: {
content: null,
charset: charsets,
name: ["viewport", "application-name", "author", "description", "generator", "keywords"],
"http-equiv": ["content-language", "content-type", "default-style", "refresh"]
}
},
meter: { attrs: { value: null, min: null, low: null, high: null, max: null, optimum: null } },
nav: s,
noframes: s,
noscript: s,
object: {
attrs: {
data: null, type: null, name: null, usemap: null, form: null, width: null, height: null,
typemustmatch: ["", "typemustmatch"]
}
},
ol: { attrs: { reversed: ["", "reversed"], start: null, type: ["1", "a", "A", "i", "I"] } },
optgroup: { attrs: { disabled: ["", "disabled"], label: null } },
option: { attrs: { disabled: ["", "disabled"], label: null, selected: ["", "selected"], value: null } },
output: { attrs: { "for": null, form: null, name: null } },
p: s,
param: { attrs: { name: null, value: null } },
pre: s,
progress: { attrs: { value: null, max: null } },
q: { attrs: { cite: null } },
rp: s,
rt: s,
ruby: s,
s: s,
samp: s,
script: {
attrs: {
type: ["text/javascript"],
src: null,
async: ["", "async"],
defer: ["", "defer"],
charset: charsets
}
},
section: s,
select: {
attrs: {
form: null, name: null, size: null,
autofocus: ["", "autofocus"],
disabled: ["", "disabled"],
multiple: ["", "multiple"]
}
},
small: s,
source: { attrs: { src: null, type: null, media: null } },
span: s,
strike: s,
strong: s,
style: {
attrs: {
type: ["text/css"],
media: media,
scoped: null
}
},
sub: s,
summary: s,
sup: s,
table: s,
tbody: s,
td: { attrs: { colspan: null, rowspan: null, headers: null } },
textarea: {
attrs: {
dirname: null, form: null, maxlength: null, name: null, placeholder: null,
rows: null, cols: null,
autofocus: ["", "autofocus"],
disabled: ["", "disabled"],
readonly: ["", "readonly"],
required: ["", "required"],
wrap: ["soft", "hard"]
}
},
tfoot: s,
th: { attrs: { colspan: null, rowspan: null, headers: null, scope: ["row", "col", "rowgroup", "colgroup"] } },
thead: s,
time: { attrs: { datetime: null } },
title: s,
tr: s,
track: {
attrs: {
src: null, label: null, "default": null,
kind: ["subtitles", "captions", "descriptions", "chapters", "metadata"],
srclang: langs
}
},
tt: s,
u: s,
ul: s,
"var": s,
video: {
attrs: {
src: null, poster: null, width: null, height: null,
crossorigin: ["anonymous", "use-credentials"],
preload: ["auto", "metadata", "none"],
autoplay: ["", "autoplay"],
mediagroup: ["movie"],
muted: ["", "muted"],
controls: ["", "controls"]
}
},
wbr: s
};
var globalAttrs = {
accesskey: ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"],
"class": null,
contenteditable: ["true", "false"],
contextmenu: null,
dir: ["ltr", "rtl", "auto"],
draggable: ["true", "false", "auto"],
dropzone: ["copy", "move", "link", "string:", "file:"],
hidden: ["hidden"],
id: null,
inert: ["inert"],
itemid: null,
itemprop: null,
itemref: null,
itemscope: ["itemscope"],
itemtype: null,
lang: ["en", "es"],
spellcheck: ["true", "false"],
style: null,
tabindex: ["1", "2", "3", "4", "5", "6", "7", "8", "9"],
title: null,
translate: ["yes", "no"],
onclick: null,
rel: ["stylesheet", "alternate", "author", "bookmark", "help", "license", "next", "nofollow", "noreferrer", "prefetch", "prev", "search", "tag"]
};
function populate(obj) {
for (var attr in globalAttrs) if (globalAttrs.hasOwnProperty(attr))
obj.attrs[attr] = globalAttrs[attr];
}
populate(s);
for (var tag in data) if (data.hasOwnProperty(tag) && data[tag] != s)
populate(data[tag]);
CodeMirror.htmlSchema = data;
function htmlHint(cm, options) {
var local = {schemaInfo: data};
if (options) for (var opt in options) local[opt] = options[opt];
return CodeMirror.hint.xml(cm, local);
}
CodeMirror.registerHelper("hint", "html", htmlHint);
});

View File

@ -0,0 +1,155 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
var Pos = CodeMirror.Pos;
function forEach(arr, f) {
for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);
}
function arrayContains(arr, item) {
if (!Array.prototype.indexOf) {
var i = arr.length;
while (i--) {
if (arr[i] === item) {
return true;
}
}
return false;
}
return arr.indexOf(item) != -1;
}
function scriptHint(editor, keywords, getToken, options) {
// Find the token at the cursor
var cur = editor.getCursor(), token = getToken(editor, cur);
if (/\b(?:string|comment)\b/.test(token.type)) return;
token.state = CodeMirror.innerMode(editor.getMode(), token.state).state;
// If it's not a 'word-style' token, ignore the token.
if (!/^[\w$_]*$/.test(token.string)) {
token = {start: cur.ch, end: cur.ch, string: "", state: token.state,
type: token.string == "." ? "property" : null};
} else if (token.end > cur.ch) {
token.end = cur.ch;
token.string = token.string.slice(0, cur.ch - token.start);
}
var tprop = token;
// If it is a property, find out what it is a property of.
while (tprop.type == "property") {
tprop = getToken(editor, Pos(cur.line, tprop.start));
if (tprop.string != ".") return;
tprop = getToken(editor, Pos(cur.line, tprop.start));
if (!context) var context = [];
context.push(tprop);
}
return {list: getCompletions(token, context, keywords, options),
from: Pos(cur.line, token.start),
to: Pos(cur.line, token.end)};
}
function javascriptHint(editor, options) {
return scriptHint(editor, javascriptKeywords,
function (e, cur) {return e.getTokenAt(cur);},
options);
};
CodeMirror.registerHelper("hint", "javascript", javascriptHint);
function getCoffeeScriptToken(editor, cur) {
// This getToken, it is for coffeescript, imitates the behavior of
// getTokenAt method in javascript.js, that is, returning "property"
// type and treat "." as indepenent token.
var token = editor.getTokenAt(cur);
if (cur.ch == token.start + 1 && token.string.charAt(0) == '.') {
token.end = token.start;
token.string = '.';
token.type = "property";
}
else if (/^\.[\w$_]*$/.test(token.string)) {
token.type = "property";
token.start++;
token.string = token.string.replace(/\./, '');
}
return token;
}
function coffeescriptHint(editor, options) {
return scriptHint(editor, coffeescriptKeywords, getCoffeeScriptToken, options);
}
CodeMirror.registerHelper("hint", "coffeescript", coffeescriptHint);
var stringProps = ("charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight " +
"toUpperCase toLowerCase split concat match replace search").split(" ");
var arrayProps = ("length concat join splice push pop shift unshift slice reverse sort indexOf " +
"lastIndexOf every some filter forEach map reduce reduceRight ").split(" ");
var funcProps = "prototype apply call bind".split(" ");
var javascriptKeywords = ("break case catch continue debugger default delete do else false finally for function " +
"if in instanceof new null return switch throw true try typeof var void while with").split(" ");
var coffeescriptKeywords = ("and break catch class continue delete do else extends false finally for " +
"if in instanceof isnt new no not null of off on or return switch then throw true try typeof until void while with yes").split(" ");
function forAllProps(obj, callback) {
if (!Object.getOwnPropertyNames || !Object.getPrototypeOf) {
for (var name in obj) callback(name)
} else {
for (var o = obj; o; o = Object.getPrototypeOf(o))
Object.getOwnPropertyNames(o).forEach(callback)
}
}
function getCompletions(token, context, keywords, options) {
var found = [], start = token.string, global = options && options.globalScope || window;
function maybeAdd(str) {
if (str.lastIndexOf(start, 0) == 0 && !arrayContains(found, str)) found.push(str);
}
function gatherCompletions(obj) {
if (typeof obj == "string") forEach(stringProps, maybeAdd);
else if (obj instanceof Array) forEach(arrayProps, maybeAdd);
else if (obj instanceof Function) forEach(funcProps, maybeAdd);
forAllProps(obj, maybeAdd)
}
if (context && context.length) {
// If this is a property, see if it belongs to some object we can
// find in the current environment.
var obj = context.pop(), base;
if (obj.type && obj.type.indexOf("variable") === 0) {
if (options && options.additionalContext)
base = options.additionalContext[obj.string];
if (!options || options.useGlobalScope !== false)
base = base || global[obj.string];
} else if (obj.type == "string") {
base = "";
} else if (obj.type == "atom") {
base = 1;
} else if (obj.type == "function") {
if (global.jQuery != null && (obj.string == '$' || obj.string == 'jQuery') &&
(typeof global.jQuery == 'function'))
base = global.jQuery();
else if (global._ != null && (obj.string == '_') && (typeof global._ == 'function'))
base = global._();
}
while (base != null && context.length)
base = base[context.pop().string];
if (base != null) gatherCompletions(base);
} else {
// If not, just look in the global object and any local scope
// (reading into JS mode internals to get at the local and global variables)
for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name);
for (var v = token.state.globalVars; v; v = v.next) maybeAdd(v.name);
if (!options || options.useGlobalScope !== false)
gatherCompletions(global);
forEach(keywords, maybeAdd);
}
return found;
}
});

View File

@ -0,0 +1,36 @@
.CodeMirror-hints {
position: absolute;
z-index: 10;
overflow: hidden;
list-style: none;
margin: 0;
padding: 2px;
-webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
-moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
box-shadow: 2px 3px 5px rgba(0,0,0,.2);
border-radius: 3px;
border: 1px solid silver;
background: white;
font-size: 90%;
font-family: monospace;
max-height: 20em;
overflow-y: auto;
}
.CodeMirror-hint {
margin: 0;
padding: 0 4px;
border-radius: 2px;
white-space: pre;
color: black;
cursor: pointer;
}
li.CodeMirror-hint-active {
background: #08f;
color: white;
}

View File

@ -0,0 +1,438 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
var HINT_ELEMENT_CLASS = "CodeMirror-hint";
var ACTIVE_HINT_ELEMENT_CLASS = "CodeMirror-hint-active";
// This is the old interface, kept around for now to stay
// backwards-compatible.
CodeMirror.showHint = function(cm, getHints, options) {
if (!getHints) return cm.showHint(options);
if (options && options.async) getHints.async = true;
var newOpts = {hint: getHints};
if (options) for (var prop in options) newOpts[prop] = options[prop];
return cm.showHint(newOpts);
};
CodeMirror.defineExtension("showHint", function(options) {
options = parseOptions(this, this.getCursor("start"), options);
var selections = this.listSelections()
if (selections.length > 1) return;
// By default, don't allow completion when something is selected.
// A hint function can have a `supportsSelection` property to
// indicate that it can handle selections.
if (this.somethingSelected()) {
if (!options.hint.supportsSelection) return;
// Don't try with cross-line selections
for (var i = 0; i < selections.length; i++)
if (selections[i].head.line != selections[i].anchor.line) return;
}
if (this.state.completionActive) this.state.completionActive.close();
var completion = this.state.completionActive = new Completion(this, options);
if (!completion.options.hint) return;
CodeMirror.signal(this, "startCompletion", this);
completion.update(true);
});
function Completion(cm, options) {
this.cm = cm;
this.options = options;
this.widget = null;
this.debounce = 0;
this.tick = 0;
this.startPos = this.cm.getCursor("start");
this.startLen = this.cm.getLine(this.startPos.line).length - this.cm.getSelection().length;
var self = this;
cm.on("cursorActivity", this.activityFunc = function() { self.cursorActivity(); });
}
var requestAnimationFrame = window.requestAnimationFrame || function(fn) {
return setTimeout(fn, 1000/60);
};
var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout;
Completion.prototype = {
close: function() {
if (!this.active()) return;
this.cm.state.completionActive = null;
this.tick = null;
this.cm.off("cursorActivity", this.activityFunc);
if (this.widget && this.data) CodeMirror.signal(this.data, "close");
if (this.widget) this.widget.close();
CodeMirror.signal(this.cm, "endCompletion", this.cm);
},
active: function() {
return this.cm.state.completionActive == this;
},
pick: function(data, i) {
var completion = data.list[i];
if (completion.hint) completion.hint(this.cm, data, completion);
else this.cm.replaceRange(getText(completion), completion.from || data.from,
completion.to || data.to, "complete");
CodeMirror.signal(data, "pick", completion);
this.close();
},
cursorActivity: function() {
if (this.debounce) {
cancelAnimationFrame(this.debounce);
this.debounce = 0;
}
var pos = this.cm.getCursor(), line = this.cm.getLine(pos.line);
if (pos.line != this.startPos.line || line.length - pos.ch != this.startLen - this.startPos.ch ||
pos.ch < this.startPos.ch || this.cm.somethingSelected() ||
(pos.ch && this.options.closeCharacters.test(line.charAt(pos.ch - 1)))) {
this.close();
} else {
var self = this;
this.debounce = requestAnimationFrame(function() {self.update();});
if (this.widget) this.widget.disable();
}
},
update: function(first) {
if (this.tick == null) return
var self = this, myTick = ++this.tick
fetchHints(this.options.hint, this.cm, this.options, function(data) {
if (self.tick == myTick) self.finishUpdate(data, first)
})
},
finishUpdate: function(data, first) {
if (this.data) CodeMirror.signal(this.data, "update");
var picked = (this.widget && this.widget.picked) || (first && this.options.completeSingle);
if (this.widget) this.widget.close();
if (data && this.data && isNewCompletion(this.data, data)) return;
this.data = data;
if (data && data.list.length) {
if (picked && data.list.length == 1) {
this.pick(data, 0);
} else {
this.widget = new Widget(this, data);
CodeMirror.signal(data, "shown");
}
}
}
};
function isNewCompletion(old, nw) {
var moved = CodeMirror.cmpPos(nw.from, old.from)
return moved > 0 && old.to.ch - old.from.ch != nw.to.ch - nw.from.ch
}
function parseOptions(cm, pos, options) {
var editor = cm.options.hintOptions;
var out = {};
for (var prop in defaultOptions) out[prop] = defaultOptions[prop];
if (editor) for (var prop in editor)
if (editor[prop] !== undefined) out[prop] = editor[prop];
if (options) for (var prop in options)
if (options[prop] !== undefined) out[prop] = options[prop];
if (out.hint.resolve) out.hint = out.hint.resolve(cm, pos)
return out;
}
function getText(completion) {
if (typeof completion == "string") return completion;
else return completion.text;
}
function buildKeyMap(completion, handle) {
var baseMap = {
Up: function() {handle.moveFocus(-1);},
Down: function() {handle.moveFocus(1);},
PageUp: function() {handle.moveFocus(-handle.menuSize() + 1, true);},
PageDown: function() {handle.moveFocus(handle.menuSize() - 1, true);},
Home: function() {handle.setFocus(0);},
End: function() {handle.setFocus(handle.length - 1);},
Enter: handle.pick,
Tab: handle.pick,
Esc: handle.close
};
var custom = completion.options.customKeys;
var ourMap = custom ? {} : baseMap;
function addBinding(key, val) {
var bound;
if (typeof val != "string")
bound = function(cm) { return val(cm, handle); };
// This mechanism is deprecated
else if (baseMap.hasOwnProperty(val))
bound = baseMap[val];
else
bound = val;
ourMap[key] = bound;
}
if (custom)
for (var key in custom) if (custom.hasOwnProperty(key))
addBinding(key, custom[key]);
var extra = completion.options.extraKeys;
if (extra)
for (var key in extra) if (extra.hasOwnProperty(key))
addBinding(key, extra[key]);
return ourMap;
}
function getHintElement(hintsElement, el) {
while (el && el != hintsElement) {
if (el.nodeName.toUpperCase() === "LI" && el.parentNode == hintsElement) return el;
el = el.parentNode;
}
}
function Widget(completion, data) {
this.completion = completion;
this.data = data;
this.picked = false;
var widget = this, cm = completion.cm;
var hints = this.hints = document.createElement("ul");
hints.className = "CodeMirror-hints";
this.selectedHint = data.selectedHint || 0;
var completions = data.list;
for (var i = 0; i < completions.length; ++i) {
var elt = hints.appendChild(document.createElement("li")), cur = completions[i];
var className = HINT_ELEMENT_CLASS + (i != this.selectedHint ? "" : " " + ACTIVE_HINT_ELEMENT_CLASS);
if (cur.className != null) className = cur.className + " " + className;
elt.className = className;
if (cur.render) cur.render(elt, data, cur);
else elt.appendChild(document.createTextNode(cur.displayText || getText(cur)));
elt.hintId = i;
}
var pos = cm.cursorCoords(completion.options.alignWithWord ? data.from : null);
var left = pos.left, top = pos.bottom, below = true;
hints.style.left = left + "px";
hints.style.top = top + "px";
// If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.
var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth);
var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);
(completion.options.container || document.body).appendChild(hints);
var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH;
var scrolls = hints.scrollHeight > hints.clientHeight + 1
var startScroll = cm.getScrollInfo();
if (overlapY > 0) {
var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top);
if (curTop - height > 0) { // Fits above cursor
hints.style.top = (top = pos.top - height) + "px";
below = false;
} else if (height > winH) {
hints.style.height = (winH - 5) + "px";
hints.style.top = (top = pos.bottom - box.top) + "px";
var cursor = cm.getCursor();
if (data.from.ch != cursor.ch) {
pos = cm.cursorCoords(cursor);
hints.style.left = (left = pos.left) + "px";
box = hints.getBoundingClientRect();
}
}
}
var overlapX = box.right - winW;
if (overlapX > 0) {
if (box.right - box.left > winW) {
hints.style.width = (winW - 5) + "px";
overlapX -= (box.right - box.left) - winW;
}
hints.style.left = (left = pos.left - overlapX) + "px";
}
if (scrolls) for (var node = hints.firstChild; node; node = node.nextSibling)
node.style.paddingRight = cm.display.nativeBarWidth + "px"
cm.addKeyMap(this.keyMap = buildKeyMap(completion, {
moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); },
setFocus: function(n) { widget.changeActive(n); },
menuSize: function() { return widget.screenAmount(); },
length: completions.length,
close: function() { completion.close(); },
pick: function() { widget.pick(); },
data: data
}));
if (completion.options.closeOnUnfocus) {
var closingOnBlur;
cm.on("blur", this.onBlur = function() { closingOnBlur = setTimeout(function() { completion.close(); }, 100); });
cm.on("focus", this.onFocus = function() { clearTimeout(closingOnBlur); });
}
cm.on("scroll", this.onScroll = function() {
var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect();
var newTop = top + startScroll.top - curScroll.top;
var point = newTop - (window.pageYOffset || (document.documentElement || document.body).scrollTop);
if (!below) point += hints.offsetHeight;
if (point <= editor.top || point >= editor.bottom) return completion.close();
hints.style.top = newTop + "px";
hints.style.left = (left + startScroll.left - curScroll.left) + "px";
});
CodeMirror.on(hints, "dblclick", function(e) {
var t = getHintElement(hints, e.target || e.srcElement);
if (t && t.hintId != null) {widget.changeActive(t.hintId); widget.pick();}
});
CodeMirror.on(hints, "click", function(e) {
var t = getHintElement(hints, e.target || e.srcElement);
if (t && t.hintId != null) {
widget.changeActive(t.hintId);
if (completion.options.completeOnSingleClick) widget.pick();
}
});
CodeMirror.on(hints, "mousedown", function() {
setTimeout(function(){cm.focus();}, 20);
});
CodeMirror.signal(data, "select", completions[0], hints.firstChild);
return true;
}
Widget.prototype = {
close: function() {
if (this.completion.widget != this) return;
this.completion.widget = null;
this.hints.parentNode.removeChild(this.hints);
this.completion.cm.removeKeyMap(this.keyMap);
var cm = this.completion.cm;
if (this.completion.options.closeOnUnfocus) {
cm.off("blur", this.onBlur);
cm.off("focus", this.onFocus);
}
cm.off("scroll", this.onScroll);
},
disable: function() {
this.completion.cm.removeKeyMap(this.keyMap);
var widget = this;
this.keyMap = {Enter: function() { widget.picked = true; }};
this.completion.cm.addKeyMap(this.keyMap);
},
pick: function() {
this.completion.pick(this.data, this.selectedHint);
},
changeActive: function(i, avoidWrap) {
if (i >= this.data.list.length)
i = avoidWrap ? this.data.list.length - 1 : 0;
else if (i < 0)
i = avoidWrap ? 0 : this.data.list.length - 1;
if (this.selectedHint == i) return;
var node = this.hints.childNodes[this.selectedHint];
node.className = node.className.replace(" " + ACTIVE_HINT_ELEMENT_CLASS, "");
node = this.hints.childNodes[this.selectedHint = i];
node.className += " " + ACTIVE_HINT_ELEMENT_CLASS;
if (node.offsetTop < this.hints.scrollTop)
this.hints.scrollTop = node.offsetTop - 3;
else if (node.offsetTop + node.offsetHeight > this.hints.scrollTop + this.hints.clientHeight)
this.hints.scrollTop = node.offsetTop + node.offsetHeight - this.hints.clientHeight + 3;
CodeMirror.signal(this.data, "select", this.data.list[this.selectedHint], node);
},
screenAmount: function() {
return Math.floor(this.hints.clientHeight / this.hints.firstChild.offsetHeight) || 1;
}
};
function applicableHelpers(cm, helpers) {
if (!cm.somethingSelected()) return helpers
var result = []
for (var i = 0; i < helpers.length; i++)
if (helpers[i].supportsSelection) result.push(helpers[i])
return result
}
function fetchHints(hint, cm, options, callback) {
if (hint.async) {
hint(cm, callback, options)
} else {
var result = hint(cm, options)
if (result && result.then) result.then(callback)
else callback(result)
}
}
function resolveAutoHints(cm, pos) {
var helpers = cm.getHelpers(pos, "hint"), words
if (helpers.length) {
var resolved = function(cm, callback, options) {
var app = applicableHelpers(cm, helpers);
function run(i) {
if (i == app.length) return callback(null)
fetchHints(app[i], cm, options, function(result) {
if (result && result.list.length > 0) callback(result)
else run(i + 1)
})
}
run(0)
}
resolved.async = true
resolved.supportsSelection = true
return resolved
} else if (words = cm.getHelper(cm.getCursor(), "hintWords")) {
return function(cm) { return CodeMirror.hint.fromList(cm, {words: words}) }
} else if (CodeMirror.hint.anyword) {
return function(cm, options) { return CodeMirror.hint.anyword(cm, options) }
} else {
return function() {}
}
}
CodeMirror.registerHelper("hint", "auto", {
resolve: resolveAutoHints
});
CodeMirror.registerHelper("hint", "fromList", function(cm, options) {
var cur = cm.getCursor(), token = cm.getTokenAt(cur);
var to = CodeMirror.Pos(cur.line, token.end);
if (token.string && /\w/.test(token.string[token.string.length - 1])) {
var term = token.string, from = CodeMirror.Pos(cur.line, token.start);
} else {
var term = "", from = to;
}
var found = [];
for (var i = 0; i < options.words.length; i++) {
var word = options.words[i];
if (word.slice(0, term.length) == term)
found.push(word);
}
if (found.length) return {list: found, from: from, to: to};
});
CodeMirror.commands.autocomplete = CodeMirror.showHint;
var defaultOptions = {
hint: CodeMirror.hint.auto,
completeSingle: true,
alignWithWord: true,
closeCharacters: /[\s()\[\]{};:>,]/,
closeOnUnfocus: true,
completeOnSingleClick: true,
container: null,
customKeys: null,
extraKeys: null
};
CodeMirror.defineOption("hintOptions", null);
});

287
public/codemirror/addon/hint/sql-hint.js vendored Normal file
View File

@ -0,0 +1,287 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"), require("../../mode/sql/sql"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror", "../../mode/sql/sql"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
var tables;
var defaultTable;
var keywords;
var identifierQuote;
var CONS = {
QUERY_DIV: ";",
ALIAS_KEYWORD: "AS"
};
var Pos = CodeMirror.Pos, cmpPos = CodeMirror.cmpPos;
function isArray(val) { return Object.prototype.toString.call(val) == "[object Array]" }
function getKeywords(editor) {
var mode = editor.doc.modeOption;
if (mode === "sql") mode = "text/x-sql";
return CodeMirror.resolveMode(mode).keywords;
}
function getIdentifierQuote(editor) {
var mode = editor.doc.modeOption;
if (mode === "sql") mode = "text/x-sql";
return CodeMirror.resolveMode(mode).identifierQuote || "`";
}
function getText(item) {
return typeof item == "string" ? item : item.text;
}
function wrapTable(name, value) {
if (isArray(value)) value = {columns: value}
if (!value.text) value.text = name
return value
}
function parseTables(input) {
var result = {}
if (isArray(input)) {
for (var i = input.length - 1; i >= 0; i--) {
var item = input[i]
result[getText(item).toUpperCase()] = wrapTable(getText(item), item)
}
} else if (input) {
for (var name in input)
result[name.toUpperCase()] = wrapTable(name, input[name])
}
return result
}
function getTable(name) {
return tables[name.toUpperCase()]
}
function shallowClone(object) {
var result = {};
for (var key in object) if (object.hasOwnProperty(key))
result[key] = object[key];
return result;
}
function match(string, word) {
var len = string.length;
var sub = getText(word).substr(0, len);
return string.toUpperCase() === sub.toUpperCase();
}
function addMatches(result, search, wordlist, formatter) {
if (isArray(wordlist)) {
for (var i = 0; i < wordlist.length; i++)
if (match(search, wordlist[i])) result.push(formatter(wordlist[i]))
} else {
for (var word in wordlist) if (wordlist.hasOwnProperty(word)) {
var val = wordlist[word]
if (!val || val === true)
val = word
else
val = val.displayText ? {text: val.text, displayText: val.displayText} : val.text
if (match(search, val)) result.push(formatter(val))
}
}
}
function cleanName(name) {
// Get rid name from identifierQuote and preceding dot(.)
if (name.charAt(0) == ".") {
name = name.substr(1);
}
// replace doublicated identifierQuotes with single identifierQuotes
// and remove single identifierQuotes
var nameParts = name.split(identifierQuote+identifierQuote);
for (var i = 0; i < nameParts.length; i++)
nameParts[i] = nameParts[i].replace(new RegExp(identifierQuote,"g"), "");
return nameParts.join(identifierQuote);
}
function insertIdentifierQuotes(name) {
var nameParts = getText(name).split(".");
for (var i = 0; i < nameParts.length; i++)
nameParts[i] = identifierQuote +
// doublicate identifierQuotes
nameParts[i].replace(new RegExp(identifierQuote,"g"), identifierQuote+identifierQuote) +
identifierQuote;
var escaped = nameParts.join(".");
if (typeof name == "string") return escaped;
name = shallowClone(name);
name.text = escaped;
return name;
}
function nameCompletion(cur, token, result, editor) {
// Try to complete table, column names and return start position of completion
var useIdentifierQuotes = false;
var nameParts = [];
var start = token.start;
var cont = true;
while (cont) {
cont = (token.string.charAt(0) == ".");
useIdentifierQuotes = useIdentifierQuotes || (token.string.charAt(0) == identifierQuote);
start = token.start;
nameParts.unshift(cleanName(token.string));
token = editor.getTokenAt(Pos(cur.line, token.start));
if (token.string == ".") {
cont = true;
token = editor.getTokenAt(Pos(cur.line, token.start));
}
}
// Try to complete table names
var string = nameParts.join(".");
addMatches(result, string, tables, function(w) {
return useIdentifierQuotes ? insertIdentifierQuotes(w) : w;
});
// Try to complete columns from defaultTable
addMatches(result, string, defaultTable, function(w) {
return useIdentifierQuotes ? insertIdentifierQuotes(w) : w;
});
// Try to complete columns
string = nameParts.pop();
var table = nameParts.join(".");
var alias = false;
var aliasTable = table;
// Check if table is available. If not, find table by Alias
if (!getTable(table)) {
var oldTable = table;
table = findTableByAlias(table, editor);
if (table !== oldTable) alias = true;
}
var columns = getTable(table);
if (columns && columns.columns)
columns = columns.columns;
if (columns) {
addMatches(result, string, columns, function(w) {
var tableInsert = table;
if (alias == true) tableInsert = aliasTable;
if (typeof w == "string") {
w = tableInsert + "." + w;
} else {
w = shallowClone(w);
w.text = tableInsert + "." + w.text;
}
return useIdentifierQuotes ? insertIdentifierQuotes(w) : w;
});
}
return start;
}
function eachWord(lineText, f) {
if (!lineText) return;
var excepted = /[,;]/g;
var words = lineText.split(" ");
for (var i = 0; i < words.length; i++) {
f(words[i]?words[i].replace(excepted, '') : '');
}
}
function findTableByAlias(alias, editor) {
var doc = editor.doc;
var fullQuery = doc.getValue();
var aliasUpperCase = alias.toUpperCase();
var previousWord = "";
var table = "";
var separator = [];
var validRange = {
start: Pos(0, 0),
end: Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).length)
};
//add separator
var indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV);
while(indexOfSeparator != -1) {
separator.push(doc.posFromIndex(indexOfSeparator));
indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV, indexOfSeparator+1);
}
separator.unshift(Pos(0, 0));
separator.push(Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).text.length));
//find valid range
var prevItem = null;
var current = editor.getCursor()
for (var i = 0; i < separator.length; i++) {
if ((prevItem == null || cmpPos(current, prevItem) > 0) && cmpPos(current, separator[i]) <= 0) {
validRange = {start: prevItem, end: separator[i]};
break;
}
prevItem = separator[i];
}
var query = doc.getRange(validRange.start, validRange.end, false);
for (var i = 0; i < query.length; i++) {
var lineText = query[i];
eachWord(lineText, function(word) {
var wordUpperCase = word.toUpperCase();
if (wordUpperCase === aliasUpperCase && getTable(previousWord))
table = previousWord;
if (wordUpperCase !== CONS.ALIAS_KEYWORD)
previousWord = word;
});
if (table) break;
}
return table;
}
CodeMirror.registerHelper("hint", "sql", function(editor, options) {
tables = parseTables(options && options.tables)
var defaultTableName = options && options.defaultTable;
var disableKeywords = options && options.disableKeywords;
defaultTable = defaultTableName && getTable(defaultTableName);
keywords = getKeywords(editor);
identifierQuote = getIdentifierQuote(editor);
if (defaultTableName && !defaultTable)
defaultTable = findTableByAlias(defaultTableName, editor);
defaultTable = defaultTable || [];
if (defaultTable.columns)
defaultTable = defaultTable.columns;
var cur = editor.getCursor();
var result = [];
var token = editor.getTokenAt(cur), start, end, search;
if (token.end > cur.ch) {
token.end = cur.ch;
token.string = token.string.slice(0, cur.ch - token.start);
}
if (token.string.match(/^[.`"\w@]\w*$/)) {
search = token.string;
start = token.start;
end = token.end;
} else {
start = end = cur.ch;
search = "";
}
if (search.charAt(0) == "." || search.charAt(0) == identifierQuote) {
start = nameCompletion(cur, token, result, editor);
} else {
addMatches(result, search, tables, function(w) {return w;});
addMatches(result, search, defaultTable, function(w) {return w;});
if (!disableKeywords)
addMatches(result, search, keywords, function(w) {return w.toUpperCase();});
}
return {list: result, from: Pos(cur.line, start), to: Pos(cur.line, end)};
});
});

110
public/codemirror/addon/hint/xml-hint.js vendored Normal file
View File

@ -0,0 +1,110 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
var Pos = CodeMirror.Pos;
function getHints(cm, options) {
var tags = options && options.schemaInfo;
var quote = (options && options.quoteChar) || '"';
if (!tags) return;
var cur = cm.getCursor(), token = cm.getTokenAt(cur);
if (token.end > cur.ch) {
token.end = cur.ch;
token.string = token.string.slice(0, cur.ch - token.start);
}
var inner = CodeMirror.innerMode(cm.getMode(), token.state);
if (inner.mode.name != "xml") return;
var result = [], replaceToken = false, prefix;
var tag = /\btag\b/.test(token.type) && !/>$/.test(token.string);
var tagName = tag && /^\w/.test(token.string), tagStart;
if (tagName) {
var before = cm.getLine(cur.line).slice(Math.max(0, token.start - 2), token.start);
var tagType = /<\/$/.test(before) ? "close" : /<$/.test(before) ? "open" : null;
if (tagType) tagStart = token.start - (tagType == "close" ? 2 : 1);
} else if (tag && token.string == "<") {
tagType = "open";
} else if (tag && token.string == "</") {
tagType = "close";
}
if (!tag && !inner.state.tagName || tagType) {
if (tagName)
prefix = token.string;
replaceToken = tagType;
var cx = inner.state.context, curTag = cx && tags[cx.tagName];
var childList = cx ? curTag && curTag.children : tags["!top"];
if (childList && tagType != "close") {
for (var i = 0; i < childList.length; ++i) if (!prefix || childList[i].lastIndexOf(prefix, 0) == 0)
result.push("<" + childList[i]);
} else if (tagType != "close") {
for (var name in tags)
if (tags.hasOwnProperty(name) && name != "!top" && name != "!attrs" && (!prefix || name.lastIndexOf(prefix, 0) == 0))
result.push("<" + name);
}
if (cx && (!prefix || tagType == "close" && cx.tagName.lastIndexOf(prefix, 0) == 0))
result.push("</" + cx.tagName + ">");
} else {
// Attribute completion
var curTag = tags[inner.state.tagName], attrs = curTag && curTag.attrs;
var globalAttrs = tags["!attrs"];
if (!attrs && !globalAttrs) return;
if (!attrs) {
attrs = globalAttrs;
} else if (globalAttrs) { // Combine tag-local and global attributes
var set = {};
for (var nm in globalAttrs) if (globalAttrs.hasOwnProperty(nm)) set[nm] = globalAttrs[nm];
for (var nm in attrs) if (attrs.hasOwnProperty(nm)) set[nm] = attrs[nm];
attrs = set;
}
if (token.type == "string" || token.string == "=") { // A value
var before = cm.getRange(Pos(cur.line, Math.max(0, cur.ch - 60)),
Pos(cur.line, token.type == "string" ? token.start : token.end));
var atName = before.match(/([^\s\u00a0=<>\"\']+)=$/), atValues;
if (!atName || !attrs.hasOwnProperty(atName[1]) || !(atValues = attrs[atName[1]])) return;
if (typeof atValues == 'function') atValues = atValues.call(this, cm); // Functions can be used to supply values for autocomplete widget
if (token.type == "string") {
prefix = token.string;
var n = 0;
if (/['"]/.test(token.string.charAt(0))) {
quote = token.string.charAt(0);
prefix = token.string.slice(1);
n++;
}
var len = token.string.length;
if (/['"]/.test(token.string.charAt(len - 1))) {
quote = token.string.charAt(len - 1);
prefix = token.string.substr(n, len - 2);
}
replaceToken = true;
}
for (var i = 0; i < atValues.length; ++i) if (!prefix || atValues[i].lastIndexOf(prefix, 0) == 0)
result.push(quote + atValues[i] + quote);
} else { // An attribute name
if (token.type == "attribute") {
prefix = token.string;
replaceToken = true;
}
for (var attr in attrs) if (attrs.hasOwnProperty(attr) && (!prefix || attr.lastIndexOf(prefix, 0) == 0))
result.push(attr);
}
}
return {
list: result,
from: replaceToken ? Pos(cur.line, tagStart == null ? token.start : tagStart) : cur,
to: replaceToken ? Pos(cur.line, token.end) : cur
};
}
CodeMirror.registerHelper("hint", "xml", getHints);
});

View File

@ -0,0 +1,41 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
// Depends on coffeelint.js from http://www.coffeelint.org/js/coffeelint.js
// declare global: coffeelint
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
CodeMirror.registerHelper("lint", "coffeescript", function(text) {
var found = [];
var parseError = function(err) {
var loc = err.lineNumber;
found.push({from: CodeMirror.Pos(loc-1, 0),
to: CodeMirror.Pos(loc, 0),
severity: err.level,
message: err.message});
};
try {
var res = coffeelint.lint(text);
for(var i = 0; i < res.length; i++) {
parseError(res[i]);
}
} catch(e) {
found.push({from: CodeMirror.Pos(e.location.first_line, 0),
to: CodeMirror.Pos(e.location.last_line, e.location.last_column),
severity: 'error',
message: e.message});
}
return found;
});
});

View File

@ -0,0 +1,35 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
// Depends on csslint.js from https://github.com/stubbornella/csslint
// declare global: CSSLint
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
CodeMirror.registerHelper("lint", "css", function(text) {
var found = [];
if (!window.CSSLint) return found;
var results = CSSLint.verify(text), messages = results.messages, message = null;
for ( var i = 0; i < messages.length; i++) {
message = messages[i];
var startLine = message.line -1, endLine = message.line -1, startCol = message.col -1, endCol = message.col;
found.push({
from: CodeMirror.Pos(startLine, startCol),
to: CodeMirror.Pos(endLine, endCol),
message: message.message,
severity : message.type
});
}
return found;
});
});

View File

@ -0,0 +1,46 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
// Depends on htmlhint.js from http://htmlhint.com/js/htmlhint.js
// declare global: HTMLHint
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"), require("htmlhint"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror", "htmlhint"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
var defaultRules = {
"tagname-lowercase": true,
"attr-lowercase": true,
"attr-value-double-quotes": true,
"doctype-first": false,
"tag-pair": true,
"spec-char-escape": true,
"id-unique": true,
"src-not-empty": true,
"attr-no-duplication": true
};
CodeMirror.registerHelper("lint", "html", function(text, options) {
var found = [];
if (!window.HTMLHint) return found;
var messages = HTMLHint.verify(text, options && options.rules || defaultRules);
for (var i = 0; i < messages.length; i++) {
var message = messages[i];
var startLine = message.line - 1, endLine = message.line - 1, startCol = message.col - 1, endCol = message.col;
found.push({
from: CodeMirror.Pos(startLine, startCol),
to: CodeMirror.Pos(endLine, endCol),
message: message.message,
severity : message.type
});
}
return found;
});
});

View File

@ -0,0 +1,136 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
// declare global: JSHINT
var bogus = [ "Dangerous comment" ];
var warnings = [ [ "Expected '{'",
"Statement body should be inside '{ }' braces." ] ];
var errors = [ "Missing semicolon", "Extra comma", "Missing property name",
"Unmatched ", " and instead saw", " is not defined",
"Unclosed string", "Stopping, unable to continue" ];
function validator(text, options) {
if (!window.JSHINT) return [];
JSHINT(text, options, options.globals);
var errors = JSHINT.data().errors, result = [];
if (errors) parseErrors(errors, result);
return result;
}
CodeMirror.registerHelper("lint", "javascript", validator);
function cleanup(error) {
// All problems are warnings by default
fixWith(error, warnings, "warning", true);
fixWith(error, errors, "error");
return isBogus(error) ? null : error;
}
function fixWith(error, fixes, severity, force) {
var description, fix, find, replace, found;
description = error.description;
for ( var i = 0; i < fixes.length; i++) {
fix = fixes[i];
find = (typeof fix === "string" ? fix : fix[0]);
replace = (typeof fix === "string" ? null : fix[1]);
found = description.indexOf(find) !== -1;
if (force || found) {
error.severity = severity;
}
if (found && replace) {
error.description = replace;
}
}
}
function isBogus(error) {
var description = error.description;
for ( var i = 0; i < bogus.length; i++) {
if (description.indexOf(bogus[i]) !== -1) {
return true;
}
}
return false;
}
function parseErrors(errors, output) {
for ( var i = 0; i < errors.length; i++) {
var error = errors[i];
if (error) {
var linetabpositions, index;
linetabpositions = [];
// This next block is to fix a problem in jshint. Jshint
// replaces
// all tabs with spaces then performs some checks. The error
// positions (character/space) are then reported incorrectly,
// not taking the replacement step into account. Here we look
// at the evidence line and try to adjust the character position
// to the correct value.
if (error.evidence) {
// Tab positions are computed once per line and cached
var tabpositions = linetabpositions[error.line];
if (!tabpositions) {
var evidence = error.evidence;
tabpositions = [];
// ugggh phantomjs does not like this
// forEachChar(evidence, function(item, index) {
Array.prototype.forEach.call(evidence, function(item,
index) {
if (item === '\t') {
// First col is 1 (not 0) to match error
// positions
tabpositions.push(index + 1);
}
});
linetabpositions[error.line] = tabpositions;
}
if (tabpositions.length > 0) {
var pos = error.character;
tabpositions.forEach(function(tabposition) {
if (pos > tabposition) pos -= 1;
});
error.character = pos;
}
}
var start = error.character - 1, end = start + 1;
if (error.evidence) {
index = error.evidence.substring(start).search(/.\b/);
if (index > -1) {
end += index;
}
}
// Convert to format expected by validation service
error.description = error.reason;// + "(jshint)";
error.start = error.character;
error.end = end;
error = cleanup(error);
if (error)
output.push({message: error.description,
severity: error.severity,
from: CodeMirror.Pos(error.line - 1, start),
to: CodeMirror.Pos(error.line - 1, end)});
}
}
}
});

View File

@ -0,0 +1,31 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
// Depends on jsonlint.js from https://github.com/zaach/jsonlint
// declare global: jsonlint
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
CodeMirror.registerHelper("lint", "json", function(text) {
var found = [];
jsonlint.parseError = function(str, hash) {
var loc = hash.loc;
found.push({from: CodeMirror.Pos(loc.first_line - 1, loc.first_column),
to: CodeMirror.Pos(loc.last_line - 1, loc.last_column),
message: str});
};
try { jsonlint.parse(text); }
catch(e) {}
return found;
});
});

73
public/codemirror/addon/lint/lint.css vendored Normal file
View File

@ -0,0 +1,73 @@
/* The lint marker gutter */
.CodeMirror-lint-markers {
width: 16px;
}
.CodeMirror-lint-tooltip {
background-color: #ffd;
border: 1px solid black;
border-radius: 4px 4px 4px 4px;
color: black;
font-family: monospace;
font-size: 10pt;
overflow: hidden;
padding: 2px 5px;
position: fixed;
white-space: pre;
white-space: pre-wrap;
z-index: 100;
max-width: 600px;
opacity: 0;
transition: opacity .4s;
-moz-transition: opacity .4s;
-webkit-transition: opacity .4s;
-o-transition: opacity .4s;
-ms-transition: opacity .4s;
}
.CodeMirror-lint-mark-error, .CodeMirror-lint-mark-warning {
background-position: left bottom;
background-repeat: repeat-x;
}
.CodeMirror-lint-mark-error {
background-image:
url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==")
;
}
.CodeMirror-lint-mark-warning {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII=");
}
.CodeMirror-lint-marker-error, .CodeMirror-lint-marker-warning {
background-position: center center;
background-repeat: no-repeat;
cursor: pointer;
display: inline-block;
height: 16px;
width: 16px;
vertical-align: middle;
position: relative;
}
.CodeMirror-lint-message-error, .CodeMirror-lint-message-warning {
padding-left: 18px;
background-position: top left;
background-repeat: no-repeat;
}
.CodeMirror-lint-marker-error, .CodeMirror-lint-message-error {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAHlBMVEW7AAC7AACxAAC7AAC7AAAAAAC4AAC5AAD///+7AAAUdclpAAAABnRSTlMXnORSiwCK0ZKSAAAATUlEQVR42mWPOQ7AQAgDuQLx/z8csYRmPRIFIwRGnosRrpamvkKi0FTIiMASR3hhKW+hAN6/tIWhu9PDWiTGNEkTtIOucA5Oyr9ckPgAWm0GPBog6v4AAAAASUVORK5CYII=");
}
.CodeMirror-lint-marker-warning, .CodeMirror-lint-message-warning {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAANlBMVEX/uwDvrwD/uwD/uwD/uwD/uwD/uwD/uwD/uwD6twD/uwAAAADurwD2tQD7uAD+ugAAAAD/uwDhmeTRAAAADHRSTlMJ8mN1EYcbmiixgACm7WbuAAAAVklEQVR42n3PUQqAIBBFUU1LLc3u/jdbOJoW1P08DA9Gba8+YWJ6gNJoNYIBzAA2chBth5kLmG9YUoG0NHAUwFXwO9LuBQL1giCQb8gC9Oro2vp5rncCIY8L8uEx5ZkAAAAASUVORK5CYII=");
}
.CodeMirror-lint-marker-multiple {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAMAAADzjKfhAAAACVBMVEUAAAAAAAC/v7914kyHAAAAAXRSTlMAQObYZgAAACNJREFUeNo1ioEJAAAIwmz/H90iFFSGJgFMe3gaLZ0od+9/AQZ0ADosbYraAAAAAElFTkSuQmCC");
background-repeat: no-repeat;
background-position: right bottom;
width: 100%; height: 100%;
}

243
public/codemirror/addon/lint/lint.js vendored Normal file
View File

@ -0,0 +1,243 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
var GUTTER_ID = "CodeMirror-lint-markers";
function showTooltip(e, content) {
var tt = document.createElement("div");
tt.className = "CodeMirror-lint-tooltip";
tt.appendChild(content.cloneNode(true));
document.body.appendChild(tt);
function position(e) {
if (!tt.parentNode) return CodeMirror.off(document, "mousemove", position);
tt.style.top = Math.max(0, e.clientY - tt.offsetHeight - 5) + "px";
tt.style.left = (e.clientX + 5) + "px";
}
CodeMirror.on(document, "mousemove", position);
position(e);
if (tt.style.opacity != null) tt.style.opacity = 1;
return tt;
}
function rm(elt) {
if (elt.parentNode) elt.parentNode.removeChild(elt);
}
function hideTooltip(tt) {
if (!tt.parentNode) return;
if (tt.style.opacity == null) rm(tt);
tt.style.opacity = 0;
setTimeout(function() { rm(tt); }, 600);
}
function showTooltipFor(e, content, node) {
var tooltip = showTooltip(e, content);
function hide() {
CodeMirror.off(node, "mouseout", hide);
if (tooltip) { hideTooltip(tooltip); tooltip = null; }
}
var poll = setInterval(function() {
if (tooltip) for (var n = node;; n = n.parentNode) {
if (n && n.nodeType == 11) n = n.host;
if (n == document.body) return;
if (!n) { hide(); break; }
}
if (!tooltip) return clearInterval(poll);
}, 400);
CodeMirror.on(node, "mouseout", hide);
}
function LintState(cm, options, hasGutter) {
this.marked = [];
this.options = options;
this.timeout = null;
this.hasGutter = hasGutter;
this.onMouseOver = function(e) { onMouseOver(cm, e); };
this.waitingFor = 0
}
function parseOptions(_cm, options) {
if (options instanceof Function) return {getAnnotations: options};
if (!options || options === true) options = {};
return options;
}
function clearMarks(cm) {
var state = cm.state.lint;
if (state.hasGutter) cm.clearGutter(GUTTER_ID);
for (var i = 0; i < state.marked.length; ++i)
state.marked[i].clear();
state.marked.length = 0;
}
function makeMarker(labels, severity, multiple, tooltips) {
var marker = document.createElement("div"), inner = marker;
marker.className = "CodeMirror-lint-marker-" + severity;
if (multiple) {
inner = marker.appendChild(document.createElement("div"));
inner.className = "CodeMirror-lint-marker-multiple";
}
if (tooltips != false) CodeMirror.on(inner, "mouseover", function(e) {
showTooltipFor(e, labels, inner);
});
return marker;
}
function getMaxSeverity(a, b) {
if (a == "error") return a;
else return b;
}
function groupByLine(annotations) {
var lines = [];
for (var i = 0; i < annotations.length; ++i) {
var ann = annotations[i], line = ann.from.line;
(lines[line] || (lines[line] = [])).push(ann);
}
return lines;
}
function annotationTooltip(ann) {
var severity = ann.severity;
if (!severity) severity = "error";
var tip = document.createElement("div");
tip.className = "CodeMirror-lint-message-" + severity;
tip.appendChild(document.createTextNode(ann.message));
return tip;
}
function lintAsync(cm, getAnnotations, passOptions) {
var state = cm.state.lint
var id = ++state.waitingFor
function abort() {
id = -1
cm.off("change", abort)
}
cm.on("change", abort)
getAnnotations(cm.getValue(), function(annotations, arg2) {
cm.off("change", abort)
if (state.waitingFor != id) return
if (arg2 && annotations instanceof CodeMirror) annotations = arg2
updateLinting(cm, annotations)
}, passOptions, cm);
}
function startLinting(cm) {
var state = cm.state.lint, options = state.options;
var passOptions = options.options || options; // Support deprecated passing of `options` property in options
var getAnnotations = options.getAnnotations || cm.getHelper(CodeMirror.Pos(0, 0), "lint");
if (!getAnnotations) return;
if (options.async || getAnnotations.async) {
lintAsync(cm, getAnnotations, passOptions)
} else {
var annotations = getAnnotations(cm.getValue(), passOptions, cm);
if (annotations.then) annotations.then(function(issues) {
updateLinting(cm, issues);
});
else updateLinting(cm, annotations);
}
}
function updateLinting(cm, annotationsNotSorted) {
clearMarks(cm);
var state = cm.state.lint, options = state.options;
var annotations = groupByLine(annotationsNotSorted);
for (var line = 0; line < annotations.length; ++line) {
var anns = annotations[line];
if (!anns) continue;
var maxSeverity = null;
var tipLabel = state.hasGutter && document.createDocumentFragment();
for (var i = 0; i < anns.length; ++i) {
var ann = anns[i];
var severity = ann.severity;
if (!severity) severity = "error";
maxSeverity = getMaxSeverity(maxSeverity, severity);
if (options.formatAnnotation) ann = options.formatAnnotation(ann);
if (state.hasGutter) tipLabel.appendChild(annotationTooltip(ann));
if (ann.to) state.marked.push(cm.markText(ann.from, ann.to, {
className: "CodeMirror-lint-mark-" + severity,
__annotation: ann
}));
}
if (state.hasGutter)
cm.setGutterMarker(line, GUTTER_ID, makeMarker(tipLabel, maxSeverity, anns.length > 1,
state.options.tooltips));
}
if (options.onUpdateLinting) options.onUpdateLinting(annotationsNotSorted, annotations, cm);
}
function onChange(cm) {
var state = cm.state.lint;
if (!state) return;
clearTimeout(state.timeout);
state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay || 500);
}
function popupTooltips(annotations, e) {
var target = e.target || e.srcElement;
var tooltip = document.createDocumentFragment();
for (var i = 0; i < annotations.length; i++) {
var ann = annotations[i];
tooltip.appendChild(annotationTooltip(ann));
}
showTooltipFor(e, tooltip, target);
}
function onMouseOver(cm, e) {
var target = e.target || e.srcElement;
if (!/\bCodeMirror-lint-mark-/.test(target.className)) return;
var box = target.getBoundingClientRect(), x = (box.left + box.right) / 2, y = (box.top + box.bottom) / 2;
var spans = cm.findMarksAt(cm.coordsChar({left: x, top: y}, "client"));
var annotations = [];
for (var i = 0; i < spans.length; ++i) {
var ann = spans[i].__annotation;
if (ann) annotations.push(ann);
}
if (annotations.length) popupTooltips(annotations, e);
}
CodeMirror.defineOption("lint", false, function(cm, val, old) {
if (old && old != CodeMirror.Init) {
clearMarks(cm);
if (cm.state.lint.options.lintOnChange !== false)
cm.off("change", onChange);
CodeMirror.off(cm.getWrapperElement(), "mouseover", cm.state.lint.onMouseOver);
clearTimeout(cm.state.lint.timeout);
delete cm.state.lint;
}
if (val) {
var gutters = cm.getOption("gutters"), hasLintGutter = false;
for (var i = 0; i < gutters.length; ++i) if (gutters[i] == GUTTER_ID) hasLintGutter = true;
var state = cm.state.lint = new LintState(cm, parseOptions(cm, val), hasLintGutter);
if (state.options.lintOnChange !== false)
cm.on("change", onChange);
if (state.options.tooltips != false && state.options.tooltips != "gutter")
CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver);
startLinting(cm);
}
});
CodeMirror.defineExtension("performLint", function() {
if (this.state.lint) startLinting(this);
});
});

View File

@ -0,0 +1,35 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
// Depends on js-yaml.js from https://github.com/nodeca/js-yaml
// declare global: jsyaml
CodeMirror.registerHelper("lint", "yaml", function(text) {
var found = [];
try { jsyaml.load(text); }
catch(e) {
var loc = e.mark,
// js-yaml YAMLException doesn't always provide an accurate lineno
// e.g., when there are multiple yaml docs
// ---
// ---
// foo:bar
from = loc ? CodeMirror.Pos(loc.line, loc.column) : CodeMirror.Pos(0, 0),
to = from;
found.push({ from: from, to: to, message: e.message });
}
return found;
});
});

113
public/codemirror/addon/merge/merge.css vendored Normal file
View File

@ -0,0 +1,113 @@
.CodeMirror-merge {
position: relative;
border: 1px solid #ddd;
white-space: pre;
}
.CodeMirror-merge, .CodeMirror-merge .CodeMirror {
height: 350px;
}
.CodeMirror-merge-2pane .CodeMirror-merge-pane { width: 47%; }
.CodeMirror-merge-2pane .CodeMirror-merge-gap { width: 6%; }
.CodeMirror-merge-3pane .CodeMirror-merge-pane { width: 31%; }
.CodeMirror-merge-3pane .CodeMirror-merge-gap { width: 3.5%; }
.CodeMirror-merge-pane {
display: inline-block;
white-space: normal;
vertical-align: top;
}
.CodeMirror-merge-pane-rightmost {
position: absolute;
right: 0px;
z-index: 1;
}
.CodeMirror-merge-gap {
z-index: 2;
display: inline-block;
height: 100%;
-moz-box-sizing: border-box;
box-sizing: border-box;
overflow: hidden;
border-left: 1px solid #ddd;
border-right: 1px solid #ddd;
position: relative;
background: #f8f8f8;
}
.CodeMirror-merge-scrolllock-wrap {
position: absolute;
bottom: 0; left: 50%;
}
.CodeMirror-merge-scrolllock {
position: relative;
left: -50%;
cursor: pointer;
color: #555;
line-height: 1;
}
.CodeMirror-merge-copybuttons-left, .CodeMirror-merge-copybuttons-right {
position: absolute;
left: 0; top: 0;
right: 0; bottom: 0;
line-height: 1;
}
.CodeMirror-merge-copy {
position: absolute;
cursor: pointer;
color: #44c;
z-index: 3;
}
.CodeMirror-merge-copy-reverse {
position: absolute;
cursor: pointer;
color: #44c;
}
.CodeMirror-merge-copybuttons-left .CodeMirror-merge-copy { left: 2px; }
.CodeMirror-merge-copybuttons-right .CodeMirror-merge-copy { right: 2px; }
.CodeMirror-merge-r-inserted, .CodeMirror-merge-l-inserted {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAYAAACddGYaAAAAGUlEQVQI12MwuCXy3+CWyH8GBgYGJgYkAABZbAQ9ELXurwAAAABJRU5ErkJggg==);
background-position: bottom left;
background-repeat: repeat-x;
}
.CodeMirror-merge-r-deleted, .CodeMirror-merge-l-deleted {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAYAAACddGYaAAAAGUlEQVQI12M4Kyb2/6yY2H8GBgYGJgYkAABURgPz6Ks7wQAAAABJRU5ErkJggg==);
background-position: bottom left;
background-repeat: repeat-x;
}
.CodeMirror-merge-r-chunk { background: #ffffe0; }
.CodeMirror-merge-r-chunk-start { border-top: 1px solid #ee8; }
.CodeMirror-merge-r-chunk-end { border-bottom: 1px solid #ee8; }
.CodeMirror-merge-r-connect { fill: #ffffe0; stroke: #ee8; stroke-width: 1px; }
.CodeMirror-merge-l-chunk { background: #eef; }
.CodeMirror-merge-l-chunk-start { border-top: 1px solid #88e; }
.CodeMirror-merge-l-chunk-end { border-bottom: 1px solid #88e; }
.CodeMirror-merge-l-connect { fill: #eef; stroke: #88e; stroke-width: 1px; }
.CodeMirror-merge-l-chunk.CodeMirror-merge-r-chunk { background: #dfd; }
.CodeMirror-merge-l-chunk-start.CodeMirror-merge-r-chunk-start { border-top: 1px solid #4e4; }
.CodeMirror-merge-l-chunk-end.CodeMirror-merge-r-chunk-end { border-bottom: 1px solid #4e4; }
.CodeMirror-merge-collapsed-widget:before {
content: "(...)";
}
.CodeMirror-merge-collapsed-widget {
cursor: pointer;
color: #88b;
background: #eef;
border: 1px solid #ddf;
font-size: 90%;
padding: 0 3px;
border-radius: 4px;
}
.CodeMirror-merge-collapsed-line .CodeMirror-gutter-elt { display: none; }

997
public/codemirror/addon/merge/merge.js vendored Normal file
View File

@ -0,0 +1,997 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
// declare global: diff_match_patch, DIFF_INSERT, DIFF_DELETE, DIFF_EQUAL
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror")); // Note non-packaged dependency diff_match_patch
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror", "diff_match_patch"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
var Pos = CodeMirror.Pos;
var svgNS = "http://www.w3.org/2000/svg";
function DiffView(mv, type) {
this.mv = mv;
this.type = type;
this.classes = type == "left"
? {chunk: "CodeMirror-merge-l-chunk",
start: "CodeMirror-merge-l-chunk-start",
end: "CodeMirror-merge-l-chunk-end",
insert: "CodeMirror-merge-l-inserted",
del: "CodeMirror-merge-l-deleted",
connect: "CodeMirror-merge-l-connect"}
: {chunk: "CodeMirror-merge-r-chunk",
start: "CodeMirror-merge-r-chunk-start",
end: "CodeMirror-merge-r-chunk-end",
insert: "CodeMirror-merge-r-inserted",
del: "CodeMirror-merge-r-deleted",
connect: "CodeMirror-merge-r-connect"};
}
DiffView.prototype = {
constructor: DiffView,
init: function(pane, orig, options) {
this.edit = this.mv.edit;
;(this.edit.state.diffViews || (this.edit.state.diffViews = [])).push(this);
this.orig = CodeMirror(pane, copyObj({value: orig, readOnly: !this.mv.options.allowEditingOriginals}, copyObj(options)));
if (this.mv.options.connect == "align") {
if (!this.edit.state.trackAlignable) this.edit.state.trackAlignable = new TrackAlignable(this.edit)
this.orig.state.trackAlignable = new TrackAlignable(this.orig)
}
this.orig.state.diffViews = [this];
var classLocation = options.chunkClassLocation || "background";
if (Object.prototype.toString.call(classLocation) != "[object Array]") classLocation = [classLocation]
this.classes.classLocation = classLocation
this.diff = getDiff(asString(orig), asString(options.value), this.mv.options.ignoreWhitespace);
this.chunks = getChunks(this.diff);
this.diffOutOfDate = this.dealigned = false;
this.needsScrollSync = null
this.showDifferences = options.showDifferences !== false;
},
registerEvents: function(otherDv) {
this.forceUpdate = registerUpdate(this);
setScrollLock(this, true, false);
registerScroll(this, otherDv);
},
setShowDifferences: function(val) {
val = val !== false;
if (val != this.showDifferences) {
this.showDifferences = val;
this.forceUpdate("full");
}
}
};
function ensureDiff(dv) {
if (dv.diffOutOfDate) {
dv.diff = getDiff(dv.orig.getValue(), dv.edit.getValue(), dv.mv.options.ignoreWhitespace);
dv.chunks = getChunks(dv.diff);
dv.diffOutOfDate = false;
CodeMirror.signal(dv.edit, "updateDiff", dv.diff);
}
}
var updating = false;
function registerUpdate(dv) {
var edit = {from: 0, to: 0, marked: []};
var orig = {from: 0, to: 0, marked: []};
var debounceChange, updatingFast = false;
function update(mode) {
updating = true;
updatingFast = false;
if (mode == "full") {
if (dv.svg) clear(dv.svg);
if (dv.copyButtons) clear(dv.copyButtons);
clearMarks(dv.edit, edit.marked, dv.classes);
clearMarks(dv.orig, orig.marked, dv.classes);
edit.from = edit.to = orig.from = orig.to = 0;
}
ensureDiff(dv);
if (dv.showDifferences) {
updateMarks(dv.edit, dv.diff, edit, DIFF_INSERT, dv.classes);
updateMarks(dv.orig, dv.diff, orig, DIFF_DELETE, dv.classes);
}
if (dv.mv.options.connect == "align")
alignChunks(dv);
makeConnections(dv);
if (dv.needsScrollSync != null) syncScroll(dv, dv.needsScrollSync)
updating = false;
}
function setDealign(fast) {
if (updating) return;
dv.dealigned = true;
set(fast);
}
function set(fast) {
if (updating || updatingFast) return;
clearTimeout(debounceChange);
if (fast === true) updatingFast = true;
debounceChange = setTimeout(update, fast === true ? 20 : 250);
}
function change(_cm, change) {
if (!dv.diffOutOfDate) {
dv.diffOutOfDate = true;
edit.from = edit.to = orig.from = orig.to = 0;
}
// Update faster when a line was added/removed
setDealign(change.text.length - 1 != change.to.line - change.from.line);
}
function swapDoc() {
dv.diffOutOfDate = true;
dv.dealigned = true;
update("full");
}
dv.edit.on("change", change);
dv.orig.on("change", change);
dv.edit.on("swapDoc", swapDoc);
dv.orig.on("swapDoc", swapDoc);
if (dv.mv.options.connect == "align") {
CodeMirror.on(dv.edit.state.trackAlignable, "realign", setDealign)
CodeMirror.on(dv.orig.state.trackAlignable, "realign", setDealign)
}
dv.edit.on("viewportChange", function() { set(false); });
dv.orig.on("viewportChange", function() { set(false); });
update();
return update;
}
function registerScroll(dv, otherDv) {
dv.edit.on("scroll", function() {
syncScroll(dv, true) && makeConnections(dv);
});
dv.orig.on("scroll", function() {
syncScroll(dv, false) && makeConnections(dv);
if (otherDv) syncScroll(otherDv, true) && makeConnections(otherDv);
});
}
function syncScroll(dv, toOrig) {
// Change handler will do a refresh after a timeout when diff is out of date
if (dv.diffOutOfDate) {
if (dv.lockScroll && dv.needsScrollSync == null) dv.needsScrollSync = toOrig
return false
}
dv.needsScrollSync = null
if (!dv.lockScroll) return true;
var editor, other, now = +new Date;
if (toOrig) { editor = dv.edit; other = dv.orig; }
else { editor = dv.orig; other = dv.edit; }
// Don't take action if the position of this editor was recently set
// (to prevent feedback loops)
if (editor.state.scrollSetBy == dv && (editor.state.scrollSetAt || 0) + 250 > now) return false;
var sInfo = editor.getScrollInfo();
if (dv.mv.options.connect == "align") {
targetPos = sInfo.top;
} else {
var halfScreen = .5 * sInfo.clientHeight, midY = sInfo.top + halfScreen;
var mid = editor.lineAtHeight(midY, "local");
var around = chunkBoundariesAround(dv.chunks, mid, toOrig);
var off = getOffsets(editor, toOrig ? around.edit : around.orig);
var offOther = getOffsets(other, toOrig ? around.orig : around.edit);
var ratio = (midY - off.top) / (off.bot - off.top);
var targetPos = (offOther.top - halfScreen) + ratio * (offOther.bot - offOther.top);
var botDist, mix;
// Some careful tweaking to make sure no space is left out of view
// when scrolling to top or bottom.
if (targetPos > sInfo.top && (mix = sInfo.top / halfScreen) < 1) {
targetPos = targetPos * mix + sInfo.top * (1 - mix);
} else if ((botDist = sInfo.height - sInfo.clientHeight - sInfo.top) < halfScreen) {
var otherInfo = other.getScrollInfo();
var botDistOther = otherInfo.height - otherInfo.clientHeight - targetPos;
if (botDistOther > botDist && (mix = botDist / halfScreen) < 1)
targetPos = targetPos * mix + (otherInfo.height - otherInfo.clientHeight - botDist) * (1 - mix);
}
}
other.scrollTo(sInfo.left, targetPos);
other.state.scrollSetAt = now;
other.state.scrollSetBy = dv;
return true;
}
function getOffsets(editor, around) {
var bot = around.after;
if (bot == null) bot = editor.lastLine() + 1;
return {top: editor.heightAtLine(around.before || 0, "local"),
bot: editor.heightAtLine(bot, "local")};
}
function setScrollLock(dv, val, action) {
dv.lockScroll = val;
if (val && action != false) syncScroll(dv, DIFF_INSERT) && makeConnections(dv);
dv.lockButton.innerHTML = val ? "\u21db\u21da" : "\u21db&nbsp;&nbsp;\u21da";
}
// Updating the marks for editor content
function removeClass(editor, line, classes) {
var locs = classes.classLocation
for (var i = 0; i < locs.length; i++) {
editor.removeLineClass(line, locs[i], classes.chunk);
editor.removeLineClass(line, locs[i], classes.start);
editor.removeLineClass(line, locs[i], classes.end);
}
}
function clearMarks(editor, arr, classes) {
for (var i = 0; i < arr.length; ++i) {
var mark = arr[i];
if (mark instanceof CodeMirror.TextMarker)
mark.clear();
else if (mark.parent)
removeClass(editor, mark, classes);
}
arr.length = 0;
}
// FIXME maybe add a margin around viewport to prevent too many updates
function updateMarks(editor, diff, state, type, classes) {
var vp = editor.getViewport();
editor.operation(function() {
if (state.from == state.to || vp.from - state.to > 20 || state.from - vp.to > 20) {
clearMarks(editor, state.marked, classes);
markChanges(editor, diff, type, state.marked, vp.from, vp.to, classes);
state.from = vp.from; state.to = vp.to;
} else {
if (vp.from < state.from) {
markChanges(editor, diff, type, state.marked, vp.from, state.from, classes);
state.from = vp.from;
}
if (vp.to > state.to) {
markChanges(editor, diff, type, state.marked, state.to, vp.to, classes);
state.to = vp.to;
}
}
});
}
function addClass(editor, lineNr, classes, main, start, end) {
var locs = classes.classLocation, line = editor.getLineHandle(lineNr);
for (var i = 0; i < locs.length; i++) {
if (main) editor.addLineClass(line, locs[i], classes.chunk);
if (start) editor.addLineClass(line, locs[i], classes.start);
if (end) editor.addLineClass(line, locs[i], classes.end);
}
return line;
}
function markChanges(editor, diff, type, marks, from, to, classes) {
var pos = Pos(0, 0);
var top = Pos(from, 0), bot = editor.clipPos(Pos(to - 1));
var cls = type == DIFF_DELETE ? classes.del : classes.insert;
function markChunk(start, end) {
var bfrom = Math.max(from, start), bto = Math.min(to, end);
for (var i = bfrom; i < bto; ++i)
marks.push(addClass(editor, i, classes, true, i == start, i == end - 1));
// When the chunk is empty, make sure a horizontal line shows up
if (start == end && bfrom == end && bto == end) {
if (bfrom)
marks.push(addClass(editor, bfrom - 1, classes, false, false, true));
else
marks.push(addClass(editor, bfrom, classes, false, true, false));
}
}
var chunkStart = 0, pending = false;
for (var i = 0; i < diff.length; ++i) {
var part = diff[i], tp = part[0], str = part[1];
if (tp == DIFF_EQUAL) {
var cleanFrom = pos.line + (startOfLineClean(diff, i) ? 0 : 1);
moveOver(pos, str);
var cleanTo = pos.line + (endOfLineClean(diff, i) ? 1 : 0);
if (cleanTo > cleanFrom) {
if (pending) { markChunk(chunkStart, cleanFrom); pending = false }
chunkStart = cleanTo;
}
} else {
pending = true
if (tp == type) {
var end = moveOver(pos, str, true);
var a = posMax(top, pos), b = posMin(bot, end);
if (!posEq(a, b))
marks.push(editor.markText(a, b, {className: cls}));
pos = end;
}
}
}
if (pending) markChunk(chunkStart, pos.line + 1);
}
// Updating the gap between editor and original
function makeConnections(dv) {
if (!dv.showDifferences) return;
if (dv.svg) {
clear(dv.svg);
var w = dv.gap.offsetWidth;
attrs(dv.svg, "width", w, "height", dv.gap.offsetHeight);
}
if (dv.copyButtons) clear(dv.copyButtons);
var vpEdit = dv.edit.getViewport(), vpOrig = dv.orig.getViewport();
var outerTop = dv.mv.wrap.getBoundingClientRect().top
var sTopEdit = outerTop - dv.edit.getScrollerElement().getBoundingClientRect().top + dv.edit.getScrollInfo().top
var sTopOrig = outerTop - dv.orig.getScrollerElement().getBoundingClientRect().top + dv.orig.getScrollInfo().top;
for (var i = 0; i < dv.chunks.length; i++) {
var ch = dv.chunks[i];
if (ch.editFrom <= vpEdit.to && ch.editTo >= vpEdit.from &&
ch.origFrom <= vpOrig.to && ch.origTo >= vpOrig.from)
drawConnectorsForChunk(dv, ch, sTopOrig, sTopEdit, w);
}
}
function getMatchingOrigLine(editLine, chunks) {
var editStart = 0, origStart = 0;
for (var i = 0; i < chunks.length; i++) {
var chunk = chunks[i];
if (chunk.editTo > editLine && chunk.editFrom <= editLine) return null;
if (chunk.editFrom > editLine) break;
editStart = chunk.editTo;
origStart = chunk.origTo;
}
return origStart + (editLine - editStart);
}
// Combines information about chunks and widgets/markers to return
// an array of lines, in a single editor, that probably need to be
// aligned with their counterparts in the editor next to it.
function alignableFor(cm, chunks, isOrig) {
var tracker = cm.state.trackAlignable
var start = cm.firstLine(), trackI = 0
var result = []
for (var i = 0;; i++) {
var chunk = chunks[i]
var chunkStart = !chunk ? 1e9 : isOrig ? chunk.origFrom : chunk.editFrom
for (; trackI < tracker.alignable.length; trackI += 2) {
var n = tracker.alignable[trackI] + 1
if (n <= start) continue
if (n <= chunkStart) result.push(n)
else break
}
if (!chunk) break
result.push(start = isOrig ? chunk.origTo : chunk.editTo)
}
return result
}
// Given information about alignable lines in two editors, fill in
// the result (an array of three-element arrays) to reflect the
// lines that need to be aligned with each other.
function mergeAlignable(result, origAlignable, chunks, setIndex) {
var rI = 0, origI = 0, chunkI = 0, diff = 0
outer: for (;; rI++) {
var nextR = result[rI], nextO = origAlignable[origI]
if (!nextR && nextO == null) break
var rLine = nextR ? nextR[0] : 1e9, oLine = nextO == null ? 1e9 : nextO
while (chunkI < chunks.length) {
var chunk = chunks[chunkI]
if (chunk.origFrom <= oLine && chunk.origTo > oLine) {
origI++
rI--
continue outer;
}
if (chunk.editTo > rLine) {
if (chunk.editFrom <= rLine) continue outer;
break
}
diff += (chunk.origTo - chunk.origFrom) - (chunk.editTo - chunk.editFrom)
chunkI++
}
if (rLine == oLine - diff) {
nextR[setIndex] = oLine
origI++
} else if (rLine < oLine - diff) {
nextR[setIndex] = rLine + diff
} else {
var record = [oLine - diff, null, null]
record[setIndex] = oLine
result.splice(rI, 0, record)
origI++
}
}
}
function findAlignedLines(dv, other) {
var alignable = alignableFor(dv.edit, dv.chunks, false), result = []
if (other) for (var i = 0, j = 0; i < other.chunks.length; i++) {
var n = other.chunks[i].editTo
while (j < alignable.length && alignable[j] < n) j++
if (j == alignable.length || alignable[j] != n) alignable.splice(j++, 0, n)
}
for (var i = 0; i < alignable.length; i++)
result.push([alignable[i], null, null])
mergeAlignable(result, alignableFor(dv.orig, dv.chunks, true), dv.chunks, 1)
if (other)
mergeAlignable(result, alignableFor(other.orig, other.chunks, true), other.chunks, 2)
return result
}
function alignChunks(dv, force) {
if (!dv.dealigned && !force) return;
if (!dv.orig.curOp) return dv.orig.operation(function() {
alignChunks(dv, force);
});
dv.dealigned = false;
var other = dv.mv.left == dv ? dv.mv.right : dv.mv.left;
if (other) {
ensureDiff(other);
other.dealigned = false;
}
var linesToAlign = findAlignedLines(dv, other);
// Clear old aligners
var aligners = dv.mv.aligners;
for (var i = 0; i < aligners.length; i++)
aligners[i].clear();
aligners.length = 0;
var cm = [dv.edit, dv.orig], scroll = [];
if (other) cm.push(other.orig);
for (var i = 0; i < cm.length; i++)
scroll.push(cm[i].getScrollInfo().top);
for (var ln = 0; ln < linesToAlign.length; ln++)
alignLines(cm, linesToAlign[ln], aligners);
for (var i = 0; i < cm.length; i++)
cm[i].scrollTo(null, scroll[i]);
}
function alignLines(cm, lines, aligners) {
var maxOffset = 0, offset = [];
for (var i = 0; i < cm.length; i++) if (lines[i] != null) {
var off = cm[i].heightAtLine(lines[i], "local");
offset[i] = off;
maxOffset = Math.max(maxOffset, off);
}
for (var i = 0; i < cm.length; i++) if (lines[i] != null) {
var diff = maxOffset - offset[i];
if (diff > 1)
aligners.push(padAbove(cm[i], lines[i], diff));
}
}
function padAbove(cm, line, size) {
var above = true;
if (line > cm.lastLine()) {
line--;
above = false;
}
var elt = document.createElement("div");
elt.className = "CodeMirror-merge-spacer";
elt.style.height = size + "px"; elt.style.minWidth = "1px";
return cm.addLineWidget(line, elt, {height: size, above: above, mergeSpacer: true, handleMouseEvents: true});
}
function drawConnectorsForChunk(dv, chunk, sTopOrig, sTopEdit, w) {
var flip = dv.type == "left";
var top = dv.orig.heightAtLine(chunk.origFrom, "local", true) - sTopOrig;
if (dv.svg) {
var topLpx = top;
var topRpx = dv.edit.heightAtLine(chunk.editFrom, "local", true) - sTopEdit;
if (flip) { var tmp = topLpx; topLpx = topRpx; topRpx = tmp; }
var botLpx = dv.orig.heightAtLine(chunk.origTo, "local", true) - sTopOrig;
var botRpx = dv.edit.heightAtLine(chunk.editTo, "local", true) - sTopEdit;
if (flip) { var tmp = botLpx; botLpx = botRpx; botRpx = tmp; }
var curveTop = " C " + w/2 + " " + topRpx + " " + w/2 + " " + topLpx + " " + (w + 2) + " " + topLpx;
var curveBot = " C " + w/2 + " " + botLpx + " " + w/2 + " " + botRpx + " -1 " + botRpx;
attrs(dv.svg.appendChild(document.createElementNS(svgNS, "path")),
"d", "M -1 " + topRpx + curveTop + " L " + (w + 2) + " " + botLpx + curveBot + " z",
"class", dv.classes.connect);
}
if (dv.copyButtons) {
var copy = dv.copyButtons.appendChild(elt("div", dv.type == "left" ? "\u21dd" : "\u21dc",
"CodeMirror-merge-copy"));
var editOriginals = dv.mv.options.allowEditingOriginals;
copy.title = editOriginals ? "Push to left" : "Revert chunk";
copy.chunk = chunk;
copy.style.top = (chunk.origTo > chunk.origFrom ? top : dv.edit.heightAtLine(chunk.editFrom, "local") - sTopEdit) + "px";
if (editOriginals) {
var topReverse = dv.edit.heightAtLine(chunk.editFrom, "local") - sTopEdit;
var copyReverse = dv.copyButtons.appendChild(elt("div", dv.type == "right" ? "\u21dd" : "\u21dc",
"CodeMirror-merge-copy-reverse"));
copyReverse.title = "Push to right";
copyReverse.chunk = {editFrom: chunk.origFrom, editTo: chunk.origTo,
origFrom: chunk.editFrom, origTo: chunk.editTo};
copyReverse.style.top = topReverse + "px";
dv.type == "right" ? copyReverse.style.left = "2px" : copyReverse.style.right = "2px";
}
}
}
function copyChunk(dv, to, from, chunk) {
if (dv.diffOutOfDate) return;
var origStart = chunk.origTo > from.lastLine() ? Pos(chunk.origFrom - 1) : Pos(chunk.origFrom, 0)
var origEnd = Pos(chunk.origTo, 0)
var editStart = chunk.editTo > to.lastLine() ? Pos(chunk.editFrom - 1) : Pos(chunk.editFrom, 0)
var editEnd = Pos(chunk.editTo, 0)
var handler = dv.mv.options.revertChunk
if (handler)
handler(dv.mv, from, origStart, origEnd, to, editStart, editEnd)
else
to.replaceRange(from.getRange(origStart, origEnd), editStart, editEnd)
}
// Merge view, containing 0, 1, or 2 diff views.
var MergeView = CodeMirror.MergeView = function(node, options) {
if (!(this instanceof MergeView)) return new MergeView(node, options);
this.options = options;
var origLeft = options.origLeft, origRight = options.origRight == null ? options.orig : options.origRight;
var hasLeft = origLeft != null, hasRight = origRight != null;
var panes = 1 + (hasLeft ? 1 : 0) + (hasRight ? 1 : 0);
var wrap = [], left = this.left = null, right = this.right = null;
var self = this;
if (hasLeft) {
left = this.left = new DiffView(this, "left");
var leftPane = elt("div", null, "CodeMirror-merge-pane CodeMirror-merge-left");
wrap.push(leftPane);
wrap.push(buildGap(left));
}
var editPane = elt("div", null, "CodeMirror-merge-pane CodeMirror-merge-editor");
wrap.push(editPane);
if (hasRight) {
right = this.right = new DiffView(this, "right");
wrap.push(buildGap(right));
var rightPane = elt("div", null, "CodeMirror-merge-pane CodeMirror-merge-right");
wrap.push(rightPane);
}
(hasRight ? rightPane : editPane).className += " CodeMirror-merge-pane-rightmost";
wrap.push(elt("div", null, null, "height: 0; clear: both;"));
var wrapElt = this.wrap = node.appendChild(elt("div", wrap, "CodeMirror-merge CodeMirror-merge-" + panes + "pane"));
this.edit = CodeMirror(editPane, copyObj(options));
if (left) left.init(leftPane, origLeft, options);
if (right) right.init(rightPane, origRight, options);
if (options.collapseIdentical)
this.editor().operation(function() {
collapseIdenticalStretches(self, options.collapseIdentical);
});
if (options.connect == "align") {
this.aligners = [];
alignChunks(this.left || this.right, true);
}
if (left) left.registerEvents(right)
if (right) right.registerEvents(left)
var onResize = function() {
if (left) makeConnections(left);
if (right) makeConnections(right);
};
CodeMirror.on(window, "resize", onResize);
var resizeInterval = setInterval(function() {
for (var p = wrapElt.parentNode; p && p != document.body; p = p.parentNode) {}
if (!p) { clearInterval(resizeInterval); CodeMirror.off(window, "resize", onResize); }
}, 5000);
};
function buildGap(dv) {
var lock = dv.lockButton = elt("div", null, "CodeMirror-merge-scrolllock");
lock.title = "Toggle locked scrolling";
var lockWrap = elt("div", [lock], "CodeMirror-merge-scrolllock-wrap");
CodeMirror.on(lock, "click", function() { setScrollLock(dv, !dv.lockScroll); });
var gapElts = [lockWrap];
if (dv.mv.options.revertButtons !== false) {
dv.copyButtons = elt("div", null, "CodeMirror-merge-copybuttons-" + dv.type);
CodeMirror.on(dv.copyButtons, "click", function(e) {
var node = e.target || e.srcElement;
if (!node.chunk) return;
if (node.className == "CodeMirror-merge-copy-reverse") {
copyChunk(dv, dv.orig, dv.edit, node.chunk);
return;
}
copyChunk(dv, dv.edit, dv.orig, node.chunk);
});
gapElts.unshift(dv.copyButtons);
}
if (dv.mv.options.connect != "align") {
var svg = document.createElementNS && document.createElementNS(svgNS, "svg");
if (svg && !svg.createSVGRect) svg = null;
dv.svg = svg;
if (svg) gapElts.push(svg);
}
return dv.gap = elt("div", gapElts, "CodeMirror-merge-gap");
}
MergeView.prototype = {
constructor: MergeView,
editor: function() { return this.edit; },
rightOriginal: function() { return this.right && this.right.orig; },
leftOriginal: function() { return this.left && this.left.orig; },
setShowDifferences: function(val) {
if (this.right) this.right.setShowDifferences(val);
if (this.left) this.left.setShowDifferences(val);
},
rightChunks: function() {
if (this.right) { ensureDiff(this.right); return this.right.chunks; }
},
leftChunks: function() {
if (this.left) { ensureDiff(this.left); return this.left.chunks; }
}
};
function asString(obj) {
if (typeof obj == "string") return obj;
else return obj.getValue();
}
// Operations on diffs
var dmp = new diff_match_patch();
function getDiff(a, b, ignoreWhitespace) {
var diff = dmp.diff_main(a, b);
// The library sometimes leaves in empty parts, which confuse the algorithm
for (var i = 0; i < diff.length; ++i) {
var part = diff[i];
if (ignoreWhitespace ? !/[^ \t]/.test(part[1]) : !part[1]) {
diff.splice(i--, 1);
} else if (i && diff[i - 1][0] == part[0]) {
diff.splice(i--, 1);
diff[i][1] += part[1];
}
}
return diff;
}
function getChunks(diff) {
var chunks = [];
var startEdit = 0, startOrig = 0;
var edit = Pos(0, 0), orig = Pos(0, 0);
for (var i = 0; i < diff.length; ++i) {
var part = diff[i], tp = part[0];
if (tp == DIFF_EQUAL) {
var startOff = !startOfLineClean(diff, i) || edit.line < startEdit || orig.line < startOrig ? 1 : 0;
var cleanFromEdit = edit.line + startOff, cleanFromOrig = orig.line + startOff;
moveOver(edit, part[1], null, orig);
var endOff = endOfLineClean(diff, i) ? 1 : 0;
var cleanToEdit = edit.line + endOff, cleanToOrig = orig.line + endOff;
if (cleanToEdit > cleanFromEdit) {
if (i) chunks.push({origFrom: startOrig, origTo: cleanFromOrig,
editFrom: startEdit, editTo: cleanFromEdit});
startEdit = cleanToEdit; startOrig = cleanToOrig;
}
} else {
moveOver(tp == DIFF_INSERT ? edit : orig, part[1]);
}
}
if (startEdit <= edit.line || startOrig <= orig.line)
chunks.push({origFrom: startOrig, origTo: orig.line + 1,
editFrom: startEdit, editTo: edit.line + 1});
return chunks;
}
function endOfLineClean(diff, i) {
if (i == diff.length - 1) return true;
var next = diff[i + 1][1];
if ((next.length == 1 && i < diff.length - 2) || next.charCodeAt(0) != 10) return false;
if (i == diff.length - 2) return true;
next = diff[i + 2][1];
return (next.length > 1 || i == diff.length - 3) && next.charCodeAt(0) == 10;
}
function startOfLineClean(diff, i) {
if (i == 0) return true;
var last = diff[i - 1][1];
if (last.charCodeAt(last.length - 1) != 10) return false;
if (i == 1) return true;
last = diff[i - 2][1];
return last.charCodeAt(last.length - 1) == 10;
}
function chunkBoundariesAround(chunks, n, nInEdit) {
var beforeE, afterE, beforeO, afterO;
for (var i = 0; i < chunks.length; i++) {
var chunk = chunks[i];
var fromLocal = nInEdit ? chunk.editFrom : chunk.origFrom;
var toLocal = nInEdit ? chunk.editTo : chunk.origTo;
if (afterE == null) {
if (fromLocal > n) { afterE = chunk.editFrom; afterO = chunk.origFrom; }
else if (toLocal > n) { afterE = chunk.editTo; afterO = chunk.origTo; }
}
if (toLocal <= n) { beforeE = chunk.editTo; beforeO = chunk.origTo; }
else if (fromLocal <= n) { beforeE = chunk.editFrom; beforeO = chunk.origFrom; }
}
return {edit: {before: beforeE, after: afterE}, orig: {before: beforeO, after: afterO}};
}
function collapseSingle(cm, from, to) {
cm.addLineClass(from, "wrap", "CodeMirror-merge-collapsed-line");
var widget = document.createElement("span");
widget.className = "CodeMirror-merge-collapsed-widget";
widget.title = "Identical text collapsed. Click to expand.";
var mark = cm.markText(Pos(from, 0), Pos(to - 1), {
inclusiveLeft: true,
inclusiveRight: true,
replacedWith: widget,
clearOnEnter: true
});
function clear() {
mark.clear();
cm.removeLineClass(from, "wrap", "CodeMirror-merge-collapsed-line");
}
CodeMirror.on(widget, "click", clear);
return {mark: mark, clear: clear};
}
function collapseStretch(size, editors) {
var marks = [];
function clear() {
for (var i = 0; i < marks.length; i++) marks[i].clear();
}
for (var i = 0; i < editors.length; i++) {
var editor = editors[i];
var mark = collapseSingle(editor.cm, editor.line, editor.line + size);
marks.push(mark);
mark.mark.on("clear", clear);
}
return marks[0].mark;
}
function unclearNearChunks(dv, margin, off, clear) {
for (var i = 0; i < dv.chunks.length; i++) {
var chunk = dv.chunks[i];
for (var l = chunk.editFrom - margin; l < chunk.editTo + margin; l++) {
var pos = l + off;
if (pos >= 0 && pos < clear.length) clear[pos] = false;
}
}
}
function collapseIdenticalStretches(mv, margin) {
if (typeof margin != "number") margin = 2;
var clear = [], edit = mv.editor(), off = edit.firstLine();
for (var l = off, e = edit.lastLine(); l <= e; l++) clear.push(true);
if (mv.left) unclearNearChunks(mv.left, margin, off, clear);
if (mv.right) unclearNearChunks(mv.right, margin, off, clear);
for (var i = 0; i < clear.length; i++) {
if (clear[i]) {
var line = i + off;
for (var size = 1; i < clear.length - 1 && clear[i + 1]; i++, size++) {}
if (size > margin) {
var editors = [{line: line, cm: edit}];
if (mv.left) editors.push({line: getMatchingOrigLine(line, mv.left.chunks), cm: mv.left.orig});
if (mv.right) editors.push({line: getMatchingOrigLine(line, mv.right.chunks), cm: mv.right.orig});
var mark = collapseStretch(size, editors);
if (mv.options.onCollapse) mv.options.onCollapse(mv, line, size, mark);
}
}
}
}
// General utilities
function elt(tag, content, className, style) {
var e = document.createElement(tag);
if (className) e.className = className;
if (style) e.style.cssText = style;
if (typeof content == "string") e.appendChild(document.createTextNode(content));
else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);
return e;
}
function clear(node) {
for (var count = node.childNodes.length; count > 0; --count)
node.removeChild(node.firstChild);
}
function attrs(elt) {
for (var i = 1; i < arguments.length; i += 2)
elt.setAttribute(arguments[i], arguments[i+1]);
}
function copyObj(obj, target) {
if (!target) target = {};
for (var prop in obj) if (obj.hasOwnProperty(prop)) target[prop] = obj[prop];
return target;
}
function moveOver(pos, str, copy, other) {
var out = copy ? Pos(pos.line, pos.ch) : pos, at = 0;
for (;;) {
var nl = str.indexOf("\n", at);
if (nl == -1) break;
++out.line;
if (other) ++other.line;
at = nl + 1;
}
out.ch = (at ? 0 : out.ch) + (str.length - at);
if (other) other.ch = (at ? 0 : other.ch) + (str.length - at);
return out;
}
// Tracks collapsed markers and line widgets, in order to be able to
// accurately align the content of two editors.
var F_WIDGET = 1, F_WIDGET_BELOW = 2, F_MARKER = 4
function TrackAlignable(cm) {
this.cm = cm
this.alignable = []
this.height = cm.doc.height
var self = this
cm.on("markerAdded", function(_, marker) {
if (!marker.collapsed) return
var found = marker.find(1)
if (found != null) self.set(found.line, F_MARKER)
})
cm.on("markerCleared", function(_, marker, _min, max) {
if (max != null && marker.collapsed)
self.check(max, F_MARKER, self.hasMarker)
})
cm.on("markerChanged", this.signal.bind(this))
cm.on("lineWidgetAdded", function(_, widget, lineNo) {
if (widget.mergeSpacer) return
if (widget.above) self.set(lineNo - 1, F_WIDGET_BELOW)
else self.set(lineNo, F_WIDGET)
})
cm.on("lineWidgetCleared", function(_, widget, lineNo) {
if (widget.mergeSpacer) return
if (widget.above) self.check(lineNo - 1, F_WIDGET_BELOW, self.hasWidgetBelow)
else self.check(lineNo, F_WIDGET, self.hasWidget)
})
cm.on("lineWidgetChanged", this.signal.bind(this))
cm.on("change", function(_, change) {
var start = change.from.line, nBefore = change.to.line - change.from.line
var nAfter = change.text.length - 1, end = start + nAfter
if (nBefore || nAfter) self.map(start, nBefore, nAfter)
self.check(end, F_MARKER, self.hasMarker)
if (nBefore || nAfter) self.check(change.from.line, F_MARKER, self.hasMarker)
})
cm.on("viewportChange", function() {
if (self.cm.doc.height != self.height) self.signal()
})
}
TrackAlignable.prototype = {
signal: function() {
CodeMirror.signal(this, "realign")
this.height = this.cm.doc.height
},
set: function(n, flags) {
var pos = -1
for (; pos < this.alignable.length; pos += 2) {
var diff = this.alignable[pos] - n
if (diff == 0) {
if ((this.alignable[pos + 1] & flags) == flags) return
this.alignable[pos + 1] |= flags
this.signal()
return
}
if (diff > 0) break
}
this.signal()
this.alignable.splice(pos, 0, n, flags)
},
find: function(n) {
for (var i = 0; i < this.alignable.length; i += 2)
if (this.alignable[i] == n) return i
return -1
},
check: function(n, flag, pred) {
var found = this.find(n)
if (found == -1 || !(this.alignable[found + 1] & flag)) return
if (!pred.call(this, n)) {
this.signal()
var flags = this.alignable[found + 1] & ~flag
if (flags) this.alignable[found + 1] = flags
else this.alignable.splice(found, 2)
}
},
hasMarker: function(n) {
var handle = this.cm.getLineHandle(n)
if (handle.markedSpans) for (var i = 0; i < handle.markedSpans.length; i++)
if (handle.markedSpans[i].mark.collapsed && handle.markedSpans[i].to != null)
return true
return false
},
hasWidget: function(n) {
var handle = this.cm.getLineHandle(n)
if (handle.widgets) for (var i = 0; i < handle.widgets.length; i++)
if (!handle.widgets[i].above && !handle.widgets[i].mergeSpacer) return true
return false
},
hasWidgetBelow: function(n) {
if (n == this.cm.lastLine()) return false
var handle = this.cm.getLineHandle(n + 1)
if (handle.widgets) for (var i = 0; i < handle.widgets.length; i++)
if (handle.widgets[i].above && !handle.widgets[i].mergeSpacer) return true
return false
},
map: function(from, nBefore, nAfter) {
var diff = nAfter - nBefore, to = from + nBefore, widgetFrom = -1, widgetTo = -1
for (var i = 0; i < this.alignable.length; i += 2) {
var n = this.alignable[i]
if (n == from && (this.alignable[i + 1] & F_WIDGET_BELOW)) widgetFrom = i
if (n == to && (this.alignable[i + 1] & F_WIDGET_BELOW)) widgetTo = i
if (n <= from) continue
else if (n < to) this.alignable.splice(i--, 2)
else this.alignable[i] += diff
}
if (widgetFrom > -1) {
var flags = this.alignable[widgetFrom + 1]
if (flags == F_WIDGET_BELOW) this.alignable.splice(widgetFrom, 2)
else this.alignable[widgetFrom + 1] = flags & ~F_WIDGET_BELOW
}
if (widgetTo > -1 && nAfter)
this.set(from + nAfter, F_WIDGET_BELOW)
}
}
function posMin(a, b) { return (a.line - b.line || a.ch - b.ch) < 0 ? a : b; }
function posMax(a, b) { return (a.line - b.line || a.ch - b.ch) > 0 ? a : b; }
function posEq(a, b) { return a.line == b.line && a.ch == b.ch; }
function findPrevDiff(chunks, start, isOrig) {
for (var i = chunks.length - 1; i >= 0; i--) {
var chunk = chunks[i];
var to = (isOrig ? chunk.origTo : chunk.editTo) - 1;
if (to < start) return to;
}
}
function findNextDiff(chunks, start, isOrig) {
for (var i = 0; i < chunks.length; i++) {
var chunk = chunks[i];
var from = (isOrig ? chunk.origFrom : chunk.editFrom);
if (from > start) return from;
}
}
function goNearbyDiff(cm, dir) {
var found = null, views = cm.state.diffViews, line = cm.getCursor().line;
if (views) for (var i = 0; i < views.length; i++) {
var dv = views[i], isOrig = cm == dv.orig;
ensureDiff(dv);
var pos = dir < 0 ? findPrevDiff(dv.chunks, line, isOrig) : findNextDiff(dv.chunks, line, isOrig);
if (pos != null && (found == null || (dir < 0 ? pos > found : pos < found)))
found = pos;
}
if (found != null)
cm.setCursor(found, 0);
else
return CodeMirror.Pass;
}
CodeMirror.commands.goNextDiff = function(cm) {
return goNearbyDiff(cm, 1);
};
CodeMirror.commands.goPrevDiff = function(cm) {
return goNearbyDiff(cm, -1);
};
});

View File

@ -0,0 +1,64 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"), "cjs");
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], function(CM) { mod(CM, "amd"); });
else // Plain browser env
mod(CodeMirror, "plain");
})(function(CodeMirror, env) {
if (!CodeMirror.modeURL) CodeMirror.modeURL = "../mode/%N/%N.js";
var loading = {};
function splitCallback(cont, n) {
var countDown = n;
return function() { if (--countDown == 0) cont(); };
}
function ensureDeps(mode, cont) {
var deps = CodeMirror.modes[mode].dependencies;
if (!deps) return cont();
var missing = [];
for (var i = 0; i < deps.length; ++i) {
if (!CodeMirror.modes.hasOwnProperty(deps[i]))
missing.push(deps[i]);
}
if (!missing.length) return cont();
var split = splitCallback(cont, missing.length);
for (var i = 0; i < missing.length; ++i)
CodeMirror.requireMode(missing[i], split);
}
CodeMirror.requireMode = function(mode, cont) {
if (typeof mode != "string") mode = mode.name;
if (CodeMirror.modes.hasOwnProperty(mode)) return ensureDeps(mode, cont);
if (loading.hasOwnProperty(mode)) return loading[mode].push(cont);
var file = CodeMirror.modeURL.replace(/%N/g, mode);
if (env == "plain") {
var script = document.createElement("script");
script.src = file;
var others = document.getElementsByTagName("script")[0];
var list = loading[mode] = [cont];
CodeMirror.on(script, "load", function() {
ensureDeps(mode, function() {
for (var i = 0; i < list.length; ++i) list[i]();
});
});
others.parentNode.insertBefore(script, others);
} else if (env == "cjs") {
require(file);
cont();
} else if (env == "amd") {
requirejs([file], cont);
}
};
CodeMirror.autoLoadMode = function(instance, mode) {
if (!CodeMirror.modes.hasOwnProperty(mode))
CodeMirror.requireMode(mode, function() {
instance.setOption("mode", instance.getOption("mode"));
});
};
});

Some files were not shown because too many files have changed in this diff Show More